| [ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 <?php 2 //============================================================+ 3 // File name : tcpdf.php 4 // Version : 6.0.093 5 // Begin : 2002-08-03 6 // Last Update : 2014-09-02 7 // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - [email protected] 8 // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) 9 // ------------------------------------------------------------------- 10 // Copyright (C) 2002-2014 Nicola Asuni - Tecnick.com LTD 11 // 12 // This file is part of TCPDF software library. 13 // 14 // TCPDF is free software: you can ioredistribute it and/or modify it 15 // under the terms of the GNU Lesser General Public License as 16 // published by the Free Software Foundation, either version 3 of the 17 // License, or (at your option) any later version. 18 // 19 // TCPDF is distributed in the hope that it will be useful, but 20 // WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 22 // See the GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the License 25 // along with TCPDF. If not, see 26 // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>. 27 // 28 // See LICENSE.TXT file for more information. 29 // ------------------------------------------------------------------- 30 // 31 // Description : 32 // This is a PHP class for generating PDF documents without requiring external extensions. 33 // 34 // NOTE: 35 // This class was originally derived in 2002 from the Public 36 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 37 // but now is almost entirely rewritten and contains thousands of 38 // new lines of code and hundreds new features. 39 // 40 // Main features: 41 // * no external libraries are required for the basic functions; 42 // * all standard page formats, custom page formats, custom margins and units of measure; 43 // * UTF-8 Unicode and Right-To-Left languages; 44 // * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts; 45 // * font subsetting; 46 // * methods to publish some XHTML + CSS code, Javascript and Forms; 47 // * images, graphic (geometric figures) and transformation methods; 48 // * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html) 49 // * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417; 50 // * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies; 51 // * automatic page header and footer management; 52 // * document encryption up to 256 bit and digital signature certifications; 53 // * transactions to UNDO commands; 54 // * PDF annotations, including links, text and file attachments; 55 // * text rendering modes (fill, stroke and clipping); 56 // * multiple columns mode; 57 // * no-write page regions; 58 // * bookmarks, named destinations and table of content; 59 // * text hyphenation; 60 // * text stretching and spacing (tracking); 61 // * automatic page break, line break and text alignments including justification; 62 // * automatic page numbering and page groups; 63 // * move and delete pages; 64 // * page compression (requires php-zlib extension); 65 // * XOBject Templates; 66 // * Layers and object visibility. 67 // * PDF/A-1b support 68 //============================================================+ 69 70 /** 71 * @file 72 * This is a PHP class for generating PDF documents without requiring external extensions.<br> 73 * 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> 74 * <h3>TCPDF main features are:</h3> 75 * <ul> 76 * <li>no external libraries are required for the basic functions;</li> 77 * <li>all standard page formats, custom page formats, custom margins and units of measure;</li> 78 * <li>UTF-8 Unicode and Right-To-Left languages;</li> 79 * <li>TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li> 80 * <li>font subsetting;</li> 81 * <li>methods to publish some XHTML + CSS code, Javascript and Forms;</li> 82 * <li>images, graphic (geometric figures) and transformation methods; 83 * <li>supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)</li> 84 * <li>1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;</li> 85 * <li>JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li> 86 * <li>automatic page header and footer management;</li> 87 * <li>document encryption up to 256 bit and digital signature certifications;</li> 88 * <li>transactions to UNDO commands;</li> 89 * <li>PDF annotations, including links, text and file attachments;</li> 90 * <li>text rendering modes (fill, stroke and clipping);</li> 91 * <li>multiple columns mode;</li> 92 * <li>no-write page regions;</li> 93 * <li>bookmarks, named destinations and table of content;</li> 94 * <li>text hyphenation;</li> 95 * <li>text stretching and spacing (tracking);</li> 96 * <li>automatic page break, line break and text alignments including justification;</li> 97 * <li>automatic page numbering and page groups;</li> 98 * <li>move and delete pages;</li> 99 * <li>page compression (requires php-zlib extension);</li> 100 * <li>XOBject Templates;</li> 101 * <li>Layers and object visibility;</li> 102 * <li>PDF/A-1b support.</li> 103 * </ul> 104 * Tools to encode your unicode fonts are on fonts/utils directory.</p> 105 * @package com.tecnick.tcpdf 106 * @author Nicola Asuni 107 * @version 6.0.093 108 */ 109 110 // TCPDF configuration 111 require_once(dirname(__FILE__).'/tcpdf_autoconfig.php'); 112 // TCPDF static font methods and data 113 require_once(dirname(__FILE__).'/include/tcpdf_font_data.php'); 114 // TCPDF static font methods and data 115 require_once(dirname(__FILE__).'/include/tcpdf_fonts.php'); 116 // TCPDF static color methods and data 117 require_once(dirname(__FILE__).'/include/tcpdf_colors.php'); 118 // TCPDF static image methods and data 119 require_once(dirname(__FILE__).'/include/tcpdf_images.php'); 120 // TCPDF static methods and data 121 require_once(dirname(__FILE__).'/include/tcpdf_static.php'); 122 123 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 124 125 /** 126 * @class TCPDF 127 * PHP class for generating PDF documents without requiring external extensions. 128 * 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> 129 * @package com.tecnick.tcpdf 130 * @brief PHP class for generating PDF documents without requiring external extensions. 131 * @version 6.0.093 132 * @author Nicola Asuni - [email protected] 133 */ 134 class TCPDF { 135 136 // Protected properties 137 138 /** 139 * Current page number. 140 * @protected 141 */ 142 protected $page; 143 144 /** 145 * Current object number. 146 * @protected 147 */ 148 protected $n; 149 150 /** 151 * Array of object offsets. 152 * @protected 153 */ 154 protected $offsets = array(); 155 156 /** 157 * Array of object IDs for each page. 158 * @protected 159 */ 160 protected $pageobjects = array(); 161 162 /** 163 * Buffer holding in-memory PDF. 164 * @protected 165 */ 166 protected $buffer; 167 168 /** 169 * Array containing pages. 170 * @protected 171 */ 172 protected $pages = array(); 173 174 /** 175 * Current document state. 176 * @protected 177 */ 178 protected $state; 179 180 /** 181 * Compression flag. 182 * @protected 183 */ 184 protected $compress; 185 186 /** 187 * Current page orientation (P = Portrait, L = Landscape). 188 * @protected 189 */ 190 protected $CurOrientation; 191 192 /** 193 * Page dimensions. 194 * @protected 195 */ 196 protected $pagedim = array(); 197 198 /** 199 * Scale factor (number of points in user unit). 200 * @protected 201 */ 202 protected $k; 203 204 /** 205 * Width of page format in points. 206 * @protected 207 */ 208 protected $fwPt; 209 210 /** 211 * Height of page format in points. 212 * @protected 213 */ 214 protected $fhPt; 215 216 /** 217 * Current width of page in points. 218 * @protected 219 */ 220 protected $wPt; 221 222 /** 223 * Current height of page in points. 224 * @protected 225 */ 226 protected $hPt; 227 228 /** 229 * Current width of page in user unit. 230 * @protected 231 */ 232 protected $w; 233 234 /** 235 * Current height of page in user unit. 236 * @protected 237 */ 238 protected $h; 239 240 /** 241 * Left margin. 242 * @protected 243 */ 244 protected $lMargin; 245 246 /** 247 * Right margin. 248 * @protected 249 */ 250 protected $rMargin; 251 252 /** 253 * Cell left margin (used by regions). 254 * @protected 255 */ 256 protected $clMargin; 257 258 /** 259 * Cell right margin (used by regions). 260 * @protected 261 */ 262 protected $crMargin; 263 264 /** 265 * Top margin. 266 * @protected 267 */ 268 protected $tMargin; 269 270 /** 271 * Page break margin. 272 * @protected 273 */ 274 protected $bMargin; 275 276 /** 277 * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left). 278 * @since 5.9.000 (2010-10-03) 279 * @protected 280 */ 281 protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0); 282 283 /** 284 * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left). 285 * @since 5.9.000 (2010-10-04) 286 * @protected 287 */ 288 protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0); 289 290 /** 291 * Current horizontal position in user unit for cell positioning. 292 * @protected 293 */ 294 protected $x; 295 296 /** 297 * Current vertical position in user unit for cell positioning. 298 * @protected 299 */ 300 protected $y; 301 302 /** 303 * Height of last cell printed. 304 * @protected 305 */ 306 protected $lasth; 307 308 /** 309 * Line width in user unit. 310 * @protected 311 */ 312 protected $LineWidth; 313 314 /** 315 * Array of standard font names. 316 * @protected 317 */ 318 protected $CoreFonts; 319 320 /** 321 * Array of used fonts. 322 * @protected 323 */ 324 protected $fonts = array(); 325 326 /** 327 * Array of font files. 328 * @protected 329 */ 330 protected $FontFiles = array(); 331 332 /** 333 * Array of encoding differences. 334 * @protected 335 */ 336 protected $diffs = array(); 337 338 /** 339 * Array of used images. 340 * @protected 341 */ 342 protected $images = array(); 343 344 /** 345 * Array of cached files. 346 * @protected 347 */ 348 protected $cached_files = array(); 349 350 /** 351 * Array of Annotations in pages. 352 * @protected 353 */ 354 protected $PageAnnots = array(); 355 356 /** 357 * Array of internal links. 358 * @protected 359 */ 360 protected $links = array(); 361 362 /** 363 * Current font family. 364 * @protected 365 */ 366 protected $FontFamily; 367 368 /** 369 * Current font style. 370 * @protected 371 */ 372 protected $FontStyle; 373 374 /** 375 * Current font ascent (distance between font top and baseline). 376 * @protected 377 * @since 2.8.000 (2007-03-29) 378 */ 379 protected $FontAscent; 380 381 /** 382 * Current font descent (distance between font bottom and baseline). 383 * @protected 384 * @since 2.8.000 (2007-03-29) 385 */ 386 protected $FontDescent; 387 388 /** 389 * Underlining flag. 390 * @protected 391 */ 392 protected $underline; 393 394 /** 395 * Overlining flag. 396 * @protected 397 */ 398 protected $overline; 399 400 /** 401 * Current font info. 402 * @protected 403 */ 404 protected $CurrentFont; 405 406 /** 407 * Current font size in points. 408 * @protected 409 */ 410 protected $FontSizePt; 411 412 /** 413 * Current font size in user unit. 414 * @protected 415 */ 416 protected $FontSize; 417 418 /** 419 * Commands for drawing color. 420 * @protected 421 */ 422 protected $DrawColor; 423 424 /** 425 * Commands for filling color. 426 * @protected 427 */ 428 protected $FillColor; 429 430 /** 431 * Commands for text color. 432 * @protected 433 */ 434 protected $TextColor; 435 436 /** 437 * Indicates whether fill and text colors are different. 438 * @protected 439 */ 440 protected $ColorFlag; 441 442 /** 443 * Automatic page breaking. 444 * @protected 445 */ 446 protected $AutoPageBreak; 447 448 /** 449 * Threshold used to trigger page breaks. 450 * @protected 451 */ 452 protected $PageBreakTrigger; 453 454 /** 455 * Flag set when processing page header. 456 * @protected 457 */ 458 protected $InHeader = false; 459 460 /** 461 * Flag set when processing page footer. 462 * @protected 463 */ 464 protected $InFooter = false; 465 466 /** 467 * Zoom display mode. 468 * @protected 469 */ 470 protected $ZoomMode; 471 472 /** 473 * Layout display mode. 474 * @protected 475 */ 476 protected $LayoutMode; 477 478 /** 479 * If true set the document information dictionary in Unicode. 480 * @protected 481 */ 482 protected $docinfounicode = true; 483 484 /** 485 * Document title. 486 * @protected 487 */ 488 protected $title = ''; 489 490 /** 491 * Document subject. 492 * @protected 493 */ 494 protected $subject = ''; 495 496 /** 497 * Document author. 498 * @protected 499 */ 500 protected $author = ''; 501 502 /** 503 * Document keywords. 504 * @protected 505 */ 506 protected $keywords = ''; 507 508 /** 509 * Document creator. 510 * @protected 511 */ 512 protected $creator = ''; 513 514 /** 515 * Starting page number. 516 * @protected 517 */ 518 protected $starting_page_number = 1; 519 520 /** 521 * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image. 522 * @since 2002-07-31 523 * @author Nicola Asuni 524 * @protected 525 */ 526 protected $img_rb_x; 527 528 /** 529 * The right-bottom corner Y coordinate of last inserted image. 530 * @since 2002-07-31 531 * @author Nicola Asuni 532 * @protected 533 */ 534 protected $img_rb_y; 535 536 /** 537 * Adjusting factor to convert pixels to user units. 538 * @since 2004-06-14 539 * @author Nicola Asuni 540 * @protected 541 */ 542 protected $imgscale = 1; 543 544 /** 545 * Boolean flag set to true when the input text is unicode (require unicode fonts). 546 * @since 2005-01-02 547 * @author Nicola Asuni 548 * @protected 549 */ 550 protected $isunicode = false; 551 552 /** 553 * PDF version. 554 * @since 1.5.3 555 * @protected 556 */ 557 protected $PDFVersion = '1.7'; 558 559 /** 560 * ID of the stored default header template (-1 = not set). 561 * @protected 562 */ 563 protected $header_xobjid = false; 564 565 /** 566 * If true reset the Header Xobject template at each page 567 * @protected 568 */ 569 protected $header_xobj_autoreset = false; 570 571 /** 572 * Minimum distance between header and top page margin. 573 * @protected 574 */ 575 protected $header_margin; 576 577 /** 578 * Minimum distance between footer and bottom page margin. 579 * @protected 580 */ 581 protected $footer_margin; 582 583 /** 584 * Original left margin value. 585 * @protected 586 * @since 1.53.0.TC013 587 */ 588 protected $original_lMargin; 589 590 /** 591 * Original right margin value. 592 * @protected 593 * @since 1.53.0.TC013 594 */ 595 protected $original_rMargin; 596 597 /** 598 * Default font used on page header. 599 * @protected 600 */ 601 protected $header_font; 602 603 /** 604 * Default font used on page footer. 605 * @protected 606 */ 607 protected $footer_font; 608 609 /** 610 * Language templates. 611 * @protected 612 */ 613 protected $l; 614 615 /** 616 * Barcode to print on page footer (only if set). 617 * @protected 618 */ 619 protected $barcode = false; 620 621 /** 622 * Boolean flag to print/hide page header. 623 * @protected 624 */ 625 protected $print_header = true; 626 627 /** 628 * Boolean flag to print/hide page footer. 629 * @protected 630 */ 631 protected $print_footer = true; 632 633 /** 634 * Header image logo. 635 * @protected 636 */ 637 protected $header_logo = ''; 638 639 /** 640 * Width of header image logo in user units. 641 * @protected 642 */ 643 protected $header_logo_width = 30; 644 645 /** 646 * Title to be printed on default page header. 647 * @protected 648 */ 649 protected $header_title = ''; 650 651 /** 652 * String to pring on page header after title. 653 * @protected 654 */ 655 protected $header_string = ''; 656 657 /** 658 * Color for header text (RGB array). 659 * @since 5.9.174 (2012-07-25) 660 * @protected 661 */ 662 protected $header_text_color = array(0,0,0); 663 664 /** 665 * Color for header line (RGB array). 666 * @since 5.9.174 (2012-07-25) 667 * @protected 668 */ 669 protected $header_line_color = array(0,0,0); 670 671 /** 672 * Color for footer text (RGB array). 673 * @since 5.9.174 (2012-07-25) 674 * @protected 675 */ 676 protected $footer_text_color = array(0,0,0); 677 678 /** 679 * Color for footer line (RGB array). 680 * @since 5.9.174 (2012-07-25) 681 * @protected 682 */ 683 protected $footer_line_color = array(0,0,0); 684 685 /** 686 * Text shadow data array. 687 * @since 5.9.174 (2012-07-25) 688 * @protected 689 */ 690 protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal'); 691 692 /** 693 * Default number of columns for html table. 694 * @protected 695 */ 696 protected $default_table_columns = 4; 697 698 // variables for html parser 699 700 /** 701 * HTML PARSER: array to store current link and rendering styles. 702 * @protected 703 */ 704 protected $HREF = array(); 705 706 /** 707 * List of available fonts on filesystem. 708 * @protected 709 */ 710 protected $fontlist = array(); 711 712 /** 713 * Current foreground color. 714 * @protected 715 */ 716 protected $fgcolor; 717 718 /** 719 * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise. 720 * @protected 721 */ 722 protected $listordered = array(); 723 724 /** 725 * HTML PARSER: array count list items on nested lists. 726 * @protected 727 */ 728 protected $listcount = array(); 729 730 /** 731 * HTML PARSER: current list nesting level. 732 * @protected 733 */ 734 protected $listnum = 0; 735 736 /** 737 * HTML PARSER: indent amount for lists. 738 * @protected 739 */ 740 protected $listindent = 0; 741 742 /** 743 * HTML PARSER: current list indententation level. 744 * @protected 745 */ 746 protected $listindentlevel = 0; 747 748 /** 749 * Current background color. 750 * @protected 751 */ 752 protected $bgcolor; 753 754 /** 755 * Temporary font size in points. 756 * @protected 757 */ 758 protected $tempfontsize = 10; 759 760 /** 761 * Spacer string for LI tags. 762 * @protected 763 */ 764 protected $lispacer = ''; 765 766 /** 767 * Default encoding. 768 * @protected 769 * @since 1.53.0.TC010 770 */ 771 protected $encoding = 'UTF-8'; 772 773 /** 774 * PHP internal encoding. 775 * @protected 776 * @since 1.53.0.TC016 777 */ 778 protected $internal_encoding; 779 780 /** 781 * Boolean flag to indicate if the document language is Right-To-Left. 782 * @protected 783 * @since 2.0.000 784 */ 785 protected $rtl = false; 786 787 /** 788 * Boolean flag used to force RTL or LTR string direction. 789 * @protected 790 * @since 2.0.000 791 */ 792 protected $tmprtl = false; 793 794 // --- Variables used for document encryption: 795 796 /** 797 * IBoolean flag indicating whether document is protected. 798 * @protected 799 * @since 2.0.000 (2008-01-02) 800 */ 801 protected $encrypted; 802 803 /** 804 * Array containing encryption settings. 805 * @protected 806 * @since 5.0.005 (2010-05-11) 807 */ 808 protected $encryptdata = array(); 809 810 /** 811 * Last RC4 key encrypted (cached for optimisation). 812 * @protected 813 * @since 2.0.000 (2008-01-02) 814 */ 815 protected $last_enc_key; 816 817 /** 818 * Last RC4 computed key. 819 * @protected 820 * @since 2.0.000 (2008-01-02) 821 */ 822 protected $last_enc_key_c; 823 824 /** 825 * File ID (used on document trailer). 826 * @protected 827 * @since 5.0.005 (2010-05-12) 828 */ 829 protected $file_id; 830 831 // --- bookmark --- 832 833 /** 834 * Outlines for bookmark. 835 * @protected 836 * @since 2.1.002 (2008-02-12) 837 */ 838 protected $outlines = array(); 839 840 /** 841 * Outline root for bookmark. 842 * @protected 843 * @since 2.1.002 (2008-02-12) 844 */ 845 protected $OutlineRoot; 846 847 // --- javascript and form --- 848 849 /** 850 * Javascript code. 851 * @protected 852 * @since 2.1.002 (2008-02-12) 853 */ 854 protected $javascript = ''; 855 856 /** 857 * Javascript counter. 858 * @protected 859 * @since 2.1.002 (2008-02-12) 860 */ 861 protected $n_js; 862 863 /** 864 * line through state 865 * @protected 866 * @since 2.8.000 (2008-03-19) 867 */ 868 protected $linethrough; 869 870 /** 871 * Array with additional document-wide usage rights for the document. 872 * @protected 873 * @since 5.8.014 (2010-08-23) 874 */ 875 protected $ur = array(); 876 877 /** 878 * DPI (Dot Per Inch) Document Resolution (do not change). 879 * @protected 880 * @since 3.0.000 (2008-03-27) 881 */ 882 protected $dpi = 72; 883 884 /** 885 * Array of page numbers were a new page group was started (the page numbers are the keys of the array). 886 * @protected 887 * @since 3.0.000 (2008-03-27) 888 */ 889 protected $newpagegroup = array(); 890 891 /** 892 * Array that contains the number of pages in each page group. 893 * @protected 894 * @since 3.0.000 (2008-03-27) 895 */ 896 protected $pagegroups = array(); 897 898 /** 899 * Current page group number. 900 * @protected 901 * @since 3.0.000 (2008-03-27) 902 */ 903 protected $currpagegroup = 0; 904 905 /** 906 * Array of transparency objects and parameters. 907 * @protected 908 * @since 3.0.000 (2008-03-27) 909 */ 910 protected $extgstates; 911 912 /** 913 * Set the default JPEG compression quality (1-100). 914 * @protected 915 * @since 3.0.000 (2008-03-27) 916 */ 917 protected $jpeg_quality; 918 919 /** 920 * Default cell height ratio. 921 * @protected 922 * @since 3.0.014 (2008-05-23) 923 */ 924 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO; 925 926 /** 927 * PDF viewer preferences. 928 * @protected 929 * @since 3.1.000 (2008-06-09) 930 */ 931 protected $viewer_preferences; 932 933 /** 934 * A name object specifying how the document should be displayed when opened. 935 * @protected 936 * @since 3.1.000 (2008-06-09) 937 */ 938 protected $PageMode; 939 940 /** 941 * Array for storing gradient information. 942 * @protected 943 * @since 3.1.000 (2008-06-09) 944 */ 945 protected $gradients = array(); 946 947 /** 948 * Array used to store positions inside the pages buffer (keys are the page numbers). 949 * @protected 950 * @since 3.2.000 (2008-06-26) 951 */ 952 protected $intmrk = array(); 953 954 /** 955 * Array used to store positions inside the pages buffer (keys are the page numbers). 956 * @protected 957 * @since 5.7.000 (2010-08-03) 958 */ 959 protected $bordermrk = array(); 960 961 /** 962 * Array used to store page positions to track empty pages (keys are the page numbers). 963 * @protected 964 * @since 5.8.007 (2010-08-18) 965 */ 966 protected $emptypagemrk = array(); 967 968 /** 969 * Array used to store content positions inside the pages buffer (keys are the page numbers). 970 * @protected 971 * @since 4.6.021 (2009-07-20) 972 */ 973 protected $cntmrk = array(); 974 975 /** 976 * Array used to store footer positions of each page. 977 * @protected 978 * @since 3.2.000 (2008-07-01) 979 */ 980 protected $footerpos = array(); 981 982 /** 983 * Array used to store footer length of each page. 984 * @protected 985 * @since 4.0.014 (2008-07-29) 986 */ 987 protected $footerlen = array(); 988 989 /** 990 * Boolean flag to indicate if a new line is created. 991 * @protected 992 * @since 3.2.000 (2008-07-01) 993 */ 994 protected $newline = true; 995 996 /** 997 * End position of the latest inserted line. 998 * @protected 999 * @since 3.2.000 (2008-07-01) 1000 */ 1001 protected $endlinex = 0; 1002 1003 /** 1004 * PDF string for width value of the last line. 1005 * @protected 1006 * @since 4.0.006 (2008-07-16) 1007 */ 1008 protected $linestyleWidth = ''; 1009 1010 /** 1011 * PDF string for CAP value of the last line. 1012 * @protected 1013 * @since 4.0.006 (2008-07-16) 1014 */ 1015 protected $linestyleCap = '0 J'; 1016 1017 /** 1018 * PDF string for join value of the last line. 1019 * @protected 1020 * @since 4.0.006 (2008-07-16) 1021 */ 1022 protected $linestyleJoin = '0 j'; 1023 1024 /** 1025 * PDF string for dash value of the last line. 1026 * @protected 1027 * @since 4.0.006 (2008-07-16) 1028 */ 1029 protected $linestyleDash = '[] 0 d'; 1030 1031 /** 1032 * Boolean flag to indicate if marked-content sequence is open. 1033 * @protected 1034 * @since 4.0.013 (2008-07-28) 1035 */ 1036 protected $openMarkedContent = false; 1037 1038 /** 1039 * Count the latest inserted vertical spaces on HTML. 1040 * @protected 1041 * @since 4.0.021 (2008-08-24) 1042 */ 1043 protected $htmlvspace = 0; 1044 1045 /** 1046 * Array of Spot colors. 1047 * @protected 1048 * @since 4.0.024 (2008-09-12) 1049 */ 1050 protected $spot_colors = array(); 1051 1052 /** 1053 * Symbol used for HTML unordered list items. 1054 * @protected 1055 * @since 4.0.028 (2008-09-26) 1056 */ 1057 protected $lisymbol = ''; 1058 1059 /** 1060 * String used to mark the beginning and end of EPS image blocks. 1061 * @protected 1062 * @since 4.1.000 (2008-10-18) 1063 */ 1064 protected $epsmarker = 'x#!#EPS#!#x'; 1065 1066 /** 1067 * Array of transformation matrix. 1068 * @protected 1069 * @since 4.2.000 (2008-10-29) 1070 */ 1071 protected $transfmatrix = array(); 1072 1073 /** 1074 * Current key for transformation matrix. 1075 * @protected 1076 * @since 4.8.005 (2009-09-17) 1077 */ 1078 protected $transfmatrix_key = 0; 1079 1080 /** 1081 * Booklet mode for double-sided pages. 1082 * @protected 1083 * @since 4.2.000 (2008-10-29) 1084 */ 1085 protected $booklet = false; 1086 1087 /** 1088 * Epsilon value used for float calculations. 1089 * @protected 1090 * @since 4.2.000 (2008-10-29) 1091 */ 1092 protected $feps = 0.005; 1093 1094 /** 1095 * Array used for custom vertical spaces for HTML tags. 1096 * @protected 1097 * @since 4.2.001 (2008-10-30) 1098 */ 1099 protected $tagvspaces = array(); 1100 1101 /** 1102 * HTML PARSER: custom indent amount for lists. Negative value means disabled. 1103 * @protected 1104 * @since 4.2.007 (2008-11-12) 1105 */ 1106 protected $customlistindent = -1; 1107 1108 /** 1109 * Boolean flag to indicate if the border of the cell sides that cross the page should be removed. 1110 * @protected 1111 * @since 4.2.010 (2008-11-14) 1112 */ 1113 protected $opencell = true; 1114 1115 /** 1116 * Array of files to embedd. 1117 * @protected 1118 * @since 4.4.000 (2008-12-07) 1119 */ 1120 protected $embeddedfiles = array(); 1121 1122 /** 1123 * Boolean flag to indicate if we are inside a PRE tag. 1124 * @protected 1125 * @since 4.4.001 (2008-12-08) 1126 */ 1127 protected $premode = false; 1128 1129 /** 1130 * Array used to store positions of graphics transformation blocks inside the page buffer. 1131 * keys are the page numbers 1132 * @protected 1133 * @since 4.4.002 (2008-12-09) 1134 */ 1135 protected $transfmrk = array(); 1136 1137 /** 1138 * Default color for html links. 1139 * @protected 1140 * @since 4.4.003 (2008-12-09) 1141 */ 1142 protected $htmlLinkColorArray = array(0, 0, 255); 1143 1144 /** 1145 * Default font style to add to html links. 1146 * @protected 1147 * @since 4.4.003 (2008-12-09) 1148 */ 1149 protected $htmlLinkFontStyle = 'U'; 1150 1151 /** 1152 * Counts the number of pages. 1153 * @protected 1154 * @since 4.5.000 (2008-12-31) 1155 */ 1156 protected $numpages = 0; 1157 1158 /** 1159 * Array containing page lengths in bytes. 1160 * @protected 1161 * @since 4.5.000 (2008-12-31) 1162 */ 1163 protected $pagelen = array(); 1164 1165 /** 1166 * Counts the number of pages. 1167 * @protected 1168 * @since 4.5.000 (2008-12-31) 1169 */ 1170 protected $numimages = 0; 1171 1172 /** 1173 * Store the image keys. 1174 * @protected 1175 * @since 4.5.000 (2008-12-31) 1176 */ 1177 protected $imagekeys = array(); 1178 1179 /** 1180 * Length of the buffer in bytes. 1181 * @protected 1182 * @since 4.5.000 (2008-12-31) 1183 */ 1184 protected $bufferlen = 0; 1185 1186 /** 1187 * If true enables disk caching. 1188 * @protected 1189 * @since 4.5.000 (2008-12-31) 1190 */ 1191 protected $diskcache = false; 1192 1193 /** 1194 * Counts the number of fonts. 1195 * @protected 1196 * @since 4.5.000 (2009-01-02) 1197 */ 1198 protected $numfonts = 0; 1199 1200 /** 1201 * Store the font keys. 1202 * @protected 1203 * @since 4.5.000 (2009-01-02) 1204 */ 1205 protected $fontkeys = array(); 1206 1207 /** 1208 * Store the font object IDs. 1209 * @protected 1210 * @since 4.8.001 (2009-09-09) 1211 */ 1212 protected $font_obj_ids = array(); 1213 1214 /** 1215 * Store the fage status (true when opened, false when closed). 1216 * @protected 1217 * @since 4.5.000 (2009-01-02) 1218 */ 1219 protected $pageopen = array(); 1220 1221 /** 1222 * Default monospace font. 1223 * @protected 1224 * @since 4.5.025 (2009-03-10) 1225 */ 1226 protected $default_monospaced_font = 'courier'; 1227 1228 /** 1229 * Cloned copy of the current class object. 1230 * @protected 1231 * @since 4.5.029 (2009-03-19) 1232 */ 1233 protected $objcopy; 1234 1235 /** 1236 * Array used to store the lengths of cache files. 1237 * @protected 1238 * @since 4.5.029 (2009-03-19) 1239 */ 1240 protected $cache_file_length = array(); 1241 1242 /** 1243 * Table header content to be repeated on each new page. 1244 * @protected 1245 * @since 4.5.030 (2009-03-20) 1246 */ 1247 protected $thead = ''; 1248 1249 /** 1250 * Margins used for table header. 1251 * @protected 1252 * @since 4.5.030 (2009-03-20) 1253 */ 1254 protected $theadMargins = array(); 1255 1256 /** 1257 * Boolean flag to enable document digital signature. 1258 * @protected 1259 * @since 4.6.005 (2009-04-24) 1260 */ 1261 protected $sign = false; 1262 1263 /** 1264 * Digital signature data. 1265 * @protected 1266 * @since 4.6.005 (2009-04-24) 1267 */ 1268 protected $signature_data = array(); 1269 1270 /** 1271 * Digital signature max length. 1272 * @protected 1273 * @since 4.6.005 (2009-04-24) 1274 */ 1275 protected $signature_max_length = 11742; 1276 1277 /** 1278 * Data for digital signature appearance. 1279 * @protected 1280 * @since 5.3.011 (2010-06-16) 1281 */ 1282 protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); 1283 1284 /** 1285 * Array of empty digital signature appearances. 1286 * @protected 1287 * @since 5.9.101 (2011-07-06) 1288 */ 1289 protected $empty_signature_appearance = array(); 1290 1291 /** 1292 * Boolean flag to enable document timestamping with TSA. 1293 * @protected 1294 * @since 6.0.085 (2014-06-19) 1295 */ 1296 protected $tsa_timestamp = false; 1297 1298 /** 1299 * Timestamping data. 1300 * @protected 1301 * @since 6.0.085 (2014-06-19) 1302 */ 1303 protected $tsa_data = array(); 1304 1305 /** 1306 * Regular expression used to find blank characters (required for word-wrapping). 1307 * @protected 1308 * @since 4.6.006 (2009-04-28) 1309 */ 1310 protected $re_spaces = '/[^\S\xa0]/'; 1311 1312 /** 1313 * Array of $re_spaces parts. 1314 * @protected 1315 * @since 5.5.011 (2010-07-09) 1316 */ 1317 protected $re_space = array('p' => '[^\S\xa0]', 'm' => ''); 1318 1319 /** 1320 * Digital signature object ID. 1321 * @protected 1322 * @since 4.6.022 (2009-06-23) 1323 */ 1324 protected $sig_obj_id = 0; 1325 1326 /** 1327 * ID of page objects. 1328 * @protected 1329 * @since 4.7.000 (2009-08-29) 1330 */ 1331 protected $page_obj_id = array(); 1332 1333 /** 1334 * List of form annotations IDs. 1335 * @protected 1336 * @since 4.8.000 (2009-09-07) 1337 */ 1338 protected $form_obj_id = array(); 1339 1340 /** 1341 * Deafult Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. Annotation options can be directly specified using the 'aopt' entry. 1342 * @protected 1343 * @since 4.8.000 (2009-09-07) 1344 */ 1345 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 1346 1347 /** 1348 * Javascript objects array. 1349 * @protected 1350 * @since 4.8.000 (2009-09-07) 1351 */ 1352 protected $js_objects = array(); 1353 1354 /** 1355 * Current form action (used during XHTML rendering). 1356 * @protected 1357 * @since 4.8.000 (2009-09-07) 1358 */ 1359 protected $form_action = ''; 1360 1361 /** 1362 * Current form encryption type (used during XHTML rendering). 1363 * @protected 1364 * @since 4.8.000 (2009-09-07) 1365 */ 1366 protected $form_enctype = 'application/x-www-form-urlencoded'; 1367 1368 /** 1369 * Current method to submit forms. 1370 * @protected 1371 * @since 4.8.000 (2009-09-07) 1372 */ 1373 protected $form_mode = 'post'; 1374 1375 /** 1376 * List of fonts used on form fields (fontname => fontkey). 1377 * @protected 1378 * @since 4.8.001 (2009-09-09) 1379 */ 1380 protected $annotation_fonts = array(); 1381 1382 /** 1383 * List of radio buttons parent objects. 1384 * @protected 1385 * @since 4.8.001 (2009-09-09) 1386 */ 1387 protected $radiobutton_groups = array(); 1388 1389 /** 1390 * List of radio group objects IDs. 1391 * @protected 1392 * @since 4.8.001 (2009-09-09) 1393 */ 1394 protected $radio_groups = array(); 1395 1396 /** 1397 * Text indentation value (used for text-indent CSS attribute). 1398 * @protected 1399 * @since 4.8.006 (2009-09-23) 1400 */ 1401 protected $textindent = 0; 1402 1403 /** 1404 * Store page number when startTransaction() is called. 1405 * @protected 1406 * @since 4.8.006 (2009-09-23) 1407 */ 1408 protected $start_transaction_page = 0; 1409 1410 /** 1411 * Store Y position when startTransaction() is called. 1412 * @protected 1413 * @since 4.9.001 (2010-03-28) 1414 */ 1415 protected $start_transaction_y = 0; 1416 1417 /** 1418 * True when we are printing the thead section on a new page. 1419 * @protected 1420 * @since 4.8.027 (2010-01-25) 1421 */ 1422 protected $inthead = false; 1423 1424 /** 1425 * Array of column measures (width, space, starting Y position). 1426 * @protected 1427 * @since 4.9.001 (2010-03-28) 1428 */ 1429 protected $columns = array(); 1430 1431 /** 1432 * Number of colums. 1433 * @protected 1434 * @since 4.9.001 (2010-03-28) 1435 */ 1436 protected $num_columns = 1; 1437 1438 /** 1439 * Current column number. 1440 * @protected 1441 * @since 4.9.001 (2010-03-28) 1442 */ 1443 protected $current_column = 0; 1444 1445 /** 1446 * Starting page for columns. 1447 * @protected 1448 * @since 4.9.001 (2010-03-28) 1449 */ 1450 protected $column_start_page = 0; 1451 1452 /** 1453 * Maximum page and column selected. 1454 * @protected 1455 * @since 5.8.000 (2010-08-11) 1456 */ 1457 protected $maxselcol = array('page' => 0, 'column' => 0); 1458 1459 /** 1460 * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding. 1461 * @protected 1462 * @since 5.8.000 (2010-08-11) 1463 */ 1464 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 1465 1466 /** 1467 * Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor stroke text (invisible); 4 = Fill text and add to path for clipping; 5 = Stroke text and add to path for clipping; 6 = Fill, then stroke text and add to path for clipping; 7 = Add text to path for clipping. 1468 * @protected 1469 * @since 4.9.008 (2010-04-03) 1470 */ 1471 protected $textrendermode = 0; 1472 1473 /** 1474 * Text stroke width in doc units. 1475 * @protected 1476 * @since 4.9.008 (2010-04-03) 1477 */ 1478 protected $textstrokewidth = 0; 1479 1480 /** 1481 * Current stroke color. 1482 * @protected 1483 * @since 4.9.008 (2010-04-03) 1484 */ 1485 protected $strokecolor; 1486 1487 /** 1488 * Default unit of measure for document. 1489 * @protected 1490 * @since 5.0.000 (2010-04-22) 1491 */ 1492 protected $pdfunit = 'mm'; 1493 1494 /** 1495 * Boolean flag true when we are on TOC (Table Of Content) page. 1496 * @protected 1497 */ 1498 protected $tocpage = false; 1499 1500 /** 1501 * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library. 1502 * @protected 1503 * @since 5.0.000 (2010-04-26) 1504 */ 1505 protected $rasterize_vector_images = false; 1506 1507 /** 1508 * Boolean flag: if true enables font subsetting by default. 1509 * @protected 1510 * @since 5.3.002 (2010-06-07) 1511 */ 1512 protected $font_subsetting = true; 1513 1514 /** 1515 * Array of default graphic settings. 1516 * @protected 1517 * @since 5.5.008 (2010-07-02) 1518 */ 1519 protected $default_graphic_vars = array(); 1520 1521 /** 1522 * Array of XObjects. 1523 * @protected 1524 * @since 5.8.014 (2010-08-23) 1525 */ 1526 protected $xobjects = array(); 1527 1528 /** 1529 * Boolean value true when we are inside an XObject. 1530 * @protected 1531 * @since 5.8.017 (2010-08-24) 1532 */ 1533 protected $inxobj = false; 1534 1535 /** 1536 * Current XObject ID. 1537 * @protected 1538 * @since 5.8.017 (2010-08-24) 1539 */ 1540 protected $xobjid = ''; 1541 1542 /** 1543 * Percentage of character stretching. 1544 * @protected 1545 * @since 5.9.000 (2010-09-29) 1546 */ 1547 protected $font_stretching = 100; 1548 1549 /** 1550 * Increases or decreases the space between characters in a text by the specified amount (tracking). 1551 * @protected 1552 * @since 5.9.000 (2010-09-29) 1553 */ 1554 protected $font_spacing = 0; 1555 1556 /** 1557 * Array of no-write regions. 1558 * ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right) 1559 * @protected 1560 * @since 5.9.003 (2010-10-14) 1561 */ 1562 protected $page_regions = array(); 1563 1564 /** 1565 * Boolean value true when page region check is active. 1566 * @protected 1567 */ 1568 protected $check_page_regions = true; 1569 1570 /** 1571 * Array of PDF layers data. 1572 * @protected 1573 * @since 5.9.102 (2011-07-13) 1574 */ 1575 protected $pdflayers = array(); 1576 1577 /** 1578 * A dictionary of names and corresponding destinations (Dests key on document Catalog). 1579 * @protected 1580 * @since 5.9.097 (2011-06-23) 1581 */ 1582 protected $dests = array(); 1583 1584 /** 1585 * Object ID for Named Destinations 1586 * @protected 1587 * @since 5.9.097 (2011-06-23) 1588 */ 1589 protected $n_dests; 1590 1591 /** 1592 * Embedded Files Names 1593 * @protected 1594 * @since 5.9.204 (2013-01-23) 1595 */ 1596 protected $efnames = array(); 1597 1598 /** 1599 * Directory used for the last SVG image. 1600 * @protected 1601 * @since 5.0.000 (2010-05-05) 1602 */ 1603 protected $svgdir = ''; 1604 1605 /** 1606 * Deafult unit of measure for SVG. 1607 * @protected 1608 * @since 5.0.000 (2010-05-02) 1609 */ 1610 protected $svgunit = 'px'; 1611 1612 /** 1613 * Array of SVG gradients. 1614 * @protected 1615 * @since 5.0.000 (2010-05-02) 1616 */ 1617 protected $svggradients = array(); 1618 1619 /** 1620 * ID of last SVG gradient. 1621 * @protected 1622 * @since 5.0.000 (2010-05-02) 1623 */ 1624 protected $svggradientid = 0; 1625 1626 /** 1627 * Boolean value true when in SVG defs group. 1628 * @protected 1629 * @since 5.0.000 (2010-05-02) 1630 */ 1631 protected $svgdefsmode = false; 1632 1633 /** 1634 * Array of SVG defs. 1635 * @protected 1636 * @since 5.0.000 (2010-05-02) 1637 */ 1638 protected $svgdefs = array(); 1639 1640 /** 1641 * Boolean value true when in SVG clipPath tag. 1642 * @protected 1643 * @since 5.0.000 (2010-04-26) 1644 */ 1645 protected $svgclipmode = false; 1646 1647 /** 1648 * Array of SVG clipPath commands. 1649 * @protected 1650 * @since 5.0.000 (2010-05-02) 1651 */ 1652 protected $svgclippaths = array(); 1653 1654 /** 1655 * Array of SVG clipPath tranformation matrix. 1656 * @protected 1657 * @since 5.8.022 (2010-08-31) 1658 */ 1659 protected $svgcliptm = array(); 1660 1661 /** 1662 * ID of last SVG clipPath. 1663 * @protected 1664 * @since 5.0.000 (2010-05-02) 1665 */ 1666 protected $svgclipid = 0; 1667 1668 /** 1669 * SVG text. 1670 * @protected 1671 * @since 5.0.000 (2010-05-02) 1672 */ 1673 protected $svgtext = ''; 1674 1675 /** 1676 * SVG text properties. 1677 * @protected 1678 * @since 5.8.013 (2010-08-23) 1679 */ 1680 protected $svgtextmode = array(); 1681 1682 /** 1683 * Array of SVG properties. 1684 * @protected 1685 * @since 5.0.000 (2010-05-02) 1686 */ 1687 protected $svgstyles = array(array( 1688 'alignment-baseline' => 'auto', 1689 'baseline-shift' => 'baseline', 1690 'clip' => 'auto', 1691 'clip-path' => 'none', 1692 'clip-rule' => 'nonzero', 1693 'color' => 'black', 1694 'color-interpolation' => 'sRGB', 1695 'color-interpolation-filters' => 'linearRGB', 1696 'color-profile' => 'auto', 1697 'color-rendering' => 'auto', 1698 'cursor' => 'auto', 1699 'direction' => 'ltr', 1700 'display' => 'inline', 1701 'dominant-baseline' => 'auto', 1702 'enable-background' => 'accumulate', 1703 'fill' => 'black', 1704 'fill-opacity' => 1, 1705 'fill-rule' => 'nonzero', 1706 'filter' => 'none', 1707 'flood-color' => 'black', 1708 'flood-opacity' => 1, 1709 'font' => '', 1710 'font-family' => 'helvetica', 1711 'font-size' => 'medium', 1712 'font-size-adjust' => 'none', 1713 'font-stretch' => 'normal', 1714 'font-style' => 'normal', 1715 'font-variant' => 'normal', 1716 'font-weight' => 'normal', 1717 'glyph-orientation-horizontal' => '0deg', 1718 'glyph-orientation-vertical' => 'auto', 1719 'image-rendering' => 'auto', 1720 'kerning' => 'auto', 1721 'letter-spacing' => 'normal', 1722 'lighting-color' => 'white', 1723 'marker' => '', 1724 'marker-end' => 'none', 1725 'marker-mid' => 'none', 1726 'marker-start' => 'none', 1727 'mask' => 'none', 1728 'opacity' => 1, 1729 'overflow' => 'auto', 1730 'pointer-events' => 'visiblePainted', 1731 'shape-rendering' => 'auto', 1732 'stop-color' => 'black', 1733 'stop-opacity' => 1, 1734 'stroke' => 'none', 1735 'stroke-dasharray' => 'none', 1736 'stroke-dashoffset' => 0, 1737 'stroke-linecap' => 'butt', 1738 'stroke-linejoin' => 'miter', 1739 'stroke-miterlimit' => 4, 1740 'stroke-opacity' => 1, 1741 'stroke-width' => 1, 1742 'text-anchor' => 'start', 1743 'text-decoration' => 'none', 1744 'text-rendering' => 'auto', 1745 'unicode-bidi' => 'normal', 1746 'visibility' => 'visible', 1747 'word-spacing' => 'normal', 1748 'writing-mode' => 'lr-tb', 1749 'text-color' => 'black', 1750 'transfmatrix' => array(1, 0, 0, 1, 0, 0) 1751 )); 1752 1753 /** 1754 * If true force sRGB color profile for all document. 1755 * @protected 1756 * @since 5.9.121 (2011-09-28) 1757 */ 1758 protected $force_srgb = false; 1759 1760 /** 1761 * If true set the document to PDF/A mode. 1762 * @protected 1763 * @since 5.9.121 (2011-09-27) 1764 */ 1765 protected $pdfa_mode = false; 1766 1767 /** 1768 * Document creation date-time 1769 * @protected 1770 * @since 5.9.152 (2012-03-22) 1771 */ 1772 protected $doc_creation_timestamp; 1773 1774 /** 1775 * Document modification date-time 1776 * @protected 1777 * @since 5.9.152 (2012-03-22) 1778 */ 1779 protected $doc_modification_timestamp; 1780 1781 /** 1782 * Custom XMP data. 1783 * @protected 1784 * @since 5.9.128 (2011-10-06) 1785 */ 1786 protected $custom_xmp = ''; 1787 1788 /** 1789 * Overprint mode array. 1790 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 1791 * @protected 1792 * @since 5.9.152 (2012-03-23) 1793 */ 1794 protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0); 1795 1796 /** 1797 * Alpha mode array. 1798 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 1799 * @protected 1800 * @since 5.9.152 (2012-03-23) 1801 */ 1802 protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false); 1803 1804 /** 1805 * Define the page boundaries boxes to be set on document. 1806 * @protected 1807 * @since 5.9.152 (2012-03-23) 1808 */ 1809 protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); 1810 1811 /** 1812 * If true print TCPDF meta link. 1813 * @protected 1814 * @since 5.9.152 (2012-03-23) 1815 */ 1816 protected $tcpdflink = true; 1817 1818 /** 1819 * Cache array for computed GD gamma values. 1820 * @protected 1821 * @since 5.9.1632 (2012-06-05) 1822 */ 1823 protected $gdgammacache = array(); 1824 1825 //------------------------------------------------------------ 1826 // METHODS 1827 //------------------------------------------------------------ 1828 1829 /** 1830 * This is the class constructor. 1831 * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes). 1832 * 1833 * IMPORTANT: Please note that this method sets the mb_internal_encoding to ASCII, so if you are using the mbstring module functions with TCPDF you need to correctly set/unset the mb_internal_encoding when needed. 1834 * 1835 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul> 1836 * @param $unit (string) 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. 1837 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). 1838 * @param $unicode (boolean) TRUE means that the input text is unicode (default = true) 1839 * @param $encoding (string) Charset encoding (used only when converting back html entities); default is UTF-8. 1840 * @param $diskcache (boolean) If TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower). 1841 * @param $pdfa (boolean) If TRUE set the document to PDF/A mode. 1842 * @public 1843 * @see getPageSizeFromFormat(), setPageFormat() 1844 */ 1845 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) { 1846 /* Set internal character encoding to ASCII */ 1847 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) { 1848 $this->internal_encoding = mb_internal_encoding(); 1849 mb_internal_encoding('ASCII'); 1850 } 1851 // set file ID for trailer 1852 $serformat = (is_array($format) ? json_encode($format) : $format); 1853 $this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding)); 1854 $this->font_obj_ids = array(); 1855 $this->page_obj_id = array(); 1856 $this->form_obj_id = array(); 1857 // set pdf/a mode 1858 $this->pdfa_mode = $pdfa; 1859 $this->force_srgb = false; 1860 // set disk caching 1861 $this->diskcache = $diskcache ? true : false; 1862 // set language direction 1863 $this->rtl = false; 1864 $this->tmprtl = false; 1865 // some checks 1866 $this->_dochecks(); 1867 // initialization of properties 1868 $this->isunicode = $unicode; 1869 $this->page = 0; 1870 $this->transfmrk[0] = array(); 1871 $this->pagedim = array(); 1872 $this->n = 2; 1873 $this->buffer = ''; 1874 $this->pages = array(); 1875 $this->state = 0; 1876 $this->fonts = array(); 1877 $this->FontFiles = array(); 1878 $this->diffs = array(); 1879 $this->images = array(); 1880 $this->links = array(); 1881 $this->gradients = array(); 1882 $this->InFooter = false; 1883 $this->lasth = 0; 1884 $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica'; 1885 $this->FontStyle = ''; 1886 $this->FontSizePt = 12; 1887 $this->underline = false; 1888 $this->overline = false; 1889 $this->linethrough = false; 1890 $this->DrawColor = '0 G'; 1891 $this->FillColor = '0 g'; 1892 $this->TextColor = '0 g'; 1893 $this->ColorFlag = false; 1894 $this->pdflayers = array(); 1895 // encryption values 1896 $this->encrypted = false; 1897 $this->last_enc_key = ''; 1898 // standard Unicode fonts 1899 $this->CoreFonts = array( 1900 'courier'=>'Courier', 1901 'courierB'=>'Courier-Bold', 1902 'courierI'=>'Courier-Oblique', 1903 'courierBI'=>'Courier-BoldOblique', 1904 'helvetica'=>'Helvetica', 1905 'helveticaB'=>'Helvetica-Bold', 1906 'helveticaI'=>'Helvetica-Oblique', 1907 'helveticaBI'=>'Helvetica-BoldOblique', 1908 'times'=>'Times-Roman', 1909 'timesB'=>'Times-Bold', 1910 'timesI'=>'Times-Italic', 1911 'timesBI'=>'Times-BoldItalic', 1912 'symbol'=>'Symbol', 1913 'zapfdingbats'=>'ZapfDingbats' 1914 ); 1915 // set scale factor 1916 $this->setPageUnit($unit); 1917 // set page format and orientation 1918 $this->setPageFormat($format, $orientation); 1919 // page margins (1 cm) 1920 $margin = 28.35 / $this->k; 1921 $this->SetMargins($margin, $margin); 1922 $this->clMargin = $this->lMargin; 1923 $this->crMargin = $this->rMargin; 1924 // internal cell padding 1925 $cpadding = $margin / 10; 1926 $this->setCellPaddings($cpadding, 0, $cpadding, 0); 1927 // cell margins 1928 $this->setCellMargins(0, 0, 0, 0); 1929 // line width (0.2 mm) 1930 $this->LineWidth = 0.57 / $this->k; 1931 $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k)); 1932 $this->linestyleCap = '0 J'; 1933 $this->linestyleJoin = '0 j'; 1934 $this->linestyleDash = '[] 0 d'; 1935 // automatic page break 1936 $this->SetAutoPageBreak(true, (2 * $margin)); 1937 // full width display mode 1938 $this->SetDisplayMode('fullwidth'); 1939 // compression 1940 $this->SetCompression(); 1941 // set default PDF version number 1942 $this->setPDFVersion(); 1943 $this->tcpdflink = true; 1944 $this->encoding = $encoding; 1945 $this->HREF = array(); 1946 $this->getFontsList(); 1947 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0); 1948 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0); 1949 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255); 1950 $this->extgstates = array(); 1951 $this->setTextShadow(); 1952 // signature 1953 $this->sign = false; 1954 $this->tsa_timestamp = false; 1955 $this->tsa_data = array(); 1956 $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature'); 1957 $this->empty_signature_appearance = array(); 1958 // user's rights 1959 $this->ur['enabled'] = false; 1960 $this->ur['document'] = '/FullSave'; 1961 $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export'; 1962 $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate'; 1963 $this->ur['signature'] = '/Modify'; 1964 $this->ur['ef'] = '/Create/Delete/Modify/Import'; 1965 $this->ur['formex'] = ''; 1966 // set default JPEG quality 1967 $this->jpeg_quality = 75; 1968 // initialize some settings 1969 TCPDF_FONTS::utf8Bidi(array(''), '', false, $this->isunicode, $this->CurrentFont); 1970 // set default font 1971 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 1972 $this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt)); 1973 $this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt)); 1974 // check if PCRE Unicode support is enabled 1975 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) { 1976 // PCRE unicode support is turned ON 1977 // \s : any whitespace character 1978 // \p{Z} : any separator 1979 // \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words. 1980 // \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0) 1981 //$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u'); 1982 $this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u'); 1983 } else { 1984 // PCRE unicode support is turned OFF 1985 $this->setSpacesRE('/[^\S\xa0]/'); 1986 } 1987 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 1988 // set document creation and modification timestamp 1989 $this->doc_creation_timestamp = time(); 1990 $this->doc_modification_timestamp = $this->doc_creation_timestamp; 1991 // get default graphic vars 1992 $this->default_graphic_vars = $this->getGraphicVars(); 1993 $this->header_xobj_autoreset = false; 1994 $this->custom_xmp = ''; 1995 } 1996 1997 /** 1998 * Default destructor. 1999 * @public 2000 * @since 1.53.0.TC016 2001 */ 2002 public function __destruct() { 2003 // restore internal encoding 2004 if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { 2005 mb_internal_encoding($this->internal_encoding); 2006 } 2007 // unset all class variables 2008 $this->_destroy(true); 2009 } 2010 2011 /** 2012 * Set the units of measure for the document. 2013 * @param $unit (string) 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. 2014 * @public 2015 * @since 3.0.015 (2008-06-06) 2016 */ 2017 public function setPageUnit($unit) { 2018 $unit = strtolower($unit); 2019 //Set scale factor 2020 switch ($unit) { 2021 // points 2022 case 'px': 2023 case 'pt': { 2024 $this->k = 1; 2025 break; 2026 } 2027 // millimeters 2028 case 'mm': { 2029 $this->k = $this->dpi / 25.4; 2030 break; 2031 } 2032 // centimeters 2033 case 'cm': { 2034 $this->k = $this->dpi / 2.54; 2035 break; 2036 } 2037 // inches 2038 case 'in': { 2039 $this->k = $this->dpi; 2040 break; 2041 } 2042 // unsupported unit 2043 default : { 2044 $this->Error('Incorrect unit: '.$unit); 2045 break; 2046 } 2047 } 2048 $this->pdfunit = $unit; 2049 if (isset($this->CurOrientation)) { 2050 $this->setPageOrientation($this->CurOrientation); 2051 } 2052 } 2053 2054 /** 2055 * Change the format of the current page 2056 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() documentation or an array of two numbers (width, height) or an array containing the following measures and options:<ul> 2057 * <li>['format'] = page format name (one of the above);</li> 2058 * <li>['Rotate'] : The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li> 2059 * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li> 2060 * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li> 2061 * <li>['MediaBox']['llx'] : lower-left x coordinate</li> 2062 * <li>['MediaBox']['lly'] : lower-left y coordinate</li> 2063 * <li>['MediaBox']['urx'] : upper-right x coordinate</li> 2064 * <li>['MediaBox']['ury'] : upper-right y coordinate</li> 2065 * <li>['CropBox'] : the visible region of default user space:</li> 2066 * <li>['CropBox']['llx'] : lower-left x coordinate</li> 2067 * <li>['CropBox']['lly'] : lower-left y coordinate</li> 2068 * <li>['CropBox']['urx'] : upper-right x coordinate</li> 2069 * <li>['CropBox']['ury'] : upper-right y coordinate</li> 2070 * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li> 2071 * <li>['BleedBox']['llx'] : lower-left x coordinate</li> 2072 * <li>['BleedBox']['lly'] : lower-left y coordinate</li> 2073 * <li>['BleedBox']['urx'] : upper-right x coordinate</li> 2074 * <li>['BleedBox']['ury'] : upper-right y coordinate</li> 2075 * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li> 2076 * <li>['TrimBox']['llx'] : lower-left x coordinate</li> 2077 * <li>['TrimBox']['lly'] : lower-left y coordinate</li> 2078 * <li>['TrimBox']['urx'] : upper-right x coordinate</li> 2079 * <li>['TrimBox']['ury'] : upper-right y coordinate</li> 2080 * <li>['ArtBox'] : the extent of the page's meaningful content:</li> 2081 * <li>['ArtBox']['llx'] : lower-left x coordinate</li> 2082 * <li>['ArtBox']['lly'] : lower-left y coordinate</li> 2083 * <li>['ArtBox']['urx'] : upper-right x coordinate</li> 2084 * <li>['ArtBox']['ury'] : upper-right y coordinate</li> 2085 * <li>['BoxColorInfo'] :specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for each of the possible page boundaries other than the MediaBox:</li> 2086 * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li> 2087 * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li> 2088 * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li> 2089 * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li> 2090 * <li>['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation</li> 2091 * <li>['trans']['Dur'] : The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li> 2092 * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li> 2093 * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li> 2094 * <li>['trans']['Dm'] : (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li> 2095 * <li>['trans']['M'] : (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li> 2096 * <li>['trans']['Di'] : (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li> 2097 * <li>['trans']['SS'] : (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0.</li> 2098 * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li> 2099 * </ul> 2100 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul> 2101 * <li>P or Portrait (default)</li> 2102 * <li>L or Landscape</li> 2103 * <li>'' (empty string) for automatic orientation</li> 2104 * </ul> 2105 * @protected 2106 * @since 3.0.015 (2008-06-06) 2107 * @see getPageSizeFromFormat() 2108 */ 2109 protected function setPageFormat($format, $orientation='P') { 2110 if (!empty($format) AND isset($this->pagedim[$this->page])) { 2111 // remove inherited values 2112 unset($this->pagedim[$this->page]); 2113 } 2114 if (is_string($format)) { 2115 // get page measures from format name 2116 $pf = TCPDF_STATIC::getPageSizeFromFormat($format); 2117 $this->fwPt = $pf[0]; 2118 $this->fhPt = $pf[1]; 2119 } else { 2120 // the boundaries of the physical medium on which the page shall be displayed or printed 2121 if (isset($format['MediaBox'])) { 2122 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false, $this->k, $this->pagedim); 2123 $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k); 2124 $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k); 2125 } else { 2126 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) { 2127 $pf = array(($format[0] * $this->k), ($format[1] * $this->k)); 2128 } else { 2129 if (!isset($format['format'])) { 2130 // default value 2131 $format['format'] = 'A4'; 2132 } 2133 $pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']); 2134 } 2135 $this->fwPt = $pf[0]; 2136 $this->fhPt = $pf[1]; 2137 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim); 2138 } 2139 // the visible region of default user space 2140 if (isset($format['CropBox'])) { 2141 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false, $this->k, $this->pagedim); 2142 } 2143 // the region to which the contents of the page shall be clipped when output in a production environment 2144 if (isset($format['BleedBox'])) { 2145 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false, $this->k, $this->pagedim); 2146 } 2147 // the intended dimensions of the finished page after trimming 2148 if (isset($format['TrimBox'])) { 2149 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false, $this->k, $this->pagedim); 2150 } 2151 // the page's meaningful content (including potential white space) 2152 if (isset($format['ArtBox'])) { 2153 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false, $this->k, $this->pagedim); 2154 } 2155 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries 2156 if (isset($format['BoxColorInfo'])) { 2157 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo']; 2158 } 2159 if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) { 2160 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. 2161 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']); 2162 } 2163 if (isset($format['PZ'])) { 2164 // The page's preferred zoom (magnification) factor 2165 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']); 2166 } 2167 if (isset($format['trans'])) { 2168 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation 2169 if (isset($format['trans']['Dur'])) { 2170 // The page's display duration 2171 $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']); 2172 } 2173 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade'); 2174 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) { 2175 // The transition style that shall be used when moving to this page from another during a presentation 2176 $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S']; 2177 $valid_effect = array('Split', 'Blinds'); 2178 $valid_vals = array('H', 'V'); 2179 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) { 2180 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm']; 2181 } 2182 $valid_effect = array('Split', 'Box', 'Fly'); 2183 $valid_vals = array('I', 'O'); 2184 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) { 2185 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M']; 2186 } 2187 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push'); 2188 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) { 2189 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe')) 2190 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter')) 2191 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) { 2192 $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']); 2193 } 2194 } 2195 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) { 2196 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']); 2197 } 2198 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) { 2199 $this->pagedim[$this->page]['trans']['B'] = 'true'; 2200 } 2201 } else { 2202 $this->pagedim[$this->page]['trans']['S'] = 'R'; 2203 } 2204 if (isset($format['trans']['D'])) { 2205 // The duration of the transition effect, in seconds 2206 $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']); 2207 } else { 2208 $this->pagedim[$this->page]['trans']['D'] = 1; 2209 } 2210 } 2211 } 2212 $this->setPageOrientation($orientation); 2213 } 2214 2215 /** 2216 * Set page orientation. 2217 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul> 2218 * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off. 2219 * @param $bottommargin (float) bottom margin of the page. 2220 * @public 2221 * @since 3.0.015 (2008-06-06) 2222 */ 2223 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') { 2224 if (!isset($this->pagedim[$this->page]['MediaBox'])) { 2225 // the boundaries of the physical medium on which the page shall be displayed or printed 2226 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim); 2227 } 2228 if (!isset($this->pagedim[$this->page]['CropBox'])) { 2229 // the visible region of default user space 2230 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true, $this->k, $this->pagedim); 2231 } 2232 if (!isset($this->pagedim[$this->page]['BleedBox'])) { 2233 // the region to which the contents of the page shall be clipped when output in a production environment 2234 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim); 2235 } 2236 if (!isset($this->pagedim[$this->page]['TrimBox'])) { 2237 // the intended dimensions of the finished page after trimming 2238 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim); 2239 } 2240 if (!isset($this->pagedim[$this->page]['ArtBox'])) { 2241 // the page's meaningful content (including potential white space) 2242 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim); 2243 } 2244 if (!isset($this->pagedim[$this->page]['Rotate'])) { 2245 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. 2246 $this->pagedim[$this->page]['Rotate'] = 0; 2247 } 2248 if (!isset($this->pagedim[$this->page]['PZ'])) { 2249 // The page's preferred zoom (magnification) factor 2250 $this->pagedim[$this->page]['PZ'] = 1; 2251 } 2252 if ($this->fwPt > $this->fhPt) { 2253 // landscape 2254 $default_orientation = 'L'; 2255 } else { 2256 // portrait 2257 $default_orientation = 'P'; 2258 } 2259 $valid_orientations = array('P', 'L'); 2260 if (empty($orientation)) { 2261 $orientation = $default_orientation; 2262 } else { 2263 $orientation = strtoupper($orientation[0]); 2264 } 2265 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) { 2266 $this->CurOrientation = $orientation; 2267 $this->wPt = $this->fhPt; 2268 $this->hPt = $this->fwPt; 2269 } else { 2270 $this->CurOrientation = $default_orientation; 2271 $this->wPt = $this->fwPt; 2272 $this->hPt = $this->fhPt; 2273 } 2274 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){ 2275 // swap X and Y coordinates (change page orientation) 2276 $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim); 2277 } 2278 $this->w = ($this->wPt / $this->k); 2279 $this->h = ($this->hPt / $this->k); 2280 if (TCPDF_STATIC::empty_string($autopagebreak)) { 2281 if (isset($this->AutoPageBreak)) { 2282 $autopagebreak = $this->AutoPageBreak; 2283 } else { 2284 $autopagebreak = true; 2285 } 2286 } 2287 if (TCPDF_STATIC::empty_string($bottommargin)) { 2288 if (isset($this->bMargin)) { 2289 $bottommargin = $this->bMargin; 2290 } else { 2291 // default value = 2 cm 2292 $bottommargin = 2 * 28.35 / $this->k; 2293 } 2294 } 2295 $this->SetAutoPageBreak($autopagebreak, $bottommargin); 2296 // store page dimensions 2297 $this->pagedim[$this->page]['w'] = $this->wPt; 2298 $this->pagedim[$this->page]['h'] = $this->hPt; 2299 $this->pagedim[$this->page]['wk'] = $this->w; 2300 $this->pagedim[$this->page]['hk'] = $this->h; 2301 $this->pagedim[$this->page]['tm'] = $this->tMargin; 2302 $this->pagedim[$this->page]['bm'] = $bottommargin; 2303 $this->pagedim[$this->page]['lm'] = $this->lMargin; 2304 $this->pagedim[$this->page]['rm'] = $this->rMargin; 2305 $this->pagedim[$this->page]['pb'] = $autopagebreak; 2306 $this->pagedim[$this->page]['or'] = $this->CurOrientation; 2307 $this->pagedim[$this->page]['olm'] = $this->original_lMargin; 2308 $this->pagedim[$this->page]['orm'] = $this->original_rMargin; 2309 } 2310 2311 /** 2312 * Set regular expression to detect withespaces or word separators. 2313 * The pattern delimiter must be the forward-slash character "/". 2314 * Some example patterns are: 2315 * <pre> 2316 * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/" 2317 * Unicode and PCRE unicode support: "/(?!\xa0)[\s\p{Z}]/u" 2318 * Unicode and PCRE unicode support in Chinese mode: "/(?!\xa0)[\s\p{Z}\p{Lo}]/u" 2319 * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"): 2320 * \s : any whitespace character 2321 * \p{Z} : any separator 2322 * \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words. 2323 * \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0) 2324 * </pre> 2325 * @param $re (string) regular expression (leave empty for default). 2326 * @public 2327 * @since 4.6.016 (2009-06-15) 2328 */ 2329 public function setSpacesRE($re='/[^\S\xa0]/') { 2330 $this->re_spaces = $re; 2331 $re_parts = explode('/', $re); 2332 // get pattern parts 2333 $this->re_space = array(); 2334 if (isset($re_parts[1]) AND !empty($re_parts[1])) { 2335 $this->re_space['p'] = $re_parts[1]; 2336 } else { 2337 $this->re_space['p'] = '[\s]'; 2338 } 2339 // set pattern modifiers 2340 if (isset($re_parts[2]) AND !empty($re_parts[2])) { 2341 $this->re_space['m'] = $re_parts[2]; 2342 } else { 2343 $this->re_space['m'] = ''; 2344 } 2345 } 2346 2347 /** 2348 * Enable or disable Right-To-Left language mode 2349 * @param $enable (Boolean) if true enable Right-To-Left language mode. 2350 * @param $resetx (Boolean) if true reset the X position on direction change. 2351 * @public 2352 * @since 2.0.000 (2008-01-03) 2353 */ 2354 public function setRTL($enable, $resetx=true) { 2355 $enable = $enable ? true : false; 2356 $resetx = ($resetx AND ($enable != $this->rtl)); 2357 $this->rtl = $enable; 2358 $this->tmprtl = false; 2359 if ($resetx) { 2360 $this->Ln(0); 2361 } 2362 } 2363 2364 /** 2365 * Return the RTL status 2366 * @return boolean 2367 * @public 2368 * @since 4.0.012 (2008-07-24) 2369 */ 2370 public function getRTL() { 2371 return $this->rtl; 2372 } 2373 2374 /** 2375 * Force temporary RTL language direction 2376 * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL 2377 * @public 2378 * @since 2.1.000 (2008-01-09) 2379 */ 2380 public function setTempRTL($mode) { 2381 $newmode = false; 2382 switch (strtoupper($mode)) { 2383 case 'LTR': 2384 case 'L': { 2385 if ($this->rtl) { 2386 $newmode = 'L'; 2387 } 2388 break; 2389 } 2390 case 'RTL': 2391 case 'R': { 2392 if (!$this->rtl) { 2393 $newmode = 'R'; 2394 } 2395 break; 2396 } 2397 case false: 2398 default: { 2399 $newmode = false; 2400 break; 2401 } 2402 } 2403 $this->tmprtl = $newmode; 2404 } 2405 2406 /** 2407 * Return the current temporary RTL status 2408 * @return boolean 2409 * @public 2410 * @since 4.8.014 (2009-11-04) 2411 */ 2412 public function isRTLTextDir() { 2413 return ($this->rtl OR ($this->tmprtl == 'R')); 2414 } 2415 2416 /** 2417 * Set the last cell height. 2418 * @param $h (float) cell height. 2419 * @author Nicola Asuni 2420 * @public 2421 * @since 1.53.0.TC034 2422 */ 2423 public function setLastH($h) { 2424 $this->lasth = $h; 2425 } 2426 2427 /** 2428 * Return the cell height 2429 * @param $fontsize (int) Font size in internal units 2430 * @param $padding (boolean) If true add cell padding 2431 * @public 2432 */ 2433 public function getCellHeight($fontsize, $padding=TRUE) { 2434 $height = ($fontsize * $this->cell_height_ratio); 2435 if ($padding) { 2436 $height += ($this->cell_padding['T'] + $this->cell_padding['B']); 2437 } 2438 return round($height, 6); 2439 } 2440 2441 /** 2442 * Reset the last cell height. 2443 * @public 2444 * @since 5.9.000 (2010-10-03) 2445 */ 2446 public function resetLastH() { 2447 $this->lasth = $this->getCellHeight($this->FontSize); 2448 } 2449 2450 /** 2451 * Get the last cell height. 2452 * @return last cell height 2453 * @public 2454 * @since 4.0.017 (2008-08-05) 2455 */ 2456 public function getLastH() { 2457 return $this->lasth; 2458 } 2459 2460 /** 2461 * Set the adjusting factor to convert pixels to user units. 2462 * @param $scale (float) adjusting factor to convert pixels to user units. 2463 * @author Nicola Asuni 2464 * @public 2465 * @since 1.5.2 2466 */ 2467 public function setImageScale($scale) { 2468 $this->imgscale = $scale; 2469 } 2470 2471 /** 2472 * Returns the adjusting factor to convert pixels to user units. 2473 * @return float adjusting factor to convert pixels to user units. 2474 * @author Nicola Asuni 2475 * @public 2476 * @since 1.5.2 2477 */ 2478 public function getImageScale() { 2479 return $this->imgscale; 2480 } 2481 2482 /** 2483 * Returns an array of page dimensions: 2484 * <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 user units</li><li>$this->pagedim[$this->page]['hk'] = page height in user units</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><li>$this->pagedim[$this->page]['Rotate'] = The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li><li>$this->pagedim[$this->page]['PZ'] = The page's preferred zoom (magnification) factor.</li><li>$this->pagedim[$this->page]['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation<ul><li>$this->pagedim[$this->page]['trans']['Dur'] = The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li><li>$this->pagedim[$this->page]['trans']['S'] = transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li><li>$this->pagedim[$this->page]['trans']['D'] = The duration of the transition effect, in seconds.</li><li>$this->pagedim[$this->page]['trans']['Dm'] = (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li><li>$this->pagedim[$this->page]['trans']['M'] = (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li><li>$this->pagedim[$this->page]['trans']['Di'] = (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li><li>$this->pagedim[$this->page]['trans']['SS'] = (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0. </li><li>$this->pagedim[$this->page]['trans']['B'] = (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li></ul></li><li>$this->pagedim[$this->page]['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed<ul><li>$this->pagedim[$this->page]['MediaBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['CropBox'] : the visible region of default user space<ul><li>$this->pagedim[$this->page]['CropBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment<ul><li>$this->pagedim[$this->page]['BleedBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['TrimBox'] : the intended dimensions of the finished page after trimming<ul><li>$this->pagedim[$this->page]['TrimBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['ArtBox'] : the extent of the page's meaningful content<ul><li>$this->pagedim[$this->page]['ArtBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['ury'] = upper-right y coordinate in points</li></ul></li></ul> 2485 * @param $pagenum (int) page number (empty = current page) 2486 * @return array of page dimensions. 2487 * @author Nicola Asuni 2488 * @public 2489 * @since 4.5.027 (2009-03-16) 2490 */ 2491 public function getPageDimensions($pagenum='') { 2492 if (empty($pagenum)) { 2493 $pagenum = $this->page; 2494 } 2495 return $this->pagedim[$pagenum]; 2496 } 2497 2498 /** 2499 * Returns the page width in units. 2500 * @param $pagenum (int) page number (empty = current page) 2501 * @return int page width. 2502 * @author Nicola Asuni 2503 * @public 2504 * @since 1.5.2 2505 * @see getPageDimensions() 2506 */ 2507 public function getPageWidth($pagenum='') { 2508 if (empty($pagenum)) { 2509 return $this->w; 2510 } 2511 return $this->pagedim[$pagenum]['w']; 2512 } 2513 2514 /** 2515 * Returns the page height in units. 2516 * @param $pagenum (int) page number (empty = current page) 2517 * @return int page height. 2518 * @author Nicola Asuni 2519 * @public 2520 * @since 1.5.2 2521 * @see getPageDimensions() 2522 */ 2523 public function getPageHeight($pagenum='') { 2524 if (empty($pagenum)) { 2525 return $this->h; 2526 } 2527 return $this->pagedim[$pagenum]['h']; 2528 } 2529 2530 /** 2531 * Returns the page break margin. 2532 * @param $pagenum (int) page number (empty = current page) 2533 * @return int page break margin. 2534 * @author Nicola Asuni 2535 * @public 2536 * @since 1.5.2 2537 * @see getPageDimensions() 2538 */ 2539 public function getBreakMargin($pagenum='') { 2540 if (empty($pagenum)) { 2541 return $this->bMargin; 2542 } 2543 return $this->pagedim[$pagenum]['bm']; 2544 } 2545 2546 /** 2547 * Returns the scale factor (number of points in user unit). 2548 * @return int scale factor. 2549 * @author Nicola Asuni 2550 * @public 2551 * @since 1.5.2 2552 */ 2553 public function getScaleFactor() { 2554 return $this->k; 2555 } 2556 2557 /** 2558 * Defines the left, top and right margins. 2559 * @param $left (float) Left margin. 2560 * @param $top (float) Top margin. 2561 * @param $right (float) Right margin. Default value is the left one. 2562 * @param $keepmargins (boolean) if true overwrites the default page margins 2563 * @public 2564 * @since 1.0 2565 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() 2566 */ 2567 public function SetMargins($left, $top, $right=-1, $keepmargins=false) { 2568 //Set left, top and right margins 2569 $this->lMargin = $left; 2570 $this->tMargin = $top; 2571 if ($right == -1) { 2572 $right = $left; 2573 } 2574 $this->rMargin = $right; 2575 if ($keepmargins) { 2576 // overwrite original values 2577 $this->original_lMargin = $this->lMargin; 2578 $this->original_rMargin = $this->rMargin; 2579 } 2580 } 2581 2582 /** 2583 * 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. 2584 * @param $margin (float) The margin. 2585 * @public 2586 * @since 1.4 2587 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 2588 */ 2589 public function SetLeftMargin($margin) { 2590 //Set left margin 2591 $this->lMargin = $margin; 2592 if (($this->page > 0) AND ($this->x < $margin)) { 2593 $this->x = $margin; 2594 } 2595 } 2596 2597 /** 2598 * Defines the top margin. The method can be called before creating the first page. 2599 * @param $margin (float) The margin. 2600 * @public 2601 * @since 1.5 2602 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 2603 */ 2604 public function SetTopMargin($margin) { 2605 //Set top margin 2606 $this->tMargin = $margin; 2607 if (($this->page > 0) AND ($this->y < $margin)) { 2608 $this->y = $margin; 2609 } 2610 } 2611 2612 /** 2613 * Defines the right margin. The method can be called before creating the first page. 2614 * @param $margin (float) The margin. 2615 * @public 2616 * @since 1.5 2617 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() 2618 */ 2619 public function SetRightMargin($margin) { 2620 $this->rMargin = $margin; 2621 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) { 2622 $this->x = $this->w - $margin; 2623 } 2624 } 2625 2626 /** 2627 * Set the same internal Cell padding for top, right, bottom, left- 2628 * @param $pad (float) internal padding. 2629 * @public 2630 * @since 2.1.000 (2008-01-09) 2631 * @see getCellPaddings(), setCellPaddings() 2632 */ 2633 public function SetCellPadding($pad) { 2634 if ($pad >= 0) { 2635 $this->cell_padding['L'] = $pad; 2636 $this->cell_padding['T'] = $pad; 2637 $this->cell_padding['R'] = $pad; 2638 $this->cell_padding['B'] = $pad; 2639 } 2640 } 2641 2642 /** 2643 * Set the internal Cell paddings. 2644 * @param $left (float) left padding 2645 * @param $top (float) top padding 2646 * @param $right (float) right padding 2647 * @param $bottom (float) bottom padding 2648 * @public 2649 * @since 5.9.000 (2010-10-03) 2650 * @see getCellPaddings(), SetCellPadding() 2651 */ 2652 public function setCellPaddings($left='', $top='', $right='', $bottom='') { 2653 if (($left !== '') AND ($left >= 0)) { 2654 $this->cell_padding['L'] = $left; 2655 } 2656 if (($top !== '') AND ($top >= 0)) { 2657 $this->cell_padding['T'] = $top; 2658 } 2659 if (($right !== '') AND ($right >= 0)) { 2660 $this->cell_padding['R'] = $right; 2661 } 2662 if (($bottom !== '') AND ($bottom >= 0)) { 2663 $this->cell_padding['B'] = $bottom; 2664 } 2665 } 2666 2667 /** 2668 * Get the internal Cell padding array. 2669 * @return array of padding values 2670 * @public 2671 * @since 5.9.000 (2010-10-03) 2672 * @see setCellPaddings(), SetCellPadding() 2673 */ 2674 public function getCellPaddings() { 2675 return $this->cell_padding; 2676 } 2677 2678 /** 2679 * Set the internal Cell margins. 2680 * @param $left (float) left margin 2681 * @param $top (float) top margin 2682 * @param $right (float) right margin 2683 * @param $bottom (float) bottom margin 2684 * @public 2685 * @since 5.9.000 (2010-10-03) 2686 * @see getCellMargins() 2687 */ 2688 public function setCellMargins($left='', $top='', $right='', $bottom='') { 2689 if (($left !== '') AND ($left >= 0)) { 2690 $this->cell_margin['L'] = $left; 2691 } 2692 if (($top !== '') AND ($top >= 0)) { 2693 $this->cell_margin['T'] = $top; 2694 } 2695 if (($right !== '') AND ($right >= 0)) { 2696 $this->cell_margin['R'] = $right; 2697 } 2698 if (($bottom !== '') AND ($bottom >= 0)) { 2699 $this->cell_margin['B'] = $bottom; 2700 } 2701 } 2702 2703 /** 2704 * Get the internal Cell margin array. 2705 * @return array of margin values 2706 * @public 2707 * @since 5.9.000 (2010-10-03) 2708 * @see setCellMargins() 2709 */ 2710 public function getCellMargins() { 2711 return $this->cell_margin; 2712 } 2713 2714 /** 2715 * Adjust the internal Cell padding array to take account of the line width. 2716 * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 2717 * @return array of adjustments 2718 * @public 2719 * @since 5.9.000 (2010-10-03) 2720 */ 2721 protected function adjustCellPadding($brd=0) { 2722 if (empty($brd)) { 2723 return; 2724 } 2725 if (is_string($brd)) { 2726 // convert string to array 2727 $slen = strlen($brd); 2728 $newbrd = array(); 2729 for ($i = 0; $i < $slen; ++$i) { 2730 $newbrd[$brd[$i]] = true; 2731 } 2732 $brd = $newbrd; 2733 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) { 2734 $brd = array('LRTB' => true); 2735 } 2736 if (!is_array($brd)) { 2737 return; 2738 } 2739 // store current cell padding 2740 $cp = $this->cell_padding; 2741 // select border mode 2742 if (isset($brd['mode'])) { 2743 $mode = $brd['mode']; 2744 unset($brd['mode']); 2745 } else { 2746 $mode = 'normal'; 2747 } 2748 // process borders 2749 foreach ($brd as $border => $style) { 2750 $line_width = $this->LineWidth; 2751 if (is_array($style) AND isset($style['width'])) { 2752 // get border width 2753 $line_width = $style['width']; 2754 } 2755 $adj = 0; // line width inside the cell 2756 switch ($mode) { 2757 case 'ext': { 2758 $adj = 0; 2759 break; 2760 } 2761 case 'int': { 2762 $adj = $line_width; 2763 break; 2764 } 2765 case 'normal': 2766 default: { 2767 $adj = ($line_width / 2); 2768 break; 2769 } 2770 } 2771 // correct internal cell padding if required to avoid overlap between text and lines 2772 if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) { 2773 $this->cell_padding['T'] = $adj; 2774 } 2775 if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) { 2776 $this->cell_padding['R'] = $adj; 2777 } 2778 if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) { 2779 $this->cell_padding['B'] = $adj; 2780 } 2781 if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) { 2782 $this->cell_padding['L'] = $adj; 2783 } 2784 } 2785 return array('T' => ($this->cell_padding['T'] - $cp['T']), 'R' => ($this->cell_padding['R'] - $cp['R']), 'B' => ($this->cell_padding['B'] - $cp['B']), 'L' => ($this->cell_padding['L'] - $cp['L'])); 2786 } 2787 2788 /** 2789 * 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. 2790 * @param $auto (boolean) Boolean indicating if mode should be on or off. 2791 * @param $margin (float) Distance from the bottom of the page. 2792 * @public 2793 * @since 1.0 2794 * @see Cell(), MultiCell(), AcceptPageBreak() 2795 */ 2796 public function SetAutoPageBreak($auto, $margin=0) { 2797 $this->AutoPageBreak = $auto ? true : false; 2798 $this->bMargin = $margin; 2799 $this->PageBreakTrigger = $this->h - $margin; 2800 } 2801 2802 /** 2803 * Return the auto-page-break mode (true or false). 2804 * @return boolean auto-page-break mode 2805 * @public 2806 * @since 5.9.088 2807 */ 2808 public function getAutoPageBreak() { 2809 return $this->AutoPageBreak; 2810 } 2811 2812 /** 2813 * Defines the way the document is to be displayed by the viewer. 2814 * @param $zoom (mixed) 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> 2815 * @param $layout (string) 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> 2816 * @param $mode (string) 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> 2817 * @public 2818 * @since 1.2 2819 */ 2820 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') { 2821 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) { 2822 $this->ZoomMode = $zoom; 2823 } else { 2824 $this->Error('Incorrect zoom display mode: '.$zoom); 2825 } 2826 $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout); 2827 $this->PageMode = TCPDF_STATIC::getPageMode($mode); 2828 } 2829 2830 /** 2831 * 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. 2832 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. 2833 * @param $compress (boolean) Boolean indicating if compression must be enabled. 2834 * @public 2835 * @since 1.4 2836 */ 2837 public function SetCompression($compress=true) { 2838 if (function_exists('gzcompress')) { 2839 $this->compress = $compress ? true : false; 2840 } else { 2841 $this->compress = false; 2842 } 2843 } 2844 2845 /** 2846 * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document. 2847 * @param $mode (boolean) If true force sRGB output intent. 2848 * @public 2849 * @since 5.9.121 (2011-09-28) 2850 */ 2851 public function setSRGBmode($mode=false) { 2852 $this->force_srgb = $mode ? true : false; 2853 } 2854 2855 /** 2856 * Turn on/off Unicode mode for document information dictionary (meta tags). 2857 * This has effect only when unicode mode is set to false. 2858 * @param $unicode (boolean) if true set the meta information in Unicode 2859 * @since 5.9.027 (2010-12-01) 2860 * @public 2861 */ 2862 public function SetDocInfoUnicode($unicode=true) { 2863 $this->docinfounicode = $unicode ? true : false; 2864 } 2865 2866 /** 2867 * Defines the title of the document. 2868 * @param $title (string) The title. 2869 * @public 2870 * @since 1.2 2871 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() 2872 */ 2873 public function SetTitle($title) { 2874 $this->title = $title; 2875 } 2876 2877 /** 2878 * Defines the subject of the document. 2879 * @param $subject (string) The subject. 2880 * @public 2881 * @since 1.2 2882 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() 2883 */ 2884 public function SetSubject($subject) { 2885 $this->subject = $subject; 2886 } 2887 2888 /** 2889 * Defines the author of the document. 2890 * @param $author (string) The name of the author. 2891 * @public 2892 * @since 1.2 2893 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() 2894 */ 2895 public function SetAuthor($author) { 2896 $this->author = $author; 2897 } 2898 2899 /** 2900 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. 2901 * @param $keywords (string) The list of keywords. 2902 * @public 2903 * @since 1.2 2904 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() 2905 */ 2906 public function SetKeywords($keywords) { 2907 $this->keywords = $keywords; 2908 } 2909 2910 /** 2911 * Defines the creator of the document. This is typically the name of the application that generates the PDF. 2912 * @param $creator (string) The name of the creator. 2913 * @public 2914 * @since 1.2 2915 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() 2916 */ 2917 public function SetCreator($creator) { 2918 $this->creator = $creator; 2919 } 2920 2921 /** 2922 * Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR constant is set to true. 2923 * @param $msg (string) The error message 2924 * @public 2925 * @since 1.0 2926 */ 2927 public function Error($msg) { 2928 // unset all class variables 2929 $this->_destroy(true); 2930 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) { 2931 die('<strong>TCPDF ERROR: </strong>'.$msg); 2932 } else { 2933 throw new Exception('TCPDF ERROR: '.$msg); 2934 } 2935 } 2936 2937 /** 2938 * This method begins the generation of the PDF document. 2939 * It is not necessary to call it explicitly because AddPage() does it automatically. 2940 * Note: no page is created by this method 2941 * @public 2942 * @since 1.0 2943 * @see AddPage(), Close() 2944 */ 2945 public function Open() { 2946 $this->state = 1; 2947 } 2948 2949 /** 2950 * Terminates the PDF document. 2951 * It is not necessary to call this method explicitly because Output() does it automatically. 2952 * If the document contains no page, AddPage() is called to prevent from getting an invalid document. 2953 * @public 2954 * @since 1.0 2955 * @see Open(), Output() 2956 */ 2957 public function Close() { 2958 if ($this->state == 3) { 2959 return; 2960 } 2961 if ($this->page == 0) { 2962 $this->AddPage(); 2963 } 2964 $this->endLayer(); 2965 if ($this->tcpdflink) { 2966 // save current graphic settings 2967 $gvars = $this->getGraphicVars(); 2968 $this->setEqualColumns(); 2969 $this->lastpage(true); 2970 $this->SetAutoPageBreak(false); 2971 $this->x = 0; 2972 $this->y = $this->h - (1 / $this->k); 2973 $this->lMargin = 0; 2974 $this->_outSaveGraphicsState(); 2975 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica'; 2976 $this->SetFont($font, '', 1); 2977 $this->setTextRenderingMode(0, false, false); 2978 $msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29"; 2979 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67"; 2980 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B'); 2981 $this->_outRestoreGraphicsState(); 2982 // restore graphic settings 2983 $this->setGraphicVars($gvars); 2984 } 2985 // close page 2986 $this->endPage(); 2987 // close document 2988 $this->_enddoc(); 2989 // unset all class variables (except critical ones) 2990 $this->_destroy(false); 2991 } 2992 2993 /** 2994 * Move pointer at the specified document page and update page dimensions. 2995 * @param $pnum (int) page number (1 ... numpages) 2996 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position. 2997 * @public 2998 * @since 2.1.000 (2008-01-07) 2999 * @see getPage(), lastpage(), getNumPages() 3000 */ 3001 public function setPage($pnum, $resetmargins=false) { 3002 if (($pnum == $this->page) AND ($this->state == 2)) { 3003 return; 3004 } 3005 if (($pnum > 0) AND ($pnum <= $this->numpages)) { 3006 $this->state = 2; 3007 // save current graphic settings 3008 //$gvars = $this->getGraphicVars(); 3009 $oldpage = $this->page; 3010 $this->page = $pnum; 3011 $this->wPt = $this->pagedim[$this->page]['w']; 3012 $this->hPt = $this->pagedim[$this->page]['h']; 3013 $this->w = $this->pagedim[$this->page]['wk']; 3014 $this->h = $this->pagedim[$this->page]['hk']; 3015 $this->tMargin = $this->pagedim[$this->page]['tm']; 3016 $this->bMargin = $this->pagedim[$this->page]['bm']; 3017 $this->original_lMargin = $this->pagedim[$this->page]['olm']; 3018 $this->original_rMargin = $this->pagedim[$this->page]['orm']; 3019 $this->AutoPageBreak = $this->pagedim[$this->page]['pb']; 3020 $this->CurOrientation = $this->pagedim[$this->page]['or']; 3021 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin); 3022 // restore graphic settings 3023 //$this->setGraphicVars($gvars); 3024 if ($resetmargins) { 3025 $this->lMargin = $this->pagedim[$this->page]['olm']; 3026 $this->rMargin = $this->pagedim[$this->page]['orm']; 3027 $this->SetY($this->tMargin); 3028 } else { 3029 // account for booklet mode 3030 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 3031 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm']; 3032 $this->lMargin += $deltam; 3033 $this->rMargin -= $deltam; 3034 } 3035 } 3036 } else { 3037 $this->Error('Wrong page number on setPage() function: '.$pnum); 3038 } 3039 } 3040 3041 /** 3042 * Reset pointer to the last document page. 3043 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position. 3044 * @public 3045 * @since 2.0.000 (2008-01-04) 3046 * @see setPage(), getPage(), getNumPages() 3047 */ 3048 public function lastPage($resetmargins=false) { 3049 $this->setPage($this->getNumPages(), $resetmargins); 3050 } 3051 3052 /** 3053 * Get current document page number. 3054 * @return int page number 3055 * @public 3056 * @since 2.1.000 (2008-01-07) 3057 * @see setPage(), lastpage(), getNumPages() 3058 */ 3059 public function getPage() { 3060 return $this->page; 3061 } 3062 3063 /** 3064 * Get the total number of insered pages. 3065 * @return int number of pages 3066 * @public 3067 * @since 2.1.000 (2008-01-07) 3068 * @see setPage(), getPage(), lastpage() 3069 */ 3070 public function getNumPages() { 3071 return $this->numpages; 3072 } 3073 3074 /** 3075 * Adds a new TOC (Table Of Content) page to the document. 3076 * @param $orientation (string) page orientation. 3077 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). 3078 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins 3079 * @public 3080 * @since 5.0.001 (2010-05-06) 3081 * @see AddPage(), startPage(), endPage(), endTOCPage() 3082 */ 3083 public function addTOCPage($orientation='', $format='', $keepmargins=false) { 3084 $this->AddPage($orientation, $format, $keepmargins, true); 3085 } 3086 3087 /** 3088 * Terminate the current TOC (Table Of Content) page 3089 * @public 3090 * @since 5.0.001 (2010-05-06) 3091 * @see AddPage(), startPage(), endPage(), addTOCPage() 3092 */ 3093 public function endTOCPage() { 3094 $this->endPage(true); 3095 } 3096 3097 /** 3098 * 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). 3099 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards. 3100 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 3101 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). 3102 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins 3103 * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content). 3104 * @public 3105 * @since 1.0 3106 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat() 3107 */ 3108 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) { 3109 if ($this->inxobj) { 3110 // we are inside an XObject template 3111 return; 3112 } 3113 if (!isset($this->original_lMargin) OR $keepmargins) { 3114 $this->original_lMargin = $this->lMargin; 3115 } 3116 if (!isset($this->original_rMargin) OR $keepmargins) { 3117 $this->original_rMargin = $this->rMargin; 3118 } 3119 // terminate previous page 3120 $this->endPage(); 3121 // start new page 3122 $this->startPage($orientation, $format, $tocpage); 3123 } 3124 3125 /** 3126 * Terminate the current page 3127 * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content). 3128 * @public 3129 * @since 4.2.010 (2008-11-14) 3130 * @see AddPage(), startPage(), addTOCPage(), endTOCPage() 3131 */ 3132 public function endPage($tocpage=false) { 3133 // check if page is already closed 3134 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) { 3135 return; 3136 } 3137 // print page footer 3138 $this->setFooter(); 3139 // close page 3140 $this->_endpage(); 3141 // mark page as closed 3142 $this->pageopen[$this->page] = false; 3143 if ($tocpage) { 3144 $this->tocpage = false; 3145 } 3146 } 3147 3148 /** 3149 * Starts a new page to the document. The page must be closed using the endPage() function. 3150 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. 3151 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 3152 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). 3153 * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content. 3154 * @since 4.2.010 (2008-11-14) 3155 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat() 3156 * @public 3157 */ 3158 public function startPage($orientation='', $format='', $tocpage=false) { 3159 if ($tocpage) { 3160 $this->tocpage = true; 3161 } 3162 // move page numbers of documents to be attached 3163 if ($this->tocpage) { 3164 // move reference to unexistent pages (used for page attachments) 3165 // adjust outlines 3166 $tmpoutlines = $this->outlines; 3167 foreach ($tmpoutlines as $key => $outline) { 3168 if (!$outline['f'] AND ($outline['p'] > $this->numpages)) { 3169 $this->outlines[$key]['p'] = ($outline['p'] + 1); 3170 } 3171 } 3172 // adjust dests 3173 $tmpdests = $this->dests; 3174 foreach ($tmpdests as $key => $dest) { 3175 if (!$dest['f'] AND ($dest['p'] > $this->numpages)) { 3176 $this->dests[$key]['p'] = ($dest['p'] + 1); 3177 } 3178 } 3179 // adjust links 3180 $tmplinks = $this->links; 3181 foreach ($tmplinks as $key => $link) { 3182 if (!$link['f'] AND ($link['p'] > $this->numpages)) { 3183 $this->links[$key]['p'] = ($link['p'] + 1); 3184 } 3185 } 3186 } 3187 if ($this->numpages > $this->page) { 3188 // this page has been already added 3189 $this->setPage($this->page + 1); 3190 $this->SetY($this->tMargin); 3191 return; 3192 } 3193 // start a new page 3194 if ($this->state == 0) { 3195 $this->Open(); 3196 } 3197 ++$this->numpages; 3198 $this->swapMargins($this->booklet); 3199 // save current graphic settings 3200 $gvars = $this->getGraphicVars(); 3201 // start new page 3202 $this->_beginpage($orientation, $format); 3203 // mark page as open 3204 $this->pageopen[$this->page] = true; 3205 // restore graphic settings 3206 $this->setGraphicVars($gvars); 3207 // mark this point 3208 $this->setPageMark(); 3209 // print page header 3210 $this->setHeader(); 3211 // restore graphic settings 3212 $this->setGraphicVars($gvars); 3213 // mark this point 3214 $this->setPageMark(); 3215 // print table header (if any) 3216 $this->setTableHeader(); 3217 // set mark for empty page check 3218 $this->emptypagemrk[$this->page]= $this->pagelen[$this->page]; 3219 } 3220 3221 /** 3222 * Set start-writing mark on current page stream used to put borders and fills. 3223 * Borders and fills are always created after content and inserted on the position marked by this method. 3224 * This function must be called after calling Image() function for a background image. 3225 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions. 3226 * @public 3227 * @since 4.0.016 (2008-07-30) 3228 */ 3229 public function setPageMark() { 3230 $this->intmrk[$this->page] = $this->pagelen[$this->page]; 3231 $this->bordermrk[$this->page] = $this->intmrk[$this->page]; 3232 $this->setContentMark(); 3233 } 3234 3235 /** 3236 * Set start-writing mark on selected page. 3237 * Borders and fills are always created after content and inserted on the position marked by this method. 3238 * @param $page (int) page number (default is the current page) 3239 * @protected 3240 * @since 4.6.021 (2009-07-20) 3241 */ 3242 protected function setContentMark($page=0) { 3243 if ($page <= 0) { 3244 $page = $this->page; 3245 } 3246 if (isset($this->footerlen[$page])) { 3247 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page]; 3248 } else { 3249 $this->cntmrk[$page] = $this->pagelen[$page]; 3250 } 3251 } 3252 3253 /** 3254 * Set header data. 3255 * @param $ln (string) header image logo 3256 * @param $lw (string) header image logo width in mm 3257 * @param $ht (string) string to print as title on document header 3258 * @param $hs (string) string to print on document header 3259 * @param $tc (array) RGB array color for text. 3260 * @param $lc (array) RGB array color for line. 3261 * @public 3262 */ 3263 public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) { 3264 $this->header_logo = $ln; 3265 $this->header_logo_width = $lw; 3266 $this->header_title = $ht; 3267 $this->header_string = $hs; 3268 $this->header_text_color = $tc; 3269 $this->header_line_color = $lc; 3270 } 3271 3272 /** 3273 * Set footer data. 3274 * @param $tc (array) RGB array color for text. 3275 * @param $lc (array) RGB array color for line. 3276 * @public 3277 */ 3278 public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) { 3279 $this->footer_text_color = $tc; 3280 $this->footer_line_color = $lc; 3281 } 3282 3283 /** 3284 * Returns header data: 3285 * <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> 3286 * @return array() 3287 * @public 3288 * @since 4.0.012 (2008-07-24) 3289 */ 3290 public function getHeaderData() { 3291 $ret = array(); 3292 $ret['logo'] = $this->header_logo; 3293 $ret['logo_width'] = $this->header_logo_width; 3294 $ret['title'] = $this->header_title; 3295 $ret['string'] = $this->header_string; 3296 $ret['text_color'] = $this->header_text_color; 3297 $ret['line_color'] = $this->header_line_color; 3298 return $ret; 3299 } 3300 3301 /** 3302 * Set header margin. 3303 * (minimum distance between header and top page margin) 3304 * @param $hm (int) distance in user units 3305 * @public 3306 */ 3307 public function setHeaderMargin($hm=10) { 3308 $this->header_margin = $hm; 3309 } 3310 3311 /** 3312 * Returns header margin in user units. 3313 * @return float 3314 * @since 4.0.012 (2008-07-24) 3315 * @public 3316 */ 3317 public function getHeaderMargin() { 3318 return $this->header_margin; 3319 } 3320 3321 /** 3322 * Set footer margin. 3323 * (minimum distance between footer and bottom page margin) 3324 * @param $fm (int) distance in user units 3325 * @public 3326 */ 3327 public function setFooterMargin($fm=10) { 3328 $this->footer_margin = $fm; 3329 } 3330 3331 /** 3332 * Returns footer margin in user units. 3333 * @return float 3334 * @since 4.0.012 (2008-07-24) 3335 * @public 3336 */ 3337 public function getFooterMargin() { 3338 return $this->footer_margin; 3339 } 3340 /** 3341 * Set a flag to print page header. 3342 * @param $val (boolean) set to true to print the page header (default), false otherwise. 3343 * @public 3344 */ 3345 public function setPrintHeader($val=true) { 3346 $this->print_header = $val ? true : false; 3347 } 3348 3349 /** 3350 * Set a flag to print page footer. 3351 * @param $val (boolean) set to true to print the page footer (default), false otherwise. 3352 * @public 3353 */ 3354 public function setPrintFooter($val=true) { 3355 $this->print_footer = $val ? true : false; 3356 } 3357 3358 /** 3359 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image 3360 * @return float 3361 * @public 3362 */ 3363 public function getImageRBX() { 3364 return $this->img_rb_x; 3365 } 3366 3367 /** 3368 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image 3369 * @return float 3370 * @public 3371 */ 3372 public function getImageRBY() { 3373 return $this->img_rb_y; 3374 } 3375 3376 /** 3377 * Reset the xobject template used by Header() method. 3378 * @public 3379 */ 3380 public function resetHeaderTemplate() { 3381 $this->header_xobjid = false; 3382 } 3383 3384 /** 3385 * Set a flag to automatically reset the xobject template used by Header() method at each page. 3386 * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise. 3387 * @public 3388 */ 3389 public function setHeaderTemplateAutoreset($val=true) { 3390 $this->header_xobj_autoreset = $val ? true : false; 3391 } 3392 3393 /** 3394 * This method is used to render the page header. 3395 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 3396 * @public 3397 */ 3398 public function Header() { 3399 if ($this->header_xobjid === false) { 3400 // start a new XObject Template 3401 $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin); 3402 $headerfont = $this->getHeaderFont(); 3403 $headerdata = $this->getHeaderData(); 3404 $this->y = $this->header_margin; 3405 if ($this->rtl) { 3406 $this->x = $this->w - $this->original_rMargin; 3407 } else { 3408 $this->x = $this->original_lMargin; 3409 } 3410 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) { 3411 $imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']); 3412 if (($imgtype == 'eps') OR ($imgtype == 'ai')) { 3413 $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3414 } elseif ($imgtype == 'svg') { 3415 $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3416 } else { 3417 $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3418 } 3419 $imgy = $this->getImageRBY(); 3420 } else { 3421 $imgy = $this->y; 3422 } 3423 $cell_height = $this->getCellHeight($headerfont[2] / $this->k); 3424 // set starting margin for text data cell 3425 if ($this->getRTL()) { 3426 $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1); 3427 } else { 3428 $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1); 3429 } 3430 $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1); 3431 $this->SetTextColorArray($this->header_text_color); 3432 // header title 3433 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); 3434 $this->SetX($header_x); 3435 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0); 3436 // header string 3437 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]); 3438 $this->SetX($header_x); 3439 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false); 3440 // print an ending header line 3441 $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color'])); 3442 $this->SetY((2.835 / $this->k) + max($imgy, $this->y)); 3443 if ($this->rtl) { 3444 $this->SetX($this->original_rMargin); 3445 } else { 3446 $this->SetX($this->original_lMargin); 3447 } 3448 $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C'); 3449 $this->endTemplate(); 3450 } 3451 // print header template 3452 $x = 0; 3453 $dx = 0; 3454 if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) { 3455 // adjust margins for booklet mode 3456 $dx = ($this->original_lMargin - $this->original_rMargin); 3457 } 3458 if ($this->rtl) { 3459 $x = $this->w + $dx; 3460 } else { 3461 $x = 0 + $dx; 3462 } 3463 $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false); 3464 if ($this->header_xobj_autoreset) { 3465 // reset header xobject template at each page 3466 $this->header_xobjid = false; 3467 } 3468 } 3469 3470 /** 3471 * This method is used to render the page footer. 3472 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 3473 * @public 3474 */ 3475 public function Footer() { 3476 $cur_y = $this->y; 3477 $this->SetTextColorArray($this->footer_text_color); 3478 //set style for cell border 3479 $line_width = (0.85 / $this->k); 3480 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color)); 3481 //print document barcode 3482 $barcode = $this->getBarcode(); 3483 if (!empty($barcode)) { 3484 $this->Ln($line_width); 3485 $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3); 3486 $style = array( 3487 'position' => $this->rtl?'R':'L', 3488 'align' => $this->rtl?'R':'L', 3489 'stretch' => false, 3490 'fitwidth' => true, 3491 'cellfitalign' => '', 3492 'border' => false, 3493 'padding' => 0, 3494 'fgcolor' => array(0,0,0), 3495 'bgcolor' => false, 3496 'text' => false 3497 ); 3498 $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, ''); 3499 } 3500 $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : ''; 3501 if (empty($this->pagegroups)) { 3502 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages(); 3503 } else { 3504 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias(); 3505 } 3506 $this->SetY($cur_y); 3507 //Print page number 3508 if ($this->getRTL()) { 3509 $this->SetX($this->original_rMargin); 3510 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L'); 3511 } else { 3512 $this->SetX($this->original_lMargin); 3513 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R'); 3514 } 3515 } 3516 3517 /** 3518 * This method is used to render the page header. 3519 * @protected 3520 * @since 4.0.012 (2008-07-24) 3521 */ 3522 protected function setHeader() { 3523 if (!$this->print_header OR ($this->state != 2)) { 3524 return; 3525 } 3526 $this->InHeader = true; 3527 $this->setGraphicVars($this->default_graphic_vars); 3528 $temp_thead = $this->thead; 3529 $temp_theadMargins = $this->theadMargins; 3530 $lasth = $this->lasth; 3531 $newline = $this->newline; 3532 $this->_outSaveGraphicsState(); 3533 $this->rMargin = $this->original_rMargin; 3534 $this->lMargin = $this->original_lMargin; 3535 $this->SetCellPadding(0); 3536 //set current position 3537 if ($this->rtl) { 3538 $this->SetXY($this->original_rMargin, $this->header_margin); 3539 } else { 3540 $this->SetXY($this->original_lMargin, $this->header_margin); 3541 } 3542 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); 3543 $this->Header(); 3544 //restore position 3545 if ($this->rtl) { 3546 $this->SetXY($this->original_rMargin, $this->tMargin); 3547 } else { 3548 $this->SetXY($this->original_lMargin, $this->tMargin); 3549 } 3550 $this->_outRestoreGraphicsState(); 3551 $this->lasth = $lasth; 3552 $this->thead = $temp_thead; 3553 $this->theadMargins = $temp_theadMargins; 3554 $this->newline = $newline; 3555 $this->InHeader = false; 3556 } 3557 3558 /** 3559 * This method is used to render the page footer. 3560 * @protected 3561 * @since 4.0.012 (2008-07-24) 3562 */ 3563 protected function setFooter() { 3564 if ($this->state != 2) { 3565 return; 3566 } 3567 $this->InFooter = true; 3568 // save current graphic settings 3569 $gvars = $this->getGraphicVars(); 3570 // mark this point 3571 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 3572 $this->_out("\n"); 3573 if ($this->print_footer) { 3574 $this->setGraphicVars($this->default_graphic_vars); 3575 $this->current_column = 0; 3576 $this->num_columns = 1; 3577 $temp_thead = $this->thead; 3578 $temp_theadMargins = $this->theadMargins; 3579 $lasth = $this->lasth; 3580 $this->_outSaveGraphicsState(); 3581 $this->rMargin = $this->original_rMargin; 3582 $this->lMargin = $this->original_lMargin; 3583 $this->SetCellPadding(0); 3584 //set current position 3585 $footer_y = $this->h - $this->footer_margin; 3586 if ($this->rtl) { 3587 $this->SetXY($this->original_rMargin, $footer_y); 3588 } else { 3589 $this->SetXY($this->original_lMargin, $footer_y); 3590 } 3591 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]); 3592 $this->Footer(); 3593 //restore position 3594 if ($this->rtl) { 3595 $this->SetXY($this->original_rMargin, $this->tMargin); 3596 } else { 3597 $this->SetXY($this->original_lMargin, $this->tMargin); 3598 } 3599 $this->_outRestoreGraphicsState(); 3600 $this->lasth = $lasth; 3601 $this->thead = $temp_thead; 3602 $this->theadMargins = $temp_theadMargins; 3603 } 3604 // restore graphic settings 3605 $this->setGraphicVars($gvars); 3606 $this->current_column = $gvars['current_column']; 3607 $this->num_columns = $gvars['num_columns']; 3608 // calculate footer length 3609 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1; 3610 $this->InFooter = false; 3611 } 3612 3613 /** 3614 * Check if we are on the page body (excluding page header and footer). 3615 * @return true if we are not in page header nor in page footer, false otherwise. 3616 * @protected 3617 * @since 5.9.091 (2011-06-15) 3618 */ 3619 protected function inPageBody() { 3620 return (($this->InHeader === false) AND ($this->InFooter === false)); 3621 } 3622 3623 /** 3624 * This method is used to render the table header on new page (if any). 3625 * @protected 3626 * @since 4.5.030 (2009-03-25) 3627 */ 3628 protected function setTableHeader() { 3629 if ($this->num_columns > 1) { 3630 // multi column mode 3631 return; 3632 } 3633 if (isset($this->theadMargins['top'])) { 3634 // restore the original top-margin 3635 $this->tMargin = $this->theadMargins['top']; 3636 $this->pagedim[$this->page]['tm'] = $this->tMargin; 3637 $this->y = $this->tMargin; 3638 } 3639 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) { 3640 // set margins 3641 $prev_lMargin = $this->lMargin; 3642 $prev_rMargin = $this->rMargin; 3643 $prev_cell_padding = $this->cell_padding; 3644 $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']); 3645 $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']); 3646 $this->cell_padding = $this->theadMargins['cell_padding']; 3647 if ($this->rtl) { 3648 $this->x = $this->w - $this->rMargin; 3649 } else { 3650 $this->x = $this->lMargin; 3651 } 3652 // account for special "cell" mode 3653 if ($this->theadMargins['cell']) { 3654 if ($this->rtl) { 3655 $this->x -= $this->cell_padding['R']; 3656 } else { 3657 $this->x += $this->cell_padding['L']; 3658 } 3659 } 3660 $gvars = $this->getGraphicVars(); 3661 if (!empty($this->theadMargins['gvars'])) { 3662 // set the correct graphic style 3663 $this->setGraphicVars($this->theadMargins['gvars']); 3664 $this->rMargin = $gvars['rMargin']; 3665 $this->lMargin = $gvars['lMargin']; 3666 } 3667 // print table header 3668 $this->writeHTML($this->thead, false, false, false, false, ''); 3669 $this->setGraphicVars($gvars); 3670 // set new top margin to skip the table headers 3671 if (!isset($this->theadMargins['top'])) { 3672 $this->theadMargins['top'] = $this->tMargin; 3673 } 3674 // store end of header position 3675 if (!isset($this->columns[0]['th'])) { 3676 $this->columns[0]['th'] = array(); 3677 } 3678 $this->columns[0]['th']['\''.$this->page.'\''] = $this->y; 3679 $this->tMargin = $this->y; 3680 $this->pagedim[$this->page]['tm'] = $this->tMargin; 3681 $this->lasth = 0; 3682 $this->lMargin = $prev_lMargin; 3683 $this->rMargin = $prev_rMargin; 3684 $this->cell_padding = $prev_cell_padding; 3685 } 3686 } 3687 3688 /** 3689 * Returns the current page number. 3690 * @return int page number 3691 * @public 3692 * @since 1.0 3693 * @see getAliasNbPages() 3694 */ 3695 public function PageNo() { 3696 return $this->page; 3697 } 3698 3699 /** 3700 * Returns the array of spot colors. 3701 * @return (array) Spot colors array. 3702 * @public 3703 * @since 6.0.038 (2013-09-30) 3704 */ 3705 public function getAllSpotColors() { 3706 return $this->spot_colors; 3707 } 3708 3709 /** 3710 * Defines a new spot color. 3711 * It can be expressed in RGB components or gray scale. 3712 * The method can be called before the first page is created and the value is retained from page to page. 3713 * @param $name (string) Full name of the spot color. 3714 * @param $c (float) Cyan color for CMYK. Value between 0 and 100. 3715 * @param $m (float) Magenta color for CMYK. Value between 0 and 100. 3716 * @param $y (float) Yellow color for CMYK. Value between 0 and 100. 3717 * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100. 3718 * @public 3719 * @since 4.0.024 (2008-09-12) 3720 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor() 3721 */ 3722 public function AddSpotColor($name, $c, $m, $y, $k) { 3723 if (!isset($this->spot_colors[$name])) { 3724 $i = (1 + count($this->spot_colors)); 3725 $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i); 3726 } 3727 } 3728 3729 /** 3730 * Set the spot color for the specified type ('draw', 'fill', 'text'). 3731 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3732 * @param $name (string) Name of the spot color. 3733 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3734 * @return (string) PDF color command. 3735 * @public 3736 * @since 5.9.125 (2011-10-03) 3737 */ 3738 public function setSpotColor($type, $name, $tint=100) { 3739 $spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors); 3740 if ($spotcolor === false) { 3741 $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.'); 3742 } 3743 $tint = (max(0, min(100, $tint)) / 100); 3744 $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']); 3745 switch ($type) { 3746 case 'draw': { 3747 $pdfcolor .= sprintf('CS %F SCN', $tint); 3748 $this->DrawColor = $pdfcolor; 3749 $this->strokecolor = $spotcolor; 3750 break; 3751 } 3752 case 'fill': { 3753 $pdfcolor .= sprintf('cs %F scn', $tint); 3754 $this->FillColor = $pdfcolor; 3755 $this->bgcolor = $spotcolor; 3756 break; 3757 } 3758 case 'text': { 3759 $pdfcolor .= sprintf('cs %F scn', $tint); 3760 $this->TextColor = $pdfcolor; 3761 $this->fgcolor = $spotcolor; 3762 break; 3763 } 3764 } 3765 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3766 if ($this->state == 2) { 3767 $this->_out($pdfcolor); 3768 } 3769 if ($this->inxobj) { 3770 // we are inside an XObject template 3771 $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name]; 3772 } 3773 return $pdfcolor; 3774 } 3775 3776 /** 3777 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders). 3778 * @param $name (string) Name of the spot color. 3779 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3780 * @public 3781 * @since 4.0.024 (2008-09-12) 3782 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor() 3783 */ 3784 public function SetDrawSpotColor($name, $tint=100) { 3785 $this->setSpotColor('draw', $name, $tint); 3786 } 3787 3788 /** 3789 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds). 3790 * @param $name (string) Name of the spot color. 3791 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3792 * @public 3793 * @since 4.0.024 (2008-09-12) 3794 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor() 3795 */ 3796 public function SetFillSpotColor($name, $tint=100) { 3797 $this->setSpotColor('fill', $name, $tint); 3798 } 3799 3800 /** 3801 * Defines the spot color used for text. 3802 * @param $name (string) Name of the spot color. 3803 * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3804 * @public 3805 * @since 4.0.024 (2008-09-12) 3806 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor() 3807 */ 3808 public function SetTextSpotColor($name, $tint=100) { 3809 $this->setSpotColor('text', $name, $tint); 3810 } 3811 3812 /** 3813 * Set the color array for the specified type ('draw', 'fill', 'text'). 3814 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3815 * The method can be called before the first page is created and the value is retained from page to page. 3816 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3817 * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values). 3818 * @param $ret (boolean) If true do not send the PDF command. 3819 * @return (string) The PDF command or empty string. 3820 * @public 3821 * @since 3.1.000 (2008-06-11) 3822 */ 3823 public function setColorArray($type, $color, $ret=false) { 3824 if (is_array($color)) { 3825 $color = array_values($color); 3826 // component: grey, RGB red or CMYK cyan 3827 $c = isset($color[0]) ? $color[0] : -1; 3828 // component: RGB green or CMYK magenta 3829 $m = isset($color[1]) ? $color[1] : -1; 3830 // component: RGB blue or CMYK yellow 3831 $y = isset($color[2]) ? $color[2] : -1; 3832 // component: CMYK black 3833 $k = isset($color[3]) ? $color[3] : -1; 3834 // color name 3835 $name = isset($color[4]) ? $color[4] : ''; 3836 if ($c >= 0) { 3837 return $this->setColor($type, $c, $m, $y, $k, $ret, $name); 3838 } 3839 } 3840 return ''; 3841 } 3842 3843 /** 3844 * Defines the color used for all drawing operations (lines, rectangles and cell borders). 3845 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3846 * The method can be called before the first page is created and the value is retained from page to page. 3847 * @param $color (array) Array of colors (1, 3 or 4 values). 3848 * @param $ret (boolean) If true do not send the PDF command. 3849 * @return string the PDF command 3850 * @public 3851 * @since 3.1.000 (2008-06-11) 3852 * @see SetDrawColor() 3853 */ 3854 public function SetDrawColorArray($color, $ret=false) { 3855 return $this->setColorArray('draw', $color, $ret); 3856 } 3857 3858 /** 3859 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 3860 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3861 * The method can be called before the first page is created and the value is retained from page to page. 3862 * @param $color (array) Array of colors (1, 3 or 4 values). 3863 * @param $ret (boolean) If true do not send the PDF command. 3864 * @public 3865 * @since 3.1.000 (2008-6-11) 3866 * @see SetFillColor() 3867 */ 3868 public function SetFillColorArray($color, $ret=false) { 3869 return $this->setColorArray('fill', $color, $ret); 3870 } 3871 3872 /** 3873 * Defines the color used for text. It can be expressed in RGB components or gray scale. 3874 * The method can be called before the first page is created and the value is retained from page to page. 3875 * @param $color (array) Array of colors (1, 3 or 4 values). 3876 * @param $ret (boolean) If true do not send the PDF command. 3877 * @public 3878 * @since 3.1.000 (2008-6-11) 3879 * @see SetFillColor() 3880 */ 3881 public function SetTextColorArray($color, $ret=false) { 3882 return $this->setColorArray('text', $color, $ret); 3883 } 3884 3885 /** 3886 * Defines the color used by the specified type ('draw', 'fill', 'text'). 3887 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3888 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 3889 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 3890 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 3891 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 3892 * @param $ret (boolean) If true do not send the command. 3893 * @param $name (string) spot color name (if any) 3894 * @return (string) The PDF command or empty string. 3895 * @public 3896 * @since 5.9.125 (2011-10-03) 3897 */ 3898 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 3899 // set default values 3900 if (!is_numeric($col1)) { 3901 $col1 = 0; 3902 } 3903 if (!is_numeric($col2)) { 3904 $col2 = -1; 3905 } 3906 if (!is_numeric($col3)) { 3907 $col3 = -1; 3908 } 3909 if (!is_numeric($col4)) { 3910 $col4 = -1; 3911 } 3912 // set color by case 3913 $suffix = ''; 3914 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 3915 // Grey scale 3916 $col1 = max(0, min(255, $col1)); 3917 $intcolor = array('G' => $col1); 3918 $pdfcolor = sprintf('%F ', ($col1 / 255)); 3919 $suffix = 'g'; 3920 } elseif ($col4 == -1) { 3921 // RGB 3922 $col1 = max(0, min(255, $col1)); 3923 $col2 = max(0, min(255, $col2)); 3924 $col3 = max(0, min(255, $col3)); 3925 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 3926 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255)); 3927 $suffix = 'rg'; 3928 } else { 3929 $col1 = max(0, min(100, $col1)); 3930 $col2 = max(0, min(100, $col2)); 3931 $col3 = max(0, min(100, $col3)); 3932 $col4 = max(0, min(100, $col4)); 3933 if (empty($name)) { 3934 // CMYK 3935 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 3936 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100)); 3937 $suffix = 'k'; 3938 } else { 3939 // SPOT COLOR 3940 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name); 3941 $this->AddSpotColor($name, $col1, $col2, $col3, $col4); 3942 $pdfcolor = $this->setSpotColor($type, $name, 100); 3943 } 3944 } 3945 switch ($type) { 3946 case 'draw': { 3947 $pdfcolor .= strtoupper($suffix); 3948 $this->DrawColor = $pdfcolor; 3949 $this->strokecolor = $intcolor; 3950 break; 3951 } 3952 case 'fill': { 3953 $pdfcolor .= $suffix; 3954 $this->FillColor = $pdfcolor; 3955 $this->bgcolor = $intcolor; 3956 break; 3957 } 3958 case 'text': { 3959 $pdfcolor .= $suffix; 3960 $this->TextColor = $pdfcolor; 3961 $this->fgcolor = $intcolor; 3962 break; 3963 } 3964 } 3965 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3966 if (($type != 'text') AND ($this->state == 2)) { 3967 if (!$ret) { 3968 $this->_out($pdfcolor); 3969 } 3970 return $pdfcolor; 3971 } 3972 return ''; 3973 } 3974 3975 /** 3976 * 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. 3977 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 3978 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 3979 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 3980 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 3981 * @param $ret (boolean) If true do not send the command. 3982 * @param $name (string) spot color name (if any) 3983 * @return string the PDF command 3984 * @public 3985 * @since 1.3 3986 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() 3987 */ 3988 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 3989 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name); 3990 } 3991 3992 /** 3993 * 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. 3994 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 3995 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 3996 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 3997 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 3998 * @param $ret (boolean) If true do not send the command. 3999 * @param $name (string) Spot color name (if any). 4000 * @return (string) The PDF command. 4001 * @public 4002 * @since 1.3 4003 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() 4004 */ 4005 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 4006 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name); 4007 } 4008 4009 /** 4010 * 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. 4011 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 4012 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 4013 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 4014 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 4015 * @param $ret (boolean) If true do not send the command. 4016 * @param $name (string) Spot color name (if any). 4017 * @return (string) Empty string. 4018 * @public 4019 * @since 1.3 4020 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() 4021 */ 4022 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 4023 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name); 4024 } 4025 4026 /** 4027 * Returns the length of a string in user unit. A font must be selected.<br> 4028 * @param $s (string) The string whose length is to be computed 4029 * @param $fontname (string) 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. 4030 * @param $fontstyle (string) 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-through</li><li>O: overline</li></ul> or any combination. The default value is regular. 4031 * @param $fontsize (float) Font size in points. The default value is the current size. 4032 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length. 4033 * @return mixed int total string length or array of characted widths 4034 * @author Nicola Asuni 4035 * @public 4036 * @since 1.2 4037 */ 4038 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 4039 return $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont), $s, $this->tmprtl, $this->isunicode, $this->CurrentFont), $fontname, $fontstyle, $fontsize, $getarray); 4040 } 4041 4042 /** 4043 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br> 4044 * @param $sa (string) The array of chars whose total length is to be computed 4045 * @param $fontname (string) 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. 4046 * @param $fontstyle (string) 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 through</li><li>O: overline</li></ul> or any combination. The default value is regular. 4047 * @param $fontsize (float) Font size in points. The default value is the current size. 4048 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length. 4049 * @return mixed int total string length or array of characted widths 4050 * @author Nicola Asuni 4051 * @public 4052 * @since 2.4.000 (2008-03-06) 4053 */ 4054 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 4055 // store current values 4056 if (!TCPDF_STATIC::empty_string($fontname)) { 4057 $prev_FontFamily = $this->FontFamily; 4058 $prev_FontStyle = $this->FontStyle; 4059 $prev_FontSizePt = $this->FontSizePt; 4060 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false); 4061 } 4062 // convert UTF-8 array to Latin1 if required 4063 if ($this->isunicode AND (!$this->isUnicodeFont())) { 4064 $sa = TCPDF_FONTS::UTF8ArrToLatin1Arr($sa); 4065 } 4066 $w = 0; // total width 4067 $wa = array(); // array of characters widths 4068 foreach ($sa as $ck => $char) { 4069 // character width 4070 $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)])); 4071 $wa[] = $cw; 4072 $w += $cw; 4073 } 4074 // restore previous values 4075 if (!TCPDF_STATIC::empty_string($fontname)) { 4076 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false); 4077 } 4078 if ($getarray) { 4079 return $wa; 4080 } 4081 return $w; 4082 } 4083 4084 /** 4085 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking). 4086 * @param $char (int) The char code whose length is to be returned 4087 * @param $notlast (boolean) If false ignore the font-spacing. 4088 * @return float char width 4089 * @author Nicola Asuni 4090 * @public 4091 * @since 2.4.000 (2008-03-06) 4092 */ 4093 public function GetCharWidth($char, $notlast=true) { 4094 // get raw width 4095 $chw = $this->getRawCharWidth($char); 4096 if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) { 4097 // increase/decrease font spacing 4098 $chw += $this->font_spacing; 4099 } 4100 if ($this->font_stretching != 100) { 4101 // fixed stretching mode 4102 $chw *= ($this->font_stretching / 100); 4103 } 4104 return $chw; 4105 } 4106 4107 /** 4108 * Returns the length of the char in user unit for the current font. 4109 * @param $char (int) The char code whose length is to be returned 4110 * @return float char width 4111 * @author Nicola Asuni 4112 * @public 4113 * @since 5.9.000 (2010-09-28) 4114 */ 4115 public function getRawCharWidth($char) { 4116 if ($char == 173) { 4117 // SHY character will not be printed 4118 return (0); 4119 } 4120 if (isset($this->CurrentFont['cw'][$char])) { 4121 $w = $this->CurrentFont['cw'][$char]; 4122 } elseif (isset($this->CurrentFont['dw'])) { 4123 // default width 4124 $w = $this->CurrentFont['dw']; 4125 } elseif (isset($this->CurrentFont['cw'][32])) { 4126 // default width 4127 $w = $this->CurrentFont['cw'][32]; 4128 } else { 4129 $w = 600; 4130 } 4131 return $this->getAbsFontMeasure($w); 4132 } 4133 4134 /** 4135 * Returns the numbero of characters in a string. 4136 * @param $s (string) The input string. 4137 * @return int number of characters 4138 * @public 4139 * @since 2.0.0001 (2008-01-07) 4140 */ 4141 public function GetNumChars($s) { 4142 if ($this->isUnicodeFont()) { 4143 return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont)); 4144 } 4145 return strlen($s); 4146 } 4147 4148 /** 4149 * Fill the list of available fonts ($this->fontlist). 4150 * @protected 4151 * @since 4.0.013 (2008-07-28) 4152 */ 4153 protected function getFontsList() { 4154 if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) { 4155 while (($file = readdir($fontsdir)) !== false) { 4156 if (substr($file, -4) == '.php') { 4157 array_push($this->fontlist, strtolower(basename($file, '.php'))); 4158 } 4159 } 4160 closedir($fontsdir); 4161 } 4162 } 4163 4164 /** 4165 * Returns the unicode caracter specified by the value 4166 * @param $c (int) UTF-8 value 4167 * @return Returns the specified character. 4168 * @since 2.3.000 (2008-03-05) 4169 * @public 4170 * @deprecated 4171 */ 4172 public function unichr($c) { 4173 return TCPDF_FONTS::unichr($c, $this->isunicode); 4174 } 4175 4176 /** 4177 * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable). 4178 * @param $fontfile (string) Font file (full path). 4179 * @param $fonttype (string) Font type. Leave empty for autodetect mode. Valid values are: TrueTypeUnicode, TrueType, Type1, CID0JP = CID-0 Japanese, CID0KR = CID-0 Korean, CID0CS = CID-0 Chinese Simplified, CID0CT = CID-0 Chinese Traditional. 4180 * @param $enc (string) Name of the encoding table to use. Leave empty for default mode. Omit this parameter for TrueType Unicode and symbolic fonts like Symbol or ZapfDingBats. 4181 * @param $flags (int) Unsigned 32-bit integer containing flags specifying various characteristics of the font (PDF32000:2008 - 9.8.2 Font Descriptor Flags): +1 for fixed font; +4 for symbol or +32 for non-symbol; +64 for italic. Fixed and Italic mode are generally autodetected so you have to set it to 32 = non-symbolic font (default) or 4 = symbolic font. 4182 * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder. 4183 * @param $platid (int) Platform ID for CMAP table to extract (when building a Unicode font for Windows this value should be 3, for Macintosh should be 1). 4184 * @param $encid (int) Encoding ID for CMAP table to extract (when building a Unicode font for Windows this value should be 1, for Macintosh should be 0). When Platform ID is 3, legal values for Encoding ID are: 0=Symbol, 1=Unicode, 2=ShiftJIS, 3=PRC, 4=Big5, 5=Wansung, 6=Johab, 7=Reserved, 8=Reserved, 9=Reserved, 10=UCS-4. 4185 * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file. 4186 * @return (string) TCPDF font name. 4187 * @author Nicola Asuni 4188 * @since 5.9.123 (2010-09-30) 4189 * @public 4190 * @deprecated 4191 */ 4192 public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) { 4193 return TCPDF_FONTS::addTTFfont($fontfile, $fonttype, $enc, $flags, $outpath, $platid, $encid, $addcbbox); 4194 } 4195 4196 /** 4197 * Imports a TrueType, Type1, core, or CID0 font and makes it available. 4198 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 4199 * 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. 4200 * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. 4201 * @param $style (string) 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> 4202 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 4203 * @return array containing the font data, or false in case of error. 4204 * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font. 4205 * @public 4206 * @since 1.5 4207 * @see SetFont(), setFontSubsetting() 4208 */ 4209 public function AddFont($family, $style='', $fontfile='', $subset='default') { 4210 if ($subset === 'default') { 4211 $subset = $this->font_subsetting; 4212 } 4213 if ($this->pdfa_mode) { 4214 $subset = false; 4215 } 4216 if (TCPDF_STATIC::empty_string($family)) { 4217 if (!TCPDF_STATIC::empty_string($this->FontFamily)) { 4218 $family = $this->FontFamily; 4219 } else { 4220 $this->Error('Empty font family'); 4221 } 4222 } 4223 // move embedded styles on $style 4224 if (substr($family, -1) == 'I') { 4225 $style .= 'I'; 4226 $family = substr($family, 0, -1); 4227 } 4228 if (substr($family, -1) == 'B') { 4229 $style .= 'B'; 4230 $family = substr($family, 0, -1); 4231 } 4232 // normalize family name 4233 $family = strtolower($family); 4234 if ((!$this->isunicode) AND ($family == 'arial')) { 4235 $family = 'helvetica'; 4236 } 4237 if (($family == 'symbol') OR ($family == 'zapfdingbats')) { 4238 $style = ''; 4239 } 4240 if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) { 4241 // all fonts must be embedded 4242 $family = 'pdfa'.$family; 4243 } 4244 $tempstyle = strtoupper($style); 4245 $style = ''; 4246 // underline 4247 if (strpos($tempstyle, 'U') !== false) { 4248 $this->underline = true; 4249 } else { 4250 $this->underline = false; 4251 } 4252 // line-through (deleted) 4253 if (strpos($tempstyle, 'D') !== false) { 4254 $this->linethrough = true; 4255 } else { 4256 $this->linethrough = false; 4257 } 4258 // overline 4259 if (strpos($tempstyle, 'O') !== false) { 4260 $this->overline = true; 4261 } else { 4262 $this->overline = false; 4263 } 4264 // bold 4265 if (strpos($tempstyle, 'B') !== false) { 4266 $style .= 'B'; 4267 } 4268 // oblique 4269 if (strpos($tempstyle, 'I') !== false) { 4270 $style .= 'I'; 4271 } 4272 $bistyle = $style; 4273 $fontkey = $family.$style; 4274 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : ''); 4275 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style); 4276 // check if the font has been already added 4277 $fb = $this->getFontBuffer($fontkey); 4278 if ($fb !== false) { 4279 if ($this->inxobj) { 4280 // we are inside an XObject template 4281 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i']; 4282 } 4283 return $fontdata; 4284 } 4285 // get specified font directory (if any) 4286 $fontdir = false; 4287 if (!TCPDF_STATIC::empty_string($fontfile)) { 4288 $fontdir = dirname($fontfile); 4289 if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) { 4290 $fontdir = ''; 4291 } else { 4292 $fontdir .= '/'; 4293 } 4294 } 4295 // true when the font style variation is missing 4296 $missing_style = false; 4297 // search and include font file 4298 if (TCPDF_STATIC::empty_string($fontfile) OR (!@file_exists($fontfile))) { 4299 // build a standard filenames for specified font 4300 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php'; 4301 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir); 4302 if (TCPDF_STATIC::empty_string($fontfile)) { 4303 $missing_style = true; 4304 // try to remove the style part 4305 $tmp_fontfile = str_replace(' ', '', $family).'.php'; 4306 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir); 4307 } 4308 } 4309 // include font file 4310 if (!TCPDF_STATIC::empty_string($fontfile) AND (@file_exists($fontfile))) { 4311 include($fontfile); 4312 } else { 4313 $this->Error('Could not include font definition file: '.$family.''); 4314 } 4315 // check font parameters 4316 if ((!isset($type)) OR (!isset($cw))) { 4317 $this->Error('The font definition file has a bad format: '.$fontfile.''); 4318 } 4319 // SET default parameters 4320 if (!isset($file) OR TCPDF_STATIC::empty_string($file)) { 4321 $file = ''; 4322 } 4323 if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) { 4324 $enc = ''; 4325 } 4326 if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) { 4327 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0); 4328 $cidinfo['uni2cid'] = array(); 4329 } 4330 if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) { 4331 $ctg = ''; 4332 } 4333 if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) { 4334 $desc = array(); 4335 } 4336 if (!isset($up) OR TCPDF_STATIC::empty_string($up)) { 4337 $up = -100; 4338 } 4339 if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) { 4340 $ut = 50; 4341 } 4342 if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) { 4343 $cw = array(); 4344 } 4345 if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) { 4346 // set default width 4347 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) { 4348 $dw = $desc['MissingWidth']; 4349 } elseif (isset($cw[32])) { 4350 $dw = $cw[32]; 4351 } else { 4352 $dw = 600; 4353 } 4354 } 4355 ++$this->numfonts; 4356 if ($type == 'core') { 4357 $name = $this->CoreFonts[$fontkey]; 4358 $subset = false; 4359 } elseif (($type == 'TrueType') OR ($type == 'Type1')) { 4360 $subset = false; 4361 } elseif ($type == 'TrueTypeUnicode') { 4362 $enc = 'Identity-H'; 4363 } elseif ($type == 'cidfont0') { 4364 if ($this->pdfa_mode) { 4365 $this->Error('All fonts must be embedded in PDF/A mode!'); 4366 } 4367 } else { 4368 $this->Error('Unknow font type: '.$type.''); 4369 } 4370 // set name if unset 4371 if (!isset($name) OR empty($name)) { 4372 $name = $fontkey; 4373 } 4374 // create artificial font style variations if missing (only works with non-embedded fonts) 4375 if (($type != 'core') AND $missing_style) { 4376 // style variations 4377 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic'); 4378 $name .= $styles[$bistyle]; 4379 // artificial bold 4380 if (strpos($bistyle, 'B') !== false) { 4381 if (isset($desc['StemV'])) { 4382 // from normal to bold 4383 $desc['StemV'] = round($desc['StemV'] * 1.75); 4384 } else { 4385 // bold 4386 $desc['StemV'] = 123; 4387 } 4388 } 4389 // artificial italic 4390 if (strpos($bistyle, 'I') !== false) { 4391 if (isset($desc['ItalicAngle'])) { 4392 $desc['ItalicAngle'] -= 11; 4393 } else { 4394 $desc['ItalicAngle'] = -11; 4395 } 4396 if (isset($desc['Flags'])) { 4397 $desc['Flags'] |= 64; //bit 7 4398 } else { 4399 $desc['Flags'] = 64; 4400 } 4401 } 4402 } 4403 // check if the array of characters bounding boxes is defined 4404 if (!isset($cbbox)) { 4405 $cbbox = array(); 4406 } 4407 // initialize subsetchars 4408 $subsetchars = array_fill(0, 255, true); 4409 $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'cbbox' => $cbbox, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars)); 4410 if ($this->inxobj) { 4411 // we are inside an XObject template 4412 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts; 4413 } 4414 if (isset($diff) AND (!empty($diff))) { 4415 //Search existing encodings 4416 $d = 0; 4417 $nb = count($this->diffs); 4418 for ($i=1; $i <= $nb; ++$i) { 4419 if ($this->diffs[$i] == $diff) { 4420 $d = $i; 4421 break; 4422 } 4423 } 4424 if ($d == 0) { 4425 $d = $nb + 1; 4426 $this->diffs[$d] = $diff; 4427 } 4428 $this->setFontSubBuffer($fontkey, 'diff', $d); 4429 } 4430 if (!TCPDF_STATIC::empty_string($file)) { 4431 if (!isset($this->FontFiles[$file])) { 4432 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) { 4433 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); 4434 } elseif ($type != 'core') { 4435 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); 4436 } 4437 } else { 4438 // update fontkeys that are sharing this font file 4439 $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset); 4440 if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) { 4441 $this->FontFiles[$file]['fontkeys'][] = $fontkey; 4442 } 4443 } 4444 } 4445 return $fontdata; 4446 } 4447 4448 /** 4449 * Sets the font used to print character strings. 4450 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). 4451 * The method can be called before the first page is created and the font is retained from page to page. 4452 * If you just wish to change the current font size, it is simpler to call SetFontSize(). 4453 * 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 /> 4454 * @param $family (string) 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. 4455 * @param $style (string) 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 through</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined. 4456 * @param $size (float) 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 4457 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 4458 * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font. 4459 * @param $out (boolean) if true output the font size command, otherwise only set the font properties. 4460 * @author Nicola Asuni 4461 * @public 4462 * @since 1.0 4463 * @see AddFont(), SetFontSize() 4464 */ 4465 public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) { 4466 //Select a font; size given in points 4467 if ($size === null) { 4468 $size = $this->FontSizePt; 4469 } 4470 if ($size < 0) { 4471 $size = 0; 4472 } 4473 // try to add font (if not already added) 4474 $fontdata = $this->AddFont($family, $style, $fontfile, $subset); 4475 $this->FontFamily = $fontdata['family']; 4476 $this->FontStyle = $fontdata['style']; 4477 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) { 4478 // save subset chars of the previous font 4479 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 4480 } 4481 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']); 4482 $this->SetFontSize($size, $out); 4483 } 4484 4485 /** 4486 * Defines the size of the current font. 4487 * @param $size (float) The font size in points. 4488 * @param $out (boolean) if true output the font size command, otherwise only set the font properties. 4489 * @public 4490 * @since 1.0 4491 * @see SetFont() 4492 */ 4493 public function SetFontSize($size, $out=true) { 4494 // font size in points 4495 $this->FontSizePt = $size; 4496 // font size in user units 4497 $this->FontSize = $size / $this->k; 4498 // calculate some font metrics 4499 if (isset($this->CurrentFont['desc']['FontBBox'])) { 4500 $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); 4501 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000); 4502 } else { 4503 $font_height = $size * 1.219; 4504 } 4505 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) { 4506 $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000); 4507 } 4508 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) { 4509 $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000); 4510 } 4511 if (!isset($font_ascent) AND !isset($font_descent)) { 4512 // core font 4513 $font_ascent = 0.76 * $font_height; 4514 $font_descent = $font_height - $font_ascent; 4515 } elseif (!isset($font_descent)) { 4516 $font_descent = $font_height - $font_ascent; 4517 } elseif (!isset($font_ascent)) { 4518 $font_ascent = $font_height - $font_descent; 4519 } 4520 $this->FontAscent = ($font_ascent / $this->k); 4521 $this->FontDescent = ($font_descent / $this->k); 4522 if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) { 4523 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 4524 } 4525 } 4526 4527 /** 4528 * Returns the bounding box of the current font in user units. 4529 * @return array 4530 * @public 4531 * @since 5.9.152 (2012-03-23) 4532 */ 4533 public function getFontBBox() { 4534 $fbbox = array(); 4535 if (isset($this->CurrentFont['desc']['FontBBox'])) { 4536 $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); 4537 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox); 4538 } else { 4539 // Find max width 4540 if (isset($this->CurrentFont['desc']['MaxWidth'])) { 4541 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth'])); 4542 } else { 4543 $maxw = 0; 4544 if (isset($this->CurrentFont['desc']['MissingWidth'])) { 4545 $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']); 4546 } 4547 if (isset($this->CurrentFont['desc']['AvgWidth'])) { 4548 $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']); 4549 } 4550 if (isset($this->CurrentFont['dw'])) { 4551 $maxw = max($maxw, $this->CurrentFont['dw']); 4552 } 4553 foreach ($this->CurrentFont['cw'] as $char => $w) { 4554 $maxw = max($maxw, $w); 4555 } 4556 if ($maxw == 0) { 4557 $maxw = 600; 4558 } 4559 $maxw = $this->getAbsFontMeasure($maxw); 4560 } 4561 $fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent); 4562 } 4563 return $fbbox; 4564 } 4565 4566 /** 4567 * Convert a relative font measure into absolute value. 4568 * @param $s (int) Font measure. 4569 * @return float Absolute measure. 4570 * @since 5.9.186 (2012-09-13) 4571 */ 4572 public function getAbsFontMeasure($s) { 4573 return ($s * $this->FontSize / 1000); 4574 } 4575 4576 /** 4577 * Returns the glyph bounding box of the specified character in the current font in user units. 4578 * @param $char (int) Input character code. 4579 * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined. 4580 * @since 5.9.186 (2012-09-13) 4581 */ 4582 public function getCharBBox($char) { 4583 if (isset($this->CurrentFont['cbbox'][$char])) { 4584 return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont['cbbox'][intval($char)]); 4585 } 4586 return false; 4587 } 4588 4589 /** 4590 * Return the font descent value 4591 * @param $font (string) font name 4592 * @param $style (string) font style 4593 * @param $size (float) The size (in points) 4594 * @return int font descent 4595 * @public 4596 * @author Nicola Asuni 4597 * @since 4.9.003 (2010-03-30) 4598 */ 4599 public function getFontDescent($font, $style='', $size=0) { 4600 $fontdata = $this->AddFont($font, $style); 4601 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4602 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) { 4603 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000); 4604 } else { 4605 $descent = (1.219 * 0.24 * $size); 4606 } 4607 return ($descent / $this->k); 4608 } 4609 4610 /** 4611 * Return the font ascent value. 4612 * @param $font (string) font name 4613 * @param $style (string) font style 4614 * @param $size (float) The size (in points) 4615 * @return int font ascent 4616 * @public 4617 * @author Nicola Asuni 4618 * @since 4.9.003 (2010-03-30) 4619 */ 4620 public function getFontAscent($font, $style='', $size=0) { 4621 $fontdata = $this->AddFont($font, $style); 4622 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4623 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) { 4624 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000); 4625 } else { 4626 $ascent = 1.219 * 0.76 * $size; 4627 } 4628 return ($ascent / $this->k); 4629 } 4630 4631 /** 4632 * Return true in the character is present in the specified font. 4633 * @param $char (mixed) Character to check (integer value or string) 4634 * @param $font (string) Font name (family name). 4635 * @param $style (string) Font style. 4636 * @return (boolean) true if the char is defined, false otherwise. 4637 * @public 4638 * @since 5.9.153 (2012-03-28) 4639 */ 4640 public function isCharDefined($char, $font='', $style='') { 4641 if (is_string($char)) { 4642 // get character code 4643 $char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont); 4644 $char = $char[0]; 4645 } 4646 if (TCPDF_STATIC::empty_string($font)) { 4647 if (TCPDF_STATIC::empty_string($style)) { 4648 return (isset($this->CurrentFont['cw'][intval($char)])); 4649 } 4650 $font = $this->FontFamily; 4651 } 4652 $fontdata = $this->AddFont($font, $style); 4653 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4654 return (isset($fontinfo['cw'][intval($char)])); 4655 } 4656 4657 /** 4658 * Replace missing font characters on selected font with specified substitutions. 4659 * @param $text (string) Text to process. 4660 * @param $font (string) Font name (family name). 4661 * @param $style (string) Font style. 4662 * @param $subs (array) Array of possible character substitutions. The key is the character to check (integer value) and the value is a single intege value or an array of possible substitutes. 4663 * @return (string) Processed text. 4664 * @public 4665 * @since 5.9.153 (2012-03-28) 4666 */ 4667 public function replaceMissingChars($text, $font='', $style='', $subs=array()) { 4668 if (empty($subs)) { 4669 return $text; 4670 } 4671 if (TCPDF_STATIC::empty_string($font)) { 4672 $font = $this->FontFamily; 4673 } 4674 $fontdata = $this->AddFont($font, $style); 4675 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4676 $uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont); 4677 foreach ($uniarr as $k => $chr) { 4678 if (!isset($fontinfo['cw'][$chr])) { 4679 // this character is missing on the selected font 4680 if (isset($subs[$chr])) { 4681 // we have available substitutions 4682 if (is_array($subs[$chr])) { 4683 foreach($subs[$chr] as $s) { 4684 if (isset($fontinfo['cw'][$s])) { 4685 $uniarr[$k] = $s; 4686 break; 4687 } 4688 } 4689 } elseif (isset($fontinfo['cw'][$subs[$chr]])) { 4690 $uniarr[$k] = $subs[$chr]; 4691 } 4692 } 4693 } 4694 } 4695 return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode)); 4696 } 4697 4698 /** 4699 * Defines the default monospaced font. 4700 * @param $font (string) Font name. 4701 * @public 4702 * @since 4.5.025 4703 */ 4704 public function SetDefaultMonospacedFont($font) { 4705 $this->default_monospaced_font = $font; 4706 } 4707 4708 /** 4709 * 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 /> 4710 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). 4711 * @public 4712 * @since 1.5 4713 * @see Cell(), Write(), Image(), Link(), SetLink() 4714 */ 4715 public function AddLink() { 4716 // create a new internal link 4717 $n = count($this->links) + 1; 4718 $this->links[$n] = array('p' => 0, 'y' => 0, 'f' => false); 4719 return $n; 4720 } 4721 4722 /** 4723 * Defines the page and position a link points to. 4724 * @param $link (int) The link identifier returned by AddLink() 4725 * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) 4726 * @param $page (int) Number of target page; -1 indicates the current page (default value). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages. 4727 * @public 4728 * @since 1.5 4729 * @see AddLink() 4730 */ 4731 public function SetLink($link, $y=0, $page=-1) { 4732 $fixed = false; 4733 if (!empty($page) AND ($page[0] == '*')) { 4734 $page = intval(substr($page, 1)); 4735 // this page number will not be changed when moving/add/deleting pages 4736 $fixed = true; 4737 } 4738 if ($page < 0) { 4739 $page = $this->page; 4740 } 4741 if ($y == -1) { 4742 $y = $this->y; 4743 } 4744 $this->links[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed); 4745 } 4746 4747 /** 4748 * Puts a link on a rectangular area of the page. 4749 * 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. 4750 * @param $x (float) Abscissa of the upper-left corner of the rectangle 4751 * @param $y (float) Ordinate of the upper-left corner of the rectangle 4752 * @param $w (float) Width of the rectangle 4753 * @param $h (float) Height of the rectangle 4754 * @param $link (mixed) URL or identifier returned by AddLink() 4755 * @param $spaces (int) number of spaces on the text to link 4756 * @public 4757 * @since 1.5 4758 * @see AddLink(), Annotation(), Cell(), Write(), Image() 4759 */ 4760 public function Link($x, $y, $w, $h, $link, $spaces=0) { 4761 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces); 4762 } 4763 4764 /** 4765 * Puts a markup annotation on a rectangular area of the page. 4766 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!! 4767 * @param $x (float) Abscissa of the upper-left corner of the rectangle 4768 * @param $y (float) Ordinate of the upper-left corner of the rectangle 4769 * @param $w (float) Width of the rectangle 4770 * @param $h (float) Height of the rectangle 4771 * @param $text (string) annotation text or alternate content 4772 * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7). 4773 * @param $spaces (int) number of spaces on the text to link 4774 * @public 4775 * @since 4.0.018 (2008-08-06) 4776 */ 4777 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) { 4778 if ($this->inxobj) { 4779 // store parameters for later use on template 4780 $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces); 4781 return; 4782 } 4783 if ($x === '') { 4784 $x = $this->x; 4785 } 4786 if ($y === '') { 4787 $y = $this->y; 4788 } 4789 // check page for no-write regions and adapt page margins if necessary 4790 list($x, $y) = $this->checkPageRegions($h, $x, $y); 4791 // recalculate coordinates to account for graphic transformations 4792 if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) { 4793 for ($i=$this->transfmatrix_key; $i > 0; --$i) { 4794 $maxid = count($this->transfmatrix[$i]) - 1; 4795 for ($j=$maxid; $j >= 0; --$j) { 4796 $ctm = $this->transfmatrix[$i][$j]; 4797 if (isset($ctm['a'])) { 4798 $x = $x * $this->k; 4799 $y = ($this->h - $y) * $this->k; 4800 $w = $w * $this->k; 4801 $h = $h * $this->k; 4802 // top left 4803 $xt = $x; 4804 $yt = $y; 4805 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4806 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4807 // top right 4808 $xt = $x + $w; 4809 $yt = $y; 4810 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4811 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4812 // bottom left 4813 $xt = $x; 4814 $yt = $y - $h; 4815 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4816 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4817 // bottom right 4818 $xt = $x + $w; 4819 $yt = $y - $h; 4820 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4821 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4822 // new coordinates (rectangle area) 4823 $x = min($x1, $x2, $x3, $x4); 4824 $y = max($y1, $y2, $y3, $y4); 4825 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k; 4826 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k; 4827 $x = $x / $this->k; 4828 $y = $this->h - ($y / $this->k); 4829 } 4830 } 4831 } 4832 } 4833 if ($this->page <= 0) { 4834 $page = 1; 4835 } else { 4836 $page = $this->page; 4837 } 4838 if (!isset($this->PageAnnots[$page])) { 4839 $this->PageAnnots[$page] = array(); 4840 } 4841 $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); 4842 if (!$this->pdfa_mode) { 4843 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS'])) 4844 AND (@file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS'])) 4845 AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) { 4846 $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']); 4847 } 4848 } 4849 // Add widgets annotation's icons 4850 if (isset($opt['mk']['i']) AND @file_exists($opt['mk']['i'])) { 4851 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true); 4852 } 4853 if (isset($opt['mk']['ri']) AND @file_exists($opt['mk']['ri'])) { 4854 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 4855 } 4856 if (isset($opt['mk']['ix']) AND @file_exists($opt['mk']['ix'])) { 4857 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 4858 } 4859 } 4860 4861 /** 4862 * Embedd the attached files. 4863 * @since 4.4.000 (2008-12-07) 4864 * @protected 4865 * @see Annotation() 4866 */ 4867 protected function _putEmbeddedFiles() { 4868 if ($this->pdfa_mode) { 4869 // embedded files are not allowed in PDF/A mode 4870 return; 4871 } 4872 reset($this->embeddedfiles); 4873 foreach ($this->embeddedfiles as $filename => $filedata) { 4874 $data = TCPDF_STATIC::fileGetContents($filedata['file']); 4875 if ($data !== FALSE) { 4876 $rawsize = strlen($data); 4877 if ($rawsize > 0) { 4878 // update name tree 4879 $this->efnames[$filename] = $filedata['f'].' 0 R'; 4880 // embedded file specification object 4881 $out = $this->_getobj($filedata['f'])."\n"; 4882 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>'; 4883 $out .= "\n".'endobj'; 4884 $this->_out($out); 4885 // embedded file object 4886 $filter = ''; 4887 if ($this->compress) { 4888 $data = gzcompress($data); 4889 $filter = ' /Filter /FlateDecode'; 4890 } 4891 $stream = $this->_getrawstream($data, $filedata['n']); 4892 $out = $this->_getobj($filedata['n'])."\n"; 4893 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>'; 4894 $out .= ' stream'."\n".$stream."\n".'endstream'; 4895 $out .= "\n".'endobj'; 4896 $this->_out($out); 4897 } 4898 } 4899 } 4900 } 4901 4902 /** 4903 * Prints a text cell at the specified position. 4904 * This method allows to place a string precisely on the page. 4905 * @param $x (float) Abscissa of the cell origin 4906 * @param $y (float) Ordinate of the cell origin 4907 * @param $txt (string) String to print 4908 * @param $fstroke (int) outline size in user units (false = disable) 4909 * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation). 4910 * @param $ffill (boolean) if true fills the text 4911 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 4912 * @param $ln (int) 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. 4913 * @param $align (string) 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> 4914 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 4915 * @param $link (mixed) URL or identifier returned by AddLink(). 4916 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. 4917 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 4918 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li><li>B : cell bottom</li></ul> 4919 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul> 4920 * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position. 4921 * @public 4922 * @since 1.0 4923 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell() 4924 */ 4925 public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) { 4926 $textrendermode = $this->textrendermode; 4927 $textstrokewidth = $this->textstrokewidth; 4928 $this->setTextRenderingMode($fstroke, $ffill, $fclip); 4929 $this->SetXY($x, $y, $rtloff); 4930 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign); 4931 // restore previous rendering mode 4932 $this->textrendermode = $textrendermode; 4933 $this->textstrokewidth = $textstrokewidth; 4934 } 4935 4936 /** 4937 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 4938 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> 4939 * This method is called automatically and should not be called directly by the application. 4940 * @return boolean 4941 * @public 4942 * @since 1.4 4943 * @see SetAutoPageBreak() 4944 */ 4945 public function AcceptPageBreak() { 4946 if ($this->num_columns > 1) { 4947 // multi column mode 4948 if ($this->current_column < ($this->num_columns - 1)) { 4949 // go to next column 4950 $this->selectColumn($this->current_column + 1); 4951 } elseif ($this->AutoPageBreak) { 4952 // add a new page 4953 $this->AddPage(); 4954 // set first column 4955 $this->selectColumn(0); 4956 } 4957 // avoid page breaking from checkPageBreak() 4958 return false; 4959 } 4960 return $this->AutoPageBreak; 4961 } 4962 4963 /** 4964 * Add page if needed. 4965 * @param $h (float) Cell height. Default value: 0. 4966 * @param $y (mixed) starting y position, leave empty for current position. 4967 * @param $addpage (boolean) if true add a page, otherwise only return the true/false state 4968 * @return boolean true in case of page break, false otherwise. 4969 * @since 3.2.000 (2008-07-01) 4970 * @protected 4971 */ 4972 protected function checkPageBreak($h=0, $y='', $addpage=true) { 4973 if (TCPDF_STATIC::empty_string($y)) { 4974 $y = $this->y; 4975 } 4976 $current_page = $this->page; 4977 if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) { 4978 if ($addpage) { 4979 //Automatic page break 4980 $x = $this->x; 4981 $this->AddPage($this->CurOrientation); 4982 $this->y = $this->tMargin; 4983 $oldpage = $this->page - 1; 4984 if ($this->rtl) { 4985 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) { 4986 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']); 4987 } else { 4988 $this->x = $x; 4989 } 4990 } else { 4991 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 4992 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']); 4993 } else { 4994 $this->x = $x; 4995 } 4996 } 4997 } 4998 return true; 4999 } 5000 if ($current_page != $this->page) { 5001 // account for columns mode 5002 return true; 5003 } 5004 return false; 5005 } 5006 5007 /** 5008 * 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 /> 5009 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 5010 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 5011 * @param $h (float) Cell height. Default value: 0. 5012 * @param $txt (string) String to print. Default value: empty string. 5013 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 5014 * @param $ln (int) 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. 5015 * @param $align (string) 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> 5016 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 5017 * @param $link (mixed) URL or identifier returned by AddLink(). 5018 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. 5019 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 5020 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul> 5021 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul> 5022 * @public 5023 * @since 1.0 5024 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() 5025 */ 5026 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') { 5027 $prev_cell_margin = $this->cell_margin; 5028 $prev_cell_padding = $this->cell_padding; 5029 $this->adjustCellPadding($border); 5030 if (!$ignore_min_height) { 5031 $min_cell_height = $this->getCellHeight($this->FontSize); 5032 if ($h < $min_cell_height) { 5033 $h = $min_cell_height; 5034 } 5035 } 5036 $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']); 5037 // apply text shadow if enabled 5038 if ($this->txtshadow['enabled']) { 5039 // save data 5040 $x = $this->x; 5041 $y = $this->y; 5042 $bc = $this->bgcolor; 5043 $fc = $this->fgcolor; 5044 $sc = $this->strokecolor; 5045 $alpha = $this->alpha; 5046 // print shadow 5047 $this->x += $this->txtshadow['depth_w']; 5048 $this->y += $this->txtshadow['depth_h']; 5049 $this->SetFillColorArray($this->txtshadow['color']); 5050 $this->SetTextColorArray($this->txtshadow['color']); 5051 $this->SetDrawColorArray($this->txtshadow['color']); 5052 if ($this->txtshadow['opacity'] != $alpha['CA']) { 5053 $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']); 5054 } 5055 if ($this->state == 2) { 5056 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); 5057 } 5058 //restore data 5059 $this->x = $x; 5060 $this->y = $y; 5061 $this->SetFillColorArray($bc); 5062 $this->SetTextColorArray($fc); 5063 $this->SetDrawColorArray($sc); 5064 if ($this->txtshadow['opacity'] != $alpha['CA']) { 5065 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']); 5066 } 5067 } 5068 if ($this->state == 2) { 5069 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); 5070 } 5071 $this->cell_padding = $prev_cell_padding; 5072 $this->cell_margin = $prev_cell_margin; 5073 } 5074 5075 /** 5076 * 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 /> 5077 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 5078 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 5079 * @param $h (float) Cell height. Default value: 0. 5080 * @param $txt (string) String to print. Default value: empty string. 5081 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 5082 * @param $ln (int) 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. 5083 * @param $align (string) 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> 5084 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 5085 * @param $link (mixed) URL or identifier returned by AddLink(). 5086 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. 5087 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 5088 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul> 5089 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>M : middle</li><li>B : bottom</li></ul> 5090 * @return string containing cell code 5091 * @protected 5092 * @since 1.0 5093 * @see Cell() 5094 */ 5095 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') { 5096 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space 5097 $txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt); 5098 $prev_cell_margin = $this->cell_margin; 5099 $prev_cell_padding = $this->cell_padding; 5100 $txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode); 5101 $rs = ''; //string to be returned 5102 $this->adjustCellPadding($border); 5103 if (!$ignore_min_height) { 5104 $min_cell_height = $this->getCellHeight($this->FontSize); 5105 if ($h < $min_cell_height) { 5106 $h = $min_cell_height; 5107 } 5108 } 5109 $k = $this->k; 5110 // check page for no-write regions and adapt page margins if necessary 5111 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); 5112 if ($this->rtl) { 5113 $x = $this->x - $this->cell_margin['R']; 5114 } else { 5115 $x = $this->x + $this->cell_margin['L']; 5116 } 5117 $y = $this->y + $this->cell_margin['T']; 5118 $prev_font_stretching = $this->font_stretching; 5119 $prev_font_spacing = $this->font_spacing; 5120 // cell vertical alignment 5121 switch ($calign) { 5122 case 'A': { 5123 // font top 5124 switch ($valign) { 5125 case 'T': { 5126 // top 5127 $y -= $this->cell_padding['T']; 5128 break; 5129 } 5130 case 'B': { 5131 // bottom 5132 $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent); 5133 break; 5134 } 5135 default: 5136 case 'C': 5137 case 'M': { 5138 // center 5139 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2); 5140 break; 5141 } 5142 } 5143 break; 5144 } 5145 case 'L': { 5146 // font baseline 5147 switch ($valign) { 5148 case 'T': { 5149 // top 5150 $y -= ($this->cell_padding['T'] + $this->FontAscent); 5151 break; 5152 } 5153 case 'B': { 5154 // bottom 5155 $y -= ($h - $this->cell_padding['B'] - $this->FontDescent); 5156 break; 5157 } 5158 default: 5159 case 'C': 5160 case 'M': { 5161 // center 5162 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2); 5163 break; 5164 } 5165 } 5166 break; 5167 } 5168 case 'D': { 5169 // font bottom 5170 switch ($valign) { 5171 case 'T': { 5172 // top 5173 $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent); 5174 break; 5175 } 5176 case 'B': { 5177 // bottom 5178 $y -= ($h - $this->cell_padding['B']); 5179 break; 5180 } 5181 default: 5182 case 'C': 5183 case 'M': { 5184 // center 5185 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2); 5186 break; 5187 } 5188 } 5189 break; 5190 } 5191 case 'B': { 5192 // cell bottom 5193 $y -= $h; 5194 break; 5195 } 5196 case 'C': 5197 case 'M': { 5198 // cell center 5199 $y -= ($h / 2); 5200 break; 5201 } 5202 default: 5203 case 'T': { 5204 // cell top 5205 break; 5206 } 5207 } 5208 // text vertical alignment 5209 switch ($valign) { 5210 case 'T': { 5211 // top 5212 $yt = $y + $this->cell_padding['T']; 5213 break; 5214 } 5215 case 'B': { 5216 // bottom 5217 $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent; 5218 break; 5219 } 5220 default: 5221 case 'C': 5222 case 'M': { 5223 // center 5224 $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2); 5225 break; 5226 } 5227 } 5228 $basefonty = $yt + $this->FontAscent; 5229 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 5230 if ($this->rtl) { 5231 $w = $x - $this->lMargin; 5232 } else { 5233 $w = $this->w - $this->rMargin - $x; 5234 } 5235 } 5236 $s = ''; 5237 // fill and borders 5238 if (is_string($border) AND (strlen($border) == 4)) { 5239 // full border 5240 $border = 1; 5241 } 5242 if ($fill OR ($border == 1)) { 5243 if ($fill) { 5244 $op = ($border == 1) ? 'B' : 'f'; 5245 } else { 5246 $op = 'S'; 5247 } 5248 if ($this->rtl) { 5249 $xk = (($x - $w) * $k); 5250 } else { 5251 $xk = ($x * $k); 5252 } 5253 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op); 5254 } 5255 // draw borders 5256 $s .= $this->getCellBorder($x, $y, $w, $h, $border); 5257 if ($txt != '') { 5258 $txt2 = $txt; 5259 if ($this->isunicode) { 5260 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 5261 $txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont); 5262 } else { 5263 $unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values 5264 $unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont); 5265 // replace thai chars (if any) 5266 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) { 5267 // number of chars 5268 $numchars = count($unicode); 5269 // po pla, for far, for fan 5270 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f); 5271 // do chada, to patak 5272 $lowtail = array(0x0e0e, 0x0e0f); 5273 // mai hun arkad, sara i, sara ii, sara ue, sara uee 5274 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37); 5275 // mai ek, mai tho, mai tri, mai chattawa, karan 5276 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c); 5277 // sara u, sara uu, pinthu 5278 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a); 5279 $output = array(); 5280 for ($i = 0; $i < $numchars; $i++) { 5281 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) { 5282 $ch0 = $unicode[$i]; 5283 $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0; 5284 $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0; 5285 $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0; 5286 if (in_array($ch0, $tonemark)) { 5287 if ($chn == 0x0e33) { 5288 // sara um 5289 if (in_array($ch1, $longtail)) { 5290 // tonemark at upper left 5291 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); 5292 } else { 5293 // tonemark at upper right (normal position) 5294 $output[] = $ch0; 5295 } 5296 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) { 5297 // tonemark at lower left 5298 $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48)); 5299 } elseif (in_array($ch1, $upvowel)) { 5300 if (in_array($ch2, $longtail)) { 5301 // tonemark at upper left 5302 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); 5303 } else { 5304 // tonemark at upper right (normal position) 5305 $output[] = $ch0; 5306 } 5307 } else { 5308 // tonemark at lower right 5309 $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48)); 5310 } 5311 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) { 5312 // add lower left nikhahit and sara aa 5313 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) { 5314 $output[] = 0xf711; 5315 $this->CurrentFont['subsetchars'][0xf711] = true; 5316 $output[] = 0x0e32; 5317 $this->CurrentFont['subsetchars'][0x0e32] = true; 5318 } else { 5319 $output[] = $ch0; 5320 } 5321 } elseif (in_array($ch1, $longtail)) { 5322 if ($ch0 == 0x0e31) { 5323 // lower left mai hun arkad 5324 $output[] = $this->replaceChar($ch0, 0xf710); 5325 } elseif (in_array($ch0, $upvowel)) { 5326 // lower left 5327 $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34)); 5328 } elseif ($ch0 == 0x0e47) { 5329 // lower left mai tai koo 5330 $output[] = $this->replaceChar($ch0, 0xf712); 5331 } else { 5332 // normal character 5333 $output[] = $ch0; 5334 } 5335 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) { 5336 // lower vowel 5337 $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38)); 5338 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) { 5339 // yo ying without lower part 5340 $output[] = $this->replaceChar($ch0, 0xf70f); 5341 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) { 5342 // tho santan without lower part 5343 $output[] = $this->replaceChar($ch0, 0xf700); 5344 } else { 5345 $output[] = $ch0; 5346 } 5347 } else { 5348 // non-thai character 5349 $output[] = $unicode[$i]; 5350 } 5351 } 5352 $unicode = $output; 5353 // update font subsetchars 5354 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 5355 } // end of K_THAI_TOPCHARS 5356 $txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false); 5357 } 5358 } 5359 $txt2 = TCPDF_STATIC::_escape($txt2); 5360 // get current text width (considering general font stretching and spacing) 5361 $txwidth = $this->GetStringWidth($txt); 5362 $width = $txwidth; 5363 // check for stretch mode 5364 if ($stretch > 0) { 5365 // calculate ratio between cell width and text width 5366 if ($width <= 0) { 5367 $ratio = 1; 5368 } else { 5369 $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width); 5370 } 5371 // check if stretching is required 5372 if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) { 5373 // the text will be stretched to fit cell width 5374 if ($stretch > 2) { 5375 // set new character spacing 5376 $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100)); 5377 } else { 5378 // set new horizontal stretching 5379 $this->font_stretching *= $ratio; 5380 } 5381 // recalculate text width (the text fills the entire cell) 5382 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 5383 // reset alignment 5384 $align = ''; 5385 } 5386 } 5387 if ($this->font_stretching != 100) { 5388 // apply font stretching 5389 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching); 5390 } 5391 if ($this->font_spacing != 0) { 5392 // increase/decrease font spacing 5393 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k)); 5394 } 5395 if ($this->ColorFlag AND ($this->textrendermode < 4)) { 5396 $s .= 'q '.$this->TextColor.' '; 5397 } 5398 // rendering mode 5399 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k)); 5400 // count number of spaces 5401 $ns = substr_count($txt, chr(32)); 5402 // Justification 5403 $spacewidth = 0; 5404 if (($align == 'J') AND ($ns > 0)) { 5405 if ($this->isUnicodeFont()) { 5406 // get string width without spaces 5407 $width = $this->GetStringWidth(str_replace(' ', '', $txt)); 5408 // calculate average space width 5409 $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1); 5410 if ($this->font_stretching != 100) { 5411 // word spacing is affected by stretching 5412 $spacewidth /= ($this->font_stretching / 100); 5413 } 5414 // set word position to be used with TJ operator 5415 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2); 5416 $unicode_justification = true; 5417 } else { 5418 // get string width 5419 $width = $txwidth; 5420 // new space width 5421 $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k; 5422 if ($this->font_stretching != 100) { 5423 // word spacing (Tw) is affected by stretching 5424 $spacewidth /= ($this->font_stretching / 100); 5425 } 5426 // set word spacing 5427 $rs .= sprintf('BT %F Tw ET ', $spacewidth); 5428 } 5429 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 5430 } 5431 // replace carriage return characters 5432 $txt2 = str_replace("\r", ' ', $txt2); 5433 switch ($align) { 5434 case 'C': { 5435 $dx = ($w - $width) / 2; 5436 break; 5437 } 5438 case 'R': { 5439 if ($this->rtl) { 5440 $dx = $this->cell_padding['R']; 5441 } else { 5442 $dx = $w - $width - $this->cell_padding['R']; 5443 } 5444 break; 5445 } 5446 case 'L': { 5447 if ($this->rtl) { 5448 $dx = $w - $width - $this->cell_padding['L']; 5449 } else { 5450 $dx = $this->cell_padding['L']; 5451 } 5452 break; 5453 } 5454 case 'J': 5455 default: { 5456 if ($this->rtl) { 5457 $dx = $this->cell_padding['R']; 5458 } else { 5459 $dx = $this->cell_padding['L']; 5460 } 5461 break; 5462 } 5463 } 5464 if ($this->rtl) { 5465 $xdx = $x - $dx - $width; 5466 } else { 5467 $xdx = $x + $dx; 5468 } 5469 $xdk = $xdx * $k; 5470 // print text 5471 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2); 5472 if (isset($uniblock)) { 5473 // print overlapping characters as separate string 5474 $xshift = 0; // horizontal shift 5475 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k); 5476 $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)); 5477 foreach ($uniblock as $uk => $uniarr) { 5478 if (($uk % 2) == 0) { 5479 // x space to skip 5480 if ($spacewidth != 0) { 5481 // justification shift 5482 $xshift += (count(array_keys($uniarr, 32)) * $spw); 5483 } 5484 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification 5485 } else { 5486 // character to print 5487 $topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false); 5488 $topchr = TCPDF_STATIC::_escape($topchr); 5489 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr); 5490 } 5491 } 5492 } 5493 if ($this->underline) { 5494 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width); 5495 } 5496 if ($this->linethrough) { 5497 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width); 5498 } 5499 if ($this->overline) { 5500 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width); 5501 } 5502 if ($this->ColorFlag AND ($this->textrendermode < 4)) { 5503 $s .= ' Q'; 5504 } 5505 if ($link) { 5506 $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns); 5507 } 5508 } 5509 // output cell 5510 if ($s) { 5511 // output cell 5512 $rs .= $s; 5513 if ($this->font_spacing != 0) { 5514 // reset font spacing mode 5515 $rs .= ' BT 0 Tc ET'; 5516 } 5517 if ($this->font_stretching != 100) { 5518 // reset font stretching mode 5519 $rs .= ' BT 100 Tz ET'; 5520 } 5521 } 5522 // reset word spacing 5523 if (!$this->isUnicodeFont() AND ($align == 'J')) { 5524 $rs .= ' BT 0 Tw ET'; 5525 } 5526 // reset stretching and spacing 5527 $this->font_stretching = $prev_font_stretching; 5528 $this->font_spacing = $prev_font_spacing; 5529 $this->lasth = $h; 5530 if ($ln > 0) { 5531 //Go to the beginning of the next line 5532 $this->y = $y + $h + $this->cell_margin['B']; 5533 if ($ln == 1) { 5534 if ($this->rtl) { 5535 $this->x = $this->w - $this->rMargin; 5536 } else { 5537 $this->x = $this->lMargin; 5538 } 5539 } 5540 } else { 5541 // go left or right by case 5542 if ($this->rtl) { 5543 $this->x = $x - $w - $this->cell_margin['L']; 5544 } else { 5545 $this->x = $x + $w + $this->cell_margin['R']; 5546 } 5547 } 5548 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n"; 5549 $rs = $gstyles.$rs; 5550 $this->cell_padding = $prev_cell_padding; 5551 $this->cell_margin = $prev_cell_margin; 5552 return $rs; 5553 } 5554 5555 /** 5556 * Replace a char if is defined on the current font. 5557 * @param $oldchar (int) Integer code (unicode) of the character to replace. 5558 * @param $newchar (int) Integer code (unicode) of the new character. 5559 * @return int the replaced char or the old char in case the new char i not defined 5560 * @protected 5561 * @since 5.9.167 (2012-06-22) 5562 */ 5563 protected function replaceChar($oldchar, $newchar) { 5564 if ($this->isCharDefined($newchar)) { 5565 // add the new char on the subset list 5566 $this->CurrentFont['subsetchars'][$newchar] = true; 5567 // return the new character 5568 return $newchar; 5569 } 5570 // return the old char 5571 return $oldchar; 5572 } 5573 5574 /** 5575 * Returns the code to draw the cell border 5576 * @param $x (float) X coordinate. 5577 * @param $y (float) Y coordinate. 5578 * @param $w (float) Cell width. 5579 * @param $h (float) Cell height. 5580 * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 5581 * @return string containing cell border code 5582 * @protected 5583 * @see SetLineStyle() 5584 * @since 5.7.000 (2010-08-02) 5585 */ 5586 protected function getCellBorder($x, $y, $w, $h, $brd) { 5587 $s = ''; // string to be returned 5588 if (empty($brd)) { 5589 return $s; 5590 } 5591 if ($brd == 1) { 5592 $brd = array('LRTB' => true); 5593 } 5594 // calculate coordinates for border 5595 $k = $this->k; 5596 if ($this->rtl) { 5597 $xeL = ($x - $w) * $k; 5598 $xeR = $x * $k; 5599 } else { 5600 $xeL = $x * $k; 5601 $xeR = ($x + $w) * $k; 5602 } 5603 $yeL = (($this->h - ($y + $h)) * $k); 5604 $yeT = (($this->h - $y) * $k); 5605 $xeT = $xeL; 5606 $xeB = $xeR; 5607 $yeR = $yeT; 5608 $yeB = $yeL; 5609 if (is_string($brd)) { 5610 // convert string to array 5611 $slen = strlen($brd); 5612 $newbrd = array(); 5613 for ($i = 0; $i < $slen; ++$i) { 5614 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter'); 5615 } 5616 $brd = $newbrd; 5617 } 5618 if (isset($brd['mode'])) { 5619 $mode = $brd['mode']; 5620 unset($brd['mode']); 5621 } else { 5622 $mode = 'normal'; 5623 } 5624 foreach ($brd as $border => $style) { 5625 if (is_array($style) AND !empty($style)) { 5626 // apply border style 5627 $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '; 5628 $s .= $this->SetLineStyle($style, true)."\n"; 5629 } 5630 switch ($mode) { 5631 case 'ext': { 5632 $off = (($this->LineWidth / 2) * $k); 5633 $xL = $xeL - $off; 5634 $xR = $xeR + $off; 5635 $yT = $yeT + $off; 5636 $yL = $yeL - $off; 5637 $xT = $xL; 5638 $xB = $xR; 5639 $yR = $yT; 5640 $yB = $yL; 5641 $w += $this->LineWidth; 5642 $h += $this->LineWidth; 5643 break; 5644 } 5645 case 'int': { 5646 $off = ($this->LineWidth / 2) * $k; 5647 $xL = $xeL + $off; 5648 $xR = $xeR - $off; 5649 $yT = $yeT - $off; 5650 $yL = $yeL + $off; 5651 $xT = $xL; 5652 $xB = $xR; 5653 $yR = $yT; 5654 $yB = $yL; 5655 $w -= $this->LineWidth; 5656 $h -= $this->LineWidth; 5657 break; 5658 } 5659 case 'normal': 5660 default: { 5661 $xL = $xeL; 5662 $xT = $xeT; 5663 $xB = $xeB; 5664 $xR = $xeR; 5665 $yL = $yeL; 5666 $yT = $yeT; 5667 $yB = $yeB; 5668 $yR = $yeR; 5669 break; 5670 } 5671 } 5672 // draw borders by case 5673 if (strlen($border) == 4) { 5674 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k)); 5675 } elseif (strlen($border) == 3) { 5676 if (strpos($border,'B') === false) { // LTR 5677 $s .= sprintf('%F %F m ', $xL, $yL); 5678 $s .= sprintf('%F %F l ', $xT, $yT); 5679 $s .= sprintf('%F %F l ', $xR, $yR); 5680 $s .= sprintf('%F %F l ', $xB, $yB); 5681 $s .= 'S '; 5682 } elseif (strpos($border,'L') === false) { // TRB 5683 $s .= sprintf('%F %F m ', $xT, $yT); 5684 $s .= sprintf('%F %F l ', $xR, $yR); 5685 $s .= sprintf('%F %F l ', $xB, $yB); 5686 $s .= sprintf('%F %F l ', $xL, $yL); 5687 $s .= 'S '; 5688 } elseif (strpos($border,'T') === false) { // RBL 5689 $s .= sprintf('%F %F m ', $xR, $yR); 5690 $s .= sprintf('%F %F l ', $xB, $yB); 5691 $s .= sprintf('%F %F l ', $xL, $yL); 5692 $s .= sprintf('%F %F l ', $xT, $yT); 5693 $s .= 'S '; 5694 } elseif (strpos($border,'R') === false) { // BLT 5695 $s .= sprintf('%F %F m ', $xB, $yB); 5696 $s .= sprintf('%F %F l ', $xL, $yL); 5697 $s .= sprintf('%F %F l ', $xT, $yT); 5698 $s .= sprintf('%F %F l ', $xR, $yR); 5699 $s .= 'S '; 5700 } 5701 } elseif (strlen($border) == 2) { 5702 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT 5703 $s .= sprintf('%F %F m ', $xL, $yL); 5704 $s .= sprintf('%F %F l ', $xT, $yT); 5705 $s .= sprintf('%F %F l ', $xR, $yR); 5706 $s .= 'S '; 5707 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR 5708 $s .= sprintf('%F %F m ', $xT, $yT); 5709 $s .= sprintf('%F %F l ', $xR, $yR); 5710 $s .= sprintf('%F %F l ', $xB, $yB); 5711 $s .= 'S '; 5712 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB 5713 $s .= sprintf('%F %F m ', $xR, $yR); 5714 $s .= sprintf('%F %F l ', $xB, $yB); 5715 $s .= sprintf('%F %F l ', $xL, $yL); 5716 $s .= 'S '; 5717 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL 5718 $s .= sprintf('%F %F m ', $xB, $yB); 5719 $s .= sprintf('%F %F l ', $xL, $yL); 5720 $s .= sprintf('%F %F l ', $xT, $yT); 5721 $s .= 'S '; 5722 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR 5723 $s .= sprintf('%F %F m ', $xL, $yL); 5724 $s .= sprintf('%F %F l ', $xT, $yT); 5725 $s .= 'S '; 5726 $s .= sprintf('%F %F m ', $xR, $yR); 5727 $s .= sprintf('%F %F l ', $xB, $yB); 5728 $s .= 'S '; 5729 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB 5730 $s .= sprintf('%F %F m ', $xT, $yT); 5731 $s .= sprintf('%F %F l ', $xR, $yR); 5732 $s .= 'S '; 5733 $s .= sprintf('%F %F m ', $xB, $yB); 5734 $s .= sprintf('%F %F l ', $xL, $yL); 5735 $s .= 'S '; 5736 } 5737 } else { // strlen($border) == 1 5738 if (strpos($border,'L') !== false) { // L 5739 $s .= sprintf('%F %F m ', $xL, $yL); 5740 $s .= sprintf('%F %F l ', $xT, $yT); 5741 $s .= 'S '; 5742 } elseif (strpos($border,'T') !== false) { // T 5743 $s .= sprintf('%F %F m ', $xT, $yT); 5744 $s .= sprintf('%F %F l ', $xR, $yR); 5745 $s .= 'S '; 5746 } elseif (strpos($border,'R') !== false) { // R 5747 $s .= sprintf('%F %F m ', $xR, $yR); 5748 $s .= sprintf('%F %F l ', $xB, $yB); 5749 $s .= 'S '; 5750 } elseif (strpos($border,'B') !== false) { // B 5751 $s .= sprintf('%F %F m ', $xB, $yB); 5752 $s .= sprintf('%F %F l ', $xL, $yL); 5753 $s .= 'S '; 5754 } 5755 } 5756 if (is_array($style) AND !empty($style)) { 5757 // reset border style to previous value 5758 $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n"; 5759 } 5760 } 5761 return $s; 5762 } 5763 5764 /** 5765 * This method allows printing text with line breaks. 5766 * 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 /> 5767 * Text can be aligned, centered or justified. The cell block can be framed and the background painted. 5768 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 5769 * @param $h (float) Cell minimum height. The cell extends automatically if needed. 5770 * @param $txt (string) String to print 5771 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 5772 * @param $align (string) 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> 5773 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 5774 * @param $ln (int) 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> 5775 * @param $x (float) x position in user units 5776 * @param $y (float) y position in user units 5777 * @param $reseth (boolean) if true reset the last cell height (default true). 5778 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. 5779 * @param $ishtml (boolean) INTERNAL USE ONLY -- set to true if $txt is HTML content (default = false). Never set this parameter to true, use instead writeHTMLCell() or writeHTML() methods. 5780 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width. 5781 * @param $maxh (float) 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. 5782 * @param $valign (string) Vertical alignment of text (requires $maxh = $h > 0). Possible values are:<ul><li>T: TOP</li><li>M: middle</li><li>B: bottom</li></ul>. This feature works only when $ishtml=false and the cell must fit in a single page. 5783 * @param $fitcell (boolean) if true attempt to fit all the text within the cell by reducing the font size (do not work in HTML mode). $maxh must be greater than 0 and wqual to $h. 5784 * @return int Return the number of cells or 1 for html mode. 5785 * @public 5786 * @since 1.3 5787 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() 5788 */ 5789 public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) { 5790 $prev_cell_margin = $this->cell_margin; 5791 $prev_cell_padding = $this->cell_padding; 5792 // adjust internal padding 5793 $this->adjustCellPadding($border); 5794 $mc_padding = $this->cell_padding; 5795 $mc_margin = $this->cell_margin; 5796 $this->cell_padding['T'] = 0; 5797 $this->cell_padding['B'] = 0; 5798 $this->setCellMargins(0, 0, 0, 0); 5799 if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) { 5800 // reset row height 5801 $this->resetLastH(); 5802 } 5803 if (!TCPDF_STATIC::empty_string($y)) { 5804 $this->SetY($y); 5805 } else { 5806 $y = $this->GetY(); 5807 } 5808 $resth = 0; 5809 if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) { 5810 // spit cell in more pages/columns 5811 $newh = ($this->PageBreakTrigger - $y); 5812 $resth = ($h - $newh); // cell to be printed on the next page/column 5813 $h = $newh; 5814 } 5815 // get current page number 5816 $startpage = $this->page; 5817 // get current column 5818 $startcolumn = $this->current_column; 5819 if (!TCPDF_STATIC::empty_string($x)) { 5820 $this->SetX($x); 5821 } else { 5822 $x = $this->GetX(); 5823 } 5824 // check page for no-write regions and adapt page margins if necessary 5825 list($x, $y) = $this->checkPageRegions(0, $x, $y); 5826 // apply margins 5827 $oy = $y + $mc_margin['T']; 5828 if ($this->rtl) { 5829 $ox = ($this->w - $x - $mc_margin['R']); 5830 } else { 5831 $ox = ($x + $mc_margin['L']); 5832 } 5833 $this->x = $ox; 5834 $this->y = $oy; 5835 // set width 5836 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 5837 if ($this->rtl) { 5838 $w = ($this->x - $this->lMargin - $mc_margin['L']); 5839 } else { 5840 $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']); 5841 } 5842 } 5843 // store original margin values 5844 $lMargin = $this->lMargin; 5845 $rMargin = $this->rMargin; 5846 if ($this->rtl) { 5847 $this->rMargin = ($this->w - $this->x); 5848 $this->lMargin = ($this->x - $w); 5849 } else { 5850 $this->lMargin = ($this->x); 5851 $this->rMargin = ($this->w - $this->x - $w); 5852 } 5853 $this->clMargin = $this->lMargin; 5854 $this->crMargin = $this->rMargin; 5855 if ($autopadding) { 5856 // add top padding 5857 $this->y += $mc_padding['T']; 5858 } 5859 if ($ishtml) { // ******* Write HTML text 5860 $this->writeHTML($txt, true, false, $reseth, true, $align); 5861 $nl = 1; 5862 } else { // ******* Write simple text 5863 $prev_FontSizePt = $this->FontSizePt; 5864 if ($fitcell) { 5865 // ajust height values 5866 $tobottom = ($this->h - $this->y - $this->bMargin - $this->cell_padding['T'] - $this->cell_padding['B']); 5867 $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom)); 5868 } 5869 // vertical alignment 5870 if ($maxh > 0) { 5871 // get text height 5872 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5873 if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt > 1)) { 5874 // try to reduce font size to fit text on cell (use a quick search algorithm) 5875 $fmin = 1; 5876 $fmax = $this->FontSizePt; 5877 $diff_epsilon = (1 / $this->k); // one point (min resolution) 5878 $maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations 5879 while ($maxit >= 0) { 5880 $fmid = (($fmax + $fmin) / 2); 5881 $this->SetFontSize($fmid, false); 5882 $this->resetLastH(); 5883 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5884 $diff = ($maxh - $text_height); 5885 if ($diff >= 0) { 5886 if ($diff <= $diff_epsilon) { 5887 break; 5888 } 5889 $fmin = $fmid; 5890 } else { 5891 $fmax = $fmid; 5892 } 5893 --$maxit; 5894 } 5895 if ($maxit < 0) { 5896 // premature exit, we get the minimum font value to fit the cell 5897 $this->SetFontSize($fmin); 5898 $this->resetLastH(); 5899 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5900 } else { 5901 $this->SetFontSize($fmid); 5902 $this->resetLastH(); 5903 } 5904 } 5905 if ($text_height < $maxh) { 5906 if ($valign == 'M') { 5907 // text vertically centered 5908 $this->y += (($maxh - $text_height) / 2); 5909 } elseif ($valign == 'B') { 5910 // text vertically aligned on bottom 5911 $this->y += ($maxh - $text_height); 5912 } 5913 } 5914 } 5915 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin); 5916 if ($fitcell) { 5917 // restore font size 5918 $this->SetFontSize($prev_FontSizePt); 5919 } 5920 } 5921 if ($autopadding) { 5922 // add bottom padding 5923 $this->y += $mc_padding['B']; 5924 } 5925 // Get end-of-text Y position 5926 $currentY = $this->y; 5927 // get latest page number 5928 $endpage = $this->page; 5929 if ($resth > 0) { 5930 $skip = ($endpage - $startpage); 5931 $tmpresth = $resth; 5932 while ($tmpresth > 0) { 5933 if ($skip <= 0) { 5934 // add a page (or trig AcceptPageBreak() for multicolumn mode) 5935 $this->checkPageBreak($this->PageBreakTrigger + 1); 5936 } 5937 if ($this->num_columns > 1) { 5938 $tmpresth -= ($this->h - $this->y - $this->bMargin); 5939 } else { 5940 $tmpresth -= ($this->h - $this->tMargin - $this->bMargin); 5941 } 5942 --$skip; 5943 } 5944 $currentY = $this->y; 5945 $endpage = $this->page; 5946 } 5947 // get latest column 5948 $endcolumn = $this->current_column; 5949 if ($this->num_columns == 0) { 5950 $this->num_columns = 1; 5951 } 5952 // disable page regions check 5953 $check_page_regions = $this->check_page_regions; 5954 $this->check_page_regions = false; 5955 // get border modes 5956 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 5957 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 5958 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 5959 // design borders around HTML cells. 5960 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 5961 $ccode = ''; 5962 $this->setPage($page); 5963 if ($this->num_columns < 2) { 5964 // single-column mode 5965 $this->SetX($x); 5966 $this->y = $this->tMargin; 5967 } 5968 // account for margin changes 5969 if ($page > $startpage) { 5970 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 5971 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 5972 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 5973 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 5974 } 5975 } 5976 if ($startpage == $endpage) { 5977 // single page 5978 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 5979 $this->selectColumn($column); 5980 if ($this->rtl) { 5981 $this->x -= $mc_margin['R']; 5982 } else { 5983 $this->x += $mc_margin['L']; 5984 } 5985 if ($startcolumn == $endcolumn) { // single column 5986 $cborder = $border; 5987 $h = max($h, ($currentY - $oy)); 5988 $this->y = $oy; 5989 } elseif ($column == $startcolumn) { // first column 5990 $cborder = $border_start; 5991 $this->y = $oy; 5992 $h = $this->h - $this->y - $this->bMargin; 5993 } elseif ($column == $endcolumn) { // end column 5994 $cborder = $border_end; 5995 $h = $currentY - $this->y; 5996 if ($resth > $h) { 5997 $h = $resth; 5998 } 5999 } else { // middle column 6000 $cborder = $border_middle; 6001 $h = $this->h - $this->y - $this->bMargin; 6002 $resth -= $h; 6003 } 6004 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6005 } // end for each column 6006 } elseif ($page == $startpage) { // first page 6007 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 6008 $this->selectColumn($column); 6009 if ($this->rtl) { 6010 $this->x -= $mc_margin['R']; 6011 } else { 6012 $this->x += $mc_margin['L']; 6013 } 6014 if ($column == $startcolumn) { // first column 6015 $cborder = $border_start; 6016 $this->y = $oy; 6017 $h = $this->h - $this->y - $this->bMargin; 6018 } else { // middle column 6019 $cborder = $border_middle; 6020 $h = $this->h - $this->y - $this->bMargin; 6021 $resth -= $h; 6022 } 6023 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6024 } // end for each column 6025 } elseif ($page == $endpage) { // last page 6026 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 6027 $this->selectColumn($column); 6028 if ($this->rtl) { 6029 $this->x -= $mc_margin['R']; 6030 } else { 6031 $this->x += $mc_margin['L']; 6032 } 6033 if ($column == $endcolumn) { 6034 // end column 6035 $cborder = $border_end; 6036 $h = $currentY - $this->y; 6037 if ($resth > $h) { 6038 $h = $resth; 6039 } 6040 } else { 6041 // middle column 6042 $cborder = $border_middle; 6043 $h = $this->h - $this->y - $this->bMargin; 6044 $resth -= $h; 6045 } 6046 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6047 } // end for each column 6048 } else { // middle page 6049 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 6050 $this->selectColumn($column); 6051 if ($this->rtl) { 6052 $this->x -= $mc_margin['R']; 6053 } else { 6054 $this->x += $mc_margin['L']; 6055 } 6056 $cborder = $border_middle; 6057 $h = $this->h - $this->y - $this->bMargin; 6058 $resth -= $h; 6059 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6060 } // end for each column 6061 } 6062 if ($cborder OR $fill) { 6063 $offsetlen = strlen($ccode); 6064 // draw border and fill 6065 if ($this->inxobj) { 6066 // we are inside an XObject template 6067 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 6068 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 6069 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 6070 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 6071 } else { 6072 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 6073 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 6074 } 6075 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 6076 $pstart = substr($pagebuff, 0, $pagemark); 6077 $pend = substr($pagebuff, $pagemark); 6078 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 6079 } else { 6080 if (end($this->transfmrk[$this->page]) !== false) { 6081 $pagemarkkey = key($this->transfmrk[$this->page]); 6082 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 6083 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; 6084 } elseif ($this->InFooter) { 6085 $pagemark = $this->footerpos[$this->page]; 6086 $this->footerpos[$this->page] += $offsetlen; 6087 } else { 6088 $pagemark = $this->intmrk[$this->page]; 6089 $this->intmrk[$this->page] += $offsetlen; 6090 } 6091 $pagebuff = $this->getPageBuffer($this->page); 6092 $pstart = substr($pagebuff, 0, $pagemark); 6093 $pend = substr($pagebuff, $pagemark); 6094 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 6095 } 6096 } 6097 } // end for each page 6098 // restore page regions check 6099 $this->check_page_regions = $check_page_regions; 6100 // Get end-of-cell Y position 6101 $currentY = $this->GetY(); 6102 // restore previous values 6103 if ($this->num_columns > 1) { 6104 $this->selectColumn(); 6105 } else { 6106 // restore original margins 6107 $this->lMargin = $lMargin; 6108 $this->rMargin = $rMargin; 6109 if ($this->page > $startpage) { 6110 // check for margin variations between pages (i.e. booklet mode) 6111 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']); 6112 $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']); 6113 if (($dl != 0) OR ($dr != 0)) { 6114 $this->lMargin += $dl; 6115 $this->rMargin += $dr; 6116 } 6117 } 6118 } 6119 if ($ln > 0) { 6120 //Go to the beginning of the next line 6121 $this->SetY($currentY + $mc_margin['B']); 6122 if ($ln == 2) { 6123 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); 6124 } 6125 } else { 6126 // go left or right by case 6127 $this->setPage($startpage); 6128 $this->y = $y; 6129 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); 6130 } 6131 $this->setContentMark(); 6132 $this->cell_padding = $prev_cell_padding; 6133 $this->cell_margin = $prev_cell_margin; 6134 $this->clMargin = $this->lMargin; 6135 $this->crMargin = $this->rMargin; 6136 return $nl; 6137 } 6138 6139 /** 6140 * This method return the estimated number of lines for print a simple text string using Multicell() method. 6141 * @param $txt (string) String for calculating his height 6142 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 6143 * @param $reseth (boolean) if true reset the last cell height (default false). 6144 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true). 6145 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding. 6146 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 6147 * @return float Return the minimal height needed for multicell method for printing the $txt param. 6148 * @author Alexander Escalona Fern\E1ndez, Nicola Asuni 6149 * @public 6150 * @since 4.5.011 6151 */ 6152 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { 6153 if ($txt === NULL) { 6154 return 0; 6155 } 6156 if ($txt === '') { 6157 // empty string 6158 return 1; 6159 } 6160 // adjust internal padding 6161 $prev_cell_padding = $this->cell_padding; 6162 $prev_lasth = $this->lasth; 6163 if (is_array($cellpadding)) { 6164 $this->cell_padding = $cellpadding; 6165 } 6166 $this->adjustCellPadding($border); 6167 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 6168 if ($this->rtl) { 6169 $w = $this->x - $this->lMargin; 6170 } else { 6171 $w = $this->w - $this->rMargin - $this->x; 6172 } 6173 } 6174 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 6175 if ($reseth) { 6176 // reset row height 6177 $this->resetLastH(); 6178 } 6179 $lines = 1; 6180 $sum = 0; 6181 $chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6182 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true); 6183 $length = count($chars); 6184 $lastSeparator = -1; 6185 for ($i = 0; $i < $length; ++$i) { 6186 $c = $chars[$i]; 6187 $charWidth = $charsWidth[$i]; 6188 if (($c != 160) 6189 AND (($c == 173) 6190 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode)) 6191 OR (($c == 45) 6192 AND ($i > 0) AND ($i < ($length - 1)) 6193 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode)) 6194 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode)) 6195 ) 6196 ) 6197 ) { 6198 $lastSeparator = $i; 6199 } 6200 if ((($sum + $charWidth) > $wmax) OR ($c == 10)) { 6201 ++$lines; 6202 if ($c == 10) { 6203 $lastSeparator = -1; 6204 $sum = 0; 6205 } elseif ($lastSeparator != -1) { 6206 $i = $lastSeparator; 6207 $lastSeparator = -1; 6208 $sum = 0; 6209 } else { 6210 $sum = $charWidth; 6211 } 6212 } else { 6213 $sum += $charWidth; 6214 } 6215 } 6216 if ($chars[($length - 1)] == 10) { 6217 --$lines; 6218 } 6219 $this->cell_padding = $prev_cell_padding; 6220 $this->lasth = $prev_lasth; 6221 return $lines; 6222 } 6223 6224 /** 6225 * This method return the estimated height needed for printing a simple text string using the Multicell() method. 6226 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique: 6227 * @pre 6228 * // store current object 6229 * $pdf->startTransaction(); 6230 * // store starting values 6231 * $start_y = $pdf->GetY(); 6232 * $start_page = $pdf->getPage(); 6233 * // call your printing functions with your parameters 6234 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6235 * $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0); 6236 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6237 * // get the new Y 6238 * $end_y = $pdf->GetY(); 6239 * $end_page = $pdf->getPage(); 6240 * // calculate height 6241 * $height = 0; 6242 * if ($end_page == $start_page) { 6243 * $height = $end_y - $start_y; 6244 * } else { 6245 * for ($page=$start_page; $page <= $end_page; ++$page) { 6246 * $this->setPage($page); 6247 * if ($page == $start_page) { 6248 * // first page 6249 * $height = $this->h - $start_y - $this->bMargin; 6250 * } elseif ($page == $end_page) { 6251 * // last page 6252 * $height = $end_y - $this->tMargin; 6253 * } else { 6254 * $height = $this->h - $this->tMargin - $this->bMargin; 6255 * } 6256 * } 6257 * } 6258 * // restore previous object 6259 * $pdf = $pdf->rollbackTransaction(); 6260 * 6261 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 6262 * @param $txt (string) String for calculating his height 6263 * @param $reseth (boolean) if true reset the last cell height (default false). 6264 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true). 6265 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding. 6266 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 6267 * @return float Return the minimal height needed for multicell method for printing the $txt param. 6268 * @author Nicola Asuni, Alexander Escalona Fern\E1ndez 6269 * @public 6270 */ 6271 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { 6272 // adjust internal padding 6273 $prev_cell_padding = $this->cell_padding; 6274 $prev_lasth = $this->lasth; 6275 if (is_array($cellpadding)) { 6276 $this->cell_padding = $cellpadding; 6277 } 6278 $this->adjustCellPadding($border); 6279 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border); 6280 $height = $this->getCellHeight(($lines * $this->FontSize), $autopadding); 6281 $this->cell_padding = $prev_cell_padding; 6282 $this->lasth = $prev_lasth; 6283 return $height; 6284 } 6285 6286 /** 6287 * This method prints text from the current position.<br /> 6288 * @param $h (float) Line height 6289 * @param $txt (string) String to print 6290 * @param $link (mixed) URL or identifier returned by AddLink() 6291 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 6292 * @param $align (string) 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> 6293 * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line. 6294 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. 6295 * @param $firstline (boolean) if true prints only the first line and return the remaining string. 6296 * @param $firstblock (boolean) if true the string is the starting of a line. 6297 * @param $maxh (float) maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. 6298 * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode). 6299 * @param $margin (array) margin array of the parent container 6300 * @return mixed Return the number of cells or the remaining string if $firstline = true. 6301 * @public 6302 * @since 1.5 6303 */ 6304 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') { 6305 // check page for no-write regions and adapt page margins if necessary 6306 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); 6307 if (strlen($txt) == 0) { 6308 // fix empty text 6309 $txt = ' '; 6310 } 6311 if ($margin === '') { 6312 // set default margins 6313 $margin = $this->cell_margin; 6314 } 6315 // remove carriage returns 6316 $s = str_replace("\r", '', $txt); 6317 // check if string contains arabic text 6318 if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) { 6319 $arabic = true; 6320 } else { 6321 $arabic = false; 6322 } 6323 // check if string contains RTL text 6324 if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) { 6325 $rtlmode = true; 6326 } else { 6327 $rtlmode = false; 6328 } 6329 // get a char width 6330 $chrwidth = $this->GetCharWidth(46); // dot character 6331 // get array of unicode values 6332 $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont); 6333 // calculate maximum width for a single character on string 6334 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true); 6335 array_walk($chrw, array($this, 'getRawCharWidth')); 6336 $maxchwidth = max($chrw); 6337 // get array of chars 6338 $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode); 6339 // get the number of characters 6340 $nb = count($chars); 6341 // replacement for SHY character (minus symbol) 6342 $shy_replacement = 45; 6343 $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode); 6344 // widht for SHY replacement 6345 $shy_replacement_width = $this->GetCharWidth($shy_replacement); 6346 // page width 6347 $pw = $w = $this->w - $this->lMargin - $this->rMargin; 6348 // calculate remaining line width ($w) 6349 if ($this->rtl) { 6350 $w = $this->x - $this->lMargin; 6351 } else { 6352 $w = $this->w - $this->rMargin - $this->x; 6353 } 6354 // max column width 6355 $wmax = ($w - $wadj); 6356 if (!$firstline) { 6357 $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']); 6358 } 6359 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) { 6360 // the maximum width character do not fit on column 6361 return ''; 6362 } 6363 // minimum row height 6364 $row_height = max($h, $this->getCellHeight($this->FontSize)); 6365 // max Y 6366 $maxy = $this->y + $maxh - max($row_height, $h); 6367 $start_page = $this->page; 6368 $i = 0; // character position 6369 $j = 0; // current starting position 6370 $sep = -1; // position of the last blank space 6371 $prevsep = $sep; // previous separator 6372 $shy = false; // true if the last blank is a soft hypen (SHY) 6373 $prevshy = $shy; // previous shy mode 6374 $l = 0; // current string length 6375 $nl = 0; //number of lines 6376 $linebreak = false; 6377 $pc = 0; // previous character 6378 // for each character 6379 while ($i < $nb) { 6380 if (($maxh > 0) AND ($this->y > $maxy) ) { 6381 break; 6382 } 6383 //Get the current character 6384 $c = $chars[$i]; 6385 if ($c == 10) { // 10 = "\n" = new line 6386 //Explicit line break 6387 if ($align == 'J') { 6388 if ($this->rtl) { 6389 $talign = 'R'; 6390 } else { 6391 $talign = 'L'; 6392 } 6393 } else { 6394 $talign = $align; 6395 } 6396 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6397 if ($firstline) { 6398 $startx = $this->x; 6399 $tmparr = array_slice($chars, $j, ($i - $j)); 6400 if ($rtlmode) { 6401 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6402 } 6403 $linew = $this->GetArrStringWidth($tmparr); 6404 unset($tmparr); 6405 if ($this->rtl) { 6406 $this->endlinex = $startx - $linew; 6407 } else { 6408 $this->endlinex = $startx + $linew; 6409 } 6410 $w = $linew; 6411 $tmpcellpadding = $this->cell_padding; 6412 if ($maxh == 0) { 6413 $this->SetCellPadding(0); 6414 } 6415 } 6416 if ($firstblock AND $this->isRTLTextDir()) { 6417 $tmpstr = $this->stringRightTrim($tmpstr); 6418 } 6419 // Skip newlines at the begining of a page or column 6420 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) { 6421 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch); 6422 } 6423 unset($tmpstr); 6424 if ($firstline) { 6425 $this->cell_padding = $tmpcellpadding; 6426 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6427 } 6428 ++$nl; 6429 $j = $i + 1; 6430 $l = 0; 6431 $sep = -1; 6432 $prevsep = $sep; 6433 $shy = false; 6434 // account for margin changes 6435 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { 6436 $this->AcceptPageBreak(); 6437 if ($this->rtl) { 6438 $this->x -= $margin['R']; 6439 } else { 6440 $this->x += $margin['L']; 6441 } 6442 $this->lMargin += $margin['L']; 6443 $this->rMargin += $margin['R']; 6444 } 6445 $w = $this->getRemainingWidth(); 6446 $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']); 6447 } else { 6448 // 160 is the non-breaking space. 6449 // 173 is SHY (Soft Hypen). 6450 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 6451 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 6452 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 6453 if (($c != 160) 6454 AND (($c == 173) 6455 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode)) 6456 OR (($c == 45) 6457 AND ($i < ($nb - 1)) 6458 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode)) 6459 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode)) 6460 ) 6461 ) 6462 ) { 6463 // update last blank space position 6464 $prevsep = $sep; 6465 $sep = $i; 6466 // check if is a SHY 6467 if (($c == 173) OR ($c == 45)) { 6468 $prevshy = $shy; 6469 $shy = true; 6470 if ($pc == 45) { 6471 $tmp_shy_replacement_width = 0; 6472 $tmp_shy_replacement_char = ''; 6473 } else { 6474 $tmp_shy_replacement_width = $shy_replacement_width; 6475 $tmp_shy_replacement_char = $shy_replacement_char; 6476 } 6477 } else { 6478 $shy = false; 6479 } 6480 } 6481 // update string length 6482 if ($this->isUnicodeFont() AND ($arabic)) { 6483 // with bidirectional algorithm some chars may be changed affecting the line length 6484 // *** very slow *** 6485 $l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont)); 6486 } else { 6487 $l += $this->GetCharWidth($c); 6488 } 6489 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) >= $wmax))) { 6490 if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) { 6491 $sep = $prevsep; 6492 $shy = $prevshy; 6493 } 6494 // we have reached the end of column 6495 if ($sep == -1) { 6496 // check if the line was already started 6497 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth))) 6498 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) { 6499 // print a void cell and go to next line 6500 $this->Cell($w, $h, '', 0, 1); 6501 $linebreak = true; 6502 if ($firstline) { 6503 return (TCPDF_FONTS::UniArrSubString($uchars, $j)); 6504 } 6505 } else { 6506 // truncate the word because do not fit on column 6507 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6508 if ($firstline) { 6509 $startx = $this->x; 6510 $tmparr = array_slice($chars, $j, ($i - $j)); 6511 if ($rtlmode) { 6512 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6513 } 6514 $linew = $this->GetArrStringWidth($tmparr); 6515 unset($tmparr); 6516 if ($this->rtl) { 6517 $this->endlinex = $startx - $linew; 6518 } else { 6519 $this->endlinex = $startx + $linew; 6520 } 6521 $w = $linew; 6522 $tmpcellpadding = $this->cell_padding; 6523 if ($maxh == 0) { 6524 $this->SetCellPadding(0); 6525 } 6526 } 6527 if ($firstblock AND $this->isRTLTextDir()) { 6528 $tmpstr = $this->stringRightTrim($tmpstr); 6529 } 6530 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 6531 unset($tmpstr); 6532 if ($firstline) { 6533 $this->cell_padding = $tmpcellpadding; 6534 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6535 } 6536 $j = $i; 6537 --$i; 6538 } 6539 } else { 6540 // word wrapping 6541 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) { 6542 $endspace = 1; 6543 } else { 6544 $endspace = 0; 6545 } 6546 // check the length of the next string 6547 $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)); 6548 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest)); 6549 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) { 6550 // truncate the word because do not fit on a full page width 6551 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6552 if ($firstline) { 6553 $startx = $this->x; 6554 $tmparr = array_slice($chars, $j, ($i - $j)); 6555 if ($rtlmode) { 6556 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6557 } 6558 $linew = $this->GetArrStringWidth($tmparr); 6559 unset($tmparr); 6560 if ($this->rtl) { 6561 $this->endlinex = ($startx - $linew); 6562 } else { 6563 $this->endlinex = ($startx + $linew); 6564 } 6565 $w = $linew; 6566 $tmpcellpadding = $this->cell_padding; 6567 if ($maxh == 0) { 6568 $this->SetCellPadding(0); 6569 } 6570 } 6571 if ($firstblock AND $this->isRTLTextDir()) { 6572 $tmpstr = $this->stringRightTrim($tmpstr); 6573 } 6574 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 6575 unset($tmpstr); 6576 if ($firstline) { 6577 $this->cell_padding = $tmpcellpadding; 6578 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6579 } 6580 $j = $i; 6581 --$i; 6582 } else { 6583 // word wrapping 6584 if ($shy) { 6585 // add hypen (minus symbol) at the end of the line 6586 $shy_width = $tmp_shy_replacement_width; 6587 if ($this->rtl) { 6588 $shy_char_left = $tmp_shy_replacement_char; 6589 $shy_char_right = ''; 6590 } else { 6591 $shy_char_left = ''; 6592 $shy_char_right = $tmp_shy_replacement_char; 6593 } 6594 } else { 6595 $shy_width = 0; 6596 $shy_char_left = ''; 6597 $shy_char_right = ''; 6598 } 6599 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace)); 6600 if ($firstline) { 6601 $startx = $this->x; 6602 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j)); 6603 if ($rtlmode) { 6604 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6605 } 6606 $linew = $this->GetArrStringWidth($tmparr); 6607 unset($tmparr); 6608 if ($this->rtl) { 6609 $this->endlinex = $startx - $linew - $shy_width; 6610 } else { 6611 $this->endlinex = $startx + $linew + $shy_width; 6612 } 6613 $w = $linew; 6614 $tmpcellpadding = $this->cell_padding; 6615 if ($maxh == 0) { 6616 $this->SetCellPadding(0); 6617 } 6618 } 6619 // print the line 6620 if ($firstblock AND $this->isRTLTextDir()) { 6621 $tmpstr = $this->stringRightTrim($tmpstr); 6622 } 6623 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch); 6624 unset($tmpstr); 6625 if ($firstline) { 6626 if ($chars[$sep] == 45) { 6627 $endspace += 1; 6628 } 6629 // return the remaining text 6630 $this->cell_padding = $tmpcellpadding; 6631 return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace))); 6632 } 6633 $i = $sep; 6634 $sep = -1; 6635 $shy = false; 6636 $j = ($i + 1); 6637 } 6638 } 6639 // account for margin changes 6640 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { 6641 $this->AcceptPageBreak(); 6642 if ($this->rtl) { 6643 $this->x -= $margin['R']; 6644 } else { 6645 $this->x += $margin['L']; 6646 } 6647 $this->lMargin += $margin['L']; 6648 $this->rMargin += $margin['R']; 6649 } 6650 $w = $this->getRemainingWidth(); 6651 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 6652 if ($linebreak) { 6653 $linebreak = false; 6654 } else { 6655 ++$nl; 6656 $l = 0; 6657 } 6658 } 6659 } 6660 // save last character 6661 $pc = $c; 6662 ++$i; 6663 } // end while i < nb 6664 // print last substring (if any) 6665 if ($l > 0) { 6666 switch ($align) { 6667 case 'J': 6668 case 'C': { 6669 $w = $w; 6670 break; 6671 } 6672 case 'L': { 6673 if ($this->rtl) { 6674 $w = $w; 6675 } else { 6676 $w = $l; 6677 } 6678 break; 6679 } 6680 case 'R': { 6681 if ($this->rtl) { 6682 $w = $l; 6683 } else { 6684 $w = $w; 6685 } 6686 break; 6687 } 6688 default: { 6689 $w = $l; 6690 break; 6691 } 6692 } 6693 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb); 6694 if ($firstline) { 6695 $startx = $this->x; 6696 $tmparr = array_slice($chars, $j, ($nb - $j)); 6697 if ($rtlmode) { 6698 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6699 } 6700 $linew = $this->GetArrStringWidth($tmparr); 6701 unset($tmparr); 6702 if ($this->rtl) { 6703 $this->endlinex = $startx - $linew; 6704 } else { 6705 $this->endlinex = $startx + $linew; 6706 } 6707 $w = $linew; 6708 $tmpcellpadding = $this->cell_padding; 6709 if ($maxh == 0) { 6710 $this->SetCellPadding(0); 6711 } 6712 } 6713 if ($firstblock AND $this->isRTLTextDir()) { 6714 $tmpstr = $this->stringRightTrim($tmpstr); 6715 } 6716 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch); 6717 unset($tmpstr); 6718 if ($firstline) { 6719 $this->cell_padding = $tmpcellpadding; 6720 return (TCPDF_FONTS::UniArrSubString($uchars, $nb)); 6721 } 6722 ++$nl; 6723 } 6724 if ($firstline) { 6725 return ''; 6726 } 6727 return $nl; 6728 } 6729 6730 /** 6731 * Returns the remaining width between the current position and margins. 6732 * @return int Return the remaining width 6733 * @protected 6734 */ 6735 protected function getRemainingWidth() { 6736 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y); 6737 if ($this->rtl) { 6738 return ($this->x - $this->lMargin); 6739 } else { 6740 return ($this->w - $this->rMargin - $this->x); 6741 } 6742 } 6743 6744 /** 6745 * Set the block dimensions accounting for page breaks and page/column fitting 6746 * @param $w (float) width 6747 * @param $h (float) height 6748 * @param $x (float) X coordinate 6749 * @param $y (float) Y coodiante 6750 * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions. 6751 * @return array($w, $h, $x, $y) 6752 * @protected 6753 * @since 5.5.009 (2010-07-05) 6754 */ 6755 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) { 6756 if ($w <= 0) { 6757 // set maximum width 6758 $w = ($this->w - $this->lMargin - $this->rMargin); 6759 if ($w <= 0) { 6760 $w = 1; 6761 } 6762 } 6763 if ($h <= 0) { 6764 // set maximum height 6765 $h = ($this->PageBreakTrigger - $this->tMargin); 6766 if ($h <= 0) { 6767 $h = 1; 6768 } 6769 } 6770 // resize the block to be vertically contained on a single page or single column 6771 if ($fitonpage OR $this->AutoPageBreak) { 6772 $ratio_wh = ($w / $h); 6773 if ($h > ($this->PageBreakTrigger - $this->tMargin)) { 6774 $h = $this->PageBreakTrigger - $this->tMargin; 6775 $w = ($h * $ratio_wh); 6776 } 6777 // resize the block to be horizontally contained on a single page or single column 6778 if ($fitonpage) { 6779 $maxw = ($this->w - $this->lMargin - $this->rMargin); 6780 if ($w > $maxw) { 6781 $w = $maxw; 6782 $h = ($w / $ratio_wh); 6783 } 6784 } 6785 } 6786 // Check whether we need a new page or new column first as this does not fit 6787 $prev_x = $this->x; 6788 $prev_y = $this->y; 6789 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) { 6790 $y = $this->y; 6791 if ($this->rtl) { 6792 $x += ($prev_x - $this->x); 6793 } else { 6794 $x += ($this->x - $prev_x); 6795 } 6796 $this->newline = true; 6797 } 6798 // resize the block to be contained on the remaining available page or column space 6799 if ($fitonpage) { 6800 $ratio_wh = ($w / $h); 6801 if (($y + $h) > $this->PageBreakTrigger) { 6802 $h = $this->PageBreakTrigger - $y; 6803 $w = ($h * $ratio_wh); 6804 } 6805 if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) { 6806 $w = $this->w - $this->rMargin - $x; 6807 $h = ($w / $ratio_wh); 6808 } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) { 6809 $w = $x - $this->lMargin; 6810 $h = ($w / $ratio_wh); 6811 } 6812 } 6813 return array($w, $h, $x, $y); 6814 } 6815 6816 /** 6817 * Puts an image in the page. 6818 * The upper-left corner must be given. 6819 * The dimensions can be specified in different ways:<ul> 6820 * <li>explicit width and height (expressed in user unit)</li> 6821 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li> 6822 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> 6823 * 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; 6824 * The format can be specified explicitly or inferred from the file extension.<br /> 6825 * It is possible to put a link on the image.<br /> 6826 * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> 6827 * @param $file (string) Name of the file containing the image or a '@' character followed by the image data string. To link an image without embedding it on the document, set an asterisk character before the URL (i.e.: '*http://www.example.com/image.jpg'). 6828 * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL). 6829 * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL). 6830 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 6831 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 6832 * @param $type (string) 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. 6833 * @param $link (mixed) URL or identifier returned by AddLink(). 6834 * @param $align (string) 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> 6835 * @param $resize (mixed) If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); if false do not resize; if 2 force resize in all cases (upscaling and downscaling). 6836 * @param $dpi (int) dot-per-inch resolution used on resize 6837 * @param $palign (string) 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> 6838 * @param $ismask (boolean) true if this image is a mask, false otherwise 6839 * @param $imgmask (mixed) image object returned by this function or false 6840 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 6841 * @param $fitbox (mixed) If not false scale image dimensions proportionally to fit within the ($w, $h) box. $fitbox can be true or a 2 characters string indicating the image alignment inside the box. The first character indicate the horizontal alignment (L = left, C = center, R = right) the second character indicate the vertical algnment (T = top, M = middle, B = bottom). 6842 * @param $hidden (boolean) If true do not display the image. 6843 * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions. 6844 * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned). 6845 * @param $altimgs (array) Array of alternate images IDs. Each alternative image must be an array with two values: an integer representing the image ID (the value returned by the Image method) and a boolean value to indicate if the image is the default for printing. 6846 * @return image information 6847 * @public 6848 * @since 1.1 6849 */ 6850 public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array()) { 6851 if ($this->state != 2) { 6852 return; 6853 } 6854 if ($x === '') { 6855 $x = $this->x; 6856 } 6857 if ($y === '') { 6858 $y = $this->y; 6859 } 6860 // check page for no-write regions and adapt page margins if necessary 6861 list($x, $y) = $this->checkPageRegions($h, $x, $y); 6862 $exurl = ''; // external streams 6863 $imsize = FALSE; 6864 // check if we are passing an image as file or string 6865 if ($file[0] === '@') { 6866 // image from string 6867 $imgdata = substr($file, 1); 6868 } else { // image file 6869 if ($file[0] === '*') { 6870 // image as external stream 6871 $file = substr($file, 1); 6872 $exurl = $file; 6873 } 6874 // check if is a local file 6875 if (!@file_exists($file)) { 6876 // try to encode spaces on filename 6877 $tfile = str_replace(' ', '%20', $file); 6878 if (@file_exists($tfile)) { 6879 $file = $tfile; 6880 } 6881 } 6882 if (($imsize = @getimagesize($file)) === FALSE) { 6883 if (in_array($file, $this->imagekeys)) { 6884 // get existing image data 6885 $info = $this->getImageBuffer($file); 6886 $imsize = array($info['w'], $info['h']); 6887 } elseif (strpos($file, '__tcpdf_img') === FALSE) { 6888 $imgdata = TCPDF_STATIC::fileGetContents($file); 6889 } 6890 } 6891 } 6892 if (!empty($imgdata)) { 6893 // copy image to cache 6894 $original_file = $file; 6895 $file = TCPDF_STATIC::getObjFilename('img'); 6896 $fp = fopen($file, 'w'); 6897 if (!$fp) { 6898 $this->Error('Unable to write file: '.$file); 6899 } 6900 fwrite($fp, $imgdata); 6901 fclose($fp); 6902 unset($imgdata); 6903 $imsize = @getimagesize($file); 6904 if ($imsize === FALSE) { 6905 unlink($file); 6906 $file = $original_file; 6907 } else { 6908 $this->cached_files[] = $file; 6909 } 6910 } 6911 if ($imsize === FALSE) { 6912 if (($w > 0) AND ($h > 0)) { 6913 // get measures from specified data 6914 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 6915 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 6916 $imsize = array($pw, $ph); 6917 } else { 6918 $this->Error('[Image] Unable to get the size of the image: '.$file); 6919 } 6920 } 6921 // file hash 6922 $filehash = md5($this->file_id.$file); 6923 // get original image width and height in pixels 6924 list($pixw, $pixh) = $imsize; 6925 // calculate image width and height on document 6926 if (($w <= 0) AND ($h <= 0)) { 6927 // convert image size to document unit 6928 $w = $this->pixelsToUnits($pixw); 6929 $h = $this->pixelsToUnits($pixh); 6930 } elseif ($w <= 0) { 6931 $w = $h * $pixw / $pixh; 6932 } elseif ($h <= 0) { 6933 $h = $w * $pixh / $pixw; 6934 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) { 6935 if (strlen($fitbox) !== 2) { 6936 // set default alignment 6937 $fitbox = '--'; 6938 } 6939 // scale image dimensions proportionally to fit within the ($w, $h) box 6940 if ((($w * $pixh) / ($h * $pixw)) < 1) { 6941 // store current height 6942 $oldh = $h; 6943 // calculate new height 6944 $h = $w * $pixh / $pixw; 6945 // height difference 6946 $hdiff = ($oldh - $h); 6947 // vertical alignment 6948 switch (strtoupper($fitbox[1])) { 6949 case 'T': { 6950 break; 6951 } 6952 case 'M': { 6953 $y += ($hdiff / 2); 6954 break; 6955 } 6956 case 'B': { 6957 $y += $hdiff; 6958 break; 6959 } 6960 } 6961 } else { 6962 // store current width 6963 $oldw = $w; 6964 // calculate new width 6965 $w = $h * $pixw / $pixh; 6966 // width difference 6967 $wdiff = ($oldw - $w); 6968 // horizontal alignment 6969 switch (strtoupper($fitbox[0])) { 6970 case 'L': { 6971 if ($this->rtl) { 6972 $x -= $wdiff; 6973 } 6974 break; 6975 } 6976 case 'C': { 6977 if ($this->rtl) { 6978 $x -= ($wdiff / 2); 6979 } else { 6980 $x += ($wdiff / 2); 6981 } 6982 break; 6983 } 6984 case 'R': { 6985 if (!$this->rtl) { 6986 $x += $wdiff; 6987 } 6988 break; 6989 } 6990 } 6991 } 6992 } 6993 // fit the image on available space 6994 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 6995 // calculate new minimum dimensions in pixels 6996 $neww = round($w * $this->k * $dpi / $this->dpi); 6997 $newh = round($h * $this->k * $dpi / $this->dpi); 6998 // check if resize is necessary (resize is used only to reduce the image) 6999 $newsize = ($neww * $newh); 7000 $pixsize = ($pixw * $pixh); 7001 if (intval($resize) == 2) { 7002 $resize = true; 7003 } elseif ($newsize >= $pixsize) { 7004 $resize = false; 7005 } 7006 // check if image has been already added on document 7007 $newimage = true; 7008 if (in_array($file, $this->imagekeys)) { 7009 $newimage = false; 7010 // get existing image data 7011 $info = $this->getImageBuffer($file); 7012 if (strpos($file, '__tcpdf_imgmask_') === FALSE) { 7013 // check if the newer image is larger 7014 $oldsize = ($info['w'] * $info['h']); 7015 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 7016 $newimage = true; 7017 } 7018 } 7019 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)) { 7020 // create temp image file (without alpha channel) 7021 $tempfile_plain = K_PATH_CACHE.'__tcpdf_imgmask_plain_'.$filehash; 7022 // create temp alpha file 7023 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_imgmask_alpha_'.$filehash; 7024 // check for cached images 7025 if (in_array($tempfile_plain, $this->imagekeys)) { 7026 // get existing image data 7027 $info = $this->getImageBuffer($tempfile_plain); 7028 // check if the newer image is larger 7029 $oldsize = ($info['w'] * $info['h']); 7030 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 7031 $newimage = true; 7032 } else { 7033 $newimage = false; 7034 // embed mask image 7035 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 7036 // embed image, masked with previously embedded mask 7037 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 7038 } 7039 } 7040 } 7041 if ($newimage) { 7042 //First use of image, get info 7043 $type = strtolower($type); 7044 if ($type == '') { 7045 $type = TCPDF_IMAGES::getImageFileType($file, $imsize); 7046 } elseif ($type == 'jpg') { 7047 $type = 'jpeg'; 7048 } 7049 $mqr = TCPDF_STATIC::get_mqr(); 7050 TCPDF_STATIC::set_mqr(false); 7051 // Specific image handlers (defined on TCPDF_IMAGES CLASS) 7052 $mtd = '_parse'.$type; 7053 // GD image handler function 7054 $gdfunction = 'imagecreatefrom'.$type; 7055 $info = false; 7056 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) { 7057 // TCPDF image functions 7058 $info = TCPDF_IMAGES::$mtd($file); 7059 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE) 7060 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) { 7061 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash); 7062 } 7063 } 7064 if (($info === false) AND function_exists($gdfunction)) { 7065 try { 7066 // GD library 7067 $img = $gdfunction($file); 7068 if ($img !== false) { 7069 if ($resize) { 7070 $imgr = imagecreatetruecolor($neww, $newh); 7071 if (($type == 'gif') OR ($type == 'png')) { 7072 $imgr = TCPDF_IMAGES::setGDImageTransparency($imgr, $img); 7073 } 7074 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 7075 $img = $imgr; 7076 } 7077 if (($type == 'gif') OR ($type == 'png')) { 7078 $info = TCPDF_IMAGES::_toPNG($img); 7079 } else { 7080 $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality); 7081 } 7082 } 7083 } catch(Exception $e) { 7084 $info = false; 7085 } 7086 } 7087 if (($info === false) AND extension_loaded('imagick')) { 7088 try { 7089 // ImageMagick library 7090 $img = new Imagick(); 7091 if ($type == 'svg') { 7092 if ($file[0] === '@') { 7093 // image from string 7094 $svgimg = substr($file, 1); 7095 } else { 7096 // get SVG file content 7097 $svgimg = TCPDF_STATIC::fileGetContents($file); 7098 } 7099 if ($svgimg !== FALSE) { 7100 // get width and height 7101 $regs = array(); 7102 if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) { 7103 $svgtag = $regs[1]; 7104 $tmp = array(); 7105 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { 7106 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 7107 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit; 7108 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1); 7109 } else { 7110 $ow = $w; 7111 } 7112 $tmp = array(); 7113 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { 7114 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 7115 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit; 7116 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1); 7117 } else { 7118 $oh = $h; 7119 } 7120 $tmp = array(); 7121 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) { 7122 $vbw = ($ow * $this->imgscale * $this->k); 7123 $vbh = ($oh * $this->imgscale * $this->k); 7124 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh); 7125 $svgtag = $vbox.$svgtag; 7126 } 7127 $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1); 7128 } 7129 $img->readImageBlob($svgimg); 7130 } 7131 } else { 7132 $img->readImage($file); 7133 } 7134 if ($resize) { 7135 $img->resizeImage($neww, $newh, 10, 1, false); 7136 } 7137 $img->setCompressionQuality($this->jpeg_quality); 7138 $img->setImageFormat('jpeg'); 7139 $tempname = TCPDF_STATIC::getObjFilename('img'); 7140 $img->writeImage($tempname); 7141 $info = TCPDF_IMAGES::_parsejpeg($tempname); 7142 unlink($tempname); 7143 $img->destroy(); 7144 } catch(Exception $e) { 7145 $info = false; 7146 } 7147 } 7148 if ($info === false) { 7149 // unable to process image 7150 return; 7151 } 7152 TCPDF_STATIC::set_mqr($mqr); 7153 if ($ismask) { 7154 // force grayscale 7155 $info['cs'] = 'DeviceGray'; 7156 } 7157 if ($imgmask !== false) { 7158 $info['masked'] = $imgmask; 7159 } 7160 if (!empty($exurl)) { 7161 $info['exurl'] = $exurl; 7162 } 7163 // array of alternative images 7164 $info['altimgs'] = $altimgs; 7165 // add image to document 7166 $info['i'] = $this->setImageBuffer($file, $info); 7167 } 7168 // set alignment 7169 $this->img_rb_y = $y + $h; 7170 // set alignment 7171 if ($this->rtl) { 7172 if ($palign == 'L') { 7173 $ximg = $this->lMargin; 7174 } elseif ($palign == 'C') { 7175 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 7176 } elseif ($palign == 'R') { 7177 $ximg = $this->w - $this->rMargin - $w; 7178 } else { 7179 $ximg = $x - $w; 7180 } 7181 $this->img_rb_x = $ximg; 7182 } else { 7183 if ($palign == 'L') { 7184 $ximg = $this->lMargin; 7185 } elseif ($palign == 'C') { 7186 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 7187 } elseif ($palign == 'R') { 7188 $ximg = $this->w - $this->rMargin - $w; 7189 } else { 7190 $ximg = $x; 7191 } 7192 $this->img_rb_x = $ximg + $w; 7193 } 7194 if ($ismask OR $hidden) { 7195 // image is not displayed 7196 return $info['i']; 7197 } 7198 $xkimg = $ximg * $this->k; 7199 if (!$alt) { 7200 // only non-alternative immages will be set 7201 $this->_out(sprintf('q %F 0 0 %F %F %F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i'])); 7202 } 7203 if (!empty($border)) { 7204 $bx = $this->x; 7205 $by = $this->y; 7206 $this->x = $ximg; 7207 if ($this->rtl) { 7208 $this->x += $w; 7209 } 7210 $this->y = $y; 7211 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 7212 $this->x = $bx; 7213 $this->y = $by; 7214 } 7215 if ($link) { 7216 $this->Link($ximg, $y, $w, $h, $link, 0); 7217 } 7218 // set pointer to align the next text/objects 7219 switch($align) { 7220 case 'T': { 7221 $this->y = $y; 7222 $this->x = $this->img_rb_x; 7223 break; 7224 } 7225 case 'M': { 7226 $this->y = $y + round($h/2); 7227 $this->x = $this->img_rb_x; 7228 break; 7229 } 7230 case 'B': { 7231 $this->y = $this->img_rb_y; 7232 $this->x = $this->img_rb_x; 7233 break; 7234 } 7235 case 'N': { 7236 $this->SetY($this->img_rb_y); 7237 break; 7238 } 7239 default:{ 7240 break; 7241 } 7242 } 7243 $this->endlinex = $this->img_rb_x; 7244 if ($this->inxobj) { 7245 // we are inside an XObject template 7246 $this->xobjects[$this->xobjid]['images'][] = $info['i']; 7247 } 7248 return $info['i']; 7249 } 7250 7251 /** 7252 * Extract info from a PNG image with alpha channel using the Imagick or GD library. 7253 * @param $file (string) Name of the file containing the image. 7254 * @param $x (float) Abscissa of the upper-left corner. 7255 * @param $y (float) Ordinate of the upper-left corner. 7256 * @param $wpx (float) Original width of the image in pixels. 7257 * @param $hpx (float) original height of the image in pixels. 7258 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 7259 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 7260 * @param $type (string) 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. 7261 * @param $link (mixed) URL or identifier returned by AddLink(). 7262 * @param $align (string) 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> 7263 * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library). 7264 * @param $dpi (int) dot-per-inch resolution used on resize 7265 * @param $palign (string) 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> 7266 * @param $filehash (string) File hash used to build unique file names. 7267 * @author Nicola Asuni 7268 * @protected 7269 * @since 4.3.007 (2008-12-04) 7270 * @see Image() 7271 */ 7272 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') { 7273 // create temp images 7274 if (empty($filehash)) { 7275 $filehash = md5($this->file_id.$file); 7276 } 7277 // create temp image file (without alpha channel) 7278 $tempfile_plain = K_PATH_CACHE.'__tcpdf_imgmask_plain_'.$filehash; 7279 // create temp alpha file 7280 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_imgmask_alpha_'.$filehash; 7281 $parsed = false; 7282 $parse_error = ''; 7283 // ImageMagick extension 7284 if (($parsed === false) AND extension_loaded('imagick')) { 7285 try { 7286 // ImageMagick library 7287 $img = new Imagick(); 7288 $img->readImage($file); 7289 // clone image object 7290 $imga = TCPDF_STATIC::objclone($img); 7291 // extract alpha channel 7292 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) { 7293 $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT); 7294 } else { 7295 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE); 7296 $img->negateImage(true); 7297 } 7298 $img->setImageFormat('png'); 7299 $img->writeImage($tempfile_alpha); 7300 // remove alpha channel 7301 if (method_exists($imga, 'setImageMatte')) { 7302 $imga->setImageMatte(false); 7303 } else { 7304 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE)); 7305 } 7306 $imga->setImageFormat('png'); 7307 $imga->writeImage($tempfile_plain); 7308 $parsed = true; 7309 } catch (Exception $e) { 7310 // Imagemagick fails, try with GD 7311 $parse_error = 'Imagick library error: '.$e->getMessage(); 7312 } 7313 } 7314 // GD extension 7315 if (($parsed === false) AND function_exists('imagecreatefrompng')) { 7316 try { 7317 // generate images 7318 $img = imagecreatefrompng($file); 7319 $imgalpha = imagecreate($wpx, $hpx); 7320 // generate gray scale palette (0 -> 255) 7321 for ($c = 0; $c < 256; ++$c) { 7322 ImageColorAllocate($imgalpha, $c, $c, $c); 7323 } 7324 // extract alpha channel 7325 for ($xpx = 0; $xpx < $wpx; ++$xpx) { 7326 for ($ypx = 0; $ypx < $hpx; ++$ypx) { 7327 $color = imagecolorat($img, $xpx, $ypx); 7328 // get and correct gamma color 7329 $alpha = $this->getGDgamma($img, $color); 7330 imagesetpixel($imgalpha, $xpx, $ypx, $alpha); 7331 } 7332 } 7333 imagepng($imgalpha, $tempfile_alpha); 7334 imagedestroy($imgalpha); 7335 // extract image without alpha channel 7336 $imgplain = imagecreatetruecolor($wpx, $hpx); 7337 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx); 7338 imagepng($imgplain, $tempfile_plain); 7339 imagedestroy($imgplain); 7340 $parsed = true; 7341 } catch (Exception $e) { 7342 // GD fails 7343 $parse_error = 'GD library error: '.$e->getMessage(); 7344 } 7345 } 7346 if ($parsed === false) { 7347 if (empty($parse_error)) { 7348 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.'); 7349 } else { 7350 $this->Error($parse_error); 7351 } 7352 } 7353 // embed mask image 7354 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 7355 // embed image, masked with previously embedded mask 7356 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 7357 // remove temp files 7358 unlink($tempfile_alpha); 7359 unlink($tempfile_plain); 7360 } 7361 7362 /** 7363 * Get the GD-corrected PNG gamma value from alpha color 7364 * @param $img (int) GD image Resource ID. 7365 * @param $c (int) alpha color 7366 * @protected 7367 * @since 4.3.007 (2008-12-04) 7368 */ 7369 protected function getGDgamma($img, $c) { 7370 if (!isset($this->gdgammacache['#'.$c])) { 7371 $colors = imagecolorsforindex($img, $c); 7372 // GD alpha is only 7 bit (0 -> 127) 7373 $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255); 7374 // correct gamma 7375 $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255); 7376 // store the latest values on cache to improve performances 7377 if (count($this->gdgammacache) > 8) { 7378 // remove one element from the cache array 7379 array_shift($this->gdgammacache); 7380 } 7381 } 7382 return $this->gdgammacache['#'.$c]; 7383 } 7384 7385 /** 7386 * Performs a line break. 7387 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. 7388 * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell. 7389 * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate 7390 * @public 7391 * @since 1.0 7392 * @see Cell() 7393 */ 7394 public function Ln($h='', $cell=false) { 7395 if (($this->num_columns > 1) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) { 7396 // revove vertical space from the top of the column 7397 return; 7398 } 7399 if ($cell) { 7400 if ($this->rtl) { 7401 $cellpadding = $this->cell_padding['R']; 7402 } else { 7403 $cellpadding = $this->cell_padding['L']; 7404 } 7405 } else { 7406 $cellpadding = 0; 7407 } 7408 if ($this->rtl) { 7409 $this->x = $this->w - $this->rMargin - $cellpadding; 7410 } else { 7411 $this->x = $this->lMargin + $cellpadding; 7412 } 7413 if (is_string($h)) { 7414 $this->y += $this->lasth; 7415 } else { 7416 $this->y += $h; 7417 } 7418 $this->newline = true; 7419 } 7420 7421 /** 7422 * Returns the relative X value of current position. 7423 * The value is relative to the left border for LTR languages and to the right border for RTL languages. 7424 * @return float 7425 * @public 7426 * @since 1.2 7427 * @see SetX(), GetY(), SetY() 7428 */ 7429 public function GetX() { 7430 //Get x position 7431 if ($this->rtl) { 7432 return ($this->w - $this->x); 7433 } else { 7434 return $this->x; 7435 } 7436 } 7437 7438 /** 7439 * Returns the absolute X value of current position. 7440 * @return float 7441 * @public 7442 * @since 1.2 7443 * @see SetX(), GetY(), SetY() 7444 */ 7445 public function GetAbsX() { 7446 return $this->x; 7447 } 7448 7449 /** 7450 * Returns the ordinate of the current position. 7451 * @return float 7452 * @public 7453 * @since 1.0 7454 * @see SetY(), GetX(), SetX() 7455 */ 7456 public function GetY() { 7457 return $this->y; 7458 } 7459 7460 /** 7461 * Defines the abscissa of the current position. 7462 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). 7463 * @param $x (float) The value of the abscissa in user units. 7464 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. 7465 * @public 7466 * @since 1.2 7467 * @see GetX(), GetY(), SetY(), SetXY() 7468 */ 7469 public function SetX($x, $rtloff=false) { 7470 $x = floatval($x); 7471 if (!$rtloff AND $this->rtl) { 7472 if ($x >= 0) { 7473 $this->x = $this->w - $x; 7474 } else { 7475 $this->x = abs($x); 7476 } 7477 } else { 7478 if ($x >= 0) { 7479 $this->x = $x; 7480 } else { 7481 $this->x = $this->w + $x; 7482 } 7483 } 7484 if ($this->x < 0) { 7485 $this->x = 0; 7486 } 7487 if ($this->x > $this->w) { 7488 $this->x = $this->w; 7489 } 7490 } 7491 7492 /** 7493 * Moves the current abscissa back to the left margin and sets the ordinate. 7494 * If the passed value is negative, it is relative to the bottom of the page. 7495 * @param $y (float) The value of the ordinate in user units. 7496 * @param $resetx (bool) if true (default) reset the X position. 7497 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. 7498 * @public 7499 * @since 1.0 7500 * @see GetX(), GetY(), SetY(), SetXY() 7501 */ 7502 public function SetY($y, $resetx=true, $rtloff=false) { 7503 $y = floatval($y); 7504 if ($resetx) { 7505 //reset x 7506 if (!$rtloff AND $this->rtl) { 7507 $this->x = $this->w - $this->rMargin; 7508 } else { 7509 $this->x = $this->lMargin; 7510 } 7511 } 7512 if ($y >= 0) { 7513 $this->y = $y; 7514 } else { 7515 $this->y = $this->h + $y; 7516 } 7517 if ($this->y < 0) { 7518 $this->y = 0; 7519 } 7520 if ($this->y > $this->h) { 7521 $this->y = $this->h; 7522 } 7523 } 7524 7525 /** 7526 * Defines the abscissa and ordinate of the current position. 7527 * If the passed values are negative, they are relative respectively to the right and bottom of the page. 7528 * @param $x (float) The value of the abscissa. 7529 * @param $y (float) The value of the ordinate. 7530 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. 7531 * @public 7532 * @since 1.2 7533 * @see SetX(), SetY() 7534 */ 7535 public function SetXY($x, $y, $rtloff=false) { 7536 $this->SetY($y, false, $rtloff); 7537 $this->SetX($x, $rtloff); 7538 } 7539 7540 /** 7541 * Set the absolute X coordinate of the current pointer. 7542 * @param $x (float) The value of the abscissa in user units. 7543 * @public 7544 * @since 5.9.186 (2012-09-13) 7545 * @see setAbsX(), setAbsY(), SetAbsXY() 7546 */ 7547 public function SetAbsX($x) { 7548 $this->x = floatval($x); 7549 } 7550 7551 /** 7552 * Set the absolute Y coordinate of the current pointer. 7553 * @param $y (float) (float) The value of the ordinate in user units. 7554 * @public 7555 * @since 5.9.186 (2012-09-13) 7556 * @see setAbsX(), setAbsY(), SetAbsXY() 7557 */ 7558 public function SetAbsY($y) { 7559 $this->y = floatval($y); 7560 } 7561 7562 /** 7563 * Set the absolute X and Y coordinates of the current pointer. 7564 * @param $x (float) The value of the abscissa in user units. 7565 * @param $y (float) (float) The value of the ordinate in user units. 7566 * @public 7567 * @since 5.9.186 (2012-09-13) 7568 * @see setAbsX(), setAbsY(), SetAbsXY() 7569 */ 7570 public function SetAbsXY($x, $y) { 7571 $this->SetAbsX($x); 7572 $this->SetAbsY($y); 7573 } 7574 7575 /** 7576 * Send the document to a given destination: string, local file or browser. 7577 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> 7578 * The method first calls Close() if necessary to terminate the document. 7579 * @param $name (string) The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character. 7580 * @param $dest (string) Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local server file with the name given by name.</li><li>S: return the document as a string (name is ignored).</li><li>FI: equivalent to F + I option</li><li>FD: equivalent to F + D option</li><li>E: return the document as base64 mime multi-part email attachment (RFC 2045)</li></ul> 7581 * @public 7582 * @since 1.0 7583 * @see Close() 7584 */ 7585 public function Output($name='doc.pdf', $dest='I') { 7586 //Output PDF to some destination 7587 //Finish document if necessary 7588 if ($this->state < 3) { 7589 $this->Close(); 7590 } 7591 //Normalize parameters 7592 if (is_bool($dest)) { 7593 $dest = $dest ? 'D' : 'F'; 7594 } 7595 $dest = strtoupper($dest); 7596 if ($dest[0] != 'F') { 7597 $name = preg_replace('/[\s]+/', '_', $name); 7598 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name); 7599 } 7600 if ($this->sign) { 7601 // *** apply digital signature to the document *** 7602 // get the document content 7603 $pdfdoc = $this->getBuffer(); 7604 // remove last newline 7605 $pdfdoc = substr($pdfdoc, 0, -1); 7606 // Remove the original buffer 7607 if (isset($this->diskcache) AND $this->diskcache) { 7608 // remove buffer file from cache 7609 unlink($this->buffer); 7610 } 7611 unset($this->buffer); 7612 // remove filler space 7613 $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string); 7614 // define the ByteRange 7615 $byte_range = array(); 7616 $byte_range[0] = 0; 7617 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10; 7618 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2; 7619 $byte_range[3] = strlen($pdfdoc) - $byte_range[2]; 7620 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]); 7621 // replace the ByteRange 7622 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]); 7623 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange))); 7624 $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc); 7625 // write the document to a temporary folder 7626 $tempdoc = TCPDF_STATIC::getObjFilename('doc'); 7627 $f = fopen($tempdoc, 'wb'); 7628 if (!$f) { 7629 $this->Error('Unable to create temporary file: '.$tempdoc); 7630 } 7631 $pdfdoc_length = strlen($pdfdoc); 7632 fwrite($f, $pdfdoc, $pdfdoc_length); 7633 fclose($f); 7634 // get digital signature via openssl library 7635 $tempsign = TCPDF_STATIC::getObjFilename('sig'); 7636 if (empty($this->signature_data['extracerts'])) { 7637 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED); 7638 } else { 7639 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']); 7640 } 7641 unlink($tempdoc); 7642 // read signature 7643 $signature = file_get_contents($tempsign); 7644 unlink($tempsign); 7645 // extract signature 7646 $signature = substr($signature, $pdfdoc_length); 7647 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); 7648 $tmparr = explode("\n\n", $signature); 7649 $signature = $tmparr[1]; 7650 unset($tmparr); 7651 // decode signature 7652 $signature = base64_decode(trim($signature)); 7653 // add TSA timestamp to signature 7654 $signature = $this->applyTSA($signature); 7655 // convert signature to hex 7656 $signature = current(unpack('H*', $signature)); 7657 $signature = str_pad($signature, $this->signature_max_length, '0'); 7658 // disable disk caching 7659 $this->diskcache = false; 7660 // Add signature to the document 7661 $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]); 7662 $this->bufferlen = strlen($this->buffer); 7663 } 7664 switch($dest) { 7665 case 'I': { 7666 // Send PDF to the standard output 7667 if (ob_get_contents()) { 7668 $this->Error('Some data has already been output, can\'t send PDF file'); 7669 } 7670 if (php_sapi_name() != 'cli') { 7671 // send output to a browser 7672 header('Content-Type: application/pdf'); 7673 if (headers_sent()) { 7674 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7675 } 7676 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7677 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7678 header('Pragma: public'); 7679 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7680 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7681 header('Content-Disposition: inline; filename="'.basename($name).'"'); 7682 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen); 7683 } else { 7684 echo $this->getBuffer(); 7685 } 7686 break; 7687 } 7688 case 'D': { 7689 // download PDF as file 7690 if (ob_get_contents()) { 7691 $this->Error('Some data has already been output, can\'t send PDF file'); 7692 } 7693 header('Content-Description: File Transfer'); 7694 if (headers_sent()) { 7695 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7696 } 7697 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7698 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7699 header('Pragma: public'); 7700 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7701 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7702 // force download dialog 7703 if (strpos(php_sapi_name(), 'cgi') === false) { 7704 header('Content-Type: application/force-download'); 7705 header('Content-Type: application/octet-stream', false); 7706 header('Content-Type: application/download', false); 7707 header('Content-Type: application/pdf', false); 7708 } else { 7709 header('Content-Type: application/pdf'); 7710 } 7711 // use the Content-Disposition header to supply a recommended filename 7712 header('Content-Disposition: attachment; filename="'.basename($name).'"'); 7713 header('Content-Transfer-Encoding: binary'); 7714 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen); 7715 break; 7716 } 7717 case 'F': 7718 case 'FI': 7719 case 'FD': { 7720 // save PDF to a local file 7721 if ($this->diskcache) { 7722 copy($this->buffer, $name); 7723 } else { 7724 $f = fopen($name, 'wb'); 7725 if (!$f) { 7726 $this->Error('Unable to create output file: '.$name); 7727 } 7728 fwrite($f, $this->getBuffer(), $this->bufferlen); 7729 fclose($f); 7730 } 7731 if ($dest == 'FI') { 7732 // send headers to browser 7733 header('Content-Type: application/pdf'); 7734 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7735 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7736 header('Pragma: public'); 7737 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7738 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7739 header('Content-Disposition: inline; filename="'.basename($name).'"'); 7740 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name)); 7741 } elseif ($dest == 'FD') { 7742 // send headers to browser 7743 if (ob_get_contents()) { 7744 $this->Error('Some data has already been output, can\'t send PDF file'); 7745 } 7746 header('Content-Description: File Transfer'); 7747 if (headers_sent()) { 7748 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7749 } 7750 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7751 header('Pragma: public'); 7752 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7753 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7754 // force download dialog 7755 if (strpos(php_sapi_name(), 'cgi') === false) { 7756 header('Content-Type: application/force-download'); 7757 header('Content-Type: application/octet-stream', false); 7758 header('Content-Type: application/download', false); 7759 header('Content-Type: application/pdf', false); 7760 } else { 7761 header('Content-Type: application/pdf'); 7762 } 7763 // use the Content-Disposition header to supply a recommended filename 7764 header('Content-Disposition: attachment; filename="'.basename($name).'"'); 7765 header('Content-Transfer-Encoding: binary'); 7766 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name)); 7767 } 7768 break; 7769 } 7770 case 'E': { 7771 // return PDF as base64 mime multi-part email attachment (RFC 2045) 7772 $retval = 'Content-Type: application/pdf;'."\r\n"; 7773 $retval .= ' name="'.$name.'"'."\r\n"; 7774 $retval .= 'Content-Transfer-Encoding: base64'."\r\n"; 7775 $retval .= 'Content-Disposition: attachment;'."\r\n"; 7776 $retval .= ' filename="'.$name.'"'."\r\n\r\n"; 7777 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n"); 7778 return $retval; 7779 } 7780 case 'S': { 7781 // returns PDF as a string 7782 return $this->getBuffer(); 7783 } 7784 default: { 7785 $this->Error('Incorrect output destination: '.$dest); 7786 } 7787 } 7788 return ''; 7789 } 7790 7791 /** 7792 * Unset all class variables except the following critical variables. 7793 * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables. 7794 * @param $preserve_objcopy (boolean) if true preserves the objcopy variable 7795 * @public 7796 * @since 4.5.016 (2009-02-24) 7797 */ 7798 public function _destroy($destroyall=false, $preserve_objcopy=false) { 7799 if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!TCPDF_STATIC::empty_string($this->buffer))) { 7800 // remove buffer file from cache 7801 unlink($this->buffer); 7802 } 7803 if ($destroyall AND !empty($this->cached_files)) { 7804 // remove cached files 7805 foreach ($this->cached_files as $cachefile) { 7806 if (is_file($cachefile)) { 7807 unlink($cachefile); 7808 } 7809 } 7810 unset($this->cached_files); 7811 } 7812 $preserve = array( 7813 'internal_encoding', 7814 'state', 7815 'bufferlen', 7816 'buffer', 7817 'diskcache', 7818 'cached_files', 7819 'sign', 7820 'signature_data', 7821 'signature_max_length', 7822 'byterange_string', 7823 'tsa_timestamp', 7824 'tsa_data' 7825 ); 7826 foreach (array_keys(get_object_vars($this)) as $val) { 7827 if ($destroyall OR !in_array($val, $preserve)) { 7828 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) { 7829 unset($this->$val); 7830 } 7831 } 7832 } 7833 } 7834 7835 /** 7836 * Check for locale-related bug 7837 * @protected 7838 */ 7839 protected function _dochecks() { 7840 //Check for locale-related bug 7841 if (1.1 == 1) { 7842 $this->Error('Don\'t alter the locale before including class file'); 7843 } 7844 //Check for decimal separator 7845 if (sprintf('%.1F', 1.0) != '1.0') { 7846 setlocale(LC_NUMERIC, 'C'); 7847 } 7848 } 7849 7850 /** 7851 * Return an array containing variations for the basic page number alias. 7852 * @param $a (string) Base alias. 7853 * @return array of page number aliases 7854 * @protected 7855 */ 7856 protected function getInternalPageNumberAliases($a= '') { 7857 $alias = array(); 7858 // build array of Unicode + ASCII variants (the order is important) 7859 $alias = array('u' => array(), 'a' => array()); 7860 $u = '{'.$a.'}'; 7861 $alias['u'][] = TCPDF_STATIC::_escape($u); 7862 if ($this->isunicode) { 7863 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont)); 7864 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont)); 7865 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont)); 7866 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont)); 7867 } 7868 $alias['a'][] = TCPDF_STATIC::_escape($a); 7869 return $alias; 7870 } 7871 7872 /** 7873 * Return an array containing all internal page aliases. 7874 * @return array of page number aliases 7875 * @protected 7876 */ 7877 protected function getAllInternalPageNumberAliases() { 7878 $basic_alias = array(TCPDF_STATIC::$alias_tot_pages, TCPDF_STATIC::$alias_num_page, TCPDF_STATIC::$alias_group_tot_pages, TCPDF_STATIC::$alias_group_num_page, TCPDF_STATIC::$alias_right_shift); 7879 $pnalias = array(); 7880 foreach($basic_alias as $k => $a) { 7881 $pnalias[$k] = $this->getInternalPageNumberAliases($a); 7882 } 7883 return $pnalias; 7884 } 7885 7886 /** 7887 * Replace right shift page number aliases with spaces to correct right alignment. 7888 * This works perfectly only when using monospaced fonts. 7889 * @param $page (string) Page content. 7890 * @param $aliases (array) Array of page aliases. 7891 * @param $diff (int) initial difference to add. 7892 * @return replaced page content. 7893 * @protected 7894 */ 7895 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) { 7896 foreach ($aliases as $type => $alias) { 7897 foreach ($alias as $a) { 7898 // find position of compensation factor 7899 $startnum = (strpos($a, ':') + 1); 7900 $a = substr($a, 0, $startnum); 7901 if (($pos = strpos($page, $a)) !== false) { 7902 // end of alias 7903 $endnum = strpos($page, '}', $pos); 7904 // string to be replaced 7905 $aa = substr($page, $pos, ($endnum - $pos + 1)); 7906 // get compensation factor 7907 $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum)); 7908 $ratio = preg_replace('/[^0-9\.]/', '', $ratio); 7909 $ratio = floatval($ratio); 7910 if ($type == 'u') { 7911 $chrdiff = floor(($diff + 12) * $ratio); 7912 $shift = str_repeat(' ', $chrdiff); 7913 $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont); 7914 } else { 7915 $chrdiff = floor(($diff + 11) * $ratio); 7916 $shift = str_repeat(' ', $chrdiff); 7917 } 7918 $page = str_replace($aa, $shift, $page); 7919 } 7920 } 7921 } 7922 return $page; 7923 } 7924 7925 /** 7926 * Set page boxes to be included on page descriptions. 7927 * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'). 7928 * @protected 7929 */ 7930 protected function setPageBoxTypes($boxes) { 7931 $this->page_boxes = array(); 7932 foreach ($boxes as $box) { 7933 if (in_array($box, TCPDF_STATIC::$pageboxes)) { 7934 $this->page_boxes[] = $box; 7935 } 7936 } 7937 } 7938 7939 /** 7940 * Output pages (and replace page number aliases). 7941 * @protected 7942 */ 7943 protected function _putpages() { 7944 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 7945 // get internal aliases for page numbers 7946 $pnalias = $this->getAllInternalPageNumberAliases(); 7947 $num_pages = $this->numpages; 7948 $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1)); 7949 $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont); 7950 $ptp_num_chars = $this->GetNumChars($ptpa); 7951 $pagegroupnum = 0; 7952 $groupnum = 0; 7953 $ptgu = 1; 7954 $ptga = 1; 7955 $ptg_num_chars = 1; 7956 for ($n = 1; $n <= $num_pages; ++$n) { 7957 // get current page 7958 $temppage = $this->getPageBuffer($n); 7959 $pagelen = strlen($temppage); 7960 // set replacements for total pages number 7961 $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1)); 7962 $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont); 7963 $pnp_num_chars = $this->GetNumChars($pnpa); 7964 $pdiff = 0; // difference used for right shift alignment of page numbers 7965 $gdiff = 0; // difference used for right shift alignment of page group numbers 7966 if (!empty($this->pagegroups)) { 7967 if (isset($this->newpagegroup[$n])) { 7968 $pagegroupnum = 0; 7969 ++$groupnum; 7970 $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]); 7971 $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont); 7972 $ptg_num_chars = $this->GetNumChars($ptga); 7973 } 7974 ++$pagegroupnum; 7975 $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum); 7976 $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont); 7977 $png_num_chars = $this->GetNumChars($pnga); 7978 // replace page numbers 7979 $replace = array(); 7980 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']); 7981 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']); 7982 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']); 7983 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']); 7984 list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff); 7985 } 7986 // replace page numbers 7987 $replace = array(); 7988 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']); 7989 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']); 7990 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']); 7991 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']); 7992 list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff); 7993 // replace right shift alias 7994 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff)); 7995 // replace EPS marker 7996 $temppage = str_replace($this->epsmarker, '', $temppage); 7997 //Page 7998 $this->page_obj_id[$n] = $this->_newobj(); 7999 $out = '<<'; 8000 $out .= ' /Type /Page'; 8001 $out .= ' /Parent 1 0 R'; 8002 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 8003 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp); 8004 } 8005 $out .= ' /Resources 2 0 R'; 8006 foreach ($this->page_boxes as $box) { 8007 $out .= ' /'.$box; 8008 $out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']); 8009 } 8010 if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) { 8011 $out .= ' /BoxColorInfo <<'; 8012 foreach ($this->page_boxes as $box) { 8013 if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) { 8014 $out .= ' /'.$box.' <<'; 8015 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) { 8016 $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C']; 8017 $out .= ' /C ['; 8018 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); 8019 $out .= ' ]'; 8020 } 8021 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) { 8022 $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k); 8023 } 8024 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) { 8025 $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S']; 8026 } 8027 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) { 8028 $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D']; 8029 $out .= ' /D ['; 8030 foreach ($dashes as $dash) { 8031 $out .= sprintf(' %F', ($dash * $this->k)); 8032 } 8033 $out .= ' ]'; 8034 } 8035 $out .= ' >>'; 8036 } 8037 } 8038 $out .= ' >>'; 8039 } 8040 $out .= ' /Contents '.($this->n + 1).' 0 R'; 8041 $out .= ' /Rotate '.$this->pagedim[$n]['Rotate']; 8042 if (!$this->pdfa_mode) { 8043 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>'; 8044 } 8045 if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) { 8046 // page transitions 8047 if (isset($this->pagedim[$n]['trans']['Dur'])) { 8048 $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur']; 8049 } 8050 $out .= ' /Trans <<'; 8051 $out .= ' /Type /Trans'; 8052 if (isset($this->pagedim[$n]['trans']['S'])) { 8053 $out .= ' /S /'.$this->pagedim[$n]['trans']['S']; 8054 } 8055 if (isset($this->pagedim[$n]['trans']['D'])) { 8056 $out .= ' /D '.$this->pagedim[$n]['trans']['D']; 8057 } 8058 if (isset($this->pagedim[$n]['trans']['Dm'])) { 8059 $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm']; 8060 } 8061 if (isset($this->pagedim[$n]['trans']['M'])) { 8062 $out .= ' /M /'.$this->pagedim[$n]['trans']['M']; 8063 } 8064 if (isset($this->pagedim[$n]['trans']['Di'])) { 8065 $out .= ' /Di '.$this->pagedim[$n]['trans']['Di']; 8066 } 8067 if (isset($this->pagedim[$n]['trans']['SS'])) { 8068 $out .= ' /SS '.$this->pagedim[$n]['trans']['SS']; 8069 } 8070 if (isset($this->pagedim[$n]['trans']['B'])) { 8071 $out .= ' /B '.$this->pagedim[$n]['trans']['B']; 8072 } 8073 $out .= ' >>'; 8074 } 8075 $out .= $this->_getannotsrefs($n); 8076 $out .= ' /PZ '.$this->pagedim[$n]['PZ']; 8077 $out .= ' >>'; 8078 $out .= "\n".'endobj'; 8079 $this->_out($out); 8080 //Page content 8081 $p = ($this->compress) ? gzcompress($temppage) : $temppage; 8082 $this->_newobj(); 8083 $p = $this->_getrawstream($p); 8084 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj'); 8085 if ($this->diskcache) { 8086 // remove temporary files 8087 unlink($this->pages[$n]); 8088 } 8089 } 8090 //Pages root 8091 $out = $this->_getobj(1)."\n"; 8092 $out .= '<< /Type /Pages /Kids ['; 8093 foreach($this->page_obj_id as $page_obj) { 8094 $out .= ' '.$page_obj.' 0 R'; 8095 } 8096 $out .= ' ] /Count '.$num_pages.' >>'; 8097 $out .= "\n".'endobj'; 8098 $this->_out($out); 8099 } 8100 8101 /** 8102 * Output references to page annotations 8103 * @param $n (int) page number 8104 * @protected 8105 * @author Nicola Asuni 8106 * @since 4.7.000 (2008-08-29) 8107 * @deprecated 8108 */ 8109 protected function _putannotsrefs($n) { 8110 $this->_out($this->_getannotsrefs($n)); 8111 } 8112 8113 /** 8114 * Get references to page annotations. 8115 * @param $n (int) page number 8116 * @return string 8117 * @protected 8118 * @author Nicola Asuni 8119 * @since 5.0.010 (2010-05-17) 8120 */ 8121 protected function _getannotsrefs($n) { 8122 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) { 8123 return ''; 8124 } 8125 $out = ' /Annots ['; 8126 if (isset($this->PageAnnots[$n])) { 8127 foreach ($this->PageAnnots[$n] as $key => $val) { 8128 if (!in_array($val['n'], $this->radio_groups)) { 8129 $out .= ' '.$val['n'].' 0 R'; 8130 } 8131 } 8132 // add radiobutton groups 8133 if (isset($this->radiobutton_groups[$n])) { 8134 foreach ($this->radiobutton_groups[$n] as $key => $data) { 8135 if (isset($data['n'])) { 8136 $out .= ' '.$data['n'].' 0 R'; 8137 } 8138 } 8139 } 8140 } 8141 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) { 8142 // set reference for signature object 8143 $out .= ' '.$this->sig_obj_id.' 0 R'; 8144 } 8145 if (!empty($this->empty_signature_appearance)) { 8146 foreach ($this->empty_signature_appearance as $esa) { 8147 if ($esa['page'] == $n) { 8148 // set reference for empty signature objects 8149 $out .= ' '.$esa['objid'].' 0 R'; 8150 } 8151 } 8152 } 8153 $out .= ' ]'; 8154 return $out; 8155 } 8156 8157 /** 8158 * Output annotations objects for all pages. 8159 * !!! THIS METHOD IS NOT YET COMPLETED !!! 8160 * See section 12.5 of PDF 32000_2008 reference. 8161 * @protected 8162 * @author Nicola Asuni 8163 * @since 4.0.018 (2008-08-06) 8164 */ 8165 protected function _putannotsobjs() { 8166 // reset object counter 8167 for ($n=1; $n <= $this->numpages; ++$n) { 8168 if (isset($this->PageAnnots[$n])) { 8169 // set page annotations 8170 foreach ($this->PageAnnots[$n] as $key => $pl) { 8171 $annot_obj_id = $this->PageAnnots[$n][$key]['n']; 8172 // create annotation object for grouping radiobuttons 8173 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) { 8174 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n']; 8175 $annots = '<<'; 8176 $annots .= ' /Type /Annot'; 8177 $annots .= ' /Subtype /Widget'; 8178 $annots .= ' /Rect [0 0 0 0]'; 8179 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) { 8180 // read only 8181 $annots .= ' /F 68'; 8182 $annots .= ' /Ff 49153'; 8183 } else { 8184 $annots .= ' /F 4'; // default print for PDF/A 8185 $annots .= ' /Ff 49152'; 8186 } 8187 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id); 8188 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { 8189 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id); 8190 } 8191 $annots .= ' /FT /Btn'; 8192 $annots .= ' /Kids ['; 8193 $defval = ''; 8194 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) { 8195 if (isset($data['kid'])) { 8196 $annots .= ' '.$data['kid'].' 0 R'; 8197 if ($data['def'] !== 'Off') { 8198 $defval = $data['def']; 8199 } 8200 } 8201 } 8202 $annots .= ' ]'; 8203 if (!empty($defval)) { 8204 $annots .= ' /V /'.$defval; 8205 } 8206 $annots .= ' >>'; 8207 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj'); 8208 $this->form_obj_id[] = $radio_button_obj_id; 8209 // store object id to be used on Parent entry of Kids 8210 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id; 8211 } 8212 $formfield = false; 8213 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER); 8214 $a = $pl['x'] * $this->k; 8215 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k); 8216 $c = $pl['w'] * $this->k; 8217 $d = $pl['h'] * $this->k; 8218 $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d); 8219 // create new annotation object 8220 $annots = '<</Type /Annot'; 8221 $annots .= ' /Subtype /'.$pl['opt']['subtype']; 8222 $annots .= ' /Rect ['.$rect.']'; 8223 $ft = array('Btn', 'Tx', 'Ch', 'Sig'); 8224 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) { 8225 $annots .= ' /FT /'.$pl['opt']['ft']; 8226 $formfield = true; 8227 } 8228 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id); 8229 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R'; 8230 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id); 8231 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp); 8232 if (isset($pl['opt']['f'])) { 8233 $fval = 0; 8234 if (is_array($pl['opt']['f'])) { 8235 foreach ($pl['opt']['f'] as $f) { 8236 switch (strtolower($f)) { 8237 case 'invisible': { 8238 $fval += 1 << 0; 8239 break; 8240 } 8241 case 'hidden': { 8242 $fval += 1 << 1; 8243 break; 8244 } 8245 case 'print': { 8246 $fval += 1 << 2; 8247 break; 8248 } 8249 case 'nozoom': { 8250 $fval += 1 << 3; 8251 break; 8252 } 8253 case 'norotate': { 8254 $fval += 1 << 4; 8255 break; 8256 } 8257 case 'noview': { 8258 $fval += 1 << 5; 8259 break; 8260 } 8261 case 'readonly': { 8262 $fval += 1 << 6; 8263 break; 8264 } 8265 case 'locked': { 8266 $fval += 1 << 8; 8267 break; 8268 } 8269 case 'togglenoview': { 8270 $fval += 1 << 9; 8271 break; 8272 } 8273 case 'lockedcontents': { 8274 $fval += 1 << 10; 8275 break; 8276 } 8277 default: { 8278 break; 8279 } 8280 } 8281 } 8282 } else { 8283 $fval = intval($pl['opt']['f']); 8284 } 8285 } else { 8286 $fval = 4; 8287 } 8288 if ($this->pdfa_mode) { 8289 // force print flag for PDF/A mode 8290 $fval |= 4; 8291 } 8292 $annots .= ' /F '.intval($fval); 8293 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) { 8294 $annots .= ' /AS /'.$pl['opt']['as']; 8295 } 8296 if (isset($pl['opt']['ap'])) { 8297 // appearance stream 8298 $annots .= ' /AP <<'; 8299 if (is_array($pl['opt']['ap'])) { 8300 foreach ($pl['opt']['ap'] as $apmode => $apdef) { 8301 // $apmode can be: n = normal; r = rollover; d = down; 8302 $annots .= ' /'.strtoupper($apmode); 8303 if (is_array($apdef)) { 8304 $annots .= ' <<'; 8305 foreach ($apdef as $apstate => $stream) { 8306 // reference to XObject that define the appearance for this mode-state 8307 $apsobjid = $this->_putAPXObject($c, $d, $stream); 8308 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R'; 8309 } 8310 $annots .= ' >>'; 8311 } else { 8312 // reference to XObject that define the appearance for this mode 8313 $apsobjid = $this->_putAPXObject($c, $d, $apdef); 8314 $annots .= ' '.$apsobjid.' 0 R'; 8315 } 8316 } 8317 } else { 8318 $annots .= $pl['opt']['ap']; 8319 } 8320 $annots .= ' >>'; 8321 } 8322 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) { 8323 $annots .= ' /BS <<'; 8324 $annots .= ' /Type /Border'; 8325 if (isset($pl['opt']['bs']['w'])) { 8326 $annots .= ' /W '.intval($pl['opt']['bs']['w']); 8327 } 8328 $bstyles = array('S', 'D', 'B', 'I', 'U'); 8329 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) { 8330 $annots .= ' /S /'.$pl['opt']['bs']['s']; 8331 } 8332 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) { 8333 $annots .= ' /D ['; 8334 foreach ($pl['opt']['bs']['d'] as $cord) { 8335 $annots .= ' '.intval($cord); 8336 } 8337 $annots .= ']'; 8338 } 8339 $annots .= ' >>'; 8340 } else { 8341 $annots .= ' /Border ['; 8342 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) { 8343 $annots .= intval($pl['opt']['border'][0]).' '; 8344 $annots .= intval($pl['opt']['border'][1]).' '; 8345 $annots .= intval($pl['opt']['border'][2]); 8346 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) { 8347 $annots .= ' ['; 8348 foreach ($pl['opt']['border'][3] as $dash) { 8349 $annots .= intval($dash).' '; 8350 } 8351 $annots .= ']'; 8352 } 8353 } else { 8354 $annots .= '0 0 0'; 8355 } 8356 $annots .= ']'; 8357 } 8358 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) { 8359 $annots .= ' /BE <<'; 8360 $bstyles = array('S', 'C'); 8361 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) { 8362 $annots .= ' /S /'.$pl['opt']['bs']['s']; 8363 } else { 8364 $annots .= ' /S /S'; 8365 } 8366 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) { 8367 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']); 8368 } 8369 $annots .= '>>'; 8370 } 8371 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) { 8372 $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']); 8373 } 8374 //$annots .= ' /StructParent '; 8375 //$annots .= ' /OC '; 8376 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound'); 8377 if (in_array(strtolower($pl['opt']['subtype']), $markups)) { 8378 // this is a markup type 8379 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 8380 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id); 8381 } 8382 //$annots .= ' /Popup '; 8383 if (isset($pl['opt']['ca'])) { 8384 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca'])); 8385 } 8386 if (isset($pl['opt']['rc'])) { 8387 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); 8388 } 8389 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp); 8390 //$annots .= ' /IRT '; 8391 if (isset($pl['opt']['subj'])) { 8392 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id); 8393 } 8394 //$annots .= ' /RT '; 8395 //$annots .= ' /IT '; 8396 //$annots .= ' /ExData '; 8397 } 8398 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash'); 8399 // Annotation types 8400 switch (strtolower($pl['opt']['subtype'])) { 8401 case 'text': { 8402 if (isset($pl['opt']['open'])) { 8403 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false'); 8404 } 8405 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph'); 8406 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8407 $annots .= ' /Name /'.$pl['opt']['name']; 8408 } else { 8409 $annots .= ' /Name /Note'; 8410 } 8411 $statemodels = array('Marked', 'Review'); 8412 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) { 8413 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 8414 } else { 8415 $pl['opt']['statemodel'] = 'Marked'; 8416 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 8417 } 8418 if ($pl['opt']['statemodel'] == 'Marked') { 8419 $states = array('Accepted', 'Unmarked'); 8420 } else { 8421 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None'); 8422 } 8423 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) { 8424 $annots .= ' /State /'.$pl['opt']['state']; 8425 } else { 8426 if ($pl['opt']['statemodel'] == 'Marked') { 8427 $annots .= ' /State /Unmarked'; 8428 } else { 8429 $annots .= ' /State /None'; 8430 } 8431 } 8432 break; 8433 } 8434 case 'link': { 8435 if (is_string($pl['txt'])) { 8436 if ($pl['txt'][0] == '#') { 8437 // internal destination 8438 $annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1)); 8439 } elseif ($pl['txt'][0] == '%') { 8440 // embedded PDF file 8441 $filename = basename(substr($pl['txt'], 1)); 8442 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; 8443 } elseif ($pl['txt'][0] == '*') { 8444 // embedded generic file 8445 $filename = basename(substr($pl['txt'], 1)); 8446 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});'; 8447 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>'; 8448 } else { 8449 $parsedUrl = parse_url($pl['txt']); 8450 if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) { 8451 // relative link to a PDF file 8452 $dest = '[0 /Fit]'; // default page 0 8453 if (!empty($parsedUrl['fragment'])) { 8454 // check for named destination 8455 $tmp = explode('=', $parsedUrl['fragment']); 8456 $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')'; 8457 } 8458 $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>'; 8459 } else { 8460 // external URI link 8461 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>'; 8462 } 8463 } 8464 } elseif (isset($this->links[$pl['txt']])) { 8465 // internal link ID 8466 $l = $this->links[$pl['txt']]; 8467 if (isset($this->page_obj_id[($l['p'])])) { 8468 $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k))); 8469 } 8470 } 8471 $hmodes = array('N', 'I', 'O', 'P'); 8472 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) { 8473 $annots .= ' /H /'.$pl['opt']['h']; 8474 } else { 8475 $annots .= ' /H /I'; 8476 } 8477 //$annots .= ' /PA '; 8478 //$annots .= ' /Quadpoints '; 8479 break; 8480 } 8481 case 'freetext': { 8482 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 8483 $annots .= ' /DA ('.$pl['opt']['da'].')'; 8484 } 8485 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 8486 $annots .= ' /Q '.intval($pl['opt']['q']); 8487 } 8488 if (isset($pl['opt']['rc'])) { 8489 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); 8490 } 8491 if (isset($pl['opt']['ds'])) { 8492 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id); 8493 } 8494 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) { 8495 $annots .= ' /CL ['; 8496 foreach ($pl['opt']['cl'] as $cl) { 8497 $annots .= sprintf('%F ', $cl * $this->k); 8498 } 8499 $annots .= ']'; 8500 } 8501 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter'); 8502 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) { 8503 $annots .= ' /IT /'.$pl['opt']['it']; 8504 } 8505 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) { 8506 $l = $pl['opt']['rd'][0] * $this->k; 8507 $r = $pl['opt']['rd'][1] * $this->k; 8508 $t = $pl['opt']['rd'][2] * $this->k; 8509 $b = $pl['opt']['rd'][3] * $this->k; 8510 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']'; 8511 } 8512 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) { 8513 $annots .= ' /LE /'.$pl['opt']['le']; 8514 } 8515 break; 8516 } 8517 case 'line': { 8518 break; 8519 } 8520 case 'square': { 8521 break; 8522 } 8523 case 'circle': { 8524 break; 8525 } 8526 case 'polygon': { 8527 break; 8528 } 8529 case 'polyline': { 8530 break; 8531 } 8532 case 'highlight': { 8533 break; 8534 } 8535 case 'underline': { 8536 break; 8537 } 8538 case 'squiggly': { 8539 break; 8540 } 8541 case 'strikeout': { 8542 break; 8543 } 8544 case 'stamp': { 8545 break; 8546 } 8547 case 'caret': { 8548 break; 8549 } 8550 case 'ink': { 8551 break; 8552 } 8553 case 'popup': { 8554 break; 8555 } 8556 case 'fileattachment': { 8557 if ($this->pdfa_mode) { 8558 // embedded files are not allowed in PDF/A mode 8559 break; 8560 } 8561 if (!isset($pl['opt']['fs'])) { 8562 break; 8563 } 8564 $filename = basename($pl['opt']['fs']); 8565 if (isset($this->embeddedfiles[$filename]['f'])) { 8566 $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R'; 8567 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag'); 8568 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8569 $annots .= ' /Name /'.$pl['opt']['name']; 8570 } else { 8571 $annots .= ' /Name /PushPin'; 8572 } 8573 // index (zero-based) of the annotation in the Annots array of this page 8574 $this->embeddedfiles[$filename]['a'] = $key; 8575 } 8576 break; 8577 } 8578 case 'sound': { 8579 if (!isset($pl['opt']['fs'])) { 8580 break; 8581 } 8582 $filename = basename($pl['opt']['fs']); 8583 if (isset($this->embeddedfiles[$filename]['f'])) { 8584 // ... TO BE COMPLETED ... 8585 // /R /C /B /E /CO /CP 8586 $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R'; 8587 $iconsapp = array('Speaker', 'Mic'); 8588 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8589 $annots .= ' /Name /'.$pl['opt']['name']; 8590 } else { 8591 $annots .= ' /Name /Speaker'; 8592 } 8593 } 8594 break; 8595 } 8596 case 'movie': { 8597 break; 8598 } 8599 case 'widget': { 8600 $hmode = array('N', 'I', 'O', 'P', 'T'); 8601 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) { 8602 $annots .= ' /H /'.$pl['opt']['h']; 8603 } 8604 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) { 8605 $annots .= ' /MK <<'; 8606 if (isset($pl['opt']['mk']['r'])) { 8607 $annots .= ' /R '.$pl['opt']['mk']['r']; 8608 } 8609 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) { 8610 $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']); 8611 } 8612 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) { 8613 $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']); 8614 } 8615 if (isset($pl['opt']['mk']['ca'])) { 8616 $annots .= ' /CA '.$pl['opt']['mk']['ca']; 8617 } 8618 if (isset($pl['opt']['mk']['rc'])) { 8619 $annots .= ' /RC '.$pl['opt']['mk']['rc']; 8620 } 8621 if (isset($pl['opt']['mk']['ac'])) { 8622 $annots .= ' /AC '.$pl['opt']['mk']['ac']; 8623 } 8624 if (isset($pl['opt']['mk']['i'])) { 8625 $info = $this->getImageBuffer($pl['opt']['mk']['i']); 8626 if ($info !== false) { 8627 $annots .= ' /I '.$info['n'].' 0 R'; 8628 } 8629 } 8630 if (isset($pl['opt']['mk']['ri'])) { 8631 $info = $this->getImageBuffer($pl['opt']['mk']['ri']); 8632 if ($info !== false) { 8633 $annots .= ' /RI '.$info['n'].' 0 R'; 8634 } 8635 } 8636 if (isset($pl['opt']['mk']['ix'])) { 8637 $info = $this->getImageBuffer($pl['opt']['mk']['ix']); 8638 if ($info !== false) { 8639 $annots .= ' /IX '.$info['n'].' 0 R'; 8640 } 8641 } 8642 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) { 8643 $annots .= ' /IF <<'; 8644 $if_sw = array('A', 'B', 'S', 'N'); 8645 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) { 8646 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw']; 8647 } 8648 $if_s = array('A', 'P'); 8649 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) { 8650 $annots .= ' /S /'.$pl['opt']['mk']['if']['s']; 8651 } 8652 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) { 8653 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]); 8654 } 8655 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) { 8656 $annots .= ' /FB true'; 8657 } 8658 $annots .= '>>'; 8659 } 8660 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) { 8661 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']); 8662 } 8663 $annots .= '>>'; 8664 } // end MK 8665 // --- Entries for field dictionaries --- 8666 if (isset($this->radiobutton_groups[$n][$pl['txt']])) { 8667 // set parent 8668 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R'; 8669 } 8670 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 8671 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id); 8672 } 8673 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { 8674 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id); 8675 } 8676 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) { 8677 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id); 8678 } 8679 if (isset($pl['opt']['ff'])) { 8680 if (is_array($pl['opt']['ff'])) { 8681 // array of bit settings 8682 $flag = 0; 8683 foreach($pl['opt']['ff'] as $val) { 8684 $flag += 1 << ($val - 1); 8685 } 8686 } else { 8687 $flag = intval($pl['opt']['ff']); 8688 } 8689 $annots .= ' /Ff '.$flag; 8690 } 8691 if (isset($pl['opt']['maxlen'])) { 8692 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']); 8693 } 8694 if (isset($pl['opt']['v'])) { 8695 $annots .= ' /V'; 8696 if (is_array($pl['opt']['v'])) { 8697 foreach ($pl['opt']['v'] AS $optval) { 8698 if (is_float($optval)) { 8699 $optval = sprintf('%F', $optval); 8700 } 8701 $annots .= ' '.$optval; 8702 } 8703 } else { 8704 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id); 8705 } 8706 } 8707 if (isset($pl['opt']['dv'])) { 8708 $annots .= ' /DV'; 8709 if (is_array($pl['opt']['dv'])) { 8710 foreach ($pl['opt']['dv'] AS $optval) { 8711 if (is_float($optval)) { 8712 $optval = sprintf('%F', $optval); 8713 } 8714 $annots .= ' '.$optval; 8715 } 8716 } else { 8717 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id); 8718 } 8719 } 8720 if (isset($pl['opt']['rv'])) { 8721 $annots .= ' /RV'; 8722 if (is_array($pl['opt']['rv'])) { 8723 foreach ($pl['opt']['rv'] AS $optval) { 8724 if (is_float($optval)) { 8725 $optval = sprintf('%F', $optval); 8726 } 8727 $annots .= ' '.$optval; 8728 } 8729 } else { 8730 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id); 8731 } 8732 } 8733 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) { 8734 $annots .= ' /A << '.$pl['opt']['a'].' >>'; 8735 } 8736 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) { 8737 $annots .= ' /AA << '.$pl['opt']['aa'].' >>'; 8738 } 8739 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 8740 $annots .= ' /DA ('.$pl['opt']['da'].')'; 8741 } 8742 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 8743 $annots .= ' /Q '.intval($pl['opt']['q']); 8744 } 8745 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) { 8746 $annots .= ' /Opt ['; 8747 foreach($pl['opt']['opt'] AS $copt) { 8748 if (is_array($copt)) { 8749 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']'; 8750 } else { 8751 $annots .= ' '.$this->_textstring($copt, $annot_obj_id); 8752 } 8753 } 8754 $annots .= ']'; 8755 } 8756 if (isset($pl['opt']['ti'])) { 8757 $annots .= ' /TI '.intval($pl['opt']['ti']); 8758 } 8759 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) { 8760 $annots .= ' /I ['; 8761 foreach($pl['opt']['i'] AS $copt) { 8762 $annots .= intval($copt).' '; 8763 } 8764 $annots .= ']'; 8765 } 8766 break; 8767 } 8768 case 'screen': { 8769 break; 8770 } 8771 case 'printermark': { 8772 break; 8773 } 8774 case 'trapnet': { 8775 break; 8776 } 8777 case 'watermark': { 8778 break; 8779 } 8780 case '3d': { 8781 break; 8782 } 8783 default: { 8784 break; 8785 } 8786 } 8787 $annots .= '>>'; 8788 // create new annotation object 8789 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj'); 8790 if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) { 8791 // store reference of form object 8792 $this->form_obj_id[] = $annot_obj_id; 8793 } 8794 } 8795 } 8796 } // end for each page 8797 } 8798 8799 /** 8800 * Put appearance streams XObject used to define annotation's appearance states. 8801 * @param $w (int) annotation width 8802 * @param $h (int) annotation height 8803 * @param $stream (string) appearance stream 8804 * @return int object ID 8805 * @protected 8806 * @since 4.8.001 (2009-09-09) 8807 */ 8808 protected function _putAPXObject($w=0, $h=0, $stream='') { 8809 $stream = trim($stream); 8810 $out = $this->_getobj()."\n"; 8811 $this->xobjects['AX'.$this->n] = array('n' => $this->n); 8812 $out .= '<<'; 8813 $out .= ' /Type /XObject'; 8814 $out .= ' /Subtype /Form'; 8815 $out .= ' /FormType 1'; 8816 if ($this->compress) { 8817 $stream = gzcompress($stream); 8818 $out .= ' /Filter /FlateDecode'; 8819 } 8820 $rect = sprintf('%F %F', $w, $h); 8821 $out .= ' /BBox [0 0 '.$rect.']'; 8822 $out .= ' /Matrix [1 0 0 1 0 0]'; 8823 $out .= ' /Resources 2 0 R'; 8824 $stream = $this->_getrawstream($stream); 8825 $out .= ' /Length '.strlen($stream); 8826 $out .= ' >>'; 8827 $out .= ' stream'."\n".$stream."\n".'endstream'; 8828 $out .= "\n".'endobj'; 8829 $this->_out($out); 8830 return $this->n; 8831 } 8832 8833 /** 8834 * Output fonts. 8835 * @author Nicola Asuni 8836 * @protected 8837 */ 8838 protected function _putfonts() { 8839 $nf = $this->n; 8840 foreach ($this->diffs as $diff) { 8841 //Encodings 8842 $this->_newobj(); 8843 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj'); 8844 } 8845 $mqr = TCPDF_STATIC::get_mqr(); 8846 TCPDF_STATIC::set_mqr(false); 8847 foreach ($this->FontFiles as $file => $info) { 8848 // search and get font file to embedd 8849 $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']); 8850 if (!TCPDF_STATIC::empty_string($fontfile)) { 8851 $font = file_get_contents($fontfile); 8852 $compressed = (substr($file, -2) == '.z'); 8853 if ((!$compressed) AND (isset($info['length2']))) { 8854 $header = (ord($font[0]) == 128); 8855 if ($header) { 8856 // strip first binary header 8857 $font = substr($font, 6); 8858 } 8859 if ($header AND (ord($font[$info['length1']]) == 128)) { 8860 // strip second binary header 8861 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6)); 8862 } 8863 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) { 8864 if ($compressed) { 8865 // uncompress font 8866 $font = gzuncompress($font); 8867 } 8868 // merge subset characters 8869 $subsetchars = array(); // used chars 8870 foreach ($info['fontkeys'] as $fontkey) { 8871 $fontinfo = $this->getFontBuffer($fontkey); 8872 $subsetchars += $fontinfo['subsetchars']; 8873 } 8874 // rebuild a font subset 8875 $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars); 8876 // calculate new font length 8877 $info['length1'] = strlen($font); 8878 if ($compressed) { 8879 // recompress font 8880 $font = gzcompress($font); 8881 } 8882 } 8883 $this->_newobj(); 8884 $this->FontFiles[$file]['n'] = $this->n; 8885 $stream = $this->_getrawstream($font); 8886 $out = '<< /Length '.strlen($stream); 8887 if ($compressed) { 8888 $out .= ' /Filter /FlateDecode'; 8889 } 8890 $out .= ' /Length1 '.$info['length1']; 8891 if (isset($info['length2'])) { 8892 $out .= ' /Length2 '.$info['length2'].' /Length3 0'; 8893 } 8894 $out .= ' >>'; 8895 $out .= ' stream'."\n".$stream."\n".'endstream'; 8896 $out .= "\n".'endobj'; 8897 $this->_out($out); 8898 } 8899 } 8900 TCPDF_STATIC::set_mqr($mqr); 8901 foreach ($this->fontkeys as $k) { 8902 //Font objects 8903 $font = $this->getFontBuffer($k); 8904 $type = $font['type']; 8905 $name = $font['name']; 8906 if ($type == 'core') { 8907 // standard core font 8908 $out = $this->_getobj($this->font_obj_ids[$k])."\n"; 8909 $out .= '<</Type /Font'; 8910 $out .= ' /Subtype /Type1'; 8911 $out .= ' /BaseFont /'.$name; 8912 $out .= ' /Name /F'.$font['i']; 8913 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) { 8914 $out .= ' /Encoding /WinAnsiEncoding'; 8915 } 8916 if ($k == 'helvetica') { 8917 // add default font for annotations 8918 $this->annotation_fonts[$k] = $font['i']; 8919 } 8920 $out .= ' >>'; 8921 $out .= "\n".'endobj'; 8922 $this->_out($out); 8923 } elseif (($type == 'Type1') OR ($type == 'TrueType')) { 8924 // additional Type1 or TrueType font 8925 $out = $this->_getobj($this->font_obj_ids[$k])."\n"; 8926 $out .= '<</Type /Font'; 8927 $out .= ' /Subtype /'.$type; 8928 $out .= ' /BaseFont /'.$name; 8929 $out .= ' /Name /F'.$font['i']; 8930 $out .= ' /FirstChar 32 /LastChar 255'; 8931 $out .= ' /Widths '.($this->n + 1).' 0 R'; 8932 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R'; 8933 if ($font['enc']) { 8934 if (isset($font['diff'])) { 8935 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R'; 8936 } else { 8937 $out .= ' /Encoding /WinAnsiEncoding'; 8938 } 8939 } 8940 $out .= ' >>'; 8941 $out .= "\n".'endobj'; 8942 $this->_out($out); 8943 // Widths 8944 $this->_newobj(); 8945 $s = '['; 8946 for ($i = 32; $i < 256; ++$i) { 8947 if (isset($font['cw'][$i])) { 8948 $s .= $font['cw'][$i].' '; 8949 } else { 8950 $s .= $font['dw'].' '; 8951 } 8952 } 8953 $s .= ']'; 8954 $s .= "\n".'endobj'; 8955 $this->_out($s); 8956 //Descriptor 8957 $this->_newobj(); 8958 $s = '<</Type /FontDescriptor /FontName /'.$name; 8959 foreach ($font['desc'] as $fdk => $fdv) { 8960 if (is_float($fdv)) { 8961 $fdv = sprintf('%F', $fdv); 8962 } 8963 $s .= ' /'.$fdk.' '.$fdv.''; 8964 } 8965 if (!TCPDF_STATIC::empty_string($font['file'])) { 8966 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; 8967 } 8968 $s .= '>>'; 8969 $s .= "\n".'endobj'; 8970 $this->_out($s); 8971 } else { 8972 // additional types 8973 $mtd = '_put'.strtolower($type); 8974 if (!method_exists($this, $mtd)) { 8975 $this->Error('Unsupported font type: '.$type); 8976 } 8977 $this->$mtd($font); 8978 } 8979 } 8980 } 8981 8982 /** 8983 * Adds unicode fonts.<br> 8984 * Based on PDF Reference 1.3 (section 5) 8985 * @param $font (array) font data 8986 * @protected 8987 * @author Nicola Asuni 8988 * @since 1.52.0.TC005 (2005-01-05) 8989 */ 8990 protected function _puttruetypeunicode($font) { 8991 $fontname = ''; 8992 if ($font['subset']) { 8993 // change name for font subsetting 8994 $subtag = sprintf('%06u', $font['i']); 8995 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ'); 8996 $fontname .= $subtag.'+'; 8997 } 8998 $fontname .= $font['name']; 8999 // Type0 Font 9000 // A composite font composed of other fonts, organized hierarchically 9001 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; 9002 $out .= '<< /Type /Font'; 9003 $out .= ' /Subtype /Type0'; 9004 $out .= ' /BaseFont /'.$fontname; 9005 $out .= ' /Name /F'.$font['i']; 9006 $out .= ' /Encoding /'.$font['enc']; 9007 $out .= ' /ToUnicode '.($this->n + 1).' 0 R'; 9008 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]'; 9009 $out .= ' >>'; 9010 $out .= "\n".'endobj'; 9011 $this->_out($out); 9012 // ToUnicode map for Identity-H 9013 $stream = TCPDF_FONT_DATA::$uni_identity_h; 9014 // ToUnicode Object 9015 $this->_newobj(); 9016 $stream = ($this->compress) ? gzcompress($stream) : $stream; 9017 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 9018 $stream = $this->_getrawstream($stream); 9019 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj'); 9020 // CIDFontType2 9021 // A CIDFont whose glyph descriptions are based on TrueType font technology 9022 $oid = $this->_newobj(); 9023 $out = '<< /Type /Font'; 9024 $out .= ' /Subtype /CIDFontType2'; 9025 $out .= ' /BaseFont /'.$fontname; 9026 // A dictionary containing entries that define the character collection of the CIDFont. 9027 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); 9028 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); 9029 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 9030 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>'; 9031 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 9032 $out .= ' /DW '.$font['dw']; // default width 9033 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0); 9034 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) { 9035 $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R'; 9036 } 9037 $out .= ' >>'; 9038 $out .= "\n".'endobj'; 9039 $this->_out($out); 9040 // Font descriptor 9041 // A font descriptor describing the CIDFont default metrics other than its glyph widths 9042 $this->_newobj(); 9043 $out = '<< /Type /FontDescriptor'; 9044 $out .= ' /FontName /'.$fontname; 9045 foreach ($font['desc'] as $key => $value) { 9046 if (is_float($value)) { 9047 $value = sprintf('%F', $value); 9048 } 9049 $out .= ' /'.$key.' '.$value; 9050 } 9051 $fontdir = false; 9052 if (!TCPDF_STATIC::empty_string($font['file'])) { 9053 // A stream containing a TrueType font 9054 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'; 9055 $fontdir = $this->FontFiles[$font['file']]['fontdir']; 9056 } 9057 $out .= ' >>'; 9058 $out .= "\n".'endobj'; 9059 $this->_out($out); 9060 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) { 9061 $this->_newobj(); 9062 // Embed CIDToGIDMap 9063 // A specification of the mapping from CIDs to glyph indices 9064 // search and get CTG font file to embedd 9065 $ctgfile = strtolower($font['ctg']); 9066 // search and get ctg font file to embedd 9067 $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir); 9068 if (TCPDF_STATIC::empty_string($fontfile)) { 9069 $this->Error('Font file not found: '.$ctgfile); 9070 } 9071 $stream = $this->_getrawstream(file_get_contents($fontfile)); 9072 $out = '<< /Length '.strlen($stream).''; 9073 if (substr($fontfile, -2) == '.z') { // check file extension 9074 // Decompresses data encoded using the public-domain 9075 // zlib/deflate compression method, reproducing the 9076 // original text or binary data 9077 $out .= ' /Filter /FlateDecode'; 9078 } 9079 $out .= ' >>'; 9080 $out .= ' stream'."\n".$stream."\n".'endstream'; 9081 $out .= "\n".'endobj'; 9082 $this->_out($out); 9083 } 9084 } 9085 9086 /** 9087 * Output CID-0 fonts. 9088 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format 9089 * @param $font (array) font data 9090 * @protected 9091 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira 9092 * @since 3.2.000 (2008-06-23) 9093 */ 9094 protected function _putcidfont0($font) { 9095 $cidoffset = 0; 9096 if (!isset($font['cw'][1])) { 9097 $cidoffset = 31; 9098 } 9099 if (isset($font['cidinfo']['uni2cid'])) { 9100 // convert unicode to cid. 9101 $uni2cid = $font['cidinfo']['uni2cid']; 9102 $cw = array(); 9103 foreach ($font['cw'] as $uni => $width) { 9104 if (isset($uni2cid[$uni])) { 9105 $cw[($uni2cid[$uni] + $cidoffset)] = $width; 9106 } elseif ($uni < 256) { 9107 $cw[$uni] = $width; 9108 } // else unknown character 9109 } 9110 $font = array_merge($font, array('cw' => $cw)); 9111 } 9112 $name = $font['name']; 9113 $enc = $font['enc']; 9114 if ($enc) { 9115 $longname = $name.'-'.$enc; 9116 } else { 9117 $longname = $name; 9118 } 9119 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; 9120 $out .= '<</Type /Font'; 9121 $out .= ' /Subtype /Type0'; 9122 $out .= ' /BaseFont /'.$longname; 9123 $out .= ' /Name /F'.$font['i']; 9124 if ($enc) { 9125 $out .= ' /Encoding /'.$enc; 9126 } 9127 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]'; 9128 $out .= ' >>'; 9129 $out .= "\n".'endobj'; 9130 $this->_out($out); 9131 $oid = $this->_newobj(); 9132 $out = '<</Type /Font'; 9133 $out .= ' /Subtype /CIDFontType0'; 9134 $out .= ' /BaseFont /'.$name; 9135 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); 9136 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); 9137 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 9138 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>'; 9139 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 9140 $out .= ' /DW '.$font['dw']; 9141 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset); 9142 $out .= ' >>'; 9143 $out .= "\n".'endobj'; 9144 $this->_out($out); 9145 $this->_newobj(); 9146 $s = '<</Type /FontDescriptor /FontName /'.$name; 9147 foreach ($font['desc'] as $k => $v) { 9148 if ($k != 'Style') { 9149 if (is_float($v)) { 9150 $v = sprintf('%F', $v); 9151 } 9152 $s .= ' /'.$k.' '.$v.''; 9153 } 9154 } 9155 $s .= '>>'; 9156 $s .= "\n".'endobj'; 9157 $this->_out($s); 9158 } 9159 9160 /** 9161 * Output images. 9162 * @protected 9163 */ 9164 protected function _putimages() { 9165 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 9166 foreach ($this->imagekeys as $file) { 9167 $info = $this->getImageBuffer($file); 9168 // set object for alternate images array 9169 if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) { 9170 $altoid = $this->_newobj(); 9171 $out = '['; 9172 foreach ($info['altimgs'] as $altimage) { 9173 if (isset($this->xobjects['I'.$altimage[0]]['n'])) { 9174 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R'; 9175 $out .= ' /DefaultForPrinting'; 9176 if ($altimage[1] === true) { 9177 $out .= ' true'; 9178 } else { 9179 $out .= ' false'; 9180 } 9181 $out .= ' >>'; 9182 } 9183 } 9184 $out .= ' ]'; 9185 $out .= "\n".'endobj'; 9186 $this->_out($out); 9187 } 9188 // set image object 9189 $oid = $this->_newobj(); 9190 $this->xobjects['I'.$info['i']] = array('n' => $oid); 9191 $this->setImageSubBuffer($file, 'n', $this->n); 9192 $out = '<</Type /XObject'; 9193 $out .= ' /Subtype /Image'; 9194 $out .= ' /Width '.$info['w']; 9195 $out .= ' /Height '.$info['h']; 9196 if (array_key_exists('masked', $info)) { 9197 $out .= ' /SMask '.($this->n - 1).' 0 R'; 9198 } 9199 // set color space 9200 $icc = false; 9201 if (isset($info['icc']) AND ($info['icc'] !== false)) { 9202 // ICC Colour Space 9203 $icc = true; 9204 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]'; 9205 } elseif ($info['cs'] == 'Indexed') { 9206 // Indexed Colour Space 9207 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]'; 9208 } else { 9209 // Device Colour Space 9210 $out .= ' /ColorSpace /'.$info['cs']; 9211 } 9212 if ($info['cs'] == 'DeviceCMYK') { 9213 $out .= ' /Decode [1 0 1 0 1 0 1 0]'; 9214 } 9215 $out .= ' /BitsPerComponent '.$info['bpc']; 9216 if (isset($altoid) AND ($altoid > 0)) { 9217 // reference to alternate images dictionary 9218 $out .= ' /Alternates '.$altoid.' 0 R'; 9219 } 9220 if (isset($info['exurl']) AND !empty($info['exurl'])) { 9221 // external stream 9222 $out .= ' /Length 0'; 9223 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>'; 9224 if (isset($info['f'])) { 9225 $out .= ' /FFilter /'.$info['f']; 9226 } 9227 $out .= ' >>'; 9228 $out .= ' stream'."\n".'endstream'; 9229 } else { 9230 if (isset($info['f'])) { 9231 $out .= ' /Filter /'.$info['f']; 9232 } 9233 if (isset($info['parms'])) { 9234 $out .= ' '.$info['parms']; 9235 } 9236 if (isset($info['trns']) AND is_array($info['trns'])) { 9237 $trns = ''; 9238 $count_info = count($info['trns']); 9239 if ($info['cs'] == 'Indexed') { 9240 $maxval =(pow(2, $info['bpc']) - 1); 9241 for ($i = 0; $i < $count_info; ++$i) { 9242 if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) { 9243 // this is not a binary type mask @TODO: create a SMask 9244 $trns = ''; 9245 break; 9246 } elseif (empty($trns) AND ($info['trns'][$i] == 0)) { 9247 // store the first fully transparent value 9248 $trns .= $i.' '.$i.' '; 9249 } 9250 } 9251 } else { 9252 // grayscale or RGB 9253 for ($i = 0; $i < $count_info; ++$i) { 9254 if ($info['trns'][$i] == 0) { 9255 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; 9256 } 9257 } 9258 } 9259 // Colour Key Masking 9260 if (!empty($trns)) { 9261 $out .= ' /Mask ['.$trns.']'; 9262 } 9263 } 9264 $stream = $this->_getrawstream($info['data']); 9265 $out .= ' /Length '.strlen($stream).' >>'; 9266 $out .= ' stream'."\n".$stream."\n".'endstream'; 9267 } 9268 $out .= "\n".'endobj'; 9269 $this->_out($out); 9270 if ($icc) { 9271 // ICC colour profile 9272 $this->_newobj(); 9273 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc']; 9274 $icc = $this->_getrawstream($icc); 9275 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); 9276 } elseif ($info['cs'] == 'Indexed') { 9277 // colour palette 9278 $this->_newobj(); 9279 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; 9280 $pal = $this->_getrawstream($pal); 9281 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj'); 9282 } 9283 } 9284 } 9285 9286 /** 9287 * Output Form XObjects Templates. 9288 * @author Nicola Asuni 9289 * @since 5.8.017 (2010-08-24) 9290 * @protected 9291 * @see startTemplate(), endTemplate(), printTemplate() 9292 */ 9293 protected function _putxobjects() { 9294 foreach ($this->xobjects as $key => $data) { 9295 if (isset($data['outdata'])) { 9296 $stream = str_replace($this->epsmarker, '', trim($data['outdata'])); 9297 $out = $this->_getobj($data['n'])."\n"; 9298 $out .= '<<'; 9299 $out .= ' /Type /XObject'; 9300 $out .= ' /Subtype /Form'; 9301 $out .= ' /FormType 1'; 9302 if ($this->compress) { 9303 $stream = gzcompress($stream); 9304 $out .= ' /Filter /FlateDecode'; 9305 } 9306 $out .= sprintf(' /BBox [%F %F %F %F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k)); 9307 $out .= ' /Matrix [1 0 0 1 0 0]'; 9308 $out .= ' /Resources <<'; 9309 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 9310 if (!$this->pdfa_mode) { 9311 // transparency 9312 if (isset($data['extgstates']) AND !empty($data['extgstates'])) { 9313 $out .= ' /ExtGState <<'; 9314 foreach ($data['extgstates'] as $k => $extgstate) { 9315 if (isset($this->extgstates[$k]['name'])) { 9316 $out .= ' /'.$this->extgstates[$k]['name']; 9317 } else { 9318 $out .= ' /GS'.$k; 9319 } 9320 $out .= ' '.$this->extgstates[$k]['n'].' 0 R'; 9321 } 9322 $out .= ' >>'; 9323 } 9324 if (isset($data['gradients']) AND !empty($data['gradients'])) { 9325 $gp = ''; 9326 $gs = ''; 9327 foreach ($data['gradients'] as $id => $grad) { 9328 // gradient patterns 9329 $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R'; 9330 // gradient shadings 9331 $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R'; 9332 } 9333 $out .= ' /Pattern <<'.$gp.' >>'; 9334 $out .= ' /Shading <<'.$gs.' >>'; 9335 } 9336 } 9337 // spot colors 9338 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) { 9339 $out .= ' /ColorSpace <<'; 9340 foreach ($data['spot_colors'] as $name => $color) { 9341 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R'; 9342 } 9343 $out .= ' >>'; 9344 } 9345 // fonts 9346 if (!empty($data['fonts'])) { 9347 $out .= ' /Font <<'; 9348 foreach ($data['fonts'] as $fontkey => $fontid) { 9349 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; 9350 } 9351 $out .= ' >>'; 9352 } 9353 // images or nested xobjects 9354 if (!empty($data['images']) OR !empty($data['xobjects'])) { 9355 $out .= ' /XObject <<'; 9356 foreach ($data['images'] as $imgid) { 9357 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R'; 9358 } 9359 foreach ($data['xobjects'] as $sub_id => $sub_objid) { 9360 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R'; 9361 } 9362 $out .= ' >>'; 9363 } 9364 $out .= ' >>'; //end resources 9365 if (isset($data['group']) AND ($data['group'] !== false)) { 9366 // set transparency group 9367 $out .= ' /Group << /Type /Group /S /Transparency'; 9368 if (is_array($data['group'])) { 9369 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) { 9370 $out .= ' /CS /'.$data['group']['CS']; 9371 } 9372 if (isset($data['group']['I'])) { 9373 $out .= ' /I /'.($data['group']['I']===true?'true':'false'); 9374 } 9375 if (isset($data['group']['K'])) { 9376 $out .= ' /K /'.($data['group']['K']===true?'true':'false'); 9377 } 9378 } 9379 $out .= ' >>'; 9380 } 9381 $stream = $this->_getrawstream($stream, $data['n']); 9382 $out .= ' /Length '.strlen($stream); 9383 $out .= ' >>'; 9384 $out .= ' stream'."\n".$stream."\n".'endstream'; 9385 $out .= "\n".'endobj'; 9386 $this->_out($out); 9387 } 9388 } 9389 } 9390 9391 /** 9392 * Output Spot Colors Resources. 9393 * @protected 9394 * @since 4.0.024 (2008-09-12) 9395 */ 9396 protected function _putspotcolors() { 9397 foreach ($this->spot_colors as $name => $color) { 9398 $this->_newobj(); 9399 $this->spot_colors[$name]['n'] = $this->n; 9400 $out = '[/Separation /'.str_replace(' ', '#20', $name); 9401 $out .= ' /DeviceCMYK <<'; 9402 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]'; 9403 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100)); 9404 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]'; 9405 $out .= "\n".'endobj'; 9406 $this->_out($out); 9407 } 9408 } 9409 9410 /** 9411 * Return XObjects Dictionary. 9412 * @return string XObjects dictionary 9413 * @protected 9414 * @since 5.8.014 (2010-08-23) 9415 */ 9416 protected function _getxobjectdict() { 9417 $out = ''; 9418 foreach ($this->xobjects as $id => $objid) { 9419 $out .= ' /'.$id.' '.$objid['n'].' 0 R'; 9420 } 9421 return $out; 9422 } 9423 9424 /** 9425 * Output Resources Dictionary. 9426 * @protected 9427 */ 9428 protected function _putresourcedict() { 9429 $out = $this->_getobj(2)."\n"; 9430 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 9431 $out .= ' /Font <<'; 9432 foreach ($this->fontkeys as $fontkey) { 9433 $font = $this->getFontBuffer($fontkey); 9434 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R'; 9435 } 9436 $out .= ' >>'; 9437 $out .= ' /XObject <<'; 9438 $out .= $this->_getxobjectdict(); 9439 $out .= ' >>'; 9440 // layers 9441 if (!empty($this->pdflayers)) { 9442 $out .= ' /Properties <<'; 9443 foreach ($this->pdflayers as $layer) { 9444 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R'; 9445 } 9446 $out .= ' >>'; 9447 } 9448 if (!$this->pdfa_mode) { 9449 // transparency 9450 if (isset($this->extgstates) AND !empty($this->extgstates)) { 9451 $out .= ' /ExtGState <<'; 9452 foreach ($this->extgstates as $k => $extgstate) { 9453 if (isset($extgstate['name'])) { 9454 $out .= ' /'.$extgstate['name']; 9455 } else { 9456 $out .= ' /GS'.$k; 9457 } 9458 $out .= ' '.$extgstate['n'].' 0 R'; 9459 } 9460 $out .= ' >>'; 9461 } 9462 if (isset($this->gradients) AND !empty($this->gradients)) { 9463 $gp = ''; 9464 $gs = ''; 9465 foreach ($this->gradients as $id => $grad) { 9466 // gradient patterns 9467 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R'; 9468 // gradient shadings 9469 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R'; 9470 } 9471 $out .= ' /Pattern <<'.$gp.' >>'; 9472 $out .= ' /Shading <<'.$gs.' >>'; 9473 } 9474 } 9475 // spot colors 9476 if (isset($this->spot_colors) AND !empty($this->spot_colors)) { 9477 $out .= ' /ColorSpace <<'; 9478 foreach ($this->spot_colors as $color) { 9479 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R'; 9480 } 9481 $out .= ' >>'; 9482 } 9483 $out .= ' >>'; 9484 $out .= "\n".'endobj'; 9485 $this->_out($out); 9486 } 9487 9488 /** 9489 * Output Resources. 9490 * @protected 9491 */ 9492 protected function _putresources() { 9493 $this->_putextgstates(); 9494 $this->_putocg(); 9495 $this->_putfonts(); 9496 $this->_putimages(); 9497 $this->_putspotcolors(); 9498 $this->_putshaders(); 9499 $this->_putxobjects(); 9500 $this->_putresourcedict(); 9501 $this->_putdests(); 9502 $this->_putEmbeddedFiles(); 9503 $this->_putannotsobjs(); 9504 $this->_putjavascript(); 9505 $this->_putbookmarks(); 9506 $this->_putencryption(); 9507 } 9508 9509 /** 9510 * Adds some Metadata information (Document Information Dictionary) 9511 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference) 9512 * @return int object id 9513 * @protected 9514 */ 9515 protected function _putinfo() { 9516 $oid = $this->_newobj(); 9517 $out = '<<'; 9518 // store current isunicode value 9519 $prev_isunicode = $this->isunicode; 9520 if ($this->docinfounicode) { 9521 $this->isunicode = true; 9522 } 9523 if (!TCPDF_STATIC::empty_string($this->title)) { 9524 // The document's title. 9525 $out .= ' /Title '.$this->_textstring($this->title, $oid); 9526 } 9527 if (!TCPDF_STATIC::empty_string($this->author)) { 9528 // The name of the person who created the document. 9529 $out .= ' /Author '.$this->_textstring($this->author, $oid); 9530 } 9531 if (!TCPDF_STATIC::empty_string($this->subject)) { 9532 // The subject of the document. 9533 $out .= ' /Subject '.$this->_textstring($this->subject, $oid); 9534 } 9535 if (!TCPDF_STATIC::empty_string($this->keywords)) { 9536 // Keywords associated with the document. 9537 $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid); 9538 } 9539 if (!TCPDF_STATIC::empty_string($this->creator)) { 9540 // If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted. 9541 $out .= ' /Creator '.$this->_textstring($this->creator, $oid); 9542 } 9543 // restore previous isunicode value 9544 $this->isunicode = $prev_isunicode; 9545 // default producer 9546 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid); 9547 // The date and time the document was created, in human-readable form 9548 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp); 9549 // The date and time the document was most recently modified, in human-readable form 9550 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp); 9551 // A name object indicating whether the document has been modified to include trapping information 9552 $out .= ' /Trapped /False'; 9553 $out .= ' >>'; 9554 $out .= "\n".'endobj'; 9555 $this->_out($out); 9556 return $oid; 9557 } 9558 9559 /** 9560 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag. 9561 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method! 9562 * @param $xmp (string) Custom XMP data. 9563 * @since 5.9.128 (2011-10-06) 9564 * @public 9565 */ 9566 public function setExtraXMP($xmp) { 9567 $this->custom_xmp = $xmp; 9568 } 9569 9570 /** 9571 * Put XMP data object and return ID. 9572 * @return (int) The object ID. 9573 * @since 5.9.121 (2011-09-28) 9574 * @protected 9575 */ 9576 protected function _putXMP() { 9577 $oid = $this->_newobj(); 9578 // store current isunicode value 9579 $prev_isunicode = $this->isunicode; 9580 $this->isunicode = true; 9581 $prev_encrypted = $this->encrypted; 9582 $this->encrypted = false; 9583 // set XMP data 9584 $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n"; 9585 $xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n"; 9586 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n"; 9587 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n"; 9588 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n"; 9589 $xmp .= "\t\t\t".'<dc:title>'."\n"; 9590 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; 9591 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n"; 9592 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; 9593 $xmp .= "\t\t\t".'</dc:title>'."\n"; 9594 $xmp .= "\t\t\t".'<dc:creator>'."\n"; 9595 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n"; 9596 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n"; 9597 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n"; 9598 $xmp .= "\t\t\t".'</dc:creator>'."\n"; 9599 $xmp .= "\t\t\t".'<dc:description>'."\n"; 9600 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; 9601 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n"; 9602 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; 9603 $xmp .= "\t\t\t".'</dc:description>'."\n"; 9604 $xmp .= "\t\t\t".'<dc:subject>'."\n"; 9605 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; 9606 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n"; 9607 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; 9608 $xmp .= "\t\t\t".'</dc:subject>'."\n"; 9609 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9610 // convert doc creation date format 9611 $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp); 9612 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2); 9613 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2); 9614 $doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2); 9615 $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate); 9616 // convert doc modification date format 9617 $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp); 9618 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2); 9619 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2); 9620 $docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2); 9621 $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate); 9622 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n"; 9623 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n"; 9624 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n"; 9625 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n"; 9626 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n"; 9627 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9628 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n"; 9629 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n"; 9630 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n"; 9631 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9632 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n"; 9633 $uuid = 'uuid:'.substr($this->file_id, 0, 8).'-'.substr($this->file_id, 8, 4).'-'.substr($this->file_id, 12, 4).'-'.substr($this->file_id, 16, 4).'-'.substr($this->file_id, 20, 12); 9634 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n"; 9635 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n"; 9636 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9637 if ($this->pdfa_mode) { 9638 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n"; 9639 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n"; 9640 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n"; 9641 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9642 } 9643 // XMP extension schemas 9644 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n"; 9645 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n"; 9646 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; 9647 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9648 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n"; 9649 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n"; 9650 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n"; 9651 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9652 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9653 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n"; 9654 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n"; 9655 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n"; 9656 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 9657 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 9658 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9659 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9660 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n"; 9661 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n"; 9662 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n"; 9663 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9664 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 9665 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 9666 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9667 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9668 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n"; 9669 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n"; 9670 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n"; 9671 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 9672 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 9673 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9674 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9675 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n"; 9676 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n"; 9677 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n"; 9678 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9679 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9680 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9681 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n"; 9682 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n"; 9683 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; 9684 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9685 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9686 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9687 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n"; 9688 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n"; 9689 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; 9690 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9691 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 9692 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 9693 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9694 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; 9695 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n"; 9696 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9697 $xmp .= "\t".'</rdf:RDF>'."\n"; 9698 $xmp .= $this->custom_xmp; 9699 $xmp .= '</x:xmpmeta>'."\n"; 9700 $xmp .= '<?xpacket end="w"?>'; 9701 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj'; 9702 // restore previous isunicode value 9703 $this->isunicode = $prev_isunicode; 9704 $this->encrypted = $prev_encrypted; 9705 $this->_out($out); 9706 return $oid; 9707 } 9708 9709 /** 9710 * Output Catalog. 9711 * @return int object id 9712 * @protected 9713 */ 9714 protected function _putcatalog() { 9715 // put XMP 9716 $xmpobj = $this->_putXMP(); 9717 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile 9718 if ($this->pdfa_mode OR $this->force_srgb) { 9719 $iccobj = $this->_newobj(); 9720 $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc'); 9721 $filter = ''; 9722 if ($this->compress) { 9723 $filter = ' /Filter /FlateDecode'; 9724 $icc = gzcompress($icc); 9725 } 9726 $icc = $this->_getrawstream($icc); 9727 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); 9728 } 9729 // start catalog 9730 $oid = $this->_newobj(); 9731 $out = '<< /Type /Catalog'; 9732 $out .= ' /Version /'.$this->PDFVersion; 9733 //$out .= ' /Extensions <<>>'; 9734 $out .= ' /Pages 1 0 R'; 9735 //$out .= ' /PageLabels ' //...; 9736 $out .= ' /Names <<'; 9737 if ((!$this->pdfa_mode) AND !empty($this->n_js)) { 9738 $out .= ' /JavaScript '.$this->n_js; 9739 } 9740 if (!empty($this->efnames)) { 9741 $out .= ' /EmbeddedFiles <</Names ['; 9742 foreach ($this->efnames AS $fn => $fref) { 9743 $out .= ' '.$this->_datastring($fn).' '.$fref; 9744 } 9745 $out .= ' ]>>'; 9746 } 9747 $out .= ' >>'; 9748 if (!empty($this->dests)) { 9749 $out .= ' /Dests '.($this->n_dests).' 0 R'; 9750 } 9751 $out .= $this->_putviewerpreferences(); 9752 if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) { 9753 $out .= ' /PageLayout /'.$this->LayoutMode; 9754 } 9755 if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) { 9756 $out .= ' /PageMode /'.$this->PageMode; 9757 } 9758 if (count($this->outlines) > 0) { 9759 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R'; 9760 $out .= ' /PageMode /UseOutlines'; 9761 } 9762 //$out .= ' /Threads []'; 9763 if ($this->ZoomMode == 'fullpage') { 9764 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]'; 9765 } elseif ($this->ZoomMode == 'fullwidth') { 9766 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]'; 9767 } elseif ($this->ZoomMode == 'real') { 9768 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]'; 9769 } elseif (!is_string($this->ZoomMode)) { 9770 $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100)); 9771 } 9772 //$out .= ' /AA <<>>'; 9773 //$out .= ' /URI <<>>'; 9774 $out .= ' /Metadata '.$xmpobj.' 0 R'; 9775 //$out .= ' /StructTreeRoot <<>>'; 9776 //$out .= ' /MarkInfo <<>>'; 9777 if (isset($this->l['a_meta_language'])) { 9778 $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid); 9779 } 9780 //$out .= ' /SpiderInfo <<>>'; 9781 // set OutputIntent to sRGB IEC61966-2.1 if required 9782 if ($this->pdfa_mode OR $this->force_srgb) { 9783 $out .= ' /OutputIntents [<<'; 9784 $out .= ' /Type /OutputIntent'; 9785 $out .= ' /S /GTS_PDFA1'; 9786 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9787 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9788 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid); 9789 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9790 $out .= ' /DestOutputProfile '.$iccobj.' 0 R'; 9791 $out .= ' >>]'; 9792 } 9793 //$out .= ' /PieceInfo <<>>'; 9794 if (!empty($this->pdflayers)) { 9795 $lyrobjs = ''; 9796 $lyrobjs_off = ''; 9797 $lyrobjs_lock = ''; 9798 foreach ($this->pdflayers as $layer) { 9799 $layer_obj_ref = ' '.$layer['objid'].' 0 R'; 9800 $lyrobjs .= $layer_obj_ref; 9801 if ($layer['view'] === false) { 9802 $lyrobjs_off .= $layer_obj_ref; 9803 } 9804 if ($layer['lock']) { 9805 $lyrobjs_lock .= $layer_obj_ref; 9806 } 9807 } 9808 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']'; 9809 $out .= ' /D <<'; 9810 $out .= ' /Name '.$this->_textstring('Layers', $oid); 9811 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid); 9812 $out .= ' /BaseState /ON'; 9813 $out .= ' /OFF ['.$lyrobjs_off.']'; 9814 $out .= ' /Locked ['.$lyrobjs_lock.']'; 9815 $out .= ' /Intent /View'; 9816 $out .= ' /AS ['; 9817 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>'; 9818 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>'; 9819 $out .= ' ]'; 9820 $out .= ' /Order ['.$lyrobjs.']'; 9821 $out .= ' /ListMode /AllPages'; 9822 //$out .= ' /RBGroups ['..']'; 9823 //$out .= ' /Locked ['..']'; 9824 $out .= ' >>'; 9825 $out .= ' >>'; 9826 } 9827 // AcroForm 9828 if (!empty($this->form_obj_id) 9829 OR ($this->sign AND isset($this->signature_data['cert_type'])) 9830 OR !empty($this->empty_signature_appearance)) { 9831 $out .= ' /AcroForm <<'; 9832 $objrefs = ''; 9833 if ($this->sign AND isset($this->signature_data['cert_type'])) { 9834 // set reference for signature object 9835 $objrefs .= $this->sig_obj_id.' 0 R'; 9836 } 9837 if (!empty($this->empty_signature_appearance)) { 9838 foreach ($this->empty_signature_appearance as $esa) { 9839 // set reference for empty signature objects 9840 $objrefs .= ' '.$esa['objid'].' 0 R'; 9841 } 9842 } 9843 if (!empty($this->form_obj_id)) { 9844 foreach($this->form_obj_id as $objid) { 9845 $objrefs .= ' '.$objid.' 0 R'; 9846 } 9847 } 9848 $out .= ' /Fields ['.$objrefs.']'; 9849 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields. 9850 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 9851 $out .= ' /NeedAppearances false'; 9852 } 9853 if ($this->sign AND isset($this->signature_data['cert_type'])) { 9854 if ($this->signature_data['cert_type'] > 0) { 9855 $out .= ' /SigFlags 3'; 9856 } else { 9857 $out .= ' /SigFlags 1'; 9858 } 9859 } 9860 //$out .= ' /CO '; 9861 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) { 9862 $out .= ' /DR <<'; 9863 $out .= ' /Font <<'; 9864 foreach ($this->annotation_fonts as $fontkey => $fontid) { 9865 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; 9866 } 9867 $out .= ' >> >>'; 9868 } 9869 $font = $this->getFontBuffer('helvetica'); 9870 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)'; 9871 $out .= ' /Q '.(($this->rtl)?'2':'0'); 9872 //$out .= ' /XFA '; 9873 $out .= ' >>'; 9874 // signatures 9875 if ($this->sign AND isset($this->signature_data['cert_type']) 9876 AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) { 9877 if ($this->signature_data['cert_type'] > 0) { 9878 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>'; 9879 } else { 9880 $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>'; 9881 } 9882 } 9883 } 9884 //$out .= ' /Legal <<>>'; 9885 //$out .= ' /Requirements []'; 9886 //$out .= ' /Collection <<>>'; 9887 //$out .= ' /NeedsRendering true'; 9888 $out .= ' >>'; 9889 $out .= "\n".'endobj'; 9890 $this->_out($out); 9891 return $oid; 9892 } 9893 9894 /** 9895 * Output viewer preferences. 9896 * @return string for viewer preferences 9897 * @author Nicola asuni 9898 * @since 3.1.000 (2008-06-09) 9899 * @protected 9900 */ 9901 protected function _putviewerpreferences() { 9902 $vp = $this->viewer_preferences; 9903 $out = ' /ViewerPreferences <<'; 9904 if ($this->rtl) { 9905 $out .= ' /Direction /R2L'; 9906 } else { 9907 $out .= ' /Direction /L2R'; 9908 } 9909 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) { 9910 $out .= ' /HideToolbar true'; 9911 } 9912 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) { 9913 $out .= ' /HideMenubar true'; 9914 } 9915 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) { 9916 $out .= ' /HideWindowUI true'; 9917 } 9918 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) { 9919 $out .= ' /FitWindow true'; 9920 } 9921 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) { 9922 $out .= ' /CenterWindow true'; 9923 } 9924 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) { 9925 $out .= ' /DisplayDocTitle true'; 9926 } 9927 if (isset($vp['NonFullScreenPageMode'])) { 9928 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode']; 9929 } 9930 if (isset($vp['ViewArea'])) { 9931 $out .= ' /ViewArea /'.$vp['ViewArea']; 9932 } 9933 if (isset($vp['ViewClip'])) { 9934 $out .= ' /ViewClip /'.$vp['ViewClip']; 9935 } 9936 if (isset($vp['PrintArea'])) { 9937 $out .= ' /PrintArea /'.$vp['PrintArea']; 9938 } 9939 if (isset($vp['PrintClip'])) { 9940 $out .= ' /PrintClip /'.$vp['PrintClip']; 9941 } 9942 if (isset($vp['PrintScaling'])) { 9943 $out .= ' /PrintScaling /'.$vp['PrintScaling']; 9944 } 9945 if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) { 9946 $out .= ' /Duplex /'.$vp['Duplex']; 9947 } 9948 if (isset($vp['PickTrayByPDFSize'])) { 9949 if ($vp['PickTrayByPDFSize']) { 9950 $out .= ' /PickTrayByPDFSize true'; 9951 } else { 9952 $out .= ' /PickTrayByPDFSize false'; 9953 } 9954 } 9955 if (isset($vp['PrintPageRange'])) { 9956 $PrintPageRangeNum = ''; 9957 foreach ($vp['PrintPageRange'] as $k => $v) { 9958 $PrintPageRangeNum .= ' '.($v - 1).''; 9959 } 9960 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']'; 9961 } 9962 if (isset($vp['NumCopies'])) { 9963 $out .= ' /NumCopies '.intval($vp['NumCopies']); 9964 } 9965 $out .= ' >>'; 9966 return $out; 9967 } 9968 9969 /** 9970 * Output PDF File Header (7.5.2). 9971 * @protected 9972 */ 9973 protected function _putheader() { 9974 $this->_out('%PDF-'.$this->PDFVersion); 9975 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3)); 9976 } 9977 9978 /** 9979 * Output end of document (EOF). 9980 * @protected 9981 */ 9982 protected function _enddoc() { 9983 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) { 9984 // save subset chars of the previous font 9985 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 9986 } 9987 $this->state = 1; 9988 $this->_putheader(); 9989 $this->_putpages(); 9990 $this->_putresources(); 9991 // empty signature fields 9992 if (!empty($this->empty_signature_appearance)) { 9993 foreach ($this->empty_signature_appearance as $key => $esa) { 9994 // widget annotation for empty signature 9995 $out = $this->_getobj($esa['objid'])."\n"; 9996 $out .= '<< /Type /Annot'; 9997 $out .= ' /Subtype /Widget'; 9998 $out .= ' /Rect ['.$esa['rect'].']'; 9999 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page 10000 $out .= ' /F 4'; 10001 $out .= ' /FT /Sig'; 10002 $signame = $esa['name'].sprintf(' [%03d]', ($key + 1)); 10003 $out .= ' /T '.$this->_textstring($signame, $esa['objid']); 10004 $out .= ' /Ff 0'; 10005 $out .= ' >>'; 10006 $out .= "\n".'endobj'; 10007 $this->_out($out); 10008 } 10009 } 10010 // Signature 10011 if ($this->sign AND isset($this->signature_data['cert_type'])) { 10012 // widget annotation for signature 10013 $out = $this->_getobj($this->sig_obj_id)."\n"; 10014 $out .= '<< /Type /Annot'; 10015 $out .= ' /Subtype /Widget'; 10016 $out .= ' /Rect ['.$this->signature_appearance['rect'].']'; 10017 $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page 10018 $out .= ' /F 4'; 10019 $out .= ' /FT /Sig'; 10020 $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id); 10021 $out .= ' /Ff 0'; 10022 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R'; 10023 $out .= ' >>'; 10024 $out .= "\n".'endobj'; 10025 $this->_out($out); 10026 // signature 10027 $this->_putsignature(); 10028 } 10029 // Info 10030 $objid_info = $this->_putinfo(); 10031 // Catalog 10032 $objid_catalog = $this->_putcatalog(); 10033 // Cross-ref 10034 $o = $this->bufferlen; 10035 // XREF section 10036 $this->_out('xref'); 10037 $this->_out('0 '.($this->n + 1)); 10038 $this->_out('0000000000 65535 f '); 10039 $freegen = ($this->n + 2); 10040 for ($i=1; $i <= $this->n; ++$i) { 10041 if (!isset($this->offsets[$i]) AND ($i > 1)) { 10042 $this->_out(sprintf('0000000000 %05d f ', $freegen)); 10043 ++$freegen; 10044 } else { 10045 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 10046 } 10047 } 10048 // TRAILER 10049 $out = 'trailer'."\n"; 10050 $out .= '<<'; 10051 $out .= ' /Size '.($this->n + 1); 10052 $out .= ' /Root '.$objid_catalog.' 0 R'; 10053 $out .= ' /Info '.$objid_info.' 0 R'; 10054 if ($this->encrypted) { 10055 $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R'; 10056 } 10057 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]'; 10058 $out .= ' >>'; 10059 $this->_out($out); 10060 $this->_out('startxref'); 10061 $this->_out($o); 10062 $this->_out('%%EOF'); 10063 $this->state = 3; // end-of-doc 10064 if ($this->diskcache) { 10065 // remove temporary files used for images 10066 foreach ($this->imagekeys as $key) { 10067 // remove temporary files 10068 unlink($this->images[$key]); 10069 } 10070 foreach ($this->fontkeys as $key) { 10071 // remove temporary files 10072 unlink($this->fonts[$key]); 10073 } 10074 } 10075 } 10076 10077 /** 10078 * Initialize a new page. 10079 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 10080 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). 10081 * @protected 10082 * @see getPageSizeFromFormat(), setPageFormat() 10083 */ 10084 protected function _beginpage($orientation='', $format='') { 10085 ++$this->page; 10086 $this->pageobjects[$this->page] = array(); 10087 $this->setPageBuffer($this->page, ''); 10088 // initialize array for graphics tranformation positions inside a page buffer 10089 $this->transfmrk[$this->page] = array(); 10090 $this->state = 2; 10091 if (TCPDF_STATIC::empty_string($orientation)) { 10092 if (isset($this->CurOrientation)) { 10093 $orientation = $this->CurOrientation; 10094 } elseif ($this->fwPt > $this->fhPt) { 10095 // landscape 10096 $orientation = 'L'; 10097 } else { 10098 // portrait 10099 $orientation = 'P'; 10100 } 10101 } 10102 if (TCPDF_STATIC::empty_string($format)) { 10103 $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)]; 10104 $this->setPageOrientation($orientation); 10105 } else { 10106 $this->setPageFormat($format, $orientation); 10107 } 10108 if ($this->rtl) { 10109 $this->x = $this->w - $this->rMargin; 10110 } else { 10111 $this->x = $this->lMargin; 10112 } 10113 $this->y = $this->tMargin; 10114 if (isset($this->newpagegroup[$this->page])) { 10115 // start a new group 10116 $this->currpagegroup = $this->newpagegroup[$this->page]; 10117 $this->pagegroups[$this->currpagegroup] = 1; 10118 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { 10119 ++$this->pagegroups[$this->currpagegroup]; 10120 } 10121 } 10122 10123 /** 10124 * Mark end of page. 10125 * @protected 10126 */ 10127 protected function _endpage() { 10128 $this->setVisibility('all'); 10129 $this->state = 1; 10130 } 10131 10132 /** 10133 * Begin a new object and return the object number. 10134 * @return int object number 10135 * @protected 10136 */ 10137 protected function _newobj() { 10138 $this->_out($this->_getobj()); 10139 return $this->n; 10140 } 10141 10142 /** 10143 * Return the starting object string for the selected object ID. 10144 * @param $objid (int) Object ID (leave empty to get a new ID). 10145 * @return string the starting object string 10146 * @protected 10147 * @since 5.8.009 (2010-08-20) 10148 */ 10149 protected function _getobj($objid='') { 10150 if ($objid === '') { 10151 ++$this->n; 10152 $objid = $this->n; 10153 } 10154 $this->offsets[$objid] = $this->bufferlen; 10155 $this->pageobjects[$this->page][] = $objid; 10156 return $objid.' 0 obj'; 10157 } 10158 10159 /** 10160 * Underline text. 10161 * @param $x (int) X coordinate 10162 * @param $y (int) Y coordinate 10163 * @param $txt (string) text to underline 10164 * @protected 10165 */ 10166 protected function _dounderline($x, $y, $txt) { 10167 $w = $this->GetStringWidth($txt); 10168 return $this->_dounderlinew($x, $y, $w); 10169 } 10170 10171 /** 10172 * Underline for rectangular text area. 10173 * @param $x (int) X coordinate 10174 * @param $y (int) Y coordinate 10175 * @param $w (int) width to underline 10176 * @protected 10177 * @since 4.8.008 (2009-09-29) 10178 */ 10179 protected function _dounderlinew($x, $y, $w) { 10180 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10181 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew); 10182 } 10183 10184 /** 10185 * Line through text. 10186 * @param $x (int) X coordinate 10187 * @param $y (int) Y coordinate 10188 * @param $txt (string) text to linethrough 10189 * @protected 10190 */ 10191 protected function _dolinethrough($x, $y, $txt) { 10192 $w = $this->GetStringWidth($txt); 10193 return $this->_dolinethroughw($x, $y, $w); 10194 } 10195 10196 /** 10197 * Line through for rectangular text area. 10198 * @param $x (int) X coordinate 10199 * @param $y (int) Y coordinate 10200 * @param $w (int) line length (width) 10201 * @protected 10202 * @since 4.9.008 (2009-09-29) 10203 */ 10204 protected function _dolinethroughw($x, $y, $w) { 10205 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10206 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew); 10207 } 10208 10209 /** 10210 * Overline text. 10211 * @param $x (int) X coordinate 10212 * @param $y (int) Y coordinate 10213 * @param $txt (string) text to overline 10214 * @protected 10215 * @since 4.9.015 (2010-04-19) 10216 */ 10217 protected function _dooverline($x, $y, $txt) { 10218 $w = $this->GetStringWidth($txt); 10219 return $this->_dooverlinew($x, $y, $w); 10220 } 10221 10222 /** 10223 * Overline for rectangular text area. 10224 * @param $x (int) X coordinate 10225 * @param $y (int) Y coordinate 10226 * @param $w (int) width to overline 10227 * @protected 10228 * @since 4.9.015 (2010-04-19) 10229 */ 10230 protected function _dooverlinew($x, $y, $w) { 10231 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10232 return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew); 10233 10234 } 10235 10236 /** 10237 * Format a data string for meta information 10238 * @param $s (string) data string to escape. 10239 * @param $n (int) object ID 10240 * @return string escaped string. 10241 * @protected 10242 */ 10243 protected function _datastring($s, $n=0) { 10244 if ($n == 0) { 10245 $n = $this->n; 10246 } 10247 $s = $this->_encrypt_data($n, $s); 10248 return '('. TCPDF_STATIC::_escape($s).')'; 10249 } 10250 10251 /** 10252 * Set the document creation timestamp 10253 * @param $time (mixed) Document creation timestamp in seconds or date-time string. 10254 * @public 10255 * @since 5.9.152 (2012-03-23) 10256 */ 10257 public function setDocCreationTimestamp($time) { 10258 if (is_string($time)) { 10259 $time = TCPDF_STATIC::getTimestamp($time); 10260 } 10261 $this->doc_creation_timestamp = intval($time); 10262 } 10263 10264 /** 10265 * Set the document modification timestamp 10266 * @param $time (mixed) Document modification timestamp in seconds or date-time string. 10267 * @public 10268 * @since 5.9.152 (2012-03-23) 10269 */ 10270 public function setDocModificationTimestamp($time) { 10271 if (is_string($time)) { 10272 $time = TCPDF_STATIC::getTimestamp($time); 10273 } 10274 $this->doc_modification_timestamp = intval($time); 10275 } 10276 10277 /** 10278 * Returns document creation timestamp in seconds. 10279 * @return (int) Creation timestamp in seconds. 10280 * @public 10281 * @since 5.9.152 (2012-03-23) 10282 */ 10283 public function getDocCreationTimestamp() { 10284 return $this->doc_creation_timestamp; 10285 } 10286 10287 /** 10288 * Returns document modification timestamp in seconds. 10289 * @return (int) Modfication timestamp in seconds. 10290 * @public 10291 * @since 5.9.152 (2012-03-23) 10292 */ 10293 public function getDocModificationTimestamp() { 10294 return $this->doc_modification_timestamp; 10295 } 10296 10297 /** 10298 * Returns a formatted date for meta information 10299 * @param $n (int) Object ID. 10300 * @param $timestamp (int) Timestamp to convert. 10301 * @return string escaped date string. 10302 * @protected 10303 * @since 4.6.028 (2009-08-25) 10304 */ 10305 protected function _datestring($n=0, $timestamp=0) { 10306 if ((empty($timestamp)) OR ($timestamp < 0)) { 10307 $timestamp = $this->doc_creation_timestamp; 10308 } 10309 return $this->_datastring('D:'.TCPDF_STATIC::getFormattedDate($timestamp), $n); 10310 } 10311 10312 /** 10313 * Format a text string for meta information 10314 * @param $s (string) string to escape. 10315 * @param $n (int) object ID 10316 * @return string escaped string. 10317 * @protected 10318 */ 10319 protected function _textstring($s, $n=0) { 10320 if ($this->isunicode) { 10321 //Convert string to UTF-16BE 10322 $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont); 10323 } 10324 return $this->_datastring($s, $n); 10325 } 10326 10327 /** 10328 * THIS METHOD IS DEPRECATED 10329 * Format a text string 10330 * @param $s (string) string to escape. 10331 * @return string escaped string. 10332 * @protected 10333 * @deprecated 10334 */ 10335 protected function _escapetext($s) { 10336 if ($this->isunicode) { 10337 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 10338 $s = TCPDF_FONTS::UTF8ToLatin1($s, $this->isunicode, $this->CurrentFont); 10339 } else { 10340 //Convert string to UTF-16BE and reverse RTL language 10341 $s = TCPDF_FONTS::utf8StrRev($s, false, $this->tmprtl, $this->isunicode, $this->CurrentFont); 10342 } 10343 } 10344 return TCPDF_STATIC::_escape($s); 10345 } 10346 10347 /** 10348 * get raw output stream. 10349 * @param $s (string) string to output. 10350 * @param $n (int) object reference for encryption mode 10351 * @protected 10352 * @author Nicola Asuni 10353 * @since 5.5.000 (2010-06-22) 10354 */ 10355 protected function _getrawstream($s, $n=0) { 10356 if ($n <= 0) { 10357 // default to current object 10358 $n = $this->n; 10359 } 10360 return $this->_encrypt_data($n, $s); 10361 } 10362 10363 /** 10364 * Format output stream (DEPRECATED). 10365 * @param $s (string) string to output. 10366 * @param $n (int) object reference for encryption mode 10367 * @protected 10368 * @deprecated 10369 */ 10370 protected function _getstream($s, $n=0) { 10371 return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream'; 10372 } 10373 10374 /** 10375 * Output a stream (DEPRECATED). 10376 * @param $s (string) string to output. 10377 * @param $n (int) object reference for encryption mode 10378 * @protected 10379 * @deprecated 10380 */ 10381 protected function _putstream($s, $n=0) { 10382 $this->_out($this->_getstream($s, $n)); 10383 } 10384 10385 /** 10386 * Output a string to the document. 10387 * @param $s (string) string to output. 10388 * @protected 10389 */ 10390 protected function _out($s) { 10391 if ($this->state == 2) { 10392 if ($this->inxobj) { 10393 // we are inside an XObject template 10394 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n"; 10395 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { 10396 // puts data before page footer 10397 $pagebuff = $this->getPageBuffer($this->page); 10398 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); 10399 $footer = substr($pagebuff, -$this->footerlen[$this->page]); 10400 $this->setPageBuffer($this->page, $page.$s."\n".$footer); 10401 // update footer position 10402 $this->footerpos[$this->page] += strlen($s."\n"); 10403 } else { 10404 // set page data 10405 $this->setPageBuffer($this->page, $s."\n", true); 10406 } 10407 } elseif ($this->state > 0) { 10408 // set general data 10409 $this->setBuffer($s."\n"); 10410 } 10411 } 10412 10413 /** 10414 * Set header font. 10415 * @param $font (array) Array describing the basic font parameters: (family, style, size). 10416 * @public 10417 * @since 1.1 10418 */ 10419 public function setHeaderFont($font) { 10420 $this->header_font = $font; 10421 } 10422 10423 /** 10424 * Get header font. 10425 * @return array() Array describing the basic font parameters: (family, style, size). 10426 * @public 10427 * @since 4.0.012 (2008-07-24) 10428 */ 10429 public function getHeaderFont() { 10430 return $this->header_font; 10431 } 10432 10433 /** 10434 * Set footer font. 10435 * @param $font (array) Array describing the basic font parameters: (family, style, size). 10436 * @public 10437 * @since 1.1 10438 */ 10439 public function setFooterFont($font) { 10440 $this->footer_font = $font; 10441 } 10442 10443 /** 10444 * Get Footer font. 10445 * @return array() Array describing the basic font parameters: (family, style, size). 10446 * @public 10447 * @since 4.0.012 (2008-07-24) 10448 */ 10449 public function getFooterFont() { 10450 return $this->footer_font; 10451 } 10452 10453 /** 10454 * Set language array. 10455 * @param $language (array) 10456 * @public 10457 * @since 1.1 10458 */ 10459 public function setLanguageArray($language) { 10460 $this->l = $language; 10461 if (isset($this->l['a_meta_dir'])) { 10462 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; 10463 } else { 10464 $this->rtl = false; 10465 } 10466 } 10467 10468 /** 10469 * Returns the PDF data. 10470 * @public 10471 */ 10472 public function getPDFData() { 10473 if ($this->state < 3) { 10474 $this->Close(); 10475 } 10476 return $this->buffer; 10477 } 10478 10479 /** 10480 * Output anchor link. 10481 * @param $url (string) link URL or internal link (i.e.: <a href="#23,4.5">link to page 23 at 4.5 Y position</a>) 10482 * @param $name (string) link name 10483 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 10484 * @param $firstline (boolean) if true prints only the first line and return the remaining string. 10485 * @param $color (array) array of RGB text color 10486 * @param $style (string) font style (U, D, B, I) 10487 * @param $firstblock (boolean) if true the string is the starting of a line. 10488 * @return the number of cells used or the remaining text if $firstline = true; 10489 * @public 10490 */ 10491 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) { 10492 if (isset($url[1]) AND ($url[0] == '#')) { 10493 // convert url to internal link 10494 $lnkdata = explode(',', $url); 10495 if (isset($lnkdata[0]) ) { 10496 $page = substr($lnkdata[0], 1); 10497 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { 10498 $lnky = floatval($lnkdata[1]); 10499 } else { 10500 $lnky = 0; 10501 } 10502 $url = $this->AddLink(); 10503 $this->SetLink($url, $lnky, $page); 10504 } 10505 } 10506 // store current settings 10507 $prevcolor = $this->fgcolor; 10508 $prevstyle = $this->FontStyle; 10509 if (empty($color)) { 10510 $this->SetTextColorArray($this->htmlLinkColorArray); 10511 } else { 10512 $this->SetTextColorArray($color); 10513 } 10514 if ($style == -1) { 10515 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle); 10516 } else { 10517 $this->SetFont('', $this->FontStyle.$style); 10518 } 10519 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0); 10520 // restore settings 10521 $this->SetFont('', $prevstyle); 10522 $this->SetTextColorArray($prevcolor); 10523 return $ret; 10524 } 10525 10526 /** 10527 * Converts pixels to User's Units. 10528 * @param $px (int) pixels 10529 * @return float value in user's unit 10530 * @public 10531 * @see setImageScale(), getImageScale() 10532 */ 10533 public function pixelsToUnits($px) { 10534 return ($px / ($this->imgscale * $this->k)); 10535 } 10536 10537 /** 10538 * Reverse function for htmlentities. 10539 * Convert entities in UTF-8. 10540 * @param $text_to_convert (string) Text to convert. 10541 * @return string converted text string 10542 * @public 10543 */ 10544 public function unhtmlentities($text_to_convert) { 10545 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); 10546 } 10547 10548 // ENCRYPTION METHODS ---------------------------------- 10549 10550 /** 10551 * Compute encryption key depending on object number where the encrypted data is stored. 10552 * This is used for all strings and streams without crypt filter specifier. 10553 * @param $n (int) object number 10554 * @return int object key 10555 * @protected 10556 * @author Nicola Asuni 10557 * @since 2.0.000 (2008-01-02) 10558 */ 10559 protected function _objectkey($n) { 10560 $objkey = $this->encryptdata['key'].pack('VXxx', $n); 10561 if ($this->encryptdata['mode'] == 2) { // AES-128 10562 // AES padding 10563 $objkey .= "\x73\x41\x6C\x54"; // sAlT 10564 } 10565 $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5)); 10566 $objkey = substr($objkey, 0, 16); 10567 return $objkey; 10568 } 10569 10570 /** 10571 * Encrypt the input string. 10572 * @param $n (int) object number 10573 * @param $s (string) data string to encrypt 10574 * @return encrypted string 10575 * @protected 10576 * @author Nicola Asuni 10577 * @since 5.0.005 (2010-05-11) 10578 */ 10579 protected function _encrypt_data($n, $s) { 10580 if (!$this->encrypted) { 10581 return $s; 10582 } 10583 switch ($this->encryptdata['mode']) { 10584 case 0: // RC4-40 10585 case 1: { // RC4-128 10586 $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c); 10587 break; 10588 } 10589 case 2: { // AES-128 10590 $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s); 10591 break; 10592 } 10593 case 3: { // AES-256 10594 $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s); 10595 break; 10596 } 10597 } 10598 return $s; 10599 } 10600 10601 /** 10602 * Put encryption on PDF document. 10603 * @protected 10604 * @author Nicola Asuni 10605 * @since 2.0.000 (2008-01-02) 10606 */ 10607 protected function _putencryption() { 10608 if (!$this->encrypted) { 10609 return; 10610 } 10611 $this->encryptdata['objid'] = $this->_newobj(); 10612 $out = '<<'; 10613 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) { 10614 $this->encryptdata['Filter'] = 'Standard'; 10615 } 10616 $out .= ' /Filter /'.$this->encryptdata['Filter']; 10617 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) { 10618 $out .= ' /SubFilter /'.$this->encryptdata['SubFilter']; 10619 } 10620 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) { 10621 $this->encryptdata['V'] = 1; 10622 } 10623 // V is a code specifying the algorithm to be used in encrypting and decrypting the document 10624 $out .= ' /V '.$this->encryptdata['V']; 10625 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) { 10626 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256 10627 $out .= ' /Length '.$this->encryptdata['Length']; 10628 } else { 10629 $out .= ' /Length 40'; 10630 } 10631 if ($this->encryptdata['V'] >= 4) { 10632 if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) { 10633 $this->encryptdata['StmF'] = 'Identity'; 10634 } 10635 if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) { 10636 // The name of the crypt filter that shall be used when decrypting all strings in the document. 10637 $this->encryptdata['StrF'] = 'Identity'; 10638 } 10639 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries. 10640 if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) { 10641 $out .= ' /CF <<'; 10642 $out .= ' /'.$this->encryptdata['StmF'].' <<'; 10643 $out .= ' /Type /CryptFilter'; 10644 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) { 10645 // The method used 10646 $out .= ' /CFM /'.$this->encryptdata['CF']['CFM']; 10647 if ($this->encryptdata['pubkey']) { 10648 $out .= ' /Recipients ['; 10649 foreach ($this->encryptdata['Recipients'] as $rec) { 10650 $out .= ' <'.$rec.'>'; 10651 } 10652 $out .= ' ]'; 10653 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { 10654 $out .= ' /EncryptMetadata false'; 10655 } else { 10656 $out .= ' /EncryptMetadata true'; 10657 } 10658 } 10659 } else { 10660 $out .= ' /CFM /None'; 10661 } 10662 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) { 10663 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter. 10664 $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent']; 10665 } else { 10666 $out .= ' /AuthEvent /DocOpen'; 10667 } 10668 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) { 10669 // The bit length of the encryption key. 10670 $out .= ' /Length '.$this->encryptdata['CF']['Length']; 10671 } 10672 $out .= ' >> >>'; 10673 } 10674 // The name of the crypt filter that shall be used by default when decrypting streams. 10675 $out .= ' /StmF /'.$this->encryptdata['StmF']; 10676 // The name of the crypt filter that shall be used when decrypting all strings in the document. 10677 $out .= ' /StrF /'.$this->encryptdata['StrF']; 10678 if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) { 10679 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier. 10680 $out .= ' /EFF /'.$this->encryptdata['']; 10681 } 10682 } 10683 // Additional encryption dictionary entries for the standard security handler 10684 if ($this->encryptdata['pubkey']) { 10685 if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) { 10686 $out .= ' /Recipients ['; 10687 foreach ($this->encryptdata['Recipients'] as $rec) { 10688 $out .= ' <'.$rec.'>'; 10689 } 10690 $out .= ' ]'; 10691 } 10692 } else { 10693 $out .= ' /R'; 10694 if ($this->encryptdata['V'] == 5) { // AES-256 10695 $out .= ' 5'; 10696 $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')'; 10697 $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')'; 10698 $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')'; 10699 } elseif ($this->encryptdata['V'] == 4) { // AES-128 10700 $out .= ' 4'; 10701 } elseif ($this->encryptdata['V'] < 2) { // RC-40 10702 $out .= ' 2'; 10703 } else { // RC-128 10704 $out .= ' 3'; 10705 } 10706 $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')'; 10707 $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')'; 10708 $out .= ' /P '.$this->encryptdata['P']; 10709 if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) { 10710 $out .= ' /EncryptMetadata false'; 10711 } else { 10712 $out .= ' /EncryptMetadata true'; 10713 } 10714 } 10715 $out .= ' >>'; 10716 $out .= "\n".'endobj'; 10717 $this->_out($out); 10718 } 10719 10720 /** 10721 * Compute U value (used for encryption) 10722 * @return string U value 10723 * @protected 10724 * @since 2.0.000 (2008-01-02) 10725 * @author Nicola Asuni 10726 */ 10727 protected function _Uvalue() { 10728 if ($this->encryptdata['mode'] == 0) { // RC4-40 10729 return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c); 10730 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128 10731 $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']); 10732 $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c); 10733 $len = strlen($tmp); 10734 for ($i = 1; $i <= 19; ++$i) { 10735 $ek = ''; 10736 for ($j = 0; $j < $len; ++$j) { 10737 $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i); 10738 } 10739 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c); 10740 } 10741 $enc .= str_repeat("\x00", 16); 10742 return substr($enc, 0, 32); 10743 } elseif ($this->encryptdata['mode'] == 3) { // AES-256 10744 $seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed()); 10745 // User Validation Salt 10746 $this->encryptdata['UVS'] = substr($seed, 0, 8); 10747 // User Key Salt 10748 $this->encryptdata['UKS'] = substr($seed, 8, 16); 10749 return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS']; 10750 } 10751 } 10752 10753 /** 10754 * Compute UE value (used for encryption) 10755 * @return string UE value 10756 * @protected 10757 * @since 5.9.006 (2010-10-19) 10758 * @author Nicola Asuni 10759 */ 10760 protected function _UEvalue() { 10761 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true); 10762 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)); 10763 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv); 10764 } 10765 10766 /** 10767 * Compute O value (used for encryption) 10768 * @return string O value 10769 * @protected 10770 * @since 2.0.000 (2008-01-02) 10771 * @author Nicola Asuni 10772 */ 10773 protected function _Ovalue() { 10774 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128 10775 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']); 10776 if ($this->encryptdata['mode'] > 0) { 10777 for ($i = 0; $i < 50; ++$i) { 10778 $tmp = TCPDF_STATIC::_md5_16($tmp); 10779 } 10780 } 10781 $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8)); 10782 $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c); 10783 if ($this->encryptdata['mode'] > 0) { 10784 $len = strlen($owner_key); 10785 for ($i = 1; $i <= 19; ++$i) { 10786 $ek = ''; 10787 for ($j = 0; $j < $len; ++$j) { 10788 $ek .= chr(ord($owner_key[$j]) ^ $i); 10789 } 10790 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c); 10791 } 10792 } 10793 return $enc; 10794 } elseif ($this->encryptdata['mode'] == 3) { // AES-256 10795 $seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed()); 10796 // Owner Validation Salt 10797 $this->encryptdata['OVS'] = substr($seed, 0, 8); 10798 // Owner Key Salt 10799 $this->encryptdata['OKS'] = substr($seed, 8, 16); 10800 return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS']; 10801 } 10802 } 10803 10804 /** 10805 * Compute OE value (used for encryption) 10806 * @return string OE value 10807 * @protected 10808 * @since 5.9.006 (2010-10-19) 10809 * @author Nicola Asuni 10810 */ 10811 protected function _OEvalue() { 10812 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true); 10813 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)); 10814 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv); 10815 } 10816 10817 /** 10818 * Convert password for AES-256 encryption mode 10819 * @param $password (string) password 10820 * @return string password 10821 * @protected 10822 * @since 5.9.006 (2010-10-19) 10823 * @author Nicola Asuni 10824 */ 10825 protected function _fixAES256Password($password) { 10826 $psw = ''; // password to be returned 10827 $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont); 10828 foreach ($psw_array as $c) { 10829 $psw .= TCPDF_FONTS::unichr($c, $this->isunicode); 10830 } 10831 return substr($psw, 0, 127); 10832 } 10833 10834 /** 10835 * Compute encryption key 10836 * @protected 10837 * @since 2.0.000 (2008-01-02) 10838 * @author Nicola Asuni 10839 */ 10840 protected function _generateencryptionkey() { 10841 $keybytelen = ($this->encryptdata['Length'] / 8); 10842 if (!$this->encryptdata['pubkey']) { // standard mode 10843 if ($this->encryptdata['mode'] == 3) { // AES-256 10844 // generate 256 bit random key 10845 $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen); 10846 // truncate passwords 10847 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']); 10848 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']); 10849 // Compute U value 10850 $this->encryptdata['U'] = $this->_Uvalue(); 10851 // Compute UE value 10852 $this->encryptdata['UE'] = $this->_UEvalue(); 10853 // Compute O value 10854 $this->encryptdata['O'] = $this->_Ovalue(); 10855 // Compute OE value 10856 $this->encryptdata['OE'] = $this->_OEvalue(); 10857 // Compute P value 10858 $this->encryptdata['P'] = $this->encryptdata['protection']; 10859 // Computing the encryption dictionary's Perms (permissions) value 10860 $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3 10861 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7 10862 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8 10863 $perms .= 'F'; 10864 } else { 10865 $perms .= 'T'; 10866 } 10867 $perms .= 'adb'; // bytes 9-11 10868 $perms .= 'nick'; // bytes 12-15 10869 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB)); 10870 $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv); 10871 } else { // RC4-40, RC4-128, AES-128 10872 // Pad passwords 10873 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32); 10874 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32); 10875 // Compute O value 10876 $this->encryptdata['O'] = $this->_Ovalue(); 10877 // get default permissions (reverse byte order) 10878 $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); 10879 // Compute encryption key 10880 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']); 10881 if ($this->encryptdata['mode'] > 0) { 10882 for ($i = 0; $i < 50; ++$i) { 10883 $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen)); 10884 } 10885 } 10886 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen); 10887 // Compute U value 10888 $this->encryptdata['U'] = $this->_Uvalue(); 10889 // Compute P value 10890 $this->encryptdata['P'] = $this->encryptdata['protection']; 10891 } 10892 } else { // Public-Key mode 10893 // random 20-byte seed 10894 $seed = sha1(TCPDF_STATIC::getRandomSeed(), true); 10895 $recipient_bytes = ''; 10896 foreach ($this->encryptdata['pubkeys'] as $pubkey) { 10897 // for each public certificate 10898 if (isset($pubkey['p'])) { 10899 $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']); 10900 } else { 10901 $pkprotection = $this->encryptdata['protection']; 10902 } 10903 // get default permissions (reverse byte order) 10904 $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection); 10905 // envelope data 10906 $envelope = $seed.$pkpermissions; 10907 // write the envelope data to a temporary file 10908 $tempkeyfile = TCPDF_STATIC::getObjFilename('key'); 10909 $f = fopen($tempkeyfile, 'wb'); 10910 if (!$f) { 10911 $this->Error('Unable to create temporary key file: '.$tempkeyfile); 10912 } 10913 $envelope_length = strlen($envelope); 10914 fwrite($f, $envelope, $envelope_length); 10915 fclose($f); 10916 $tempencfile = TCPDF_STATIC::getObjFilename('enc'); 10917 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) { 10918 $this->Error('Unable to encrypt the file: '.$tempkeyfile); 10919 } 10920 unlink($tempkeyfile); 10921 // read encryption signature 10922 $signature = file_get_contents($tempencfile, false, null, $envelope_length); 10923 unlink($tempencfile); 10924 // extract signature 10925 $signature = substr($signature, strpos($signature, 'Content-Disposition')); 10926 $tmparr = explode("\n\n", $signature); 10927 $signature = trim($tmparr[1]); 10928 unset($tmparr); 10929 // decode signature 10930 $signature = base64_decode($signature); 10931 // convert signature to hex 10932 $hexsignature = current(unpack('H*', $signature)); 10933 // store signature on recipients array 10934 $this->encryptdata['Recipients'][] = $hexsignature; 10935 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array 10936 $recipient_bytes .= $signature; 10937 } 10938 // calculate encryption key 10939 if ($this->encryptdata['mode'] == 3) { // AES-256 10940 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen); 10941 } else { // RC4-40, RC4-128, AES-128 10942 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen); 10943 } 10944 } 10945 } 10946 10947 /** 10948 * Set document protection 10949 * Remark: the protection against modification is for people who have the full Acrobat product. 10950 * 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. 10951 * 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. 10952 * @param $permissions (Array) the set of permissions (specify the ones you want to block):<ul><li>print : Print the document;</li><li>modify : Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';</li><li>copy : Copy or otherwise extract text and graphics from the document;</li><li>annot-forms : Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);</li><li>fill-forms : Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;</li><li>extract : Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);</li><li>assemble : Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;</li><li>print-high : Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.</li><li>owner : (inverted logic - only for public-key) when set permits change of encryption and enables all other permissions.</li></ul> 10953 * @param $user_pass (String) user password. Empty by default. 10954 * @param $owner_pass (String) owner password. If not specified, a random value is used. 10955 * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit. 10956 * @param $pubkeys (String) array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../examples/data/cert/tcpdf.crt', 'p' => array('print'))) 10957 * @public 10958 * @since 2.0.000 (2008-01-02) 10959 * @author Nicola Asuni 10960 */ 10961 public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) { 10962 if ($this->pdfa_mode) { 10963 // encryption is not allowed in PDF/A mode 10964 return; 10965 } 10966 $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode); 10967 if (($pubkeys !== null) AND (is_array($pubkeys))) { 10968 // public-key mode 10969 $this->encryptdata['pubkeys'] = $pubkeys; 10970 if ($mode == 0) { 10971 // public-Key Security requires at least 128 bit 10972 $mode = 1; 10973 } 10974 if (!function_exists('openssl_pkcs7_encrypt')) { 10975 $this->Error('Public-Key Security requires openssl library.'); 10976 } 10977 // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec) 10978 $this->encryptdata['pubkey'] = true; 10979 $this->encryptdata['Filter'] = 'Adobe.PubSec'; 10980 $this->encryptdata['StmF'] = 'DefaultCryptFilter'; 10981 $this->encryptdata['StrF'] = 'DefaultCryptFilter'; 10982 } else { 10983 // standard mode (password mode) 10984 $this->encryptdata['pubkey'] = false; 10985 $this->encryptdata['Filter'] = 'Standard'; 10986 $this->encryptdata['StmF'] = 'StdCF'; 10987 $this->encryptdata['StrF'] = 'StdCF'; 10988 } 10989 if ($mode > 1) { // AES 10990 if (!extension_loaded('mcrypt')) { 10991 $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).'); 10992 } 10993 if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) { 10994 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.'); 10995 } 10996 if (($mode == 3) AND !function_exists('hash')) { 10997 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2. 10998 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).'); 10999 } 11000 } 11001 if ($owner_pass === null) { 11002 $owner_pass = md5(TCPDF_STATIC::getRandomSeed()); 11003 } 11004 $this->encryptdata['user_password'] = $user_pass; 11005 $this->encryptdata['owner_password'] = $owner_pass; 11006 $this->encryptdata['mode'] = $mode; 11007 switch ($mode) { 11008 case 0: { // RC4 40 bit 11009 $this->encryptdata['V'] = 1; 11010 $this->encryptdata['Length'] = 40; 11011 $this->encryptdata['CF']['CFM'] = 'V2'; 11012 break; 11013 } 11014 case 1: { // RC4 128 bit 11015 $this->encryptdata['V'] = 2; 11016 $this->encryptdata['Length'] = 128; 11017 $this->encryptdata['CF']['CFM'] = 'V2'; 11018 if ($this->encryptdata['pubkey']) { 11019 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4'; 11020 $this->encryptdata['Recipients'] = array(); 11021 } 11022 break; 11023 } 11024 case 2: { // AES 128 bit 11025 $this->encryptdata['V'] = 4; 11026 $this->encryptdata['Length'] = 128; 11027 $this->encryptdata['CF']['CFM'] = 'AESV2'; 11028 $this->encryptdata['CF']['Length'] = 128; 11029 if ($this->encryptdata['pubkey']) { 11030 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; 11031 $this->encryptdata['Recipients'] = array(); 11032 } 11033 break; 11034 } 11035 case 3: { // AES 256 bit 11036 $this->encryptdata['V'] = 5; 11037 $this->encryptdata['Length'] = 256; 11038 $this->encryptdata['CF']['CFM'] = 'AESV3'; 11039 $this->encryptdata['CF']['Length'] = 256; 11040 if ($this->encryptdata['pubkey']) { 11041 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; 11042 $this->encryptdata['Recipients'] = array(); 11043 } 11044 break; 11045 } 11046 } 11047 $this->encrypted = true; 11048 $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id); 11049 $this->_generateencryptionkey(); 11050 } 11051 11052 // END OF ENCRYPTION FUNCTIONS ------------------------- 11053 11054 // START TRANSFORMATIONS SECTION ----------------------- 11055 11056 /** 11057 * Starts a 2D tranformation saving current graphic state. 11058 * This function must be called before scaling, mirroring, translation, rotation and skewing. 11059 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 11060 * @public 11061 * @since 2.1.000 (2008-01-07) 11062 * @see StartTransform(), StopTransform() 11063 */ 11064 public function StartTransform() { 11065 if ($this->state != 2) { 11066 return; 11067 } 11068 $this->_outSaveGraphicsState(); 11069 if ($this->inxobj) { 11070 // we are inside an XObject template 11071 $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']); 11072 } else { 11073 $this->transfmrk[$this->page][] = $this->pagelen[$this->page]; 11074 } 11075 ++$this->transfmatrix_key; 11076 $this->transfmatrix[$this->transfmatrix_key] = array(); 11077 } 11078 11079 /** 11080 * Stops a 2D tranformation restoring previous graphic state. 11081 * This function must be called after scaling, mirroring, translation, rotation and skewing. 11082 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 11083 * @public 11084 * @since 2.1.000 (2008-01-07) 11085 * @see StartTransform(), StopTransform() 11086 */ 11087 public function StopTransform() { 11088 if ($this->state != 2) { 11089 return; 11090 } 11091 $this->_outRestoreGraphicsState(); 11092 if (isset($this->transfmatrix[$this->transfmatrix_key])) { 11093 array_pop($this->transfmatrix[$this->transfmatrix_key]); 11094 --$this->transfmatrix_key; 11095 } 11096 if ($this->inxobj) { 11097 // we are inside an XObject template 11098 array_pop($this->xobjects[$this->xobjid]['transfmrk']); 11099 } else { 11100 array_pop($this->transfmrk[$this->page]); 11101 } 11102 } 11103 /** 11104 * Horizontal Scaling. 11105 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed. 11106 * @param $x (int) abscissa of the scaling center. Default is current x position 11107 * @param $y (int) ordinate of the scaling center. Default is current y position 11108 * @public 11109 * @since 2.1.000 (2008-01-07) 11110 * @see StartTransform(), StopTransform() 11111 */ 11112 public function ScaleX($s_x, $x='', $y='') { 11113 $this->Scale($s_x, 100, $x, $y); 11114 } 11115 11116 /** 11117 * Vertical Scaling. 11118 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed. 11119 * @param $x (int) abscissa of the scaling center. Default is current x position 11120 * @param $y (int) ordinate of the scaling center. Default is current y position 11121 * @public 11122 * @since 2.1.000 (2008-01-07) 11123 * @see StartTransform(), StopTransform() 11124 */ 11125 public function ScaleY($s_y, $x='', $y='') { 11126 $this->Scale(100, $s_y, $x, $y); 11127 } 11128 11129 /** 11130 * Vertical and horizontal proportional Scaling. 11131 * @param $s (float) scaling factor for width and height as percent. 0 is not allowed. 11132 * @param $x (int) abscissa of the scaling center. Default is current x position 11133 * @param $y (int) ordinate of the scaling center. Default is current y position 11134 * @public 11135 * @since 2.1.000 (2008-01-07) 11136 * @see StartTransform(), StopTransform() 11137 */ 11138 public function ScaleXY($s, $x='', $y='') { 11139 $this->Scale($s, $s, $x, $y); 11140 } 11141 11142 /** 11143 * Vertical and horizontal non-proportional Scaling. 11144 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed. 11145 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed. 11146 * @param $x (int) abscissa of the scaling center. Default is current x position 11147 * @param $y (int) ordinate of the scaling center. Default is current y position 11148 * @public 11149 * @since 2.1.000 (2008-01-07) 11150 * @see StartTransform(), StopTransform() 11151 */ 11152 public function Scale($s_x, $s_y, $x='', $y='') { 11153 if ($x === '') { 11154 $x = $this->x; 11155 } 11156 if ($y === '') { 11157 $y = $this->y; 11158 } 11159 if (($s_x == 0) OR ($s_y == 0)) { 11160 $this->Error('Please do not use values equal to zero for scaling'); 11161 } 11162 $y = ($this->h - $y) * $this->k; 11163 $x *= $this->k; 11164 //calculate elements of transformation matrix 11165 $s_x /= 100; 11166 $s_y /= 100; 11167 $tm = array(); 11168 $tm[0] = $s_x; 11169 $tm[1] = 0; 11170 $tm[2] = 0; 11171 $tm[3] = $s_y; 11172 $tm[4] = $x * (1 - $s_x); 11173 $tm[5] = $y * (1 - $s_y); 11174 //scale the coordinate system 11175 $this->Transform($tm); 11176 } 11177 11178 /** 11179 * Horizontal Mirroring. 11180 * @param $x (int) abscissa of the point. Default is current x position 11181 * @public 11182 * @since 2.1.000 (2008-01-07) 11183 * @see StartTransform(), StopTransform() 11184 */ 11185 public function MirrorH($x='') { 11186 $this->Scale(-100, 100, $x); 11187 } 11188 11189 /** 11190 * Verical Mirroring. 11191 * @param $y (int) ordinate of the point. Default is current y position 11192 * @public 11193 * @since 2.1.000 (2008-01-07) 11194 * @see StartTransform(), StopTransform() 11195 */ 11196 public function MirrorV($y='') { 11197 $this->Scale(100, -100, '', $y); 11198 } 11199 11200 /** 11201 * Point reflection mirroring. 11202 * @param $x (int) abscissa of the point. Default is current x position 11203 * @param $y (int) ordinate of the point. Default is current y position 11204 * @public 11205 * @since 2.1.000 (2008-01-07) 11206 * @see StartTransform(), StopTransform() 11207 */ 11208 public function MirrorP($x='',$y='') { 11209 $this->Scale(-100, -100, $x, $y); 11210 } 11211 11212 /** 11213 * Reflection against a straight line through point (x, y) with the gradient angle (angle). 11214 * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line). 11215 * @param $x (int) abscissa of the point. Default is current x position 11216 * @param $y (int) ordinate of the point. Default is current y position 11217 * @public 11218 * @since 2.1.000 (2008-01-07) 11219 * @see StartTransform(), StopTransform() 11220 */ 11221 public function MirrorL($angle=0, $x='',$y='') { 11222 $this->Scale(-100, 100, $x, $y); 11223 $this->Rotate(-2*($angle-90), $x, $y); 11224 } 11225 11226 /** 11227 * Translate graphic object horizontally. 11228 * @param $t_x (int) movement to the right (or left for RTL) 11229 * @public 11230 * @since 2.1.000 (2008-01-07) 11231 * @see StartTransform(), StopTransform() 11232 */ 11233 public function TranslateX($t_x) { 11234 $this->Translate($t_x, 0); 11235 } 11236 11237 /** 11238 * Translate graphic object vertically. 11239 * @param $t_y (int) movement to the bottom 11240 * @public 11241 * @since 2.1.000 (2008-01-07) 11242 * @see StartTransform(), StopTransform() 11243 */ 11244 public function TranslateY($t_y) { 11245 $this->Translate(0, $t_y); 11246 } 11247 11248 /** 11249 * Translate graphic object horizontally and vertically. 11250 * @param $t_x (int) movement to the right 11251 * @param $t_y (int) movement to the bottom 11252 * @public 11253 * @since 2.1.000 (2008-01-07) 11254 * @see StartTransform(), StopTransform() 11255 */ 11256 public function Translate($t_x, $t_y) { 11257 //calculate elements of transformation matrix 11258 $tm = array(); 11259 $tm[0] = 1; 11260 $tm[1] = 0; 11261 $tm[2] = 0; 11262 $tm[3] = 1; 11263 $tm[4] = $t_x * $this->k; 11264 $tm[5] = -$t_y * $this->k; 11265 //translate the coordinate system 11266 $this->Transform($tm); 11267 } 11268 11269 /** 11270 * Rotate object. 11271 * @param $angle (float) angle in degrees for counter-clockwise rotation 11272 * @param $x (int) abscissa of the rotation center. Default is current x position 11273 * @param $y (int) ordinate of the rotation center. Default is current y position 11274 * @public 11275 * @since 2.1.000 (2008-01-07) 11276 * @see StartTransform(), StopTransform() 11277 */ 11278 public function Rotate($angle, $x='', $y='') { 11279 if ($x === '') { 11280 $x = $this->x; 11281 } 11282 if ($y === '') { 11283 $y = $this->y; 11284 } 11285 $y = ($this->h - $y) * $this->k; 11286 $x *= $this->k; 11287 //calculate elements of transformation matrix 11288 $tm = array(); 11289 $tm[0] = cos(deg2rad($angle)); 11290 $tm[1] = sin(deg2rad($angle)); 11291 $tm[2] = -$tm[1]; 11292 $tm[3] = $tm[0]; 11293 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x); 11294 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x); 11295 //rotate the coordinate system around ($x,$y) 11296 $this->Transform($tm); 11297 } 11298 11299 /** 11300 * Skew horizontally. 11301 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right) 11302 * @param $x (int) abscissa of the skewing center. default is current x position 11303 * @param $y (int) ordinate of the skewing center. default is current y position 11304 * @public 11305 * @since 2.1.000 (2008-01-07) 11306 * @see StartTransform(), StopTransform() 11307 */ 11308 public function SkewX($angle_x, $x='', $y='') { 11309 $this->Skew($angle_x, 0, $x, $y); 11310 } 11311 11312 /** 11313 * Skew vertically. 11314 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 11315 * @param $x (int) abscissa of the skewing center. default is current x position 11316 * @param $y (int) ordinate of the skewing center. default is current y position 11317 * @public 11318 * @since 2.1.000 (2008-01-07) 11319 * @see StartTransform(), StopTransform() 11320 */ 11321 public function SkewY($angle_y, $x='', $y='') { 11322 $this->Skew(0, $angle_y, $x, $y); 11323 } 11324 11325 /** 11326 * Skew. 11327 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right) 11328 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 11329 * @param $x (int) abscissa of the skewing center. default is current x position 11330 * @param $y (int) ordinate of the skewing center. default is current y position 11331 * @public 11332 * @since 2.1.000 (2008-01-07) 11333 * @see StartTransform(), StopTransform() 11334 */ 11335 public function Skew($angle_x, $angle_y, $x='', $y='') { 11336 if ($x === '') { 11337 $x = $this->x; 11338 } 11339 if ($y === '') { 11340 $y = $this->y; 11341 } 11342 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) { 11343 $this->Error('Please use values between -90 and +90 degrees for Skewing.'); 11344 } 11345 $x *= $this->k; 11346 $y = ($this->h - $y) * $this->k; 11347 //calculate elements of transformation matrix 11348 $tm = array(); 11349 $tm[0] = 1; 11350 $tm[1] = tan(deg2rad($angle_y)); 11351 $tm[2] = tan(deg2rad($angle_x)); 11352 $tm[3] = 1; 11353 $tm[4] = -$tm[2] * $y; 11354 $tm[5] = -$tm[1] * $x; 11355 //skew the coordinate system 11356 $this->Transform($tm); 11357 } 11358 11359 /** 11360 * Apply graphic transformations. 11361 * @param $tm (array) transformation matrix 11362 * @protected 11363 * @since 2.1.000 (2008-01-07) 11364 * @see StartTransform(), StopTransform() 11365 */ 11366 protected function Transform($tm) { 11367 if ($this->state != 2) { 11368 return; 11369 } 11370 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); 11371 // add tranformation matrix 11372 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]); 11373 // update transformation mark 11374 if ($this->inxobj) { 11375 // we are inside an XObject template 11376 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 11377 $key = key($this->xobjects[$this->xobjid]['transfmrk']); 11378 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']); 11379 } 11380 } elseif (end($this->transfmrk[$this->page]) !== false) { 11381 $key = key($this->transfmrk[$this->page]); 11382 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page]; 11383 } 11384 } 11385 11386 // END TRANSFORMATIONS SECTION ------------------------- 11387 11388 // START GRAPHIC FUNCTIONS SECTION --------------------- 11389 // The following section is based on the code provided by David Hernandez Sanz 11390 11391 /** 11392 * 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. 11393 * @param $width (float) The width. 11394 * @public 11395 * @since 1.0 11396 * @see Line(), Rect(), Cell(), MultiCell() 11397 */ 11398 public function SetLineWidth($width) { 11399 //Set line width 11400 $this->LineWidth = $width; 11401 $this->linestyleWidth = sprintf('%F w', ($width * $this->k)); 11402 if ($this->state == 2) { 11403 $this->_out($this->linestyleWidth); 11404 } 11405 } 11406 11407 /** 11408 * Returns the current the line width. 11409 * @return int Line width 11410 * @public 11411 * @since 2.1.000 (2008-01-07) 11412 * @see Line(), SetLineWidth() 11413 */ 11414 public function GetLineWidth() { 11415 return $this->LineWidth; 11416 } 11417 11418 /** 11419 * Set line style. 11420 * @param $style (array) Line style. Array with keys among the following: 11421 * <ul> 11422 * <li>width (float): Width of the line in user units.</li> 11423 * <li>cap (string): Type of cap to put on the line. Possible values are: 11424 * butt, round, square. The difference between "square" and "butt" is that 11425 * "square" projects a flat end past the end of the line.</li> 11426 * <li>join (string): Type of join. Possible values are: miter, round, 11427 * bevel.</li> 11428 * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with 11429 * series of length values, which are the lengths of the on and off dashes. 11430 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, 11431 * 1 off, 2 on, 1 off, ...</li> 11432 * <li>phase (integer): Modifier on the dash pattern which is used to shift 11433 * the point at which the pattern starts.</li> 11434 * <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName).</li> 11435 * </ul> 11436 * @param $ret (boolean) if true do not send the command. 11437 * @return string the PDF command 11438 * @public 11439 * @since 2.1.000 (2008-01-08) 11440 */ 11441 public function SetLineStyle($style, $ret=false) { 11442 $s = ''; // string to be returned 11443 if (!is_array($style)) { 11444 return; 11445 } 11446 if (isset($style['width'])) { 11447 $this->LineWidth = $style['width']; 11448 $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k)); 11449 $s .= $this->linestyleWidth.' '; 11450 } 11451 if (isset($style['cap'])) { 11452 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2); 11453 if (isset($ca[$style['cap']])) { 11454 $this->linestyleCap = $ca[$style['cap']].' J'; 11455 $s .= $this->linestyleCap.' '; 11456 } 11457 } 11458 if (isset($style['join'])) { 11459 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); 11460 if (isset($ja[$style['join']])) { 11461 $this->linestyleJoin = $ja[$style['join']].' j'; 11462 $s .= $this->linestyleJoin.' '; 11463 } 11464 } 11465 if (isset($style['dash'])) { 11466 $dash_string = ''; 11467 if ($style['dash']) { 11468 if (preg_match('/^.+,/', $style['dash']) > 0) { 11469 $tab = explode(',', $style['dash']); 11470 } else { 11471 $tab = array($style['dash']); 11472 } 11473 $dash_string = ''; 11474 foreach ($tab as $i => $v) { 11475 if ($i) { 11476 $dash_string .= ' '; 11477 } 11478 $dash_string .= sprintf('%F', $v); 11479 } 11480 } 11481 if (!isset($style['phase']) OR !$style['dash']) { 11482 $style['phase'] = 0; 11483 } 11484 $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']); 11485 $s .= $this->linestyleDash.' '; 11486 } 11487 if (isset($style['color'])) { 11488 $s .= $this->SetDrawColorArray($style['color'], true).' '; 11489 } 11490 if (!$ret AND ($this->state == 2)) { 11491 $this->_out($s); 11492 } 11493 return $s; 11494 } 11495 11496 /** 11497 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment. 11498 * @param $x (float) Abscissa of point. 11499 * @param $y (float) Ordinate of point. 11500 * @protected 11501 * @since 2.1.000 (2008-01-08) 11502 */ 11503 protected function _outPoint($x, $y) { 11504 if ($this->state == 2) { 11505 $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k))); 11506 } 11507 } 11508 11509 /** 11510 * Append a straight line segment from the current point to the point (x, y). 11511 * The new current point shall be (x, y). 11512 * @param $x (float) Abscissa of end point. 11513 * @param $y (float) Ordinate of end point. 11514 * @protected 11515 * @since 2.1.000 (2008-01-08) 11516 */ 11517 protected function _outLine($x, $y) { 11518 if ($this->state == 2) { 11519 $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k))); 11520 } 11521 } 11522 11523 /** 11524 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space. 11525 * @param $x (float) Abscissa of upper-left corner. 11526 * @param $y (float) Ordinate of upper-left corner. 11527 * @param $w (float) Width. 11528 * @param $h (float) Height. 11529 * @param $op (string) options 11530 * @protected 11531 * @since 2.1.000 (2008-01-08) 11532 */ 11533 protected function _outRect($x, $y, $w, $h, $op) { 11534 if ($this->state == 2) { 11535 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op)); 11536 } 11537 } 11538 11539 /** 11540 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the Bezier control points. 11541 * The new current point shall be (x3, y3). 11542 * @param $x1 (float) Abscissa of control point 1. 11543 * @param $y1 (float) Ordinate of control point 1. 11544 * @param $x2 (float) Abscissa of control point 2. 11545 * @param $y2 (float) Ordinate of control point 2. 11546 * @param $x3 (float) Abscissa of end point. 11547 * @param $y3 (float) Ordinate of end point. 11548 * @protected 11549 * @since 2.1.000 (2008-01-08) 11550 */ 11551 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { 11552 if ($this->state == 2) { 11553 $this->_out(sprintf('%F %F %F %F %F %F 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))); 11554 } 11555 } 11556 11557 /** 11558 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the Bezier control points. 11559 * The new current point shall be (x3, y3). 11560 * @param $x2 (float) Abscissa of control point 2. 11561 * @param $y2 (float) Ordinate of control point 2. 11562 * @param $x3 (float) Abscissa of end point. 11563 * @param $y3 (float) Ordinate of end point. 11564 * @protected 11565 * @since 4.9.019 (2010-04-26) 11566 */ 11567 protected function _outCurveV($x2, $y2, $x3, $y3) { 11568 if ($this->state == 2) { 11569 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k))); 11570 } 11571 } 11572 11573 /** 11574 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the Bezier control points. 11575 * The new current point shall be (x3, y3). 11576 * @param $x1 (float) Abscissa of control point 1. 11577 * @param $y1 (float) Ordinate of control point 1. 11578 * @param $x3 (float) Abscissa of end point. 11579 * @param $y3 (float) Ordinate of end point. 11580 * @protected 11581 * @since 2.1.000 (2008-01-08) 11582 */ 11583 protected function _outCurveY($x1, $y1, $x3, $y3) { 11584 if ($this->state == 2) { 11585 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k))); 11586 } 11587 } 11588 11589 /** 11590 * Draws a line between two points. 11591 * @param $x1 (float) Abscissa of first point. 11592 * @param $y1 (float) Ordinate of first point. 11593 * @param $x2 (float) Abscissa of second point. 11594 * @param $y2 (float) Ordinate of second point. 11595 * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array). 11596 * @public 11597 * @since 1.0 11598 * @see SetLineWidth(), SetDrawColor(), SetLineStyle() 11599 */ 11600 public function Line($x1, $y1, $x2, $y2, $style=array()) { 11601 if ($this->state != 2) { 11602 return; 11603 } 11604 if (is_array($style)) { 11605 $this->SetLineStyle($style); 11606 } 11607 $this->_outPoint($x1, $y1); 11608 $this->_outLine($x2, $y2); 11609 $this->_out('S'); 11610 } 11611 11612 /** 11613 * Draws a rectangle. 11614 * @param $x (float) Abscissa of upper-left corner. 11615 * @param $y (float) Ordinate of upper-left corner. 11616 * @param $w (float) Width. 11617 * @param $h (float) Height. 11618 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11619 * @param $border_style (array) Border style of rectangle. Array with keys among the following: 11620 * <ul> 11621 * <li>all: Line style of all borders. Array like for SetLineStyle().</li> 11622 * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li> 11623 * </ul> 11624 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array). 11625 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). 11626 * @public 11627 * @since 1.0 11628 * @see SetLineStyle() 11629 */ 11630 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) { 11631 if ($this->state != 2) { 11632 return; 11633 } 11634 if (empty($style)) { 11635 $style = 'S'; 11636 } 11637 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) { 11638 // set background color 11639 $this->SetFillColorArray($fill_color); 11640 } 11641 if (!empty($border_style)) { 11642 if (isset($border_style['all']) AND !empty($border_style['all'])) { 11643 //set global style for border 11644 $this->SetLineStyle($border_style['all']); 11645 $border_style = array(); 11646 } else { 11647 // remove stroke operator from style 11648 $opnostroke = array('S' => '', 'D' => '', 's' => '', 'd' => '', 'B' => 'F', 'FD' => 'F', 'DF' => 'F', 'B*' => 'F*', 'F*D' => 'F*', 'DF*' => 'F*', 'b' => 'f', 'fd' => 'f', 'df' => 'f', 'b*' => 'f*', 'f*d' => 'f*', 'df*' => 'f*' ); 11649 if (isset($opnostroke[$style])) { 11650 $style = $opnostroke[$style]; 11651 } 11652 } 11653 } 11654 if (!empty($style)) { 11655 $op = TCPDF_STATIC::getPathPaintOperator($style); 11656 $this->_outRect($x, $y, $w, $h, $op); 11657 } 11658 if (!empty($border_style)) { 11659 $border_style2 = array(); 11660 foreach ($border_style as $line => $value) { 11661 $length = strlen($line); 11662 for ($i = 0; $i < $length; ++$i) { 11663 $border_style2[$line[$i]] = $value; 11664 } 11665 } 11666 $border_style = $border_style2; 11667 if (isset($border_style['L']) AND $border_style['L']) { 11668 $this->Line($x, $y, $x, $y + $h, $border_style['L']); 11669 } 11670 if (isset($border_style['T']) AND $border_style['T']) { 11671 $this->Line($x, $y, $x + $w, $y, $border_style['T']); 11672 } 11673 if (isset($border_style['R']) AND $border_style['R']) { 11674 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']); 11675 } 11676 if (isset($border_style['B']) AND $border_style['B']) { 11677 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']); 11678 } 11679 } 11680 } 11681 11682 /** 11683 * Draws a Bezier curve. 11684 * The Bezier curve is a tangent to the line between the control points at 11685 * either end of the curve. 11686 * @param $x0 (float) Abscissa of start point. 11687 * @param $y0 (float) Ordinate of start point. 11688 * @param $x1 (float) Abscissa of control point 1. 11689 * @param $y1 (float) Ordinate of control point 1. 11690 * @param $x2 (float) Abscissa of control point 2. 11691 * @param $y2 (float) Ordinate of control point 2. 11692 * @param $x3 (float) Abscissa of end point. 11693 * @param $y3 (float) Ordinate of end point. 11694 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11695 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array). 11696 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). 11697 * @public 11698 * @see SetLineStyle() 11699 * @since 2.1.000 (2008-01-08) 11700 */ 11701 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) { 11702 if ($this->state != 2) { 11703 return; 11704 } 11705 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11706 $this->SetFillColorArray($fill_color); 11707 } 11708 $op = TCPDF_STATIC::getPathPaintOperator($style); 11709 if ($line_style) { 11710 $this->SetLineStyle($line_style); 11711 } 11712 $this->_outPoint($x0, $y0); 11713 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 11714 $this->_out($op); 11715 } 11716 11717 /** 11718 * Draws a poly-Bezier curve. 11719 * Each Bezier curve segment is a tangent to the line between the control points at 11720 * either end of the curve. 11721 * @param $x0 (float) Abscissa of start point. 11722 * @param $y0 (float) Ordinate of start point. 11723 * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3). 11724 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11725 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array). 11726 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). 11727 * @public 11728 * @see SetLineStyle() 11729 * @since 3.0008 (2008-05-12) 11730 */ 11731 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) { 11732 if ($this->state != 2) { 11733 return; 11734 } 11735 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11736 $this->SetFillColorArray($fill_color); 11737 } 11738 $op = TCPDF_STATIC::getPathPaintOperator($style); 11739 if ($op == 'f') { 11740 $line_style = array(); 11741 } 11742 if ($line_style) { 11743 $this->SetLineStyle($line_style); 11744 } 11745 $this->_outPoint($x0, $y0); 11746 foreach ($segments as $segment) { 11747 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment; 11748 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 11749 } 11750 $this->_out($op); 11751 } 11752 11753 /** 11754 * Draws an ellipse. 11755 * An ellipse is formed from n Bezier curves. 11756 * @param $x0 (float) Abscissa of center point. 11757 * @param $y0 (float) Ordinate of center point. 11758 * @param $rx (float) Horizontal radius. 11759 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0. 11760 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0. 11761 * @param $astart: (float) Angle start of draw line. Default value: 0. 11762 * @param $afinish: (float) Angle finish of draw line. Default value: 360. 11763 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11764 * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array). 11765 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). 11766 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse. 11767 * @author Nicola Asuni 11768 * @public 11769 * @since 2.1.000 (2008-01-08) 11770 */ 11771 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 11772 if ($this->state != 2) { 11773 return; 11774 } 11775 if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) { 11776 $ry = $rx; 11777 } 11778 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11779 $this->SetFillColorArray($fill_color); 11780 } 11781 $op = TCPDF_STATIC::getPathPaintOperator($style); 11782 if ($op == 'f') { 11783 $line_style = array(); 11784 } 11785 if ($line_style) { 11786 $this->SetLineStyle($line_style); 11787 } 11788 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false); 11789 $this->_out($op); 11790 } 11791 11792 /** 11793 * Append an elliptical arc to the current path. 11794 * An ellipse is formed from n Bezier curves. 11795 * @param $xc (float) Abscissa of center point. 11796 * @param $yc (float) Ordinate of center point. 11797 * @param $rx (float) Horizontal radius. 11798 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0. 11799 * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0. 11800 * @param $angs: (float) Angle start of draw line. Default value: 0. 11801 * @param $angf: (float) Angle finish of draw line. Default value: 360. 11802 * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors). 11803 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse. 11804 * @param $startpoint (boolean) if true output a starting point. 11805 * @param $ccw (boolean) if true draws in counter-clockwise. 11806 * @param $svg (boolean) if true the angles are in svg mode (already calculated). 11807 * @return array bounding box coordinates (x min, y min, x max, y max) 11808 * @author Nicola Asuni 11809 * @protected 11810 * @since 4.9.019 (2010-04-26) 11811 */ 11812 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) { 11813 if (($rx <= 0) OR ($ry < 0)) { 11814 return; 11815 } 11816 $k = $this->k; 11817 if ($nc < 2) { 11818 $nc = 2; 11819 } 11820 $xmin = 2147483647; 11821 $ymin = 2147483647; 11822 $xmax = 0; 11823 $ymax = 0; 11824 if ($pie) { 11825 // center of the arc 11826 $this->_outPoint($xc, $yc); 11827 } 11828 $xang = deg2rad((float) $xang); 11829 $angs = deg2rad((float) $angs); 11830 $angf = deg2rad((float) $angf); 11831 if ($svg) { 11832 $as = $angs; 11833 $af = $angf; 11834 } else { 11835 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx)); 11836 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx)); 11837 } 11838 if ($as < 0) { 11839 $as += (2 * M_PI); 11840 } 11841 if ($af < 0) { 11842 $af += (2 * M_PI); 11843 } 11844 if ($ccw AND ($as > $af)) { 11845 // reverse rotation 11846 $as -= (2 * M_PI); 11847 } elseif (!$ccw AND ($as < $af)) { 11848 // reverse rotation 11849 $af -= (2 * M_PI); 11850 } 11851 $total_angle = ($af - $as); 11852 if ($nc < 2) { 11853 $nc = 2; 11854 } 11855 // total arcs to draw 11856 $nc *= (2 * abs($total_angle) / M_PI); 11857 $nc = round($nc) + 1; 11858 // angle of each arc 11859 $arcang = ($total_angle / $nc); 11860 // center point in PDF coordinates 11861 $x0 = $xc; 11862 $y0 = ($this->h - $yc); 11863 // starting angle 11864 $ang = $as; 11865 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3); 11866 $cos_xang = cos($xang); 11867 $sin_xang = sin($xang); 11868 $cos_ang = cos($ang); 11869 $sin_ang = sin($ang); 11870 // first arc point 11871 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 11872 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 11873 // first Bezier control point 11874 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 11875 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 11876 if ($pie) { 11877 // line from center to arc starting point 11878 $this->_outLine($px1, $this->h - $py1); 11879 } elseif ($startpoint) { 11880 // arc starting point 11881 $this->_outPoint($px1, $this->h - $py1); 11882 } 11883 // draw arcs 11884 for ($i = 1; $i <= $nc; ++$i) { 11885 // starting angle 11886 $ang = $as + ($i * $arcang); 11887 if ($i == $nc) { 11888 $ang = $af; 11889 } 11890 $cos_ang = cos($ang); 11891 $sin_ang = sin($ang); 11892 // second arc point 11893 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 11894 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 11895 // second Bezier control point 11896 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 11897 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 11898 // draw arc 11899 $cx1 = ($px1 + $qx1); 11900 $cy1 = ($this->h - ($py1 + $qy1)); 11901 $cx2 = ($px2 - $qx2); 11902 $cy2 = ($this->h - ($py2 - $qy2)); 11903 $cx3 = $px2; 11904 $cy3 = ($this->h - $py2); 11905 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3); 11906 // get bounding box coordinates 11907 $xmin = min($xmin, $cx1, $cx2, $cx3); 11908 $ymin = min($ymin, $cy1, $cy2, $cy3); 11909 $xmax = max($xmax, $cx1, $cx2, $cx3); 11910 $ymax = max($ymax, $cy1, $cy2, $cy3); 11911 // move to next point 11912 $px1 = $px2; 11913 $py1 = $py2; 11914 $qx1 = $qx2; 11915 $qy1 = $qy2; 11916 } 11917 if ($pie) { 11918 $this->_outLine($xc, $yc); 11919 // get bounding box coordinates 11920 $xmin = min($xmin, $xc); 11921 $ymin = min($ymin, $yc); 11922 $xmax = max($xmax, $xc); 11923 $ymax = max($ymax, $yc); 11924 } 11925 return array($xmin, $ymin, $xmax, $ymax); 11926 } 11927 11928 /** 11929 * Draws a circle. 11930 * A circle is formed from n Bezier curves. 11931 * @param $x0 (float) Abscissa of center point. 11932 * @param $y0 (float) Ordinate of center point. 11933 * @param $r (float) Radius. 11934 * @param $angstr: (float) Angle start of draw line. Default value: 0. 11935 * @param $angend: (float) Angle finish of draw line. Default value: 360. 11936 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11937 * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array). 11938 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). 11939 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle. 11940 * @public 11941 * @since 2.1.000 (2008-01-08) 11942 */ 11943 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 11944 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc); 11945 } 11946 11947 /** 11948 * Draws a polygonal line 11949 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 11950 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11951 * @param $line_style (array) Line style of polygon. Array with keys among the following: 11952 * <ul> 11953 * <li>all: Line style of all lines. Array like for SetLineStyle().</li> 11954 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li> 11955 * </ul> 11956 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 11957 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). 11958 * @since 4.8.003 (2009-09-15) 11959 * @public 11960 */ 11961 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) { 11962 $this->Polygon($p, $style, $line_style, $fill_color, false); 11963 } 11964 11965 /** 11966 * Draws a polygon. 11967 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 11968 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11969 * @param $line_style (array) Line style of polygon. Array with keys among the following: 11970 * <ul> 11971 * <li>all: Line style of all lines. Array like for SetLineStyle().</li> 11972 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li> 11973 * </ul> 11974 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 11975 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). 11976 * @param $closed (boolean) if true the polygon is closes, otherwise will remain open 11977 * @public 11978 * @since 2.1.000 (2008-01-08) 11979 */ 11980 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) { 11981 if ($this->state != 2) { 11982 return; 11983 } 11984 $nc = count($p); // number of coordinates 11985 $np = $nc / 2; // number of points 11986 if ($closed) { 11987 // close polygon by adding the first 2 points at the end (one line) 11988 for ($i = 0; $i < 4; ++$i) { 11989 $p[$nc + $i] = $p[$i]; 11990 } 11991 // copy style for the last added line 11992 if (isset($line_style[0])) { 11993 $line_style[$np] = $line_style[0]; 11994 } 11995 $nc += 4; 11996 } 11997 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11998 $this->SetFillColorArray($fill_color); 11999 } 12000 $op = TCPDF_STATIC::getPathPaintOperator($style); 12001 if ($op == 'f') { 12002 $line_style = array(); 12003 } 12004 $draw = true; 12005 if ($line_style) { 12006 if (isset($line_style['all'])) { 12007 $this->SetLineStyle($line_style['all']); 12008 } else { 12009 $draw = false; 12010 if ($op == 'B') { 12011 // draw fill 12012 $op = 'f'; 12013 $this->_outPoint($p[0], $p[1]); 12014 for ($i = 2; $i < $nc; $i = $i + 2) { 12015 $this->_outLine($p[$i], $p[$i + 1]); 12016 } 12017 $this->_out($op); 12018 } 12019 // draw outline 12020 $this->_outPoint($p[0], $p[1]); 12021 for ($i = 2; $i < $nc; $i = $i + 2) { 12022 $line_num = ($i / 2) - 1; 12023 if (isset($line_style[$line_num])) { 12024 if ($line_style[$line_num] != 0) { 12025 if (is_array($line_style[$line_num])) { 12026 $this->_out('S'); 12027 $this->SetLineStyle($line_style[$line_num]); 12028 $this->_outPoint($p[$i - 2], $p[$i - 1]); 12029 $this->_outLine($p[$i], $p[$i + 1]); 12030 $this->_out('S'); 12031 $this->_outPoint($p[$i], $p[$i + 1]); 12032 } else { 12033 $this->_outLine($p[$i], $p[$i + 1]); 12034 } 12035 } 12036 } else { 12037 $this->_outLine($p[$i], $p[$i + 1]); 12038 } 12039 } 12040 $this->_out($op); 12041 } 12042 } 12043 if ($draw) { 12044 $this->_outPoint($p[0], $p[1]); 12045 for ($i = 2; $i < $nc; $i = $i + 2) { 12046 $this->_outLine($p[$i], $p[$i + 1]); 12047 } 12048 $this->_out($op); 12049 } 12050 } 12051 12052 /** 12053 * Draws a regular polygon. 12054 * @param $x0 (float) Abscissa of center point. 12055 * @param $y0 (float) Ordinate of center point. 12056 * @param $r: (float) Radius of inscribed circle. 12057 * @param $ns (integer) Number of sides. 12058 * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0. 12059 * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false. 12060 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12061 * @param $line_style (array) Line style of polygon sides. Array with keys among the following: 12062 * <ul> 12063 * <li>all: Line style of all sides. Array like for SetLineStyle().</li> 12064 * <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li> 12065 * </ul> 12066 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 12067 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). 12068 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are: 12069 * <ul> 12070 * <li>D or empty string: Draw (default).</li> 12071 * <li>F: Fill.</li> 12072 * <li>DF or FD: Draw and fill.</li> 12073 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 12074 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 12075 * </ul> 12076 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array). 12077 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 12078 * @public 12079 * @since 2.1.000 (2008-01-08) 12080 */ 12081 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()) { 12082 if (3 > $ns) { 12083 $ns = 3; 12084 } 12085 if ($draw_circle) { 12086 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 12087 } 12088 $p = array(); 12089 for ($i = 0; $i < $ns; ++$i) { 12090 $a = $angle + ($i * 360 / $ns); 12091 $a_rad = deg2rad((float) $a); 12092 $p[] = $x0 + ($r * sin($a_rad)); 12093 $p[] = $y0 + ($r * cos($a_rad)); 12094 } 12095 $this->Polygon($p, $style, $line_style, $fill_color); 12096 } 12097 12098 /** 12099 * Draws a star polygon 12100 * @param $x0 (float) Abscissa of center point. 12101 * @param $y0 (float) Ordinate of center point. 12102 * @param $r (float) Radius of inscribed circle. 12103 * @param $nv (integer) Number of vertices. 12104 * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon). 12105 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0. 12106 * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false. 12107 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12108 * @param $line_style (array) Line style of polygon sides. Array with keys among the following: 12109 * <ul> 12110 * <li>all: Line style of all sides. Array like for 12111 * SetLineStyle().</li> 12112 * <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li> 12113 * </ul> 12114 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 12115 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). 12116 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are: 12117 * <ul> 12118 * <li>D or empty string: Draw (default).</li> 12119 * <li>F: Fill.</li> 12120 * <li>DF or FD: Draw and fill.</li> 12121 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 12122 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 12123 * </ul> 12124 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array). 12125 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 12126 * @public 12127 * @since 2.1.000 (2008-01-08) 12128 */ 12129 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()) { 12130 if ($nv < 2) { 12131 $nv = 2; 12132 } 12133 if ($draw_circle) { 12134 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 12135 } 12136 $p2 = array(); 12137 $visited = array(); 12138 for ($i = 0; $i < $nv; ++$i) { 12139 $a = $angle + ($i * 360 / $nv); 12140 $a_rad = deg2rad((float) $a); 12141 $p2[] = $x0 + ($r * sin($a_rad)); 12142 $p2[] = $y0 + ($r * cos($a_rad)); 12143 $visited[] = false; 12144 } 12145 $p = array(); 12146 $i = 0; 12147 do { 12148 $p[] = $p2[$i * 2]; 12149 $p[] = $p2[($i * 2) + 1]; 12150 $visited[$i] = true; 12151 $i += $ng; 12152 $i %= $nv; 12153 } while (!$visited[$i]); 12154 $this->Polygon($p, $style, $line_style, $fill_color); 12155 } 12156 12157 /** 12158 * Draws a rounded rectangle. 12159 * @param $x (float) Abscissa of upper-left corner. 12160 * @param $y (float) Ordinate of upper-left corner. 12161 * @param $w (float) Width. 12162 * @param $h (float) Height. 12163 * @param $r (float) the radius of the circle used to round off the corners of the rectangle. 12164 * @param $round_corner (string) 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 right, bottom right, bottom left and top left. Default value: all rounded corner ("1111"). 12165 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12166 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array). 12167 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). 12168 * @public 12169 * @since 2.1.000 (2008-01-08) 12170 */ 12171 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 12172 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color); 12173 } 12174 12175 /** 12176 * Draws a rounded rectangle. 12177 * @param $x (float) Abscissa of upper-left corner. 12178 * @param $y (float) Ordinate of upper-left corner. 12179 * @param $w (float) Width. 12180 * @param $h (float) Height. 12181 * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle. 12182 * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle. 12183 * @param $round_corner (string) 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 right, bottom right, bottom left and top left. Default value: all rounded corner ("1111"). 12184 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12185 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array). 12186 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). 12187 * @public 12188 * @since 4.9.019 (2010-04-22) 12189 */ 12190 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 12191 if ($this->state != 2) { 12192 return; 12193 } 12194 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) { 12195 // Not rounded 12196 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); 12197 return; 12198 } 12199 // Rounded 12200 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 12201 $this->SetFillColorArray($fill_color); 12202 } 12203 $op = TCPDF_STATIC::getPathPaintOperator($style); 12204 if ($op == 'f') { 12205 $border_style = array(); 12206 } 12207 if ($border_style) { 12208 $this->SetLineStyle($border_style); 12209 } 12210 $MyArc = 4 / 3 * (sqrt(2) - 1); 12211 $this->_outPoint($x + $rx, $y); 12212 $xc = $x + $w - $rx; 12213 $yc = $y + $ry; 12214 $this->_outLine($xc, $y); 12215 if ($round_corner[0]) { 12216 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc); 12217 } else { 12218 $this->_outLine($x + $w, $y); 12219 } 12220 $xc = $x + $w - $rx; 12221 $yc = $y + $h - $ry; 12222 $this->_outLine($x + $w, $yc); 12223 if ($round_corner[1]) { 12224 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry); 12225 } else { 12226 $this->_outLine($x + $w, $y + $h); 12227 } 12228 $xc = $x + $rx; 12229 $yc = $y + $h - $ry; 12230 $this->_outLine($xc, $y + $h); 12231 if ($round_corner[2]) { 12232 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc); 12233 } else { 12234 $this->_outLine($x, $y + $h); 12235 } 12236 $xc = $x + $rx; 12237 $yc = $y + $ry; 12238 $this->_outLine($x, $yc); 12239 if ($round_corner[3]) { 12240 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry); 12241 } else { 12242 $this->_outLine($x, $y); 12243 $this->_outLine($x + $rx, $y); 12244 } 12245 $this->_out($op); 12246 } 12247 12248 /** 12249 * Draws a grahic arrow. 12250 * @param $x0 (float) Abscissa of first point. 12251 * @param $y0 (float) Ordinate of first point. 12252 * @param $x1 (float) Abscissa of second point. 12253 * @param $y1 (float) Ordinate of second point. 12254 * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead) 12255 * @param $arm_size (float) length of arrowhead arms 12256 * @param $arm_angle (int) angle between an arm and the shaft 12257 * @author Piotr Galecki, Nicola Asuni, Andy Meier 12258 * @since 4.6.018 (2009-07-10) 12259 */ 12260 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) { 12261 // getting arrow direction angle 12262 // 0 deg angle is when both arms go along X axis. angle grows clockwise. 12263 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1)); 12264 if ($dir_angle < 0) { 12265 $dir_angle += (2 * M_PI); 12266 } 12267 $arm_angle = deg2rad($arm_angle); 12268 $sx1 = $x1; 12269 $sy1 = $y1; 12270 if ($head_style > 0) { 12271 // calculate the stopping point for the arrow shaft 12272 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle)); 12273 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle)); 12274 } 12275 // main arrow line / shaft 12276 $this->Line($x0, $y0, $sx1, $sy1); 12277 // left arrowhead arm tip 12278 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle)); 12279 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle)); 12280 // right arrowhead arm tip 12281 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle)); 12282 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle)); 12283 $mode = 'D'; 12284 $style = array(); 12285 switch ($head_style) { 12286 case 0: { 12287 // draw only arrowhead arms 12288 $mode = 'D'; 12289 $style = array(1, 1, 0); 12290 break; 12291 } 12292 case 1: { 12293 // draw closed arrowhead, but no fill 12294 $mode = 'D'; 12295 break; 12296 } 12297 case 2: { 12298 // closed and filled arrowhead 12299 $mode = 'DF'; 12300 break; 12301 } 12302 case 3: { 12303 // filled arrowhead 12304 $mode = 'F'; 12305 break; 12306 } 12307 } 12308 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array()); 12309 } 12310 12311 // END GRAPHIC FUNCTIONS SECTION ----------------------- 12312 12313 /** 12314 * Add a Named Destination. 12315 * NOTE: destination names are unique, so only last entry will be saved. 12316 * @param $name (string) Destination name. 12317 * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;). 12318 * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages. 12319 * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;). 12320 * @return (string) Stripped named destination identifier or false in case of error. 12321 * @public 12322 * @author Christian Deligant, Nicola Asuni 12323 * @since 5.9.097 (2011-06-23) 12324 */ 12325 public function setDestination($name, $y=-1, $page='', $x=-1) { 12326 // remove unsupported characters 12327 $name = TCPDF_STATIC::encodeNameObject($name); 12328 if (TCPDF_STATIC::empty_string($name)) { 12329 return false; 12330 } 12331 if ($y == -1) { 12332 $y = $this->GetY(); 12333 } elseif ($y < 0) { 12334 $y = 0; 12335 } elseif ($y > $this->h) { 12336 $y = $this->h; 12337 } 12338 if ($x == -1) { 12339 $x = $this->GetX(); 12340 } elseif ($x < 0) { 12341 $x = 0; 12342 } elseif ($x > $this->w) { 12343 $x = $this->w; 12344 } 12345 $fixed = false; 12346 if (!empty($page) AND ($page[0] == '*')) { 12347 $page = intval(substr($page, 1)); 12348 // this page number will not be changed when moving/add/deleting pages 12349 $fixed = true; 12350 } 12351 if (empty($page)) { 12352 $page = $this->PageNo(); 12353 if (empty($page)) { 12354 return; 12355 } 12356 } 12357 $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed); 12358 return $name; 12359 } 12360 12361 /** 12362 * Return the Named Destination array. 12363 * @return (array) Named Destination array. 12364 * @public 12365 * @author Nicola Asuni 12366 * @since 5.9.097 (2011-06-23) 12367 */ 12368 public function getDestination() { 12369 return $this->dests; 12370 } 12371 12372 /** 12373 * Insert Named Destinations. 12374 * @protected 12375 * @author Johannes G\FCntert, Nicola Asuni 12376 * @since 5.9.098 (2011-06-23) 12377 */ 12378 protected function _putdests() { 12379 if (empty($this->dests)) { 12380 return; 12381 } 12382 $this->n_dests = $this->_newobj(); 12383 $out = ' <<'; 12384 foreach($this->dests as $name => $o) { 12385 $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); 12386 } 12387 $out .= ' >>'; 12388 $out .= "\n".'endobj'; 12389 $this->_out($out); 12390 } 12391 12392 /** 12393 * Adds a bookmark - alias for Bookmark(). 12394 * @param $txt (string) Bookmark description. 12395 * @param $level (int) Bookmark level (minimum value is 0). 12396 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;). 12397 * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages. 12398 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. 12399 * @param $color (array) RGB color array (values from 0 to 255). 12400 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). 12401 * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name). 12402 * @public 12403 */ 12404 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { 12405 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link); 12406 } 12407 12408 /** 12409 * Adds a bookmark. 12410 * @param $txt (string) Bookmark description. 12411 * @param $level (int) Bookmark level (minimum value is 0). 12412 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;). 12413 * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages. 12414 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. 12415 * @param $color (array) RGB color array (values from 0 to 255). 12416 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). 12417 * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name). 12418 * @public 12419 * @since 2.1.002 (2008-02-12) 12420 */ 12421 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { 12422 if ($level < 0) { 12423 $level = 0; 12424 } 12425 if (isset($this->outlines[0])) { 12426 $lastoutline = end($this->outlines); 12427 $maxlevel = $lastoutline['l'] + 1; 12428 } else { 12429 $maxlevel = 0; 12430 } 12431 if ($level > $maxlevel) { 12432 $level = $maxlevel; 12433 } 12434 if ($y == -1) { 12435 $y = $this->GetY(); 12436 } elseif ($y < 0) { 12437 $y = 0; 12438 } elseif ($y > $this->h) { 12439 $y = $this->h; 12440 } 12441 if ($x == -1) { 12442 $x = $this->GetX(); 12443 } elseif ($x < 0) { 12444 $x = 0; 12445 } elseif ($x > $this->w) { 12446 $x = $this->w; 12447 } 12448 $fixed = false; 12449 if (!empty($page) AND ($page[0] == '*')) { 12450 $page = intval(substr($page, 1)); 12451 // this page number will not be changed when moving/add/deleting pages 12452 $fixed = true; 12453 } 12454 if (empty($page)) { 12455 $page = $this->PageNo(); 12456 if (empty($page)) { 12457 return; 12458 } 12459 } 12460 $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link); 12461 } 12462 12463 /** 12464 * Sort bookmarks for page and key. 12465 * @protected 12466 * @since 5.9.119 (2011-09-19) 12467 */ 12468 protected function sortBookmarks() { 12469 // get sorting columns 12470 $outline_p = array(); 12471 $outline_y = array(); 12472 foreach ($this->outlines as $key => $row) { 12473 $outline_p[$key] = $row['p']; 12474 $outline_k[$key] = $key; 12475 } 12476 // sort outlines by page and original position 12477 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines); 12478 } 12479 12480 /** 12481 * Create a bookmark PDF string. 12482 * @protected 12483 * @author Olivier Plathey, Nicola Asuni 12484 * @since 2.1.002 (2008-02-12) 12485 */ 12486 protected function _putbookmarks() { 12487 $nb = count($this->outlines); 12488 if ($nb == 0) { 12489 return; 12490 } 12491 // sort bookmarks 12492 $this->sortBookmarks(); 12493 $lru = array(); 12494 $level = 0; 12495 foreach ($this->outlines as $i => $o) { 12496 if ($o['l'] > 0) { 12497 $parent = $lru[($o['l'] - 1)]; 12498 //Set parent and last pointers 12499 $this->outlines[$i]['parent'] = $parent; 12500 $this->outlines[$parent]['last'] = $i; 12501 if ($o['l'] > $level) { 12502 //Level increasing: set first pointer 12503 $this->outlines[$parent]['first'] = $i; 12504 } 12505 } else { 12506 $this->outlines[$i]['parent'] = $nb; 12507 } 12508 if (($o['l'] <= $level) AND ($i > 0)) { 12509 //Set prev and next pointers 12510 $prev = $lru[$o['l']]; 12511 $this->outlines[$prev]['next'] = $i; 12512 $this->outlines[$i]['prev'] = $prev; 12513 } 12514 $lru[$o['l']] = $i; 12515 $level = $o['l']; 12516 } 12517 //Outline items 12518 $n = $this->n + 1; 12519 $nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si'; 12520 foreach ($this->outlines as $i => $o) { 12521 $oid = $this->_newobj(); 12522 // covert HTML title to string 12523 $title = preg_replace($nltags, "\n", $o['t']); 12524 $title = preg_replace("/[\r]+/si", '', $title); 12525 $title = preg_replace("/[\n]+/si", "\n", $title); 12526 $title = strip_tags($title); 12527 $title = $this->stringTrim($title); 12528 $out = '<</Title '.$this->_textstring($title, $oid); 12529 $out .= ' /Parent '.($n + $o['parent']).' 0 R'; 12530 if (isset($o['prev'])) { 12531 $out .= ' /Prev '.($n + $o['prev']).' 0 R'; 12532 } 12533 if (isset($o['next'])) { 12534 $out .= ' /Next '.($n + $o['next']).' 0 R'; 12535 } 12536 if (isset($o['first'])) { 12537 $out .= ' /First '.($n + $o['first']).' 0 R'; 12538 } 12539 if (isset($o['last'])) { 12540 $out .= ' /Last '.($n + $o['last']).' 0 R'; 12541 } 12542 if (isset($o['u']) AND !empty($o['u'])) { 12543 // link 12544 if (is_string($o['u'])) { 12545 if ($o['u'][0] == '#') { 12546 // internal destination 12547 $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1)); 12548 } elseif ($o['u'][0] == '%') { 12549 // embedded PDF file 12550 $filename = basename(substr($o['u'], 1)); 12551 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; 12552 } elseif ($o['u'][0] == '*') { 12553 // embedded generic file 12554 $filename = basename(substr($o['u'], 1)); 12555 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});'; 12556 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>'; 12557 } else { 12558 // external URI link 12559 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>'; 12560 } 12561 } elseif (isset($this->links[$o['u']])) { 12562 // internal link ID 12563 $l = $this->links[$o['u']]; 12564 if (isset($this->page_obj_id[($l['p'])])) { 12565 $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k))); 12566 } 12567 } 12568 } elseif (isset($this->page_obj_id[($o['p'])])) { 12569 // link to a page 12570 $out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); 12571 } 12572 // set font style 12573 $style = 0; 12574 if (!empty($o['s'])) { 12575 // bold 12576 if (strpos($o['s'], 'B') !== false) { 12577 $style |= 2; 12578 } 12579 // oblique 12580 if (strpos($o['s'], 'I') !== false) { 12581 $style |= 1; 12582 } 12583 } 12584 $out .= sprintf(' /F %d', $style); 12585 // set bookmark color 12586 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) { 12587 $color = array_values($o['c']); 12588 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); 12589 } else { 12590 // black 12591 $out .= ' /C [0.0 0.0 0.0]'; 12592 } 12593 $out .= ' /Count 0'; // normally closed item 12594 $out .= ' >>'; 12595 $out .= "\n".'endobj'; 12596 $this->_out($out); 12597 } 12598 //Outline root 12599 $this->OutlineRoot = $this->_newobj(); 12600 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj'); 12601 } 12602 12603 // --- JAVASCRIPT ------------------------------------------------------ 12604 12605 /** 12606 * Adds a javascript 12607 * @param $script (string) Javascript code 12608 * @public 12609 * @author Johannes G\FCntert, Nicola Asuni 12610 * @since 2.1.002 (2008-02-12) 12611 */ 12612 public function IncludeJS($script) { 12613 $this->javascript .= $script; 12614 } 12615 12616 /** 12617 * Adds a javascript object and return object ID 12618 * @param $script (string) Javascript code 12619 * @param $onload (boolean) if true executes this object when opening the document 12620 * @return int internal object ID 12621 * @public 12622 * @author Nicola Asuni 12623 * @since 4.8.000 (2009-09-07) 12624 */ 12625 public function addJavascriptObject($script, $onload=false) { 12626 if ($this->pdfa_mode) { 12627 // javascript is not allowed in PDF/A mode 12628 return false; 12629 } 12630 ++$this->n; 12631 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload); 12632 return $this->n; 12633 } 12634 12635 /** 12636 * Create a javascript PDF string. 12637 * @protected 12638 * @author Johannes G\FCntert, Nicola Asuni 12639 * @since 2.1.002 (2008-02-12) 12640 */ 12641 protected function _putjavascript() { 12642 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) { 12643 return; 12644 } 12645 if (strpos($this->javascript, 'this.addField') > 0) { 12646 if (!$this->ur['enabled']) { 12647 //$this->setUserRights(); 12648 } 12649 // the following two lines are used to avoid form fields duplication after saving 12650 // The addField method only works when releasing user rights (UR3) 12651 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1); 12652 $jsb = "getField('tcpdfdocsaved').value='saved';"; 12653 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb; 12654 } 12655 // name tree for javascript 12656 $this->n_js = '<< /Names ['; 12657 if (!empty($this->javascript)) { 12658 $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R'; 12659 } 12660 if (!empty($this->js_objects)) { 12661 foreach ($this->js_objects as $key => $val) { 12662 if ($val['onload']) { 12663 $this->n_js .= ' (JS'.$key.') '.$key.' 0 R'; 12664 } 12665 } 12666 } 12667 $this->n_js .= ' ] >>'; 12668 // default Javascript object 12669 if (!empty($this->javascript)) { 12670 $obj_id = $this->_newobj(); 12671 $out = '<< /S /JavaScript'; 12672 $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id); 12673 $out .= ' >>'; 12674 $out .= "\n".'endobj'; 12675 $this->_out($out); 12676 } 12677 // additional Javascript objects 12678 if (!empty($this->js_objects)) { 12679 foreach ($this->js_objects as $key => $val) { 12680 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj'; 12681 $this->_out($out); 12682 } 12683 } 12684 } 12685 12686 /** 12687 * Adds a javascript form field. 12688 * @param $type (string) field type 12689 * @param $name (string) field name 12690 * @param $x (int) horizontal position 12691 * @param $y (int) vertical position 12692 * @param $w (int) width 12693 * @param $h (int) height 12694 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12695 * @protected 12696 * @author Denis Van Nuffelen, Nicola Asuni 12697 * @since 2.1.002 (2008-02-12) 12698 */ 12699 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) { 12700 if ($this->rtl) { 12701 $x = $x - $w; 12702 } 12703 // the followind avoid fields duplication after saving the document 12704 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {"; 12705 $k = $this->k; 12706 $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n"; 12707 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n"; 12708 while (list($key, $val) = each($prop)) { 12709 if (strcmp(substr($key, -5), 'Color') == 0) { 12710 $val = TCPDF_COLORS::_JScolor($val); 12711 } else { 12712 $val = "'".$val."'"; 12713 } 12714 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n"; 12715 } 12716 if ($this->rtl) { 12717 $this->x -= $w; 12718 } else { 12719 $this->x += $w; 12720 } 12721 $this->javascript .= '}'; 12722 } 12723 12724 // --- FORM FIELDS ----------------------------------------------------- 12725 12726 12727 12728 /** 12729 * Set default properties for form fields. 12730 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12731 * @public 12732 * @author Nicola Asuni 12733 * @since 4.8.000 (2009-09-06) 12734 */ 12735 public function setFormDefaultProp($prop=array()) { 12736 $this->default_form_prop = $prop; 12737 } 12738 12739 /** 12740 * Return the default properties for form fields. 12741 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12742 * @public 12743 * @author Nicola Asuni 12744 * @since 4.8.000 (2009-09-06) 12745 */ 12746 public function getFormDefaultProp() { 12747 return $this->default_form_prop; 12748 } 12749 12750 /** 12751 * Creates a text field 12752 * @param $name (string) field name 12753 * @param $w (float) Width of the rectangle 12754 * @param $h (float) Height of the rectangle 12755 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12756 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 12757 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12758 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12759 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 12760 * @public 12761 * @author Nicola Asuni 12762 * @since 4.8.000 (2009-09-07) 12763 */ 12764 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 12765 if ($x === '') { 12766 $x = $this->x; 12767 } 12768 if ($y === '') { 12769 $y = $this->y; 12770 } 12771 // check page for no-write regions and adapt page margins if necessary 12772 list($x, $y) = $this->checkPageRegions($h, $x, $y); 12773 if ($js) { 12774 $this->_addfield('text', $name, $x, $y, $w, $h, $prop); 12775 return; 12776 } 12777 // get default style 12778 $prop = array_merge($this->getFormDefaultProp(), $prop); 12779 // get annotation data 12780 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12781 // set default appearance stream 12782 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 12783 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 12784 $popt['da'] = $fontstyle; 12785 // build appearance stream 12786 $popt['ap'] = array(); 12787 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 12788 $text = ''; 12789 if (isset($prop['value']) AND !empty($prop['value'])) { 12790 $text = $prop['value']; 12791 } elseif (isset($opt['v']) AND !empty($opt['v'])) { 12792 $text = $opt['v']; 12793 } 12794 $tmpid = $this->startTemplate($w, $h, false); 12795 $align = ''; 12796 if (isset($popt['q'])) { 12797 switch ($popt['q']) { 12798 case 0: { 12799 $align = 'L'; 12800 break; 12801 } 12802 case 1: { 12803 $align = 'C'; 12804 break; 12805 } 12806 case 2: { 12807 $align = 'R'; 12808 break; 12809 } 12810 default: { 12811 $align = ''; 12812 break; 12813 } 12814 } 12815 } 12816 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 12817 $this->endTemplate(); 12818 --$this->n; 12819 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 12820 unset($this->xobjects[$tmpid]); 12821 $popt['ap']['n'] .= 'Q EMC'; 12822 // merge options 12823 $opt = array_merge($popt, $opt); 12824 // remove some conflicting options 12825 unset($opt['bs']); 12826 // set remaining annotation data 12827 $opt['Subtype'] = 'Widget'; 12828 $opt['ft'] = 'Tx'; 12829 $opt['t'] = $name; 12830 // Additional annotation's parameters (check _putannotsobj() method): 12831 //$opt['f'] 12832 //$opt['as'] 12833 //$opt['bs'] 12834 //$opt['be'] 12835 //$opt['c'] 12836 //$opt['border'] 12837 //$opt['h'] 12838 //$opt['mk']; 12839 //$opt['mk']['r'] 12840 //$opt['mk']['bc']; 12841 //$opt['mk']['bg']; 12842 unset($opt['mk']['ca']); 12843 unset($opt['mk']['rc']); 12844 unset($opt['mk']['ac']); 12845 unset($opt['mk']['i']); 12846 unset($opt['mk']['ri']); 12847 unset($opt['mk']['ix']); 12848 unset($opt['mk']['if']); 12849 //$opt['mk']['if']['sw']; 12850 //$opt['mk']['if']['s']; 12851 //$opt['mk']['if']['a']; 12852 //$opt['mk']['if']['fb']; 12853 unset($opt['mk']['tp']); 12854 //$opt['tu'] 12855 //$opt['tm'] 12856 //$opt['ff'] 12857 //$opt['v'] 12858 //$opt['dv'] 12859 //$opt['a'] 12860 //$opt['aa'] 12861 //$opt['q'] 12862 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 12863 if ($this->rtl) { 12864 $this->x -= $w; 12865 } else { 12866 $this->x += $w; 12867 } 12868 } 12869 12870 /** 12871 * Creates a RadioButton field. 12872 * @param $name (string) Field name. 12873 * @param $w (int) Width of the radio button. 12874 * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12875 * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference. 12876 * @param $onvalue (string) Value to be returned if selected. 12877 * @param $checked (boolean) Define the initial state. 12878 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12879 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12880 * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered). 12881 * @public 12882 * @author Nicola Asuni 12883 * @since 4.8.000 (2009-09-07) 12884 */ 12885 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) { 12886 if ($x === '') { 12887 $x = $this->x; 12888 } 12889 if ($y === '') { 12890 $y = $this->y; 12891 } 12892 // check page for no-write regions and adapt page margins if necessary 12893 list($x, $y) = $this->checkPageRegions($w, $x, $y); 12894 if ($js) { 12895 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop); 12896 return; 12897 } 12898 if (TCPDF_STATIC::empty_string($onvalue)) { 12899 $onvalue = 'On'; 12900 } 12901 if ($checked) { 12902 $defval = $onvalue; 12903 } else { 12904 $defval = 'Off'; 12905 } 12906 // set font 12907 $font = 'zapfdingbats'; 12908 if ($this->pdfa_mode) { 12909 // all fonts must be embedded 12910 $font = 'pdfa'.$font; 12911 } 12912 $this->AddFont($font); 12913 $tmpfont = $this->getFontBuffer($font); 12914 // set data for parent group 12915 if (!isset($this->radiobutton_groups[$this->page])) { 12916 $this->radiobutton_groups[$this->page] = array(); 12917 } 12918 if (!isset($this->radiobutton_groups[$this->page][$name])) { 12919 $this->radiobutton_groups[$this->page][$name] = array(); 12920 ++$this->n; 12921 $this->radiobutton_groups[$this->page][$name]['n'] = $this->n; 12922 $this->radio_groups[] = $this->n; 12923 } 12924 $kid = ($this->n + 1); 12925 // save object ID to be added on Kids entry on parent object 12926 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval); 12927 // get default style 12928 $prop = array_merge($this->getFormDefaultProp(), $prop); 12929 $prop['NoToggleToOff'] = 'true'; 12930 $prop['Radio'] = 'true'; 12931 $prop['borderStyle'] = 'inset'; 12932 // get annotation data 12933 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12934 // set additional default options 12935 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; 12936 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); 12937 $popt['da'] = $fontstyle; 12938 // build appearance stream 12939 $popt['ap'] = array(); 12940 $popt['ap']['n'] = array(); 12941 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k); 12942 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); 12943 $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy); 12944 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy); 12945 if (!isset($popt['mk'])) { 12946 $popt['mk'] = array(); 12947 } 12948 $popt['mk']['ca'] = '(l)'; 12949 // merge options 12950 $opt = array_merge($popt, $opt); 12951 // set remaining annotation data 12952 $opt['Subtype'] = 'Widget'; 12953 $opt['ft'] = 'Btn'; 12954 if ($checked) { 12955 $opt['v'] = array('/'.$onvalue); 12956 $opt['as'] = $onvalue; 12957 } else { 12958 $opt['as'] = 'Off'; 12959 } 12960 // store readonly flag 12961 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) { 12962 $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false; 12963 } 12964 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64); 12965 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 12966 if ($this->rtl) { 12967 $this->x -= $w; 12968 } else { 12969 $this->x += $w; 12970 } 12971 } 12972 12973 /** 12974 * Creates a List-box field 12975 * @param $name (string) field name 12976 * @param $w (int) width 12977 * @param $h (int) height 12978 * @param $values (array) array containing the list of values. 12979 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12980 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 12981 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12982 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12983 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 12984 * @public 12985 * @author Nicola Asuni 12986 * @since 4.8.000 (2009-09-07) 12987 */ 12988 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 12989 if ($x === '') { 12990 $x = $this->x; 12991 } 12992 if ($y === '') { 12993 $y = $this->y; 12994 } 12995 // check page for no-write regions and adapt page margins if necessary 12996 list($x, $y) = $this->checkPageRegions($h, $x, $y); 12997 if ($js) { 12998 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop); 12999 $s = ''; 13000 foreach ($values as $value) { 13001 if (is_array($value)) { 13002 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']'; 13003 } else { 13004 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']'; 13005 } 13006 } 13007 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n"; 13008 return; 13009 } 13010 // get default style 13011 $prop = array_merge($this->getFormDefaultProp(), $prop); 13012 // get annotation data 13013 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13014 // set additional default values 13015 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 13016 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 13017 $popt['da'] = $fontstyle; 13018 // build appearance stream 13019 $popt['ap'] = array(); 13020 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 13021 $text = ''; 13022 foreach($values as $item) { 13023 if (is_array($item)) { 13024 $text .= $item[1]."\n"; 13025 } else { 13026 $text .= $item."\n"; 13027 } 13028 } 13029 $tmpid = $this->startTemplate($w, $h, false); 13030 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 13031 $this->endTemplate(); 13032 --$this->n; 13033 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 13034 unset($this->xobjects[$tmpid]); 13035 $popt['ap']['n'] .= 'Q EMC'; 13036 // merge options 13037 $opt = array_merge($popt, $opt); 13038 // set remaining annotation data 13039 $opt['Subtype'] = 'Widget'; 13040 $opt['ft'] = 'Ch'; 13041 $opt['t'] = $name; 13042 $opt['opt'] = $values; 13043 unset($opt['mk']['ca']); 13044 unset($opt['mk']['rc']); 13045 unset($opt['mk']['ac']); 13046 unset($opt['mk']['i']); 13047 unset($opt['mk']['ri']); 13048 unset($opt['mk']['ix']); 13049 unset($opt['mk']['if']); 13050 unset($opt['mk']['tp']); 13051 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 13052 if ($this->rtl) { 13053 $this->x -= $w; 13054 } else { 13055 $this->x += $w; 13056 } 13057 } 13058 13059 /** 13060 * Creates a Combo-box field 13061 * @param $name (string) field name 13062 * @param $w (int) width 13063 * @param $h (int) height 13064 * @param $values (array) array containing the list of values. 13065 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 13066 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 13067 * @param $x (float) Abscissa of the upper-left corner of the rectangle 13068 * @param $y (float) Ordinate of the upper-left corner of the rectangle 13069 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 13070 * @public 13071 * @author Nicola Asuni 13072 * @since 4.8.000 (2009-09-07) 13073 */ 13074 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 13075 if ($x === '') { 13076 $x = $this->x; 13077 } 13078 if ($y === '') { 13079 $y = $this->y; 13080 } 13081 // check page for no-write regions and adapt page margins if necessary 13082 list($x, $y) = $this->checkPageRegions($h, $x, $y); 13083 if ($js) { 13084 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop); 13085 $s = ''; 13086 foreach ($values as $value) { 13087 if (is_array($value)) { 13088 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']'; 13089 } else { 13090 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']'; 13091 } 13092 } 13093 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n"; 13094 return; 13095 } 13096 // get default style 13097 $prop = array_merge($this->getFormDefaultProp(), $prop); 13098 $prop['Combo'] = true; 13099 // get annotation data 13100 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13101 // set additional default options 13102 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 13103 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 13104 $popt['da'] = $fontstyle; 13105 // build appearance stream 13106 $popt['ap'] = array(); 13107 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 13108 $text = ''; 13109 foreach($values as $item) { 13110 if (is_array($item)) { 13111 $text .= $item[1]."\n"; 13112 } else { 13113 $text .= $item."\n"; 13114 } 13115 } 13116 $tmpid = $this->startTemplate($w, $h, false); 13117 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 13118 $this->endTemplate(); 13119 --$this->n; 13120 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 13121 unset($this->xobjects[$tmpid]); 13122 $popt['ap']['n'] .= 'Q EMC'; 13123 // merge options 13124 $opt = array_merge($popt, $opt); 13125 // set remaining annotation data 13126 $opt['Subtype'] = 'Widget'; 13127 $opt['ft'] = 'Ch'; 13128 $opt['t'] = $name; 13129 $opt['opt'] = $values; 13130 unset($opt['mk']['ca']); 13131 unset($opt['mk']['rc']); 13132 unset($opt['mk']['ac']); 13133 unset($opt['mk']['i']); 13134 unset($opt['mk']['ri']); 13135 unset($opt['mk']['ix']); 13136 unset($opt['mk']['if']); 13137 unset($opt['mk']['tp']); 13138 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 13139 if ($this->rtl) { 13140 $this->x -= $w; 13141 } else { 13142 $this->x += $w; 13143 } 13144 } 13145 13146 /** 13147 * Creates a CheckBox field 13148 * @param $name (string) field name 13149 * @param $w (int) width 13150 * @param $checked (boolean) define the initial state. 13151 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 13152 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 13153 * @param $onvalue (string) value to be returned if selected. 13154 * @param $x (float) Abscissa of the upper-left corner of the rectangle 13155 * @param $y (float) Ordinate of the upper-left corner of the rectangle 13156 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 13157 * @public 13158 * @author Nicola Asuni 13159 * @since 4.8.000 (2009-09-07) 13160 */ 13161 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) { 13162 if ($x === '') { 13163 $x = $this->x; 13164 } 13165 if ($y === '') { 13166 $y = $this->y; 13167 } 13168 // check page for no-write regions and adapt page margins if necessary 13169 list($x, $y) = $this->checkPageRegions($w, $x, $y); 13170 if ($js) { 13171 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop); 13172 return; 13173 } 13174 if (!isset($prop['value'])) { 13175 $prop['value'] = array('Yes'); 13176 } 13177 // get default style 13178 $prop = array_merge($this->getFormDefaultProp(), $prop); 13179 $prop['borderStyle'] = 'inset'; 13180 // get annotation data 13181 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13182 // set additional default options 13183 $font = 'zapfdingbats'; 13184 if ($this->pdfa_mode) { 13185 // all fonts must be embedded 13186 $font = 'pdfa'.$font; 13187 } 13188 $this->AddFont($font); 13189 $tmpfont = $this->getFontBuffer($font); 13190 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; 13191 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); 13192 $popt['da'] = $fontstyle; 13193 // build appearance stream 13194 $popt['ap'] = array(); 13195 $popt['ap']['n'] = array(); 13196 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k); 13197 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); 13198 $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy); 13199 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy); 13200 // merge options 13201 $opt = array_merge($popt, $opt); 13202 // set remaining annotation data 13203 $opt['Subtype'] = 'Widget'; 13204 $opt['ft'] = 'Btn'; 13205 $opt['t'] = $name; 13206 if (TCPDF_STATIC::empty_string($onvalue)) { 13207 $onvalue = 'Yes'; 13208 } 13209 $opt['opt'] = array($onvalue); 13210 if ($checked) { 13211 $opt['v'] = array('/Yes'); 13212 $opt['as'] = 'Yes'; 13213 } else { 13214 $opt['v'] = array('/Off'); 13215 $opt['as'] = 'Off'; 13216 } 13217 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 13218 if ($this->rtl) { 13219 $this->x -= $w; 13220 } else { 13221 $this->x += $w; 13222 } 13223 } 13224 13225 /** 13226 * Creates a button field 13227 * @param $name (string) field name 13228 * @param $w (int) width 13229 * @param $h (int) height 13230 * @param $caption (string) caption. 13231 * @param $action (mixed) action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008. 13232 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 13233 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 13234 * @param $x (float) Abscissa of the upper-left corner of the rectangle 13235 * @param $y (float) Ordinate of the upper-left corner of the rectangle 13236 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 13237 * @public 13238 * @author Nicola Asuni 13239 * @since 4.8.000 (2009-09-07) 13240 */ 13241 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 13242 if ($x === '') { 13243 $x = $this->x; 13244 } 13245 if ($y === '') { 13246 $y = $this->y; 13247 } 13248 // check page for no-write regions and adapt page margins if necessary 13249 list($x, $y) = $this->checkPageRegions($h, $x, $y); 13250 if ($js) { 13251 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop); 13252 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n"; 13253 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n"; 13254 $this->javascript .= 'f'.$name.".highlight='push';\n"; 13255 $this->javascript .= 'f'.$name.".print=false;\n"; 13256 return; 13257 } 13258 // get default style 13259 $prop = array_merge($this->getFormDefaultProp(), $prop); 13260 $prop['Pushbutton'] = 'true'; 13261 $prop['highlight'] = 'push'; 13262 $prop['display'] = 'display.noPrint'; 13263 // get annotation data 13264 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13265 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 13266 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 13267 $popt['da'] = $fontstyle; 13268 // build appearance stream 13269 $popt['ap'] = array(); 13270 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 13271 $tmpid = $this->startTemplate($w, $h, false); 13272 $bw = (2 / $this->k); // border width 13273 $border = array( 13274 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 13275 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)), 13276 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 13277 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51))); 13278 $this->SetFillColor(204); 13279 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M'); 13280 $this->endTemplate(); 13281 --$this->n; 13282 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 13283 unset($this->xobjects[$tmpid]); 13284 $popt['ap']['n'] .= 'Q EMC'; 13285 // set additional default options 13286 if (!isset($popt['mk'])) { 13287 $popt['mk'] = array(); 13288 } 13289 $ann_obj_id = ($this->n + 1); 13290 if (!empty($action) AND !is_array($action)) { 13291 $ann_obj_id = ($this->n + 2); 13292 } 13293 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id); 13294 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id); 13295 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id); 13296 // merge options 13297 $opt = array_merge($popt, $opt); 13298 // set remaining annotation data 13299 $opt['Subtype'] = 'Widget'; 13300 $opt['ft'] = 'Btn'; 13301 $opt['t'] = $caption; 13302 $opt['v'] = $name; 13303 if (!empty($action)) { 13304 if (is_array($action)) { 13305 // form action options as on section 12.7.5 of PDF32000_2008. 13306 $opt['aa'] = '/D <<'; 13307 $bmode = array('SubmitForm', 'ResetForm', 'ImportData'); 13308 foreach ($action AS $key => $val) { 13309 if (($key == 'S') AND in_array($val, $bmode)) { 13310 $opt['aa'] .= ' /S /'.$val; 13311 } elseif (($key == 'F') AND (!empty($val))) { 13312 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id); 13313 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) { 13314 $opt['aa'] .= ' /Fields ['; 13315 foreach ($val AS $field) { 13316 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id); 13317 } 13318 $opt['aa'] .= ']'; 13319 } elseif (($key == 'Flags')) { 13320 $ff = 0; 13321 if (is_array($val)) { 13322 foreach ($val AS $flag) { 13323 switch ($flag) { 13324 case 'Include/Exclude': { 13325 $ff += 1 << 0; 13326 break; 13327 } 13328 case 'IncludeNoValueFields': { 13329 $ff += 1 << 1; 13330 break; 13331 } 13332 case 'ExportFormat': { 13333 $ff += 1 << 2; 13334 break; 13335 } 13336 case 'GetMethod': { 13337 $ff += 1 << 3; 13338 break; 13339 } 13340 case 'SubmitCoordinates': { 13341 $ff += 1 << 4; 13342 break; 13343 } 13344 case 'XFDF': { 13345 $ff += 1 << 5; 13346 break; 13347 } 13348 case 'IncludeAppendSaves': { 13349 $ff += 1 << 6; 13350 break; 13351 } 13352 case 'IncludeAnnotations': { 13353 $ff += 1 << 7; 13354 break; 13355 } 13356 case 'SubmitPDF': { 13357 $ff += 1 << 8; 13358 break; 13359 } 13360 case 'CanonicalFormat': { 13361 $ff += 1 << 9; 13362 break; 13363 } 13364 case 'ExclNonUserAnnots': { 13365 $ff += 1 << 10; 13366 break; 13367 } 13368 case 'ExclFKey': { 13369 $ff += 1 << 11; 13370 break; 13371 } 13372 case 'EmbedForm': { 13373 $ff += 1 << 13; 13374 break; 13375 } 13376 } 13377 } 13378 } else { 13379 $ff = intval($val); 13380 } 13381 $opt['aa'] .= ' /Flags '.$ff; 13382 } 13383 } 13384 $opt['aa'] .= ' >>'; 13385 } else { 13386 // Javascript action or raw action command 13387 $js_obj_id = $this->addJavascriptObject($action); 13388 $opt['aa'] = '/D '.$js_obj_id.' 0 R'; 13389 } 13390 } 13391 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 13392 if ($this->rtl) { 13393 $this->x -= $w; 13394 } else { 13395 $this->x += $w; 13396 } 13397 } 13398 13399 // --- END FORMS FIELDS ------------------------------------------------ 13400 13401 /** 13402 * Add certification signature (DocMDP or UR3) 13403 * You can set only one signature type 13404 * @protected 13405 * @author Nicola Asuni 13406 * @since 4.6.008 (2009-05-07) 13407 */ 13408 protected function _putsignature() { 13409 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) { 13410 return; 13411 } 13412 $sigobjid = ($this->sig_obj_id + 1); 13413 $out = $this->_getobj($sigobjid)."\n"; 13414 $out .= '<< /Type /Sig'; 13415 $out .= ' /Filter /Adobe.PPKLite'; 13416 $out .= ' /SubFilter /adbe.pkcs7.detached'; 13417 $out .= ' '.TCPDF_STATIC::$byterange_string; 13418 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>'; 13419 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 13420 $out .= ' /Reference ['; // array of signature reference dictionaries 13421 $out .= ' << /Type /SigRef'; 13422 if ($this->signature_data['cert_type'] > 0) { 13423 $out .= ' /TransformMethod /DocMDP'; 13424 $out .= ' /TransformParams <<'; 13425 $out .= ' /Type /TransformParams'; 13426 $out .= ' /P '.$this->signature_data['cert_type']; 13427 $out .= ' /V /1.2'; 13428 } else { 13429 $out .= ' /TransformMethod /UR3'; 13430 $out .= ' /TransformParams <<'; 13431 $out .= ' /Type /TransformParams'; 13432 $out .= ' /V /2.2'; 13433 if (!TCPDF_STATIC::empty_string($this->ur['document'])) { 13434 $out .= ' /Document['.$this->ur['document'].']'; 13435 } 13436 if (!TCPDF_STATIC::empty_string($this->ur['form'])) { 13437 $out .= ' /Form['.$this->ur['form'].']'; 13438 } 13439 if (!TCPDF_STATIC::empty_string($this->ur['signature'])) { 13440 $out .= ' /Signature['.$this->ur['signature'].']'; 13441 } 13442 if (!TCPDF_STATIC::empty_string($this->ur['annots'])) { 13443 $out .= ' /Annots['.$this->ur['annots'].']'; 13444 } 13445 if (!TCPDF_STATIC::empty_string($this->ur['ef'])) { 13446 $out .= ' /EF['.$this->ur['ef'].']'; 13447 } 13448 if (!TCPDF_STATIC::empty_string($this->ur['formex'])) { 13449 $out .= ' /FormEX['.$this->ur['formex'].']'; 13450 } 13451 } 13452 $out .= ' >>'; // close TransformParams 13453 // optional digest data (values must be calculated and replaced later) 13454 //$out .= ' /Data ********** 0 R'; 13455 //$out .= ' /DigestMethod/MD5'; 13456 //$out .= ' /DigestLocation[********** 34]'; 13457 //$out .= ' /DigestValue<********************************>'; 13458 $out .= ' >>'; 13459 $out .= ' ]'; // end of reference 13460 } 13461 if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) { 13462 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid); 13463 } 13464 if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) { 13465 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid); 13466 } 13467 if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) { 13468 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid); 13469 } 13470 if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) { 13471 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid); 13472 } 13473 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp); 13474 $out .= ' >>'; 13475 $out .= "\n".'endobj'; 13476 $this->_out($out); 13477 } 13478 13479 /** 13480 * Set User's Rights for PDF Reader 13481 * WARNING: This is experimental and currently do not work. 13482 * Check the PDF Reference 8.7.1 Transform Methods, 13483 * Table 8.105 Entries in the UR transform parameters dictionary 13484 * @param $enable (boolean) if true enable user's rights on PDF reader 13485 * @param $document (string) 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. 13486 * @param $annots (string) 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. 13487 * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 13488 * @param $signature (string) 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. 13489 * @param $ef (string) Names specifying additional usage rights for named embedded files in the document. Valid names are /Create/Delete/Modify/Import, which permit the user to perform the named operation on named embedded files 13490 Names specifying additional embedded-files-related usage rights for the document. 13491 * @param $formex (string) Names specifying additional form-field-related usage rights. The only valid name is BarcodePlaintext, which permits text form field data to be encoded as a plaintext two-dimensional barcode. 13492 * @public 13493 * @author Nicola Asuni 13494 * @since 2.9.000 (2008-03-26) 13495 */ 13496 public function setUserRights( 13497 $enable=true, 13498 $document='/FullSave', 13499 $annots='/Create/Delete/Modify/Copy/Import/Export', 13500 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', 13501 $signature='/Modify', 13502 $ef='/Create/Delete/Modify/Import', 13503 $formex='') { 13504 $this->ur['enabled'] = $enable; 13505 $this->ur['document'] = $document; 13506 $this->ur['annots'] = $annots; 13507 $this->ur['form'] = $form; 13508 $this->ur['signature'] = $signature; 13509 $this->ur['ef'] = $ef; 13510 $this->ur['formex'] = $formex; 13511 if (!$this->sign) { 13512 $this->setSignature('', '', '', '', 0, array()); 13513 } 13514 } 13515 13516 /** 13517 * Enable document signature (requires the OpenSSL Library). 13518 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader. 13519 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt 13520 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 13521 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes 13522 * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://') 13523 * @param $private_key (mixed) private key (string or filename prefixed with 'file://') 13524 * @param $private_key_password (string) password 13525 * @param $extracerts (string) 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. 13526 * @param $cert_type (int) 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. 13527 * @param $info (array) array of option information: Name, Location, Reason, ContactInfo. 13528 * @param $approval (string) Enable approval signature eg. for PDF incremental update 13529 * @public 13530 * @author Nicola Asuni 13531 * @since 4.6.005 (2009-04-24) 13532 */ 13533 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') { 13534 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt 13535 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 13536 // to convert pfx certificate to pem: openssl 13537 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes 13538 $this->sign = true; 13539 ++$this->n; 13540 $this->sig_obj_id = $this->n; // signature widget 13541 ++$this->n; // signature object ($this->sig_obj_id + 1) 13542 $this->signature_data = array(); 13543 if (strlen($signing_cert) == 0) { 13544 $this->Error('Please provide a certificate file and password!'); 13545 } 13546 if (strlen($private_key) == 0) { 13547 $private_key = $signing_cert; 13548 } 13549 $this->signature_data['signcert'] = $signing_cert; 13550 $this->signature_data['privkey'] = $private_key; 13551 $this->signature_data['password'] = $private_key_password; 13552 $this->signature_data['extracerts'] = $extracerts; 13553 $this->signature_data['cert_type'] = $cert_type; 13554 $this->signature_data['info'] = $info; 13555 $this->signature_data['approval'] = $approval; 13556 } 13557 13558 /** 13559 * Set the digital signature appearance (a cliccable rectangle area to get signature properties) 13560 * @param $x (float) Abscissa of the upper-left corner. 13561 * @param $y (float) Ordinate of the upper-left corner. 13562 * @param $w (float) Width of the signature area. 13563 * @param $h (float) Height of the signature area. 13564 * @param $page (int) option page number (if < 0 the current page is used). 13565 * @param $name (string) Name of the signature. 13566 * @public 13567 * @author Nicola Asuni 13568 * @since 5.3.011 (2010-06-17) 13569 */ 13570 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13571 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name); 13572 } 13573 13574 /** 13575 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties) 13576 * @param $x (float) Abscissa of the upper-left corner. 13577 * @param $y (float) Ordinate of the upper-left corner. 13578 * @param $w (float) Width of the signature area. 13579 * @param $h (float) Height of the signature area. 13580 * @param $page (int) option page number (if < 0 the current page is used). 13581 * @param $name (string) Name of the signature. 13582 * @public 13583 * @author Nicola Asuni 13584 * @since 5.9.101 (2011-07-06) 13585 */ 13586 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13587 ++$this->n; 13588 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name); 13589 } 13590 13591 /** 13592 * Get the array that defines the signature appearance (page and rectangle coordinates). 13593 * @param $x (float) Abscissa of the upper-left corner. 13594 * @param $y (float) Ordinate of the upper-left corner. 13595 * @param $w (float) Width of the signature area. 13596 * @param $h (float) Height of the signature area. 13597 * @param $page (int) option page number (if < 0 the current page is used). 13598 * @param $name (string) Name of the signature. 13599 * @return (array) Array defining page and rectangle coordinates of signature appearance. 13600 * @protected 13601 * @author Nicola Asuni 13602 * @since 5.9.101 (2011-07-06) 13603 */ 13604 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13605 $sigapp = array(); 13606 if (($page < 1) OR ($page > $this->numpages)) { 13607 $sigapp['page'] = $this->page; 13608 } else { 13609 $sigapp['page'] = intval($page); 13610 } 13611 if (empty($name)) { 13612 $sigapp['name'] = 'Signature'; 13613 } else { 13614 $sigapp['name'] = $name; 13615 } 13616 $a = $x * $this->k; 13617 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k); 13618 $c = $w * $this->k; 13619 $d = $h * $this->k; 13620 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d)); 13621 return $sigapp; 13622 } 13623 13624 /** 13625 * Enable document timestamping (requires the OpenSSL Library). 13626 * The trusted timestamping improve document security that means that no one should be able to change the document once it has been recorded. 13627 * Use with digital signature only! 13628 * @param $tsa_host (string) Time Stamping Authority (TSA) server (prefixed with 'https://') 13629 * @param $tsa_username (string) Specifies the username for TSA authorization (optional) OR specifies the TSA authorization PEM file (see: example_66.php, optional) 13630 * @param $tsa_password (string) Specifies the password for TSA authorization (optional) 13631 * @param $tsa_cert (string) Specifies the location of TSA certificate for authorization (optional for cURL) 13632 * @public 13633 * @author Richard Stockinger 13634 * @since 6.0.090 (2014-06-16) 13635 */ 13636 public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') { 13637 $this->tsa_data = array(); 13638 if (!function_exists('curl_init')) { 13639 $this->Error('Please enable cURL PHP extension!'); 13640 } 13641 if (strlen($tsa_host) == 0) { 13642 $this->Error('Please specify the host of Time Stamping Authority (TSA)!'); 13643 } 13644 $this->tsa_data['tsa_host'] = $tsa_host; 13645 if (is_file($tsa_username)) { 13646 $this->tsa_data['tsa_auth'] = $tsa_username; 13647 } else { 13648 $this->tsa_data['tsa_username'] = $tsa_username; 13649 } 13650 $this->tsa_data['tsa_password'] = $tsa_password; 13651 $this->tsa_data['tsa_cert'] = $tsa_cert; 13652 $this->tsa_timestamp = true; 13653 } 13654 13655 /** 13656 * NOT YET IMPLEMENTED 13657 * Request TSA for a timestamp 13658 * @param $signature (string) Digital signature as binary string 13659 * @return (string) Timestamped digital signature 13660 * @protected 13661 * @author Richard Stockinger 13662 * @since 6.0.090 (2014-06-16) 13663 */ 13664 protected function applyTSA($signature) { 13665 if (!$this->tsa_timestamp) { 13666 return $signature; 13667 } 13668 //@TODO: implement this feature 13669 return $signature; 13670 } 13671 13672 /** 13673 * Create a new page group. 13674 * NOTE: call this function before calling AddPage() 13675 * @param $page (int) starting group page (leave empty for next page). 13676 * @public 13677 * @since 3.0.000 (2008-03-27) 13678 */ 13679 public function startPageGroup($page='') { 13680 if (empty($page)) { 13681 $page = $this->page + 1; 13682 } 13683 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1; 13684 } 13685 13686 /** 13687 * Set the starting page number. 13688 * @param $num (int) Starting page number. 13689 * @since 5.9.093 (2011-06-16) 13690 * @public 13691 */ 13692 public function setStartingPageNumber($num=1) { 13693 $this->starting_page_number = max(0, intval($num)); 13694 } 13695 13696 /** 13697 * Returns the string alias used right align page numbers. 13698 * If the current font is unicode type, the returned string wil contain an additional open curly brace. 13699 * @return string 13700 * @since 5.9.099 (2011-06-27) 13701 * @public 13702 */ 13703 public function getAliasRightShift() { 13704 // calculate aproximatively the ratio between widths of aliases and replacements. 13705 $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}'; 13706 $rep = str_repeat(' ', $this->GetNumChars($ref)); 13707 $wrep = $this->GetStringWidth($rep); 13708 if ($wrep > 0) { 13709 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep)); 13710 } else { 13711 $wdiff = 1; 13712 } 13713 $sdiff = sprintf('%F', $wdiff); 13714 $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}'; 13715 if ($this->isUnicodeFont()) { 13716 $alias = '{'.$alias; 13717 } 13718 return $alias; 13719 } 13720 13721 /** 13722 * Returns the string alias used for the total number of pages. 13723 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13724 * This alias will be replaced by the total number of pages in the document. 13725 * @return string 13726 * @since 4.0.018 (2008-08-08) 13727 * @public 13728 */ 13729 public function getAliasNbPages() { 13730 if ($this->isUnicodeFont()) { 13731 return '{'.TCPDF_STATIC::$alias_tot_pages.'}'; 13732 } 13733 return TCPDF_STATIC::$alias_tot_pages; 13734 } 13735 13736 /** 13737 * Returns the string alias used for the page number. 13738 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13739 * This alias will be replaced by the page number. 13740 * @return string 13741 * @since 4.5.000 (2009-01-02) 13742 * @public 13743 */ 13744 public function getAliasNumPage() { 13745 if ($this->isUnicodeFont()) { 13746 return '{'.TCPDF_STATIC::$alias_num_page.'}'; 13747 } 13748 return TCPDF_STATIC::$alias_num_page; 13749 } 13750 13751 /** 13752 * Return the alias for the total number of pages in the current page group. 13753 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13754 * This alias will be replaced by the total number of pages in this group. 13755 * @return alias of the current page group 13756 * @public 13757 * @since 3.0.000 (2008-03-27) 13758 */ 13759 public function getPageGroupAlias() { 13760 if ($this->isUnicodeFont()) { 13761 return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}'; 13762 } 13763 return TCPDF_STATIC::$alias_group_tot_pages; 13764 } 13765 13766 /** 13767 * Return the alias for the page number on the current page group. 13768 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13769 * This alias will be replaced by the page number (relative to the belonging group). 13770 * @return alias of the current page group 13771 * @public 13772 * @since 4.5.000 (2009-01-02) 13773 */ 13774 public function getPageNumGroupAlias() { 13775 if ($this->isUnicodeFont()) { 13776 return '{'.TCPDF_STATIC::$alias_group_num_page.'}'; 13777 } 13778 return TCPDF_STATIC::$alias_group_num_page; 13779 } 13780 13781 /** 13782 * Return the current page in the group. 13783 * @return current page in the group 13784 * @public 13785 * @since 3.0.000 (2008-03-27) 13786 */ 13787 public function getGroupPageNo() { 13788 return $this->pagegroups[$this->currpagegroup]; 13789 } 13790 13791 /** 13792 * Returns the current group page number formatted as a string. 13793 * @public 13794 * @since 4.3.003 (2008-11-18) 13795 * @see PaneNo(), formatPageNumber() 13796 */ 13797 public function getGroupPageNoFormatted() { 13798 return TCPDF_STATIC::formatPageNumber($this->getGroupPageNo()); 13799 } 13800 13801 /** 13802 * Returns the current page number formatted as a string. 13803 * @public 13804 * @since 4.2.005 (2008-11-06) 13805 * @see PaneNo(), formatPageNumber() 13806 */ 13807 public function PageNoFormatted() { 13808 return TCPDF_STATIC::formatPageNumber($this->PageNo()); 13809 } 13810 13811 /** 13812 * Put pdf layers. 13813 * @protected 13814 * @since 3.0.000 (2008-03-27) 13815 */ 13816 protected function _putocg() { 13817 if (empty($this->pdflayers)) { 13818 return; 13819 } 13820 foreach ($this->pdflayers as $key => $layer) { 13821 $this->pdflayers[$key]['objid'] = $this->_newobj(); 13822 $out = '<< /Type /OCG'; 13823 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']); 13824 $out .= ' /Usage <<'; 13825 if (isset($layer['print']) AND ($layer['print'] !== NULL)) { 13826 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>'; 13827 } 13828 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>'; 13829 $out .= ' >> >>'; 13830 $out .= "\n".'endobj'; 13831 $this->_out($out); 13832 } 13833 } 13834 13835 /** 13836 * Start a new pdf layer. 13837 * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name. 13838 * @param $print (boolean|null) Set to TRUE to print this layer, FALSE to not print and NULL to not set this option 13839 * @param $view (boolean) Set to true to view this layer. 13840 * @param $lock (boolean) If true lock the layer 13841 * @public 13842 * @since 5.9.102 (2011-07-13) 13843 */ 13844 public function startLayer($name='', $print=true, $view=true, $lock=true) { 13845 if ($this->state != 2) { 13846 return; 13847 } 13848 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1)); 13849 if (empty($name)) { 13850 $name = $layer; 13851 } else { 13852 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name); 13853 } 13854 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock); 13855 $this->openMarkedContent = true; 13856 $this->_out('/OC /'.$layer.' BDC'); 13857 } 13858 13859 /** 13860 * End the current PDF layer. 13861 * @public 13862 * @since 5.9.102 (2011-07-13) 13863 */ 13864 public function endLayer() { 13865 if ($this->state != 2) { 13866 return; 13867 } 13868 if ($this->openMarkedContent) { 13869 // close existing open marked-content layer 13870 $this->_out('EMC'); 13871 $this->openMarkedContent = false; 13872 } 13873 } 13874 13875 /** 13876 * Set the visibility of the successive elements. 13877 * This can be useful, for instance, to put a background 13878 * image or color that will show on screen but won't print. 13879 * @param $v (string) visibility mode. Legal values are: all, print, screen or view. 13880 * @public 13881 * @since 3.0.000 (2008-03-27) 13882 */ 13883 public function setVisibility($v) { 13884 if ($this->state != 2) { 13885 return; 13886 } 13887 $this->endLayer(); 13888 switch($v) { 13889 case 'print': { 13890 $this->startLayer('Print', true, false); 13891 break; 13892 } 13893 case 'view': 13894 case 'screen': { 13895 $this->startLayer('View', false, true); 13896 break; 13897 } 13898 case 'all': { 13899 $this->_out(''); 13900 break; 13901 } 13902 default: { 13903 $this->Error('Incorrect visibility: '.$v); 13904 break; 13905 } 13906 } 13907 } 13908 13909 /** 13910 * Add transparency parameters to the current extgstate 13911 * @param $parms (array) parameters 13912 * @return the number of extgstates 13913 * @protected 13914 * @since 3.0.000 (2008-03-27) 13915 */ 13916 protected function addExtGState($parms) { 13917 if ($this->pdfa_mode) { 13918 // transparencies are not allowed in PDF/A mode 13919 return; 13920 } 13921 // check if this ExtGState already exist 13922 foreach ($this->extgstates as $i => $ext) { 13923 if ($ext['parms'] == $parms) { 13924 if ($this->inxobj) { 13925 // we are inside an XObject template 13926 $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext; 13927 } 13928 // return reference to existing ExtGState 13929 return $i; 13930 } 13931 } 13932 $n = (count($this->extgstates) + 1); 13933 $this->extgstates[$n] = array('parms' => $parms); 13934 if ($this->inxobj) { 13935 // we are inside an XObject template 13936 $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n]; 13937 } 13938 return $n; 13939 } 13940 13941 /** 13942 * Add an extgstate 13943 * @param $gs (array) extgstate 13944 * @protected 13945 * @since 3.0.000 (2008-03-27) 13946 */ 13947 protected function setExtGState($gs) { 13948 if ($this->pdfa_mode OR ($this->state != 2)) { 13949 // transparency is not allowed in PDF/A mode 13950 return; 13951 } 13952 $this->_out(sprintf('/GS%d gs', $gs)); 13953 } 13954 13955 /** 13956 * Put extgstates for object transparency 13957 * @protected 13958 * @since 3.0.000 (2008-03-27) 13959 */ 13960 protected function _putextgstates() { 13961 foreach ($this->extgstates as $i => $ext) { 13962 $this->extgstates[$i]['n'] = $this->_newobj(); 13963 $out = '<< /Type /ExtGState'; 13964 foreach ($ext['parms'] as $k => $v) { 13965 if (is_float($v)) { 13966 $v = sprintf('%F', $v); 13967 } elseif ($v === true) { 13968 $v = 'true'; 13969 } elseif ($v === false) { 13970 $v = 'false'; 13971 } 13972 $out .= ' /'.$k.' '.$v; 13973 } 13974 $out .= ' >>'; 13975 $out .= "\n".'endobj'; 13976 $this->_out($out); 13977 } 13978 } 13979 13980 /** 13981 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations. 13982 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 13983 * @param $stroking (boolean) If true apply overprint for stroking operations. 13984 * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking. 13985 * @param $mode (integer) Overprint mode: (0 = each source colour component value replaces the value previously painted for the corresponding device colorant; 1 = a tint value of 0.0 for a source colour component shall leave the corresponding component of the previously painted colour unchanged). 13986 * @public 13987 * @since 5.9.152 (2012-03-23) 13988 */ 13989 public function setOverprint($stroking=true, $nonstroking='', $mode=0) { 13990 if ($this->state != 2) { 13991 return; 13992 } 13993 $stroking = $stroking ? true : false; 13994 if (TCPDF_STATIC::empty_string($nonstroking)) { 13995 // default value if not set 13996 $nonstroking = $stroking; 13997 } else { 13998 $nonstroking = $nonstroking ? true : false; 13999 } 14000 if (($mode != 0) AND ($mode != 1)) { 14001 $mode = 0; 14002 } 14003 $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode); 14004 $gs = $this->addExtGState($this->overprint); 14005 $this->setExtGState($gs); 14006 } 14007 14008 /** 14009 * Get the overprint mode array (OP, op, OPM). 14010 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 14011 * @return array. 14012 * @public 14013 * @since 5.9.152 (2012-03-23) 14014 */ 14015 public function getOverprint() { 14016 return $this->overprint; 14017 } 14018 14019 /** 14020 * Set alpha for stroking (CA) and non-stroking (ca) operations. 14021 * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque). 14022 * @param $bm (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity 14023 * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque). 14024 * @param $ais (boolean) 14025 * @public 14026 * @since 3.0.000 (2008-03-27) 14027 */ 14028 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) { 14029 if ($this->pdfa_mode) { 14030 // transparency is not allowed in PDF/A mode 14031 return; 14032 } 14033 $stroking = floatval($stroking); 14034 if (TCPDF_STATIC::empty_string($nonstroking)) { 14035 // default value if not set 14036 $nonstroking = $stroking; 14037 } else { 14038 $nonstroking = floatval($nonstroking); 14039 } 14040 if ($bm[0] == '/') { 14041 // remove trailing slash 14042 $bm = substr($bm, 1); 14043 } 14044 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) { 14045 $bm = 'Normal'; 14046 } 14047 $ais = $ais ? true : false; 14048 $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais); 14049 $gs = $this->addExtGState($this->alpha); 14050 $this->setExtGState($gs); 14051 } 14052 14053 /** 14054 * Get the alpha mode array (CA, ca, BM, AIS). 14055 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 14056 * @return array. 14057 * @public 14058 * @since 5.9.152 (2012-03-23) 14059 */ 14060 public function getAlpha() { 14061 return $this->alpha; 14062 } 14063 14064 /** 14065 * Set the default JPEG compression quality (1-100) 14066 * @param $quality (int) JPEG quality, integer between 1 and 100 14067 * @public 14068 * @since 3.0.000 (2008-03-27) 14069 */ 14070 public function setJPEGQuality($quality) { 14071 if (($quality < 1) OR ($quality > 100)) { 14072 $quality = 75; 14073 } 14074 $this->jpeg_quality = intval($quality); 14075 } 14076 14077 /** 14078 * Set the default number of columns in a row for HTML tables. 14079 * @param $cols (int) number of columns 14080 * @public 14081 * @since 3.0.014 (2008-06-04) 14082 */ 14083 public function setDefaultTableColumns($cols=4) { 14084 $this->default_table_columns = intval($cols); 14085 } 14086 14087 /** 14088 * Set the height of the cell (line height) respect the font height. 14089 * @param $h (int) cell proportion respect font height (typical value = 1.25). 14090 * @public 14091 * @since 3.0.014 (2008-06-04) 14092 */ 14093 public function setCellHeightRatio($h) { 14094 $this->cell_height_ratio = $h; 14095 } 14096 14097 /** 14098 * return the height of cell repect font height. 14099 * @public 14100 * @since 4.0.012 (2008-07-24) 14101 */ 14102 public function getCellHeightRatio() { 14103 return $this->cell_height_ratio; 14104 } 14105 14106 /** 14107 * Set the PDF version (check PDF reference for valid values). 14108 * @param $version (string) PDF document version. 14109 * @public 14110 * @since 3.1.000 (2008-06-09) 14111 */ 14112 public function setPDFVersion($version='1.7') { 14113 if ($this->pdfa_mode) { 14114 // PDF/A mode 14115 $this->PDFVersion = '1.4'; 14116 } else { 14117 $this->PDFVersion = $version; 14118 } 14119 } 14120 14121 /** 14122 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print. 14123 * (see Section 8.1 of PDF reference, "Viewer Preferences"). 14124 * <ul><li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li><li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li><li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li><li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li><li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li><li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li><li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li></ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li><li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li></ul></li><li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li><li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li><li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li><li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li></ul> 14125 * @param $preferences (array) array of options. 14126 * @author Nicola Asuni 14127 * @public 14128 * @since 3.1.000 (2008-06-09) 14129 */ 14130 public function setViewerPreferences($preferences) { 14131 $this->viewer_preferences = $preferences; 14132 } 14133 14134 /** 14135 * Paints color transition registration bars 14136 * @param $x (float) abscissa of the top left corner of the rectangle. 14137 * @param $y (float) ordinate of the top left corner of the rectangle. 14138 * @param $w (float) width of the rectangle. 14139 * @param $h (float) height of the rectangle. 14140 * @param $transition (boolean) if true prints tcolor transitions to white. 14141 * @param $vertical (boolean) if true prints bar vertically. 14142 * @param $colors (string) colors to print separated by comma. Valid values are: A,W,R,G,B,C,M,Y,K,RGB,CMYK,ALL,ALLSPOT,<SPOT_COLOR_NAME>. Where: A = grayscale black, W = grayscale white, R = RGB red, G RGB green, B RGB blue, C = CMYK cyan, M = CMYK magenta, Y = CMYK yellow, K = CMYK key/black, RGB = RGB registration color, CMYK = CMYK registration color, ALL = Spot registration color, ALLSPOT = print all defined spot colors, <SPOT_COLOR_NAME> = name of the spot color to print. 14143 * @author Nicola Asuni 14144 * @since 4.9.000 (2010-03-26) 14145 * @public 14146 */ 14147 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') { 14148 if (strpos($colors, 'ALLSPOT') !== false) { 14149 // expand spot colors 14150 $spot_colors = ''; 14151 foreach ($this->spot_colors as $spot_color_name => $v) { 14152 $spot_colors .= ','.$spot_color_name; 14153 } 14154 if (!empty($spot_colors)) { 14155 $spot_colors = substr($spot_colors, 1); 14156 $colors = str_replace('ALLSPOT', $spot_colors, $colors); 14157 } else { 14158 $colors = str_replace('ALLSPOT', 'NONE', $colors); 14159 } 14160 } 14161 $bars = explode(',', $colors); 14162 $numbars = count($bars); // number of bars to print 14163 if ($numbars <= 0) { 14164 return; 14165 } 14166 // set bar measures 14167 if ($vertical) { 14168 $coords = array(0, 0, 0, 1); 14169 $wb = $w / $numbars; // bar width 14170 $hb = $h; // bar height 14171 $xd = $wb; // delta x 14172 $yd = 0; // delta y 14173 } else { 14174 $coords = array(1, 0, 0, 0); 14175 $wb = $w; // bar width 14176 $hb = $h / $numbars; // bar height 14177 $xd = 0; // delta x 14178 $yd = $hb; // delta y 14179 } 14180 $xb = $x; 14181 $yb = $y; 14182 foreach ($bars as $col) { 14183 switch ($col) { 14184 // set transition colors 14185 case 'A': { // BLACK (GRAYSCALE) 14186 $col_a = array(255); 14187 $col_b = array(0); 14188 break; 14189 } 14190 case 'W': { // WHITE (GRAYSCALE) 14191 $col_a = array(0); 14192 $col_b = array(255); 14193 break; 14194 } 14195 case 'R': { // RED (RGB) 14196 $col_a = array(255,255,255); 14197 $col_b = array(255,0,0); 14198 break; 14199 } 14200 case 'G': { // GREEN (RGB) 14201 $col_a = array(255,255,255); 14202 $col_b = array(0,255,0); 14203 break; 14204 } 14205 case 'B': { // BLUE (RGB) 14206 $col_a = array(255,255,255); 14207 $col_b = array(0,0,255); 14208 break; 14209 } 14210 case 'C': { // CYAN (CMYK) 14211 $col_a = array(0,0,0,0); 14212 $col_b = array(100,0,0,0); 14213 break; 14214 } 14215 case 'M': { // MAGENTA (CMYK) 14216 $col_a = array(0,0,0,0); 14217 $col_b = array(0,100,0,0); 14218 break; 14219 } 14220 case 'Y': { // YELLOW (CMYK) 14221 $col_a = array(0,0,0,0); 14222 $col_b = array(0,0,100,0); 14223 break; 14224 } 14225 case 'K': { // KEY - BLACK (CMYK) 14226 $col_a = array(0,0,0,0); 14227 $col_b = array(0,0,0,100); 14228 break; 14229 } 14230 case 'RGB': { // BLACK REGISTRATION (RGB) 14231 $col_a = array(255,255,255); 14232 $col_b = array(0,0,0); 14233 break; 14234 } 14235 case 'CMYK': { // BLACK REGISTRATION (CMYK) 14236 $col_a = array(0,0,0,0); 14237 $col_b = array(100,100,100,100); 14238 break; 14239 } 14240 case 'ALL': { // SPOT COLOR REGISTRATION 14241 $col_a = array(0,0,0,0,'None'); 14242 $col_b = array(100,100,100,100,'All'); 14243 break; 14244 } 14245 case 'NONE': { // SKIP THIS COLOR 14246 $col_a = array(0,0,0,0,'None'); 14247 $col_b = array(0,0,0,0,'None'); 14248 break; 14249 } 14250 default: { // SPECIFIC SPOT COLOR NAME 14251 $col_a = array(0,0,0,0,'None'); 14252 $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors); 14253 if ($col_b === false) { 14254 // in case of error defaults to the registration color 14255 $col_b = array(100,100,100,100,'All'); 14256 } 14257 break; 14258 } 14259 } 14260 if ($col != 'NONE') { 14261 if ($transition) { 14262 // color gradient 14263 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords); 14264 } else { 14265 $this->SetFillColorArray($col_b); 14266 // colored rectangle 14267 $this->Rect($xb, $yb, $wb, $hb, 'F', array()); 14268 } 14269 $xb += $xd; 14270 $yb += $yd; 14271 } 14272 } 14273 } 14274 14275 /** 14276 * Paints crop marks. 14277 * @param $x (float) abscissa of the crop mark center. 14278 * @param $y (float) ordinate of the crop mark center. 14279 * @param $w (float) width of the crop mark. 14280 * @param $h (float) height of the crop mark. 14281 * @param $type (string) type of crop mark, one symbol per type separated by comma: T = TOP, F = BOTTOM, L = LEFT, R = RIGHT, TL = A = TOP-LEFT, TR = B = TOP-RIGHT, BL = C = BOTTOM-LEFT, BR = D = BOTTOM-RIGHT. 14282 * @param $color (array) crop mark color (default spot registration color). 14283 * @author Nicola Asuni 14284 * @since 4.9.000 (2010-03-26) 14285 * @public 14286 */ 14287 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) { 14288 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color)); 14289 $type = strtoupper($type); 14290 $type = preg_replace('/[^A-Z\-\,]*/', '', $type); 14291 // split type in single components 14292 $type = str_replace('-', ',', $type); 14293 $type = str_replace('TL', 'T,L', $type); 14294 $type = str_replace('TR', 'T,R', $type); 14295 $type = str_replace('BL', 'F,L', $type); 14296 $type = str_replace('BR', 'F,R', $type); 14297 $type = str_replace('A', 'T,L', $type); 14298 $type = str_replace('B', 'T,R', $type); 14299 $type = str_replace('T,RO', 'BO', $type); 14300 $type = str_replace('C', 'F,L', $type); 14301 $type = str_replace('D', 'F,R', $type); 14302 $crops = explode(',', strtoupper($type)); 14303 // remove duplicates 14304 $crops = array_unique($crops); 14305 $dw = ($w / 4); // horizontal space to leave before the intersection point 14306 $dh = ($h / 4); // vertical space to leave before the intersection point 14307 foreach ($crops as $crop) { 14308 switch ($crop) { 14309 case 'T': 14310 case 'TOP': { 14311 $x1 = $x; 14312 $y1 = ($y - $h); 14313 $x2 = $x; 14314 $y2 = ($y - $dh); 14315 break; 14316 } 14317 case 'F': 14318 case 'BOTTOM': { 14319 $x1 = $x; 14320 $y1 = ($y + $dh); 14321 $x2 = $x; 14322 $y2 = ($y + $h); 14323 break; 14324 } 14325 case 'L': 14326 case 'LEFT': { 14327 $x1 = ($x - $w); 14328 $y1 = $y; 14329 $x2 = ($x - $dw); 14330 $y2 = $y; 14331 break; 14332 } 14333 case 'R': 14334 case 'RIGHT': { 14335 $x1 = ($x + $dw); 14336 $y1 = $y; 14337 $x2 = ($x + $w); 14338 $y2 = $y; 14339 break; 14340 } 14341 } 14342 $this->Line($x1, $y1, $x2, $y2); 14343 } 14344 } 14345 14346 /** 14347 * Paints a registration mark 14348 * @param $x (float) abscissa of the registration mark center. 14349 * @param $y (float) ordinate of the registration mark center. 14350 * @param $r (float) radius of the crop mark. 14351 * @param $double (boolean) if true print two concentric crop marks. 14352 * @param $cola (array) crop mark color (default spot registration color 'All'). 14353 * @param $colb (array) second crop mark color (default spot registration color 'None'). 14354 * @author Nicola Asuni 14355 * @since 4.9.000 (2010-03-26) 14356 * @public 14357 */ 14358 public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) { 14359 $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola); 14360 $this->SetFillColorArray($cola); 14361 $this->PieSector($x, $y, $r, 90, 180, 'F'); 14362 $this->PieSector($x, $y, $r, 270, 360, 'F'); 14363 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); 14364 if ($double) { 14365 $ri = $r * 0.5; 14366 $this->SetFillColorArray($colb); 14367 $this->PieSector($x, $y, $ri, 90, 180, 'F'); 14368 $this->PieSector($x, $y, $ri, 270, 360, 'F'); 14369 $this->SetFillColorArray($cola); 14370 $this->PieSector($x, $y, $ri, 0, 90, 'F'); 14371 $this->PieSector($x, $y, $ri, 180, 270, 'F'); 14372 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8); 14373 } 14374 } 14375 14376 /** 14377 * Paints a CMYK registration mark 14378 * @param $x (float) abscissa of the registration mark center. 14379 * @param $y (float) ordinate of the registration mark center. 14380 * @param $r (float) radius of the crop mark. 14381 * @author Nicola Asuni 14382 * @since 6.0.038 (2013-09-30) 14383 * @public 14384 */ 14385 public function registrationMarkCMYK($x, $y, $r) { 14386 // line width 14387 $lw = max((0.5 / $this->k),($r / 8)); 14388 // internal radius 14389 $ri = ($r * 0.6); 14390 // external radius 14391 $re = ($r * 1.3); 14392 // Cyan 14393 $this->SetFillColorArray(array(100,0,0,0)); 14394 $this->PieSector($x, $y, $ri, 270, 360, 'F'); 14395 // Magenta 14396 $this->SetFillColorArray(array(0,100,0,0)); 14397 $this->PieSector($x, $y, $ri, 0, 90, 'F'); 14398 // Yellow 14399 $this->SetFillColorArray(array(0,0,100,0)); 14400 $this->PieSector($x, $y, $ri, 90, 180, 'F'); 14401 // Key - black 14402 $this->SetFillColorArray(array(0,0,0,100)); 14403 $this->PieSector($x, $y, $ri, 180, 270, 'F'); 14404 // registration color 14405 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All')); 14406 $this->SetFillColorArray(array(100,100,100,100,'All')); 14407 // external circle 14408 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); 14409 // cross lines 14410 $this->Line($x, ($y - $re), $x, ($y - $ri)); 14411 $this->Line($x, ($y + $ri), $x, ($y + $re)); 14412 $this->Line(($x - $re), $y, ($x - $ri), $y); 14413 $this->Line(($x + $ri), $y, ($x + $re), $y); 14414 } 14415 14416 /** 14417 * Paints a linear colour gradient. 14418 * @param $x (float) abscissa of the top left corner of the rectangle. 14419 * @param $y (float) ordinate of the top left corner of the rectangle. 14420 * @param $w (float) width of the rectangle. 14421 * @param $h (float) height of the rectangle. 14422 * @param $col1 (array) first color (Grayscale, RGB or CMYK components). 14423 * @param $col2 (array) second color (Grayscale, RGB or CMYK components). 14424 * @param $coords (array) 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). 14425 * @author Andreas W\FCrmser, Nicola Asuni 14426 * @since 3.1.000 (2008-06-09) 14427 * @public 14428 */ 14429 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) { 14430 $this->Clip($x, $y, $w, $h); 14431 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 14432 } 14433 14434 /** 14435 * Paints a radial colour gradient. 14436 * @param $x (float) abscissa of the top left corner of the rectangle. 14437 * @param $y (float) ordinate of the top left corner of the rectangle. 14438 * @param $w (float) width of the rectangle. 14439 * @param $h (float) height of the rectangle. 14440 * @param $col1 (array) first color (Grayscale, RGB or CMYK components). 14441 * @param $col2 (array) second color (Grayscale, RGB or CMYK components). 14442 * @param $coords (array) 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. 14443 * @author Andreas W\FCrmser, Nicola Asuni 14444 * @since 3.1.000 (2008-06-09) 14445 * @public 14446 */ 14447 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) { 14448 $this->Clip($x, $y, $w, $h); 14449 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 14450 } 14451 14452 /** 14453 * Paints a coons patch mesh. 14454 * @param $x (float) abscissa of the top left corner of the rectangle. 14455 * @param $y (float) ordinate of the top left corner of the rectangle. 14456 * @param $w (float) width of the rectangle. 14457 * @param $h (float) height of the rectangle. 14458 * @param $col1 (array) first color (lower left corner) (RGB components). 14459 * @param $col2 (array) second color (lower right corner) (RGB components). 14460 * @param $col3 (array) third color (upper right corner) (RGB components). 14461 * @param $col4 (array) fourth color (upper left corner) (RGB components). 14462 * @param $coords (array) <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> 14463 * @param $coords_min (array) minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0 14464 * @param $coords_max (array) maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1 14465 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts. 14466 * @author Andreas W\FCrmser, Nicola Asuni 14467 * @since 3.1.000 (2008-06-09) 14468 * @public 14469 */ 14470 public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1, $antialias=false) { 14471 if ($this->pdfa_mode OR ($this->state != 2)) { 14472 return; 14473 } 14474 $this->Clip($x, $y, $w, $h); 14475 $n = count($this->gradients) + 1; 14476 $this->gradients[$n] = array(); 14477 $this->gradients[$n]['type'] = 6; //coons patch mesh 14478 $this->gradients[$n]['coords'] = array(); 14479 $this->gradients[$n]['antialias'] = $antialias; 14480 $this->gradients[$n]['colors'] = array(); 14481 $this->gradients[$n]['transparency'] = false; 14482 //check the coords array if it is the simple array or the multi patch array 14483 if (!isset($coords[0]['f'])) { 14484 //simple array -> convert to multi patch array 14485 if (!isset($col1[1])) { 14486 $col1[1] = $col1[2] = $col1[0]; 14487 } 14488 if (!isset($col2[1])) { 14489 $col2[1] = $col2[2] = $col2[0]; 14490 } 14491 if (!isset($col3[1])) { 14492 $col3[1] = $col3[2] = $col3[0]; 14493 } 14494 if (!isset($col4[1])) { 14495 $col4[1] = $col4[2] = $col4[0]; 14496 } 14497 $patch_array[0]['f'] = 0; 14498 $patch_array[0]['points'] = $coords; 14499 $patch_array[0]['colors'][0]['r'] = $col1[0]; 14500 $patch_array[0]['colors'][0]['g'] = $col1[1]; 14501 $patch_array[0]['colors'][0]['b'] = $col1[2]; 14502 $patch_array[0]['colors'][1]['r'] = $col2[0]; 14503 $patch_array[0]['colors'][1]['g'] = $col2[1]; 14504 $patch_array[0]['colors'][1]['b'] = $col2[2]; 14505 $patch_array[0]['colors'][2]['r'] = $col3[0]; 14506 $patch_array[0]['colors'][2]['g'] = $col3[1]; 14507 $patch_array[0]['colors'][2]['b'] = $col3[2]; 14508 $patch_array[0]['colors'][3]['r'] = $col4[0]; 14509 $patch_array[0]['colors'][3]['g'] = $col4[1]; 14510 $patch_array[0]['colors'][3]['b'] = $col4[2]; 14511 } else { 14512 //multi patch array 14513 $patch_array = $coords; 14514 } 14515 $bpcd = 65535; //16 bits per coordinate 14516 //build the data stream 14517 $this->gradients[$n]['stream'] = ''; 14518 $count_patch = count($patch_array); 14519 for ($i=0; $i < $count_patch; ++$i) { 14520 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit 14521 $count_points = count($patch_array[$i]['points']); 14522 for ($j=0; $j < $count_points; ++$j) { 14523 //each point as 16 bit 14524 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd; 14525 if ($patch_array[$i]['points'][$j] < 0) { 14526 $patch_array[$i]['points'][$j] = 0; 14527 } 14528 if ($patch_array[$i]['points'][$j] > $bpcd) { 14529 $patch_array[$i]['points'][$j] = $bpcd; 14530 } 14531 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256)); 14532 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256)); 14533 } 14534 $count_cols = count($patch_array[$i]['colors']); 14535 for ($j=0; $j < $count_cols; ++$j) { 14536 //each color component as 8 bit 14537 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']); 14538 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']); 14539 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']); 14540 } 14541 } 14542 //paint the gradient 14543 $this->_out('/Sh'.$n.' sh'); 14544 //restore previous Graphic State 14545 $this->_outRestoreGraphicsState(); 14546 if ($this->inxobj) { 14547 // we are inside an XObject template 14548 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; 14549 } 14550 } 14551 14552 /** 14553 * Set a rectangular clipping area. 14554 * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode). 14555 * @param $y (float) ordinate of the top left corner of the rectangle. 14556 * @param $w (float) width of the rectangle. 14557 * @param $h (float) height of the rectangle. 14558 * @author Andreas W\FCrmser, Nicola Asuni 14559 * @since 3.1.000 (2008-06-09) 14560 * @protected 14561 */ 14562 protected function Clip($x, $y, $w, $h) { 14563 if ($this->state != 2) { 14564 return; 14565 } 14566 if ($this->rtl) { 14567 $x = $this->w - $x - $w; 14568 } 14569 //save current Graphic State 14570 $s = 'q'; 14571 //set clipping area 14572 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k); 14573 //set up transformation matrix for gradient 14574 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k); 14575 $this->_out($s); 14576 } 14577 14578 /** 14579 * Output gradient. 14580 * @param $type (int) type of gradient (1 Function-based shading; 2 Axial shading; 3 Radial shading; 4 Free-form Gouraud-shaded triangle mesh; 5 Lattice-form Gouraud-shaded triangle mesh; 6 Coons patch mesh; 7 Tensor-product patch mesh). (Not all types are currently supported) 14581 * @param $coords (array) array of coordinates. 14582 * @param $stops (array) array gradient color components: color = array of GRAY, RGB or CMYK color components; offset = (0 to 1) represents a location along the gradient vector; exponent = exponent of the exponential interpolation function (default = 1). 14583 * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value. 14584 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts. 14585 * @author Nicola Asuni 14586 * @since 3.1.000 (2008-06-09) 14587 * @public 14588 */ 14589 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) { 14590 if ($this->pdfa_mode OR ($this->state != 2)) { 14591 return; 14592 } 14593 $n = count($this->gradients) + 1; 14594 $this->gradients[$n] = array(); 14595 $this->gradients[$n]['type'] = $type; 14596 $this->gradients[$n]['coords'] = $coords; 14597 $this->gradients[$n]['antialias'] = $antialias; 14598 $this->gradients[$n]['colors'] = array(); 14599 $this->gradients[$n]['transparency'] = false; 14600 // color space 14601 $numcolspace = count($stops[0]['color']); 14602 $bcolor = array_values($background); 14603 switch($numcolspace) { 14604 case 5: // SPOT 14605 case 4: { // CMYK 14606 $this->gradients[$n]['colspace'] = 'DeviceCMYK'; 14607 if (!empty($background)) { 14608 $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100); 14609 } 14610 break; 14611 } 14612 case 3: { // RGB 14613 $this->gradients[$n]['colspace'] = 'DeviceRGB'; 14614 if (!empty($background)) { 14615 $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255); 14616 } 14617 break; 14618 } 14619 case 1: { // GRAY SCALE 14620 $this->gradients[$n]['colspace'] = 'DeviceGray'; 14621 if (!empty($background)) { 14622 $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255); 14623 } 14624 break; 14625 } 14626 } 14627 $num_stops = count($stops); 14628 $last_stop_id = $num_stops - 1; 14629 foreach ($stops as $key => $stop) { 14630 $this->gradients[$n]['colors'][$key] = array(); 14631 // offset represents a location along the gradient vector 14632 if (isset($stop['offset'])) { 14633 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset']; 14634 } else { 14635 if ($key == 0) { 14636 $this->gradients[$n]['colors'][$key]['offset'] = 0; 14637 } elseif ($key == $last_stop_id) { 14638 $this->gradients[$n]['colors'][$key]['offset'] = 1; 14639 } else { 14640 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key); 14641 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep; 14642 } 14643 } 14644 if (isset($stop['opacity'])) { 14645 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity']; 14646 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) { 14647 $this->gradients[$n]['transparency'] = true; 14648 } 14649 } else { 14650 $this->gradients[$n]['colors'][$key]['opacity'] = 1; 14651 } 14652 // exponent for the exponential interpolation function 14653 if (isset($stop['exponent'])) { 14654 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent']; 14655 } else { 14656 $this->gradients[$n]['colors'][$key]['exponent'] = 1; 14657 } 14658 // set colors 14659 $color = array_values($stop['color']); 14660 switch($numcolspace) { 14661 case 5: // SPOT 14662 case 4: { // CMYK 14663 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100); 14664 break; 14665 } 14666 case 3: { // RGB 14667 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255); 14668 break; 14669 } 14670 case 1: { // GRAY SCALE 14671 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255); 14672 break; 14673 } 14674 } 14675 } 14676 if ($this->gradients[$n]['transparency']) { 14677 // paint luminosity gradient 14678 $this->_out('/TGS'.$n.' gs'); 14679 } 14680 //paint the gradient 14681 $this->_out('/Sh'.$n.' sh'); 14682 //restore previous Graphic State 14683 $this->_outRestoreGraphicsState(); 14684 if ($this->inxobj) { 14685 // we are inside an XObject template 14686 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; 14687 } 14688 } 14689 14690 /** 14691 * Output gradient shaders. 14692 * @author Nicola Asuni 14693 * @since 3.1.000 (2008-06-09) 14694 * @protected 14695 */ 14696 function _putshaders() { 14697 if ($this->pdfa_mode) { 14698 return; 14699 } 14700 $idt = count($this->gradients); //index for transparency gradients 14701 foreach ($this->gradients as $id => $grad) { 14702 if (($grad['type'] == 2) OR ($grad['type'] == 3)) { 14703 $fc = $this->_newobj(); 14704 $out = '<<'; 14705 $out .= ' /FunctionType 3'; 14706 $out .= ' /Domain [0 1]'; 14707 $functions = ''; 14708 $bounds = ''; 14709 $encode = ''; 14710 $i = 1; 14711 $num_cols = count($grad['colors']); 14712 $lastcols = $num_cols - 1; 14713 for ($i = 1; $i < $num_cols; ++$i) { 14714 $functions .= ($fc + $i).' 0 R '; 14715 if ($i < $lastcols) { 14716 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']); 14717 } 14718 $encode .= '0 1 '; 14719 } 14720 $out .= ' /Functions ['.trim($functions).']'; 14721 $out .= ' /Bounds ['.trim($bounds).']'; 14722 $out .= ' /Encode ['.trim($encode).']'; 14723 $out .= ' >>'; 14724 $out .= "\n".'endobj'; 14725 $this->_out($out); 14726 for ($i = 1; $i < $num_cols; ++$i) { 14727 $this->_newobj(); 14728 $out = '<<'; 14729 $out .= ' /FunctionType 2'; 14730 $out .= ' /Domain [0 1]'; 14731 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']'; 14732 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']'; 14733 $out .= ' /N '.$grad['colors'][$i]['exponent']; 14734 $out .= ' >>'; 14735 $out .= "\n".'endobj'; 14736 $this->_out($out); 14737 } 14738 // set transparency fuctions 14739 if ($grad['transparency']) { 14740 $ft = $this->_newobj(); 14741 $out = '<<'; 14742 $out .= ' /FunctionType 3'; 14743 $out .= ' /Domain [0 1]'; 14744 $functions = ''; 14745 $i = 1; 14746 $num_cols = count($grad['colors']); 14747 for ($i = 1; $i < $num_cols; ++$i) { 14748 $functions .= ($ft + $i).' 0 R '; 14749 } 14750 $out .= ' /Functions ['.trim($functions).']'; 14751 $out .= ' /Bounds ['.trim($bounds).']'; 14752 $out .= ' /Encode ['.trim($encode).']'; 14753 $out .= ' >>'; 14754 $out .= "\n".'endobj'; 14755 $this->_out($out); 14756 for ($i = 1; $i < $num_cols; ++$i) { 14757 $this->_newobj(); 14758 $out = '<<'; 14759 $out .= ' /FunctionType 2'; 14760 $out .= ' /Domain [0 1]'; 14761 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']'; 14762 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']'; 14763 $out .= ' /N '.$grad['colors'][$i]['exponent']; 14764 $out .= ' >>'; 14765 $out .= "\n".'endobj'; 14766 $this->_out($out); 14767 } 14768 } 14769 } 14770 // set shading object 14771 $this->_newobj(); 14772 $out = '<< /ShadingType '.$grad['type']; 14773 if (isset($grad['colspace'])) { 14774 $out .= ' /ColorSpace /'.$grad['colspace']; 14775 } else { 14776 $out .= ' /ColorSpace /DeviceRGB'; 14777 } 14778 if (isset($grad['background']) AND !empty($grad['background'])) { 14779 $out .= ' /Background ['.$grad['background'].']'; 14780 } 14781 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) { 14782 $out .= ' /AntiAlias true'; 14783 } 14784 if ($grad['type'] == 2) { 14785 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]); 14786 $out .= ' /Domain [0 1]'; 14787 $out .= ' /Function '.$fc.' 0 R'; 14788 $out .= ' /Extend [true true]'; 14789 $out .= ' >>'; 14790 } elseif ($grad['type'] == 3) { 14791 //x0, y0, r0, x1, y1, r1 14792 //at this this time radius of inner circle is 0 14793 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]); 14794 $out .= ' /Domain [0 1]'; 14795 $out .= ' /Function '.$fc.' 0 R'; 14796 $out .= ' /Extend [true true]'; 14797 $out .= ' >>'; 14798 } elseif ($grad['type'] == 6) { 14799 $out .= ' /BitsPerCoordinate 16'; 14800 $out .= ' /BitsPerComponent 8'; 14801 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]'; 14802 $out .= ' /BitsPerFlag 8'; 14803 $stream = $this->_getrawstream($grad['stream']); 14804 $out .= ' /Length '.strlen($stream); 14805 $out .= ' >>'; 14806 $out .= ' stream'."\n".$stream."\n".'endstream'; 14807 } 14808 $out .= "\n".'endobj'; 14809 $this->_out($out); 14810 if ($grad['transparency']) { 14811 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out); 14812 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency); 14813 } 14814 $this->gradients[$id]['id'] = $this->n; 14815 // set pattern object 14816 $this->_newobj(); 14817 $out = '<< /Type /Pattern /PatternType 2'; 14818 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R'; 14819 $out .= ' >>'; 14820 $out .= "\n".'endobj'; 14821 $this->_out($out); 14822 $this->gradients[$id]['pattern'] = $this->n; 14823 // set shading and pattern for transparency mask 14824 if ($grad['transparency']) { 14825 // luminosity pattern 14826 $idgs = $id + $idt; 14827 $this->_newobj(); 14828 $this->_out($shading_transparency); 14829 $this->gradients[$idgs]['id'] = $this->n; 14830 $this->_newobj(); 14831 $out = '<< /Type /Pattern /PatternType 2'; 14832 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R'; 14833 $out .= ' >>'; 14834 $out .= "\n".'endobj'; 14835 $this->_out($out); 14836 $this->gradients[$idgs]['pattern'] = $this->n; 14837 // luminosity XObject 14838 $oid = $this->_newobj(); 14839 $this->xobjects['LX'.$oid] = array('n' => $oid); 14840 $filter = ''; 14841 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q'; 14842 if ($this->compress) { 14843 $filter = ' /Filter /FlateDecode'; 14844 $stream = gzcompress($stream); 14845 } 14846 $stream = $this->_getrawstream($stream); 14847 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter; 14848 $out .= ' /Length '.strlen($stream); 14849 $rect = sprintf('%F %F', $this->wPt, $this->hPt); 14850 $out .= ' /BBox [0 0 '.$rect.']'; 14851 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>'; 14852 $out .= ' /Resources <<'; 14853 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>'; 14854 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>'; 14855 $out .= ' >>'; 14856 $out .= ' >> '; 14857 $out .= ' stream'."\n".$stream."\n".'endstream'; 14858 $out .= "\n".'endobj'; 14859 $this->_out($out); 14860 // SMask 14861 $this->_newobj(); 14862 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj'; 14863 $this->_out($out); 14864 // ExtGState 14865 $this->_newobj(); 14866 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj'; 14867 $this->_out($out); 14868 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id); 14869 } 14870 } 14871 } 14872 14873 /** 14874 * Draw the sector of a circle. 14875 * It can be used for instance to render pie charts. 14876 * @param $xc (float) abscissa of the center. 14877 * @param $yc (float) ordinate of the center. 14878 * @param $r (float) radius. 14879 * @param $a (float) start angle (in degrees). 14880 * @param $b (float) end angle (in degrees). 14881 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 14882 * @param $cw: (float) indicates whether to go clockwise (default: true). 14883 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90. 14884 * @author Maxime Delorme, Nicola Asuni 14885 * @since 3.1.000 (2008-06-09) 14886 * @public 14887 */ 14888 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) { 14889 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o); 14890 } 14891 14892 /** 14893 * Draw the sector of an ellipse. 14894 * It can be used for instance to render pie charts. 14895 * @param $xc (float) abscissa of the center. 14896 * @param $yc (float) ordinate of the center. 14897 * @param $rx (float) the x-axis radius. 14898 * @param $ry (float) the y-axis radius. 14899 * @param $a (float) start angle (in degrees). 14900 * @param $b (float) end angle (in degrees). 14901 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 14902 * @param $cw: (float) indicates whether to go clockwise. 14903 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). 14904 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc. 14905 * @author Maxime Delorme, Nicola Asuni 14906 * @since 3.1.000 (2008-06-09) 14907 * @public 14908 */ 14909 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) { 14910 if ($this->state != 2) { 14911 return; 14912 } 14913 if ($this->rtl) { 14914 $xc = ($this->w - $xc); 14915 } 14916 $op = TCPDF_STATIC::getPathPaintOperator($style); 14917 if ($op == 'f') { 14918 $line_style = array(); 14919 } 14920 if ($cw) { 14921 $d = $b; 14922 $b = (360 - $a + $o); 14923 $a = (360 - $d + $o); 14924 } else { 14925 $b += $o; 14926 $a += $o; 14927 } 14928 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc); 14929 $this->_out($op); 14930 } 14931 14932 /** 14933 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files. 14934 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library. 14935 * Only vector drawing is supported, not text or bitmap. 14936 * 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). 14937 * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string. 14938 * @param $x (float) Abscissa of the upper-left corner. 14939 * @param $y (float) Ordinate of the upper-left corner. 14940 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 14941 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 14942 * @param $link (mixed) URL or identifier returned by AddLink(). 14943 * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true. 14944 * @param $align (string) 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> 14945 * @param $palign (string) 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> 14946 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 14947 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions. 14948 * @param $fixoutvals (boolean) if true remove values outside the bounding box. 14949 * @author Valentin Schmidt, Nicola Asuni 14950 * @since 3.1.000 (2008-06-09) 14951 * @public 14952 */ 14953 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) { 14954 if ($this->state != 2) { 14955 return; 14956 } 14957 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { 14958 // convert EPS to raster image using GD or ImageMagick libraries 14959 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 14960 } 14961 if ($x === '') { 14962 $x = $this->x; 14963 } 14964 if ($y === '') { 14965 $y = $this->y; 14966 } 14967 // check page for no-write regions and adapt page margins if necessary 14968 list($x, $y) = $this->checkPageRegions($h, $x, $y); 14969 $k = $this->k; 14970 if ($file[0] === '@') { // image from string 14971 $data = substr($file, 1); 14972 } else { // EPS/AI file 14973 $data = TCPDF_STATIC::fileGetContents($file); 14974 } 14975 if ($data === FALSE) { 14976 $this->Error('EPS file not found: '.$file); 14977 } 14978 $regs = array(); 14979 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!) 14980 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator 14981 if (count($regs) > 1) { 14982 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0" 14983 if (strpos($version_str, 'Adobe Illustrator') !== false) { 14984 $versexp = explode(' ', $version_str); 14985 $version = (float)array_pop($versexp); 14986 if ($version >= 9) { 14987 $this->Error('This version of Adobe Illustrator file is not supported: '.$file); 14988 } 14989 } 14990 } 14991 // strip binary bytes in front of PS-header 14992 $start = strpos($data, '%!PS-Adobe'); 14993 if ($start > 0) { 14994 $data = substr($data, $start); 14995 } 14996 // find BoundingBox params 14997 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs); 14998 if (count($regs) > 1) { 14999 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1])); 15000 } else { 15001 $this->Error('No BoundingBox found in EPS/AI file: '.$file); 15002 } 15003 $start = strpos($data, '%%EndSetup'); 15004 if ($start === false) { 15005 $start = strpos($data, '%%EndProlog'); 15006 } 15007 if ($start === false) { 15008 $start = strpos($data, '%%BoundingBox'); 15009 } 15010 $data = substr($data, $start); 15011 $end = strpos($data, '%%PageTrailer'); 15012 if ($end===false) { 15013 $end = strpos($data, 'showpage'); 15014 } 15015 if ($end) { 15016 $data = substr($data, 0, $end); 15017 } 15018 // calculate image width and height on document 15019 if (($w <= 0) AND ($h <= 0)) { 15020 $w = ($x2 - $x1) / $k; 15021 $h = ($y2 - $y1) / $k; 15022 } elseif ($w <= 0) { 15023 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k)); 15024 } elseif ($h <= 0) { 15025 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k)); 15026 } 15027 // fit the image on available space 15028 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 15029 if ($this->rasterize_vector_images) { 15030 // convert EPS to raster image using GD or ImageMagick libraries 15031 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 15032 } 15033 // set scaling factors 15034 $scale_x = $w / (($x2 - $x1) / $k); 15035 $scale_y = $h / (($y2 - $y1) / $k); 15036 // set alignment 15037 $this->img_rb_y = $y + $h; 15038 // set alignment 15039 if ($this->rtl) { 15040 if ($palign == 'L') { 15041 $ximg = $this->lMargin; 15042 } elseif ($palign == 'C') { 15043 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15044 } elseif ($palign == 'R') { 15045 $ximg = $this->w - $this->rMargin - $w; 15046 } else { 15047 $ximg = $x - $w; 15048 } 15049 $this->img_rb_x = $ximg; 15050 } else { 15051 if ($palign == 'L') { 15052 $ximg = $this->lMargin; 15053 } elseif ($palign == 'C') { 15054 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15055 } elseif ($palign == 'R') { 15056 $ximg = $this->w - $this->rMargin - $w; 15057 } else { 15058 $ximg = $x; 15059 } 15060 $this->img_rb_x = $ximg + $w; 15061 } 15062 if ($useBoundingBox) { 15063 $dx = $ximg * $k - $x1; 15064 $dy = $y * $k - $y1; 15065 } else { 15066 $dx = $ximg * $k; 15067 $dy = $y * $k; 15068 } 15069 // save the current graphic state 15070 $this->_out('q'.$this->epsmarker); 15071 // translate 15072 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1)))); 15073 // scale 15074 if (isset($scale_x)) { 15075 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y))); 15076 } 15077 // handle pc/unix/mac line endings 15078 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY); 15079 $u=0; 15080 $cnt = count($lines); 15081 for ($i=0; $i < $cnt; ++$i) { 15082 $line = $lines[$i]; 15083 if (($line == '') OR ($line[0] == '%')) { 15084 continue; 15085 } 15086 $len = strlen($line); 15087 // check for spot color names 15088 $color_name = ''; 15089 if (strcasecmp('x', substr(trim($line), -1)) == 0) { 15090 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) { 15091 // extract spot color name 15092 $color_name = $matches[0]; 15093 // remove color name from string 15094 $line = str_replace(' '.$color_name, '', $line); 15095 // remove pharentesis from color name 15096 $color_name = substr($color_name, 1, -1); 15097 } 15098 } 15099 $chunks = explode(' ', $line); 15100 $cmd = trim(array_pop($chunks)); 15101 // RGB 15102 if (($cmd == 'Xa') OR ($cmd == 'XA')) { 15103 $b = array_pop($chunks); 15104 $g = array_pop($chunks); 15105 $r = array_pop($chunks); 15106 $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! 15107 continue; 15108 } 15109 $skip = false; 15110 if ($fixoutvals) { 15111 // check for values outside the bounding box 15112 switch ($cmd) { 15113 case 'm': 15114 case 'l': 15115 case 'L': { 15116 // skip values outside bounding box 15117 foreach ($chunks as $key => $val) { 15118 if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) { 15119 $skip = true; 15120 } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) { 15121 $skip = true; 15122 } 15123 } 15124 } 15125 } 15126 } 15127 switch ($cmd) { 15128 case 'm': 15129 case 'l': 15130 case 'v': 15131 case 'y': 15132 case 'c': 15133 case 'k': 15134 case 'K': 15135 case 'g': 15136 case 'G': 15137 case 's': 15138 case 'S': 15139 case 'J': 15140 case 'j': 15141 case 'w': 15142 case 'M': 15143 case 'd': 15144 case 'n': { 15145 if ($skip) { 15146 break; 15147 } 15148 $this->_out($line); 15149 break; 15150 } 15151 case 'x': {// custom fill color 15152 if (empty($color_name)) { 15153 // CMYK color 15154 list($col_c, $col_m, $col_y, $col_k) = $chunks; 15155 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k'); 15156 } else { 15157 // Spot Color (CMYK + tint) 15158 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; 15159 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); 15160 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t)); 15161 $this->_out($color_cmd); 15162 } 15163 break; 15164 } 15165 case 'X': { // custom stroke color 15166 if (empty($color_name)) { 15167 // CMYK color 15168 list($col_c, $col_m, $col_y, $col_k) = $chunks; 15169 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K'); 15170 } else { 15171 // Spot Color (CMYK + tint) 15172 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; 15173 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); 15174 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t)); 15175 $this->_out($color_cmd); 15176 } 15177 break; 15178 } 15179 case 'Y': 15180 case 'N': 15181 case 'V': 15182 case 'L': 15183 case 'C': { 15184 if ($skip) { 15185 break; 15186 } 15187 $line[($len - 1)] = strtolower($cmd); 15188 $this->_out($line); 15189 break; 15190 } 15191 case 'b': 15192 case 'B': { 15193 $this->_out($cmd . '*'); 15194 break; 15195 } 15196 case 'f': 15197 case 'F': { 15198 if ($u > 0) { 15199 $isU = false; 15200 $max = min(($i + 5), $cnt); 15201 for ($j = ($i + 1); $j < $max; ++$j) { 15202 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U'))); 15203 } 15204 if ($isU) { 15205 $this->_out('f*'); 15206 } 15207 } else { 15208 $this->_out('f*'); 15209 } 15210 break; 15211 } 15212 case '*u': { 15213 ++$u; 15214 break; 15215 } 15216 case '*U': { 15217 --$u; 15218 break; 15219 } 15220 } 15221 } 15222 // restore previous graphic state 15223 $this->_out($this->epsmarker.'Q'); 15224 if (!empty($border)) { 15225 $bx = $this->x; 15226 $by = $this->y; 15227 $this->x = $ximg; 15228 if ($this->rtl) { 15229 $this->x += $w; 15230 } 15231 $this->y = $y; 15232 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 15233 $this->x = $bx; 15234 $this->y = $by; 15235 } 15236 if ($link) { 15237 $this->Link($ximg, $y, $w, $h, $link, 0); 15238 } 15239 // set pointer to align the next text/objects 15240 switch($align) { 15241 case 'T':{ 15242 $this->y = $y; 15243 $this->x = $this->img_rb_x; 15244 break; 15245 } 15246 case 'M':{ 15247 $this->y = $y + round($h/2); 15248 $this->x = $this->img_rb_x; 15249 break; 15250 } 15251 case 'B':{ 15252 $this->y = $this->img_rb_y; 15253 $this->x = $this->img_rb_x; 15254 break; 15255 } 15256 case 'N':{ 15257 $this->SetY($this->img_rb_y); 15258 break; 15259 } 15260 default:{ 15261 break; 15262 } 15263 } 15264 $this->endlinex = $this->img_rb_x; 15265 } 15266 15267 /** 15268 * Set document barcode. 15269 * @param $bc (string) barcode 15270 * @public 15271 */ 15272 public function setBarcode($bc='') { 15273 $this->barcode = $bc; 15274 } 15275 15276 /** 15277 * Get current barcode. 15278 * @return string 15279 * @public 15280 * @since 4.0.012 (2008-07-24) 15281 */ 15282 public function getBarcode() { 15283 return $this->barcode; 15284 } 15285 15286 /** 15287 * Print a Linear Barcode. 15288 * @param $code (string) code to print 15289 * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats). 15290 * @param $x (int) x position in user units (empty string = current x position) 15291 * @param $y (int) y position in user units (empty string = current y position) 15292 * @param $w (int) width in user units (empty string = remaining page width) 15293 * @param $h (int) height in user units (empty string = remaining page height) 15294 * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm) 15295 * @param $style (array) array of options:<ul> 15296 * <li>boolean $style['border'] if true prints a border</li> 15297 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li> 15298 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li> 15299 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li> 15300 * <li>array $style['fgcolor'] color array for bars and text</li> 15301 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li> 15302 * <li>boolean $style['text'] if true prints text below the barcode</li> 15303 * <li>string $style['label'] override default label</li> 15304 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li> 15305 * <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> 15306 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li> 15307 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li> 15308 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li> 15309 * <li>string $style['fitwidth'] if true reduce the width to fit the barcode width + padding. When this option is enabled the 'stretch' option is automatically disabled.</li> 15310 * <li>string $style['cellfitalign'] this option works only when 'fitwidth' is true and 'position' is unset or empty. Set the horizontal position of the containing barcode cell inside the specified rectangle: L = left; C = center; R = right.</li></ul> 15311 * @param $align (string) 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> 15312 * @author Nicola Asuni 15313 * @since 3.1.000 (2008-06-09) 15314 * @public 15315 */ 15316 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') { 15317 if (TCPDF_STATIC::empty_string(trim($code))) { 15318 return; 15319 } 15320 require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php'); 15321 // save current graphic settings 15322 $gvars = $this->getGraphicVars(); 15323 // create new barcode object 15324 $barcodeobj = new TCPDFBarcode($code, $type); 15325 $arrcode = $barcodeobj->getBarcodeArray(); 15326 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) { 15327 $this->Error('Error in 1D barcode string'); 15328 } 15329 if ($arrcode['maxh'] <= 0) { 15330 $arrcode['maxh'] = 1; 15331 } 15332 // set default values 15333 if (!isset($style['position'])) { 15334 $style['position'] = ''; 15335 } elseif ($style['position'] == 'S') { 15336 // keep this for backward compatibility 15337 $style['position'] = ''; 15338 $style['stretch'] = true; 15339 } 15340 if (!isset($style['fitwidth'])) { 15341 if (!isset($style['stretch'])) { 15342 $style['fitwidth'] = true; 15343 } else { 15344 $style['fitwidth'] = false; 15345 } 15346 } 15347 if ($style['fitwidth']) { 15348 // disable stretch 15349 $style['stretch'] = false; 15350 } 15351 if (!isset($style['stretch'])) { 15352 if (($w === '') OR ($w <= 0)) { 15353 $style['stretch'] = false; 15354 } else { 15355 $style['stretch'] = true; 15356 } 15357 } 15358 if (!isset($style['fgcolor'])) { 15359 $style['fgcolor'] = array(0,0,0); // default black 15360 } 15361 if (!isset($style['bgcolor'])) { 15362 $style['bgcolor'] = false; // default transparent 15363 } 15364 if (!isset($style['border'])) { 15365 $style['border'] = false; 15366 } 15367 $fontsize = 0; 15368 if (!isset($style['text'])) { 15369 $style['text'] = false; 15370 } 15371 if ($style['text'] AND isset($style['font'])) { 15372 if (isset($style['fontsize'])) { 15373 $fontsize = $style['fontsize']; 15374 } 15375 $this->SetFont($style['font'], '', $fontsize); 15376 } 15377 if (!isset($style['stretchtext'])) { 15378 $style['stretchtext'] = 4; 15379 } 15380 if ($x === '') { 15381 $x = $this->x; 15382 } 15383 if ($y === '') { 15384 $y = $this->y; 15385 } 15386 // check page for no-write regions and adapt page margins if necessary 15387 list($x, $y) = $this->checkPageRegions($h, $x, $y); 15388 if (($w === '') OR ($w <= 0)) { 15389 if ($this->rtl) { 15390 $w = $x - $this->lMargin; 15391 } else { 15392 $w = $this->w - $this->rMargin - $x; 15393 } 15394 } 15395 // padding 15396 if (!isset($style['padding'])) { 15397 $padding = 0; 15398 } elseif ($style['padding'] === 'auto') { 15399 $padding = 10 * ($w / ($arrcode['maxw'] + 20)); 15400 } else { 15401 $padding = floatval($style['padding']); 15402 } 15403 // horizontal padding 15404 if (!isset($style['hpadding'])) { 15405 $hpadding = $padding; 15406 } elseif ($style['hpadding'] === 'auto') { 15407 $hpadding = 10 * ($w / ($arrcode['maxw'] + 20)); 15408 } else { 15409 $hpadding = floatval($style['hpadding']); 15410 } 15411 // vertical padding 15412 if (!isset($style['vpadding'])) { 15413 $vpadding = $padding; 15414 } elseif ($style['vpadding'] === 'auto') { 15415 $vpadding = ($hpadding / 2); 15416 } else { 15417 $vpadding = floatval($style['vpadding']); 15418 } 15419 // calculate xres (single bar width) 15420 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw']; 15421 if ($style['stretch']) { 15422 $xres = $max_xres; 15423 } else { 15424 if (TCPDF_STATIC::empty_string($xres)) { 15425 $xres = (0.141 * $this->k); // default bar width = 0.4 mm 15426 } 15427 if ($xres > $max_xres) { 15428 // correct xres to fit on $w 15429 $xres = $max_xres; 15430 } 15431 if ((isset($style['padding']) AND ($style['padding'] === 'auto')) 15432 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) { 15433 $hpadding = 10 * $xres; 15434 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) { 15435 $vpadding = ($hpadding / 2); 15436 } 15437 } 15438 } 15439 if ($style['fitwidth']) { 15440 $wold = $w; 15441 $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding)); 15442 if (isset($style['cellfitalign'])) { 15443 switch ($style['cellfitalign']) { 15444 case 'L': { 15445 if ($this->rtl) { 15446 $x -= ($wold - $w); 15447 } 15448 break; 15449 } 15450 case 'R': { 15451 if (!$this->rtl) { 15452 $x += ($wold - $w); 15453 } 15454 break; 15455 } 15456 case 'C': { 15457 if ($this->rtl) { 15458 $x -= (($wold - $w) / 2); 15459 } else { 15460 $x += (($wold - $w) / 2); 15461 } 15462 break; 15463 } 15464 default : { 15465 break; 15466 } 15467 } 15468 } 15469 } 15470 $text_height = $this->getCellHeight($fontsize / $this->k); 15471 // height 15472 if (($h === '') OR ($h <= 0)) { 15473 // set default height 15474 $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height; 15475 } 15476 $barh = $h - $text_height - (2 * $vpadding); 15477 if ($barh <=0) { 15478 // try to reduce font or padding to fit barcode on available height 15479 if ($text_height > $h) { 15480 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio)); 15481 $text_height = $this->getCellHeight($fontsize / $this->k); 15482 $this->SetFont($style['font'], '', $fontsize); 15483 } 15484 if ($vpadding > 0) { 15485 $vpadding = (($h - $text_height) / 4); 15486 } 15487 $barh = $h - $text_height - (2 * $vpadding); 15488 } 15489 // fit the barcode on available space 15490 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); 15491 // set alignment 15492 $this->img_rb_y = $y + $h; 15493 // set alignment 15494 if ($this->rtl) { 15495 if ($style['position'] == 'L') { 15496 $xpos = $this->lMargin; 15497 } elseif ($style['position'] == 'C') { 15498 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15499 } elseif ($style['position'] == 'R') { 15500 $xpos = $this->w - $this->rMargin - $w; 15501 } else { 15502 $xpos = $x - $w; 15503 } 15504 $this->img_rb_x = $xpos; 15505 } else { 15506 if ($style['position'] == 'L') { 15507 $xpos = $this->lMargin; 15508 } elseif ($style['position'] == 'C') { 15509 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15510 } elseif ($style['position'] == 'R') { 15511 $xpos = $this->w - $this->rMargin - $w; 15512 } else { 15513 $xpos = $x; 15514 } 15515 $this->img_rb_x = $xpos + $w; 15516 } 15517 $xpos_rect = $xpos; 15518 if (!isset($style['align'])) { 15519 $style['align'] = 'C'; 15520 } 15521 switch ($style['align']) { 15522 case 'L': { 15523 $xpos = $xpos_rect + $hpadding; 15524 break; 15525 } 15526 case 'R': { 15527 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding; 15528 break; 15529 } 15530 case 'C': 15531 default : { 15532 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2); 15533 break; 15534 } 15535 } 15536 $xpos_text = $xpos; 15537 // barcode is always printed in LTR direction 15538 $tempRTL = $this->rtl; 15539 $this->rtl = false; 15540 // print background color 15541 if ($style['bgcolor']) { 15542 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 15543 } elseif ($style['border']) { 15544 $this->Rect($xpos_rect, $y, $w, $h, 'D'); 15545 } 15546 // set foreground color 15547 $this->SetDrawColorArray($style['fgcolor']); 15548 $this->SetTextColorArray($style['fgcolor']); 15549 // print bars 15550 foreach ($arrcode['bcode'] as $k => $v) { 15551 $bw = ($v['w'] * $xres); 15552 if ($v['t']) { 15553 // draw a vertical bar 15554 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']); 15555 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']); 15556 } 15557 $xpos += $bw; 15558 } 15559 // print text 15560 if ($style['text']) { 15561 if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) { 15562 $label = $style['label']; 15563 } else { 15564 $label = $code; 15565 } 15566 $txtwidth = ($arrcode['maxw'] * $xres); 15567 if ($this->GetStringWidth($label) > $txtwidth) { 15568 $style['stretchtext'] = 2; 15569 } 15570 // print text 15571 $this->x = $xpos_text; 15572 $this->y = $y + $vpadding + $barh; 15573 $cellpadding = $this->cell_padding; 15574 $this->SetCellPadding(0); 15575 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T'); 15576 $this->cell_padding = $cellpadding; 15577 } 15578 // restore original direction 15579 $this->rtl = $tempRTL; 15580 // restore previous settings 15581 $this->setGraphicVars($gvars); 15582 // set pointer to align the next text/objects 15583 switch($align) { 15584 case 'T':{ 15585 $this->y = $y; 15586 $this->x = $this->img_rb_x; 15587 break; 15588 } 15589 case 'M':{ 15590 $this->y = $y + round($h / 2); 15591 $this->x = $this->img_rb_x; 15592 break; 15593 } 15594 case 'B':{ 15595 $this->y = $this->img_rb_y; 15596 $this->x = $this->img_rb_x; 15597 break; 15598 } 15599 case 'N':{ 15600 $this->SetY($this->img_rb_y); 15601 break; 15602 } 15603 default:{ 15604 break; 15605 } 15606 } 15607 $this->endlinex = $this->img_rb_x; 15608 } 15609 15610 /** 15611 * Print 2D Barcode. 15612 * @param $code (string) code to print 15613 * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats). 15614 * @param $x (int) x position in user units 15615 * @param $y (int) y position in user units 15616 * @param $w (int) width in user units 15617 * @param $h (int) height in user units 15618 * @param $style (array) array of options:<ul> 15619 * <li>boolean $style['border'] if true prints a border around the barcode</li> 15620 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li> 15621 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li> 15622 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li> 15623 * <li>int $style['module_width'] width of a single module in points</li> 15624 * <li>int $style['module_height'] height of a single module in points</li> 15625 * <li>array $style['fgcolor'] color array for bars and text</li> 15626 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li> 15627 * <li>string $style['position'] barcode position on the page: L = left margin; C = center; R = right margin; S = stretch</li><li>$style['module_width'] width of a single module in points</li> 15628 * <li>$style['module_height'] height of a single module in points</li></ul> 15629 * @param $align (string) 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> 15630 * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio 15631 * @author Nicola Asuni 15632 * @since 4.5.037 (2009-04-07) 15633 * @public 15634 */ 15635 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) { 15636 if (TCPDF_STATIC::empty_string(trim($code))) { 15637 return; 15638 } 15639 require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php'); 15640 // save current graphic settings 15641 $gvars = $this->getGraphicVars(); 15642 // create new barcode object 15643 $barcodeobj = new TCPDF2DBarcode($code, $type); 15644 $arrcode = $barcodeobj->getBarcodeArray(); 15645 if (($arrcode === false) OR empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) { 15646 $this->Error('Error in 2D barcode string'); 15647 } 15648 // set default values 15649 if (!isset($style['position'])) { 15650 $style['position'] = ''; 15651 } 15652 if (!isset($style['fgcolor'])) { 15653 $style['fgcolor'] = array(0,0,0); // default black 15654 } 15655 if (!isset($style['bgcolor'])) { 15656 $style['bgcolor'] = false; // default transparent 15657 } 15658 if (!isset($style['border'])) { 15659 $style['border'] = false; 15660 } 15661 // padding 15662 if (!isset($style['padding'])) { 15663 $style['padding'] = 0; 15664 } elseif ($style['padding'] === 'auto') { 15665 $style['padding'] = 4; 15666 } 15667 if (!isset($style['hpadding'])) { 15668 $style['hpadding'] = $style['padding']; 15669 } elseif ($style['hpadding'] === 'auto') { 15670 $style['hpadding'] = 4; 15671 } 15672 if (!isset($style['vpadding'])) { 15673 $style['vpadding'] = $style['padding']; 15674 } elseif ($style['vpadding'] === 'auto') { 15675 $style['vpadding'] = 4; 15676 } 15677 $hpad = (2 * $style['hpadding']); 15678 $vpad = (2 * $style['vpadding']); 15679 // cell (module) dimension 15680 if (!isset($style['module_width'])) { 15681 $style['module_width'] = 1; // width of a single module in points 15682 } 15683 if (!isset($style['module_height'])) { 15684 $style['module_height'] = 1; // height of a single module in points 15685 } 15686 if ($x === '') { 15687 $x = $this->x; 15688 } 15689 if ($y === '') { 15690 $y = $this->y; 15691 } 15692 // check page for no-write regions and adapt page margins if necessary 15693 list($x, $y) = $this->checkPageRegions($h, $x, $y); 15694 // number of barcode columns and rows 15695 $rows = $arrcode['num_rows']; 15696 $cols = $arrcode['num_cols']; 15697 if (($rows <= 0) || ($cols <= 0)){ 15698 $this->Error('Error in 2D barcode string'); 15699 } 15700 // module width and height 15701 $mw = $style['module_width']; 15702 $mh = $style['module_height']; 15703 if (($mw <= 0) OR ($mh <= 0)) { 15704 $this->Error('Error in 2D barcode string'); 15705 } 15706 // get max dimensions 15707 if ($this->rtl) { 15708 $maxw = $x - $this->lMargin; 15709 } else { 15710 $maxw = $this->w - $this->rMargin - $x; 15711 } 15712 $maxh = ($this->h - $this->tMargin - $this->bMargin); 15713 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad)); 15714 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad)); 15715 if (!$distort) { 15716 if (($maxw * $ratioHW) > $maxh) { 15717 $maxw = $maxh * $ratioWH; 15718 } 15719 if (($maxh * $ratioWH) > $maxw) { 15720 $maxh = $maxw * $ratioHW; 15721 } 15722 } 15723 // set maximum dimesions 15724 if ($w > $maxw) { 15725 $w = $maxw; 15726 } 15727 if ($h > $maxh) { 15728 $h = $maxh; 15729 } 15730 // set dimensions 15731 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) { 15732 $w = ($cols + $hpad) * ($mw / $this->k); 15733 $h = ($rows + $vpad) * ($mh / $this->k); 15734 } elseif (($w === '') OR ($w <= 0)) { 15735 $w = $h * $ratioWH; 15736 } elseif (($h === '') OR ($h <= 0)) { 15737 $h = $w * $ratioHW; 15738 } 15739 // barcode size (excluding padding) 15740 $bw = ($w * $cols) / ($cols + $hpad); 15741 $bh = ($h * $rows) / ($rows + $vpad); 15742 // dimension of single barcode cell unit 15743 $cw = $bw / $cols; 15744 $ch = $bh / $rows; 15745 if (!$distort) { 15746 if (($cw / $ch) > ($mw / $mh)) { 15747 // correct horizontal distortion 15748 $cw = $ch * $mw / $mh; 15749 $bw = $cw * $cols; 15750 $style['hpadding'] = ($w - $bw) / (2 * $cw); 15751 } else { 15752 // correct vertical distortion 15753 $ch = $cw * $mh / $mw; 15754 $bh = $ch * $rows; 15755 $style['vpadding'] = ($h - $bh) / (2 * $ch); 15756 } 15757 } 15758 // fit the barcode on available space 15759 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); 15760 // set alignment 15761 $this->img_rb_y = $y + $h; 15762 // set alignment 15763 if ($this->rtl) { 15764 if ($style['position'] == 'L') { 15765 $xpos = $this->lMargin; 15766 } elseif ($style['position'] == 'C') { 15767 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15768 } elseif ($style['position'] == 'R') { 15769 $xpos = $this->w - $this->rMargin - $w; 15770 } else { 15771 $xpos = $x - $w; 15772 } 15773 $this->img_rb_x = $xpos; 15774 } else { 15775 if ($style['position'] == 'L') { 15776 $xpos = $this->lMargin; 15777 } elseif ($style['position'] == 'C') { 15778 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15779 } elseif ($style['position'] == 'R') { 15780 $xpos = $this->w - $this->rMargin - $w; 15781 } else { 15782 $xpos = $x; 15783 } 15784 $this->img_rb_x = $xpos + $w; 15785 } 15786 $xstart = $xpos + ($style['hpadding'] * $cw); 15787 $ystart = $y + ($style['vpadding'] * $ch); 15788 // barcode is always printed in LTR direction 15789 $tempRTL = $this->rtl; 15790 $this->rtl = false; 15791 // print background color 15792 if ($style['bgcolor']) { 15793 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 15794 } elseif ($style['border']) { 15795 $this->Rect($xpos, $y, $w, $h, 'D'); 15796 } 15797 // set foreground color 15798 $this->SetDrawColorArray($style['fgcolor']); 15799 // print barcode cells 15800 // for each row 15801 for ($r = 0; $r < $rows; ++$r) { 15802 $xr = $xstart; 15803 // for each column 15804 for ($c = 0; $c < $cols; ++$c) { 15805 if ($arrcode['bcode'][$r][$c] == 1) { 15806 // draw a single barcode cell 15807 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']); 15808 } 15809 $xr += $cw; 15810 } 15811 $ystart += $ch; 15812 } 15813 // restore original direction 15814 $this->rtl = $tempRTL; 15815 // restore previous settings 15816 $this->setGraphicVars($gvars); 15817 // set pointer to align the next text/objects 15818 switch($align) { 15819 case 'T':{ 15820 $this->y = $y; 15821 $this->x = $this->img_rb_x; 15822 break; 15823 } 15824 case 'M':{ 15825 $this->y = $y + round($h/2); 15826 $this->x = $this->img_rb_x; 15827 break; 15828 } 15829 case 'B':{ 15830 $this->y = $this->img_rb_y; 15831 $this->x = $this->img_rb_x; 15832 break; 15833 } 15834 case 'N':{ 15835 $this->SetY($this->img_rb_y); 15836 break; 15837 } 15838 default:{ 15839 break; 15840 } 15841 } 15842 $this->endlinex = $this->img_rb_x; 15843 } 15844 15845 /** 15846 * Returns an array containing current margins: 15847 * <ul> 15848 <li>$ret['left'] = left margin</li> 15849 <li>$ret['right'] = right margin</li> 15850 <li>$ret['top'] = top margin</li> 15851 <li>$ret['bottom'] = bottom margin</li> 15852 <li>$ret['header'] = header margin</li> 15853 <li>$ret['footer'] = footer margin</li> 15854 <li>$ret['cell'] = cell padding array</li> 15855 <li>$ret['padding_left'] = cell left padding</li> 15856 <li>$ret['padding_top'] = cell top padding</li> 15857 <li>$ret['padding_right'] = cell right padding</li> 15858 <li>$ret['padding_bottom'] = cell bottom padding</li> 15859 * </ul> 15860 * @return array containing all margins measures 15861 * @public 15862 * @since 3.2.000 (2008-06-23) 15863 */ 15864 public function getMargins() { 15865 $ret = array( 15866 'left' => $this->lMargin, 15867 'right' => $this->rMargin, 15868 'top' => $this->tMargin, 15869 'bottom' => $this->bMargin, 15870 'header' => $this->header_margin, 15871 'footer' => $this->footer_margin, 15872 'cell' => $this->cell_padding, 15873 'padding_left' => $this->cell_padding['L'], 15874 'padding_top' => $this->cell_padding['T'], 15875 'padding_right' => $this->cell_padding['R'], 15876 'padding_bottom' => $this->cell_padding['B'] 15877 ); 15878 return $ret; 15879 } 15880 15881 /** 15882 * Returns an array containing original margins: 15883 * <ul> 15884 <li>$ret['left'] = left margin</li> 15885 <li>$ret['right'] = right margin</li> 15886 * </ul> 15887 * @return array containing all margins measures 15888 * @public 15889 * @since 4.0.012 (2008-07-24) 15890 */ 15891 public function getOriginalMargins() { 15892 $ret = array( 15893 'left' => $this->original_lMargin, 15894 'right' => $this->original_rMargin 15895 ); 15896 return $ret; 15897 } 15898 15899 /** 15900 * Returns the current font size. 15901 * @return current font size 15902 * @public 15903 * @since 3.2.000 (2008-06-23) 15904 */ 15905 public function getFontSize() { 15906 return $this->FontSize; 15907 } 15908 15909 /** 15910 * Returns the current font size in points unit. 15911 * @return current font size in points unit 15912 * @public 15913 * @since 3.2.000 (2008-06-23) 15914 */ 15915 public function getFontSizePt() { 15916 return $this->FontSizePt; 15917 } 15918 15919 /** 15920 * Returns the current font family name. 15921 * @return string current font family name 15922 * @public 15923 * @since 4.3.008 (2008-12-05) 15924 */ 15925 public function getFontFamily() { 15926 return $this->FontFamily; 15927 } 15928 15929 /** 15930 * Returns the current font style. 15931 * @return string current font style 15932 * @public 15933 * @since 4.3.008 (2008-12-05) 15934 */ 15935 public function getFontStyle() { 15936 return $this->FontStyle; 15937 } 15938 15939 /** 15940 * Cleanup HTML code (requires HTML Tidy library). 15941 * @param $html (string) htmlcode to fix 15942 * @param $default_css (string) CSS commands to add 15943 * @param $tagvs (array) parameters for setHtmlVSpace method 15944 * @param $tidy_options (array) options for tidy_parse_string function 15945 * @return string XHTML code cleaned up 15946 * @author Nicola Asuni 15947 * @public 15948 * @since 5.9.017 (2010-11-16) 15949 * @see setHtmlVSpace() 15950 */ 15951 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') { 15952 return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces); 15953 } 15954 15955 /** 15956 * Returns the border width from CSS property 15957 * @param $width (string) border width 15958 * @return int with in user units 15959 * @protected 15960 * @since 5.7.000 (2010-08-02) 15961 */ 15962 protected function getCSSBorderWidth($width) { 15963 if ($width == 'thin') { 15964 $width = (2 / $this->k); 15965 } elseif ($width == 'medium') { 15966 $width = (4 / $this->k); 15967 } elseif ($width == 'thick') { 15968 $width = (6 / $this->k); 15969 } else { 15970 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false); 15971 } 15972 return $width; 15973 } 15974 15975 /** 15976 * Returns the border dash style from CSS property 15977 * @param $style (string) border style to convert 15978 * @return int sash style (return -1 in case of none or hidden border) 15979 * @protected 15980 * @since 5.7.000 (2010-08-02) 15981 */ 15982 protected function getCSSBorderDashStyle($style) { 15983 switch (strtolower($style)) { 15984 case 'none': 15985 case 'hidden': { 15986 $dash = -1; 15987 break; 15988 } 15989 case 'dotted': { 15990 $dash = 1; 15991 break; 15992 } 15993 case 'dashed': { 15994 $dash = 3; 15995 break; 15996 } 15997 case 'double': 15998 case 'groove': 15999 case 'ridge': 16000 case 'inset': 16001 case 'outset': 16002 case 'solid': 16003 default: { 16004 $dash = 0; 16005 break; 16006 } 16007 } 16008 return $dash; 16009 } 16010 16011 /** 16012 * Returns the border style array from CSS border properties 16013 * @param $cssborder (string) border properties 16014 * @return array containing border properties 16015 * @protected 16016 * @since 5.7.000 (2010-08-02) 16017 */ 16018 protected function getCSSBorderStyle($cssborder) { 16019 $bprop = preg_split('/[\s]+/', trim($cssborder)); 16020 $border = array(); // value to be returned 16021 switch (count($bprop)) { 16022 case 3: { 16023 $width = $bprop[0]; 16024 $style = $bprop[1]; 16025 $color = $bprop[2]; 16026 break; 16027 } 16028 case 2: { 16029 $width = 'medium'; 16030 $style = $bprop[0]; 16031 $color = $bprop[1]; 16032 break; 16033 } 16034 case 1: { 16035 $width = 'medium'; 16036 $style = $bprop[0]; 16037 $color = 'black'; 16038 break; 16039 } 16040 default: { 16041 $width = 'medium'; 16042 $style = 'solid'; 16043 $color = 'black'; 16044 break; 16045 } 16046 } 16047 if ($style == 'none') { 16048 return array(); 16049 } 16050 $border['cap'] = 'square'; 16051 $border['join'] = 'miter'; 16052 $border['dash'] = $this->getCSSBorderDashStyle($style); 16053 if ($border['dash'] < 0) { 16054 return array(); 16055 } 16056 $border['width'] = $this->getCSSBorderWidth($width); 16057 $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors); 16058 return $border; 16059 } 16060 16061 /** 16062 * Get the internal Cell padding from CSS attribute. 16063 * @param $csspadding (string) padding properties 16064 * @param $width (float) width of the containing element 16065 * @return array of cell paddings 16066 * @public 16067 * @since 5.9.000 (2010-10-04) 16068 */ 16069 public function getCSSPadding($csspadding, $width=0) { 16070 $padding = preg_split('/[\s]+/', trim($csspadding)); 16071 $cell_padding = array(); // value to be returned 16072 switch (count($padding)) { 16073 case 4: { 16074 $cell_padding['T'] = $padding[0]; 16075 $cell_padding['R'] = $padding[1]; 16076 $cell_padding['B'] = $padding[2]; 16077 $cell_padding['L'] = $padding[3]; 16078 break; 16079 } 16080 case 3: { 16081 $cell_padding['T'] = $padding[0]; 16082 $cell_padding['R'] = $padding[1]; 16083 $cell_padding['B'] = $padding[2]; 16084 $cell_padding['L'] = $padding[1]; 16085 break; 16086 } 16087 case 2: { 16088 $cell_padding['T'] = $padding[0]; 16089 $cell_padding['R'] = $padding[1]; 16090 $cell_padding['B'] = $padding[0]; 16091 $cell_padding['L'] = $padding[1]; 16092 break; 16093 } 16094 case 1: { 16095 $cell_padding['T'] = $padding[0]; 16096 $cell_padding['R'] = $padding[0]; 16097 $cell_padding['B'] = $padding[0]; 16098 $cell_padding['L'] = $padding[0]; 16099 break; 16100 } 16101 default: { 16102 return $this->cell_padding; 16103 } 16104 } 16105 if ($width == 0) { 16106 $width = $this->w - $this->lMargin - $this->rMargin; 16107 } 16108 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false); 16109 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false); 16110 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false); 16111 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false); 16112 return $cell_padding; 16113 } 16114 16115 /** 16116 * Get the internal Cell margin from CSS attribute. 16117 * @param $cssmargin (string) margin properties 16118 * @param $width (float) width of the containing element 16119 * @return array of cell margins 16120 * @public 16121 * @since 5.9.000 (2010-10-04) 16122 */ 16123 public function getCSSMargin($cssmargin, $width=0) { 16124 $margin = preg_split('/[\s]+/', trim($cssmargin)); 16125 $cell_margin = array(); // value to be returned 16126 switch (count($margin)) { 16127 case 4: { 16128 $cell_margin['T'] = $margin[0]; 16129 $cell_margin['R'] = $margin[1]; 16130 $cell_margin['B'] = $margin[2]; 16131 $cell_margin['L'] = $margin[3]; 16132 break; 16133 } 16134 case 3: { 16135 $cell_margin['T'] = $margin[0]; 16136 $cell_margin['R'] = $margin[1]; 16137 $cell_margin['B'] = $margin[2]; 16138 $cell_margin['L'] = $margin[1]; 16139 break; 16140 } 16141 case 2: { 16142 $cell_margin['T'] = $margin[0]; 16143 $cell_margin['R'] = $margin[1]; 16144 $cell_margin['B'] = $margin[0]; 16145 $cell_margin['L'] = $margin[1]; 16146 break; 16147 } 16148 case 1: { 16149 $cell_margin['T'] = $margin[0]; 16150 $cell_margin['R'] = $margin[0]; 16151 $cell_margin['B'] = $margin[0]; 16152 $cell_margin['L'] = $margin[0]; 16153 break; 16154 } 16155 default: { 16156 return $this->cell_margin; 16157 } 16158 } 16159 if ($width == 0) { 16160 $width = $this->w - $this->lMargin - $this->rMargin; 16161 } 16162 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false); 16163 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false); 16164 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false); 16165 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false); 16166 return $cell_margin; 16167 } 16168 16169 /** 16170 * Get the border-spacing from CSS attribute. 16171 * @param $cssbspace (string) border-spacing CSS properties 16172 * @param $width (float) width of the containing element 16173 * @return array of border spacings 16174 * @public 16175 * @since 5.9.010 (2010-10-27) 16176 */ 16177 public function getCSSBorderMargin($cssbspace, $width=0) { 16178 $space = preg_split('/[\s]+/', trim($cssbspace)); 16179 $border_spacing = array(); // value to be returned 16180 switch (count($space)) { 16181 case 2: { 16182 $border_spacing['H'] = $space[0]; 16183 $border_spacing['V'] = $space[1]; 16184 break; 16185 } 16186 case 1: { 16187 $border_spacing['H'] = $space[0]; 16188 $border_spacing['V'] = $space[0]; 16189 break; 16190 } 16191 default: { 16192 return array('H' => 0, 'V' => 0); 16193 } 16194 } 16195 if ($width == 0) { 16196 $width = $this->w - $this->lMargin - $this->rMargin; 16197 } 16198 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false); 16199 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false); 16200 return $border_spacing; 16201 } 16202 16203 /** 16204 * Returns the letter-spacing value from CSS value 16205 * @param $spacing (string) letter-spacing value 16206 * @param $parent (float) font spacing (tracking) value of the parent element 16207 * @return float quantity to increases or decreases the space between characters in a text. 16208 * @protected 16209 * @since 5.9.000 (2010-10-02) 16210 */ 16211 protected function getCSSFontSpacing($spacing, $parent=0) { 16212 $val = 0; // value to be returned 16213 $spacing = trim($spacing); 16214 switch ($spacing) { 16215 case 'normal': { 16216 $val = 0; 16217 break; 16218 } 16219 case 'inherit': { 16220 if ($parent == 'normal') { 16221 $val = 0; 16222 } else { 16223 $val = $parent; 16224 } 16225 break; 16226 } 16227 default: { 16228 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false); 16229 } 16230 } 16231 return $val; 16232 } 16233 16234 /** 16235 * Returns the percentage of font stretching from CSS value 16236 * @param $stretch (string) stretch mode 16237 * @param $parent (float) stretch value of the parent element 16238 * @return float font stretching percentage 16239 * @protected 16240 * @since 5.9.000 (2010-10-02) 16241 */ 16242 protected function getCSSFontStretching($stretch, $parent=100) { 16243 $val = 100; // value to be returned 16244 $stretch = trim($stretch); 16245 switch ($stretch) { 16246 case 'ultra-condensed': { 16247 $val = 40; 16248 break; 16249 } 16250 case 'extra-condensed': { 16251 $val = 55; 16252 break; 16253 } 16254 case 'condensed': { 16255 $val = 70; 16256 break; 16257 } 16258 case 'semi-condensed': { 16259 $val = 85; 16260 break; 16261 } 16262 case 'normal': { 16263 $val = 100; 16264 break; 16265 } 16266 case 'semi-expanded': { 16267 $val = 115; 16268 break; 16269 } 16270 case 'expanded': { 16271 $val = 130; 16272 break; 16273 } 16274 case 'extra-expanded': { 16275 $val = 145; 16276 break; 16277 } 16278 case 'ultra-expanded': { 16279 $val = 160; 16280 break; 16281 } 16282 case 'wider': { 16283 $val = ($parent + 10); 16284 break; 16285 } 16286 case 'narrower': { 16287 $val = ($parent - 10); 16288 break; 16289 } 16290 case 'inherit': { 16291 if ($parent == 'normal') { 16292 $val = 100; 16293 } else { 16294 $val = $parent; 16295 } 16296 break; 16297 } 16298 default: { 16299 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false); 16300 } 16301 } 16302 return $val; 16303 } 16304 16305 /** 16306 * Convert HTML string containing font size value to points 16307 * @param $val (string) String containing font size value and unit. 16308 * @param $refsize (float) Reference font size in points. 16309 * @param $parent_size (float) Parent font size in points. 16310 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). 16311 * @return float value in points 16312 * @public 16313 */ 16314 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') { 16315 $refsize = TCPDF_FONTS::getFontRefSize($refsize); 16316 $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize); 16317 switch ($val) { 16318 case 'xx-small': { 16319 $size = ($refsize - 4); 16320 break; 16321 } 16322 case 'x-small': { 16323 $size = ($refsize - 3); 16324 break; 16325 } 16326 case 'small': { 16327 $size = ($refsize - 2); 16328 break; 16329 } 16330 case 'medium': { 16331 $size = $refsize; 16332 break; 16333 } 16334 case 'large': { 16335 $size = ($refsize + 2); 16336 break; 16337 } 16338 case 'x-large': { 16339 $size = ($refsize + 4); 16340 break; 16341 } 16342 case 'xx-large': { 16343 $size = ($refsize + 6); 16344 break; 16345 } 16346 case 'smaller': { 16347 $size = ($parent_size - 3); 16348 break; 16349 } 16350 case 'larger': { 16351 $size = ($parent_size + 3); 16352 break; 16353 } 16354 default: { 16355 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true); 16356 } 16357 } 16358 return $size; 16359 } 16360 16361 /** 16362 * Returns the HTML DOM array. 16363 * @param $html (string) html code 16364 * @return array 16365 * @protected 16366 * @since 3.2.000 (2008-06-20) 16367 */ 16368 protected function getHtmlDomArray($html) { 16369 // array of CSS styles ( selector => properties). 16370 $css = array(); 16371 // get CSS array defined at previous call 16372 $matches = array(); 16373 if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) { 16374 if (isset($matches[1][0])) { 16375 $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true)); 16376 } 16377 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html); 16378 } 16379 // extract external CSS files 16380 $matches = array(); 16381 if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) { 16382 foreach ($matches[1] as $key => $link) { 16383 $type = array(); 16384 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) { 16385 $type = array(); 16386 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type); 16387 // get 'all' and 'print' media, other media types are discarded 16388 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 16389 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { 16390 $type = array(); 16391 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) { 16392 // read CSS data file 16393 $cssdata = TCPDF_STATIC::fileGetContents(trim($type[1])); 16394 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) { 16395 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata)); 16396 } 16397 } 16398 } 16399 } 16400 } 16401 } 16402 // extract style tags 16403 $matches = array(); 16404 if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) { 16405 foreach ($matches[1] as $key => $media) { 16406 $type = array(); 16407 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type); 16408 // get 'all' and 'print' media, other media types are discarded 16409 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 16410 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { 16411 $cssdata = $matches[2][$key]; 16412 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata)); 16413 } 16414 } 16415 } 16416 // create a special tag to contain the CSS array (used for table content) 16417 $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>'; 16418 // remove head and style blocks 16419 $html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html); 16420 $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html); 16421 // define block tags 16422 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td'); 16423 // define self-closing tags 16424 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta'); 16425 // remove all unsupported tags (the line below lists all supported tags) 16426 $html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>'); 16427 //replace some blank characters 16428 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag 16429 $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html); 16430 $html = preg_replace('@(\r\n|\r)@', "\n", $html); 16431 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); 16432 $html = strtr($html, $repTable); 16433 $offset = 0; 16434 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) { 16435 $html_a = substr($html, 0, $offset); 16436 $html_b = substr($html, $offset, ($pos - $offset + 6)); 16437 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) { 16438 // preserve newlines on <pre> tag 16439 $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b); 16440 } 16441 while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) { 16442 // preserve spaces on <pre> tag 16443 $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2 \\3</pre>", $html_b); 16444 } 16445 $html = $html_a.$html_b.substr($html, $pos + 6); 16446 $offset = strlen($html_a.$html_b); 16447 } 16448 $offset = 0; 16449 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) { 16450 $html_a = substr($html, 0, $offset); 16451 $html_b = substr($html, $offset, ($pos - $offset + 11)); 16452 while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) { 16453 // preserve newlines on <textarea> tag 16454 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b); 16455 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b); 16456 } 16457 $html = $html_a.$html_b.substr($html, $pos + 11); 16458 $offset = strlen($html_a.$html_b); 16459 } 16460 $html = preg_replace('/([\s]*)<option/si', '<option', $html); 16461 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html); 16462 $offset = 0; 16463 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) { 16464 $html_a = substr($html, 0, $offset); 16465 $html_b = substr($html, $offset, ($pos - $offset + 9)); 16466 while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) { 16467 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b); 16468 $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b); 16469 } 16470 $html = $html_a.$html_b.substr($html, $pos + 9); 16471 $offset = strlen($html_a.$html_b); 16472 } 16473 if (preg_match("'</select'si", $html)) { 16474 $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html); 16475 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html); 16476 } 16477 $html = str_replace("\n", ' ', $html); 16478 // restore textarea newlines 16479 $html = str_replace('<TBR>', "\n", $html); 16480 // remove extra spaces from code 16481 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html); 16482 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html); 16483 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html); 16484 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html); 16485 $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html); 16486 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html); 16487 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html); 16488 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html); 16489 $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1> \\2', $html); 16490 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html); 16491 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag 16492 $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html); 16493 $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1> </li>', $html); 16494 $html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1"> </font><img', $html); 16495 $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1> ', $html); // preserve some spaces 16496 $html = preg_replace('/[\s]<\/([^\>]*)>/', ' </\\1>', $html); // preserve some spaces 16497 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment 16498 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment 16499 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space 16500 // trim string 16501 $html = $this->stringTrim($html); 16502 // fix br tag after li 16503 $html = preg_replace('/<li><br([^\>]*)>/', '<li> <br\\1>', $html); 16504 // fix first image tag alignment 16505 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1); 16506 // pattern for generic tag 16507 $tagpattern = '/(<[^>]+>)/'; 16508 // explodes the string 16509 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 16510 // count elements 16511 $maxel = count($a); 16512 $elkey = 0; 16513 $key = 0; 16514 // create an array of elements 16515 $dom = array(); 16516 $dom[$key] = array(); 16517 // set inheritable properties fot the first void element 16518 // possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing 16519 $dom[$key]['tag'] = false; 16520 $dom[$key]['block'] = false; 16521 $dom[$key]['value'] = ''; 16522 $dom[$key]['parent'] = 0; 16523 $dom[$key]['hide'] = false; 16524 $dom[$key]['fontname'] = $this->FontFamily; 16525 $dom[$key]['fontstyle'] = $this->FontStyle; 16526 $dom[$key]['fontsize'] = $this->FontSizePt; 16527 $dom[$key]['font-stretch'] = $this->font_stretching; 16528 $dom[$key]['letter-spacing'] = $this->font_spacing; 16529 $dom[$key]['stroke'] = $this->textstrokewidth; 16530 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0); 16531 $dom[$key]['clip'] = ($this->textrendermode > 3); 16532 $dom[$key]['line-height'] = $this->cell_height_ratio; 16533 $dom[$key]['bgcolor'] = false; 16534 $dom[$key]['fgcolor'] = $this->fgcolor; // color 16535 $dom[$key]['strokecolor'] = $this->strokecolor; 16536 $dom[$key]['align'] = ''; 16537 $dom[$key]['listtype'] = ''; 16538 $dom[$key]['text-indent'] = 0; 16539 $dom[$key]['text-transform'] = ''; 16540 $dom[$key]['border'] = array(); 16541 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr'; 16542 $thead = false; // true when we are inside the THEAD tag 16543 ++$key; 16544 $level = array(); 16545 array_push($level, 0); // root 16546 while ($elkey < $maxel) { 16547 $dom[$key] = array(); 16548 $element = $a[$elkey]; 16549 $dom[$key]['elkey'] = $elkey; 16550 if (preg_match($tagpattern, $element)) { 16551 // html tag 16552 $element = substr($element, 1, -1); 16553 // get tag name 16554 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); 16555 $tagname = strtolower($tag[1]); 16556 // check if we are inside a table header 16557 if ($tagname == 'thead') { 16558 if ($element[0] == '/') { 16559 $thead = false; 16560 } else { 16561 $thead = true; 16562 } 16563 ++$elkey; 16564 continue; 16565 } 16566 $dom[$key]['tag'] = true; 16567 $dom[$key]['value'] = $tagname; 16568 if (in_array($dom[$key]['value'], $blocktags)) { 16569 $dom[$key]['block'] = true; 16570 } else { 16571 $dom[$key]['block'] = false; 16572 } 16573 if ($element[0] == '/') { 16574 // *** closing html tag 16575 $dom[$key]['opening'] = false; 16576 $dom[$key]['parent'] = end($level); 16577 array_pop($level); 16578 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide']; 16579 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname']; 16580 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle']; 16581 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize']; 16582 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch']; 16583 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing']; 16584 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke']; 16585 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill']; 16586 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip']; 16587 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height']; 16588 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor']; 16589 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor']; 16590 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor']; 16591 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align']; 16592 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform']; 16593 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir']; 16594 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) { 16595 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype']; 16596 } 16597 // set the number of columns in table tag 16598 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) { 16599 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols']; 16600 } 16601 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 16602 $dom[($dom[$key]['parent'])]['content'] = $csstagarray; 16603 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) { 16604 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']]; 16605 } 16606 $key = $i; 16607 // mark nested tables 16608 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']); 16609 // remove thead sections from nested tables 16610 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']); 16611 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']); 16612 } 16613 // store header rows on a new table 16614 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) { 16615 if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) { 16616 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']]; 16617 } 16618 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) { 16619 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']]; 16620 } 16621 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) { 16622 $dom[($dom[$key]['parent'])]['attribute'] = array(); 16623 } 16624 // header elements must be always contained in a single page 16625 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true'; 16626 } 16627 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) { 16628 // remove the nobr attributes from the table header 16629 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']); 16630 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>'; 16631 } 16632 } else { 16633 // *** opening or self-closing html tag 16634 $dom[$key]['opening'] = true; 16635 $dom[$key]['parent'] = end($level); 16636 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) { 16637 // self-closing tag 16638 $dom[$key]['self'] = true; 16639 } else { 16640 // opening tag 16641 array_push($level, $key); 16642 $dom[$key]['self'] = false; 16643 } 16644 // copy some values from parent 16645 $parentkey = 0; 16646 if ($key > 0) { 16647 $parentkey = $dom[$key]['parent']; 16648 $dom[$key]['hide'] = $dom[$parentkey]['hide']; 16649 $dom[$key]['fontname'] = $dom[$parentkey]['fontname']; 16650 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle']; 16651 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize']; 16652 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch']; 16653 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing']; 16654 $dom[$key]['stroke'] = $dom[$parentkey]['stroke']; 16655 $dom[$key]['fill'] = $dom[$parentkey]['fill']; 16656 $dom[$key]['clip'] = $dom[$parentkey]['clip']; 16657 $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; 16658 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor']; 16659 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor']; 16660 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor']; 16661 $dom[$key]['align'] = $dom[$parentkey]['align']; 16662 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 16663 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 16664 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform']; 16665 $dom[$key]['border'] = array(); 16666 $dom[$key]['dir'] = $dom[$parentkey]['dir']; 16667 } 16668 // get attributes 16669 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER); 16670 $dom[$key]['attribute'] = array(); // reset attribute array 16671 while (list($id, $name) = each($attr_array[1])) { 16672 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; 16673 } 16674 if (!empty($css)) { 16675 // merge CSS style to current style 16676 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css); 16677 $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']); 16678 } 16679 // split style attributes 16680 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) { 16681 // get style attributes 16682 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); 16683 $dom[$key]['style'] = array(); // reset style attribute array 16684 while (list($id, $name) = each($style_array[1])) { 16685 // in case of duplicate attribute the last replace the previous 16686 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); 16687 } 16688 // --- get some style attributes --- 16689 // text direction 16690 if (isset($dom[$key]['style']['direction'])) { 16691 $dom[$key]['dir'] = $dom[$key]['style']['direction']; 16692 } 16693 // display 16694 if (isset($dom[$key]['style']['display'])) { 16695 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none'); 16696 } 16697 // font family 16698 if (isset($dom[$key]['style']['font-family'])) { 16699 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']); 16700 } 16701 // list-style-type 16702 if (isset($dom[$key]['style']['list-style-type'])) { 16703 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type'])); 16704 if ($dom[$key]['listtype'] == 'inherit') { 16705 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 16706 } 16707 } 16708 // text-indent 16709 if (isset($dom[$key]['style']['text-indent'])) { 16710 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']); 16711 if ($dom[$key]['text-indent'] == 'inherit') { 16712 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 16713 } 16714 } 16715 // text-transform 16716 if (isset($dom[$key]['style']['text-transform'])) { 16717 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform']; 16718 } 16719 // font size 16720 if (isset($dom[$key]['style']['font-size'])) { 16721 $fsize = trim($dom[$key]['style']['font-size']); 16722 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt'); 16723 } 16724 // font-stretch 16725 if (isset($dom[$key]['style']['font-stretch'])) { 16726 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']); 16727 } 16728 // letter-spacing 16729 if (isset($dom[$key]['style']['letter-spacing'])) { 16730 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']); 16731 } 16732 // line-height (internally is the cell height ratio) 16733 if (isset($dom[$key]['style']['line-height'])) { 16734 $lineheight = trim($dom[$key]['style']['line-height']); 16735 switch ($lineheight) { 16736 // A normal line height. This is default 16737 case 'normal': { 16738 $dom[$key]['line-height'] = $dom[0]['line-height']; 16739 break; 16740 } 16741 case 'inherit': { 16742 $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; 16743 } 16744 default: { 16745 if (is_numeric($lineheight)) { 16746 // convert to percentage of font height 16747 $lineheight = ($lineheight * 100).'%'; 16748 } 16749 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true); 16750 if (substr($lineheight, -1) !== '%') { 16751 if ($dom[$key]['fontsize'] <= 0) { 16752 $dom[$key]['line-height'] = 1; 16753 } else { 16754 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']); 16755 } 16756 } 16757 } 16758 } 16759 } 16760 // font style 16761 if (isset($dom[$key]['style']['font-weight'])) { 16762 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') { 16763 if (strpos($dom[$key]['fontstyle'], 'B') !== false) { 16764 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']); 16765 } 16766 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') { 16767 $dom[$key]['fontstyle'] .= 'B'; 16768 } 16769 } 16770 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) { 16771 $dom[$key]['fontstyle'] .= 'I'; 16772 } 16773 // font color 16774 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) { 16775 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors); 16776 } elseif ($dom[$key]['value'] == 'a') { 16777 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; 16778 } 16779 // background color 16780 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) { 16781 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors); 16782 } 16783 // text-decoration 16784 if (isset($dom[$key]['style']['text-decoration'])) { 16785 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration'])); 16786 foreach ($decors as $dec) { 16787 $dec = trim($dec); 16788 if (!TCPDF_STATIC::empty_string($dec)) { 16789 if ($dec[0] == 'u') { 16790 // underline 16791 $dom[$key]['fontstyle'] .= 'U'; 16792 } elseif ($dec[0] == 'l') { 16793 // line-through 16794 $dom[$key]['fontstyle'] .= 'D'; 16795 } elseif ($dec[0] == 'o') { 16796 // overline 16797 $dom[$key]['fontstyle'] .= 'O'; 16798 } 16799 } 16800 } 16801 } elseif ($dom[$key]['value'] == 'a') { 16802 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; 16803 } 16804 // check for width attribute 16805 if (isset($dom[$key]['style']['width'])) { 16806 $dom[$key]['width'] = $dom[$key]['style']['width']; 16807 } 16808 // check for height attribute 16809 if (isset($dom[$key]['style']['height'])) { 16810 $dom[$key]['height'] = $dom[$key]['style']['height']; 16811 } 16812 // check for text alignment 16813 if (isset($dom[$key]['style']['text-align'])) { 16814 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]); 16815 } 16816 // check for CSS border properties 16817 if (isset($dom[$key]['style']['border'])) { 16818 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']); 16819 if (!empty($borderstyle)) { 16820 $dom[$key]['border']['LTRB'] = $borderstyle; 16821 } 16822 } 16823 if (isset($dom[$key]['style']['border-color'])) { 16824 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color'])); 16825 if (isset($brd_colors[3])) { 16826 $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors); 16827 } 16828 if (isset($brd_colors[1])) { 16829 $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors); 16830 } 16831 if (isset($brd_colors[0])) { 16832 $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors); 16833 } 16834 if (isset($brd_colors[2])) { 16835 $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors); 16836 } 16837 } 16838 if (isset($dom[$key]['style']['border-width'])) { 16839 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width'])); 16840 if (isset($brd_widths[3])) { 16841 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]); 16842 } 16843 if (isset($brd_widths[1])) { 16844 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]); 16845 } 16846 if (isset($brd_widths[0])) { 16847 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]); 16848 } 16849 if (isset($brd_widths[2])) { 16850 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]); 16851 } 16852 } 16853 if (isset($dom[$key]['style']['border-style'])) { 16854 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style'])); 16855 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) { 16856 $dom[$key]['border']['L']['cap'] = 'square'; 16857 $dom[$key]['border']['L']['join'] = 'miter'; 16858 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]); 16859 if ($dom[$key]['border']['L']['dash'] < 0) { 16860 $dom[$key]['border']['L'] = array(); 16861 } 16862 } 16863 if (isset($brd_styles[1])) { 16864 $dom[$key]['border']['R']['cap'] = 'square'; 16865 $dom[$key]['border']['R']['join'] = 'miter'; 16866 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]); 16867 if ($dom[$key]['border']['R']['dash'] < 0) { 16868 $dom[$key]['border']['R'] = array(); 16869 } 16870 } 16871 if (isset($brd_styles[0])) { 16872 $dom[$key]['border']['T']['cap'] = 'square'; 16873 $dom[$key]['border']['T']['join'] = 'miter'; 16874 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]); 16875 if ($dom[$key]['border']['T']['dash'] < 0) { 16876 $dom[$key]['border']['T'] = array(); 16877 } 16878 } 16879 if (isset($brd_styles[2])) { 16880 $dom[$key]['border']['B']['cap'] = 'square'; 16881 $dom[$key]['border']['B']['join'] = 'miter'; 16882 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]); 16883 if ($dom[$key]['border']['B']['dash'] < 0) { 16884 $dom[$key]['border']['B'] = array(); 16885 } 16886 } 16887 } 16888 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom'); 16889 foreach ($cellside as $bsk => $bsv) { 16890 if (isset($dom[$key]['style']['border-'.$bsv])) { 16891 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]); 16892 if (!empty($borderstyle)) { 16893 $dom[$key]['border'][$bsk] = $borderstyle; 16894 } 16895 } 16896 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) { 16897 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors); 16898 } 16899 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) { 16900 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']); 16901 } 16902 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) { 16903 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']); 16904 if ($dom[$key]['border'][$bsk]['dash'] < 0) { 16905 $dom[$key]['border'][$bsk] = array(); 16906 } 16907 } 16908 } 16909 // check for CSS padding properties 16910 if (isset($dom[$key]['style']['padding'])) { 16911 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']); 16912 } else { 16913 $dom[$key]['padding'] = $this->cell_padding; 16914 } 16915 foreach ($cellside as $psk => $psv) { 16916 if (isset($dom[$key]['style']['padding-'.$psv])) { 16917 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false); 16918 } 16919 } 16920 // check for CSS margin properties 16921 if (isset($dom[$key]['style']['margin'])) { 16922 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']); 16923 } else { 16924 $dom[$key]['margin'] = $this->cell_margin; 16925 } 16926 foreach ($cellside as $psk => $psv) { 16927 if (isset($dom[$key]['style']['margin-'.$psv])) { 16928 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false); 16929 } 16930 } 16931 // check for CSS border-spacing properties 16932 if (isset($dom[$key]['style']['border-spacing'])) { 16933 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']); 16934 } 16935 // page-break-inside 16936 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) { 16937 $dom[$key]['attribute']['nobr'] = 'true'; 16938 } 16939 // page-break-before 16940 if (isset($dom[$key]['style']['page-break-before'])) { 16941 if ($dom[$key]['style']['page-break-before'] == 'always') { 16942 $dom[$key]['attribute']['pagebreak'] = 'true'; 16943 } elseif ($dom[$key]['style']['page-break-before'] == 'left') { 16944 $dom[$key]['attribute']['pagebreak'] = 'left'; 16945 } elseif ($dom[$key]['style']['page-break-before'] == 'right') { 16946 $dom[$key]['attribute']['pagebreak'] = 'right'; 16947 } 16948 } 16949 // page-break-after 16950 if (isset($dom[$key]['style']['page-break-after'])) { 16951 if ($dom[$key]['style']['page-break-after'] == 'always') { 16952 $dom[$key]['attribute']['pagebreakafter'] = 'true'; 16953 } elseif ($dom[$key]['style']['page-break-after'] == 'left') { 16954 $dom[$key]['attribute']['pagebreakafter'] = 'left'; 16955 } elseif ($dom[$key]['style']['page-break-after'] == 'right') { 16956 $dom[$key]['attribute']['pagebreakafter'] = 'right'; 16957 } 16958 } 16959 } 16960 if (isset($dom[$key]['attribute']['display'])) { 16961 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none'); 16962 } 16963 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) { 16964 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black'); 16965 if (!empty($borderstyle)) { 16966 $dom[$key]['border']['LTRB'] = $borderstyle; 16967 } 16968 } 16969 // check for font tag 16970 if ($dom[$key]['value'] == 'font') { 16971 // font family 16972 if (isset($dom[$key]['attribute']['face'])) { 16973 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']); 16974 } 16975 // font size 16976 if (isset($dom[$key]['attribute']['size'])) { 16977 if ($key > 0) { 16978 if ($dom[$key]['attribute']['size'][0] == '+') { 16979 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1)); 16980 } elseif ($dom[$key]['attribute']['size'][0] == '-') { 16981 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1)); 16982 } else { 16983 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 16984 } 16985 } else { 16986 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 16987 } 16988 } 16989 } 16990 // force natural alignment for lists 16991 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) 16992 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) { 16993 if ($this->rtl) { 16994 $dom[$key]['align'] = 'R'; 16995 } else { 16996 $dom[$key]['align'] = 'L'; 16997 } 16998 } 16999 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) { 17000 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { 17001 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO; 17002 } 17003 } 17004 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) { 17005 $dom[$key]['fontstyle'] .= 'B'; 17006 } 17007 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) { 17008 $dom[$key]['fontstyle'] .= 'I'; 17009 } 17010 if ($dom[$key]['value'] == 'u') { 17011 $dom[$key]['fontstyle'] .= 'U'; 17012 } 17013 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) { 17014 $dom[$key]['fontstyle'] .= 'D'; 17015 } 17016 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) { 17017 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; 17018 } 17019 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) { 17020 $dom[$key]['fontname'] = $this->default_monospaced_font; 17021 } 17022 if (!empty($dom[$key]['value']) AND ($dom[$key]['value'][0] == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) { 17023 // headings h1, h2, h3, h4, h5, h6 17024 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { 17025 $headsize = (4 - intval($dom[$key]['value']{1})) * 2; 17026 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize; 17027 } 17028 if (!isset($dom[$key]['style']['font-weight'])) { 17029 $dom[$key]['fontstyle'] .= 'B'; 17030 } 17031 } 17032 if (($dom[$key]['value'] == 'table')) { 17033 $dom[$key]['rows'] = 0; // number of rows 17034 $dom[$key]['trids'] = array(); // IDs of TR elements 17035 $dom[$key]['thead'] = ''; // table header rows 17036 } 17037 if (($dom[$key]['value'] == 'tr')) { 17038 $dom[$key]['cols'] = 0; 17039 if ($thead) { 17040 $dom[$key]['thead'] = true; 17041 // rows on thead block are printed as a separate table 17042 } else { 17043 $dom[$key]['thead'] = false; 17044 // store the number of rows on table element 17045 ++$dom[($dom[$key]['parent'])]['rows']; 17046 // store the TR elements IDs on table element 17047 array_push($dom[($dom[$key]['parent'])]['trids'], $key); 17048 } 17049 } 17050 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) { 17051 if (isset($dom[$key]['attribute']['colspan'])) { 17052 $colspan = intval($dom[$key]['attribute']['colspan']); 17053 } else { 17054 $colspan = 1; 17055 } 17056 $dom[$key]['attribute']['colspan'] = $colspan; 17057 $dom[($dom[$key]['parent'])]['cols'] += $colspan; 17058 } 17059 // text direction 17060 if (isset($dom[$key]['attribute']['dir'])) { 17061 $dom[$key]['dir'] = $dom[$key]['attribute']['dir']; 17062 } 17063 // set foreground color attribute 17064 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) { 17065 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors); 17066 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) { 17067 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; 17068 } 17069 // set background color attribute 17070 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) { 17071 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors); 17072 } 17073 // set stroke color attribute 17074 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) { 17075 $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors); 17076 } 17077 // check for width attribute 17078 if (isset($dom[$key]['attribute']['width'])) { 17079 $dom[$key]['width'] = $dom[$key]['attribute']['width']; 17080 } 17081 // check for height attribute 17082 if (isset($dom[$key]['attribute']['height'])) { 17083 $dom[$key]['height'] = $dom[$key]['attribute']['height']; 17084 } 17085 // check for text alignment 17086 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) { 17087 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]); 17088 } 17089 // check for text rendering mode (the following attributes do not exist in HTML) 17090 if (isset($dom[$key]['attribute']['stroke'])) { 17091 // font stroke width 17092 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true); 17093 } 17094 if (isset($dom[$key]['attribute']['fill'])) { 17095 // font fill 17096 if ($dom[$key]['attribute']['fill'] == 'true') { 17097 $dom[$key]['fill'] = true; 17098 } else { 17099 $dom[$key]['fill'] = false; 17100 } 17101 } 17102 if (isset($dom[$key]['attribute']['clip'])) { 17103 // clipping mode 17104 if ($dom[$key]['attribute']['clip'] == 'true') { 17105 $dom[$key]['clip'] = true; 17106 } else { 17107 $dom[$key]['clip'] = false; 17108 } 17109 } 17110 } // end opening tag 17111 } else { 17112 // text 17113 $dom[$key]['tag'] = false; 17114 $dom[$key]['block'] = false; 17115 $dom[$key]['parent'] = end($level); 17116 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir']; 17117 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) { 17118 // text-transform for unicode requires mb_convert_case (Multibyte String Functions) 17119 if (function_exists('mb_convert_case')) { 17120 $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER); 17121 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) { 17122 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding); 17123 } 17124 } elseif (!$this->isunicode) { 17125 switch ($dom[$dom[$key]['parent']]['text-transform']) { 17126 case 'capitalize': { 17127 $element = ucwords(strtolower($element)); 17128 break; 17129 } 17130 case 'uppercase': { 17131 $element = strtoupper($element); 17132 break; 17133 } 17134 case 'lowercase': { 17135 $element = strtolower($element); 17136 break; 17137 } 17138 } 17139 } 17140 } 17141 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element)); 17142 } 17143 ++$elkey; 17144 ++$key; 17145 } 17146 return $dom; 17147 } 17148 17149 /** 17150 * Returns the string used to find spaces 17151 * @return string 17152 * @protected 17153 * @author Nicola Asuni 17154 * @since 4.8.024 (2010-01-15) 17155 */ 17156 protected function getSpaceString() { 17157 $spacestr = chr(32); 17158 if ($this->isUnicodeFont()) { 17159 $spacestr = chr(0).chr(32); 17160 } 17161 return $spacestr; 17162 } 17163 17164 /** 17165 * Serialize an array of parameters to be used with TCPDF tag in HTML code. 17166 * @param $pararray (array) parameters array 17167 * @return sting containing serialized data 17168 * @since 4.9.006 (2010-04-02) 17169 * @public 17170 * @deprecated 17171 */ 17172 public function serializeTCPDFtagParameters($pararray) { 17173 return TCPDF_STATIC::serializeTCPDFtagParameters($pararray); 17174 } 17175 17176 /** 17177 * Prints a cell (rectangular area) with optional borders, background color and html text string. 17178 * 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 /> 17179 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 17180 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. 17181 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul 17182 * NOTE: all the HTML attributes must be enclosed in double-quote. 17183 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 17184 * @param $h (float) Cell minimum height. The cell extends automatically if needed. 17185 * @param $x (float) upper-left corner X coordinate 17186 * @param $y (float) upper-left corner Y coordinate 17187 * @param $html (string) html text to print. Default value: empty string. 17188 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 17189 * @param $ln (int) 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> 17190 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 17191 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 17192 * @param $reseth (boolean) if true reset the last cell height (default true). 17193 * @param $align (string) 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> 17194 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width. 17195 * @see Multicell(), writeHTML() 17196 * @public 17197 */ 17198 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) { 17199 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false); 17200 } 17201 17202 /** 17203 * Allows to preserve some HTML formatting (limited support).<br /> 17204 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. 17205 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul 17206 * NOTE: all the HTML attributes must be enclosed in double-quote. 17207 * @param $html (string) text to display 17208 * @param $ln (boolean) if true add a new line after text (default = true) 17209 * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false). 17210 * @param $reseth (boolean) if true reset the last cell height (default false). 17211 * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false). 17212 * @param $align (string) 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> 17213 * @public 17214 */ 17215 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') { 17216 $gvars = $this->getGraphicVars(); 17217 // store current values 17218 $prev_cell_margin = $this->cell_margin; 17219 $prev_cell_padding = $this->cell_padding; 17220 $prevPage = $this->page; 17221 $prevlMargin = $this->lMargin; 17222 $prevrMargin = $this->rMargin; 17223 $curfontname = $this->FontFamily; 17224 $curfontstyle = $this->FontStyle; 17225 $curfontsize = $this->FontSizePt; 17226 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize); 17227 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize); 17228 $curfontstretcing = $this->font_stretching; 17229 $curfonttracking = $this->font_spacing; 17230 $this->newline = true; 17231 $newline = true; 17232 $startlinepage = $this->page; 17233 $minstartliney = $this->y; 17234 $maxbottomliney = 0; 17235 $startlinex = $this->x; 17236 $startliney = $this->y; 17237 $yshift = 0; 17238 $loop = 0; 17239 $curpos = 0; 17240 $this_method_vars = array(); 17241 $undo = false; 17242 $fontaligned = false; 17243 $reverse_dir = false; // true when the text direction is reversed 17244 $this->premode = false; 17245 if ($this->inxobj) { 17246 // we are inside an XObject template 17247 $pask = count($this->xobjects[$this->xobjid]['annotations']); 17248 } elseif (isset($this->PageAnnots[$this->page])) { 17249 $pask = count($this->PageAnnots[$this->page]); 17250 } else { 17251 $pask = 0; 17252 } 17253 if ($this->inxobj) { 17254 // we are inside an XObject template 17255 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); 17256 } elseif (!$this->InFooter) { 17257 if (isset($this->footerlen[$this->page])) { 17258 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 17259 } else { 17260 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 17261 } 17262 $startlinepos = $this->footerpos[$this->page]; 17263 } else { 17264 // we are inside the footer 17265 $startlinepos = $this->pagelen[$this->page]; 17266 } 17267 $lalign = $align; 17268 $plalign = $align; 17269 if ($this->rtl) { 17270 $w = $this->x - $this->lMargin; 17271 } else { 17272 $w = $this->w - $this->rMargin - $this->x; 17273 } 17274 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']); 17275 if ($cell) { 17276 if ($this->rtl) { 17277 $this->x -= $this->cell_padding['R']; 17278 $this->lMargin += $this->cell_padding['R']; 17279 } else { 17280 $this->x += $this->cell_padding['L']; 17281 $this->rMargin += $this->cell_padding['L']; 17282 } 17283 } 17284 if ($this->customlistindent >= 0) { 17285 $this->listindent = $this->customlistindent; 17286 } else { 17287 $this->listindent = $this->GetStringWidth('000000'); 17288 } 17289 $this->listindentlevel = 0; 17290 // save previous states 17291 $prev_cell_height_ratio = $this->cell_height_ratio; 17292 $prev_listnum = $this->listnum; 17293 $prev_listordered = $this->listordered; 17294 $prev_listcount = $this->listcount; 17295 $prev_lispacer = $this->lispacer; 17296 $this->listnum = 0; 17297 $this->listordered = array(); 17298 $this->listcount = array(); 17299 $this->lispacer = ''; 17300 if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) { 17301 // reset row height 17302 $this->resetLastH(); 17303 } 17304 $dom = $this->getHtmlDomArray($html); 17305 $maxel = count($dom); 17306 $key = 0; 17307 while ($key < $maxel) { 17308 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) { 17309 // store the node key 17310 $hidden_node_key = $key; 17311 if ($dom[$key]['self']) { 17312 // skip just this self-closing tag 17313 ++$key; 17314 } else { 17315 // skip this and all children tags 17316 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) { 17317 // skip hidden objects 17318 ++$key; 17319 } 17320 ++$key; 17321 } 17322 } 17323 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) { 17324 // check for pagebreak 17325 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) { 17326 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17327 $this->checkPageBreak($this->PageBreakTrigger + 1); 17328 $this->htmlvspace = ($this->PageBreakTrigger + 1); 17329 } 17330 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 17331 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 17332 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17333 $this->checkPageBreak($this->PageBreakTrigger + 1); 17334 $this->htmlvspace = ($this->PageBreakTrigger + 1); 17335 } 17336 } 17337 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) { 17338 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { 17339 $dom[$key]['attribute']['nobr'] = false; 17340 } else { 17341 // store current object 17342 $this->startTransaction(); 17343 // save this method vars 17344 $this_method_vars['html'] = $html; 17345 $this_method_vars['ln'] = $ln; 17346 $this_method_vars['fill'] = $fill; 17347 $this_method_vars['reseth'] = $reseth; 17348 $this_method_vars['cell'] = $cell; 17349 $this_method_vars['align'] = $align; 17350 $this_method_vars['gvars'] = $gvars; 17351 $this_method_vars['prevPage'] = $prevPage; 17352 $this_method_vars['prev_cell_margin'] = $prev_cell_margin; 17353 $this_method_vars['prev_cell_padding'] = $prev_cell_padding; 17354 $this_method_vars['prevlMargin'] = $prevlMargin; 17355 $this_method_vars['prevrMargin'] = $prevrMargin; 17356 $this_method_vars['curfontname'] = $curfontname; 17357 $this_method_vars['curfontstyle'] = $curfontstyle; 17358 $this_method_vars['curfontsize'] = $curfontsize; 17359 $this_method_vars['curfontascent'] = $curfontascent; 17360 $this_method_vars['curfontdescent'] = $curfontdescent; 17361 $this_method_vars['curfontstretcing'] = $curfontstretcing; 17362 $this_method_vars['curfonttracking'] = $curfonttracking; 17363 $this_method_vars['minstartliney'] = $minstartliney; 17364 $this_method_vars['maxbottomliney'] = $maxbottomliney; 17365 $this_method_vars['yshift'] = $yshift; 17366 $this_method_vars['startlinepage'] = $startlinepage; 17367 $this_method_vars['startlinepos'] = $startlinepos; 17368 $this_method_vars['startlinex'] = $startlinex; 17369 $this_method_vars['startliney'] = $startliney; 17370 $this_method_vars['newline'] = $newline; 17371 $this_method_vars['loop'] = $loop; 17372 $this_method_vars['curpos'] = $curpos; 17373 $this_method_vars['pask'] = $pask; 17374 $this_method_vars['lalign'] = $lalign; 17375 $this_method_vars['plalign'] = $plalign; 17376 $this_method_vars['w'] = $w; 17377 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio; 17378 $this_method_vars['prev_listnum'] = $prev_listnum; 17379 $this_method_vars['prev_listordered'] = $prev_listordered; 17380 $this_method_vars['prev_listcount'] = $prev_listcount; 17381 $this_method_vars['prev_lispacer'] = $prev_lispacer; 17382 $this_method_vars['fontaligned'] = $fontaligned; 17383 $this_method_vars['key'] = $key; 17384 $this_method_vars['dom'] = $dom; 17385 } 17386 } 17387 // print THEAD block 17388 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) { 17389 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) { 17390 $this->inthead = true; 17391 // print table header (thead) 17392 $this->writeHTML($this->thead, false, false, false, false, ''); 17393 // check if we are on a new page or on a new column 17394 if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) { 17395 // we are on a new page or on a new column and the total object height is less than the available vertical space. 17396 // restore previous object 17397 $this->rollbackTransaction(true); 17398 // restore previous values 17399 foreach ($this_method_vars as $vkey => $vval) { 17400 $$vkey = $vval; 17401 } 17402 // disable table header 17403 $tmp_thead = $this->thead; 17404 $this->thead = ''; 17405 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17406 $pre_y = $this->y; 17407 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 17408 // fix for multicolumn mode 17409 $startliney = $this->y; 17410 } 17411 $this->start_transaction_page = $this->page; 17412 $this->start_transaction_y = $this->y; 17413 // restore table header 17414 $this->thead = $tmp_thead; 17415 // fix table border properties 17416 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) { 17417 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px'); 17418 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) { 17419 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V']; 17420 } else { 17421 $tmp_cellspacing = 0; 17422 } 17423 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page; 17424 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column; 17425 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing; 17426 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']); 17427 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset; 17428 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset; 17429 // print table header (thead) 17430 $this->writeHTML($this->thead, false, false, false, false, ''); 17431 } 17432 } 17433 // move $key index forward to skip THEAD block 17434 while ( ($key < $maxel) AND (!( 17435 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead'])) 17436 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) { 17437 ++$key; 17438 } 17439 } 17440 if ($dom[$key]['tag'] OR ($key == 0)) { 17441 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) { 17442 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L'; 17443 } 17444 // vertically align image in line 17445 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) { 17446 // get image height 17447 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px'); 17448 $autolinebreak = false; 17449 if (!empty($dom[$key]['width'])) { 17450 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false); 17451 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R'])) 17452 AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L']))) 17453 OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) { 17454 // add automatic line break 17455 $autolinebreak = true; 17456 $this->Ln('', $cell); 17457 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) { 17458 // go back to evaluate this line break 17459 --$key; 17460 } 17461 } 17462 } 17463 if (!$autolinebreak) { 17464 if ($this->inPageBody()) { 17465 $pre_y = $this->y; 17466 // check for page break 17467 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) { 17468 // fix for multicolumn mode 17469 $startliney = $this->y; 17470 } 17471 } 17472 if ($this->page > $startlinepage) { 17473 // fix line splitted over two pages 17474 if (isset($this->footerlen[$startlinepage])) { 17475 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17476 } 17477 // line to be moved one page forward 17478 $pagebuff = $this->getPageBuffer($startlinepage); 17479 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 17480 $tstart = substr($pagebuff, 0, $startlinepos); 17481 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 17482 // remove line from previous page 17483 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 17484 $pagebuff = $this->getPageBuffer($this->page); 17485 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 17486 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 17487 // add line start to current page 17488 $yshift = ($minstartliney - $this->y); 17489 if ($fontaligned) { 17490 $yshift += ($curfontsize / $this->k); 17491 } 17492 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k)); 17493 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 17494 // shift the annotations and links 17495 if (isset($this->PageAnnots[$this->page])) { 17496 $next_pask = count($this->PageAnnots[$this->page]); 17497 } else { 17498 $next_pask = 0; 17499 } 17500 if (isset($this->PageAnnots[$startlinepage])) { 17501 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 17502 if ($pak >= $pask) { 17503 $this->PageAnnots[$this->page][] = $pac; 17504 unset($this->PageAnnots[$startlinepage][$pak]); 17505 $npak = count($this->PageAnnots[$this->page]) - 1; 17506 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 17507 } 17508 } 17509 } 17510 $pask = $next_pask; 17511 $startlinepos = $this->cntmrk[$this->page]; 17512 $startlinepage = $this->page; 17513 $startliney = $this->y; 17514 $this->newline = false; 17515 } 17516 $this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh); 17517 $minstartliney = min($this->y, $minstartliney); 17518 $maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k)); 17519 } 17520 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) { 17521 // account for different font size 17522 $pfontname = $curfontname; 17523 $pfontstyle = $curfontstyle; 17524 $pfontsize = $curfontsize; 17525 $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname); 17526 $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle); 17527 $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize); 17528 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize); 17529 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize); 17530 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize) 17531 OR ($this->cell_height_ratio != $dom[$key]['line-height']) 17532 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) { 17533 if (($key < ($maxel - 1)) AND ( 17534 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) 17535 OR ($this->cell_height_ratio != $dom[$key]['line-height']) 17536 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) 17537 AND ($fontsize >= 0) AND ($curfontsize >= 0) 17538 AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname))) 17539 )) { 17540 if ($this->page > $startlinepage) { 17541 // fix lines splitted over two pages 17542 if (isset($this->footerlen[$startlinepage])) { 17543 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17544 } 17545 // line to be moved one page forward 17546 $pagebuff = $this->getPageBuffer($startlinepage); 17547 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 17548 $tstart = substr($pagebuff, 0, $startlinepos); 17549 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 17550 // remove line start from previous page 17551 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 17552 $pagebuff = $this->getPageBuffer($this->page); 17553 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 17554 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 17555 // add line start to current page 17556 $yshift = ($minstartliney - $this->y); 17557 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k)); 17558 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 17559 // shift the annotations and links 17560 if (isset($this->PageAnnots[$this->page])) { 17561 $next_pask = count($this->PageAnnots[$this->page]); 17562 } else { 17563 $next_pask = 0; 17564 } 17565 if (isset($this->PageAnnots[$startlinepage])) { 17566 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 17567 if ($pak >= $pask) { 17568 $this->PageAnnots[$this->page][] = $pac; 17569 unset($this->PageAnnots[$startlinepage][$pak]); 17570 $npak = count($this->PageAnnots[$this->page]) - 1; 17571 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 17572 } 17573 } 17574 } 17575 $pask = $next_pask; 17576 $startlinepos = $this->cntmrk[$this->page]; 17577 $startlinepage = $this->page; 17578 $startliney = $this->y; 17579 } 17580 if (!isset($dom[$key]['line-height'])) { 17581 $dom[$key]['line-height'] = $this->cell_height_ratio; 17582 } 17583 if (!$dom[$key]['block']) { 17584 if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) { 17585 $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2; 17586 } 17587 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) { 17588 $current_line_align_data = array($key, $minstartliney, $maxbottomliney); 17589 if (isset($line_align_data) AND (($line_align_data[0] == ($key - 1)) OR (($line_align_data[0] == ($key - 2)) AND (isset($dom[($key - 1)])) AND (preg_match('/^([\s]+)$/', $dom[($key - 1)]['value']) > 0)))) { 17590 $minstartliney = min($this->y, $line_align_data[1]); 17591 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]); 17592 } else { 17593 $minstartliney = min($this->y, $minstartliney); 17594 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney); 17595 } 17596 $line_align_data = $current_line_align_data; 17597 } 17598 } 17599 $this->cell_height_ratio = $dom[$key]['line-height']; 17600 $fontaligned = true; 17601 } 17602 $this->SetFont($fontname, $fontstyle, $fontsize); 17603 // reset row height 17604 $this->resetLastH(); 17605 $curfontname = $fontname; 17606 $curfontstyle = $fontstyle; 17607 $curfontsize = $fontsize; 17608 $curfontascent = $fontascent; 17609 $curfontdescent = $fontdescent; 17610 } 17611 } 17612 // set text rendering mode 17613 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth; 17614 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0); 17615 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3); 17616 $this->setTextRenderingMode($textstroke, $textfill, $textclip); 17617 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) { 17618 $this->setFontStretching($dom[$key]['font-stretch']); 17619 } 17620 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) { 17621 $this->setFontSpacing($dom[$key]['letter-spacing']); 17622 } 17623 if (($plalign == 'J') AND $dom[$key]['block']) { 17624 $plalign = ''; 17625 } 17626 // get current position on page buffer 17627 $curpos = $this->pagelen[$startlinepage]; 17628 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) { 17629 $this->SetFillColorArray($dom[$key]['bgcolor']); 17630 $wfill = true; 17631 } else { 17632 $wfill = $fill | false; 17633 } 17634 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) { 17635 $this->SetTextColorArray($dom[$key]['fgcolor']); 17636 } 17637 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) { 17638 $this->SetDrawColorArray($dom[$key]['strokecolor']); 17639 } 17640 if (isset($dom[$key]['align'])) { 17641 $lalign = $dom[$key]['align']; 17642 } 17643 if (TCPDF_STATIC::empty_string($lalign)) { 17644 $lalign = $align; 17645 } 17646 } 17647 // align lines 17648 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) { 17649 $newline = true; 17650 $fontaligned = false; 17651 // we are at the beginning of a new line 17652 if (isset($startlinex)) { 17653 $yshift = ($minstartliney - $startliney); 17654 if (($yshift > 0) OR ($this->page > $startlinepage)) { 17655 $yshift = 0; 17656 } 17657 $t_x = 0; 17658 // the last line must be shifted to be aligned as requested 17659 $linew = abs($this->endlinex - $startlinex); 17660 if ($this->inxobj) { 17661 // we are inside an XObject template 17662 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); 17663 if (isset($opentagpos)) { 17664 $midpos = $opentagpos; 17665 } else { 17666 $midpos = 0; 17667 } 17668 if ($midpos > 0) { 17669 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); 17670 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); 17671 } else { 17672 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); 17673 $pend = ''; 17674 } 17675 } else { 17676 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 17677 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 17678 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17679 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 17680 } elseif (isset($opentagpos)) { 17681 $midpos = $opentagpos; 17682 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 17683 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17684 $midpos = $this->footerpos[$startlinepage]; 17685 } else { 17686 $midpos = 0; 17687 } 17688 if ($midpos > 0) { 17689 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 17690 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 17691 } else { 17692 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 17693 $pend = ''; 17694 } 17695 } 17696 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { 17697 // calculate shifting amount 17698 $tw = $w; 17699 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) { 17700 $tw += $this->cell_padding['R']; 17701 } 17702 if ($this->lMargin != $prevlMargin) { 17703 $tw += ($prevlMargin - $this->lMargin); 17704 } 17705 if ($this->rMargin != $prevrMargin) { 17706 $tw += ($prevrMargin - $this->rMargin); 17707 } 17708 $one_space_width = $this->GetStringWidth(chr(32)); 17709 $no = 0; // number of spaces on a line contained on a single block 17710 if ($this->isRTLTextDir()) { // RTL 17711 // remove left space if exist 17712 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[('); 17713 if ($pos1 > 0) { 17714 $pos1 = intval($pos1); 17715 if ($this->isUnicodeFont()) { 17716 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32))); 17717 $spacelen = 2; 17718 } else { 17719 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32))); 17720 $spacelen = 1; 17721 } 17722 if ($pos1 == $pos2) { 17723 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); 17724 if (substr($pmid, $pos1, 4) == '[()]') { 17725 $linew -= $one_space_width; 17726 } elseif ($pos1 == strpos($pmid, '[(')) { 17727 $no = 1; 17728 } 17729 } 17730 } 17731 } else { // LTR 17732 // remove right space if exist 17733 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]'); 17734 if ($pos1 > 0) { 17735 $pos1 = intval($pos1); 17736 if ($this->isUnicodeFont()) { 17737 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2; 17738 $spacelen = 2; 17739 } else { 17740 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1; 17741 $spacelen = 1; 17742 } 17743 if ($pos1 == $pos2) { 17744 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); 17745 $linew -= $one_space_width; 17746 } 17747 } 17748 } 17749 $mdiff = ($tw - $linew); 17750 if ($plalign == 'C') { 17751 if ($this->rtl) { 17752 $t_x = -($mdiff / 2); 17753 } else { 17754 $t_x = ($mdiff / 2); 17755 } 17756 } elseif ($plalign == 'R') { 17757 // right alignment on LTR document 17758 $t_x = $mdiff; 17759 } elseif ($plalign == 'L') { 17760 // left alignment on RTL document 17761 $t_x = -$mdiff; 17762 } elseif (($plalign == 'J') AND ($plalign == $lalign)) { 17763 // Justification 17764 if ($this->isRTLTextDir()) { 17765 // align text on the left 17766 $t_x = -$mdiff; 17767 } 17768 $ns = 0; // number of spaces 17769 $pmidtemp = $pmid; 17770 // escape special characters 17771 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 17772 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 17773 // search spaces 17774 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) { 17775 $spacestr = $this->getSpaceString(); 17776 $maxkk = count($lnstring[1]) - 1; 17777 for ($kk=0; $kk <= $maxkk; ++$kk) { 17778 // restore special characters 17779 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]); 17780 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]); 17781 // store number of spaces on the strings 17782 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr); 17783 // count total spaces on line 17784 $ns += $lnstring[2][$kk]; 17785 $lnstring[3][$kk] = $ns; 17786 } 17787 if ($ns == 0) { 17788 $ns = 1; 17789 } 17790 // calculate additional space to add to each existing space 17791 $spacewidth = ($mdiff / ($ns - $no)) * $this->k; 17792 if ($this->FontSize <= 0) { 17793 $this->FontSize = 1; 17794 } 17795 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize; 17796 if ($this->font_spacing != 0) { 17797 // fixed spacing mode 17798 $osw = -1000 * $this->font_spacing / $this->FontSize; 17799 $spacewidthu += $osw; 17800 } 17801 $nsmax = $ns; 17802 $ns = 0; 17803 reset($lnstring); 17804 $offset = 0; 17805 $strcount = 0; 17806 $prev_epsposbeg = 0; 17807 $textpos = 0; 17808 if ($this->isRTLTextDir()) { 17809 $textpos = $this->wPt; 17810 } 17811 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) { 17812 // check if we are inside a string section '[( ... )]' 17813 $stroffset = strpos($pmid, '[(', $offset); 17814 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) { 17815 // set offset to the end of string section 17816 $offset = strpos($pmid, ')]', $stroffset); 17817 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) { 17818 $offset = strpos($pmid, ')]', ($offset + 1)); 17819 } 17820 if ($offset === false) { 17821 $this->Error('HTML Justification: malformed PDF code.'); 17822 } 17823 continue; 17824 } 17825 if ($this->isRTLTextDir()) { 17826 $spacew = ($spacewidth * ($nsmax - $ns)); 17827 } else { 17828 $spacew = ($spacewidth * $ns); 17829 } 17830 $offset = $strpiece[2][1] + strlen($strpiece[2][0]); 17831 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset); 17832 if ($epsposend !== null) { 17833 $epsposend += strlen($this->epsmarker.'Q'); 17834 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); 17835 if ($epsposbeg === null) { 17836 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6)); 17837 $prev_epsposbeg = $epsposbeg; 17838 } 17839 if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) { 17840 // shift EPS images 17841 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew); 17842 $pmid_b = substr($pmid, 0, $epsposbeg); 17843 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg)); 17844 $pmid_e = substr($pmid, $epsposend); 17845 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e; 17846 $offset = $epsposend; 17847 continue; 17848 } 17849 } 17850 $currentxpos = 0; 17851 // shift blocks of code 17852 switch ($strpiece[2][0]) { 17853 case 'Td': 17854 case 'cm': 17855 case 'm': 17856 case 'l': { 17857 // get current X position 17858 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); 17859 if (!isset($xmatches[1])) { 17860 break; 17861 } 17862 $currentxpos = $xmatches[1]; 17863 $textpos = $currentxpos; 17864 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) { 17865 $ns = $lnstring[3][$strcount]; 17866 if ($this->isRTLTextDir()) { 17867 $spacew = ($spacewidth * ($nsmax - $ns)); 17868 } 17869 ++$strcount; 17870 } 17871 // justify block 17872 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) { 17873 $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4]; 17874 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17875 unset($pmatch, $newpmid); 17876 } 17877 break; 17878 } 17879 case 're': { 17880 // justify block 17881 if (!TCPDF_STATIC::empty_string($this->lispacer)) { 17882 $this->lispacer = ''; 17883 continue; 17884 } 17885 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches); 17886 if (!isset($xmatches[1])) { 17887 break; 17888 } 17889 $currentxpos = $xmatches[1]; 17890 $x_diff = 0; 17891 $w_diff = 0; 17892 if ($this->isRTLTextDir()) { // RTL 17893 if ($currentxpos < $textpos) { 17894 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount])); 17895 $w_diff = ($spacewidth * $lnstring[2][$strcount]); 17896 } else { 17897 if ($strcount > 0) { 17898 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)])); 17899 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); 17900 } 17901 } 17902 } else { // LTR 17903 if ($currentxpos > $textpos) { 17904 if ($strcount > 0) { 17905 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]); 17906 } 17907 $w_diff = ($spacewidth * $lnstring[2][$strcount]); 17908 } else { 17909 if ($strcount > 1) { 17910 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]); 17911 } 17912 if ($strcount > 0) { 17913 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); 17914 } 17915 } 17916 } 17917 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) { 17918 $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff)); 17919 $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff)); 17920 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6]; 17921 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17922 unset($pmatch, $newpmid, $newx, $neww); 17923 } 17924 break; 17925 } 17926 case 'c': { 17927 // get current X position 17928 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $xmatches); 17929 if (!isset($xmatches[1])) { 17930 break; 17931 } 17932 $currentxpos = $xmatches[1]; 17933 // justify block 17934 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $pmatch) == 1) { 17935 $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew)); 17936 $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew)); 17937 $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew)); 17938 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8]; 17939 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17940 unset($pmatch, $newpmid, $newx1, $newx2, $newx3); 17941 } 17942 break; 17943 } 17944 } 17945 // shift the annotations and links 17946 $cxpos = ($currentxpos / $this->k); 17947 $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps); 17948 if ($this->inxobj) { 17949 // we are inside an XObject template 17950 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 17951 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 17952 if ($cxpos > $lmpos) { 17953 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k); 17954 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17955 } else { 17956 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17957 } 17958 break; 17959 } 17960 } 17961 } elseif (isset($this->PageAnnots[$this->page])) { 17962 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 17963 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 17964 if ($cxpos > $lmpos) { 17965 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k); 17966 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17967 } else { 17968 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17969 } 17970 break; 17971 } 17972 } 17973 } 17974 } // end of while 17975 // remove markers 17976 $pmid = str_replace('x*#!#*x', '', $pmid); 17977 if ($this->isUnicodeFont()) { 17978 // multibyte characters 17979 $spacew = $spacewidthu; 17980 if ($this->font_stretching != 100) { 17981 // word spacing is affected by stretching 17982 $spacew /= ($this->font_stretching / 100); 17983 } 17984 // escape special characters 17985 $pos = 0; 17986 $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid); 17987 $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid); 17988 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) { 17989 foreach($pamatch[0] as $pk => $pmatch) { 17990 $replace = $pamatch[1][$pk]; 17991 $replace = str_replace('#!#OP#!#', '(', $replace); 17992 $replace = str_replace('#!#CP#!#', ')', $replace); 17993 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]'; 17994 $pos = strpos($pmid, $pmatch, $pos); 17995 if ($pos !== FALSE) { 17996 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch)); 17997 } 17998 ++$pos; 17999 } 18000 unset($pamatch); 18001 } 18002 if ($this->inxobj) { 18003 // we are inside an XObject template 18004 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend; 18005 } else { 18006 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend); 18007 } 18008 $endlinepos = strlen($pstart."\n".$pmid."\n"); 18009 } else { 18010 // non-unicode (single-byte characters) 18011 if ($this->font_stretching != 100) { 18012 // word spacing (Tw) is affected by stretching 18013 $spacewidth /= ($this->font_stretching / 100); 18014 } 18015 $rs = sprintf('%F Tw', $spacewidth); 18016 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid); 18017 if ($this->inxobj) { 18018 // we are inside an XObject template 18019 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend; 18020 } else { 18021 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend); 18022 } 18023 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n"); 18024 } 18025 } 18026 } // end of J 18027 } // end if $startlinex 18028 if (($t_x != 0) OR ($yshift < 0)) { 18029 // shift the line 18030 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k)); 18031 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; 18032 $endlinepos = strlen($pstart); 18033 if ($this->inxobj) { 18034 // we are inside an XObject template 18035 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; 18036 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 18037 if ($pak >= $pask) { 18038 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; 18039 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; 18040 } 18041 } 18042 } else { 18043 $this->setPageBuffer($startlinepage, $pstart.$pend); 18044 // shift the annotations and links 18045 if (isset($this->PageAnnots[$this->page])) { 18046 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 18047 if ($pak >= $pask) { 18048 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 18049 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 18050 } 18051 } 18052 } 18053 } 18054 $this->y -= $yshift; 18055 } 18056 } 18057 $pbrk = $this->checkPageBreak($this->lasth); 18058 $this->newline = false; 18059 $startlinex = $this->x; 18060 $startliney = $this->y; 18061 if ($dom[$dom[$key]['parent']]['value'] == 'sup') { 18062 $startliney -= ((0.3 * $this->FontSizePt) / $this->k); 18063 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') { 18064 $startliney -= (($this->FontSizePt / 0.7) / $this->k); 18065 } else { 18066 $minstartliney = $startliney; 18067 $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k)); 18068 } 18069 $startlinepage = $this->page; 18070 if (isset($endlinepos) AND (!$pbrk)) { 18071 $startlinepos = $endlinepos; 18072 } else { 18073 if ($this->inxobj) { 18074 // we are inside an XObject template 18075 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); 18076 } elseif (!$this->InFooter) { 18077 if (isset($this->footerlen[$this->page])) { 18078 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 18079 } else { 18080 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 18081 } 18082 $startlinepos = $this->footerpos[$this->page]; 18083 } else { 18084 $startlinepos = $this->pagelen[$this->page]; 18085 } 18086 } 18087 unset($endlinepos); 18088 $plalign = $lalign; 18089 if (isset($this->PageAnnots[$this->page])) { 18090 $pask = count($this->PageAnnots[$this->page]); 18091 } else { 18092 $pask = 0; 18093 } 18094 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table') 18095 AND (isset($this->emptypagemrk[$this->page])) 18096 AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) { 18097 $this->SetFont($fontname, $fontstyle, $fontsize); 18098 if ($wfill) { 18099 $this->SetFillColorArray($this->bgcolor); 18100 } 18101 } 18102 } // end newline 18103 if (isset($opentagpos)) { 18104 unset($opentagpos); 18105 } 18106 if ($dom[$key]['tag']) { 18107 if ($dom[$key]['opening']) { 18108 // get text indentation (if any) 18109 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) { 18110 $this->textindent = $dom[$key]['text-indent']; 18111 $this->newline = true; 18112 } 18113 // table 18114 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) { 18115 // available page width 18116 if ($this->rtl) { 18117 $wtmp = $this->x - $this->lMargin; 18118 } else { 18119 $wtmp = $this->w - $this->rMargin - $this->x; 18120 } 18121 // get cell spacing 18122 if (isset($dom[$key]['attribute']['cellspacing'])) { 18123 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px'); 18124 $cellspacing = array('H' => $clsp, 'V' => $clsp); 18125 } elseif (isset($dom[$key]['border-spacing'])) { 18126 $cellspacing = $dom[$key]['border-spacing']; 18127 } else { 18128 $cellspacing = array('H' => 0, 'V' => 0); 18129 } 18130 // table width 18131 if (isset($dom[$key]['width'])) { 18132 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); 18133 } else { 18134 $table_width = $wtmp; 18135 } 18136 $table_width -= (2 * $cellspacing['H']); 18137 if (!$this->inthead) { 18138 $this->y += $cellspacing['V']; 18139 } 18140 if ($this->rtl) { 18141 $cellspacingx = -$cellspacing['H']; 18142 } else { 18143 $cellspacingx = $cellspacing['H']; 18144 } 18145 // total table width without cellspaces 18146 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1))); 18147 // minimum column width 18148 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']); 18149 // array of custom column widths 18150 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width); 18151 } 18152 // table row 18153 if ($dom[$key]['value'] == 'tr') { 18154 // reset column counter 18155 $colid = 0; 18156 } 18157 // table cell 18158 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 18159 $trid = $dom[$key]['parent']; 18160 $table_el = $dom[$trid]['parent']; 18161 if (!isset($dom[$table_el]['cols'])) { 18162 $dom[$table_el]['cols'] = $dom[$trid]['cols']; 18163 } 18164 // store border info 18165 $tdborder = 0; 18166 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) { 18167 $tdborder = $dom[$key]['border']; 18168 } 18169 $colspan = intval($dom[$key]['attribute']['colspan']); 18170 if ($colspan <= 0) { 18171 $colspan = 1; 18172 } 18173 $old_cell_padding = $this->cell_padding; 18174 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { 18175 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px'); 18176 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd); 18177 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) { 18178 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding']; 18179 } else { 18180 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0); 18181 } 18182 $this->cell_padding = $current_cell_padding; 18183 if (isset($dom[$key]['height'])) { 18184 // minimum cell height 18185 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px'); 18186 } else { 18187 $cellh = 0; 18188 } 18189 if (isset($dom[$key]['content'])) { 18190 $cell_content = stripslashes($dom[$key]['content']); 18191 } else { 18192 $cell_content = ' '; 18193 } 18194 $tagtype = $dom[$key]['value']; 18195 $parentid = $key; 18196 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) { 18197 // move $key index forward 18198 ++$key; 18199 } 18200 if (!isset($dom[$trid]['startpage'])) { 18201 $dom[$trid]['startpage'] = $this->page; 18202 } else { 18203 $this->setPage($dom[$trid]['startpage']); 18204 } 18205 if (!isset($dom[$trid]['startcolumn'])) { 18206 $dom[$trid]['startcolumn'] = $this->current_column; 18207 } elseif ($this->current_column != $dom[$trid]['startcolumn']) { 18208 $tmpx = $this->x; 18209 $this->selectColumn($dom[$trid]['startcolumn']); 18210 $this->x = $tmpx; 18211 } 18212 if (!isset($dom[$trid]['starty'])) { 18213 $dom[$trid]['starty'] = $this->y; 18214 } else { 18215 $this->y = $dom[$trid]['starty']; 18216 } 18217 if (!isset($dom[$trid]['startx'])) { 18218 $dom[$trid]['startx'] = $this->x; 18219 $this->x += $cellspacingx; 18220 } else { 18221 $this->x += ($cellspacingx / 2); 18222 } 18223 if (isset($dom[$parentid]['attribute']['rowspan'])) { 18224 $rowspan = intval($dom[$parentid]['attribute']['rowspan']); 18225 } else { 18226 $rowspan = 1; 18227 } 18228 // skip row-spanned cells started on the previous rows 18229 if (isset($dom[$table_el]['rowspans'])) { 18230 $rsk = 0; 18231 $rskmax = count($dom[$table_el]['rowspans']); 18232 while ($rsk < $rskmax) { 18233 $trwsp = $dom[$table_el]['rowspans'][$rsk]; 18234 $rsstartx = $trwsp['startx']; 18235 $rsendx = $trwsp['endx']; 18236 // account for margin changes 18237 if ($trwsp['startpage'] < $this->page) { 18238 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) { 18239 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']); 18240 $rsstartx -= $dl; 18241 $rsendx -= $dl; 18242 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) { 18243 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']); 18244 $rsstartx += $dl; 18245 $rsendx += $dl; 18246 } 18247 } 18248 if (($trwsp['rowspan'] > 0) 18249 AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps)) 18250 AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps)) 18251 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) { 18252 // set the starting X position of the current cell 18253 $this->x = $rsendx + $cellspacingx; 18254 // increment column indicator 18255 $colid += $trwsp['colspan']; 18256 if (($trwsp['rowspan'] == 1) 18257 AND (isset($dom[$trid]['endy'])) 18258 AND (isset($dom[$trid]['endpage'])) 18259 AND (isset($dom[$trid]['endcolumn'])) 18260 AND ($trwsp['endpage'] == $dom[$trid]['endpage']) 18261 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { 18262 // set ending Y position for row 18263 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 18264 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy']; 18265 } 18266 $rsk = 0; 18267 } else { 18268 ++$rsk; 18269 } 18270 } 18271 } 18272 if (isset($dom[$parentid]['width'])) { 18273 // user specified width 18274 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px'); 18275 $tmpcw = ($cellw / $colspan); 18276 for ($i = 0; $i < $colspan; ++$i) { 18277 $table_colwidths[($colid + $i)] = $tmpcw; 18278 } 18279 } else { 18280 // inherit column width 18281 $cellw = 0; 18282 for ($i = 0; $i < $colspan; ++$i) { 18283 $cellw += $table_colwidths[($colid + $i)]; 18284 } 18285 } 18286 $cellw += (($colspan - 1) * $cellspacing['H']); 18287 // increment column indicator 18288 $colid += $colspan; 18289 // add rowspan information to table element 18290 if ($rowspan > 1) { 18291 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y)); 18292 } 18293 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x)); 18294 if ($rowspan > 1) { 18295 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1); 18296 } 18297 // push background colors 18298 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) { 18299 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor']; 18300 } 18301 // store border info 18302 if (isset($tdborder) AND !empty($tdborder)) { 18303 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder; 18304 } 18305 $prevLastH = $this->lasth; 18306 // store some info for multicolumn mode 18307 if ($this->rtl) { 18308 $this->colxshift['x'] = $this->w - $this->x - $this->rMargin; 18309 } else { 18310 $this->colxshift['x'] = $this->x - $this->lMargin; 18311 } 18312 $this->colxshift['s'] = $cellspacing; 18313 $this->colxshift['p'] = $current_cell_padding; 18314 // ****** write the cell content ****** 18315 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false); 18316 // restore some values 18317 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 18318 $this->lasth = $prevLastH; 18319 $this->cell_padding = $old_cell_padding; 18320 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x; 18321 // update the end of row position 18322 if ($rowspan <= 1) { 18323 if (isset($dom[$trid]['endy'])) { 18324 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) { 18325 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']); 18326 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) { 18327 $dom[$trid]['endy'] = $this->y; 18328 } 18329 } else { 18330 $dom[$trid]['endy'] = $this->y; 18331 } 18332 if (isset($dom[$trid]['endpage'])) { 18333 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']); 18334 } else { 18335 $dom[$trid]['endpage'] = $this->page; 18336 } 18337 if (isset($dom[$trid]['endcolumn'])) { 18338 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']); 18339 } else { 18340 $dom[$trid]['endcolumn'] = $this->current_column; 18341 } 18342 } else { 18343 // account for row-spanned cells 18344 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x; 18345 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y; 18346 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page; 18347 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column; 18348 } 18349 if (isset($dom[$table_el]['rowspans'])) { 18350 // update endy and endpage on rowspanned cells 18351 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 18352 if ($trwsp['rowspan'] > 0) { 18353 if (isset($dom[$trid]['endpage'])) { 18354 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { 18355 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 18356 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) { 18357 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy']; 18358 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage']; 18359 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn']; 18360 } else { 18361 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm']; 18362 } 18363 } 18364 } 18365 } 18366 } 18367 $this->x += ($cellspacingx / 2); 18368 } else { 18369 // opening tag (or self-closing tag) 18370 if (!isset($opentagpos)) { 18371 if ($this->inxobj) { 18372 // we are inside an XObject template 18373 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']); 18374 } elseif (!$this->InFooter) { 18375 if (isset($this->footerlen[$this->page])) { 18376 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 18377 } else { 18378 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 18379 } 18380 $opentagpos = $this->footerpos[$this->page]; 18381 } 18382 } 18383 $dom = $this->openHTMLTagHandler($dom, $key, $cell); 18384 } 18385 } else { // closing tag 18386 $prev_numpages = $this->numpages; 18387 $old_bordermrk = $this->bordermrk[$this->page]; 18388 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney); 18389 if ($this->bordermrk[$this->page] > $old_bordermrk) { 18390 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk); 18391 } 18392 if ($prev_numpages > $this->numpages) { 18393 $startlinepage = $this->page; 18394 } 18395 } 18396 } elseif (strlen($dom[$key]['value']) > 0) { 18397 // print list-item 18398 if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) { 18399 $this->SetFont($pfontname, $pfontstyle, $pfontsize); 18400 $this->resetLastH(); 18401 $minstartliney = $this->y; 18402 $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize)); 18403 if (is_numeric($pfontsize) AND ($pfontsize > 0)) { 18404 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize); 18405 } 18406 $this->SetFont($curfontname, $curfontstyle, $curfontsize); 18407 $this->resetLastH(); 18408 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) { 18409 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize); 18410 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize); 18411 $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2; 18412 $minstartliney = min($this->y, $minstartliney); 18413 $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney); 18414 } 18415 } 18416 // text 18417 $this->htmlvspace = 0; 18418 if ((!$this->premode) AND $this->isRTLTextDir()) { 18419 // reverse spaces order 18420 $lsp = ''; // left spaces 18421 $rsp = ''; // right spaces 18422 if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { 18423 $lsp = $matches[1]; 18424 } 18425 if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { 18426 $rsp = $matches[1]; 18427 } 18428 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp; 18429 } 18430 if ($newline) { 18431 if (!$this->premode) { 18432 $prelen = strlen($dom[$key]['value']); 18433 if ($this->isRTLTextDir()) { 18434 // right trim except non-breaking space 18435 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']); 18436 } else { 18437 // left trim except non-breaking space 18438 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']); 18439 } 18440 $postlen = strlen($dom[$key]['value']); 18441 if (($postlen == 0) AND ($prelen > 0)) { 18442 $dom[$key]['trimmed_space'] = true; 18443 } 18444 } 18445 $newline = false; 18446 $firstblock = true; 18447 } else { 18448 $firstblock = false; 18449 // replace empty multiple spaces string with a single space 18450 $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']); 18451 } 18452 $strrest = ''; 18453 if ($this->rtl) { 18454 $this->x -= $this->textindent; 18455 } else { 18456 $this->x += $this->textindent; 18457 } 18458 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) { 18459 $strlinelen = $this->GetStringWidth($dom[$key]['value']); 18460 if (!empty($this->HREF) AND (isset($this->HREF['url']))) { 18461 // HTML <a> Link 18462 $hrefcolor = ''; 18463 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) { 18464 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor']; 18465 } 18466 $hrefstyle = -1; 18467 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) { 18468 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle']; 18469 } 18470 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true); 18471 } else { 18472 $wadj = 0; // space to leave for block continuity 18473 if ($this->rtl) { 18474 $cwa = ($this->x - $this->lMargin); 18475 } else { 18476 $cwa = ($this->w - $this->rMargin - $this->x); 18477 } 18478 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) { 18479 // check the next text blocks for continuity 18480 $nkey = ($key + 1); 18481 $write_block = true; 18482 $same_textdir = true; 18483 $tmp_fontname = $this->FontFamily; 18484 $tmp_fontstyle = $this->FontStyle; 18485 $tmp_fontsize = $this->FontSizePt; 18486 while ($write_block AND isset($dom[$nkey])) { 18487 if ($dom[$nkey]['tag']) { 18488 if ($dom[$nkey]['block']) { 18489 // end of block 18490 $write_block = false; 18491 } 18492 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily; 18493 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle; 18494 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt; 18495 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']); 18496 } else { 18497 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']); 18498 if (isset($nextstr[0]) AND $same_textdir) { 18499 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize); 18500 if (isset($nextstr[1])) { 18501 $write_block = false; 18502 } 18503 } 18504 } 18505 ++$nkey; 18506 } 18507 } 18508 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) { 18509 $wadj = 0; 18510 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']); 18511 $numblks = count($nextstr); 18512 if ($numblks > 1) { 18513 // try to split on blank spaces 18514 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)])); 18515 } else { 18516 // set the entire block on new line 18517 $wadj = $this->GetStringWidth($nextstr[0]); 18518 } 18519 } 18520 // check for reversed text direction 18521 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) { 18522 // LTR text on RTL direction or RTL text on LTR direction 18523 $reverse_dir = true; 18524 $this->rtl = !$this->rtl; 18525 $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems 18526 if ($this->rtl) { 18527 $this->x += $revshift; 18528 } else { 18529 $this->x -= $revshift; 18530 } 18531 $xws = $this->x; 18532 } 18533 // ****** write only until the end of the line and get the rest ****** 18534 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj); 18535 // restore default direction 18536 if ($reverse_dir AND ($wadj == 0)) { 18537 $this->x = $xws; 18538 $this->rtl = !$this->rtl; 18539 $reverse_dir = false; 18540 } 18541 } 18542 } 18543 $this->textindent = 0; 18544 if (strlen($strrest) > 0) { 18545 // store the remaining string on the previous $key position 18546 $this->newline = true; 18547 if ($strrest == $dom[$key]['value']) { 18548 // used to avoid infinite loop 18549 ++$loop; 18550 } else { 18551 $loop = 0; 18552 } 18553 $dom[$key]['value'] = $strrest; 18554 if ($cell) { 18555 if ($this->rtl) { 18556 $this->x -= $this->cell_padding['R']; 18557 } else { 18558 $this->x += $this->cell_padding['L']; 18559 } 18560 } 18561 if ($loop < 3) { 18562 --$key; 18563 } 18564 } else { 18565 $loop = 0; 18566 // add the positive font spacing of the last character (if any) 18567 if ($this->font_spacing > 0) { 18568 if ($this->rtl) { 18569 $this->x -= $this->font_spacing; 18570 } else { 18571 $this->x += $this->font_spacing; 18572 } 18573 } 18574 } 18575 } 18576 ++$key; 18577 if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { 18578 // check if we are on a new page or on a new column 18579 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) { 18580 // we are on a new page or on a new column and the total object height is less than the available vertical space. 18581 // restore previous object 18582 $this->rollbackTransaction(true); 18583 // restore previous values 18584 foreach ($this_method_vars as $vkey => $vval) { 18585 $$vkey = $vval; 18586 } 18587 if (!empty($dom[$key]['thead'])) { 18588 $this->inthead = true; 18589 } 18590 // add a page (or trig AcceptPageBreak() for multicolumn mode) 18591 $pre_y = $this->y; 18592 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 18593 $startliney = $this->y; 18594 } 18595 $undo = true; // avoid infinite loop 18596 } else { 18597 $undo = false; 18598 } 18599 } 18600 } // end for each $key 18601 // align the last line 18602 if (isset($startlinex)) { 18603 $yshift = ($minstartliney - $startliney); 18604 if (($yshift > 0) OR ($this->page > $startlinepage)) { 18605 $yshift = 0; 18606 } 18607 $t_x = 0; 18608 // the last line must be shifted to be aligned as requested 18609 $linew = abs($this->endlinex - $startlinex); 18610 if ($this->inxobj) { 18611 // we are inside an XObject template 18612 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); 18613 if (isset($opentagpos)) { 18614 $midpos = $opentagpos; 18615 } else { 18616 $midpos = 0; 18617 } 18618 if ($midpos > 0) { 18619 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); 18620 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); 18621 } else { 18622 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); 18623 $pend = ''; 18624 } 18625 } else { 18626 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 18627 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 18628 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 18629 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 18630 } elseif (isset($opentagpos)) { 18631 $midpos = $opentagpos; 18632 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 18633 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 18634 $midpos = $this->footerpos[$startlinepage]; 18635 } else { 18636 $midpos = 0; 18637 } 18638 if ($midpos > 0) { 18639 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 18640 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 18641 } else { 18642 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 18643 $pend = ''; 18644 } 18645 } 18646 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { 18647 // calculate shifting amount 18648 $tw = $w; 18649 if ($this->lMargin != $prevlMargin) { 18650 $tw += ($prevlMargin - $this->lMargin); 18651 } 18652 if ($this->rMargin != $prevrMargin) { 18653 $tw += ($prevrMargin - $this->rMargin); 18654 } 18655 $one_space_width = $this->GetStringWidth(chr(32)); 18656 $no = 0; // number of spaces on a line contained on a single block 18657 if ($this->isRTLTextDir()) { // RTL 18658 // remove left space if exist 18659 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[('); 18660 if ($pos1 > 0) { 18661 $pos1 = intval($pos1); 18662 if ($this->isUnicodeFont()) { 18663 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32))); 18664 $spacelen = 2; 18665 } else { 18666 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32))); 18667 $spacelen = 1; 18668 } 18669 if ($pos1 == $pos2) { 18670 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); 18671 if (substr($pmid, $pos1, 4) == '[()]') { 18672 $linew -= $one_space_width; 18673 } elseif ($pos1 == strpos($pmid, '[(')) { 18674 $no = 1; 18675 } 18676 } 18677 } 18678 } else { // LTR 18679 // remove right space if exist 18680 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]'); 18681 if ($pos1 > 0) { 18682 $pos1 = intval($pos1); 18683 if ($this->isUnicodeFont()) { 18684 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2; 18685 $spacelen = 2; 18686 } else { 18687 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1; 18688 $spacelen = 1; 18689 } 18690 if ($pos1 == $pos2) { 18691 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); 18692 $linew -= $one_space_width; 18693 } 18694 } 18695 } 18696 $mdiff = ($tw - $linew); 18697 if ($plalign == 'C') { 18698 if ($this->rtl) { 18699 $t_x = -($mdiff / 2); 18700 } else { 18701 $t_x = ($mdiff / 2); 18702 } 18703 } elseif ($plalign == 'R') { 18704 // right alignment on LTR document 18705 $t_x = $mdiff; 18706 } elseif ($plalign == 'L') { 18707 // left alignment on RTL document 18708 $t_x = -$mdiff; 18709 } 18710 } // end if startlinex 18711 if (($t_x != 0) OR ($yshift < 0)) { 18712 // shift the line 18713 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k)); 18714 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; 18715 $endlinepos = strlen($pstart); 18716 if ($this->inxobj) { 18717 // we are inside an XObject template 18718 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; 18719 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 18720 if ($pak >= $pask) { 18721 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; 18722 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; 18723 } 18724 } 18725 } else { 18726 $this->setPageBuffer($startlinepage, $pstart.$pend); 18727 // shift the annotations and links 18728 if (isset($this->PageAnnots[$this->page])) { 18729 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 18730 if ($pak >= $pask) { 18731 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 18732 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 18733 } 18734 } 18735 } 18736 } 18737 $this->y -= $yshift; 18738 $yshift = 0; 18739 } 18740 } 18741 // restore previous values 18742 $this->setGraphicVars($gvars); 18743 if ($this->num_columns > 1) { 18744 $this->selectColumn(); 18745 } elseif ($this->page > $prevPage) { 18746 $this->lMargin = $this->pagedim[$this->page]['olm']; 18747 $this->rMargin = $this->pagedim[$this->page]['orm']; 18748 } 18749 // restore previous list state 18750 $this->cell_height_ratio = $prev_cell_height_ratio; 18751 $this->listnum = $prev_listnum; 18752 $this->listordered = $prev_listordered; 18753 $this->listcount = $prev_listcount; 18754 $this->lispacer = $prev_lispacer; 18755 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) { 18756 $this->Ln($this->lasth); 18757 if ($this->y < $maxbottomliney) { 18758 $this->y = $maxbottomliney; 18759 } 18760 } 18761 unset($dom); 18762 } 18763 18764 /** 18765 * Process opening tags. 18766 * @param $dom (array) html dom array 18767 * @param $key (int) current element id 18768 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 18769 * @return $dom array 18770 * @protected 18771 */ 18772 protected function openHTMLTagHandler($dom, $key, $cell) { 18773 $tag = $dom[$key]; 18774 $parent = $dom[($dom[$key]['parent'])]; 18775 $firsttag = ($key == 1); 18776 // check for text direction attribute 18777 if (isset($tag['dir'])) { 18778 $this->setTempRTL($tag['dir']); 18779 } else { 18780 $this->tmprtl = false; 18781 } 18782 if ($tag['block']) { 18783 $hbz = 0; // distance from y to line bottom 18784 $hb = 0; // vertical space between block tags 18785 // calculate vertical space for block tags 18786 if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) { 18787 $cur_h = $this->tagvspaces[$tag['value']][0]['h']; 18788 } elseif (isset($tag['fontsize'])) { 18789 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k); 18790 } else { 18791 $cur_h = $this->getCellHeight($this->FontSize); 18792 } 18793 if (isset($this->tagvspaces[$tag['value']][0]['n'])) { 18794 $on = $this->tagvspaces[$tag['value']][0]['n']; 18795 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 18796 $on = 0.6; 18797 } else { 18798 $on = 1; 18799 } 18800 if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) { 18801 $hb = 0; 18802 } else { 18803 $hb = ($on * $cur_h); 18804 } 18805 if (($this->htmlvspace <= 0) AND ($on > 0)) { 18806 if (isset($parent['fontsize'])) { 18807 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); 18808 } else { 18809 $hbz = $this->getCellHeight($this->FontSize); 18810 } 18811 } 18812 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) { 18813 // fix vertical space after table 18814 $hbz = 0; 18815 } 18816 // closing vertical space 18817 $hbc = 0; 18818 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { 18819 $pre_h = $this->tagvspaces[$tag['value']][1]['h']; 18820 } elseif (isset($parent['fontsize'])) { 18821 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k); 18822 } else { 18823 $pre_h = $this->getCellHeight($this->FontSize); 18824 } 18825 if (isset($this->tagvspaces[$tag['value']][1]['n'])) { 18826 $cn = $this->tagvspaces[$tag['value']][1]['n']; 18827 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 18828 $cn = 0.6; 18829 } else { 18830 $cn = 1; 18831 } 18832 if (isset($this->tagvspaces[$tag['value']][1])) { 18833 $hbc = ($cn * $pre_h); 18834 } 18835 } 18836 // Opening tag 18837 switch($tag['value']) { 18838 case 'table': { 18839 $cp = 0; 18840 $cs = 0; 18841 $dom[$key]['rowspans'] = array(); 18842 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) { 18843 $this->htmlvspace = 0; 18844 // set table header 18845 if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) { 18846 // set table header 18847 $this->thead = $dom[$key]['thead']; 18848 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) { 18849 $this->theadMargins = array(); 18850 $this->theadMargins['cell_padding'] = $this->cell_padding; 18851 $this->theadMargins['lmargin'] = $this->lMargin; 18852 $this->theadMargins['rmargin'] = $this->rMargin; 18853 $this->theadMargins['page'] = $this->page; 18854 $this->theadMargins['cell'] = $cell; 18855 $this->theadMargins['gvars'] = $this->getGraphicVars(); 18856 } 18857 } 18858 } 18859 // store current margins and page 18860 $dom[$key]['old_cell_padding'] = $this->cell_padding; 18861 if (isset($tag['attribute']['cellpadding'])) { 18862 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px'); 18863 $this->SetCellPadding($pad); 18864 } elseif (isset($tag['padding'])) { 18865 $this->cell_padding = $tag['padding']; 18866 } 18867 if (isset($tag['attribute']['cellspacing'])) { 18868 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 18869 } elseif (isset($tag['border-spacing'])) { 18870 $cs = $tag['border-spacing']['V']; 18871 } 18872 $prev_y = $this->y; 18873 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) { 18874 $this->inthead = true; 18875 // add a page (or trig AcceptPageBreak() for multicolumn mode) 18876 $this->checkPageBreak($this->PageBreakTrigger + 1); 18877 } 18878 break; 18879 } 18880 case 'tr': { 18881 // array of columns positions 18882 $dom[$key]['cellpos'] = array(); 18883 break; 18884 } 18885 case 'hr': { 18886 if ((isset($tag['height'])) AND ($tag['height'] != '')) { 18887 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px'); 18888 } else { 18889 $hrHeight = $this->GetLineWidth(); 18890 } 18891 $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag); 18892 $x = $this->GetX(); 18893 $y = $this->GetY(); 18894 $wtmp = $this->w - $this->lMargin - $this->rMargin; 18895 if ($cell) { 18896 $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']); 18897 } 18898 if ((isset($tag['width'])) AND ($tag['width'] != '')) { 18899 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px'); 18900 } else { 18901 $hrWidth = $wtmp; 18902 } 18903 $prevlinewidth = $this->GetLineWidth(); 18904 $this->SetLineWidth($hrHeight); 18905 $this->Line($x, $y, $x + $hrWidth, $y); 18906 $this->SetLineWidth($prevlinewidth); 18907 $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)])); 18908 break; 18909 } 18910 case 'a': { 18911 if (array_key_exists('href', $tag['attribute'])) { 18912 $this->HREF['url'] = $tag['attribute']['href']; 18913 } 18914 break; 18915 } 18916 case 'img': { 18917 if (!empty($tag['attribute']['src'])) { 18918 if ($tag['attribute']['src'][0] === '@') { 18919 // data stream 18920 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1)); 18921 $type = ''; 18922 } else { 18923 // get image type 18924 $type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']); 18925 } 18926 if (!isset($tag['width'])) { 18927 $tag['width'] = 0; 18928 } 18929 if (!isset($tag['height'])) { 18930 $tag['height'] = 0; 18931 } 18932 //if (!isset($tag['attribute']['align'])) { 18933 // the only alignment supported is "bottom" 18934 // further development is required for other modes. 18935 $tag['attribute']['align'] = 'bottom'; 18936 //} 18937 switch($tag['attribute']['align']) { 18938 case 'top': { 18939 $align = 'T'; 18940 break; 18941 } 18942 case 'middle': { 18943 $align = 'M'; 18944 break; 18945 } 18946 case 'bottom': { 18947 $align = 'B'; 18948 break; 18949 } 18950 default: { 18951 $align = 'B'; 18952 break; 18953 } 18954 } 18955 $prevy = $this->y; 18956 $xpos = $this->x; 18957 $imglink = ''; 18958 if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) { 18959 $imglink = $this->HREF['url']; 18960 if ($imglink[0] == '#') { 18961 // convert url to internal link 18962 $lnkdata = explode(',', $imglink); 18963 if (isset($lnkdata[0])) { 18964 $page = intval(substr($lnkdata[0], 1)); 18965 if (empty($page) OR ($page <= 0)) { 18966 $page = $this->page; 18967 } 18968 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { 18969 $lnky = floatval($lnkdata[1]); 18970 } else { 18971 $lnky = 0; 18972 } 18973 $imglink = $this->AddLink(); 18974 $this->SetLink($imglink, $lnky, $page); 18975 } 18976 } 18977 } 18978 $border = 0; 18979 if (isset($tag['border']) AND !empty($tag['border'])) { 18980 // currently only support 1 (frame) or a combination of 'LTRB' 18981 $border = $tag['border']; 18982 } 18983 $iw = ''; 18984 if (isset($tag['width'])) { 18985 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false); 18986 } 18987 $ih = ''; 18988 if (isset($tag['height'])) { 18989 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false); 18990 } 18991 if (($type == 'eps') OR ($type == 'ai')) { 18992 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true); 18993 } elseif ($type == 'svg') { 18994 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true); 18995 } else { 18996 $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true); 18997 } 18998 switch($align) { 18999 case 'T': { 19000 $this->y = $prevy; 19001 break; 19002 } 19003 case 'M': { 19004 $this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2); 19005 break; 19006 } 19007 case 'B': { 19008 $this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio)); 19009 break; 19010 } 19011 } 19012 } 19013 break; 19014 } 19015 case 'dl': { 19016 ++$this->listnum; 19017 if ($this->listnum == 1) { 19018 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19019 } else { 19020 $this->addHTMLVertSpace(0, 0, $cell, $firsttag); 19021 } 19022 break; 19023 } 19024 case 'dt': { 19025 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19026 break; 19027 } 19028 case 'dd': { 19029 if ($this->rtl) { 19030 $this->rMargin += $this->listindent; 19031 } else { 19032 $this->lMargin += $this->listindent; 19033 } 19034 ++$this->listindentlevel; 19035 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19036 break; 19037 } 19038 case 'ul': 19039 case 'ol': { 19040 ++$this->listnum; 19041 if ($tag['value'] == 'ol') { 19042 $this->listordered[$this->listnum] = true; 19043 } else { 19044 $this->listordered[$this->listnum] = false; 19045 } 19046 if (isset($tag['attribute']['start'])) { 19047 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1; 19048 } else { 19049 $this->listcount[$this->listnum] = 0; 19050 } 19051 if ($this->rtl) { 19052 $this->rMargin += $this->listindent; 19053 $this->x -= $this->listindent; 19054 } else { 19055 $this->lMargin += $this->listindent; 19056 $this->x += $this->listindent; 19057 } 19058 ++$this->listindentlevel; 19059 if ($this->listnum == 1) { 19060 if ($key > 1) { 19061 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19062 } 19063 } else { 19064 $this->addHTMLVertSpace(0, 0, $cell, $firsttag); 19065 } 19066 break; 19067 } 19068 case 'li': { 19069 if ($key > 2) { 19070 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19071 } 19072 if ($this->listordered[$this->listnum]) { 19073 // ordered item 19074 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) { 19075 $this->lispacer = $parent['attribute']['type']; 19076 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) { 19077 $this->lispacer = $parent['listtype']; 19078 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) { 19079 $this->lispacer = $this->lisymbol; 19080 } else { 19081 $this->lispacer = '#'; 19082 } 19083 ++$this->listcount[$this->listnum]; 19084 if (isset($tag['attribute']['value'])) { 19085 $this->listcount[$this->listnum] = intval($tag['attribute']['value']); 19086 } 19087 } else { 19088 // unordered item 19089 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) { 19090 $this->lispacer = $parent['attribute']['type']; 19091 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) { 19092 $this->lispacer = $parent['listtype']; 19093 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) { 19094 $this->lispacer = $this->lisymbol; 19095 } else { 19096 $this->lispacer = '!'; 19097 } 19098 } 19099 break; 19100 } 19101 case 'blockquote': { 19102 if ($this->rtl) { 19103 $this->rMargin += $this->listindent; 19104 } else { 19105 $this->lMargin += $this->listindent; 19106 } 19107 ++$this->listindentlevel; 19108 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19109 break; 19110 } 19111 case 'br': { 19112 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19113 break; 19114 } 19115 case 'div': { 19116 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19117 break; 19118 } 19119 case 'p': { 19120 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19121 break; 19122 } 19123 case 'pre': { 19124 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19125 $this->premode = true; 19126 break; 19127 } 19128 case 'sup': { 19129 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k)); 19130 break; 19131 } 19132 case 'sub': { 19133 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k)); 19134 break; 19135 } 19136 case 'h1': 19137 case 'h2': 19138 case 'h3': 19139 case 'h4': 19140 case 'h5': 19141 case 'h6': { 19142 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19143 break; 19144 } 19145 // Form fields (since 4.8.000 - 2009-09-07) 19146 case 'form': { 19147 if (isset($tag['attribute']['action'])) { 19148 $this->form_action = $tag['attribute']['action']; 19149 } else { 19150 $this->Error('Please explicitly set action attribute path!'); 19151 } 19152 if (isset($tag['attribute']['enctype'])) { 19153 $this->form_enctype = $tag['attribute']['enctype']; 19154 } else { 19155 $this->form_enctype = 'application/x-www-form-urlencoded'; 19156 } 19157 if (isset($tag['attribute']['method'])) { 19158 $this->form_mode = $tag['attribute']['method']; 19159 } else { 19160 $this->form_mode = 'post'; 19161 } 19162 break; 19163 } 19164 case 'input': { 19165 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19166 $name = $tag['attribute']['name']; 19167 } else { 19168 break; 19169 } 19170 $prop = array(); 19171 $opt = array(); 19172 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) { 19173 $prop['readonly'] = true; 19174 } 19175 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) { 19176 $value = $tag['attribute']['value']; 19177 } 19178 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) { 19179 $opt['maxlen'] = intval($tag['attribute']['maxlength']); 19180 } 19181 $h = $this->getCellHeight($this->FontSize); 19182 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) { 19183 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2; 19184 } else { 19185 $w = $h; 19186 } 19187 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) { 19188 $checked = true; 19189 } else { 19190 $checked = false; 19191 } 19192 if (isset($tag['align'])) { 19193 switch ($tag['align']) { 19194 case 'C': { 19195 $opt['q'] = 1; 19196 break; 19197 } 19198 case 'R': { 19199 $opt['q'] = 2; 19200 break; 19201 } 19202 case 'L': 19203 default: { 19204 break; 19205 } 19206 } 19207 } 19208 switch ($tag['attribute']['type']) { 19209 case 'text': { 19210 if (isset($value)) { 19211 $opt['v'] = $value; 19212 } 19213 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19214 break; 19215 } 19216 case 'password': { 19217 if (isset($value)) { 19218 $opt['v'] = $value; 19219 } 19220 $prop['password'] = 'true'; 19221 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19222 break; 19223 } 19224 case 'checkbox': { 19225 if (!isset($value)) { 19226 break; 19227 } 19228 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false); 19229 break; 19230 } 19231 case 'radio': { 19232 if (!isset($value)) { 19233 break; 19234 } 19235 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false); 19236 break; 19237 } 19238 case 'submit': { 19239 if (!isset($value)) { 19240 $value = 'submit'; 19241 } 19242 $w = $this->GetStringWidth($value) * 1.5; 19243 $h *= 1.6; 19244 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19245 $action = array(); 19246 $action['S'] = 'SubmitForm'; 19247 $action['F'] = $this->form_action; 19248 if ($this->form_enctype != 'FDF') { 19249 $action['Flags'] = array('ExportFormat'); 19250 } 19251 if ($this->form_mode == 'get') { 19252 $action['Flags'] = array('GetMethod'); 19253 } 19254 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false); 19255 break; 19256 } 19257 case 'reset': { 19258 if (!isset($value)) { 19259 $value = 'reset'; 19260 } 19261 $w = $this->GetStringWidth($value) * 1.5; 19262 $h *= 1.6; 19263 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19264 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false); 19265 break; 19266 } 19267 case 'file': { 19268 $prop['fileSelect'] = 'true'; 19269 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19270 if (!isset($value)) { 19271 $value = '*'; 19272 } 19273 $w = $this->GetStringWidth($value) * 2; 19274 $h *= 1.2; 19275 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19276 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();'; 19277 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19278 break; 19279 } 19280 case 'hidden': { 19281 if (isset($value)) { 19282 $opt['v'] = $value; 19283 } 19284 $opt['f'] = array('invisible', 'hidden'); 19285 $this->TextField($name, 0, 0, $prop, $opt, '', '', false); 19286 break; 19287 } 19288 case 'image': { 19289 // THIS TYPE MUST BE FIXED 19290 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) { 19291 $img = $tag['attribute']['src']; 19292 } else { 19293 break; 19294 } 19295 $value = 'img'; 19296 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false)); 19297 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 19298 $jsaction = $tag['attribute']['onclick']; 19299 } else { 19300 $jsaction = ''; 19301 } 19302 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19303 break; 19304 } 19305 case 'button': { 19306 if (!isset($value)) { 19307 $value = ' '; 19308 } 19309 $w = $this->GetStringWidth($value) * 1.5; 19310 $h *= 1.6; 19311 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19312 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 19313 $jsaction = $tag['attribute']['onclick']; 19314 } else { 19315 $jsaction = ''; 19316 } 19317 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19318 break; 19319 } 19320 } 19321 break; 19322 } 19323 case 'textarea': { 19324 $prop = array(); 19325 $opt = array(); 19326 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) { 19327 $prop['readonly'] = true; 19328 } 19329 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19330 $name = $tag['attribute']['name']; 19331 } else { 19332 break; 19333 } 19334 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) { 19335 $opt['v'] = $tag['attribute']['value']; 19336 } 19337 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) { 19338 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2; 19339 } else { 19340 $w = 40; 19341 } 19342 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) { 19343 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize); 19344 } else { 19345 $h = 10; 19346 } 19347 $prop['multiline'] = 'true'; 19348 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19349 break; 19350 } 19351 case 'select': { 19352 $h = $this->getCellHeight($this->FontSize); 19353 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) { 19354 $h *= ($tag['attribute']['size'] + 1); 19355 } 19356 $prop = array(); 19357 $opt = array(); 19358 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19359 $name = $tag['attribute']['name']; 19360 } else { 19361 break; 19362 } 19363 $w = 0; 19364 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) { 19365 $options = explode('#!NwL!#', $tag['attribute']['opt']); 19366 $values = array(); 19367 foreach ($options as $val) { 19368 if (strpos($val, '#!TaB!#') !== false) { 19369 $opts = explode('#!TaB!#', $val); 19370 $values[] = $opts; 19371 $w = max($w, $this->GetStringWidth($opts[1])); 19372 } else { 19373 $values[] = $val; 19374 $w = max($w, $this->GetStringWidth($val)); 19375 } 19376 } 19377 } else { 19378 break; 19379 } 19380 $w *= 2; 19381 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) { 19382 $prop['multipleSelection'] = 'true'; 19383 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false); 19384 } else { 19385 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false); 19386 } 19387 break; 19388 } 19389 case 'tcpdf': { 19390 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) { 19391 // Special tag used to call TCPDF methods 19392 if (isset($tag['attribute']['method'])) { 19393 $tcpdf_method = $tag['attribute']['method']; 19394 if (method_exists($this, $tcpdf_method)) { 19395 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) { 19396 $params = TCPDF_STATIC::unserializeTCPDFtagParameters($tag['attribute']['params']); 19397 call_user_func_array(array($this, $tcpdf_method), $params); 19398 } else { 19399 $this->$tcpdf_method(); 19400 } 19401 $this->newline = true; 19402 } 19403 } 19404 } 19405 break; 19406 } 19407 default: { 19408 break; 19409 } 19410 } 19411 // define tags that support borders and background colors 19412 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table'); 19413 if (in_array($tag['value'], $bordertags)) { 19414 // set border 19415 $dom[$key]['borderposition'] = $this->getBorderStartPosition(); 19416 } 19417 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) { 19418 $pba = $dom[$key]['attribute']['pagebreakafter']; 19419 // check for pagebreak 19420 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 19421 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19422 $this->checkPageBreak($this->PageBreakTrigger + 1); 19423 } 19424 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 19425 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 19426 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19427 $this->checkPageBreak($this->PageBreakTrigger + 1); 19428 } 19429 } 19430 return $dom; 19431 } 19432 19433 /** 19434 * Process closing tags. 19435 * @param $dom (array) html dom array 19436 * @param $key (int) current element id 19437 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 19438 * @param $maxbottomliney (int) maximum y value of current line 19439 * @return $dom array 19440 * @protected 19441 */ 19442 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) { 19443 $tag = $dom[$key]; 19444 $parent = $dom[($dom[$key]['parent'])]; 19445 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker'))); 19446 $in_table_head = false; 19447 // maximum x position (used to draw borders) 19448 if ($this->rtl) { 19449 $xmax = $this->w; 19450 } else { 19451 $xmax = 0; 19452 } 19453 if ($tag['block']) { 19454 $hbz = 0; // distance from y to line bottom 19455 $hb = 0; // vertical space between block tags 19456 // calculate vertical space for block tags 19457 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { 19458 $pre_h = $this->tagvspaces[$tag['value']][1]['h']; 19459 } elseif (isset($parent['fontsize'])) { 19460 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k); 19461 } else { 19462 $pre_h = $this->getCellHeight($this->FontSize); 19463 } 19464 if (isset($this->tagvspaces[$tag['value']][1]['n'])) { 19465 $cn = $this->tagvspaces[$tag['value']][1]['n']; 19466 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 19467 $cn = 0.6; 19468 } else { 19469 $cn = 1; 19470 } 19471 if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) { 19472 $hb = 0; 19473 } else { 19474 $hb = ($cn * $pre_h); 19475 } 19476 if ($maxbottomliney > $this->PageBreakTrigger) { 19477 $hbz = $this->getCellHeight($this->FontSize); 19478 } elseif ($this->y < $maxbottomliney) { 19479 $hbz = ($maxbottomliney - $this->y); 19480 } 19481 } 19482 // Closing tag 19483 switch($tag['value']) { 19484 case 'tr': { 19485 $table_el = $dom[($dom[$key]['parent'])]['parent']; 19486 if (!isset($parent['endy'])) { 19487 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 19488 $parent['endy'] = $this->y; 19489 } 19490 if (!isset($parent['endpage'])) { 19491 $dom[($dom[$key]['parent'])]['endpage'] = $this->page; 19492 $parent['endpage'] = $this->page; 19493 } 19494 if (!isset($parent['endcolumn'])) { 19495 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column; 19496 $parent['endcolumn'] = $this->current_column; 19497 } 19498 // update row-spanned cells 19499 if (isset($dom[$table_el]['rowspans'])) { 19500 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19501 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1; 19502 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19503 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) { 19504 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']); 19505 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) { 19506 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 19507 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 19508 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; 19509 } 19510 } 19511 } 19512 // report new endy and endpage to the rowspanned cells 19513 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19514 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19515 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']); 19516 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 19517 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']); 19518 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; 19519 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']); 19520 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 19521 } 19522 } 19523 // update remaining rowspanned cells 19524 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19525 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19526 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage']; 19527 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn']; 19528 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy']; 19529 } 19530 } 19531 } 19532 $prev_page = $this->page; 19533 $this->setPage($dom[($dom[$key]['parent'])]['endpage']); 19534 if ($this->num_columns > 1) { 19535 if ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1))) 19536 OR (($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']) AND ($prev_page < $this->page))) { 19537 // page jump 19538 $this->selectColumn(0); 19539 $dom[($dom[$key]['parent'])]['endcolumn'] = 0; 19540 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 19541 } else { 19542 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']); 19543 $this->y = $dom[($dom[$key]['parent'])]['endy']; 19544 } 19545 } else { 19546 $this->y = $dom[($dom[$key]['parent'])]['endy']; 19547 } 19548 if (isset($dom[$table_el]['attribute']['cellspacing'])) { 19549 $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px'); 19550 } elseif (isset($dom[$table_el]['border-spacing'])) { 19551 $this->y += $dom[$table_el]['border-spacing']['V']; 19552 } 19553 $this->Ln(0, $cell); 19554 if ($this->current_column == $parent['startcolumn']) { 19555 $this->x = $parent['startx']; 19556 } 19557 // account for booklet mode 19558 if ($this->page > $parent['startpage']) { 19559 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) { 19560 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']); 19561 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) { 19562 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']); 19563 } 19564 } 19565 break; 19566 } 19567 case 'tablehead': 19568 // closing tag used for the thead part 19569 $in_table_head = true; 19570 $this->inthead = false; 19571 case 'table': { 19572 $table_el = $parent; 19573 // set default border 19574 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) { 19575 // set default border 19576 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0))); 19577 } else { 19578 $border = 0; 19579 } 19580 $default_border = $border; 19581 // fix bottom line alignment of last line before page break 19582 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) { 19583 // update row-spanned cells 19584 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 19585 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 19586 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) { 19587 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey; 19588 } 19589 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) { 19590 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1; 19591 } 19592 } 19593 } 19594 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) { 19595 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm']; 19596 $dom[$prevtrkey]['endy'] = $pgendy; 19597 // update row-spanned cells 19598 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 19599 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 19600 if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) { 19601 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy; 19602 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1; 19603 } 19604 } 19605 } 19606 } 19607 $prevtrkey = $trkey; 19608 $table_el = $dom[($dom[$key]['parent'])]; 19609 } 19610 // for each row 19611 if (count($table_el['trids']) > 0) { 19612 unset($xmax); 19613 } 19614 foreach ($table_el['trids'] as $j => $trkey) { 19615 $parent = $dom[$trkey]; 19616 if (!isset($xmax)) { 19617 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx']; 19618 } 19619 // for each cell on the row 19620 foreach ($parent['cellpos'] as $k => $cellpos) { 19621 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) { 19622 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx']; 19623 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx']; 19624 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy']; 19625 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage']; 19626 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage']; 19627 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn']; 19628 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn']; 19629 } else { 19630 $endy = $parent['endy']; 19631 $startpage = $parent['startpage']; 19632 $endpage = $parent['endpage']; 19633 $startcolumn = $parent['startcolumn']; 19634 $endcolumn = $parent['endcolumn']; 19635 } 19636 if ($this->num_columns == 0) { 19637 $this->num_columns = 1; 19638 } 19639 if (isset($cellpos['border'])) { 19640 $border = $cellpos['border']; 19641 } 19642 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { 19643 $this->SetFillColorArray($cellpos['bgcolor']); 19644 $fill = true; 19645 } else { 19646 $fill = false; 19647 } 19648 $x = $cellpos['startx']; 19649 $y = $parent['starty']; 19650 $starty = $y; 19651 $w = abs($cellpos['endx'] - $cellpos['startx']); 19652 // get border modes 19653 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 19654 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 19655 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 19656 // design borders around HTML cells. 19657 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 19658 $ccode = ''; 19659 $this->setPage($page); 19660 if ($this->num_columns < 2) { 19661 // single-column mode 19662 $this->x = $x; 19663 $this->y = $this->tMargin; 19664 } 19665 // account for margin changes 19666 if ($page > $startpage) { 19667 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 19668 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 19669 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 19670 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 19671 } 19672 } 19673 if ($startpage == $endpage) { // single page 19674 $deltacol = 0; 19675 $deltath = 0; 19676 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 19677 $this->selectColumn($column); 19678 if ($startcolumn == $endcolumn) { // single column 19679 $cborder = $border; 19680 $h = $endy - $parent['starty']; 19681 $this->y = $y; 19682 $this->x = $x; 19683 } elseif ($column == $startcolumn) { // first column 19684 $cborder = $border_start; 19685 $this->y = $starty; 19686 $this->x = $x; 19687 $h = $this->h - $this->y - $this->bMargin; 19688 if ($this->rtl) { 19689 $deltacol = $this->x + $this->rMargin - $this->w; 19690 } else { 19691 $deltacol = $this->x - $this->lMargin; 19692 } 19693 } elseif ($column == $endcolumn) { // end column 19694 $cborder = $border_end; 19695 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19696 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19697 } 19698 $this->x += $deltacol; 19699 $h = $endy - $this->y; 19700 } else { // middle column 19701 $cborder = $border_middle; 19702 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19703 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19704 } 19705 $this->x += $deltacol; 19706 $h = $this->h - $this->y - $this->bMargin; 19707 } 19708 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19709 } // end for each column 19710 } elseif ($page == $startpage) { // first page 19711 $deltacol = 0; 19712 $deltath = 0; 19713 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 19714 $this->selectColumn($column); 19715 if ($column == $startcolumn) { // first column 19716 $cborder = $border_start; 19717 $this->y = $starty; 19718 $this->x = $x; 19719 $h = $this->h - $this->y - $this->bMargin; 19720 if ($this->rtl) { 19721 $deltacol = $this->x + $this->rMargin - $this->w; 19722 } else { 19723 $deltacol = $this->x - $this->lMargin; 19724 } 19725 } else { // middle column 19726 $cborder = $border_middle; 19727 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19728 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19729 } 19730 $this->x += $deltacol; 19731 $h = $this->h - $this->y - $this->bMargin; 19732 } 19733 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19734 } // end for each column 19735 } elseif ($page == $endpage) { // last page 19736 $deltacol = 0; 19737 $deltath = 0; 19738 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 19739 $this->selectColumn($column); 19740 if ($column == $endcolumn) { // end column 19741 $cborder = $border_end; 19742 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19743 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19744 } 19745 $this->x += $deltacol; 19746 $h = $endy - $this->y; 19747 } else { // middle column 19748 $cborder = $border_middle; 19749 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19750 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19751 } 19752 $this->x += $deltacol; 19753 $h = $this->h - $this->y - $this->bMargin; 19754 } 19755 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19756 } // end for each column 19757 } else { // middle page 19758 $deltacol = 0; 19759 $deltath = 0; 19760 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 19761 $this->selectColumn($column); 19762 $cborder = $border_middle; 19763 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19764 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19765 } 19766 $this->x += $deltacol; 19767 $h = $this->h - $this->y - $this->bMargin; 19768 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19769 } // end for each column 19770 } 19771 if ($cborder OR $fill) { 19772 $offsetlen = strlen($ccode); 19773 // draw border and fill 19774 if ($this->inxobj) { 19775 // we are inside an XObject template 19776 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 19777 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 19778 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 19779 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 19780 } else { 19781 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 19782 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 19783 } 19784 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 19785 $pstart = substr($pagebuff, 0, $pagemark); 19786 $pend = substr($pagebuff, $pagemark); 19787 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 19788 } else { 19789 // draw border and fill 19790 if (end($this->transfmrk[$this->page]) !== false) { 19791 $pagemarkkey = key($this->transfmrk[$this->page]); 19792 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 19793 } elseif ($this->InFooter) { 19794 $pagemark = $this->footerpos[$this->page]; 19795 } else { 19796 $pagemark = $this->intmrk[$this->page]; 19797 } 19798 $pagebuff = $this->getPageBuffer($this->page); 19799 $pstart = substr($pagebuff, 0, $pagemark); 19800 $pend = substr($pagebuff, $pagemark); 19801 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 19802 } 19803 } 19804 } // end for each page 19805 // restore default border 19806 $border = $default_border; 19807 } // end for each cell on the row 19808 if (isset($table_el['attribute']['cellspacing'])) { 19809 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px'); 19810 } elseif (isset($table_el['border-spacing'])) { 19811 $this->y += $table_el['border-spacing']['V']; 19812 } 19813 $this->Ln(0, $cell); 19814 $this->x = $parent['startx']; 19815 if ($endpage > $startpage) { 19816 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) { 19817 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']); 19818 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) { 19819 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']); 19820 } 19821 } 19822 } 19823 if (!$in_table_head) { // we are not inside a thead section 19824 $this->cell_padding = $table_el['old_cell_padding']; 19825 // reset row height 19826 $this->resetLastH(); 19827 if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) { 19828 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]); 19829 if (($plendiff > 0) AND ($plendiff < 60)) { 19830 $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff); 19831 if (substr($pagediff, 0, 5) == 'BT /F') { 19832 // the difference is only a font setting 19833 $plendiff = 0; 19834 } 19835 } 19836 if ($plendiff == 0) { 19837 // remove last blank page 19838 $this->deletePage($this->numpages); 19839 } 19840 } 19841 if (isset($this->theadMargins['top'])) { 19842 // restore top margin 19843 $this->tMargin = $this->theadMargins['top']; 19844 } 19845 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) { 19846 // reset main table header 19847 $this->thead = ''; 19848 $this->theadMargins = array(); 19849 $this->pagedim[$this->page]['tm'] = $this->tMargin; 19850 } 19851 } 19852 $parent = $table_el; 19853 break; 19854 } 19855 case 'a': { 19856 $this->HREF = ''; 19857 break; 19858 } 19859 case 'sup': { 19860 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k)); 19861 break; 19862 } 19863 case 'sub': { 19864 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k)); 19865 break; 19866 } 19867 case 'div': { 19868 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19869 break; 19870 } 19871 case 'blockquote': { 19872 if ($this->rtl) { 19873 $this->rMargin -= $this->listindent; 19874 } else { 19875 $this->lMargin -= $this->listindent; 19876 } 19877 --$this->listindentlevel; 19878 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19879 break; 19880 } 19881 case 'p': { 19882 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19883 break; 19884 } 19885 case 'pre': { 19886 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19887 $this->premode = false; 19888 break; 19889 } 19890 case 'dl': { 19891 --$this->listnum; 19892 if ($this->listnum <= 0) { 19893 $this->listnum = 0; 19894 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19895 } else { 19896 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19897 } 19898 $this->resetLastH(); 19899 break; 19900 } 19901 case 'dt': { 19902 $this->lispacer = ''; 19903 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19904 break; 19905 } 19906 case 'dd': { 19907 $this->lispacer = ''; 19908 if ($this->rtl) { 19909 $this->rMargin -= $this->listindent; 19910 } else { 19911 $this->lMargin -= $this->listindent; 19912 } 19913 --$this->listindentlevel; 19914 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19915 break; 19916 } 19917 case 'ul': 19918 case 'ol': { 19919 --$this->listnum; 19920 $this->lispacer = ''; 19921 if ($this->rtl) { 19922 $this->rMargin -= $this->listindent; 19923 } else { 19924 $this->lMargin -= $this->listindent; 19925 } 19926 --$this->listindentlevel; 19927 if ($this->listnum <= 0) { 19928 $this->listnum = 0; 19929 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19930 } else { 19931 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19932 } 19933 $this->resetLastH(); 19934 break; 19935 } 19936 case 'li': { 19937 $this->lispacer = ''; 19938 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19939 break; 19940 } 19941 case 'h1': 19942 case 'h2': 19943 case 'h3': 19944 case 'h4': 19945 case 'h5': 19946 case 'h6': { 19947 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19948 break; 19949 } 19950 // Form fields (since 4.8.000 - 2009-09-07) 19951 case 'form': { 19952 $this->form_action = ''; 19953 $this->form_enctype = 'application/x-www-form-urlencoded'; 19954 break; 19955 } 19956 default : { 19957 break; 19958 } 19959 } 19960 // draw border and background (if any) 19961 $this->drawHTMLTagBorder($parent, $xmax); 19962 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) { 19963 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter']; 19964 // check for pagebreak 19965 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 19966 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19967 $this->checkPageBreak($this->PageBreakTrigger + 1); 19968 } 19969 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 19970 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 19971 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19972 $this->checkPageBreak($this->PageBreakTrigger + 1); 19973 } 19974 } 19975 $this->tmprtl = false; 19976 return $dom; 19977 } 19978 19979 /** 19980 * Add vertical spaces if needed. 19981 * @param $hbz (string) Distance between current y and line bottom. 19982 * @param $hb (string) The height of the break. 19983 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 19984 * @param $firsttag (boolean) set to true when the tag is the first. 19985 * @param $lasttag (boolean) set to true when the tag is the last. 19986 * @protected 19987 */ 19988 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) { 19989 if ($firsttag) { 19990 $this->Ln(0, $cell); 19991 $this->htmlvspace = 0; 19992 return; 19993 } 19994 if ($lasttag) { 19995 $this->Ln($hbz, $cell); 19996 $this->htmlvspace = 0; 19997 return; 19998 } 19999 if ($hb < $this->htmlvspace) { 20000 $hd = 0; 20001 } else { 20002 $hd = $hb - $this->htmlvspace; 20003 $this->htmlvspace = $hb; 20004 } 20005 $this->Ln(($hbz + $hd), $cell); 20006 } 20007 20008 /** 20009 * Return the starting coordinates to draw an html border 20010 * @return array containing top-left border coordinates 20011 * @protected 20012 * @since 5.7.000 (2010-08-03) 20013 */ 20014 protected function getBorderStartPosition() { 20015 if ($this->rtl) { 20016 $xmax = $this->lMargin; 20017 } else { 20018 $xmax = $this->w - $this->rMargin; 20019 } 20020 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax); 20021 } 20022 20023 /** 20024 * Draw an HTML block border and fill 20025 * @param $tag (array) array of tag properties. 20026 * @param $xmax (int) end X coordinate for border. 20027 * @protected 20028 * @since 5.7.000 (2010-08-03) 20029 */ 20030 protected function drawHTMLTagBorder($tag, $xmax) { 20031 if (!isset($tag['borderposition'])) { 20032 // nothing to draw 20033 return; 20034 } 20035 $prev_x = $this->x; 20036 $prev_y = $this->y; 20037 $prev_lasth = $this->lasth; 20038 $border = 0; 20039 $fill = false; 20040 $this->lasth = 0; 20041 if (isset($tag['border']) AND !empty($tag['border'])) { 20042 // get border style 20043 $border = $tag['border']; 20044 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) { 20045 // border for table header 20046 $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 20047 } 20048 } 20049 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) { 20050 // get background color 20051 $old_bgcolor = $this->bgcolor; 20052 $this->SetFillColorArray($tag['bgcolor']); 20053 $fill = true; 20054 } 20055 if (!$border AND !$fill) { 20056 // nothing to draw 20057 return; 20058 } 20059 if (isset($tag['attribute']['cellspacing'])) { 20060 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 20061 $cellspacing = array('H' => $clsp, 'V' => $clsp); 20062 } elseif (isset($tag['border-spacing'])) { 20063 $cellspacing = $tag['border-spacing']; 20064 } else { 20065 $cellspacing = array('H' => 0, 'V' => 0); 20066 } 20067 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) { 20068 // draw the border externally respect the sqare edge. 20069 $border['mode'] = 'ext'; 20070 } 20071 if ($this->rtl) { 20072 if ($xmax >= $tag['borderposition']['x']) { 20073 $xmax = $tag['borderposition']['xmax']; 20074 } 20075 $w = ($tag['borderposition']['x'] - $xmax); 20076 } else { 20077 if ($xmax <= $tag['borderposition']['x']) { 20078 $xmax = $tag['borderposition']['xmax']; 20079 } 20080 $w = ($xmax - $tag['borderposition']['x']); 20081 } 20082 if ($w <= 0) { 20083 return; 20084 } 20085 $w += $cellspacing['H']; 20086 $startpage = $tag['borderposition']['page']; 20087 $startcolumn = $tag['borderposition']['column']; 20088 $x = $tag['borderposition']['x']; 20089 $y = $tag['borderposition']['y']; 20090 $endpage = $this->page; 20091 $starty = $tag['borderposition']['y'] - $cellspacing['V']; 20092 $currentY = $this->y; 20093 $this->x = $x; 20094 // get latest column 20095 $endcolumn = $this->current_column; 20096 if ($this->num_columns == 0) { 20097 $this->num_columns = 1; 20098 } 20099 // get border modes 20100 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 20101 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 20102 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 20103 // temporary disable page regions 20104 $temp_page_regions = $this->page_regions; 20105 $this->page_regions = array(); 20106 // design borders around HTML cells. 20107 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 20108 $ccode = ''; 20109 $this->setPage($page); 20110 if ($this->num_columns < 2) { 20111 // single-column mode 20112 $this->x = $x; 20113 $this->y = $this->tMargin; 20114 } 20115 // account for margin changes 20116 if ($page > $startpage) { 20117 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 20118 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 20119 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 20120 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 20121 } 20122 } 20123 if ($startpage == $endpage) { 20124 // single page 20125 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 20126 $this->selectColumn($column); 20127 if ($startcolumn == $endcolumn) { // single column 20128 $cborder = $border; 20129 $h = ($currentY - $y) + $cellspacing['V']; 20130 $this->y = $starty; 20131 } elseif ($column == $startcolumn) { // first column 20132 $cborder = $border_start; 20133 $this->y = $starty; 20134 $h = $this->h - $this->y - $this->bMargin; 20135 } elseif ($column == $endcolumn) { // end column 20136 $cborder = $border_end; 20137 $h = $currentY - $this->y; 20138 } else { // middle column 20139 $cborder = $border_middle; 20140 $h = $this->h - $this->y - $this->bMargin; 20141 } 20142 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20143 } // end for each column 20144 } elseif ($page == $startpage) { // first page 20145 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 20146 $this->selectColumn($column); 20147 if ($column == $startcolumn) { // first column 20148 $cborder = $border_start; 20149 $this->y = $starty; 20150 $h = $this->h - $this->y - $this->bMargin; 20151 } else { // middle column 20152 $cborder = $border_middle; 20153 $h = $this->h - $this->y - $this->bMargin; 20154 } 20155 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20156 } // end for each column 20157 } elseif ($page == $endpage) { // last page 20158 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 20159 $this->selectColumn($column); 20160 if ($column == $endcolumn) { 20161 // end column 20162 $cborder = $border_end; 20163 $h = $currentY - $this->y; 20164 } else { 20165 // middle column 20166 $cborder = $border_middle; 20167 $h = $this->h - $this->y - $this->bMargin; 20168 } 20169 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20170 } // end for each column 20171 } else { // middle page 20172 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 20173 $this->selectColumn($column); 20174 $cborder = $border_middle; 20175 $h = $this->h - $this->y - $this->bMargin; 20176 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20177 } // end for each column 20178 } 20179 if ($cborder OR $fill) { 20180 $offsetlen = strlen($ccode); 20181 // draw border and fill 20182 if ($this->inxobj) { 20183 // we are inside an XObject template 20184 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 20185 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 20186 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 20187 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 20188 } else { 20189 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 20190 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 20191 } 20192 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 20193 $pstart = substr($pagebuff, 0, $pagemark); 20194 $pend = substr($pagebuff, $pagemark); 20195 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 20196 } else { 20197 if (end($this->transfmrk[$this->page]) !== false) { 20198 $pagemarkkey = key($this->transfmrk[$this->page]); 20199 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 20200 } elseif ($this->InFooter) { 20201 $pagemark = $this->footerpos[$this->page]; 20202 } else { 20203 $pagemark = $this->intmrk[$this->page]; 20204 } 20205 $pagebuff = $this->getPageBuffer($this->page); 20206 $pstart = substr($pagebuff, 0, $pagemark); 20207 $pend = substr($pagebuff, $pagemark); 20208 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 20209 $this->bordermrk[$this->page] += $offsetlen; 20210 $this->cntmrk[$this->page] += $offsetlen; 20211 } 20212 } 20213 } // end for each page 20214 // restore page regions 20215 $this->page_regions = $temp_page_regions; 20216 if (isset($old_bgcolor)) { 20217 // restore background color 20218 $this->SetFillColorArray($old_bgcolor); 20219 } 20220 // restore pointer position 20221 $this->x = $prev_x; 20222 $this->y = $prev_y; 20223 $this->lasth = $prev_lasth; 20224 } 20225 20226 /** 20227 * Set the default bullet to be used as LI bullet symbol 20228 * @param $symbol (string) 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', 'img|type|width|height|image.ext') 20229 * @public 20230 * @since 4.0.028 (2008-09-26) 20231 */ 20232 public function setLIsymbol($symbol='!') { 20233 // check for custom image symbol 20234 if (substr($symbol, 0, 4) == 'img|') { 20235 $this->lisymbol = $symbol; 20236 return; 20237 } 20238 $symbol = strtolower($symbol); 20239 $valid_symbols = array('!', '#', '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'); 20240 if (in_array($symbol, $valid_symbols)) { 20241 $this->lisymbol = $symbol; 20242 } else { 20243 $this->lisymbol = ''; 20244 } 20245 } 20246 20247 /** 20248 * Set the booklet mode for double-sided pages. 20249 * @param $booklet (boolean) true set the booklet mode on, false otherwise. 20250 * @param $inner (float) Inner page margin. 20251 * @param $outer (float) Outer page margin. 20252 * @public 20253 * @since 4.2.000 (2008-10-29) 20254 */ 20255 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) { 20256 $this->booklet = $booklet; 20257 if ($inner >= 0) { 20258 $this->lMargin = $inner; 20259 } 20260 if ($outer >= 0) { 20261 $this->rMargin = $outer; 20262 } 20263 } 20264 20265 /** 20266 * Swap the left and right margins. 20267 * @param $reverse (boolean) if true swap left and right margins. 20268 * @protected 20269 * @since 4.2.000 (2008-10-29) 20270 */ 20271 protected function swapMargins($reverse=true) { 20272 if ($reverse) { 20273 // swap left and right margins 20274 $mtemp = $this->original_lMargin; 20275 $this->original_lMargin = $this->original_rMargin; 20276 $this->original_rMargin = $mtemp; 20277 $deltam = $this->original_lMargin - $this->original_rMargin; 20278 $this->lMargin += $deltam; 20279 $this->rMargin -= $deltam; 20280 } 20281 } 20282 20283 /** 20284 * Set the vertical spaces for HTML tags. 20285 * The array must have the following structure (example): 20286 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1))); 20287 * The first array level contains the tag names, 20288 * the second level contains 0 for opening tags or 1 for closing tags, 20289 * the third level contains the vertical space unit (h) and the number spaces to add (n). 20290 * If the h parameter is not specified, default values are used. 20291 * @param $tagvs (array) array of tags and relative vertical spaces. 20292 * @public 20293 * @since 4.2.001 (2008-10-30) 20294 */ 20295 public function setHtmlVSpace($tagvs) { 20296 $this->tagvspaces = $tagvs; 20297 } 20298 20299 /** 20300 * Set custom width for list indentation. 20301 * @param $width (float) width of the indentation. Use negative value to disable it. 20302 * @public 20303 * @since 4.2.007 (2008-11-12) 20304 */ 20305 public function setListIndentWidth($width) { 20306 return $this->customlistindent = floatval($width); 20307 } 20308 20309 /** 20310 * Set the top/bottom cell sides to be open or closed when the cell cross the page. 20311 * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page. 20312 * @public 20313 * @since 4.2.010 (2008-11-14) 20314 */ 20315 public function setOpenCell($isopen) { 20316 $this->opencell = $isopen; 20317 } 20318 20319 /** 20320 * Set the color and font style for HTML links. 20321 * @param $color (array) RGB array of colors 20322 * @param $fontstyle (string) additional font styles to add 20323 * @public 20324 * @since 4.4.003 (2008-12-09) 20325 */ 20326 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') { 20327 $this->htmlLinkColorArray = $color; 20328 $this->htmlLinkFontStyle = $fontstyle; 20329 } 20330 20331 /** 20332 * Convert HTML string containing value and unit of measure to user's units or points. 20333 * @param $htmlval (string) String containing values and unit. 20334 * @param $refsize (string) Reference value in points. 20335 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). 20336 * @param $points (boolean) If true returns points, otherwise returns value in user's units. 20337 * @return float value in user's unit or point if $points=true 20338 * @public 20339 * @since 4.4.004 (2008-12-10) 20340 */ 20341 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) { 20342 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt'); 20343 $retval = 0; 20344 $value = 0; 20345 $unit = 'px'; 20346 if ($points) { 20347 $k = 1; 20348 } else { 20349 $k = $this->k; 20350 } 20351 if (in_array($defaultunit, $supportedunits)) { 20352 $unit = $defaultunit; 20353 } 20354 if (is_numeric($htmlval)) { 20355 $value = floatval($htmlval); 20356 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) { 20357 $value = floatval($mnum[1]); 20358 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) { 20359 if (in_array($munit[1], $supportedunits)) { 20360 $unit = $munit[1]; 20361 } 20362 } 20363 } 20364 switch ($unit) { 20365 // percentage 20366 case '%': { 20367 $retval = (($value * $refsize) / 100); 20368 break; 20369 } 20370 // relative-size 20371 case 'em': { 20372 $retval = ($value * $refsize); 20373 break; 20374 } 20375 // height of lower case 'x' (about half the font-size) 20376 case 'ex': { 20377 $retval = ($value * ($refsize / 2)); 20378 break; 20379 } 20380 // absolute-size 20381 case 'in': { 20382 $retval = (($value * $this->dpi) / $k); 20383 break; 20384 } 20385 // centimeters 20386 case 'cm': { 20387 $retval = (($value / 2.54 * $this->dpi) / $k); 20388 break; 20389 } 20390 // millimeters 20391 case 'mm': { 20392 $retval = (($value / 25.4 * $this->dpi) / $k); 20393 break; 20394 } 20395 // one pica is 12 points 20396 case 'pc': { 20397 $retval = (($value * 12) / $k); 20398 break; 20399 } 20400 // points 20401 case 'pt': { 20402 $retval = ($value / $k); 20403 break; 20404 } 20405 // pixels 20406 case 'px': { 20407 $retval = $this->pixelsToUnits($value); 20408 if ($points) { 20409 $retval *= $this->k; 20410 } 20411 break; 20412 } 20413 } 20414 return $retval; 20415 } 20416 20417 /** 20418 * Output an HTML list bullet or ordered item symbol 20419 * @param $listdepth (int) list nesting level 20420 * @param $listtype (string) type of list 20421 * @param $size (float) current font size 20422 * @protected 20423 * @since 4.4.004 (2008-12-10) 20424 */ 20425 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) { 20426 if ($this->state != 2) { 20427 return; 20428 } 20429 $size /= $this->k; 20430 $fill = ''; 20431 $bgcolor = $this->bgcolor; 20432 $color = $this->fgcolor; 20433 $strokecolor = $this->strokecolor; 20434 $width = 0; 20435 $textitem = ''; 20436 $tmpx = $this->x; 20437 $lspace = $this->GetStringWidth(' '); 20438 if ($listtype == '^') { 20439 // special symbol used for avoid justification of rect bullet 20440 $this->lispacer = ''; 20441 return; 20442 } elseif ($listtype == '!') { 20443 // set default list type for unordered list 20444 $deftypes = array('disc', 'circle', 'square'); 20445 $listtype = $deftypes[($listdepth - 1) % 3]; 20446 } elseif ($listtype == '#') { 20447 // set default list type for ordered list 20448 $listtype = 'decimal'; 20449 } elseif (substr($listtype, 0, 4) == 'img|') { 20450 // custom image type ('img|type|width|height|image.ext') 20451 $img = explode('|', $listtype); 20452 $listtype = 'img'; 20453 } 20454 switch ($listtype) { 20455 // unordered types 20456 case 'none': { 20457 break; 20458 } 20459 case 'disc': { 20460 $r = $size / 6; 20461 $lspace += (2 * $r); 20462 if ($this->rtl) { 20463 $this->x += $lspace; 20464 } else { 20465 $this->x -= $lspace; 20466 } 20467 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8); 20468 break; 20469 } 20470 case 'circle': { 20471 $r = $size / 6; 20472 $lspace += (2 * $r); 20473 if ($this->rtl) { 20474 $this->x += $lspace; 20475 } else { 20476 $this->x -= $lspace; 20477 } 20478 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor; 20479 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color); 20480 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8); 20481 $this->_out($prev_line_style); // restore line settings 20482 break; 20483 } 20484 case 'square': { 20485 $l = $size / 3; 20486 $lspace += $l; 20487 if ($this->rtl) {; 20488 $this->x += $lspace; 20489 } else { 20490 $this->x -= $lspace; 20491 } 20492 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color); 20493 break; 20494 } 20495 case 'img': { 20496 // 1=>type, 2=>width, 3=>height, 4=>image.ext 20497 $lspace += $img[2]; 20498 if ($this->rtl) {; 20499 $this->x += $lspace; 20500 } else { 20501 $this->x -= $lspace; 20502 } 20503 $imgtype = strtolower($img[1]); 20504 $prev_y = $this->y; 20505 switch ($imgtype) { 20506 case 'svg': { 20507 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false); 20508 break; 20509 } 20510 case 'ai': 20511 case 'eps': { 20512 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false); 20513 break; 20514 } 20515 default: { 20516 $this->Image($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], $img[1], '', 'T', false, 300, '', false, false, 0, false, false, false); 20517 break; 20518 } 20519 } 20520 $this->y = $prev_y; 20521 break; 20522 } 20523 // ordered types 20524 // $this->listcount[$this->listnum]; 20525 // $textitem 20526 case '1': 20527 case 'decimal': { 20528 $textitem = $this->listcount[$this->listnum]; 20529 break; 20530 } 20531 case 'decimal-leading-zero': { 20532 $textitem = sprintf('%02d', $this->listcount[$this->listnum]); 20533 break; 20534 } 20535 case 'i': 20536 case 'lower-roman': { 20537 $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum])); 20538 break; 20539 } 20540 case 'I': 20541 case 'upper-roman': { 20542 $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]); 20543 break; 20544 } 20545 case 'a': 20546 case 'lower-alpha': 20547 case 'lower-latin': { 20548 $textitem = chr(97 + $this->listcount[$this->listnum] - 1); 20549 break; 20550 } 20551 case 'A': 20552 case 'upper-alpha': 20553 case 'upper-latin': { 20554 $textitem = chr(65 + $this->listcount[$this->listnum] - 1); 20555 break; 20556 } 20557 case 'lower-greek': { 20558 $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode); 20559 break; 20560 } 20561 /* 20562 // Types to be implemented (special handling) 20563 case 'hebrew': { 20564 break; 20565 } 20566 case 'armenian': { 20567 break; 20568 } 20569 case 'georgian': { 20570 break; 20571 } 20572 case 'cjk-ideographic': { 20573 break; 20574 } 20575 case 'hiragana': { 20576 break; 20577 } 20578 case 'katakana': { 20579 break; 20580 } 20581 case 'hiragana-iroha': { 20582 break; 20583 } 20584 case 'katakana-iroha': { 20585 break; 20586 } 20587 */ 20588 default: { 20589 $textitem = $this->listcount[$this->listnum]; 20590 } 20591 } 20592 if (!TCPDF_STATIC::empty_string($textitem)) { 20593 // Check whether we need a new page or new column 20594 $prev_y = $this->y; 20595 $h = $this->getCellHeight($this->FontSize); 20596 if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) { 20597 $tmpx = $this->x; 20598 } 20599 // print ordered item 20600 if ($this->rtl) { 20601 $textitem = '.'.$textitem; 20602 } else { 20603 $textitem = $textitem.'.'; 20604 } 20605 $lspace += $this->GetStringWidth($textitem); 20606 if ($this->rtl) { 20607 $this->x += $lspace; 20608 } else { 20609 $this->x -= $lspace; 20610 } 20611 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false); 20612 } 20613 $this->x = $tmpx; 20614 $this->lispacer = '^'; 20615 // restore colors 20616 $this->SetFillColorArray($bgcolor); 20617 $this->SetDrawColorArray($strokecolor); 20618 $this->SettextColorArray($color); 20619 } 20620 20621 /** 20622 * Returns current graphic variables as array. 20623 * @return array of graphic variables 20624 * @protected 20625 * @since 4.2.010 (2008-11-14) 20626 */ 20627 protected function getGraphicVars() { 20628 $grapvars = array( 20629 'FontFamily' => $this->FontFamily, 20630 'FontStyle' => $this->FontStyle, 20631 'FontSizePt' => $this->FontSizePt, 20632 'rMargin' => $this->rMargin, 20633 'lMargin' => $this->lMargin, 20634 'cell_padding' => $this->cell_padding, 20635 'cell_margin' => $this->cell_margin, 20636 'LineWidth' => $this->LineWidth, 20637 'linestyleWidth' => $this->linestyleWidth, 20638 'linestyleCap' => $this->linestyleCap, 20639 'linestyleJoin' => $this->linestyleJoin, 20640 'linestyleDash' => $this->linestyleDash, 20641 'textrendermode' => $this->textrendermode, 20642 'textstrokewidth' => $this->textstrokewidth, 20643 'DrawColor' => $this->DrawColor, 20644 'FillColor' => $this->FillColor, 20645 'TextColor' => $this->TextColor, 20646 'ColorFlag' => $this->ColorFlag, 20647 'bgcolor' => $this->bgcolor, 20648 'fgcolor' => $this->fgcolor, 20649 'htmlvspace' => $this->htmlvspace, 20650 'listindent' => $this->listindent, 20651 'listindentlevel' => $this->listindentlevel, 20652 'listnum' => $this->listnum, 20653 'listordered' => $this->listordered, 20654 'listcount' => $this->listcount, 20655 'lispacer' => $this->lispacer, 20656 'cell_height_ratio' => $this->cell_height_ratio, 20657 'font_stretching' => $this->font_stretching, 20658 'font_spacing' => $this->font_spacing, 20659 'alpha' => $this->alpha, 20660 // extended 20661 'lasth' => $this->lasth, 20662 'tMargin' => $this->tMargin, 20663 'bMargin' => $this->bMargin, 20664 'AutoPageBreak' => $this->AutoPageBreak, 20665 'PageBreakTrigger' => $this->PageBreakTrigger, 20666 'x' => $this->x, 20667 'y' => $this->y, 20668 'w' => $this->w, 20669 'h' => $this->h, 20670 'wPt' => $this->wPt, 20671 'hPt' => $this->hPt, 20672 'fwPt' => $this->fwPt, 20673 'fhPt' => $this->fhPt, 20674 'page' => $this->page, 20675 'current_column' => $this->current_column, 20676 'num_columns' => $this->num_columns 20677 ); 20678 return $grapvars; 20679 } 20680 20681 /** 20682 * Set graphic variables. 20683 * @param $gvars (array) array of graphic variablesto restore 20684 * @param $extended (boolean) if true restore extended graphic variables 20685 * @protected 20686 * @since 4.2.010 (2008-11-14) 20687 */ 20688 protected function setGraphicVars($gvars, $extended=false) { 20689 if ($this->state != 2) { 20690 return; 20691 } 20692 $this->FontFamily = $gvars['FontFamily']; 20693 $this->FontStyle = $gvars['FontStyle']; 20694 $this->FontSizePt = $gvars['FontSizePt']; 20695 $this->rMargin = $gvars['rMargin']; 20696 $this->lMargin = $gvars['lMargin']; 20697 $this->cell_padding = $gvars['cell_padding']; 20698 $this->cell_margin = $gvars['cell_margin']; 20699 $this->LineWidth = $gvars['LineWidth']; 20700 $this->linestyleWidth = $gvars['linestyleWidth']; 20701 $this->linestyleCap = $gvars['linestyleCap']; 20702 $this->linestyleJoin = $gvars['linestyleJoin']; 20703 $this->linestyleDash = $gvars['linestyleDash']; 20704 $this->textrendermode = $gvars['textrendermode']; 20705 $this->textstrokewidth = $gvars['textstrokewidth']; 20706 $this->DrawColor = $gvars['DrawColor']; 20707 $this->FillColor = $gvars['FillColor']; 20708 $this->TextColor = $gvars['TextColor']; 20709 $this->ColorFlag = $gvars['ColorFlag']; 20710 $this->bgcolor = $gvars['bgcolor']; 20711 $this->fgcolor = $gvars['fgcolor']; 20712 $this->htmlvspace = $gvars['htmlvspace']; 20713 $this->listindent = $gvars['listindent']; 20714 $this->listindentlevel = $gvars['listindentlevel']; 20715 $this->listnum = $gvars['listnum']; 20716 $this->listordered = $gvars['listordered']; 20717 $this->listcount = $gvars['listcount']; 20718 $this->lispacer = $gvars['lispacer']; 20719 $this->cell_height_ratio = $gvars['cell_height_ratio']; 20720 $this->font_stretching = $gvars['font_stretching']; 20721 $this->font_spacing = $gvars['font_spacing']; 20722 $this->alpha = $gvars['alpha']; 20723 if ($extended) { 20724 // restore extended values 20725 $this->lasth = $gvars['lasth']; 20726 $this->tMargin = $gvars['tMargin']; 20727 $this->bMargin = $gvars['bMargin']; 20728 $this->AutoPageBreak = $gvars['AutoPageBreak']; 20729 $this->PageBreakTrigger = $gvars['PageBreakTrigger']; 20730 $this->x = $gvars['x']; 20731 $this->y = $gvars['y']; 20732 $this->w = $gvars['w']; 20733 $this->h = $gvars['h']; 20734 $this->wPt = $gvars['wPt']; 20735 $this->hPt = $gvars['hPt']; 20736 $this->fwPt = $gvars['fwPt']; 20737 $this->fhPt = $gvars['fhPt']; 20738 $this->page = $gvars['page']; 20739 $this->current_column = $gvars['current_column']; 20740 $this->num_columns = $gvars['num_columns']; 20741 } 20742 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.''); 20743 if (!TCPDF_STATIC::empty_string($this->FontFamily)) { 20744 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 20745 } 20746 } 20747 20748 /** 20749 * Outputs the "save graphics state" operator 'q' 20750 * @protected 20751 */ 20752 protected function _outSaveGraphicsState() { 20753 $this->_out('q'); 20754 } 20755 20756 /** 20757 * Outputs the "restore graphics state" operator 'Q' 20758 * @protected 20759 */ 20760 protected function _outRestoreGraphicsState() { 20761 $this->_out('Q'); 20762 } 20763 20764 /** 20765 * Writes data to a temporary file on filesystem. 20766 * @param $filename (string) file name 20767 * @param $data (mixed) data to write on file 20768 * @param $append (boolean) if true append data, false replace. 20769 * @param $serialize (boolean) if true serialize data. 20770 * @since 4.5.000 (2008-12-31) 20771 * @protected 20772 */ 20773 protected function writeDiskCache($filename, $data, $append=false, $serialize=false) { 20774 if ($append) { 20775 $fmode = 'ab+'; 20776 } else { 20777 $fmode = 'wb+'; 20778 } 20779 $f = @fopen($filename, $fmode); 20780 if (!$f) { 20781 $this->Error('Unable to write cache file: '.$filename); 20782 } 20783 if ($serialize) { 20784 $data = $this->file_id.serialize($data); 20785 } 20786 fwrite($f, $data); 20787 fclose($f); 20788 // update file length (needed for transactions) 20789 if (!isset($this->cache_file_length['_'.$filename])) { 20790 $this->cache_file_length['_'.$filename] = strlen($data); 20791 } else { 20792 $this->cache_file_length['_'.$filename] += strlen($data); 20793 } 20794 } 20795 20796 /** 20797 * Read data from a temporary file on filesystem. 20798 * @param $filename (string) file name 20799 * @param $unserialize (boolean) if true unserialize data. 20800 * @return mixed retrieved data 20801 * @since 4.5.000 (2008-12-31) 20802 * @protected 20803 */ 20804 protected function readDiskCache($filename, $unserialize=false) { 20805 $data = file_get_contents($filename); 20806 if ($data === FALSE) { 20807 $this->Error('Unable to read the file: '.$filename); 20808 } 20809 if ($unserialize) { 20810 if (substr($data, 0, 32) != $this->file_id) { 20811 $this->Error('Invalid cache file: '.$filename); 20812 } 20813 $data = unserialize(substr($data, 32)); 20814 } 20815 return $data; 20816 } 20817 20818 /** 20819 * Set buffer content (always append data). 20820 * @param $data (string) data 20821 * @protected 20822 * @since 4.5.000 (2009-01-02) 20823 */ 20824 protected function setBuffer($data) { 20825 $this->bufferlen += strlen($data); 20826 if ($this->diskcache) { 20827 if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) { 20828 $this->buffer = TCPDF_STATIC::getObjFilename('buf'); 20829 } 20830 $this->writeDiskCache($this->buffer, $data, true, false); 20831 } else { 20832 $this->buffer .= $data; 20833 } 20834 } 20835 20836 /** 20837 * Replace the buffer content 20838 * @param $data (string) data 20839 * @protected 20840 * @since 5.5.000 (2010-06-22) 20841 */ 20842 protected function replaceBuffer($data) { 20843 $this->bufferlen = strlen($data); 20844 if ($this->diskcache) { 20845 if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) { 20846 $this->buffer = TCPDF_STATIC::getObjFilename('buf'); 20847 } 20848 $this->writeDiskCache($this->buffer, $data, false, false); 20849 } else { 20850 $this->buffer = $data; 20851 } 20852 } 20853 20854 /** 20855 * Get buffer content. 20856 * @return string buffer content 20857 * @protected 20858 * @since 4.5.000 (2009-01-02) 20859 */ 20860 protected function getBuffer() { 20861 if ($this->diskcache) { 20862 return $this->readDiskCache($this->buffer, false); 20863 } else { 20864 return $this->buffer; 20865 } 20866 } 20867 20868 /** 20869 * Set page buffer content. 20870 * @param $page (int) page number 20871 * @param $data (string) page data 20872 * @param $append (boolean) if true append data, false replace. 20873 * @protected 20874 * @since 4.5.000 (2008-12-31) 20875 */ 20876 protected function setPageBuffer($page, $data, $append=false) { 20877 if ($this->diskcache) { 20878 if (!isset($this->pages[$page])) { 20879 $this->pages[$page] = TCPDF_STATIC::getObjFilename('page'); 20880 } 20881 $this->writeDiskCache($this->pages[$page], $data, $append, false); 20882 } else { 20883 if ($append) { 20884 $this->pages[$page] .= $data; 20885 } else { 20886 $this->pages[$page] = $data; 20887 } 20888 } 20889 if ($append AND isset($this->pagelen[$page])) { 20890 $this->pagelen[$page] += strlen($data); 20891 } else { 20892 $this->pagelen[$page] = strlen($data); 20893 } 20894 } 20895 20896 /** 20897 * Get page buffer content. 20898 * @param $page (int) page number 20899 * @return string page buffer content or false in case of error 20900 * @protected 20901 * @since 4.5.000 (2008-12-31) 20902 */ 20903 protected function getPageBuffer($page) { 20904 if ($this->diskcache) { 20905 return $this->readDiskCache($this->pages[$page], false); 20906 } elseif (isset($this->pages[$page])) { 20907 return $this->pages[$page]; 20908 } 20909 return false; 20910 } 20911 20912 /** 20913 * Set image buffer content. 20914 * @param $image (string) image key 20915 * @param $data (array) image data 20916 * @return int image index number 20917 * @protected 20918 * @since 4.5.000 (2008-12-31) 20919 */ 20920 protected function setImageBuffer($image, $data) { 20921 if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) { 20922 $this->imagekeys[$this->numimages] = $image; 20923 $data['i'] = $this->numimages; 20924 ++$this->numimages; 20925 } 20926 if ($this->diskcache) { 20927 if (!isset($this->images[$image])) { 20928 $this->images[$image] = TCPDF_STATIC::getObjFilename('img'); 20929 } 20930 $this->writeDiskCache($this->images[$image], $data, false, true); 20931 } else { 20932 $this->images[$image] = $data; 20933 } 20934 return $data['i']; 20935 } 20936 20937 /** 20938 * Set image buffer content for a specified sub-key. 20939 * @param $image (string) image key 20940 * @param $key (string) image sub-key 20941 * @param $data (array) image data 20942 * @protected 20943 * @since 4.5.000 (2008-12-31) 20944 */ 20945 protected function setImageSubBuffer($image, $key, $data) { 20946 if (!isset($this->images[$image])) { 20947 $this->setImageBuffer($image, array()); 20948 } 20949 if ($this->diskcache) { 20950 $tmpimg = $this->getImageBuffer($image); 20951 $tmpimg[$key] = $data; 20952 $this->writeDiskCache($this->images[$image], $tmpimg, false, true); 20953 } else { 20954 $this->images[$image][$key] = $data; 20955 } 20956 } 20957 20958 /** 20959 * Get image buffer content. 20960 * @param $image (string) image key 20961 * @return string image buffer content or false in case of error 20962 * @protected 20963 * @since 4.5.000 (2008-12-31) 20964 */ 20965 protected function getImageBuffer($image) { 20966 if ($this->diskcache AND isset($this->images[$image])) { 20967 return $this->readDiskCache($this->images[$image], true); 20968 } elseif (isset($this->images[$image])) { 20969 return $this->images[$image]; 20970 } 20971 return false; 20972 } 20973 20974 /** 20975 * Set font buffer content. 20976 * @param $font (string) font key 20977 * @param $data (array) font data 20978 * @protected 20979 * @since 4.5.000 (2009-01-02) 20980 */ 20981 protected function setFontBuffer($font, $data) { 20982 if ($this->diskcache) { 20983 if (!isset($this->fonts[$font])) { 20984 $this->fonts[$font] = TCPDF_STATIC::getObjFilename('font'); 20985 } 20986 $this->writeDiskCache($this->fonts[$font], $data, false, true); 20987 } else { 20988 $this->fonts[$font] = $data; 20989 } 20990 if (!in_array($font, $this->fontkeys)) { 20991 $this->fontkeys[] = $font; 20992 // store object ID for current font 20993 ++$this->n; 20994 $this->font_obj_ids[$font] = $this->n; 20995 $this->setFontSubBuffer($font, 'n', $this->n); 20996 } 20997 } 20998 20999 /** 21000 * Set font buffer content. 21001 * @param $font (string) font key 21002 * @param $key (string) font sub-key 21003 * @param $data (array) font data 21004 * @protected 21005 * @since 4.5.000 (2009-01-02) 21006 */ 21007 protected function setFontSubBuffer($font, $key, $data) { 21008 if (!isset($this->fonts[$font])) { 21009 $this->setFontBuffer($font, array()); 21010 } 21011 if ($this->diskcache) { 21012 $tmpfont = $this->getFontBuffer($font); 21013 $tmpfont[$key] = $data; 21014 $this->writeDiskCache($this->fonts[$font], $tmpfont, false, true); 21015 } else { 21016 $this->fonts[$font][$key] = $data; 21017 } 21018 } 21019 21020 /** 21021 * Get font buffer content. 21022 * @param $font (string) font key 21023 * @return string font buffer content or false in case of error 21024 * @protected 21025 * @since 4.5.000 (2009-01-02) 21026 */ 21027 protected function getFontBuffer($font) { 21028 if ($this->diskcache AND isset($this->fonts[$font])) { 21029 return $this->readDiskCache($this->fonts[$font], true); 21030 } elseif (isset($this->fonts[$font])) { 21031 return $this->fonts[$font]; 21032 } 21033 return false; 21034 } 21035 21036 /** 21037 * Move a page to a previous position. 21038 * @param $frompage (int) number of the source page 21039 * @param $topage (int) number of the destination page (must be less than $frompage) 21040 * @return true in case of success, false in case of error. 21041 * @public 21042 * @since 4.5.000 (2009-01-02) 21043 */ 21044 public function movePage($frompage, $topage) { 21045 if (($frompage > $this->numpages) OR ($frompage <= $topage)) { 21046 return false; 21047 } 21048 if ($frompage == $this->page) { 21049 // close the page before moving it 21050 $this->endPage(); 21051 } 21052 // move all page-related states 21053 $tmppage = $this->getPageBuffer($frompage); 21054 $tmppagedim = $this->pagedim[$frompage]; 21055 $tmppagelen = $this->pagelen[$frompage]; 21056 $tmpintmrk = $this->intmrk[$frompage]; 21057 $tmpbordermrk = $this->bordermrk[$frompage]; 21058 $tmpcntmrk = $this->cntmrk[$frompage]; 21059 $tmppageobjects = $this->pageobjects[$frompage]; 21060 if (isset($this->footerpos[$frompage])) { 21061 $tmpfooterpos = $this->footerpos[$frompage]; 21062 } 21063 if (isset($this->footerlen[$frompage])) { 21064 $tmpfooterlen = $this->footerlen[$frompage]; 21065 } 21066 if (isset($this->transfmrk[$frompage])) { 21067 $tmptransfmrk = $this->transfmrk[$frompage]; 21068 } 21069 if (isset($this->PageAnnots[$frompage])) { 21070 $tmpannots = $this->PageAnnots[$frompage]; 21071 } 21072 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { 21073 for ($i = $frompage; $i > $topage; --$i) { 21074 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) { 21075 --$this->pagegroups[$this->newpagegroup[$i]]; 21076 break; 21077 } 21078 } 21079 for ($i = $topage; $i > 0; --$i) { 21080 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) { 21081 ++$this->pagegroups[$this->newpagegroup[$i]]; 21082 break; 21083 } 21084 } 21085 } 21086 for ($i = $frompage; $i > $topage; --$i) { 21087 $j = $i - 1; 21088 // shift pages down 21089 $this->setPageBuffer($i, $this->getPageBuffer($j)); 21090 $this->pagedim[$i] = $this->pagedim[$j]; 21091 $this->pagelen[$i] = $this->pagelen[$j]; 21092 $this->intmrk[$i] = $this->intmrk[$j]; 21093 $this->bordermrk[$i] = $this->bordermrk[$j]; 21094 $this->cntmrk[$i] = $this->cntmrk[$j]; 21095 $this->pageobjects[$i] = $this->pageobjects[$j]; 21096 if (isset($this->footerpos[$j])) { 21097 $this->footerpos[$i] = $this->footerpos[$j]; 21098 } elseif (isset($this->footerpos[$i])) { 21099 unset($this->footerpos[$i]); 21100 } 21101 if (isset($this->footerlen[$j])) { 21102 $this->footerlen[$i] = $this->footerlen[$j]; 21103 } elseif (isset($this->footerlen[$i])) { 21104 unset($this->footerlen[$i]); 21105 } 21106 if (isset($this->transfmrk[$j])) { 21107 $this->transfmrk[$i] = $this->transfmrk[$j]; 21108 } elseif (isset($this->transfmrk[$i])) { 21109 unset($this->transfmrk[$i]); 21110 } 21111 if (isset($this->PageAnnots[$j])) { 21112 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 21113 } elseif (isset($this->PageAnnots[$i])) { 21114 unset($this->PageAnnots[$i]); 21115 } 21116 if (isset($this->newpagegroup[$j])) { 21117 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 21118 unset($this->newpagegroup[$j]); 21119 } 21120 if ($this->currpagegroup == $j) { 21121 $this->currpagegroup = $i; 21122 } 21123 } 21124 $this->setPageBuffer($topage, $tmppage); 21125 $this->pagedim[$topage] = $tmppagedim; 21126 $this->pagelen[$topage] = $tmppagelen; 21127 $this->intmrk[$topage] = $tmpintmrk; 21128 $this->bordermrk[$topage] = $tmpbordermrk; 21129 $this->cntmrk[$topage] = $tmpcntmrk; 21130 $this->pageobjects[$topage] = $tmppageobjects; 21131 if (isset($tmpfooterpos)) { 21132 $this->footerpos[$topage] = $tmpfooterpos; 21133 } elseif (isset($this->footerpos[$topage])) { 21134 unset($this->footerpos[$topage]); 21135 } 21136 if (isset($tmpfooterlen)) { 21137 $this->footerlen[$topage] = $tmpfooterlen; 21138 } elseif (isset($this->footerlen[$topage])) { 21139 unset($this->footerlen[$topage]); 21140 } 21141 if (isset($tmptransfmrk)) { 21142 $this->transfmrk[$topage] = $tmptransfmrk; 21143 } elseif (isset($this->transfmrk[$topage])) { 21144 unset($this->transfmrk[$topage]); 21145 } 21146 if (isset($tmpannots)) { 21147 $this->PageAnnots[$topage] = $tmpannots; 21148 } elseif (isset($this->PageAnnots[$topage])) { 21149 unset($this->PageAnnots[$topage]); 21150 } 21151 // adjust outlines 21152 $tmpoutlines = $this->outlines; 21153 foreach ($tmpoutlines as $key => $outline) { 21154 if (!$outline['f']) { 21155 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) { 21156 $this->outlines[$key]['p'] = ($outline['p'] + 1); 21157 } elseif ($outline['p'] == $frompage) { 21158 $this->outlines[$key]['p'] = $topage; 21159 } 21160 } 21161 } 21162 // adjust dests 21163 $tmpdests = $this->dests; 21164 foreach ($tmpdests as $key => $dest) { 21165 if (!$dest['f']) { 21166 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) { 21167 $this->dests[$key]['p'] = ($dest['p'] + 1); 21168 } elseif ($dest['p'] == $frompage) { 21169 $this->dests[$key]['p'] = $topage; 21170 } 21171 } 21172 } 21173 // adjust links 21174 $tmplinks = $this->links; 21175 foreach ($tmplinks as $key => $link) { 21176 if (!$link['f']) { 21177 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) { 21178 $this->links[$key]['p'] = ($link['p'] + 1); 21179 } elseif ($link['p'] == $frompage) { 21180 $this->links[$key]['p'] = $topage; 21181 } 21182 } 21183 } 21184 // adjust javascript 21185 $jfrompage = $frompage; 21186 $jtopage = $topage; 21187 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) { 21188 foreach($pamatch[0] as $pk => $pmatch) { 21189 $pagenum = intval($pamatch[3][$pk]) + 1; 21190 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) { 21191 $newpage = ($pagenum + 1); 21192 } elseif ($pagenum == $jfrompage) { 21193 $newpage = $jtopage; 21194 } else { 21195 $newpage = $pagenum; 21196 } 21197 --$newpage; 21198 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage; 21199 $this->javascript = str_replace($pmatch, $newjs, $this->javascript); 21200 } 21201 unset($pamatch); 21202 } 21203 // return to last page 21204 $this->lastPage(true); 21205 return true; 21206 } 21207 21208 /** 21209 * Remove the specified page. 21210 * @param $page (int) page to remove 21211 * @return true in case of success, false in case of error. 21212 * @public 21213 * @since 4.6.004 (2009-04-23) 21214 */ 21215 public function deletePage($page) { 21216 if (($page < 1) OR ($page > $this->numpages)) { 21217 return false; 21218 } 21219 // delete current page 21220 unset($this->pages[$page]); 21221 unset($this->pagedim[$page]); 21222 unset($this->pagelen[$page]); 21223 unset($this->intmrk[$page]); 21224 unset($this->bordermrk[$page]); 21225 unset($this->cntmrk[$page]); 21226 foreach ($this->pageobjects[$page] as $oid) { 21227 if (isset($this->offsets[$oid])){ 21228 unset($this->offsets[$oid]); 21229 } 21230 } 21231 unset($this->pageobjects[$page]); 21232 if (isset($this->footerpos[$page])) { 21233 unset($this->footerpos[$page]); 21234 } 21235 if (isset($this->footerlen[$page])) { 21236 unset($this->footerlen[$page]); 21237 } 21238 if (isset($this->transfmrk[$page])) { 21239 unset($this->transfmrk[$page]); 21240 } 21241 if (isset($this->PageAnnots[$page])) { 21242 unset($this->PageAnnots[$page]); 21243 } 21244 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { 21245 for ($i = $page; $i > 0; --$i) { 21246 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) { 21247 --$this->pagegroups[$this->newpagegroup[$i]]; 21248 break; 21249 } 21250 } 21251 } 21252 if (isset($this->pageopen[$page])) { 21253 unset($this->pageopen[$page]); 21254 } 21255 if ($page < $this->numpages) { 21256 // update remaining pages 21257 for ($i = $page; $i < $this->numpages; ++$i) { 21258 $j = $i + 1; 21259 // shift pages 21260 $this->setPageBuffer($i, $this->getPageBuffer($j)); 21261 $this->pagedim[$i] = $this->pagedim[$j]; 21262 $this->pagelen[$i] = $this->pagelen[$j]; 21263 $this->intmrk[$i] = $this->intmrk[$j]; 21264 $this->bordermrk[$i] = $this->bordermrk[$j]; 21265 $this->cntmrk[$i] = $this->cntmrk[$j]; 21266 $this->pageobjects[$i] = $this->pageobjects[$j]; 21267 if (isset($this->footerpos[$j])) { 21268 $this->footerpos[$i] = $this->footerpos[$j]; 21269 } elseif (isset($this->footerpos[$i])) { 21270 unset($this->footerpos[$i]); 21271 } 21272 if (isset($this->footerlen[$j])) { 21273 $this->footerlen[$i] = $this->footerlen[$j]; 21274 } elseif (isset($this->footerlen[$i])) { 21275 unset($this->footerlen[$i]); 21276 } 21277 if (isset($this->transfmrk[$j])) { 21278 $this->transfmrk[$i] = $this->transfmrk[$j]; 21279 } elseif (isset($this->transfmrk[$i])) { 21280 unset($this->transfmrk[$i]); 21281 } 21282 if (isset($this->PageAnnots[$j])) { 21283 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 21284 } elseif (isset($this->PageAnnots[$i])) { 21285 unset($this->PageAnnots[$i]); 21286 } 21287 if (isset($this->newpagegroup[$j])) { 21288 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 21289 unset($this->newpagegroup[$j]); 21290 } 21291 if ($this->currpagegroup == $j) { 21292 $this->currpagegroup = $i; 21293 } 21294 if (isset($this->pageopen[$j])) { 21295 $this->pageopen[$i] = $this->pageopen[$j]; 21296 } elseif (isset($this->pageopen[$i])) { 21297 unset($this->pageopen[$i]); 21298 } 21299 } 21300 // remove last page 21301 unset($this->pages[$this->numpages]); 21302 unset($this->pagedim[$this->numpages]); 21303 unset($this->pagelen[$this->numpages]); 21304 unset($this->intmrk[$this->numpages]); 21305 unset($this->bordermrk[$this->numpages]); 21306 unset($this->cntmrk[$this->numpages]); 21307 foreach ($this->pageobjects[$this->numpages] as $oid) { 21308 if (isset($this->offsets[$oid])){ 21309 unset($this->offsets[$oid]); 21310 } 21311 } 21312 unset($this->pageobjects[$this->numpages]); 21313 if (isset($this->footerpos[$this->numpages])) { 21314 unset($this->footerpos[$this->numpages]); 21315 } 21316 if (isset($this->footerlen[$this->numpages])) { 21317 unset($this->footerlen[$this->numpages]); 21318 } 21319 if (isset($this->transfmrk[$this->numpages])) { 21320 unset($this->transfmrk[$this->numpages]); 21321 } 21322 if (isset($this->PageAnnots[$this->numpages])) { 21323 unset($this->PageAnnots[$this->numpages]); 21324 } 21325 if (isset($this->newpagegroup[$this->numpages])) { 21326 unset($this->newpagegroup[$this->numpages]); 21327 } 21328 if ($this->currpagegroup == $this->numpages) { 21329 $this->currpagegroup = ($this->numpages - 1); 21330 } 21331 if (isset($this->pagegroups[$this->numpages])) { 21332 unset($this->pagegroups[$this->numpages]); 21333 } 21334 if (isset($this->pageopen[$this->numpages])) { 21335 unset($this->pageopen[$this->numpages]); 21336 } 21337 } 21338 --$this->numpages; 21339 $this->page = $this->numpages; 21340 // adjust outlines 21341 $tmpoutlines = $this->outlines; 21342 foreach ($tmpoutlines as $key => $outline) { 21343 if (!$outline['f']) { 21344 if ($outline['p'] > $page) { 21345 $this->outlines[$key]['p'] = $outline['p'] - 1; 21346 } elseif ($outline['p'] == $page) { 21347 unset($this->outlines[$key]); 21348 } 21349 } 21350 } 21351 // adjust dests 21352 $tmpdests = $this->dests; 21353 foreach ($tmpdests as $key => $dest) { 21354 if (!$dest['f']) { 21355 if ($dest['p'] > $page) { 21356 $this->dests[$key]['p'] = $dest['p'] - 1; 21357 } elseif ($dest['p'] == $page) { 21358 unset($this->dests[$key]); 21359 } 21360 } 21361 } 21362 // adjust links 21363 $tmplinks = $this->links; 21364 foreach ($tmplinks as $key => $link) { 21365 if (!$link['f']) { 21366 if ($link['p'] > $page) { 21367 $this->links[$key]['p'] = $link['p'] - 1; 21368 } elseif ($link['p'] == $page) { 21369 unset($this->links[$key]); 21370 } 21371 } 21372 } 21373 // adjust javascript 21374 $jpage = $page; 21375 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) { 21376 foreach($pamatch[0] as $pk => $pmatch) { 21377 $pagenum = intval($pamatch[3][$pk]) + 1; 21378 if ($pagenum >= $jpage) { 21379 $newpage = ($pagenum - 1); 21380 } elseif ($pagenum == $jpage) { 21381 $newpage = 1; 21382 } else { 21383 $newpage = $pagenum; 21384 } 21385 --$newpage; 21386 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage; 21387 $this->javascript = str_replace($pmatch, $newjs, $this->javascript); 21388 } 21389 unset($pamatch); 21390 } 21391 // return to last page 21392 if ($this->numpages > 0) { 21393 $this->lastPage(true); 21394 } 21395 return true; 21396 } 21397 21398 /** 21399 * Clone the specified page to a new page. 21400 * @param $page (int) number of page to copy (0 = current page) 21401 * @return true in case of success, false in case of error. 21402 * @public 21403 * @since 4.9.015 (2010-04-20) 21404 */ 21405 public function copyPage($page=0) { 21406 if ($page == 0) { 21407 // default value 21408 $page = $this->page; 21409 } 21410 if (($page < 1) OR ($page > $this->numpages)) { 21411 return false; 21412 } 21413 // close the last page 21414 $this->endPage(); 21415 // copy all page-related states 21416 ++$this->numpages; 21417 $this->page = $this->numpages; 21418 $this->setPageBuffer($this->page, $this->getPageBuffer($page)); 21419 $this->pagedim[$this->page] = $this->pagedim[$page]; 21420 $this->pagelen[$this->page] = $this->pagelen[$page]; 21421 $this->intmrk[$this->page] = $this->intmrk[$page]; 21422 $this->bordermrk[$this->page] = $this->bordermrk[$page]; 21423 $this->cntmrk[$this->page] = $this->cntmrk[$page]; 21424 $this->pageobjects[$this->page] = $this->pageobjects[$page]; 21425 $this->pageopen[$this->page] = false; 21426 if (isset($this->footerpos[$page])) { 21427 $this->footerpos[$this->page] = $this->footerpos[$page]; 21428 } 21429 if (isset($this->footerlen[$page])) { 21430 $this->footerlen[$this->page] = $this->footerlen[$page]; 21431 } 21432 if (isset($this->transfmrk[$page])) { 21433 $this->transfmrk[$this->page] = $this->transfmrk[$page]; 21434 } 21435 if (isset($this->PageAnnots[$page])) { 21436 $this->PageAnnots[$this->page] = $this->PageAnnots[$page]; 21437 } 21438 if (isset($this->newpagegroup[$page])) { 21439 // start a new group 21440 $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1; 21441 $this->currpagegroup = $this->newpagegroup[$this->page]; 21442 $this->pagegroups[$this->currpagegroup] = 1; 21443 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { 21444 ++$this->pagegroups[$this->currpagegroup]; 21445 } 21446 // copy outlines 21447 $tmpoutlines = $this->outlines; 21448 foreach ($tmpoutlines as $key => $outline) { 21449 if ($outline['p'] == $page) { 21450 $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 'f' => $outline['f'], 's' => $outline['s'], 'c' => $outline['c']); 21451 } 21452 } 21453 // copy links 21454 $tmplinks = $this->links; 21455 foreach ($tmplinks as $key => $link) { 21456 if ($link['p'] == $page) { 21457 $this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']); 21458 } 21459 } 21460 // return to last page 21461 $this->lastPage(true); 21462 return true; 21463 } 21464 21465 /** 21466 * Output a Table of Content Index (TOC). 21467 * This method must be called after all Bookmarks were set. 21468 * Before calling this method you have to open the page using the addTOCPage() method. 21469 * After calling this method you have to call endTOCPage() to close the TOC page. 21470 * You can override this method to achieve different styles. 21471 * @param $page (int) page number where this TOC should be inserted (leave empty for current page). 21472 * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment). 21473 * @param $filler (string) string used to fill the space between text and page number. 21474 * @param $toc_name (string) name to use for TOC bookmark. 21475 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic. 21476 * @param $color (array) RGB color array for bookmark title (values from 0 to 255). 21477 * @public 21478 * @author Nicola Asuni 21479 * @since 4.5.000 (2009-01-02) 21480 * @see addTOCPage(), endTOCPage(), addHTMLTOC() 21481 */ 21482 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) { 21483 $fontsize = $this->FontSizePt; 21484 $fontfamily = $this->FontFamily; 21485 $fontstyle = $this->FontStyle; 21486 $w = $this->w - $this->lMargin - $this->rMargin; 21487 $spacer = $this->GetStringWidth(chr(32)) * 4; 21488 $lmargin = $this->lMargin; 21489 $rmargin = $this->rMargin; 21490 $x_start = $this->GetX(); 21491 $page_first = $this->page; 21492 $current_page = $this->page; 21493 $page_fill_start = false; 21494 $page_fill_end = false; 21495 $current_column = $this->current_column; 21496 if (TCPDF_STATIC::empty_string($numbersfont)) { 21497 $numbersfont = $this->default_monospaced_font; 21498 } 21499 if (TCPDF_STATIC::empty_string($filler)) { 21500 $filler = ' '; 21501 } 21502 if (TCPDF_STATIC::empty_string($page)) { 21503 $gap = ' '; 21504 } else { 21505 $gap = ''; 21506 if ($page < 1) { 21507 $page = 1; 21508 } 21509 } 21510 $this->SetFont($numbersfont, $fontstyle, $fontsize); 21511 $numwidth = $this->GetStringWidth('00000'); 21512 $maxpage = 0; //used for pages on attached documents 21513 foreach ($this->outlines as $key => $outline) { 21514 // check for extra pages (used for attachments) 21515 if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) { 21516 $outline['p'] += ($this->page - $page_first); 21517 } 21518 if ($this->rtl) { 21519 $aligntext = 'R'; 21520 $alignnum = 'L'; 21521 } else { 21522 $aligntext = 'L'; 21523 $alignnum = 'R'; 21524 } 21525 if ($outline['l'] == 0) { 21526 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize); 21527 } else { 21528 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']); 21529 } 21530 $this->SetTextColorArray($outline['c']); 21531 // check for page break 21532 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize)); 21533 // set margins and X position 21534 if (($this->page == $current_page) AND ($this->current_column == $current_column)) { 21535 $this->lMargin = $lmargin; 21536 $this->rMargin = $rmargin; 21537 } else { 21538 if ($this->current_column != $current_column) { 21539 if ($this->rtl) { 21540 $x_start = $this->w - $this->columns[$this->current_column]['x']; 21541 } else { 21542 $x_start = $this->columns[$this->current_column]['x']; 21543 } 21544 } 21545 $lmargin = $this->lMargin; 21546 $rmargin = $this->rMargin; 21547 $current_page = $this->page; 21548 $current_column = $this->current_column; 21549 } 21550 $this->SetX($x_start); 21551 $indent = ($spacer * $outline['l']); 21552 if ($this->rtl) { 21553 $this->x -= $indent; 21554 $this->rMargin = $this->w - $this->x; 21555 } else { 21556 $this->x += $indent; 21557 $this->lMargin = $this->x; 21558 } 21559 $link = $this->AddLink(); 21560 $this->SetLink($link, $outline['y'], $outline['p']); 21561 // write the text 21562 if ($this->rtl) { 21563 $txt = ' '.$outline['t']; 21564 } else { 21565 $txt = $outline['t'].' '; 21566 } 21567 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, ''); 21568 if ($this->rtl) { 21569 $tw = $this->x - $this->lMargin; 21570 } else { 21571 $tw = $this->w - $this->rMargin - $this->x; 21572 } 21573 $this->SetFont($numbersfont, $fontstyle, $fontsize); 21574 if (TCPDF_STATIC::empty_string($page)) { 21575 $pagenum = $outline['p']; 21576 } else { 21577 // placemark to be replaced with the correct number 21578 $pagenum = '{#'.($outline['p']).'}'; 21579 if ($this->isUnicodeFont()) { 21580 $pagenum = '{'.$pagenum.'}'; 21581 } 21582 $maxpage = max($maxpage, $outline['p']); 21583 } 21584 $fw = ($tw - $this->GetStringWidth($pagenum.$filler)); 21585 $wfiller = $this->GetStringWidth($filler); 21586 if ($wfiller > 0) { 21587 $numfills = floor($fw / $wfiller); 21588 } else { 21589 $numfills = 0; 21590 } 21591 if ($numfills > 0) { 21592 $rowfill = str_repeat($filler, $numfills); 21593 } else { 21594 $rowfill = ''; 21595 } 21596 if ($this->rtl) { 21597 $pagenum = $pagenum.$gap.$rowfill; 21598 } else { 21599 $pagenum = $rowfill.$gap.$pagenum; 21600 } 21601 // write the number 21602 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0); 21603 } 21604 $page_last = $this->getPage(); 21605 $numpages = ($page_last - $page_first + 1); 21606 // account for booklet mode 21607 if ($this->booklet) { 21608 // check if a blank page is required before TOC 21609 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0)); 21610 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start))); 21611 if ($page_fill_start) { 21612 // add a page at the end (to be moved before TOC) 21613 $this->addPage(); 21614 ++$page_last; 21615 ++$numpages; 21616 } 21617 if ($page_fill_end) { 21618 // add a page at the end 21619 $this->addPage(); 21620 ++$page_last; 21621 ++$numpages; 21622 } 21623 } 21624 $maxpage = max($maxpage, $page_last); 21625 if (!TCPDF_STATIC::empty_string($page)) { 21626 for ($p = $page_first; $p <= $page_last; ++$p) { 21627 // get page data 21628 $temppage = $this->getPageBuffer($p); 21629 for ($n = 1; $n <= $maxpage; ++$n) { 21630 // update page numbers 21631 $a = '{#'.$n.'}'; 21632 // get page number aliases 21633 $pnalias = $this->getInternalPageNumberAliases($a); 21634 // calculate replacement number 21635 if (($n >= $page) AND ($n <= $this->numpages)) { 21636 $np = $n + $numpages; 21637 } else { 21638 $np = $n; 21639 } 21640 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1)); 21641 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont); 21642 // replace aliases with numbers 21643 foreach ($pnalias['u'] as $u) { 21644 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' ')))); 21645 if ($this->rtl) { 21646 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont); 21647 } else { 21648 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu; 21649 } 21650 $temppage = str_replace($u, $nr, $temppage); 21651 } 21652 foreach ($pnalias['a'] as $a) { 21653 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' ')))); 21654 if ($this->rtl) { 21655 $nr = $na.' '.$sfill; 21656 } else { 21657 $nr = $sfill.' '.$na; 21658 } 21659 $temppage = str_replace($a, $nr, $temppage); 21660 } 21661 } 21662 // save changes 21663 $this->setPageBuffer($p, $temppage); 21664 } 21665 // move pages 21666 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); 21667 if ($page_fill_start) { 21668 $this->movePage($page_last, $page_first); 21669 } 21670 for ($i = 0; $i < $numpages; ++$i) { 21671 $this->movePage($page_last, $page); 21672 } 21673 } 21674 } 21675 21676 /** 21677 * Output a Table Of Content Index (TOC) using HTML templates. 21678 * This method must be called after all Bookmarks were set. 21679 * Before calling this method you have to open the page using the addTOCPage() method. 21680 * After calling this method you have to call endTOCPage() to close the TOC page. 21681 * @param $page (int) page number where this TOC should be inserted (leave empty for current page). 21682 * @param $toc_name (string) name to use for TOC bookmark. 21683 * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number. 21684 * @param $correct_align (boolean) if true correct the number alignment (numbers must be in monospaced font like courier and right aligned on LTR, or left aligned on RTL) 21685 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic. 21686 * @param $color (array) RGB color array for title (values from 0 to 255). 21687 * @public 21688 * @author Nicola Asuni 21689 * @since 5.0.001 (2010-05-06) 21690 * @see addTOCPage(), endTOCPage(), addTOC() 21691 */ 21692 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) { 21693 $filler = ' '; 21694 $prev_htmlLinkColorArray = $this->htmlLinkColorArray; 21695 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle; 21696 // set new style for link 21697 $this->htmlLinkColorArray = array(); 21698 $this->htmlLinkFontStyle = ''; 21699 $page_first = $this->getPage(); 21700 $page_fill_start = false; 21701 $page_fill_end = false; 21702 // get the font type used for numbers in each template 21703 $current_font = $this->FontFamily; 21704 foreach ($templates as $level => $html) { 21705 $dom = $this->getHtmlDomArray($html); 21706 foreach ($dom as $key => $value) { 21707 if ($value['value'] == '#TOC_PAGE_NUMBER#') { 21708 $this->SetFont($dom[($key - 1)]['fontname']); 21709 $templates['F'.$level] = $this->isUnicodeFont(); 21710 } 21711 } 21712 } 21713 $this->SetFont($current_font); 21714 $maxpage = 0; //used for pages on attached documents 21715 foreach ($this->outlines as $key => $outline) { 21716 // get HTML template 21717 $row = $templates[$outline['l']]; 21718 if (TCPDF_STATIC::empty_string($page)) { 21719 $pagenum = $outline['p']; 21720 } else { 21721 // placemark to be replaced with the correct number 21722 $pagenum = '{#'.($outline['p']).'}'; 21723 if ($templates['F'.$outline['l']]) { 21724 $pagenum = '{'.$pagenum.'}'; 21725 } 21726 $maxpage = max($maxpage, $outline['p']); 21727 } 21728 // replace templates with current values 21729 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row); 21730 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row); 21731 // add link to page 21732 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>'; 21733 // write bookmark entry 21734 $this->writeHTML($row, false, false, true, false, ''); 21735 } 21736 // restore link styles 21737 $this->htmlLinkColorArray = $prev_htmlLinkColorArray; 21738 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle; 21739 // move TOC page and replace numbers 21740 $page_last = $this->getPage(); 21741 $numpages = ($page_last - $page_first + 1); 21742 // account for booklet mode 21743 if ($this->booklet) { 21744 // check if a blank page is required before TOC 21745 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0)); 21746 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start))); 21747 if ($page_fill_start) { 21748 // add a page at the end (to be moved before TOC) 21749 $this->addPage(); 21750 ++$page_last; 21751 ++$numpages; 21752 } 21753 if ($page_fill_end) { 21754 // add a page at the end 21755 $this->addPage(); 21756 ++$page_last; 21757 ++$numpages; 21758 } 21759 } 21760 $maxpage = max($maxpage, $page_last); 21761 if (!TCPDF_STATIC::empty_string($page)) { 21762 for ($p = $page_first; $p <= $page_last; ++$p) { 21763 // get page data 21764 $temppage = $this->getPageBuffer($p); 21765 for ($n = 1; $n <= $maxpage; ++$n) { 21766 // update page numbers 21767 $a = '{#'.$n.'}'; 21768 // get page number aliases 21769 $pnalias = $this->getInternalPageNumberAliases($a); 21770 // calculate replacement number 21771 if ($n >= $page) { 21772 $np = $n + $numpages; 21773 } else { 21774 $np = $n; 21775 } 21776 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1)); 21777 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont); 21778 // replace aliases with numbers 21779 foreach ($pnalias['u'] as $u) { 21780 if ($correct_align) { 21781 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' '))); 21782 if ($this->rtl) { 21783 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont); 21784 } else { 21785 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu; 21786 } 21787 } else { 21788 $nr = $nu; 21789 } 21790 $temppage = str_replace($u, $nr, $temppage); 21791 } 21792 foreach ($pnalias['a'] as $a) { 21793 if ($correct_align) { 21794 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' '))); 21795 if ($this->rtl) { 21796 $nr = $na.' '.$sfill; 21797 } else { 21798 $nr = $sfill.' '.$na; 21799 } 21800 } else { 21801 $nr = $na; 21802 } 21803 $temppage = str_replace($a, $nr, $temppage); 21804 } 21805 } 21806 // save changes 21807 $this->setPageBuffer($p, $temppage); 21808 } 21809 // move pages 21810 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); 21811 if ($page_fill_start) { 21812 $this->movePage($page_last, $page_first); 21813 } 21814 for ($i = 0; $i < $numpages; ++$i) { 21815 $this->movePage($page_last, $page); 21816 } 21817 } 21818 } 21819 21820 /** 21821 * Stores a copy of the current TCPDF object used for undo operation. 21822 * @public 21823 * @since 4.5.029 (2009-03-19) 21824 */ 21825 public function startTransaction() { 21826 if (isset($this->objcopy)) { 21827 // remove previous copy 21828 $this->commitTransaction(); 21829 } 21830 // record current page number and Y position 21831 $this->start_transaction_page = $this->page; 21832 $this->start_transaction_y = $this->y; 21833 // clone current object 21834 $this->objcopy = TCPDF_STATIC::objclone($this); 21835 } 21836 21837 /** 21838 * Delete the copy of the current TCPDF object used for undo operation. 21839 * @public 21840 * @since 4.5.029 (2009-03-19) 21841 */ 21842 public function commitTransaction() { 21843 if (isset($this->objcopy)) { 21844 $this->objcopy->_destroy(true, true); 21845 unset($this->objcopy); 21846 } 21847 } 21848 21849 /** 21850 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction(). 21851 * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value. 21852 * @return TCPDF object. 21853 * @public 21854 * @since 4.5.029 (2009-03-19) 21855 */ 21856 public function rollbackTransaction($self=false) { 21857 if (isset($this->objcopy)) { 21858 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) { 21859 // truncate files to previous values 21860 foreach ($this->objcopy->cache_file_length as $file => $length) { 21861 $file = substr($file, 1); 21862 $handle = fopen($file, 'r+'); 21863 ftruncate($handle, $length); 21864 } 21865 } 21866 $this->_destroy(true, true); 21867 if ($self) { 21868 $objvars = get_object_vars($this->objcopy); 21869 foreach ($objvars as $key => $value) { 21870 $this->$key = $value; 21871 } 21872 } 21873 return $this->objcopy; 21874 } 21875 return $this; 21876 } 21877 21878 // --- MULTI COLUMNS METHODS ----------------------- 21879 21880 /** 21881 * Set multiple columns of the same size 21882 * @param $numcols (int) number of columns (set to zero to disable columns mode) 21883 * @param $width (int) column width 21884 * @param $y (int) column starting Y position (leave empty for current Y position) 21885 * @public 21886 * @since 4.9.001 (2010-03-28) 21887 */ 21888 public function setEqualColumns($numcols=0, $width=0, $y='') { 21889 $this->columns = array(); 21890 if ($numcols < 2) { 21891 $numcols = 0; 21892 $this->columns = array(); 21893 } else { 21894 // maximum column width 21895 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols; 21896 if (($width == 0) OR ($width > $maxwidth)) { 21897 $width = $maxwidth; 21898 } 21899 if (TCPDF_STATIC::empty_string($y)) { 21900 $y = $this->y; 21901 } 21902 // space between columns 21903 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1)); 21904 // fill the columns array (with, space, starting Y position) 21905 for ($i = 0; $i < $numcols; ++$i) { 21906 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y); 21907 } 21908 } 21909 $this->num_columns = $numcols; 21910 $this->current_column = 0; 21911 $this->column_start_page = $this->page; 21912 $this->selectColumn(0); 21913 } 21914 21915 /** 21916 * Remove columns and reset page margins. 21917 * @public 21918 * @since 5.9.072 (2011-04-26) 21919 */ 21920 public function resetColumns() { 21921 $this->lMargin = $this->original_lMargin; 21922 $this->rMargin = $this->original_rMargin; 21923 $this->setEqualColumns(); 21924 } 21925 21926 /** 21927 * Set columns array. 21928 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position). 21929 * @param $columns (array) 21930 * @public 21931 * @since 4.9.001 (2010-03-28) 21932 */ 21933 public function setColumnsArray($columns) { 21934 $this->columns = $columns; 21935 $this->num_columns = count($columns); 21936 $this->current_column = 0; 21937 $this->column_start_page = $this->page; 21938 $this->selectColumn(0); 21939 } 21940 21941 /** 21942 * Set position at a given column 21943 * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column. 21944 * @public 21945 * @since 4.9.001 (2010-03-28) 21946 */ 21947 public function selectColumn($col='') { 21948 if (is_string($col)) { 21949 $col = $this->current_column; 21950 } elseif ($col >= $this->num_columns) { 21951 $col = 0; 21952 } 21953 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 21954 $enable_thead = false; 21955 if ($this->num_columns > 1) { 21956 if ($col != $this->current_column) { 21957 // move Y pointer at the top of the column 21958 if ($this->column_start_page == $this->page) { 21959 $this->y = $this->columns[$col]['y']; 21960 } else { 21961 $this->y = $this->tMargin; 21962 } 21963 // Avoid to write table headers more than once 21964 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) { 21965 $enable_thead = true; 21966 $this->maxselcol['page'] = $this->page; 21967 $this->maxselcol['column'] = $col; 21968 } 21969 } 21970 $xshift = $this->colxshift; 21971 // set X position of the current column by case 21972 $listindent = ($this->listindentlevel * $this->listindent); 21973 // calculate column X position 21974 $colpos = 0; 21975 for ($i = 0; $i < $col; ++$i) { 21976 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']); 21977 } 21978 if ($this->rtl) { 21979 $x = $this->w - $this->original_rMargin - $colpos; 21980 $this->rMargin = ($this->w - $x + $listindent); 21981 $this->lMargin = ($x - $this->columns[$col]['w']); 21982 $this->x = $x - $listindent; 21983 } else { 21984 $x = $this->original_lMargin + $colpos; 21985 $this->lMargin = ($x + $listindent); 21986 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']); 21987 $this->x = $x + $listindent; 21988 } 21989 $this->columns[$col]['x'] = $x; 21990 } 21991 $this->current_column = $col; 21992 // fix for HTML mode 21993 $this->newline = true; 21994 // print HTML table header (if any) 21995 if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) { 21996 if ($enable_thead) { 21997 // print table header 21998 $this->writeHTML($this->thead, false, false, false, false, ''); 21999 $this->y += $xshift['s']['V']; 22000 // store end of header position 22001 if (!isset($this->columns[$col]['th'])) { 22002 $this->columns[$col]['th'] = array(); 22003 } 22004 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y; 22005 $this->lasth = 0; 22006 } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) { 22007 $this->y = $this->columns[$col]['th']['\''.$this->page.'\'']; 22008 } 22009 } 22010 // account for an html table cell over multiple columns 22011 if ($this->rtl) { 22012 $this->rMargin += $xshift['x']; 22013 $this->x -= ($xshift['x'] + $xshift['p']['R']); 22014 } else { 22015 $this->lMargin += $xshift['x']; 22016 $this->x += $xshift['x'] + $xshift['p']['L']; 22017 } 22018 } 22019 22020 /** 22021 * Return the current column number 22022 * @return int current column number 22023 * @public 22024 * @since 5.5.011 (2010-07-08) 22025 */ 22026 public function getColumn() { 22027 return $this->current_column; 22028 } 22029 22030 /** 22031 * Return the current number of columns. 22032 * @return int number of columns 22033 * @public 22034 * @since 5.8.018 (2010-08-25) 22035 */ 22036 public function getNumberOfColumns() { 22037 return $this->num_columns; 22038 } 22039 22040 /** 22041 * Set Text rendering mode. 22042 * @param $stroke (int) outline size in user units (0 = disable). 22043 * @param $fill (boolean) if true fills the text (default). 22044 * @param $clip (boolean) if true activate clipping mode 22045 * @public 22046 * @since 4.9.008 (2009-04-02) 22047 */ 22048 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) { 22049 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode 22050 // convert text rendering parameters 22051 if ($stroke < 0) { 22052 $stroke = 0; 22053 } 22054 if ($fill === true) { 22055 if ($stroke > 0) { 22056 if ($clip === true) { 22057 // Fill, then stroke text and add to path for clipping 22058 $textrendermode = 6; 22059 } else { 22060 // Fill, then stroke text 22061 $textrendermode = 2; 22062 } 22063 $textstrokewidth = $stroke; 22064 } else { 22065 if ($clip === true) { 22066 // Fill text and add to path for clipping 22067 $textrendermode = 4; 22068 } else { 22069 // Fill text 22070 $textrendermode = 0; 22071 } 22072 } 22073 } else { 22074 if ($stroke > 0) { 22075 if ($clip === true) { 22076 // Stroke text and add to path for clipping 22077 $textrendermode = 5; 22078 } else { 22079 // Stroke text 22080 $textrendermode = 1; 22081 } 22082 $textstrokewidth = $stroke; 22083 } else { 22084 if ($clip === true) { 22085 // Add text to path for clipping 22086 $textrendermode = 7; 22087 } else { 22088 // Neither fill nor stroke text (invisible) 22089 $textrendermode = 3; 22090 } 22091 } 22092 } 22093 $this->textrendermode = $textrendermode; 22094 $this->textstrokewidth = $stroke; 22095 } 22096 22097 /** 22098 * Set parameters for drop shadow effect for text. 22099 * @param $params (array) Array of parameters: enabled (boolean) set to true to enable shadow; depth_w (float) shadow width in user units; depth_h (float) shadow height in user units; color (array) shadow color or false to use the stroke color; opacity (float) Alpha value: real value from 0 (transparent) to 1 (opaque); blend_mode (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity. 22100 * @since 5.9.174 (2012-07-25) 22101 * @public 22102 */ 22103 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) { 22104 if (isset($params['enabled'])) { 22105 $this->txtshadow['enabled'] = $params['enabled']?true:false; 22106 } else { 22107 $this->txtshadow['enabled'] = false; 22108 } 22109 if (isset($params['depth_w'])) { 22110 $this->txtshadow['depth_w'] = floatval($params['depth_w']); 22111 } else { 22112 $this->txtshadow['depth_w'] = 0; 22113 } 22114 if (isset($params['depth_h'])) { 22115 $this->txtshadow['depth_h'] = floatval($params['depth_h']); 22116 } else { 22117 $this->txtshadow['depth_h'] = 0; 22118 } 22119 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) { 22120 $this->txtshadow['color'] = $params['color']; 22121 } else { 22122 $this->txtshadow['color'] = $this->strokecolor; 22123 } 22124 if (isset($params['opacity'])) { 22125 $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity']))); 22126 } else { 22127 $this->txtshadow['opacity'] = 1; 22128 } 22129 if (isset($params['blend_mode']) AND in_array($params['blend_mode'], array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) { 22130 $this->txtshadow['blend_mode'] = $params['blend_mode']; 22131 } else { 22132 $this->txtshadow['blend_mode'] = 'Normal'; 22133 } 22134 if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) { 22135 $this->txtshadow['enabled'] = false; 22136 } 22137 } 22138 22139 /** 22140 * Return the text shadow parameters array. 22141 * @return Array of parameters. 22142 * @since 5.9.174 (2012-07-25) 22143 * @public 22144 */ 22145 public function getTextShadow() { 22146 return $this->txtshadow; 22147 } 22148 22149 /** 22150 * Returns an array of chars containing soft hyphens. 22151 * @param $word (array) array of chars 22152 * @param $patterns (array) Array of hypenation patterns. 22153 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm. 22154 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens. 22155 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens. 22156 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm. 22157 * @param $charmax (int) Maximum length of broken piece of word. 22158 * @return array text with soft hyphens 22159 * @author Nicola Asuni 22160 * @since 4.9.012 (2010-04-12) 22161 * @protected 22162 */ 22163 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 22164 $hyphenword = array(); // hyphens positions 22165 $numchars = count($word); 22166 if ($numchars <= $charmin) { 22167 return $word; 22168 } 22169 $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode); 22170 // some words will be returned as-is 22171 $pattern = '/^([a-zA-Z0-9_\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/'; 22172 if (preg_match($pattern, $word_string) > 0) { 22173 // email 22174 return $word; 22175 } 22176 $pattern = '/(([a-zA-Z0-9\-]+\.)?)((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/'; 22177 if (preg_match($pattern, $word_string) > 0) { 22178 // URL 22179 return $word; 22180 } 22181 if (isset($dictionary[$word_string])) { 22182 return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont); 22183 } 22184 // surround word with '_' characters 22185 $tmpword = array_merge(array(46), $word, array(46)); 22186 $tmpnumchars = $numchars + 2; 22187 $maxpos = $tmpnumchars - $charmin; 22188 for ($pos = 0; $pos < $maxpos; ++$pos) { 22189 $imax = min(($tmpnumchars - $pos), $charmax); 22190 for ($i = $charmin; $i <= $imax; ++$i) { 22191 $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode)); 22192 if (isset($patterns[$subword])) { 22193 $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont); 22194 $pattern_length = count($pattern); 22195 $digits = 1; 22196 for ($j = 0; $j < $pattern_length; ++$j) { 22197 // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid) 22198 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) { 22199 if ($j == 0) { 22200 $zero = $pos - 1; 22201 } else { 22202 $zero = $pos + $j - $digits; 22203 } 22204 // get hyphenation level 22205 $level = ($pattern[$j] - 48); 22206 // if two levels from two different patterns match at the same point, the higher one is selected. 22207 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) { 22208 $hyphenword[$zero] = $level; 22209 } 22210 ++$digits; 22211 } 22212 } 22213 } 22214 } 22215 } 22216 $inserted = 0; 22217 $maxpos = $numchars - $rightmin; 22218 for ($i = $leftmin; $i <= $maxpos; ++$i) { 22219 // only odd levels indicate allowed hyphenation points 22220 if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) { 22221 // 173 = soft hyphen character 22222 array_splice($word, $i + $inserted, 0, 173); 22223 ++$inserted; 22224 } 22225 } 22226 return $word; 22227 } 22228 22229 /** 22230 * Returns text with soft hyphens. 22231 * @param $text (string) text to process 22232 * @param $patterns (mixed) Array of hypenation patterns or a TEX file containing hypenation patterns. TEX patterns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/ 22233 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm. 22234 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens. 22235 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens. 22236 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm. 22237 * @param $charmax (int) Maximum length of broken piece of word. 22238 * @return array text with soft hyphens 22239 * @author Nicola Asuni 22240 * @since 4.9.012 (2010-04-12) 22241 * @public 22242 */ 22243 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 22244 $text = $this->unhtmlentities($text); 22245 $word = array(); // last word 22246 $txtarr = array(); // text to be returned 22247 $intag = false; // true if we are inside an HTML tag 22248 $skip = false; // true to skip hyphenation 22249 if (!is_array($patterns)) { 22250 $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns); 22251 } 22252 // get array of characters 22253 $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont); 22254 // for each char 22255 foreach ($unichars as $char) { 22256 if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') { 22257 // letter character 22258 $word[] = $char; 22259 } else { 22260 // other type of character 22261 if (!TCPDF_STATIC::empty_string($word)) { 22262 // hypenate the word 22263 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 22264 $word = array(); 22265 } 22266 $txtarr[] = $char; 22267 if (chr($char) == '<') { 22268 // we are inside an HTML tag 22269 $intag = true; 22270 } elseif ($intag AND (chr($char) == '>')) { 22271 // end of HTML tag 22272 $intag = false; 22273 // check for style tag 22274 $expected = array(115, 116, 121, 108, 101); // = 'style' 22275 $current = array_slice($txtarr, -6, 5); // last 5 chars 22276 $compare = array_diff($expected, $current); 22277 if (empty($compare)) { 22278 // check if it is a closing tag 22279 $expected = array(47); // = '/' 22280 $current = array_slice($txtarr, -7, 1); 22281 $compare = array_diff($expected, $current); 22282 if (empty($compare)) { 22283 // closing style tag 22284 $skip = false; 22285 } else { 22286 // opening style tag 22287 $skip = true; 22288 } 22289 } 22290 } 22291 } 22292 } 22293 if (!TCPDF_STATIC::empty_string($word)) { 22294 // hypenate the word 22295 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 22296 } 22297 // convert char array to string and return 22298 return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode); 22299 } 22300 22301 /** 22302 * Enable/disable rasterization of vector images using ImageMagick library. 22303 * @param $mode (boolean) if true enable rasterization, false otherwise. 22304 * @public 22305 * @since 5.0.000 (2010-04-27) 22306 */ 22307 public function setRasterizeVectorImages($mode) { 22308 $this->rasterize_vector_images = $mode; 22309 } 22310 22311 /** 22312 * Enable or disable default option for font subsetting. 22313 * @param $enable (boolean) if true enable font subsetting by default. 22314 * @author Nicola Asuni 22315 * @public 22316 * @since 5.3.002 (2010-06-07) 22317 */ 22318 public function setFontSubsetting($enable=true) { 22319 if ($this->pdfa_mode) { 22320 $this->font_subsetting = false; 22321 } else { 22322 $this->font_subsetting = $enable ? true : false; 22323 } 22324 } 22325 22326 /** 22327 * Return the default option for font subsetting. 22328 * @return boolean default font subsetting state. 22329 * @author Nicola Asuni 22330 * @public 22331 * @since 5.3.002 (2010-06-07) 22332 */ 22333 public function getFontSubsetting() { 22334 return $this->font_subsetting; 22335 } 22336 22337 /** 22338 * Left trim the input string 22339 * @param $str (string) string to trim 22340 * @param $replace (string) string that replace spaces. 22341 * @return left trimmed string 22342 * @author Nicola Asuni 22343 * @public 22344 * @since 5.8.000 (2010-08-11) 22345 */ 22346 public function stringLeftTrim($str, $replace='') { 22347 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str); 22348 } 22349 22350 /** 22351 * Right trim the input string 22352 * @param $str (string) string to trim 22353 * @param $replace (string) string that replace spaces. 22354 * @return right trimmed string 22355 * @author Nicola Asuni 22356 * @public 22357 * @since 5.8.000 (2010-08-11) 22358 */ 22359 public function stringRightTrim($str, $replace='') { 22360 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str); 22361 } 22362 22363 /** 22364 * Trim the input string 22365 * @param $str (string) string to trim 22366 * @param $replace (string) string that replace spaces. 22367 * @return trimmed string 22368 * @author Nicola Asuni 22369 * @public 22370 * @since 5.8.000 (2010-08-11) 22371 */ 22372 public function stringTrim($str, $replace='') { 22373 $str = $this->stringLeftTrim($str, $replace); 22374 $str = $this->stringRightTrim($str, $replace); 22375 return $str; 22376 } 22377 22378 /** 22379 * Return true if the current font is unicode type. 22380 * @return true for unicode font, false otherwise. 22381 * @author Nicola Asuni 22382 * @public 22383 * @since 5.8.002 (2010-08-14) 22384 */ 22385 public function isUnicodeFont() { 22386 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')); 22387 } 22388 22389 /** 22390 * Return normalized font name 22391 * @param $fontfamily (string) property string containing font family names 22392 * @return string normalized font name 22393 * @author Nicola Asuni 22394 * @public 22395 * @since 5.8.004 (2010-08-17) 22396 */ 22397 public function getFontFamilyName($fontfamily) { 22398 // remove spaces and symbols 22399 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily)); 22400 // extract all font names 22401 $fontslist = preg_split('/[,]/', $fontfamily); 22402 // find first valid font name 22403 foreach ($fontslist as $font) { 22404 // replace font variations 22405 $font = preg_replace('/regular$/', '', $font); 22406 $font = preg_replace('/italic$/', 'I', $font); 22407 $font = preg_replace('/oblique$/', 'I', $font); 22408 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font); 22409 // replace common family names and core fonts 22410 $pattern = array(); 22411 $replacement = array(); 22412 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/'; 22413 $replacement[] = 'times'; 22414 $pattern[] = '/^sansserif/'; 22415 $replacement[] = 'helvetica'; 22416 $pattern[] = '/^monospace/'; 22417 $replacement[] = 'courier'; 22418 $font = preg_replace($pattern, $replacement, $font); 22419 if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) { 22420 return $font; 22421 } 22422 } 22423 // return current font as default 22424 return $this->CurrentFont['fontkey']; 22425 } 22426 22427 /** 22428 * Start a new XObject Template. 22429 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images). 22430 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked. 22431 * Note: X,Y coordinates will be reset to 0,0. 22432 * @param $w (int) Template width in user units (empty string or zero = page width less margins). 22433 * @param $h (int) Template height in user units (empty string or zero = page height less margins). 22434 * @param $group (mixed) Set transparency group. Can be a boolean value or an array specifying optional parameters: 'CS' (solour space name), 'I' (boolean flag to indicate isolated group) and 'K' (boolean flag to indicate knockout group). 22435 * @return int the XObject Template ID in case of success or false in case of error. 22436 * @author Nicola Asuni 22437 * @public 22438 * @since 5.8.017 (2010-08-24) 22439 * @see endTemplate(), printTemplate() 22440 */ 22441 public function startTemplate($w=0, $h=0, $group=false) { 22442 if ($this->inxobj) { 22443 // we are already inside an XObject template 22444 return false; 22445 } 22446 $this->inxobj = true; 22447 ++$this->n; 22448 // XObject ID 22449 $this->xobjid = 'XT'.$this->n; 22450 // object ID 22451 $this->xobjects[$this->xobjid] = array('n' => $this->n); 22452 // store current graphic state 22453 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars(); 22454 // initialize data 22455 $this->xobjects[$this->xobjid]['intmrk'] = 0; 22456 $this->xobjects[$this->xobjid]['transfmrk'] = array(); 22457 $this->xobjects[$this->xobjid]['outdata'] = ''; 22458 $this->xobjects[$this->xobjid]['xobjects'] = array(); 22459 $this->xobjects[$this->xobjid]['images'] = array(); 22460 $this->xobjects[$this->xobjid]['fonts'] = array(); 22461 $this->xobjects[$this->xobjid]['annotations'] = array(); 22462 $this->xobjects[$this->xobjid]['extgstates'] = array(); 22463 $this->xobjects[$this->xobjid]['gradients'] = array(); 22464 $this->xobjects[$this->xobjid]['spot_colors'] = array(); 22465 // set new environment 22466 $this->num_columns = 1; 22467 $this->current_column = 0; 22468 $this->SetAutoPageBreak(false); 22469 if (($w === '') OR ($w <= 0)) { 22470 $w = $this->w - $this->lMargin - $this->rMargin; 22471 } 22472 if (($h === '') OR ($h <= 0)) { 22473 $h = $this->h - $this->tMargin - $this->bMargin; 22474 } 22475 $this->xobjects[$this->xobjid]['x'] = 0; 22476 $this->xobjects[$this->xobjid]['y'] = 0; 22477 $this->xobjects[$this->xobjid]['w'] = $w; 22478 $this->xobjects[$this->xobjid]['h'] = $h; 22479 $this->w = $w; 22480 $this->h = $h; 22481 $this->wPt = $this->w * $this->k; 22482 $this->hPt = $this->h * $this->k; 22483 $this->fwPt = $this->wPt; 22484 $this->fhPt = $this->hPt; 22485 $this->x = 0; 22486 $this->y = 0; 22487 $this->lMargin = 0; 22488 $this->rMargin = 0; 22489 $this->tMargin = 0; 22490 $this->bMargin = 0; 22491 // set group mode 22492 $this->xobjects[$this->xobjid]['group'] = $group; 22493 return $this->xobjid; 22494 } 22495 22496 /** 22497 * End the current XObject Template started with startTemplate() and restore the previous graphic state. 22498 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images). 22499 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked. 22500 * @return int the XObject Template ID in case of success or false in case of error. 22501 * @author Nicola Asuni 22502 * @public 22503 * @since 5.8.017 (2010-08-24) 22504 * @see startTemplate(), printTemplate() 22505 */ 22506 public function endTemplate() { 22507 if (!$this->inxobj) { 22508 // we are not inside a template 22509 return false; 22510 } 22511 $this->inxobj = false; 22512 // restore previous graphic state 22513 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true); 22514 return $this->xobjid; 22515 } 22516 22517 /** 22518 * Print an XObject Template. 22519 * You can print an XObject Template inside the currently opened Template. 22520 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images). 22521 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked. 22522 * @param $id (string) The ID of XObject Template to print. 22523 * @param $x (int) X position in user units (empty string = current x position) 22524 * @param $y (int) Y position in user units (empty string = current y position) 22525 * @param $w (int) Width in user units (zero = remaining page width) 22526 * @param $h (int) Height in user units (zero = remaining page height) 22527 * @param $align (string) Indicates the alignment of the pointer next to template insertion relative to template 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> 22528 * @param $palign (string) Allows to center or align the template 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> 22529 * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions. 22530 * @author Nicola Asuni 22531 * @public 22532 * @since 5.8.017 (2010-08-24) 22533 * @see startTemplate(), endTemplate() 22534 */ 22535 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) { 22536 if ($this->state != 2) { 22537 return; 22538 } 22539 if (!isset($this->xobjects[$id])) { 22540 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!'); 22541 } 22542 if ($this->inxobj) { 22543 if ($id == $this->xobjid) { 22544 // close current template 22545 $this->endTemplate(); 22546 } else { 22547 // use the template as resource for the template currently opened 22548 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id]; 22549 } 22550 } 22551 // set default values 22552 if ($x === '') { 22553 $x = $this->x; 22554 } 22555 if ($y === '') { 22556 $y = $this->y; 22557 } 22558 // check page for no-write regions and adapt page margins if necessary 22559 list($x, $y) = $this->checkPageRegions($h, $x, $y); 22560 $ow = $this->xobjects[$id]['w']; 22561 if ($ow <= 0) { 22562 $ow = 1; 22563 } 22564 $oh = $this->xobjects[$id]['h']; 22565 if ($oh <= 0) { 22566 $oh = 1; 22567 } 22568 // calculate template width and height on document 22569 if (($w <= 0) AND ($h <= 0)) { 22570 $w = $ow; 22571 $h = $oh; 22572 } elseif ($w <= 0) { 22573 $w = $h * $ow / $oh; 22574 } elseif ($h <= 0) { 22575 $h = $w * $oh / $ow; 22576 } 22577 // fit the template on available space 22578 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 22579 // set page alignment 22580 $rb_y = $y + $h; 22581 // set alignment 22582 if ($this->rtl) { 22583 if ($palign == 'L') { 22584 $xt = $this->lMargin; 22585 } elseif ($palign == 'C') { 22586 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22587 } elseif ($palign == 'R') { 22588 $xt = $this->w - $this->rMargin - $w; 22589 } else { 22590 $xt = $x - $w; 22591 } 22592 $rb_x = $xt; 22593 } else { 22594 if ($palign == 'L') { 22595 $xt = $this->lMargin; 22596 } elseif ($palign == 'C') { 22597 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22598 } elseif ($palign == 'R') { 22599 $xt = $this->w - $this->rMargin - $w; 22600 } else { 22601 $xt = $x; 22602 } 22603 $rb_x = $xt + $w; 22604 } 22605 // print XObject Template + Transformation matrix 22606 $this->StartTransform(); 22607 // translate and scale 22608 $sx = ($w / $ow); 22609 $sy = ($h / $oh); 22610 $tm = array(); 22611 $tm[0] = $sx; 22612 $tm[1] = 0; 22613 $tm[2] = 0; 22614 $tm[3] = $sy; 22615 $tm[4] = $xt * $this->k; 22616 $tm[5] = ($this->h - $h - $y) * $this->k; 22617 $this->Transform($tm); 22618 // set object 22619 $this->_out('/'.$id.' Do'); 22620 $this->StopTransform(); 22621 // add annotations 22622 if (!empty($this->xobjects[$id]['annotations'])) { 22623 foreach ($this->xobjects[$id]['annotations'] as $annot) { 22624 // transform original coordinates 22625 $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k))); 22626 $ax = ($coordlt[4] / $this->k); 22627 $ay = ($this->h - $h - ($coordlt[5] / $this->k)); 22628 $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k))); 22629 $aw = ($coordrb[4] / $this->k) - $ax; 22630 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay; 22631 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']); 22632 } 22633 } 22634 // set pointer to align the next text/objects 22635 switch($align) { 22636 case 'T': { 22637 $this->y = $y; 22638 $this->x = $rb_x; 22639 break; 22640 } 22641 case 'M': { 22642 $this->y = $y + round($h/2); 22643 $this->x = $rb_x; 22644 break; 22645 } 22646 case 'B': { 22647 $this->y = $rb_y; 22648 $this->x = $rb_x; 22649 break; 22650 } 22651 case 'N': { 22652 $this->SetY($rb_y); 22653 break; 22654 } 22655 default:{ 22656 break; 22657 } 22658 } 22659 } 22660 22661 /** 22662 * Set the percentage of character stretching. 22663 * @param $perc (int) percentage of stretching (100 = no stretching) 22664 * @author Nicola Asuni 22665 * @public 22666 * @since 5.9.000 (2010-09-29) 22667 */ 22668 public function setFontStretching($perc=100) { 22669 $this->font_stretching = $perc; 22670 } 22671 22672 /** 22673 * Get the percentage of character stretching. 22674 * @return float stretching value 22675 * @author Nicola Asuni 22676 * @public 22677 * @since 5.9.000 (2010-09-29) 22678 */ 22679 public function getFontStretching() { 22680 return $this->font_stretching; 22681 } 22682 22683 /** 22684 * Set the amount to increase or decrease the space between characters in a text. 22685 * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing) 22686 * @author Nicola Asuni 22687 * @public 22688 * @since 5.9.000 (2010-09-29) 22689 */ 22690 public function setFontSpacing($spacing=0) { 22691 $this->font_spacing = $spacing; 22692 } 22693 22694 /** 22695 * Get the amount to increase or decrease the space between characters in a text. 22696 * @return int font spacing (tracking) value 22697 * @author Nicola Asuni 22698 * @public 22699 * @since 5.9.000 (2010-09-29) 22700 */ 22701 public function getFontSpacing() { 22702 return $this->font_spacing; 22703 } 22704 22705 /** 22706 * Return an array of no-write page regions 22707 * @return array of no-write page regions 22708 * @author Nicola Asuni 22709 * @public 22710 * @since 5.9.003 (2010-10-13) 22711 * @see setPageRegions(), addPageRegion() 22712 */ 22713 public function getPageRegions() { 22714 return $this->page_regions; 22715 } 22716 22717 /** 22718 * Set no-write regions on page. 22719 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code. 22720 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22721 * You can set multiple regions for the same page. 22722 * @param $regions (array) array of no-write regions. For each region you can define an array as follow: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right). Omit this parameter to remove all regions. 22723 * @author Nicola Asuni 22724 * @public 22725 * @since 5.9.003 (2010-10-13) 22726 * @see addPageRegion(), getPageRegions() 22727 */ 22728 public function setPageRegions($regions=array()) { 22729 // empty current regions array 22730 $this->page_regions = array(); 22731 // add regions 22732 foreach ($regions as $data) { 22733 $this->addPageRegion($data); 22734 } 22735 } 22736 22737 /** 22738 * Add a single no-write region on selected page. 22739 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code. 22740 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22741 * You can set multiple regions for the same page. 22742 * @param $region (array) array of a single no-write region array: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right). 22743 * @author Nicola Asuni 22744 * @public 22745 * @since 5.9.003 (2010-10-13) 22746 * @see setPageRegions(), getPageRegions() 22747 */ 22748 public function addPageRegion($region) { 22749 if (!isset($region['page']) OR empty($region['page'])) { 22750 $region['page'] = $this->page; 22751 } 22752 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0) 22753 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb']) 22754 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) { 22755 $this->page_regions[] = $region; 22756 } 22757 } 22758 22759 /** 22760 * Remove a single no-write region. 22761 * @param $key (int) region key 22762 * @author Nicola Asuni 22763 * @public 22764 * @since 5.9.003 (2010-10-13) 22765 * @see setPageRegions(), getPageRegions() 22766 */ 22767 public function removePageRegion($key) { 22768 if (isset($this->page_regions[$key])) { 22769 unset($this->page_regions[$key]); 22770 } 22771 } 22772 22773 /** 22774 * Check page for no-write regions and adapt current coordinates and page margins if necessary. 22775 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code. 22776 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22777 * @param $h (float) height of the text/image/object to print in user units 22778 * @param $x (float) current X coordinate in user units 22779 * @param $y (float) current Y coordinate in user units 22780 * @return array($x, $y) 22781 * @author Nicola Asuni 22782 * @protected 22783 * @since 5.9.003 (2010-10-13) 22784 */ 22785 protected function checkPageRegions($h, $x, $y) { 22786 // set default values 22787 if ($x === '') { 22788 $x = $this->x; 22789 } 22790 if ($y === '') { 22791 $y = $this->y; 22792 } 22793 if (!$this->check_page_regions OR empty($this->page_regions)) { 22794 // no page regions defined 22795 return array($x, $y); 22796 } 22797 if (empty($h)) { 22798 $h = $this->getCellHeight($this->FontSize); 22799 } 22800 // check for page break 22801 if ($this->checkPageBreak($h, $y)) { 22802 // the content will be printed on a new page 22803 $x = $this->x; 22804 $y = $this->y; 22805 } 22806 if ($this->num_columns > 1) { 22807 if ($this->rtl) { 22808 $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']); 22809 } else { 22810 $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']); 22811 } 22812 } else { 22813 if ($this->rtl) { 22814 $this->lMargin = max($this->clMargin, $this->original_lMargin); 22815 } else { 22816 $this->rMargin = max($this->crMargin, $this->original_rMargin); 22817 } 22818 } 22819 // adjust coordinates and page margins 22820 foreach ($this->page_regions as $regid => $regdata) { 22821 if ($regdata['page'] == $this->page) { 22822 // check region boundaries 22823 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) { 22824 // Y is inside the region 22825 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient 22826 $yt = max($y, $regdata['yt']); 22827 $yb = min(($yt + $h), $regdata['yb']); 22828 $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt']; 22829 $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt']; 22830 if ($regdata['side'] == 'L') { // left side 22831 $new_margin = max($xt, $xb); 22832 if ($this->lMargin < $new_margin) { 22833 if ($this->rtl) { 22834 // adjust left page margin 22835 $this->lMargin = max(0, $new_margin); 22836 } 22837 if ($x < $new_margin) { 22838 // adjust x position 22839 $x = $new_margin; 22840 if ($new_margin > ($this->w - $this->rMargin)) { 22841 // adjust y position 22842 $y = $regdata['yb'] - $h; 22843 } 22844 } 22845 } 22846 } elseif ($regdata['side'] == 'R') { // right side 22847 $new_margin = min($xt, $xb); 22848 if (($this->w - $this->rMargin) > $new_margin) { 22849 if (!$this->rtl) { 22850 // adjust right page margin 22851 $this->rMargin = max(0, ($this->w - $new_margin)); 22852 } 22853 if ($x > $new_margin) { 22854 // adjust x position 22855 $x = $new_margin; 22856 if ($new_margin > $this->lMargin) { 22857 // adjust y position 22858 $y = $regdata['yb'] - $h; 22859 } 22860 } 22861 } 22862 } 22863 } 22864 } 22865 } 22866 return array($x, $y); 22867 } 22868 22869 // --- SVG METHODS --------------------------------------------------------- 22870 22871 /** 22872 * Embedd a Scalable Vector Graphics (SVG) image. 22873 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library. 22874 * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string. 22875 * @param $x (float) Abscissa of the upper-left corner. 22876 * @param $y (float) Ordinate of the upper-left corner. 22877 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 22878 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 22879 * @param $link (mixed) URL or identifier returned by AddLink(). 22880 * @param $align (string) 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> If the alignment is an empty string, then the pointer will be restored on the starting SVG position. 22881 * @param $palign (string) 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> 22882 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) 22883 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions. 22884 * @author Nicola Asuni 22885 * @since 5.0.000 (2010-05-02) 22886 * @public 22887 */ 22888 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) { 22889 if ($this->state != 2) { 22890 return; 22891 } 22892 // reseet SVG vars 22893 $this->svggradients = array(); 22894 $this->svggradientid = 0; 22895 $this->svgdefsmode = false; 22896 $this->svgdefs = array(); 22897 $this->svgclipmode = false; 22898 $this->svgclippaths = array(); 22899 $this->svgcliptm = array(); 22900 $this->svgclipid = 0; 22901 $this->svgtext = ''; 22902 $this->svgtextmode = array(); 22903 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { 22904 // convert SVG to raster image using GD or ImageMagick libraries 22905 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 22906 } 22907 if ($file[0] === '@') { // image from string 22908 $this->svgdir = ''; 22909 $svgdata = substr($file, 1); 22910 } else { // SVG file 22911 $this->svgdir = dirname($file); 22912 $svgdata = TCPDF_STATIC::fileGetContents($file); 22913 } 22914 if ($svgdata === FALSE) { 22915 $this->Error('SVG file not found: '.$file); 22916 } 22917 if ($x === '') { 22918 $x = $this->x; 22919 } 22920 if ($y === '') { 22921 $y = $this->y; 22922 } 22923 // check page for no-write regions and adapt page margins if necessary 22924 list($x, $y) = $this->checkPageRegions($h, $x, $y); 22925 $k = $this->k; 22926 $ox = 0; 22927 $oy = 0; 22928 $ow = $w; 22929 $oh = $h; 22930 $aspect_ratio_align = 'xMidYMid'; 22931 $aspect_ratio_ms = 'meet'; 22932 $regs = array(); 22933 // get original image width and height 22934 preg_match('/<svg([^\>]*)>/si', $svgdata, $regs); 22935 if (isset($regs[1]) AND !empty($regs[1])) { 22936 $tmp = array(); 22937 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22938 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 22939 } 22940 $tmp = array(); 22941 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22942 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 22943 } 22944 $tmp = array(); 22945 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22946 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 22947 } 22948 $tmp = array(); 22949 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22950 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 22951 } 22952 $tmp = array(); 22953 $view_box = array(); 22954 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) { 22955 if (count($tmp) == 5) { 22956 array_shift($tmp); 22957 foreach ($tmp as $key => $val) { 22958 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 22959 } 22960 $ox = $view_box[0]; 22961 $oy = $view_box[1]; 22962 } 22963 // get aspect ratio 22964 $tmp = array(); 22965 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22966 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]); 22967 switch (count($aspect_ratio)) { 22968 case 3: { 22969 $aspect_ratio_align = $aspect_ratio[1]; 22970 $aspect_ratio_ms = $aspect_ratio[2]; 22971 break; 22972 } 22973 case 2: { 22974 $aspect_ratio_align = $aspect_ratio[0]; 22975 $aspect_ratio_ms = $aspect_ratio[1]; 22976 break; 22977 } 22978 case 1: { 22979 $aspect_ratio_align = $aspect_ratio[0]; 22980 $aspect_ratio_ms = 'meet'; 22981 break; 22982 } 22983 } 22984 } 22985 } 22986 } 22987 if ($ow <= 0) { 22988 $ow = 1; 22989 } 22990 if ($oh <= 0) { 22991 $oh = 1; 22992 } 22993 // calculate image width and height on document 22994 if (($w <= 0) AND ($h <= 0)) { 22995 // convert image size to document unit 22996 $w = $ow; 22997 $h = $oh; 22998 } elseif ($w <= 0) { 22999 $w = $h * $ow / $oh; 23000 } elseif ($h <= 0) { 23001 $h = $w * $oh / $ow; 23002 } 23003 // fit the image on available space 23004 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 23005 if ($this->rasterize_vector_images) { 23006 // convert SVG to raster image using GD or ImageMagick libraries 23007 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 23008 } 23009 // set alignment 23010 $this->img_rb_y = $y + $h; 23011 // set alignment 23012 if ($this->rtl) { 23013 if ($palign == 'L') { 23014 $ximg = $this->lMargin; 23015 } elseif ($palign == 'C') { 23016 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 23017 } elseif ($palign == 'R') { 23018 $ximg = $this->w - $this->rMargin - $w; 23019 } else { 23020 $ximg = $x - $w; 23021 } 23022 $this->img_rb_x = $ximg; 23023 } else { 23024 if ($palign == 'L') { 23025 $ximg = $this->lMargin; 23026 } elseif ($palign == 'C') { 23027 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 23028 } elseif ($palign == 'R') { 23029 $ximg = $this->w - $this->rMargin - $w; 23030 } else { 23031 $ximg = $x; 23032 } 23033 $this->img_rb_x = $ximg + $w; 23034 } 23035 // store current graphic vars 23036 $gvars = $this->getGraphicVars(); 23037 // store SVG position and scale factors 23038 $svgoffset_x = ($ximg - $ox) * $this->k; 23039 $svgoffset_y = -($y - $oy) * $this->k; 23040 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) { 23041 $ow = $view_box[2]; 23042 $oh = $view_box[3]; 23043 } else { 23044 if ($ow <= 0) { 23045 $ow = $w; 23046 } 23047 if ($oh <= 0) { 23048 $oh = $h; 23049 } 23050 } 23051 $svgscale_x = $w / $ow; 23052 $svgscale_y = $h / $oh; 23053 // scaling and alignment 23054 if ($aspect_ratio_align != 'none') { 23055 // store current scaling values 23056 $svgscale_old_x = $svgscale_x; 23057 $svgscale_old_y = $svgscale_y; 23058 // force uniform scaling 23059 if ($aspect_ratio_ms == 'slice') { 23060 // the entire viewport is covered by the viewBox 23061 if ($svgscale_x > $svgscale_y) { 23062 $svgscale_y = $svgscale_x; 23063 } elseif ($svgscale_x < $svgscale_y) { 23064 $svgscale_x = $svgscale_y; 23065 } 23066 } else { // meet 23067 // the entire viewBox is visible within the viewport 23068 if ($svgscale_x < $svgscale_y) { 23069 $svgscale_y = $svgscale_x; 23070 } elseif ($svgscale_x > $svgscale_y) { 23071 $svgscale_x = $svgscale_y; 23072 } 23073 } 23074 // correct X alignment 23075 switch (substr($aspect_ratio_align, 1, 3)) { 23076 case 'Min': { 23077 // do nothing 23078 break; 23079 } 23080 case 'Max': { 23081 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x)); 23082 break; 23083 } 23084 default: 23085 case 'Mid': { 23086 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2); 23087 break; 23088 } 23089 } 23090 // correct Y alignment 23091 switch (substr($aspect_ratio_align, 5)) { 23092 case 'Min': { 23093 // do nothing 23094 break; 23095 } 23096 case 'Max': { 23097 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y)); 23098 break; 23099 } 23100 default: 23101 case 'Mid': { 23102 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2); 23103 break; 23104 } 23105 } 23106 } 23107 // store current page break mode 23108 $page_break_mode = $this->AutoPageBreak; 23109 $page_break_margin = $this->getBreakMargin(); 23110 $cell_padding = $this->cell_padding; 23111 $this->SetCellPadding(0); 23112 $this->SetAutoPageBreak(false); 23113 // save the current graphic state 23114 $this->_out('q'.$this->epsmarker); 23115 // set initial clipping mask 23116 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array()); 23117 // scale and translate 23118 $e = $ox * $this->k * (1 - $svgscale_x); 23119 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y); 23120 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y))); 23121 // creates a new XML parser to be used by the other XML functions 23122 $this->parser = xml_parser_create('UTF-8'); 23123 // the following function allows to use parser inside object 23124 xml_set_object($this->parser, $this); 23125 // disable case-folding for this XML parser 23126 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 23127 // sets the element handler functions for the XML parser 23128 xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler'); 23129 // sets the character data handler function for the XML parser 23130 xml_set_character_data_handler($this->parser, 'segSVGContentHandler'); 23131 // start parsing an XML document 23132 if (!xml_parse($this->parser, $svgdata)) { 23133 $error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser)); 23134 $this->Error($error_message); 23135 } 23136 // free this XML parser 23137 xml_parser_free($this->parser); 23138 // restore previous graphic state 23139 $this->_out($this->epsmarker.'Q'); 23140 // restore graphic vars 23141 $this->setGraphicVars($gvars); 23142 $this->lasth = $gvars['lasth']; 23143 if (!empty($border)) { 23144 $bx = $this->x; 23145 $by = $this->y; 23146 $this->x = $ximg; 23147 if ($this->rtl) { 23148 $this->x += $w; 23149 } 23150 $this->y = $y; 23151 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 23152 $this->x = $bx; 23153 $this->y = $by; 23154 } 23155 if ($link) { 23156 $this->Link($ximg, $y, $w, $h, $link, 0); 23157 } 23158 // set pointer to align the next text/objects 23159 switch($align) { 23160 case 'T':{ 23161 $this->y = $y; 23162 $this->x = $this->img_rb_x; 23163 break; 23164 } 23165 case 'M':{ 23166 $this->y = $y + round($h/2); 23167 $this->x = $this->img_rb_x; 23168 break; 23169 } 23170 case 'B':{ 23171 $this->y = $this->img_rb_y; 23172 $this->x = $this->img_rb_x; 23173 break; 23174 } 23175 case 'N':{ 23176 $this->SetY($this->img_rb_y); 23177 break; 23178 } 23179 default:{ 23180 // restore pointer to starting position 23181 $this->x = $gvars['x']; 23182 $this->y = $gvars['y']; 23183 $this->page = $gvars['page']; 23184 $this->current_column = $gvars['current_column']; 23185 $this->tMargin = $gvars['tMargin']; 23186 $this->bMargin = $gvars['bMargin']; 23187 $this->w = $gvars['w']; 23188 $this->h = $gvars['h']; 23189 $this->wPt = $gvars['wPt']; 23190 $this->hPt = $gvars['hPt']; 23191 $this->fwPt = $gvars['fwPt']; 23192 $this->fhPt = $gvars['fhPt']; 23193 break; 23194 } 23195 } 23196 $this->endlinex = $this->img_rb_x; 23197 // restore page break 23198 $this->SetAutoPageBreak($page_break_mode, $page_break_margin); 23199 $this->cell_padding = $cell_padding; 23200 } 23201 23202 /** 23203 * Convert SVG transformation matrix to PDF. 23204 * @param $tm (array) original SVG transformation matrix 23205 * @return array transformation matrix 23206 * @protected 23207 * @since 5.0.000 (2010-05-02) 23208 */ 23209 protected function convertSVGtMatrix($tm) { 23210 $a = $tm[0]; 23211 $b = -$tm[1]; 23212 $c = -$tm[2]; 23213 $d = $tm[3]; 23214 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k; 23215 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k; 23216 $x = 0; 23217 $y = $this->h * $this->k; 23218 $e = ($x * (1 - $a)) - ($y * $c) + $e; 23219 $f = ($y * (1 - $d)) - ($x * $b) + $f; 23220 return array($a, $b, $c, $d, $e, $f); 23221 } 23222 23223 /** 23224 * Apply SVG graphic transformation matrix. 23225 * @param $tm (array) original SVG transformation matrix 23226 * @protected 23227 * @since 5.0.000 (2010-05-02) 23228 */ 23229 protected function SVGTransform($tm) { 23230 $this->Transform($this->convertSVGtMatrix($tm)); 23231 } 23232 23233 /** 23234 * Apply the requested SVG styles (*** TO BE COMPLETED ***) 23235 * @param $svgstyle (array) array of SVG styles to apply 23236 * @param $prevsvgstyle (array) array of previous SVG style 23237 * @param $x (int) X origin of the bounding box 23238 * @param $y (int) Y origin of the bounding box 23239 * @param $w (int) width of the bounding box 23240 * @param $h (int) height of the bounding box 23241 * @param $clip_function (string) clip function 23242 * @param $clip_params (array) array of parameters for clipping function 23243 * @return object style 23244 * @author Nicola Asuni 23245 * @since 5.0.000 (2010-05-02) 23246 * @protected 23247 */ 23248 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) { 23249 if ($this->state != 2) { 23250 return; 23251 } 23252 $objstyle = ''; 23253 $minlen = (0.01 / $this->k); // minimum acceptable length 23254 if (!isset($svgstyle['opacity'])) { 23255 return $objstyle; 23256 } 23257 // clip-path 23258 $regs = array(); 23259 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) { 23260 $clip_path = $this->svgclippaths[$regs[1]]; 23261 foreach ($clip_path as $cp) { 23262 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']); 23263 } 23264 } 23265 // opacity 23266 if ($svgstyle['opacity'] != 1) { 23267 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false); 23268 } 23269 // color 23270 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors); 23271 $this->SetFillColorArray($fill_color); 23272 // text color 23273 $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors); 23274 $this->SetTextColorArray($text_color); 23275 // clip 23276 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) { 23277 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0); 23278 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0); 23279 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0); 23280 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0); 23281 $cx = $x + $left; 23282 $cy = $y + $top; 23283 $cw = $w - $left - $right; 23284 $ch = $h - $top - $bottom; 23285 if ($svgstyle['clip-rule'] == 'evenodd') { 23286 $clip_rule = 'CNZ'; 23287 } else { 23288 $clip_rule = 'CEO'; 23289 } 23290 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array()); 23291 } 23292 // fill 23293 $regs = array(); 23294 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) { 23295 // gradient 23296 $gradient = $this->svggradients[$regs[1]]; 23297 if (isset($gradient['xref'])) { 23298 // reference to another gradient definition 23299 $newgradient = $this->svggradients[$gradient['xref']]; 23300 $newgradient['coords'] = $gradient['coords']; 23301 $newgradient['mode'] = $gradient['mode']; 23302 $newgradient['type'] = $gradient['type']; 23303 $newgradient['gradientUnits'] = $gradient['gradientUnits']; 23304 if (isset($gradient['gradientTransform'])) { 23305 $newgradient['gradientTransform'] = $gradient['gradientTransform']; 23306 } 23307 $gradient = $newgradient; 23308 } 23309 //save current Graphic State 23310 $this->_outSaveGraphicsState(); 23311 //set clipping area 23312 if (!empty($clip_function) AND method_exists($this, $clip_function)) { 23313 $bbox = call_user_func_array(array($this, $clip_function), $clip_params); 23314 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) { 23315 list($x, $y, $w, $h) = $bbox; 23316 } 23317 } 23318 if ($gradient['mode'] == 'measure') { 23319 if (!isset($gradient['coords'][4])) { 23320 $gradient['coords'][4] = 0.5; 23321 } 23322 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) { 23323 $gtm = $gradient['gradientTransform']; 23324 // apply transformation matrix 23325 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4]; 23326 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5]; 23327 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4]; 23328 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5]; 23329 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2)); 23330 $gradient['coords'][0] = $xa; 23331 $gradient['coords'][1] = $ya; 23332 $gradient['coords'][2] = $xb; 23333 $gradient['coords'][3] = $yb; 23334 $gradient['coords'][4] = $r; 23335 } 23336 // convert SVG coordinates to user units 23337 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false); 23338 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false); 23339 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false); 23340 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false); 23341 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false); 23342 if ($w <= $minlen) { 23343 $w = $minlen; 23344 } 23345 if ($h <= $minlen) { 23346 $h = $minlen; 23347 } 23348 // shift units 23349 if ($gradient['gradientUnits'] == 'objectBoundingBox') { 23350 // convert to SVG coordinate system 23351 $gradient['coords'][0] += $x; 23352 $gradient['coords'][1] += $y; 23353 $gradient['coords'][2] += $x; 23354 $gradient['coords'][3] += $y; 23355 } 23356 // calculate percentages 23357 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w); 23358 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h); 23359 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w); 23360 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h); 23361 $gradient['coords'][4] /= $w; 23362 } elseif ($gradient['mode'] == 'percentage') { 23363 foreach($gradient['coords'] as $key => $val) { 23364 $gradient['coords'][$key] = (intval($val) / 100); 23365 if ($val < 0) { 23366 $gradient['coords'][$key] = 0; 23367 } elseif ($val > 1) { 23368 $gradient['coords'][$key] = 1; 23369 } 23370 } 23371 } 23372 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) { 23373 // single color (no shading) 23374 $gradient['coords'][0] = 1; 23375 $gradient['coords'][1] = 0; 23376 $gradient['coords'][2] = 0.999; 23377 $gradient['coords'][3] = 0; 23378 } 23379 // swap Y coordinates 23380 $tmp = $gradient['coords'][1]; 23381 $gradient['coords'][1] = $gradient['coords'][3]; 23382 $gradient['coords'][3] = $tmp; 23383 // set transformation map for gradient 23384 $cy = ($this->h - $y); 23385 if ($gradient['type'] == 3) { 23386 // circular gradient 23387 $cy -= ($gradient['coords'][1] * ($w + $h)); 23388 } else { 23389 $cy -= $h; 23390 } 23391 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k))); 23392 if (count($gradient['stops']) > 1) { 23393 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false); 23394 } 23395 } elseif ($svgstyle['fill'] != 'none') { 23396 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors); 23397 if ($svgstyle['fill-opacity'] != 1) { 23398 $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false); 23399 } 23400 $this->SetFillColorArray($fill_color); 23401 if ($svgstyle['fill-rule'] == 'evenodd') { 23402 $objstyle .= 'F*'; 23403 } else { 23404 $objstyle .= 'F'; 23405 } 23406 } 23407 // stroke 23408 if ($svgstyle['stroke'] != 'none') { 23409 if ($svgstyle['stroke-opacity'] != 1) { 23410 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false); 23411 } elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) { 23412 $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false); 23413 } 23414 $stroke_style = array( 23415 'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors), 23416 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false), 23417 'cap' => $svgstyle['stroke-linecap'], 23418 'join' => $svgstyle['stroke-linejoin'] 23419 ); 23420 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) { 23421 $stroke_style['dash'] = $svgstyle['stroke-dasharray']; 23422 } 23423 $this->SetLineStyle($stroke_style); 23424 $objstyle .= 'D'; 23425 } 23426 // font 23427 $regs = array(); 23428 if (!empty($svgstyle['font'])) { 23429 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) { 23430 $font_family = $this->getFontFamilyName($regs[1]); 23431 } else { 23432 $font_family = $svgstyle['font-family']; 23433 } 23434 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23435 $font_size = trim($regs[1]); 23436 } else { 23437 $font_size = $svgstyle['font-size']; 23438 } 23439 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23440 $font_style = trim($regs[1]); 23441 } else { 23442 $font_style = $svgstyle['font-style']; 23443 } 23444 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23445 $font_weight = trim($regs[1]); 23446 } else { 23447 $font_weight = $svgstyle['font-weight']; 23448 } 23449 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23450 $font_stretch = trim($regs[1]); 23451 } else { 23452 $font_stretch = $svgstyle['font-stretch']; 23453 } 23454 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23455 $font_spacing = trim($regs[1]); 23456 } else { 23457 $font_spacing = $svgstyle['letter-spacing']; 23458 } 23459 } else { 23460 $font_family = $this->getFontFamilyName($svgstyle['font-family']); 23461 $font_size = $svgstyle['font-size']; 23462 $font_style = $svgstyle['font-style']; 23463 $font_weight = $svgstyle['font-weight']; 23464 $font_stretch = $svgstyle['font-stretch']; 23465 $font_spacing = $svgstyle['letter-spacing']; 23466 } 23467 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit); 23468 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']); 23469 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']); 23470 switch ($font_style) { 23471 case 'italic': { 23472 $font_style = 'I'; 23473 break; 23474 } 23475 case 'oblique': { 23476 $font_style = 'I'; 23477 break; 23478 } 23479 default: 23480 case 'normal': { 23481 $font_style = ''; 23482 break; 23483 } 23484 } 23485 switch ($font_weight) { 23486 case 'bold': 23487 case 'bolder': { 23488 $font_style .= 'B'; 23489 break; 23490 } 23491 } 23492 switch ($svgstyle['text-decoration']) { 23493 case 'underline': { 23494 $font_style .= 'U'; 23495 break; 23496 } 23497 case 'overline': { 23498 $font_style .= 'O'; 23499 break; 23500 } 23501 case 'line-through': { 23502 $font_style .= 'D'; 23503 break; 23504 } 23505 default: 23506 case 'none': { 23507 break; 23508 } 23509 } 23510 $this->SetFont($font_family, $font_style, $font_size); 23511 $this->setFontStretching($font_stretch); 23512 $this->setFontSpacing($font_spacing); 23513 return $objstyle; 23514 } 23515 23516 /** 23517 * Draws an SVG path 23518 * @param $d (string) attribute d of the path SVG element 23519 * @param $style (string) Style of rendering. Possible values are: 23520 * <ul> 23521 * <li>D or empty string: Draw (default).</li> 23522 * <li>F: Fill.</li> 23523 * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li> 23524 * <li>DF or FD: Draw and fill.</li> 23525 * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li> 23526 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 23527 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 23528 * </ul> 23529 * @return array of container box measures (x, y, w, h) 23530 * @author Nicola Asuni 23531 * @since 5.0.000 (2010-05-02) 23532 * @protected 23533 */ 23534 protected function SVGPath($d, $style='') { 23535 if ($this->state != 2) { 23536 return; 23537 } 23538 // set fill/stroke style 23539 $op = TCPDF_STATIC::getPathPaintOperator($style, ''); 23540 if (empty($op)) { 23541 return; 23542 } 23543 $paths = array(); 23544 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d); 23545 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER); 23546 $x = 0; 23547 $y = 0; 23548 $x1 = 0; 23549 $y1 = 0; 23550 $x2 = 0; 23551 $y2 = 0; 23552 $xmin = 2147483647; 23553 $xmax = 0; 23554 $ymin = 2147483647; 23555 $ymax = 0; 23556 $relcoord = false; 23557 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point) 23558 $firstcmd = true; // used to print first point 23559 // draw curve pieces 23560 foreach ($paths as $key => $val) { 23561 // get curve type 23562 $cmd = trim($val[1]); 23563 if (strtolower($cmd) == $cmd) { 23564 // use relative coordinated instead of absolute 23565 $relcoord = true; 23566 $xoffset = $x; 23567 $yoffset = $y; 23568 } else { 23569 $relcoord = false; 23570 $xoffset = 0; 23571 $yoffset = 0; 23572 } 23573 $params = array(); 23574 if (isset($val[2])) { 23575 // get curve parameters 23576 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2])); 23577 $params = array(); 23578 foreach ($rawparams as $ck => $cp) { 23579 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false); 23580 if (abs($params[$ck]) < $minlen) { 23581 // aproximate little values to zero 23582 $params[$ck] = 0; 23583 } 23584 } 23585 } 23586 // store current origin point 23587 $x0 = $x; 23588 $y0 = $y; 23589 switch (strtoupper($cmd)) { 23590 case 'M': { // moveto 23591 foreach ($params as $ck => $cp) { 23592 if (($ck % 2) == 0) { 23593 $x = $cp + $xoffset; 23594 } else { 23595 $y = $cp + $yoffset; 23596 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23597 if ($ck == 1) { 23598 $this->_outPoint($x, $y); 23599 $firstcmd = false; 23600 } else { 23601 $this->_outLine($x, $y); 23602 } 23603 $x0 = $x; 23604 $y0 = $y; 23605 } 23606 $xmin = min($xmin, $x); 23607 $ymin = min($ymin, $y); 23608 $xmax = max($xmax, $x); 23609 $ymax = max($ymax, $y); 23610 if ($relcoord) { 23611 $xoffset = $x; 23612 $yoffset = $y; 23613 } 23614 } 23615 } 23616 break; 23617 } 23618 case 'L': { // lineto 23619 foreach ($params as $ck => $cp) { 23620 if (($ck % 2) == 0) { 23621 $x = $cp + $xoffset; 23622 } else { 23623 $y = $cp + $yoffset; 23624 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23625 $this->_outLine($x, $y); 23626 $x0 = $x; 23627 $y0 = $y; 23628 } 23629 $xmin = min($xmin, $x); 23630 $ymin = min($ymin, $y); 23631 $xmax = max($xmax, $x); 23632 $ymax = max($ymax, $y); 23633 if ($relcoord) { 23634 $xoffset = $x; 23635 $yoffset = $y; 23636 } 23637 } 23638 } 23639 break; 23640 } 23641 case 'H': { // horizontal lineto 23642 foreach ($params as $ck => $cp) { 23643 $x = $cp + $xoffset; 23644 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23645 $this->_outLine($x, $y); 23646 $x0 = $x; 23647 $y0 = $y; 23648 } 23649 $xmin = min($xmin, $x); 23650 $xmax = max($xmax, $x); 23651 if ($relcoord) { 23652 $xoffset = $x; 23653 } 23654 } 23655 break; 23656 } 23657 case 'V': { // vertical lineto 23658 foreach ($params as $ck => $cp) { 23659 $y = $cp + $yoffset; 23660 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23661 $this->_outLine($x, $y); 23662 $x0 = $x; 23663 $y0 = $y; 23664 } 23665 $ymin = min($ymin, $y); 23666 $ymax = max($ymax, $y); 23667 if ($relcoord) { 23668 $yoffset = $y; 23669 } 23670 } 23671 break; 23672 } 23673 case 'C': { // curveto 23674 foreach ($params as $ck => $cp) { 23675 $params[$ck] = $cp; 23676 if ((($ck + 1) % 6) == 0) { 23677 $x1 = $params[($ck - 5)] + $xoffset; 23678 $y1 = $params[($ck - 4)] + $yoffset; 23679 $x2 = $params[($ck - 3)] + $xoffset; 23680 $y2 = $params[($ck - 2)] + $yoffset; 23681 $x = $params[($ck - 1)] + $xoffset; 23682 $y = $params[($ck)] + $yoffset; 23683 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 23684 $xmin = min($xmin, $x, $x1, $x2); 23685 $ymin = min($ymin, $y, $y1, $y2); 23686 $xmax = max($xmax, $x, $x1, $x2); 23687 $ymax = max($ymax, $y, $y1, $y2); 23688 if ($relcoord) { 23689 $xoffset = $x; 23690 $yoffset = $y; 23691 } 23692 } 23693 } 23694 break; 23695 } 23696 case 'S': { // shorthand/smooth curveto 23697 foreach ($params as $ck => $cp) { 23698 $params[$ck] = $cp; 23699 if ((($ck + 1) % 4) == 0) { 23700 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) { 23701 $x1 = (2 * $x) - $x2; 23702 $y1 = (2 * $y) - $y2; 23703 } else { 23704 $x1 = $x; 23705 $y1 = $y; 23706 } 23707 $x2 = $params[($ck - 3)] + $xoffset; 23708 $y2 = $params[($ck - 2)] + $yoffset; 23709 $x = $params[($ck - 1)] + $xoffset; 23710 $y = $params[($ck)] + $yoffset; 23711 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 23712 $xmin = min($xmin, $x, $x1, $x2); 23713 $ymin = min($ymin, $y, $y1, $y2); 23714 $xmax = max($xmax, $x, $x1, $x2); 23715 $ymax = max($ymax, $y, $y1, $y2); 23716 if ($relcoord) { 23717 $xoffset = $x; 23718 $yoffset = $y; 23719 } 23720 } 23721 } 23722 break; 23723 } 23724 case 'Q': { // quadratic Bezier curveto 23725 foreach ($params as $ck => $cp) { 23726 $params[$ck] = $cp; 23727 if ((($ck + 1) % 4) == 0) { 23728 // convert quadratic points to cubic points 23729 $x1 = $params[($ck - 3)] + $xoffset; 23730 $y1 = $params[($ck - 2)] + $yoffset; 23731 $xa = ($x + (2 * $x1)) / 3; 23732 $ya = ($y + (2 * $y1)) / 3; 23733 $x = $params[($ck - 1)] + $xoffset; 23734 $y = $params[($ck)] + $yoffset; 23735 $xb = ($x + (2 * $x1)) / 3; 23736 $yb = ($y + (2 * $y1)) / 3; 23737 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 23738 $xmin = min($xmin, $x, $xa, $xb); 23739 $ymin = min($ymin, $y, $ya, $yb); 23740 $xmax = max($xmax, $x, $xa, $xb); 23741 $ymax = max($ymax, $y, $ya, $yb); 23742 if ($relcoord) { 23743 $xoffset = $x; 23744 $yoffset = $y; 23745 } 23746 } 23747 } 23748 break; 23749 } 23750 case 'T': { // shorthand/smooth quadratic Bezier curveto 23751 foreach ($params as $ck => $cp) { 23752 $params[$ck] = $cp; 23753 if (($ck % 2) != 0) { 23754 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) { 23755 $x1 = (2 * $x) - $x1; 23756 $y1 = (2 * $y) - $y1; 23757 } else { 23758 $x1 = $x; 23759 $y1 = $y; 23760 } 23761 // convert quadratic points to cubic points 23762 $xa = ($x + (2 * $x1)) / 3; 23763 $ya = ($y + (2 * $y1)) / 3; 23764 $x = $params[($ck - 1)] + $xoffset; 23765 $y = $params[($ck)] + $yoffset; 23766 $xb = ($x + (2 * $x1)) / 3; 23767 $yb = ($y + (2 * $y1)) / 3; 23768 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 23769 $xmin = min($xmin, $x, $xa, $xb); 23770 $ymin = min($ymin, $y, $ya, $yb); 23771 $xmax = max($xmax, $x, $xa, $xb); 23772 $ymax = max($ymax, $y, $ya, $yb); 23773 if ($relcoord) { 23774 $xoffset = $x; 23775 $yoffset = $y; 23776 } 23777 } 23778 } 23779 break; 23780 } 23781 case 'A': { // elliptical arc 23782 foreach ($params as $ck => $cp) { 23783 $params[$ck] = $cp; 23784 if ((($ck + 1) % 7) == 0) { 23785 $x0 = $x; 23786 $y0 = $y; 23787 $rx = abs($params[($ck - 6)]); 23788 $ry = abs($params[($ck - 5)]); 23789 $ang = -$rawparams[($ck - 4)]; 23790 $angle = deg2rad($ang); 23791 $fa = $rawparams[($ck - 3)]; // large-arc-flag 23792 $fs = $rawparams[($ck - 2)]; // sweep-flag 23793 $x = $params[($ck - 1)] + $xoffset; 23794 $y = $params[$ck] + $yoffset; 23795 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) { 23796 // endpoints are almost identical 23797 $xmin = min($xmin, $x); 23798 $ymin = min($ymin, $y); 23799 $xmax = max($xmax, $x); 23800 $ymax = max($ymax, $y); 23801 } else { 23802 $cos_ang = cos($angle); 23803 $sin_ang = sin($angle); 23804 $a = (($x0 - $x) / 2); 23805 $b = (($y0 - $y) / 2); 23806 $xa = ($a * $cos_ang) - ($b * $sin_ang); 23807 $ya = ($a * $sin_ang) + ($b * $cos_ang); 23808 $rx2 = $rx * $rx; 23809 $ry2 = $ry * $ry; 23810 $xa2 = $xa * $xa; 23811 $ya2 = $ya * $ya; 23812 $delta = ($xa2 / $rx2) + ($ya2 / $ry2); 23813 if ($delta > 1) { 23814 $rx *= sqrt($delta); 23815 $ry *= sqrt($delta); 23816 $rx2 = $rx * $rx; 23817 $ry2 = $ry * $ry; 23818 } 23819 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2)); 23820 if ($numerator < 0) { 23821 $root = 0; 23822 } else { 23823 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2))); 23824 } 23825 if ($fa == $fs){ 23826 $root *= -1; 23827 } 23828 $cax = $root * (($rx * $ya) / $ry); 23829 $cay = -$root * (($ry * $xa) / $rx); 23830 // coordinates of ellipse center 23831 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2); 23832 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2); 23833 // get angles 23834 $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry)); 23835 $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry)); 23836 if (($fs == 0) AND ($dang > 0)) { 23837 $dang -= (2 * M_PI); 23838 } elseif (($fs == 1) AND ($dang < 0)) { 23839 $dang += (2 * M_PI); 23840 } 23841 $angf = $angs - $dang; 23842 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) { 23843 // reverse angles 23844 $tmp = $angs; 23845 $angs = $angf; 23846 $angf = $tmp; 23847 } 23848 $angs = round(rad2deg($angs), 6); 23849 $angf = round(rad2deg($angf), 6); 23850 // covent angles to positive values 23851 if (($angs < 0) AND ($angf < 0)) { 23852 $angs += 360; 23853 $angf += 360; 23854 } 23855 $pie = false; 23856 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) { 23857 $pie = true; 23858 } 23859 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true); 23860 $xmin = min($xmin, $x, $axmin); 23861 $ymin = min($ymin, $y, $aymin); 23862 $xmax = max($xmax, $x, $axmax); 23863 $ymax = max($ymax, $y, $aymax); 23864 } 23865 if ($relcoord) { 23866 $xoffset = $x; 23867 $yoffset = $y; 23868 } 23869 } 23870 } 23871 break; 23872 } 23873 case 'Z': { 23874 $this->_out('h'); 23875 break; 23876 } 23877 } 23878 $firstcmd = false; 23879 } // end foreach 23880 if (!empty($op)) { 23881 $this->_out($op); 23882 } 23883 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin)); 23884 } 23885 23886 /** 23887 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***) 23888 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 23889 * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. 23890 * @param $attribs (array) The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on. 23891 * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix). 23892 * @author Nicola Asuni 23893 * @since 5.0.000 (2010-05-02) 23894 * @protected 23895 */ 23896 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) { 23897 // check if we are in clip mode 23898 if ($this->svgclipmode) { 23899 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]); 23900 return; 23901 } 23902 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) { 23903 if (isset($attribs['id'])) { 23904 $attribs['child_elements'] = array(); 23905 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs); 23906 return; 23907 } 23908 if (end($this->svgdefs) !== FALSE) { 23909 $last_svgdefs_id = key($this->svgdefs); 23910 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) { 23911 $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1); 23912 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs); 23913 return; 23914 } 23915 } 23916 return; 23917 } 23918 $clipping = false; 23919 if ($parser == 'clip-path') { 23920 // set clipping mode 23921 $clipping = true; 23922 } 23923 // get styling properties 23924 $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style 23925 $svgstyle = $this->svgstyles[0]; // set default style 23926 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) { 23927 // default fill attribute for clipping 23928 $attribs['fill'] = 'none'; 23929 } 23930 if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) { 23931 // fix style for regular expression 23932 $attribs['style'] = ';'.$attribs['style']; 23933 } 23934 foreach ($prev_svgstyle as $key => $val) { 23935 if (in_array($key, TCPDF_IMAGES::$svginheritprop)) { 23936 // inherit previous value 23937 $svgstyle[$key] = $val; 23938 } 23939 if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) { 23940 // specific attribute settings 23941 if ($attribs[$key] == 'inherit') { 23942 $svgstyle[$key] = $val; 23943 } else { 23944 $svgstyle[$key] = $attribs[$key]; 23945 } 23946 } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) { 23947 // CSS style syntax 23948 $attrval = array(); 23949 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) { 23950 if ($attrval[1] == 'inherit') { 23951 $svgstyle[$key] = $val; 23952 } else { 23953 $svgstyle[$key] = $attrval[1]; 23954 } 23955 } 23956 } 23957 } 23958 // transformation matrix 23959 if (!empty($ctm)) { 23960 $tm = $ctm; 23961 } else { 23962 $tm = array(1,0,0,1,0,0); 23963 } 23964 if (isset($attribs['transform']) AND !empty($attribs['transform'])) { 23965 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform'])); 23966 } 23967 $svgstyle['transfmatrix'] = $tm; 23968 $invisible = false; 23969 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) { 23970 // the current graphics element is invisible (nothing is painted) 23971 $invisible = true; 23972 } 23973 // process tag 23974 switch($name) { 23975 case 'defs': { 23976 $this->svgdefsmode = true; 23977 break; 23978 } 23979 // clipPath 23980 case 'clipPath': { 23981 if ($invisible) { 23982 break; 23983 } 23984 $this->svgclipmode = true; 23985 if (!isset($attribs['id'])) { 23986 $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1); 23987 } 23988 $this->svgclipid = $attribs['id']; 23989 $this->svgclippaths[$this->svgclipid] = array(); 23990 $this->svgcliptm[$this->svgclipid] = $tm; 23991 break; 23992 } 23993 case 'svg': { 23994 // start of SVG object 23995 break; 23996 } 23997 case 'g': { 23998 // group together related graphics elements 23999 array_push($this->svgstyles, $svgstyle); 24000 $this->StartTransform(); 24001 $x = (isset($attribs['x'])?$attribs['x']:0); 24002 $y = (isset($attribs['y'])?$attribs['y']:0); 24003 $w = 1;//(isset($attribs['width'])?$attribs['width']:1); 24004 $h = 1;//(isset($attribs['height'])?$attribs['height']:1); 24005 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y)); 24006 $this->SVGTransform($tm); 24007 $this->setSVGStyles($svgstyle, $prev_svgstyle); 24008 break; 24009 } 24010 case 'linearGradient': { 24011 if ($this->pdfa_mode) { 24012 break; 24013 } 24014 if (!isset($attribs['id'])) { 24015 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); 24016 } 24017 $this->svggradientid = $attribs['id']; 24018 $this->svggradients[$this->svggradientid] = array(); 24019 $this->svggradients[$this->svggradientid]['type'] = 2; 24020 $this->svggradients[$this->svggradientid]['stops'] = array(); 24021 if (isset($attribs['gradientUnits'])) { 24022 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 24023 } else { 24024 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 24025 } 24026 //$attribs['spreadMethod'] 24027 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2']))) 24028 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%')) 24029 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%')) 24030 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%')) 24031 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) { 24032 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 24033 } else { 24034 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 24035 } 24036 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0'); 24037 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0'); 24038 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100'); 24039 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0'); 24040 if (isset($attribs['gradientTransform'])) { 24041 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); 24042 } 24043 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2); 24044 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 24045 // gradient is defined on another place 24046 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 24047 } 24048 break; 24049 } 24050 case 'radialGradient': { 24051 if ($this->pdfa_mode) { 24052 break; 24053 } 24054 if (!isset($attribs['id'])) { 24055 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); 24056 } 24057 $this->svggradientid = $attribs['id']; 24058 $this->svggradients[$this->svggradientid] = array(); 24059 $this->svggradients[$this->svggradientid]['type'] = 3; 24060 $this->svggradients[$this->svggradientid]['stops'] = array(); 24061 if (isset($attribs['gradientUnits'])) { 24062 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 24063 } else { 24064 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 24065 } 24066 //$attribs['spreadMethod'] 24067 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy']))) 24068 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%')) 24069 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) { 24070 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 24071 } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) { 24072 $this->svggradients[$this->svggradientid]['mode'] = 'ratio'; 24073 } else { 24074 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 24075 } 24076 $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5); 24077 $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5); 24078 $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx); 24079 $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy); 24080 $r = (isset($attribs['r']) ? $attribs['r'] : 0.5); 24081 if (isset($attribs['gradientTransform'])) { 24082 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); 24083 } 24084 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r); 24085 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 24086 // gradient is defined on another place 24087 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 24088 } 24089 break; 24090 } 24091 case 'stop': { 24092 // gradient stops 24093 if (substr($attribs['offset'], -1) == '%') { 24094 $offset = floatval(substr($attribs['offset'], -1)) / 100; 24095 } else { 24096 $offset = floatval($attribs['offset']); 24097 if ($offset > 1) { 24098 $offset /= 100; 24099 } 24100 } 24101 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black'; 24102 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1; 24103 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity); 24104 break; 24105 } 24106 // paths 24107 case 'path': { 24108 if ($invisible) { 24109 break; 24110 } 24111 if (isset($attribs['d'])) { 24112 $d = trim($attribs['d']); 24113 if (!empty($d)) { 24114 $x = (isset($attribs['x'])?$attribs['x']:0); 24115 $y = (isset($attribs['y'])?$attribs['y']:0); 24116 $w = (isset($attribs['width'])?$attribs['width']:1); 24117 $h = (isset($attribs['height'])?$attribs['height']:1); 24118 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y)); 24119 if ($clipping) { 24120 $this->SVGTransform($tm); 24121 $this->SVGPath($d, 'CNZ'); 24122 } else { 24123 $this->StartTransform(); 24124 $this->SVGTransform($tm); 24125 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ')); 24126 if (!empty($obstyle)) { 24127 $this->SVGPath($d, $obstyle); 24128 } 24129 $this->StopTransform(); 24130 } 24131 } 24132 } 24133 break; 24134 } 24135 // shapes 24136 case 'rect': { 24137 if ($invisible) { 24138 break; 24139 } 24140 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 24141 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 24142 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 24143 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 24144 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); 24145 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx); 24146 if ($clipping) { 24147 $this->SVGTransform($tm); 24148 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array()); 24149 } else { 24150 $this->StartTransform(); 24151 $this->SVGTransform($tm); 24152 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ')); 24153 if (!empty($obstyle)) { 24154 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array()); 24155 } 24156 $this->StopTransform(); 24157 } 24158 break; 24159 } 24160 case 'circle': { 24161 if ($invisible) { 24162 break; 24163 } 24164 $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0); 24165 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); 24166 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); 24167 $x = ($cx - $r); 24168 $y = ($cy - $r); 24169 $w = (2 * $r); 24170 $h = $w; 24171 if ($clipping) { 24172 $this->SVGTransform($tm); 24173 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8); 24174 } else { 24175 $this->StartTransform(); 24176 $this->SVGTransform($tm); 24177 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ')); 24178 if (!empty($obstyle)) { 24179 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8); 24180 } 24181 $this->StopTransform(); 24182 } 24183 break; 24184 } 24185 case 'ellipse': { 24186 if ($invisible) { 24187 break; 24188 } 24189 $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0); 24190 $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0); 24191 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); 24192 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); 24193 $x = ($cx - $rx); 24194 $y = ($cy - $ry); 24195 $w = (2 * $rx); 24196 $h = (2 * $ry); 24197 if ($clipping) { 24198 $this->SVGTransform($tm); 24199 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); 24200 } else { 24201 $this->StartTransform(); 24202 $this->SVGTransform($tm); 24203 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ')); 24204 if (!empty($obstyle)) { 24205 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8); 24206 } 24207 $this->StopTransform(); 24208 } 24209 break; 24210 } 24211 case 'line': { 24212 if ($invisible) { 24213 break; 24214 } 24215 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0); 24216 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0); 24217 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0); 24218 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0); 24219 $x = $x1; 24220 $y = $y1; 24221 $w = abs($x2 - $x1); 24222 $h = abs($y2 - $y1); 24223 if (!$clipping) { 24224 $this->StartTransform(); 24225 $this->SVGTransform($tm); 24226 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2)); 24227 $this->Line($x1, $y1, $x2, $y2); 24228 $this->StopTransform(); 24229 } 24230 break; 24231 } 24232 case 'polyline': 24233 case 'polygon': { 24234 if ($invisible) { 24235 break; 24236 } 24237 $points = (isset($attribs['points'])?$attribs['points']:'0 0'); 24238 $points = trim($points); 24239 // note that point may use a complex syntax not covered here 24240 $points = preg_split('/[\,\s]+/si', $points); 24241 if (count($points) < 4) { 24242 break; 24243 } 24244 $p = array(); 24245 $xmin = 2147483647; 24246 $xmax = 0; 24247 $ymin = 2147483647; 24248 $ymax = 0; 24249 foreach ($points as $key => $val) { 24250 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 24251 if (($key % 2) == 0) { 24252 // X coordinate 24253 $xmin = min($xmin, $p[$key]); 24254 $xmax = max($xmax, $p[$key]); 24255 } else { 24256 // Y coordinate 24257 $ymin = min($ymin, $p[$key]); 24258 $ymax = max($ymax, $p[$key]); 24259 } 24260 } 24261 $x = $xmin; 24262 $y = $ymin; 24263 $w = ($xmax - $xmin); 24264 $h = ($ymax - $ymin); 24265 if ($name == 'polyline') { 24266 $this->StartTransform(); 24267 $this->SVGTransform($tm); 24268 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); 24269 if (!empty($obstyle)) { 24270 $this->PolyLine($p, $obstyle, array(), array()); 24271 } 24272 $this->StopTransform(); 24273 } else { // polygon 24274 if ($clipping) { 24275 $this->SVGTransform($tm); 24276 $this->Polygon($p, 'CNZ', array(), array(), true); 24277 } else { 24278 $this->StartTransform(); 24279 $this->SVGTransform($tm); 24280 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ')); 24281 if (!empty($obstyle)) { 24282 $this->Polygon($p, $obstyle, array(), array(), true); 24283 } 24284 $this->StopTransform(); 24285 } 24286 } 24287 break; 24288 } 24289 // image 24290 case 'image': { 24291 if ($invisible) { 24292 break; 24293 } 24294 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) { 24295 break; 24296 } 24297 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 24298 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 24299 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 24300 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 24301 $img = $attribs['xlink:href']; 24302 if (!$clipping) { 24303 $this->StartTransform(); 24304 $this->SVGTransform($tm); 24305 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h); 24306 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) { 24307 // embedded image encoded as base64 24308 $img = '@'.base64_decode(substr($img, strlen($m[0]))); 24309 } else { 24310 // fix image path 24311 if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) { 24312 // replace relative path with full server path 24313 $img = $this->svgdir.'/'.$img; 24314 } 24315 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { 24316 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']); 24317 if (($findroot === false) OR ($findroot > 1)) { 24318 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { 24319 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img; 24320 } else { 24321 $img = $_SERVER['DOCUMENT_ROOT'].$img; 24322 } 24323 } 24324 } 24325 $img = urldecode($img); 24326 $testscrtype = @parse_url($img); 24327 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { 24328 // convert URL to server path 24329 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img); 24330 } 24331 } 24332 // get image type 24333 $imgtype = TCPDF_IMAGES::getImageFileType($img); 24334 if (($imgtype == 'eps') OR ($imgtype == 'ai')) { 24335 $this->ImageEps($img, $x, $y, $w, $h); 24336 } elseif ($imgtype == 'svg') { 24337 $this->ImageSVG($img, $x, $y, $w, $h); 24338 } else { 24339 $this->Image($img, $x, $y, $w, $h); 24340 } 24341 $this->StopTransform(); 24342 } 24343 break; 24344 } 24345 // text 24346 case 'text': 24347 case 'tspan': { 24348 // only basic support - advanced features must be implemented 24349 $this->svgtextmode['invisible'] = $invisible; 24350 if ($invisible) { 24351 break; 24352 } 24353 array_push($this->svgstyles, $svgstyle); 24354 if (isset($attribs['x'])) { 24355 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false); 24356 } elseif ($name == 'tspan') { 24357 $x = $this->x; 24358 } else { 24359 $x = 0; 24360 } 24361 if (isset($attribs['dx'])) { 24362 $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false); 24363 } 24364 if (isset($attribs['y'])) { 24365 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false); 24366 } elseif ($name == 'tspan') { 24367 $y = $this->y; 24368 } else { 24369 $y = 0; 24370 } 24371 if (isset($attribs['dy'])) { 24372 $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false); 24373 } 24374 $svgstyle['text-color'] = $svgstyle['fill']; 24375 $this->svgtext = ''; 24376 if (isset($svgstyle['text-anchor'])) { 24377 $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor']; 24378 } else { 24379 $this->svgtextmode['text-anchor'] = 'start'; 24380 } 24381 if (isset($svgstyle['direction'])) { 24382 if ($svgstyle['direction'] == 'rtl') { 24383 $this->svgtextmode['rtl'] = true; 24384 } else { 24385 $this->svgtextmode['rtl'] = false; 24386 } 24387 } else { 24388 $this->svgtextmode['rtl'] = false; 24389 } 24390 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) { 24391 $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false); 24392 } else { 24393 $this->svgtextmode['stroke'] = false; 24394 } 24395 $this->StartTransform(); 24396 $this->SVGTransform($tm); 24397 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1); 24398 $this->x = $x; 24399 $this->y = $y; 24400 break; 24401 } 24402 // use 24403 case 'use': { 24404 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 24405 $svgdefid = substr($attribs['xlink:href'], 1); 24406 if (isset($this->svgdefs[$svgdefid])) { 24407 $use = $this->svgdefs[$svgdefid]; 24408 if (isset($attribs['xlink:href'])) { 24409 unset($attribs['xlink:href']); 24410 } 24411 if (isset($attribs['id'])) { 24412 unset($attribs['id']); 24413 } 24414 if (isset($use['attribs']['x']) AND isset($attribs['x'])) { 24415 $attribs['x'] += $use['attribs']['x']; 24416 } 24417 if (isset($use['attribs']['y']) AND isset($attribs['y'])) { 24418 $attribs['y'] += $use['attribs']['y']; 24419 } 24420 if (empty($attribs['style'])) { 24421 $attribs['style'] = ''; 24422 } 24423 if (!empty($use['attribs']['style'])) { 24424 // merge styles 24425 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']); 24426 } 24427 $attribs = array_merge($use['attribs'], $attribs); 24428 $this->startSVGElementHandler($parser, $use['name'], $attribs); 24429 return; 24430 } 24431 } 24432 break; 24433 } 24434 default: { 24435 break; 24436 } 24437 } // end of switch 24438 // process child elements 24439 if (!empty($attribs['child_elements'])) { 24440 $child_elements = $attribs['child_elements']; 24441 unset($attribs['child_elements']); 24442 foreach($child_elements as $child_element) { 24443 if (empty($child_element['attribs']['closing_tag'])) { 24444 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']); 24445 } else { 24446 if (isset($child_element['attribs']['content'])) { 24447 $this->svgtext = $child_element['attribs']['content']; 24448 } 24449 $this->endSVGElementHandler('child-tag', $child_element['name']); 24450 } 24451 } 24452 } 24453 } 24454 24455 /** 24456 * Sets the closing SVG element handler function for the XML parser. 24457 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 24458 * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. 24459 * @author Nicola Asuni 24460 * @since 5.0.000 (2010-05-02) 24461 * @protected 24462 */ 24463 protected function endSVGElementHandler($parser, $name) { 24464 if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {; 24465 if (end($this->svgdefs) !== FALSE) { 24466 $last_svgdefs_id = key($this->svgdefs); 24467 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) { 24468 foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) { 24469 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) { 24470 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext)); 24471 return; 24472 } 24473 } 24474 if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) { 24475 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext)); 24476 return; 24477 } 24478 } 24479 } 24480 return; 24481 } 24482 switch($name) { 24483 case 'defs': { 24484 $this->svgdefsmode = false; 24485 break; 24486 } 24487 // clipPath 24488 case 'clipPath': { 24489 $this->svgclipmode = false; 24490 break; 24491 } 24492 case 'g': { 24493 // ungroup: remove last style from array 24494 array_pop($this->svgstyles); 24495 $this->StopTransform(); 24496 break; 24497 } 24498 case 'text': 24499 case 'tspan': { 24500 if ($this->svgtextmode['invisible']) { 24501 // This implementation must be fixed to following the rule: 24502 // If the 'visibility' property is set to hidden on a 'tspan', 'tref' or 'altGlyph' element, then the text is invisible but still takes up space in text layout calculations. 24503 break; 24504 } 24505 // print text 24506 $text = $this->svgtext; 24507 //$text = $this->stringTrim($text); 24508 $textlen = $this->GetStringWidth($text); 24509 if ($this->svgtextmode['text-anchor'] != 'start') { 24510 // check if string is RTL text 24511 if ($this->svgtextmode['text-anchor'] == 'end') { 24512 if ($this->svgtextmode['rtl']) { 24513 $this->x += $textlen; 24514 } else { 24515 $this->x -= $textlen; 24516 } 24517 } elseif ($this->svgtextmode['text-anchor'] == 'middle') { 24518 if ($this->svgtextmode['rtl']) { 24519 $this->x += ($textlen / 2); 24520 } else { 24521 $this->x -= ($textlen / 2); 24522 } 24523 } 24524 } 24525 $textrendermode = $this->textrendermode; 24526 $textstrokewidth = $this->textstrokewidth; 24527 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false); 24528 if ($name == 'text') { 24529 // store current coordinates 24530 $tmpx = $this->x; 24531 $tmpy = $this->y; 24532 } 24533 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T'); 24534 if ($name == 'text') { 24535 // restore coordinates 24536 $this->x = $tmpx; 24537 $this->y = $tmpy; 24538 } 24539 // restore previous rendering mode 24540 $this->textrendermode = $textrendermode; 24541 $this->textstrokewidth = $textstrokewidth; 24542 $this->svgtext = ''; 24543 $this->StopTransform(); 24544 if (!$this->svgdefsmode) { 24545 array_pop($this->svgstyles); 24546 } 24547 break; 24548 } 24549 default: { 24550 break; 24551 } 24552 } 24553 } 24554 24555 /** 24556 * Sets the character data handler function for the XML parser. 24557 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 24558 * @param $data (string) The second parameter, data, contains the character data as a string. 24559 * @author Nicola Asuni 24560 * @since 5.0.000 (2010-05-02) 24561 * @protected 24562 */ 24563 protected function segSVGContentHandler($parser, $data) { 24564 $this->svgtext .= $data; 24565 } 24566 24567 // --- END SVG METHODS ----------------------------------------------------- 24568 24569 } // END OF TCPDF CLASS 24570 24571 //============================================================+ 24572 // END OF FILE 24573 //============================================================+
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |