[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 //============================================================+ 3 // File name : tcpdf.php 4 // Version : 6.2.12 5 // Begin : 2002-08-03 6 // Last Update : 2015-06-18 7 // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com 8 // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) 9 // ------------------------------------------------------------------- 10 // Copyright (C) 2002-2015 Nicola Asuni - Tecnick.com LTD 11 // 12 // This file is part of TCPDF software library. 13 // 14 // TCPDF is free software: you can redistribute 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 Extension, 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 Extension, 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.2.8 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.2.8 132 * @author Nicola Asuni - info@tecnick.com 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 * Depth of the svg tag, to keep track if the svg tag is a subtag or the root tag. 346 * @protected 347 */ 348 protected $svg_tag_depth = 0; 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 * Counts the number of fonts. 1188 * @protected 1189 * @since 4.5.000 (2009-01-02) 1190 */ 1191 protected $numfonts = 0; 1192 1193 /** 1194 * Store the font keys. 1195 * @protected 1196 * @since 4.5.000 (2009-01-02) 1197 */ 1198 protected $fontkeys = array(); 1199 1200 /** 1201 * Store the font object IDs. 1202 * @protected 1203 * @since 4.8.001 (2009-09-09) 1204 */ 1205 protected $font_obj_ids = array(); 1206 1207 /** 1208 * Store the fage status (true when opened, false when closed). 1209 * @protected 1210 * @since 4.5.000 (2009-01-02) 1211 */ 1212 protected $pageopen = array(); 1213 1214 /** 1215 * Default monospace font. 1216 * @protected 1217 * @since 4.5.025 (2009-03-10) 1218 */ 1219 protected $default_monospaced_font = 'courier'; 1220 1221 /** 1222 * Cloned copy of the current class object. 1223 * @protected 1224 * @since 4.5.029 (2009-03-19) 1225 */ 1226 protected $objcopy; 1227 1228 /** 1229 * Array used to store the lengths of cache files. 1230 * @protected 1231 * @since 4.5.029 (2009-03-19) 1232 */ 1233 protected $cache_file_length = array(); 1234 1235 /** 1236 * Table header content to be repeated on each new page. 1237 * @protected 1238 * @since 4.5.030 (2009-03-20) 1239 */ 1240 protected $thead = ''; 1241 1242 /** 1243 * Margins used for table header. 1244 * @protected 1245 * @since 4.5.030 (2009-03-20) 1246 */ 1247 protected $theadMargins = array(); 1248 1249 /** 1250 * Boolean flag to enable document digital signature. 1251 * @protected 1252 * @since 4.6.005 (2009-04-24) 1253 */ 1254 protected $sign = false; 1255 1256 /** 1257 * Digital signature data. 1258 * @protected 1259 * @since 4.6.005 (2009-04-24) 1260 */ 1261 protected $signature_data = array(); 1262 1263 /** 1264 * Digital signature max length. 1265 * @protected 1266 * @since 4.6.005 (2009-04-24) 1267 */ 1268 protected $signature_max_length = 11742; 1269 1270 /** 1271 * Data for digital signature appearance. 1272 * @protected 1273 * @since 5.3.011 (2010-06-16) 1274 */ 1275 protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); 1276 1277 /** 1278 * Array of empty digital signature appearances. 1279 * @protected 1280 * @since 5.9.101 (2011-07-06) 1281 */ 1282 protected $empty_signature_appearance = array(); 1283 1284 /** 1285 * Boolean flag to enable document timestamping with TSA. 1286 * @protected 1287 * @since 6.0.085 (2014-06-19) 1288 */ 1289 protected $tsa_timestamp = false; 1290 1291 /** 1292 * Timestamping data. 1293 * @protected 1294 * @since 6.0.085 (2014-06-19) 1295 */ 1296 protected $tsa_data = array(); 1297 1298 /** 1299 * Regular expression used to find blank characters (required for word-wrapping). 1300 * @protected 1301 * @since 4.6.006 (2009-04-28) 1302 */ 1303 protected $re_spaces = '/[^\S\xa0]/'; 1304 1305 /** 1306 * Array of $re_spaces parts. 1307 * @protected 1308 * @since 5.5.011 (2010-07-09) 1309 */ 1310 protected $re_space = array('p' => '[^\S\xa0]', 'm' => ''); 1311 1312 /** 1313 * Digital signature object ID. 1314 * @protected 1315 * @since 4.6.022 (2009-06-23) 1316 */ 1317 protected $sig_obj_id = 0; 1318 1319 /** 1320 * ID of page objects. 1321 * @protected 1322 * @since 4.7.000 (2009-08-29) 1323 */ 1324 protected $page_obj_id = array(); 1325 1326 /** 1327 * List of form annotations IDs. 1328 * @protected 1329 * @since 4.8.000 (2009-09-07) 1330 */ 1331 protected $form_obj_id = array(); 1332 1333 /** 1334 * 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. 1335 * @protected 1336 * @since 4.8.000 (2009-09-07) 1337 */ 1338 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 1339 1340 /** 1341 * Javascript objects array. 1342 * @protected 1343 * @since 4.8.000 (2009-09-07) 1344 */ 1345 protected $js_objects = array(); 1346 1347 /** 1348 * Current form action (used during XHTML rendering). 1349 * @protected 1350 * @since 4.8.000 (2009-09-07) 1351 */ 1352 protected $form_action = ''; 1353 1354 /** 1355 * Current form encryption type (used during XHTML rendering). 1356 * @protected 1357 * @since 4.8.000 (2009-09-07) 1358 */ 1359 protected $form_enctype = 'application/x-www-form-urlencoded'; 1360 1361 /** 1362 * Current method to submit forms. 1363 * @protected 1364 * @since 4.8.000 (2009-09-07) 1365 */ 1366 protected $form_mode = 'post'; 1367 1368 /** 1369 * List of fonts used on form fields (fontname => fontkey). 1370 * @protected 1371 * @since 4.8.001 (2009-09-09) 1372 */ 1373 protected $annotation_fonts = array(); 1374 1375 /** 1376 * List of radio buttons parent objects. 1377 * @protected 1378 * @since 4.8.001 (2009-09-09) 1379 */ 1380 protected $radiobutton_groups = array(); 1381 1382 /** 1383 * List of radio group objects IDs. 1384 * @protected 1385 * @since 4.8.001 (2009-09-09) 1386 */ 1387 protected $radio_groups = array(); 1388 1389 /** 1390 * Text indentation value (used for text-indent CSS attribute). 1391 * @protected 1392 * @since 4.8.006 (2009-09-23) 1393 */ 1394 protected $textindent = 0; 1395 1396 /** 1397 * Store page number when startTransaction() is called. 1398 * @protected 1399 * @since 4.8.006 (2009-09-23) 1400 */ 1401 protected $start_transaction_page = 0; 1402 1403 /** 1404 * Store Y position when startTransaction() is called. 1405 * @protected 1406 * @since 4.9.001 (2010-03-28) 1407 */ 1408 protected $start_transaction_y = 0; 1409 1410 /** 1411 * True when we are printing the thead section on a new page. 1412 * @protected 1413 * @since 4.8.027 (2010-01-25) 1414 */ 1415 protected $inthead = false; 1416 1417 /** 1418 * Array of column measures (width, space, starting Y position). 1419 * @protected 1420 * @since 4.9.001 (2010-03-28) 1421 */ 1422 protected $columns = array(); 1423 1424 /** 1425 * Number of colums. 1426 * @protected 1427 * @since 4.9.001 (2010-03-28) 1428 */ 1429 protected $num_columns = 1; 1430 1431 /** 1432 * Current column number. 1433 * @protected 1434 * @since 4.9.001 (2010-03-28) 1435 */ 1436 protected $current_column = 0; 1437 1438 /** 1439 * Starting page for columns. 1440 * @protected 1441 * @since 4.9.001 (2010-03-28) 1442 */ 1443 protected $column_start_page = 0; 1444 1445 /** 1446 * Maximum page and column selected. 1447 * @protected 1448 * @since 5.8.000 (2010-08-11) 1449 */ 1450 protected $maxselcol = array('page' => 0, 'column' => 0); 1451 1452 /** 1453 * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding. 1454 * @protected 1455 * @since 5.8.000 (2010-08-11) 1456 */ 1457 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 1458 1459 /** 1460 * 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. 1461 * @protected 1462 * @since 4.9.008 (2010-04-03) 1463 */ 1464 protected $textrendermode = 0; 1465 1466 /** 1467 * Text stroke width in doc units. 1468 * @protected 1469 * @since 4.9.008 (2010-04-03) 1470 */ 1471 protected $textstrokewidth = 0; 1472 1473 /** 1474 * Current stroke color. 1475 * @protected 1476 * @since 4.9.008 (2010-04-03) 1477 */ 1478 protected $strokecolor; 1479 1480 /** 1481 * Default unit of measure for document. 1482 * @protected 1483 * @since 5.0.000 (2010-04-22) 1484 */ 1485 protected $pdfunit = 'mm'; 1486 1487 /** 1488 * Boolean flag true when we are on TOC (Table Of Content) page. 1489 * @protected 1490 */ 1491 protected $tocpage = false; 1492 1493 /** 1494 * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library. 1495 * @protected 1496 * @since 5.0.000 (2010-04-26) 1497 */ 1498 protected $rasterize_vector_images = false; 1499 1500 /** 1501 * Boolean flag: if true enables font subsetting by default. 1502 * @protected 1503 * @since 5.3.002 (2010-06-07) 1504 */ 1505 protected $font_subsetting = true; 1506 1507 /** 1508 * Array of default graphic settings. 1509 * @protected 1510 * @since 5.5.008 (2010-07-02) 1511 */ 1512 protected $default_graphic_vars = array(); 1513 1514 /** 1515 * Array of XObjects. 1516 * @protected 1517 * @since 5.8.014 (2010-08-23) 1518 */ 1519 protected $xobjects = array(); 1520 1521 /** 1522 * Boolean value true when we are inside an XObject. 1523 * @protected 1524 * @since 5.8.017 (2010-08-24) 1525 */ 1526 protected $inxobj = false; 1527 1528 /** 1529 * Current XObject ID. 1530 * @protected 1531 * @since 5.8.017 (2010-08-24) 1532 */ 1533 protected $xobjid = ''; 1534 1535 /** 1536 * Percentage of character stretching. 1537 * @protected 1538 * @since 5.9.000 (2010-09-29) 1539 */ 1540 protected $font_stretching = 100; 1541 1542 /** 1543 * Increases or decreases the space between characters in a text by the specified amount (tracking). 1544 * @protected 1545 * @since 5.9.000 (2010-09-29) 1546 */ 1547 protected $font_spacing = 0; 1548 1549 /** 1550 * Array of no-write regions. 1551 * ('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) 1552 * @protected 1553 * @since 5.9.003 (2010-10-14) 1554 */ 1555 protected $page_regions = array(); 1556 1557 /** 1558 * Boolean value true when page region check is active. 1559 * @protected 1560 */ 1561 protected $check_page_regions = true; 1562 1563 /** 1564 * Array of PDF layers data. 1565 * @protected 1566 * @since 5.9.102 (2011-07-13) 1567 */ 1568 protected $pdflayers = array(); 1569 1570 /** 1571 * A dictionary of names and corresponding destinations (Dests key on document Catalog). 1572 * @protected 1573 * @since 5.9.097 (2011-06-23) 1574 */ 1575 protected $dests = array(); 1576 1577 /** 1578 * Object ID for Named Destinations 1579 * @protected 1580 * @since 5.9.097 (2011-06-23) 1581 */ 1582 protected $n_dests; 1583 1584 /** 1585 * Embedded Files Names 1586 * @protected 1587 * @since 5.9.204 (2013-01-23) 1588 */ 1589 protected $efnames = array(); 1590 1591 /** 1592 * Directory used for the last SVG image. 1593 * @protected 1594 * @since 5.0.000 (2010-05-05) 1595 */ 1596 protected $svgdir = ''; 1597 1598 /** 1599 * Deafult unit of measure for SVG. 1600 * @protected 1601 * @since 5.0.000 (2010-05-02) 1602 */ 1603 protected $svgunit = 'px'; 1604 1605 /** 1606 * Array of SVG gradients. 1607 * @protected 1608 * @since 5.0.000 (2010-05-02) 1609 */ 1610 protected $svggradients = array(); 1611 1612 /** 1613 * ID of last SVG gradient. 1614 * @protected 1615 * @since 5.0.000 (2010-05-02) 1616 */ 1617 protected $svggradientid = 0; 1618 1619 /** 1620 * Boolean value true when in SVG defs group. 1621 * @protected 1622 * @since 5.0.000 (2010-05-02) 1623 */ 1624 protected $svgdefsmode = false; 1625 1626 /** 1627 * Array of SVG defs. 1628 * @protected 1629 * @since 5.0.000 (2010-05-02) 1630 */ 1631 protected $svgdefs = array(); 1632 1633 /** 1634 * Boolean value true when in SVG clipPath tag. 1635 * @protected 1636 * @since 5.0.000 (2010-04-26) 1637 */ 1638 protected $svgclipmode = false; 1639 1640 /** 1641 * Array of SVG clipPath commands. 1642 * @protected 1643 * @since 5.0.000 (2010-05-02) 1644 */ 1645 protected $svgclippaths = array(); 1646 1647 /** 1648 * Array of SVG clipPath tranformation matrix. 1649 * @protected 1650 * @since 5.8.022 (2010-08-31) 1651 */ 1652 protected $svgcliptm = array(); 1653 1654 /** 1655 * ID of last SVG clipPath. 1656 * @protected 1657 * @since 5.0.000 (2010-05-02) 1658 */ 1659 protected $svgclipid = 0; 1660 1661 /** 1662 * SVG text. 1663 * @protected 1664 * @since 5.0.000 (2010-05-02) 1665 */ 1666 protected $svgtext = ''; 1667 1668 /** 1669 * SVG text properties. 1670 * @protected 1671 * @since 5.8.013 (2010-08-23) 1672 */ 1673 protected $svgtextmode = array(); 1674 1675 /** 1676 * Array of SVG properties. 1677 * @protected 1678 * @since 5.0.000 (2010-05-02) 1679 */ 1680 protected $svgstyles = array(array( 1681 'alignment-baseline' => 'auto', 1682 'baseline-shift' => 'baseline', 1683 'clip' => 'auto', 1684 'clip-path' => 'none', 1685 'clip-rule' => 'nonzero', 1686 'color' => 'black', 1687 'color-interpolation' => 'sRGB', 1688 'color-interpolation-filters' => 'linearRGB', 1689 'color-profile' => 'auto', 1690 'color-rendering' => 'auto', 1691 'cursor' => 'auto', 1692 'direction' => 'ltr', 1693 'display' => 'inline', 1694 'dominant-baseline' => 'auto', 1695 'enable-background' => 'accumulate', 1696 'fill' => 'black', 1697 'fill-opacity' => 1, 1698 'fill-rule' => 'nonzero', 1699 'filter' => 'none', 1700 'flood-color' => 'black', 1701 'flood-opacity' => 1, 1702 'font' => '', 1703 'font-family' => 'helvetica', 1704 'font-size' => 'medium', 1705 'font-size-adjust' => 'none', 1706 'font-stretch' => 'normal', 1707 'font-style' => 'normal', 1708 'font-variant' => 'normal', 1709 'font-weight' => 'normal', 1710 'glyph-orientation-horizontal' => '0deg', 1711 'glyph-orientation-vertical' => 'auto', 1712 'image-rendering' => 'auto', 1713 'kerning' => 'auto', 1714 'letter-spacing' => 'normal', 1715 'lighting-color' => 'white', 1716 'marker' => '', 1717 'marker-end' => 'none', 1718 'marker-mid' => 'none', 1719 'marker-start' => 'none', 1720 'mask' => 'none', 1721 'opacity' => 1, 1722 'overflow' => 'auto', 1723 'pointer-events' => 'visiblePainted', 1724 'shape-rendering' => 'auto', 1725 'stop-color' => 'black', 1726 'stop-opacity' => 1, 1727 'stroke' => 'none', 1728 'stroke-dasharray' => 'none', 1729 'stroke-dashoffset' => 0, 1730 'stroke-linecap' => 'butt', 1731 'stroke-linejoin' => 'miter', 1732 'stroke-miterlimit' => 4, 1733 'stroke-opacity' => 1, 1734 'stroke-width' => 1, 1735 'text-anchor' => 'start', 1736 'text-decoration' => 'none', 1737 'text-rendering' => 'auto', 1738 'unicode-bidi' => 'normal', 1739 'visibility' => 'visible', 1740 'word-spacing' => 'normal', 1741 'writing-mode' => 'lr-tb', 1742 'text-color' => 'black', 1743 'transfmatrix' => array(1, 0, 0, 1, 0, 0) 1744 )); 1745 1746 /** 1747 * If true force sRGB color profile for all document. 1748 * @protected 1749 * @since 5.9.121 (2011-09-28) 1750 */ 1751 protected $force_srgb = false; 1752 1753 /** 1754 * If true set the document to PDF/A mode. 1755 * @protected 1756 * @since 5.9.121 (2011-09-27) 1757 */ 1758 protected $pdfa_mode = false; 1759 1760 /** 1761 * Document creation date-time 1762 * @protected 1763 * @since 5.9.152 (2012-03-22) 1764 */ 1765 protected $doc_creation_timestamp; 1766 1767 /** 1768 * Document modification date-time 1769 * @protected 1770 * @since 5.9.152 (2012-03-22) 1771 */ 1772 protected $doc_modification_timestamp; 1773 1774 /** 1775 * Custom XMP data. 1776 * @protected 1777 * @since 5.9.128 (2011-10-06) 1778 */ 1779 protected $custom_xmp = ''; 1780 1781 /** 1782 * Overprint mode array. 1783 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 1784 * @protected 1785 * @since 5.9.152 (2012-03-23) 1786 */ 1787 protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0); 1788 1789 /** 1790 * Alpha mode array. 1791 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 1792 * @protected 1793 * @since 5.9.152 (2012-03-23) 1794 */ 1795 protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false); 1796 1797 /** 1798 * Define the page boundaries boxes to be set on document. 1799 * @protected 1800 * @since 5.9.152 (2012-03-23) 1801 */ 1802 protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); 1803 1804 /** 1805 * If true print TCPDF meta link. 1806 * @protected 1807 * @since 5.9.152 (2012-03-23) 1808 */ 1809 protected $tcpdflink = true; 1810 1811 /** 1812 * Cache array for computed GD gamma values. 1813 * @protected 1814 * @since 5.9.1632 (2012-06-05) 1815 */ 1816 protected $gdgammacache = array(); 1817 1818 //------------------------------------------------------------ 1819 // METHODS 1820 //------------------------------------------------------------ 1821 1822 /** 1823 * This is the class constructor. 1824 * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes). 1825 * 1826 * 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. 1827 * 1828 * @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> 1829 * @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. 1830 * @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(). 1831 * @param $unicode (boolean) TRUE means that the input text is unicode (default = true) 1832 * @param $encoding (string) Charset encoding (used only when converting back html entities); default is UTF-8. 1833 * @param $diskcache (boolean) DEPRECATED FEATURE 1834 * @param $pdfa (boolean) If TRUE set the document to PDF/A mode. 1835 * @public 1836 * @see getPageSizeFromFormat(), setPageFormat() 1837 */ 1838 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) { 1839 /* Set internal character encoding to ASCII */ 1840 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) { 1841 $this->internal_encoding = mb_internal_encoding(); 1842 mb_internal_encoding('ASCII'); 1843 } 1844 // set file ID for trailer 1845 $serformat = (is_array($format) ? json_encode($format) : $format); 1846 $this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding)); 1847 $this->font_obj_ids = array(); 1848 $this->page_obj_id = array(); 1849 $this->form_obj_id = array(); 1850 // set pdf/a mode 1851 $this->pdfa_mode = $pdfa; 1852 $this->force_srgb = false; 1853 // set language direction 1854 $this->rtl = false; 1855 $this->tmprtl = false; 1856 // some checks 1857 $this->_dochecks(); 1858 // initialization of properties 1859 $this->isunicode = $unicode; 1860 $this->page = 0; 1861 $this->transfmrk[0] = array(); 1862 $this->pagedim = array(); 1863 $this->n = 2; 1864 $this->buffer = ''; 1865 $this->pages = array(); 1866 $this->state = 0; 1867 $this->fonts = array(); 1868 $this->FontFiles = array(); 1869 $this->diffs = array(); 1870 $this->images = array(); 1871 $this->links = array(); 1872 $this->gradients = array(); 1873 $this->InFooter = false; 1874 $this->lasth = 0; 1875 $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica'; 1876 $this->FontStyle = ''; 1877 $this->FontSizePt = 12; 1878 $this->underline = false; 1879 $this->overline = false; 1880 $this->linethrough = false; 1881 $this->DrawColor = '0 G'; 1882 $this->FillColor = '0 g'; 1883 $this->TextColor = '0 g'; 1884 $this->ColorFlag = false; 1885 $this->pdflayers = array(); 1886 // encryption values 1887 $this->encrypted = false; 1888 $this->last_enc_key = ''; 1889 // standard Unicode fonts 1890 $this->CoreFonts = array( 1891 'courier'=>'Courier', 1892 'courierB'=>'Courier-Bold', 1893 'courierI'=>'Courier-Oblique', 1894 'courierBI'=>'Courier-BoldOblique', 1895 'helvetica'=>'Helvetica', 1896 'helveticaB'=>'Helvetica-Bold', 1897 'helveticaI'=>'Helvetica-Oblique', 1898 'helveticaBI'=>'Helvetica-BoldOblique', 1899 'times'=>'Times-Roman', 1900 'timesB'=>'Times-Bold', 1901 'timesI'=>'Times-Italic', 1902 'timesBI'=>'Times-BoldItalic', 1903 'symbol'=>'Symbol', 1904 'zapfdingbats'=>'ZapfDingbats' 1905 ); 1906 // set scale factor 1907 $this->setPageUnit($unit); 1908 // set page format and orientation 1909 $this->setPageFormat($format, $orientation); 1910 // page margins (1 cm) 1911 $margin = 28.35 / $this->k; 1912 $this->SetMargins($margin, $margin); 1913 $this->clMargin = $this->lMargin; 1914 $this->crMargin = $this->rMargin; 1915 // internal cell padding 1916 $cpadding = $margin / 10; 1917 $this->setCellPaddings($cpadding, 0, $cpadding, 0); 1918 // cell margins 1919 $this->setCellMargins(0, 0, 0, 0); 1920 // line width (0.2 mm) 1921 $this->LineWidth = 0.57 / $this->k; 1922 $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k)); 1923 $this->linestyleCap = '0 J'; 1924 $this->linestyleJoin = '0 j'; 1925 $this->linestyleDash = '[] 0 d'; 1926 // automatic page break 1927 $this->SetAutoPageBreak(true, (2 * $margin)); 1928 // full width display mode 1929 $this->SetDisplayMode('fullwidth'); 1930 // compression 1931 $this->SetCompression(); 1932 // set default PDF version number 1933 $this->setPDFVersion(); 1934 $this->tcpdflink = true; 1935 $this->encoding = $encoding; 1936 $this->HREF = array(); 1937 $this->getFontsList(); 1938 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0); 1939 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0); 1940 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255); 1941 $this->extgstates = array(); 1942 $this->setTextShadow(); 1943 // signature 1944 $this->sign = false; 1945 $this->tsa_timestamp = false; 1946 $this->tsa_data = array(); 1947 $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature'); 1948 $this->empty_signature_appearance = array(); 1949 // user's rights 1950 $this->ur['enabled'] = false; 1951 $this->ur['document'] = '/FullSave'; 1952 $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export'; 1953 $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate'; 1954 $this->ur['signature'] = '/Modify'; 1955 $this->ur['ef'] = '/Create/Delete/Modify/Import'; 1956 $this->ur['formex'] = ''; 1957 // set default JPEG quality 1958 $this->jpeg_quality = 75; 1959 // initialize some settings 1960 TCPDF_FONTS::utf8Bidi(array(''), '', false, $this->isunicode, $this->CurrentFont); 1961 // set default font 1962 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 1963 $this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt)); 1964 $this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt)); 1965 // check if PCRE Unicode support is enabled 1966 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) { 1967 // PCRE unicode support is turned ON 1968 // \s : any whitespace character 1969 // \p{Z} : any separator 1970 // \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words. 1971 // \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0) 1972 //$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u'); 1973 $this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u'); 1974 } else { 1975 // PCRE unicode support is turned OFF 1976 $this->setSpacesRE('/[^\S\xa0]/'); 1977 } 1978 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 1979 // set document creation and modification timestamp 1980 $this->doc_creation_timestamp = time(); 1981 $this->doc_modification_timestamp = $this->doc_creation_timestamp; 1982 // get default graphic vars 1983 $this->default_graphic_vars = $this->getGraphicVars(); 1984 $this->header_xobj_autoreset = false; 1985 $this->custom_xmp = ''; 1986 // Call cleanup method after script execution finishes or exit() is called. 1987 // NOTE: This will not be executed if the process is killed with a SIGTERM or SIGKILL signal. 1988 register_shutdown_function(array($this, '_destroy'), true); 1989 } 1990 1991 /** 1992 * Default destructor. 1993 * @public 1994 * @since 1.53.0.TC016 1995 */ 1996 public function __destruct() { 1997 // restore internal encoding 1998 if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { 1999 mb_internal_encoding($this->internal_encoding); 2000 } 2001 // cleanup 2002 $this->_destroy(true); 2003 } 2004 2005 /** 2006 * Set the units of measure for the document. 2007 * @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. 2008 * @public 2009 * @since 3.0.015 (2008-06-06) 2010 */ 2011 public function setPageUnit($unit) { 2012 $unit = strtolower($unit); 2013 //Set scale factor 2014 switch ($unit) { 2015 // points 2016 case 'px': 2017 case 'pt': { 2018 $this->k = 1; 2019 break; 2020 } 2021 // millimeters 2022 case 'mm': { 2023 $this->k = $this->dpi / 25.4; 2024 break; 2025 } 2026 // centimeters 2027 case 'cm': { 2028 $this->k = $this->dpi / 2.54; 2029 break; 2030 } 2031 // inches 2032 case 'in': { 2033 $this->k = $this->dpi; 2034 break; 2035 } 2036 // unsupported unit 2037 default : { 2038 $this->Error('Incorrect unit: '.$unit); 2039 break; 2040 } 2041 } 2042 $this->pdfunit = $unit; 2043 if (isset($this->CurOrientation)) { 2044 $this->setPageOrientation($this->CurOrientation); 2045 } 2046 } 2047 2048 /** 2049 * Change the format of the current page 2050 * @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> 2051 * <li>['format'] = page format name (one of the above);</li> 2052 * <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> 2053 * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li> 2054 * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li> 2055 * <li>['MediaBox']['llx'] : lower-left x coordinate</li> 2056 * <li>['MediaBox']['lly'] : lower-left y coordinate</li> 2057 * <li>['MediaBox']['urx'] : upper-right x coordinate</li> 2058 * <li>['MediaBox']['ury'] : upper-right y coordinate</li> 2059 * <li>['CropBox'] : the visible region of default user space:</li> 2060 * <li>['CropBox']['llx'] : lower-left x coordinate</li> 2061 * <li>['CropBox']['lly'] : lower-left y coordinate</li> 2062 * <li>['CropBox']['urx'] : upper-right x coordinate</li> 2063 * <li>['CropBox']['ury'] : upper-right y coordinate</li> 2064 * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li> 2065 * <li>['BleedBox']['llx'] : lower-left x coordinate</li> 2066 * <li>['BleedBox']['lly'] : lower-left y coordinate</li> 2067 * <li>['BleedBox']['urx'] : upper-right x coordinate</li> 2068 * <li>['BleedBox']['ury'] : upper-right y coordinate</li> 2069 * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li> 2070 * <li>['TrimBox']['llx'] : lower-left x coordinate</li> 2071 * <li>['TrimBox']['lly'] : lower-left y coordinate</li> 2072 * <li>['TrimBox']['urx'] : upper-right x coordinate</li> 2073 * <li>['TrimBox']['ury'] : upper-right y coordinate</li> 2074 * <li>['ArtBox'] : the extent of the page's meaningful content:</li> 2075 * <li>['ArtBox']['llx'] : lower-left x coordinate</li> 2076 * <li>['ArtBox']['lly'] : lower-left y coordinate</li> 2077 * <li>['ArtBox']['urx'] : upper-right x coordinate</li> 2078 * <li>['ArtBox']['ury'] : upper-right y coordinate</li> 2079 * <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> 2080 * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li> 2081 * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li> 2082 * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li> 2083 * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li> 2084 * <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> 2085 * <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> 2086 * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li> 2087 * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li> 2088 * <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> 2089 * <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> 2090 * <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> 2091 * <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> 2092 * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li> 2093 * </ul> 2094 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul> 2095 * <li>P or Portrait (default)</li> 2096 * <li>L or Landscape</li> 2097 * <li>'' (empty string) for automatic orientation</li> 2098 * </ul> 2099 * @protected 2100 * @since 3.0.015 (2008-06-06) 2101 * @see getPageSizeFromFormat() 2102 */ 2103 protected function setPageFormat($format, $orientation='P') { 2104 if (!empty($format) AND isset($this->pagedim[$this->page])) { 2105 // remove inherited values 2106 unset($this->pagedim[$this->page]); 2107 } 2108 if (is_string($format)) { 2109 // get page measures from format name 2110 $pf = TCPDF_STATIC::getPageSizeFromFormat($format); 2111 $this->fwPt = $pf[0]; 2112 $this->fhPt = $pf[1]; 2113 } else { 2114 // the boundaries of the physical medium on which the page shall be displayed or printed 2115 if (isset($format['MediaBox'])) { 2116 $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); 2117 $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k); 2118 $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k); 2119 } else { 2120 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) { 2121 $pf = array(($format[0] * $this->k), ($format[1] * $this->k)); 2122 } else { 2123 if (!isset($format['format'])) { 2124 // default value 2125 $format['format'] = 'A4'; 2126 } 2127 $pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']); 2128 } 2129 $this->fwPt = $pf[0]; 2130 $this->fhPt = $pf[1]; 2131 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim); 2132 } 2133 // the visible region of default user space 2134 if (isset($format['CropBox'])) { 2135 $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); 2136 } 2137 // the region to which the contents of the page shall be clipped when output in a production environment 2138 if (isset($format['BleedBox'])) { 2139 $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); 2140 } 2141 // the intended dimensions of the finished page after trimming 2142 if (isset($format['TrimBox'])) { 2143 $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); 2144 } 2145 // the page's meaningful content (including potential white space) 2146 if (isset($format['ArtBox'])) { 2147 $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); 2148 } 2149 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries 2150 if (isset($format['BoxColorInfo'])) { 2151 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo']; 2152 } 2153 if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) { 2154 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. 2155 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']); 2156 } 2157 if (isset($format['PZ'])) { 2158 // The page's preferred zoom (magnification) factor 2159 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']); 2160 } 2161 if (isset($format['trans'])) { 2162 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation 2163 if (isset($format['trans']['Dur'])) { 2164 // The page's display duration 2165 $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']); 2166 } 2167 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade'); 2168 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) { 2169 // The transition style that shall be used when moving to this page from another during a presentation 2170 $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S']; 2171 $valid_effect = array('Split', 'Blinds'); 2172 $valid_vals = array('H', 'V'); 2173 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) { 2174 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm']; 2175 } 2176 $valid_effect = array('Split', 'Box', 'Fly'); 2177 $valid_vals = array('I', 'O'); 2178 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) { 2179 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M']; 2180 } 2181 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push'); 2182 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) { 2183 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe')) 2184 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter')) 2185 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) { 2186 $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']); 2187 } 2188 } 2189 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) { 2190 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']); 2191 } 2192 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) { 2193 $this->pagedim[$this->page]['trans']['B'] = 'true'; 2194 } 2195 } else { 2196 $this->pagedim[$this->page]['trans']['S'] = 'R'; 2197 } 2198 if (isset($format['trans']['D'])) { 2199 // The duration of the transition effect, in seconds 2200 $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']); 2201 } else { 2202 $this->pagedim[$this->page]['trans']['D'] = 1; 2203 } 2204 } 2205 } 2206 $this->setPageOrientation($orientation); 2207 } 2208 2209 /** 2210 * Set page orientation. 2211 * @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> 2212 * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off. 2213 * @param $bottommargin (float) bottom margin of the page. 2214 * @public 2215 * @since 3.0.015 (2008-06-06) 2216 */ 2217 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') { 2218 if (!isset($this->pagedim[$this->page]['MediaBox'])) { 2219 // the boundaries of the physical medium on which the page shall be displayed or printed 2220 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim); 2221 } 2222 if (!isset($this->pagedim[$this->page]['CropBox'])) { 2223 // the visible region of default user space 2224 $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); 2225 } 2226 if (!isset($this->pagedim[$this->page]['BleedBox'])) { 2227 // the region to which the contents of the page shall be clipped when output in a production environment 2228 $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); 2229 } 2230 if (!isset($this->pagedim[$this->page]['TrimBox'])) { 2231 // the intended dimensions of the finished page after trimming 2232 $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); 2233 } 2234 if (!isset($this->pagedim[$this->page]['ArtBox'])) { 2235 // the page's meaningful content (including potential white space) 2236 $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); 2237 } 2238 if (!isset($this->pagedim[$this->page]['Rotate'])) { 2239 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. 2240 $this->pagedim[$this->page]['Rotate'] = 0; 2241 } 2242 if (!isset($this->pagedim[$this->page]['PZ'])) { 2243 // The page's preferred zoom (magnification) factor 2244 $this->pagedim[$this->page]['PZ'] = 1; 2245 } 2246 if ($this->fwPt > $this->fhPt) { 2247 // landscape 2248 $default_orientation = 'L'; 2249 } else { 2250 // portrait 2251 $default_orientation = 'P'; 2252 } 2253 $valid_orientations = array('P', 'L'); 2254 if (empty($orientation)) { 2255 $orientation = $default_orientation; 2256 } else { 2257 $orientation = strtoupper($orientation[0]); 2258 } 2259 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) { 2260 $this->CurOrientation = $orientation; 2261 $this->wPt = $this->fhPt; 2262 $this->hPt = $this->fwPt; 2263 } else { 2264 $this->CurOrientation = $default_orientation; 2265 $this->wPt = $this->fwPt; 2266 $this->hPt = $this->fhPt; 2267 } 2268 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){ 2269 // swap X and Y coordinates (change page orientation) 2270 $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim); 2271 } 2272 $this->w = ($this->wPt / $this->k); 2273 $this->h = ($this->hPt / $this->k); 2274 if (TCPDF_STATIC::empty_string($autopagebreak)) { 2275 if (isset($this->AutoPageBreak)) { 2276 $autopagebreak = $this->AutoPageBreak; 2277 } else { 2278 $autopagebreak = true; 2279 } 2280 } 2281 if (TCPDF_STATIC::empty_string($bottommargin)) { 2282 if (isset($this->bMargin)) { 2283 $bottommargin = $this->bMargin; 2284 } else { 2285 // default value = 2 cm 2286 $bottommargin = 2 * 28.35 / $this->k; 2287 } 2288 } 2289 $this->SetAutoPageBreak($autopagebreak, $bottommargin); 2290 // store page dimensions 2291 $this->pagedim[$this->page]['w'] = $this->wPt; 2292 $this->pagedim[$this->page]['h'] = $this->hPt; 2293 $this->pagedim[$this->page]['wk'] = $this->w; 2294 $this->pagedim[$this->page]['hk'] = $this->h; 2295 $this->pagedim[$this->page]['tm'] = $this->tMargin; 2296 $this->pagedim[$this->page]['bm'] = $bottommargin; 2297 $this->pagedim[$this->page]['lm'] = $this->lMargin; 2298 $this->pagedim[$this->page]['rm'] = $this->rMargin; 2299 $this->pagedim[$this->page]['pb'] = $autopagebreak; 2300 $this->pagedim[$this->page]['or'] = $this->CurOrientation; 2301 $this->pagedim[$this->page]['olm'] = $this->original_lMargin; 2302 $this->pagedim[$this->page]['orm'] = $this->original_rMargin; 2303 } 2304 2305 /** 2306 * Set regular expression to detect withespaces or word separators. 2307 * The pattern delimiter must be the forward-slash character "/". 2308 * Some example patterns are: 2309 * <pre> 2310 * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/" 2311 * Unicode and PCRE unicode support: "/(?!\xa0)[\s\p{Z}]/u" 2312 * Unicode and PCRE unicode support in Chinese mode: "/(?!\xa0)[\s\p{Z}\p{Lo}]/u" 2313 * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"): 2314 * \s : any whitespace character 2315 * \p{Z} : any separator 2316 * \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words. 2317 * \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0) 2318 * </pre> 2319 * @param $re (string) regular expression (leave empty for default). 2320 * @public 2321 * @since 4.6.016 (2009-06-15) 2322 */ 2323 public function setSpacesRE($re='/[^\S\xa0]/') { 2324 $this->re_spaces = $re; 2325 $re_parts = explode('/', $re); 2326 // get pattern parts 2327 $this->re_space = array(); 2328 if (isset($re_parts[1]) AND !empty($re_parts[1])) { 2329 $this->re_space['p'] = $re_parts[1]; 2330 } else { 2331 $this->re_space['p'] = '[\s]'; 2332 } 2333 // set pattern modifiers 2334 if (isset($re_parts[2]) AND !empty($re_parts[2])) { 2335 $this->re_space['m'] = $re_parts[2]; 2336 } else { 2337 $this->re_space['m'] = ''; 2338 } 2339 } 2340 2341 /** 2342 * Enable or disable Right-To-Left language mode 2343 * @param $enable (Boolean) if true enable Right-To-Left language mode. 2344 * @param $resetx (Boolean) if true reset the X position on direction change. 2345 * @public 2346 * @since 2.0.000 (2008-01-03) 2347 */ 2348 public function setRTL($enable, $resetx=true) { 2349 $enable = $enable ? true : false; 2350 $resetx = ($resetx AND ($enable != $this->rtl)); 2351 $this->rtl = $enable; 2352 $this->tmprtl = false; 2353 if ($resetx) { 2354 $this->Ln(0); 2355 } 2356 } 2357 2358 /** 2359 * Return the RTL status 2360 * @return boolean 2361 * @public 2362 * @since 4.0.012 (2008-07-24) 2363 */ 2364 public function getRTL() { 2365 return $this->rtl; 2366 } 2367 2368 /** 2369 * Force temporary RTL language direction 2370 * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL 2371 * @public 2372 * @since 2.1.000 (2008-01-09) 2373 */ 2374 public function setTempRTL($mode) { 2375 $newmode = false; 2376 switch (strtoupper($mode)) { 2377 case 'LTR': 2378 case 'L': { 2379 if ($this->rtl) { 2380 $newmode = 'L'; 2381 } 2382 break; 2383 } 2384 case 'RTL': 2385 case 'R': { 2386 if (!$this->rtl) { 2387 $newmode = 'R'; 2388 } 2389 break; 2390 } 2391 case false: 2392 default: { 2393 $newmode = false; 2394 break; 2395 } 2396 } 2397 $this->tmprtl = $newmode; 2398 } 2399 2400 /** 2401 * Return the current temporary RTL status 2402 * @return boolean 2403 * @public 2404 * @since 4.8.014 (2009-11-04) 2405 */ 2406 public function isRTLTextDir() { 2407 return ($this->rtl OR ($this->tmprtl == 'R')); 2408 } 2409 2410 /** 2411 * Set the last cell height. 2412 * @param $h (float) cell height. 2413 * @author Nicola Asuni 2414 * @public 2415 * @since 1.53.0.TC034 2416 */ 2417 public function setLastH($h) { 2418 $this->lasth = $h; 2419 } 2420 2421 /** 2422 * Return the cell height 2423 * @param $fontsize (int) Font size in internal units 2424 * @param $padding (boolean) If true add cell padding 2425 * @public 2426 */ 2427 public function getCellHeight($fontsize, $padding=TRUE) { 2428 $height = ($fontsize * $this->cell_height_ratio); 2429 if ($padding) { 2430 $height += ($this->cell_padding['T'] + $this->cell_padding['B']); 2431 } 2432 return round($height, 6); 2433 } 2434 2435 /** 2436 * Reset the last cell height. 2437 * @public 2438 * @since 5.9.000 (2010-10-03) 2439 */ 2440 public function resetLastH() { 2441 $this->lasth = $this->getCellHeight($this->FontSize); 2442 } 2443 2444 /** 2445 * Get the last cell height. 2446 * @return last cell height 2447 * @public 2448 * @since 4.0.017 (2008-08-05) 2449 */ 2450 public function getLastH() { 2451 return $this->lasth; 2452 } 2453 2454 /** 2455 * Set the adjusting factor to convert pixels to user units. 2456 * @param $scale (float) adjusting factor to convert pixels to user units. 2457 * @author Nicola Asuni 2458 * @public 2459 * @since 1.5.2 2460 */ 2461 public function setImageScale($scale) { 2462 $this->imgscale = $scale; 2463 } 2464 2465 /** 2466 * Returns the adjusting factor to convert pixels to user units. 2467 * @return float adjusting factor to convert pixels to user units. 2468 * @author Nicola Asuni 2469 * @public 2470 * @since 1.5.2 2471 */ 2472 public function getImageScale() { 2473 return $this->imgscale; 2474 } 2475 2476 /** 2477 * Returns an array of page dimensions: 2478 * <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> 2479 * @param $pagenum (int) page number (empty = current page) 2480 * @return array of page dimensions. 2481 * @author Nicola Asuni 2482 * @public 2483 * @since 4.5.027 (2009-03-16) 2484 */ 2485 public function getPageDimensions($pagenum='') { 2486 if (empty($pagenum)) { 2487 $pagenum = $this->page; 2488 } 2489 return $this->pagedim[$pagenum]; 2490 } 2491 2492 /** 2493 * Returns the page width in units. 2494 * @param $pagenum (int) page number (empty = current page) 2495 * @return int page width. 2496 * @author Nicola Asuni 2497 * @public 2498 * @since 1.5.2 2499 * @see getPageDimensions() 2500 */ 2501 public function getPageWidth($pagenum='') { 2502 if (empty($pagenum)) { 2503 return $this->w; 2504 } 2505 return $this->pagedim[$pagenum]['w']; 2506 } 2507 2508 /** 2509 * Returns the page height in units. 2510 * @param $pagenum (int) page number (empty = current page) 2511 * @return int page height. 2512 * @author Nicola Asuni 2513 * @public 2514 * @since 1.5.2 2515 * @see getPageDimensions() 2516 */ 2517 public function getPageHeight($pagenum='') { 2518 if (empty($pagenum)) { 2519 return $this->h; 2520 } 2521 return $this->pagedim[$pagenum]['h']; 2522 } 2523 2524 /** 2525 * Returns the page break margin. 2526 * @param $pagenum (int) page number (empty = current page) 2527 * @return int page break margin. 2528 * @author Nicola Asuni 2529 * @public 2530 * @since 1.5.2 2531 * @see getPageDimensions() 2532 */ 2533 public function getBreakMargin($pagenum='') { 2534 if (empty($pagenum)) { 2535 return $this->bMargin; 2536 } 2537 return $this->pagedim[$pagenum]['bm']; 2538 } 2539 2540 /** 2541 * Returns the scale factor (number of points in user unit). 2542 * @return int scale factor. 2543 * @author Nicola Asuni 2544 * @public 2545 * @since 1.5.2 2546 */ 2547 public function getScaleFactor() { 2548 return $this->k; 2549 } 2550 2551 /** 2552 * Defines the left, top and right margins. 2553 * @param $left (float) Left margin. 2554 * @param $top (float) Top margin. 2555 * @param $right (float) Right margin. Default value is the left one. 2556 * @param $keepmargins (boolean) if true overwrites the default page margins 2557 * @public 2558 * @since 1.0 2559 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() 2560 */ 2561 public function SetMargins($left, $top, $right=-1, $keepmargins=false) { 2562 //Set left, top and right margins 2563 $this->lMargin = $left; 2564 $this->tMargin = $top; 2565 if ($right == -1) { 2566 $right = $left; 2567 } 2568 $this->rMargin = $right; 2569 if ($keepmargins) { 2570 // overwrite original values 2571 $this->original_lMargin = $this->lMargin; 2572 $this->original_rMargin = $this->rMargin; 2573 } 2574 } 2575 2576 /** 2577 * 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. 2578 * @param $margin (float) The margin. 2579 * @public 2580 * @since 1.4 2581 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 2582 */ 2583 public function SetLeftMargin($margin) { 2584 //Set left margin 2585 $this->lMargin = $margin; 2586 if (($this->page > 0) AND ($this->x < $margin)) { 2587 $this->x = $margin; 2588 } 2589 } 2590 2591 /** 2592 * Defines the top margin. The method can be called before creating the first page. 2593 * @param $margin (float) The margin. 2594 * @public 2595 * @since 1.5 2596 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 2597 */ 2598 public function SetTopMargin($margin) { 2599 //Set top margin 2600 $this->tMargin = $margin; 2601 if (($this->page > 0) AND ($this->y < $margin)) { 2602 $this->y = $margin; 2603 } 2604 } 2605 2606 /** 2607 * Defines the right margin. The method can be called before creating the first page. 2608 * @param $margin (float) The margin. 2609 * @public 2610 * @since 1.5 2611 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() 2612 */ 2613 public function SetRightMargin($margin) { 2614 $this->rMargin = $margin; 2615 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) { 2616 $this->x = $this->w - $margin; 2617 } 2618 } 2619 2620 /** 2621 * Set the same internal Cell padding for top, right, bottom, left- 2622 * @param $pad (float) internal padding. 2623 * @public 2624 * @since 2.1.000 (2008-01-09) 2625 * @see getCellPaddings(), setCellPaddings() 2626 */ 2627 public function SetCellPadding($pad) { 2628 if ($pad >= 0) { 2629 $this->cell_padding['L'] = $pad; 2630 $this->cell_padding['T'] = $pad; 2631 $this->cell_padding['R'] = $pad; 2632 $this->cell_padding['B'] = $pad; 2633 } 2634 } 2635 2636 /** 2637 * Set the internal Cell paddings. 2638 * @param $left (float) left padding 2639 * @param $top (float) top padding 2640 * @param $right (float) right padding 2641 * @param $bottom (float) bottom padding 2642 * @public 2643 * @since 5.9.000 (2010-10-03) 2644 * @see getCellPaddings(), SetCellPadding() 2645 */ 2646 public function setCellPaddings($left='', $top='', $right='', $bottom='') { 2647 if (($left !== '') AND ($left >= 0)) { 2648 $this->cell_padding['L'] = $left; 2649 } 2650 if (($top !== '') AND ($top >= 0)) { 2651 $this->cell_padding['T'] = $top; 2652 } 2653 if (($right !== '') AND ($right >= 0)) { 2654 $this->cell_padding['R'] = $right; 2655 } 2656 if (($bottom !== '') AND ($bottom >= 0)) { 2657 $this->cell_padding['B'] = $bottom; 2658 } 2659 } 2660 2661 /** 2662 * Get the internal Cell padding array. 2663 * @return array of padding values 2664 * @public 2665 * @since 5.9.000 (2010-10-03) 2666 * @see setCellPaddings(), SetCellPadding() 2667 */ 2668 public function getCellPaddings() { 2669 return $this->cell_padding; 2670 } 2671 2672 /** 2673 * Set the internal Cell margins. 2674 * @param $left (float) left margin 2675 * @param $top (float) top margin 2676 * @param $right (float) right margin 2677 * @param $bottom (float) bottom margin 2678 * @public 2679 * @since 5.9.000 (2010-10-03) 2680 * @see getCellMargins() 2681 */ 2682 public function setCellMargins($left='', $top='', $right='', $bottom='') { 2683 if (($left !== '') AND ($left >= 0)) { 2684 $this->cell_margin['L'] = $left; 2685 } 2686 if (($top !== '') AND ($top >= 0)) { 2687 $this->cell_margin['T'] = $top; 2688 } 2689 if (($right !== '') AND ($right >= 0)) { 2690 $this->cell_margin['R'] = $right; 2691 } 2692 if (($bottom !== '') AND ($bottom >= 0)) { 2693 $this->cell_margin['B'] = $bottom; 2694 } 2695 } 2696 2697 /** 2698 * Get the internal Cell margin array. 2699 * @return array of margin values 2700 * @public 2701 * @since 5.9.000 (2010-10-03) 2702 * @see setCellMargins() 2703 */ 2704 public function getCellMargins() { 2705 return $this->cell_margin; 2706 } 2707 2708 /** 2709 * Adjust the internal Cell padding array to take account of the line width. 2710 * @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))) 2711 * @return array of adjustments 2712 * @public 2713 * @since 5.9.000 (2010-10-03) 2714 */ 2715 protected function adjustCellPadding($brd=0) { 2716 if (empty($brd)) { 2717 return; 2718 } 2719 if (is_string($brd)) { 2720 // convert string to array 2721 $slen = strlen($brd); 2722 $newbrd = array(); 2723 for ($i = 0; $i < $slen; ++$i) { 2724 $newbrd[$brd[$i]] = true; 2725 } 2726 $brd = $newbrd; 2727 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) { 2728 $brd = array('LRTB' => true); 2729 } 2730 if (!is_array($brd)) { 2731 return; 2732 } 2733 // store current cell padding 2734 $cp = $this->cell_padding; 2735 // select border mode 2736 if (isset($brd['mode'])) { 2737 $mode = $brd['mode']; 2738 unset($brd['mode']); 2739 } else { 2740 $mode = 'normal'; 2741 } 2742 // process borders 2743 foreach ($brd as $border => $style) { 2744 $line_width = $this->LineWidth; 2745 if (is_array($style) AND isset($style['width'])) { 2746 // get border width 2747 $line_width = $style['width']; 2748 } 2749 $adj = 0; // line width inside the cell 2750 switch ($mode) { 2751 case 'ext': { 2752 $adj = 0; 2753 break; 2754 } 2755 case 'int': { 2756 $adj = $line_width; 2757 break; 2758 } 2759 case 'normal': 2760 default: { 2761 $adj = ($line_width / 2); 2762 break; 2763 } 2764 } 2765 // correct internal cell padding if required to avoid overlap between text and lines 2766 if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) { 2767 $this->cell_padding['T'] = $adj; 2768 } 2769 if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) { 2770 $this->cell_padding['R'] = $adj; 2771 } 2772 if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) { 2773 $this->cell_padding['B'] = $adj; 2774 } 2775 if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) { 2776 $this->cell_padding['L'] = $adj; 2777 } 2778 } 2779 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'])); 2780 } 2781 2782 /** 2783 * 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. 2784 * @param $auto (boolean) Boolean indicating if mode should be on or off. 2785 * @param $margin (float) Distance from the bottom of the page. 2786 * @public 2787 * @since 1.0 2788 * @see Cell(), MultiCell(), AcceptPageBreak() 2789 */ 2790 public function SetAutoPageBreak($auto, $margin=0) { 2791 $this->AutoPageBreak = $auto ? true : false; 2792 $this->bMargin = $margin; 2793 $this->PageBreakTrigger = $this->h - $margin; 2794 } 2795 2796 /** 2797 * Return the auto-page-break mode (true or false). 2798 * @return boolean auto-page-break mode 2799 * @public 2800 * @since 5.9.088 2801 */ 2802 public function getAutoPageBreak() { 2803 return $this->AutoPageBreak; 2804 } 2805 2806 /** 2807 * Defines the way the document is to be displayed by the viewer. 2808 * @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> 2809 * @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> 2810 * @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> 2811 * @public 2812 * @since 1.2 2813 */ 2814 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') { 2815 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) { 2816 $this->ZoomMode = $zoom; 2817 } else { 2818 $this->Error('Incorrect zoom display mode: '.$zoom); 2819 } 2820 $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout); 2821 $this->PageMode = TCPDF_STATIC::getPageMode($mode); 2822 } 2823 2824 /** 2825 * 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. 2826 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. 2827 * @param $compress (boolean) Boolean indicating if compression must be enabled. 2828 * @public 2829 * @since 1.4 2830 */ 2831 public function SetCompression($compress=true) { 2832 if (function_exists('gzcompress')) { 2833 $this->compress = $compress ? true : false; 2834 } else { 2835 $this->compress = false; 2836 } 2837 } 2838 2839 /** 2840 * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document. 2841 * @param $mode (boolean) If true force sRGB output intent. 2842 * @public 2843 * @since 5.9.121 (2011-09-28) 2844 */ 2845 public function setSRGBmode($mode=false) { 2846 $this->force_srgb = $mode ? true : false; 2847 } 2848 2849 /** 2850 * Turn on/off Unicode mode for document information dictionary (meta tags). 2851 * This has effect only when unicode mode is set to false. 2852 * @param $unicode (boolean) if true set the meta information in Unicode 2853 * @since 5.9.027 (2010-12-01) 2854 * @public 2855 */ 2856 public function SetDocInfoUnicode($unicode=true) { 2857 $this->docinfounicode = $unicode ? true : false; 2858 } 2859 2860 /** 2861 * Defines the title of the document. 2862 * @param $title (string) The title. 2863 * @public 2864 * @since 1.2 2865 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() 2866 */ 2867 public function SetTitle($title) { 2868 $this->title = $title; 2869 } 2870 2871 /** 2872 * Defines the subject of the document. 2873 * @param $subject (string) The subject. 2874 * @public 2875 * @since 1.2 2876 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() 2877 */ 2878 public function SetSubject($subject) { 2879 $this->subject = $subject; 2880 } 2881 2882 /** 2883 * Defines the author of the document. 2884 * @param $author (string) The name of the author. 2885 * @public 2886 * @since 1.2 2887 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() 2888 */ 2889 public function SetAuthor($author) { 2890 $this->author = $author; 2891 } 2892 2893 /** 2894 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. 2895 * @param $keywords (string) The list of keywords. 2896 * @public 2897 * @since 1.2 2898 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() 2899 */ 2900 public function SetKeywords($keywords) { 2901 $this->keywords = $keywords; 2902 } 2903 2904 /** 2905 * Defines the creator of the document. This is typically the name of the application that generates the PDF. 2906 * @param $creator (string) The name of the creator. 2907 * @public 2908 * @since 1.2 2909 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() 2910 */ 2911 public function SetCreator($creator) { 2912 $this->creator = $creator; 2913 } 2914 2915 /** 2916 * Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR constant is set to true. 2917 * @param $msg (string) The error message 2918 * @public 2919 * @since 1.0 2920 */ 2921 public function Error($msg) { 2922 // unset all class variables 2923 $this->_destroy(true); 2924 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) { 2925 die('<strong>TCPDF ERROR: </strong>'.$msg); 2926 } else { 2927 throw new Exception('TCPDF ERROR: '.$msg); 2928 } 2929 } 2930 2931 /** 2932 * This method begins the generation of the PDF document. 2933 * It is not necessary to call it explicitly because AddPage() does it automatically. 2934 * Note: no page is created by this method 2935 * @public 2936 * @since 1.0 2937 * @see AddPage(), Close() 2938 */ 2939 public function Open() { 2940 $this->state = 1; 2941 } 2942 2943 /** 2944 * Terminates the PDF document. 2945 * It is not necessary to call this method explicitly because Output() does it automatically. 2946 * If the document contains no page, AddPage() is called to prevent from getting an invalid document. 2947 * @public 2948 * @since 1.0 2949 * @see Open(), Output() 2950 */ 2951 public function Close() { 2952 if ($this->state == 3) { 2953 return; 2954 } 2955 if ($this->page == 0) { 2956 $this->AddPage(); 2957 } 2958 $this->endLayer(); 2959 if ($this->tcpdflink) { 2960 // save current graphic settings 2961 $gvars = $this->getGraphicVars(); 2962 $this->setEqualColumns(); 2963 $this->lastpage(true); 2964 $this->SetAutoPageBreak(false); 2965 $this->x = 0; 2966 $this->y = $this->h - (1 / $this->k); 2967 $this->lMargin = 0; 2968 $this->_outSaveGraphicsState(); 2969 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica'; 2970 $this->SetFont($font, '', 1); 2971 $this->setTextRenderingMode(0, false, false); 2972 $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"; 2973 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67"; 2974 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B'); 2975 $this->_outRestoreGraphicsState(); 2976 // restore graphic settings 2977 $this->setGraphicVars($gvars); 2978 } 2979 // close page 2980 $this->endPage(); 2981 // close document 2982 $this->_enddoc(); 2983 // unset all class variables (except critical ones) 2984 $this->_destroy(false); 2985 } 2986 2987 /** 2988 * Move pointer at the specified document page and update page dimensions. 2989 * @param $pnum (int) page number (1 ... numpages) 2990 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position. 2991 * @public 2992 * @since 2.1.000 (2008-01-07) 2993 * @see getPage(), lastpage(), getNumPages() 2994 */ 2995 public function setPage($pnum, $resetmargins=false) { 2996 if (($pnum == $this->page) AND ($this->state == 2)) { 2997 return; 2998 } 2999 if (($pnum > 0) AND ($pnum <= $this->numpages)) { 3000 $this->state = 2; 3001 // save current graphic settings 3002 //$gvars = $this->getGraphicVars(); 3003 $oldpage = $this->page; 3004 $this->page = $pnum; 3005 $this->wPt = $this->pagedim[$this->page]['w']; 3006 $this->hPt = $this->pagedim[$this->page]['h']; 3007 $this->w = $this->pagedim[$this->page]['wk']; 3008 $this->h = $this->pagedim[$this->page]['hk']; 3009 $this->tMargin = $this->pagedim[$this->page]['tm']; 3010 $this->bMargin = $this->pagedim[$this->page]['bm']; 3011 $this->original_lMargin = $this->pagedim[$this->page]['olm']; 3012 $this->original_rMargin = $this->pagedim[$this->page]['orm']; 3013 $this->AutoPageBreak = $this->pagedim[$this->page]['pb']; 3014 $this->CurOrientation = $this->pagedim[$this->page]['or']; 3015 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin); 3016 // restore graphic settings 3017 //$this->setGraphicVars($gvars); 3018 if ($resetmargins) { 3019 $this->lMargin = $this->pagedim[$this->page]['olm']; 3020 $this->rMargin = $this->pagedim[$this->page]['orm']; 3021 $this->SetY($this->tMargin); 3022 } else { 3023 // account for booklet mode 3024 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 3025 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm']; 3026 $this->lMargin += $deltam; 3027 $this->rMargin -= $deltam; 3028 } 3029 } 3030 } else { 3031 $this->Error('Wrong page number on setPage() function: '.$pnum); 3032 } 3033 } 3034 3035 /** 3036 * Reset pointer to the last document page. 3037 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position. 3038 * @public 3039 * @since 2.0.000 (2008-01-04) 3040 * @see setPage(), getPage(), getNumPages() 3041 */ 3042 public function lastPage($resetmargins=false) { 3043 $this->setPage($this->getNumPages(), $resetmargins); 3044 } 3045 3046 /** 3047 * Get current document page number. 3048 * @return int page number 3049 * @public 3050 * @since 2.1.000 (2008-01-07) 3051 * @see setPage(), lastpage(), getNumPages() 3052 */ 3053 public function getPage() { 3054 return $this->page; 3055 } 3056 3057 /** 3058 * Get the total number of insered pages. 3059 * @return int number of pages 3060 * @public 3061 * @since 2.1.000 (2008-01-07) 3062 * @see setPage(), getPage(), lastpage() 3063 */ 3064 public function getNumPages() { 3065 return $this->numpages; 3066 } 3067 3068 /** 3069 * Adds a new TOC (Table Of Content) page to the document. 3070 * @param $orientation (string) page orientation. 3071 * @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(). 3072 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins 3073 * @public 3074 * @since 5.0.001 (2010-05-06) 3075 * @see AddPage(), startPage(), endPage(), endTOCPage() 3076 */ 3077 public function addTOCPage($orientation='', $format='', $keepmargins=false) { 3078 $this->AddPage($orientation, $format, $keepmargins, true); 3079 } 3080 3081 /** 3082 * Terminate the current TOC (Table Of Content) page 3083 * @public 3084 * @since 5.0.001 (2010-05-06) 3085 * @see AddPage(), startPage(), endPage(), addTOCPage() 3086 */ 3087 public function endTOCPage() { 3088 $this->endPage(true); 3089 } 3090 3091 /** 3092 * 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). 3093 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards. 3094 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 3095 * @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(). 3096 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins 3097 * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content). 3098 * @public 3099 * @since 1.0 3100 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat() 3101 */ 3102 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) { 3103 if ($this->inxobj) { 3104 // we are inside an XObject template 3105 return; 3106 } 3107 if (!isset($this->original_lMargin) OR $keepmargins) { 3108 $this->original_lMargin = $this->lMargin; 3109 } 3110 if (!isset($this->original_rMargin) OR $keepmargins) { 3111 $this->original_rMargin = $this->rMargin; 3112 } 3113 // terminate previous page 3114 $this->endPage(); 3115 // start new page 3116 $this->startPage($orientation, $format, $tocpage); 3117 } 3118 3119 /** 3120 * Terminate the current page 3121 * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content). 3122 * @public 3123 * @since 4.2.010 (2008-11-14) 3124 * @see AddPage(), startPage(), addTOCPage(), endTOCPage() 3125 */ 3126 public function endPage($tocpage=false) { 3127 // check if page is already closed 3128 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) { 3129 return; 3130 } 3131 // print page footer 3132 $this->setFooter(); 3133 // close page 3134 $this->_endpage(); 3135 // mark page as closed 3136 $this->pageopen[$this->page] = false; 3137 if ($tocpage) { 3138 $this->tocpage = false; 3139 } 3140 } 3141 3142 /** 3143 * Starts a new page to the document. The page must be closed using the endPage() function. 3144 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. 3145 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 3146 * @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(). 3147 * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content. 3148 * @since 4.2.010 (2008-11-14) 3149 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat() 3150 * @public 3151 */ 3152 public function startPage($orientation='', $format='', $tocpage=false) { 3153 if ($tocpage) { 3154 $this->tocpage = true; 3155 } 3156 // move page numbers of documents to be attached 3157 if ($this->tocpage) { 3158 // move reference to unexistent pages (used for page attachments) 3159 // adjust outlines 3160 $tmpoutlines = $this->outlines; 3161 foreach ($tmpoutlines as $key => $outline) { 3162 if (!$outline['f'] AND ($outline['p'] > $this->numpages)) { 3163 $this->outlines[$key]['p'] = ($outline['p'] + 1); 3164 } 3165 } 3166 // adjust dests 3167 $tmpdests = $this->dests; 3168 foreach ($tmpdests as $key => $dest) { 3169 if (!$dest['f'] AND ($dest['p'] > $this->numpages)) { 3170 $this->dests[$key]['p'] = ($dest['p'] + 1); 3171 } 3172 } 3173 // adjust links 3174 $tmplinks = $this->links; 3175 foreach ($tmplinks as $key => $link) { 3176 if (!$link['f'] AND ($link['p'] > $this->numpages)) { 3177 $this->links[$key]['p'] = ($link['p'] + 1); 3178 } 3179 } 3180 } 3181 if ($this->numpages > $this->page) { 3182 // this page has been already added 3183 $this->setPage($this->page + 1); 3184 $this->SetY($this->tMargin); 3185 return; 3186 } 3187 // start a new page 3188 if ($this->state == 0) { 3189 $this->Open(); 3190 } 3191 ++$this->numpages; 3192 $this->swapMargins($this->booklet); 3193 // save current graphic settings 3194 $gvars = $this->getGraphicVars(); 3195 // start new page 3196 $this->_beginpage($orientation, $format); 3197 // mark page as open 3198 $this->pageopen[$this->page] = true; 3199 // restore graphic settings 3200 $this->setGraphicVars($gvars); 3201 // mark this point 3202 $this->setPageMark(); 3203 // print page header 3204 $this->setHeader(); 3205 // restore graphic settings 3206 $this->setGraphicVars($gvars); 3207 // mark this point 3208 $this->setPageMark(); 3209 // print table header (if any) 3210 $this->setTableHeader(); 3211 // set mark for empty page check 3212 $this->emptypagemrk[$this->page]= $this->pagelen[$this->page]; 3213 } 3214 3215 /** 3216 * Set start-writing mark on current page stream used to put borders and fills. 3217 * Borders and fills are always created after content and inserted on the position marked by this method. 3218 * This function must be called after calling Image() function for a background image. 3219 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions. 3220 * @public 3221 * @since 4.0.016 (2008-07-30) 3222 */ 3223 public function setPageMark() { 3224 $this->intmrk[$this->page] = $this->pagelen[$this->page]; 3225 $this->bordermrk[$this->page] = $this->intmrk[$this->page]; 3226 $this->setContentMark(); 3227 } 3228 3229 /** 3230 * Set start-writing mark on selected page. 3231 * Borders and fills are always created after content and inserted on the position marked by this method. 3232 * @param $page (int) page number (default is the current page) 3233 * @protected 3234 * @since 4.6.021 (2009-07-20) 3235 */ 3236 protected function setContentMark($page=0) { 3237 if ($page <= 0) { 3238 $page = $this->page; 3239 } 3240 if (isset($this->footerlen[$page])) { 3241 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page]; 3242 } else { 3243 $this->cntmrk[$page] = $this->pagelen[$page]; 3244 } 3245 } 3246 3247 /** 3248 * Set header data. 3249 * @param $ln (string) header image logo 3250 * @param $lw (string) header image logo width in mm 3251 * @param $ht (string) string to print as title on document header 3252 * @param $hs (string) string to print on document header 3253 * @param $tc (array) RGB array color for text. 3254 * @param $lc (array) RGB array color for line. 3255 * @public 3256 */ 3257 public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) { 3258 $this->header_logo = $ln; 3259 $this->header_logo_width = $lw; 3260 $this->header_title = $ht; 3261 $this->header_string = $hs; 3262 $this->header_text_color = $tc; 3263 $this->header_line_color = $lc; 3264 } 3265 3266 /** 3267 * Set footer data. 3268 * @param $tc (array) RGB array color for text. 3269 * @param $lc (array) RGB array color for line. 3270 * @public 3271 */ 3272 public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) { 3273 $this->footer_text_color = $tc; 3274 $this->footer_line_color = $lc; 3275 } 3276 3277 /** 3278 * Returns header data: 3279 * <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> 3280 * @return array() 3281 * @public 3282 * @since 4.0.012 (2008-07-24) 3283 */ 3284 public function getHeaderData() { 3285 $ret = array(); 3286 $ret['logo'] = $this->header_logo; 3287 $ret['logo_width'] = $this->header_logo_width; 3288 $ret['title'] = $this->header_title; 3289 $ret['string'] = $this->header_string; 3290 $ret['text_color'] = $this->header_text_color; 3291 $ret['line_color'] = $this->header_line_color; 3292 return $ret; 3293 } 3294 3295 /** 3296 * Set header margin. 3297 * (minimum distance between header and top page margin) 3298 * @param $hm (int) distance in user units 3299 * @public 3300 */ 3301 public function setHeaderMargin($hm=10) { 3302 $this->header_margin = $hm; 3303 } 3304 3305 /** 3306 * Returns header margin in user units. 3307 * @return float 3308 * @since 4.0.012 (2008-07-24) 3309 * @public 3310 */ 3311 public function getHeaderMargin() { 3312 return $this->header_margin; 3313 } 3314 3315 /** 3316 * Set footer margin. 3317 * (minimum distance between footer and bottom page margin) 3318 * @param $fm (int) distance in user units 3319 * @public 3320 */ 3321 public function setFooterMargin($fm=10) { 3322 $this->footer_margin = $fm; 3323 } 3324 3325 /** 3326 * Returns footer margin in user units. 3327 * @return float 3328 * @since 4.0.012 (2008-07-24) 3329 * @public 3330 */ 3331 public function getFooterMargin() { 3332 return $this->footer_margin; 3333 } 3334 /** 3335 * Set a flag to print page header. 3336 * @param $val (boolean) set to true to print the page header (default), false otherwise. 3337 * @public 3338 */ 3339 public function setPrintHeader($val=true) { 3340 $this->print_header = $val ? true : false; 3341 } 3342 3343 /** 3344 * Set a flag to print page footer. 3345 * @param $val (boolean) set to true to print the page footer (default), false otherwise. 3346 * @public 3347 */ 3348 public function setPrintFooter($val=true) { 3349 $this->print_footer = $val ? true : false; 3350 } 3351 3352 /** 3353 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image 3354 * @return float 3355 * @public 3356 */ 3357 public function getImageRBX() { 3358 return $this->img_rb_x; 3359 } 3360 3361 /** 3362 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image 3363 * @return float 3364 * @public 3365 */ 3366 public function getImageRBY() { 3367 return $this->img_rb_y; 3368 } 3369 3370 /** 3371 * Reset the xobject template used by Header() method. 3372 * @public 3373 */ 3374 public function resetHeaderTemplate() { 3375 $this->header_xobjid = false; 3376 } 3377 3378 /** 3379 * Set a flag to automatically reset the xobject template used by Header() method at each page. 3380 * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise. 3381 * @public 3382 */ 3383 public function setHeaderTemplateAutoreset($val=true) { 3384 $this->header_xobj_autoreset = $val ? true : false; 3385 } 3386 3387 /** 3388 * This method is used to render the page header. 3389 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 3390 * @public 3391 */ 3392 public function Header() { 3393 if ($this->header_xobjid === false) { 3394 // start a new XObject Template 3395 $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin); 3396 $headerfont = $this->getHeaderFont(); 3397 $headerdata = $this->getHeaderData(); 3398 $this->y = $this->header_margin; 3399 if ($this->rtl) { 3400 $this->x = $this->w - $this->original_rMargin; 3401 } else { 3402 $this->x = $this->original_lMargin; 3403 } 3404 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) { 3405 $imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']); 3406 if (($imgtype == 'eps') OR ($imgtype == 'ai')) { 3407 $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3408 } elseif ($imgtype == 'svg') { 3409 $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3410 } else { 3411 $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3412 } 3413 $imgy = $this->getImageRBY(); 3414 } else { 3415 $imgy = $this->y; 3416 } 3417 $cell_height = $this->getCellHeight($headerfont[2] / $this->k); 3418 // set starting margin for text data cell 3419 if ($this->getRTL()) { 3420 $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1); 3421 } else { 3422 $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1); 3423 } 3424 $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1); 3425 $this->SetTextColorArray($this->header_text_color); 3426 // header title 3427 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); 3428 $this->SetX($header_x); 3429 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0); 3430 // header string 3431 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]); 3432 $this->SetX($header_x); 3433 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false); 3434 // print an ending header line 3435 $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color'])); 3436 $this->SetY((2.835 / $this->k) + max($imgy, $this->y)); 3437 if ($this->rtl) { 3438 $this->SetX($this->original_rMargin); 3439 } else { 3440 $this->SetX($this->original_lMargin); 3441 } 3442 $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C'); 3443 $this->endTemplate(); 3444 } 3445 // print header template 3446 $x = 0; 3447 $dx = 0; 3448 if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) { 3449 // adjust margins for booklet mode 3450 $dx = ($this->original_lMargin - $this->original_rMargin); 3451 } 3452 if ($this->rtl) { 3453 $x = $this->w + $dx; 3454 } else { 3455 $x = 0 + $dx; 3456 } 3457 $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false); 3458 if ($this->header_xobj_autoreset) { 3459 // reset header xobject template at each page 3460 $this->header_xobjid = false; 3461 } 3462 } 3463 3464 /** 3465 * This method is used to render the page footer. 3466 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 3467 * @public 3468 */ 3469 public function Footer() { 3470 $cur_y = $this->y; 3471 $this->SetTextColorArray($this->footer_text_color); 3472 //set style for cell border 3473 $line_width = (0.85 / $this->k); 3474 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color)); 3475 //print document barcode 3476 $barcode = $this->getBarcode(); 3477 if (!empty($barcode)) { 3478 $this->Ln($line_width); 3479 $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3); 3480 $style = array( 3481 'position' => $this->rtl?'R':'L', 3482 'align' => $this->rtl?'R':'L', 3483 'stretch' => false, 3484 'fitwidth' => true, 3485 'cellfitalign' => '', 3486 'border' => false, 3487 'padding' => 0, 3488 'fgcolor' => array(0,0,0), 3489 'bgcolor' => false, 3490 'text' => false 3491 ); 3492 $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, ''); 3493 } 3494 $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : ''; 3495 if (empty($this->pagegroups)) { 3496 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages(); 3497 } else { 3498 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias(); 3499 } 3500 $this->SetY($cur_y); 3501 //Print page number 3502 if ($this->getRTL()) { 3503 $this->SetX($this->original_rMargin); 3504 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L'); 3505 } else { 3506 $this->SetX($this->original_lMargin); 3507 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R'); 3508 } 3509 } 3510 3511 /** 3512 * This method is used to render the page header. 3513 * @protected 3514 * @since 4.0.012 (2008-07-24) 3515 */ 3516 protected function setHeader() { 3517 if (!$this->print_header OR ($this->state != 2)) { 3518 return; 3519 } 3520 $this->InHeader = true; 3521 $this->setGraphicVars($this->default_graphic_vars); 3522 $temp_thead = $this->thead; 3523 $temp_theadMargins = $this->theadMargins; 3524 $lasth = $this->lasth; 3525 $newline = $this->newline; 3526 $this->_outSaveGraphicsState(); 3527 $this->rMargin = $this->original_rMargin; 3528 $this->lMargin = $this->original_lMargin; 3529 $this->SetCellPadding(0); 3530 //set current position 3531 if ($this->rtl) { 3532 $this->SetXY($this->original_rMargin, $this->header_margin); 3533 } else { 3534 $this->SetXY($this->original_lMargin, $this->header_margin); 3535 } 3536 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); 3537 $this->Header(); 3538 //restore position 3539 if ($this->rtl) { 3540 $this->SetXY($this->original_rMargin, $this->tMargin); 3541 } else { 3542 $this->SetXY($this->original_lMargin, $this->tMargin); 3543 } 3544 $this->_outRestoreGraphicsState(); 3545 $this->lasth = $lasth; 3546 $this->thead = $temp_thead; 3547 $this->theadMargins = $temp_theadMargins; 3548 $this->newline = $newline; 3549 $this->InHeader = false; 3550 } 3551 3552 /** 3553 * This method is used to render the page footer. 3554 * @protected 3555 * @since 4.0.012 (2008-07-24) 3556 */ 3557 protected function setFooter() { 3558 if ($this->state != 2) { 3559 return; 3560 } 3561 $this->InFooter = true; 3562 // save current graphic settings 3563 $gvars = $this->getGraphicVars(); 3564 // mark this point 3565 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 3566 $this->_out("\n"); 3567 if ($this->print_footer) { 3568 $this->setGraphicVars($this->default_graphic_vars); 3569 $this->current_column = 0; 3570 $this->num_columns = 1; 3571 $temp_thead = $this->thead; 3572 $temp_theadMargins = $this->theadMargins; 3573 $lasth = $this->lasth; 3574 $this->_outSaveGraphicsState(); 3575 $this->rMargin = $this->original_rMargin; 3576 $this->lMargin = $this->original_lMargin; 3577 $this->SetCellPadding(0); 3578 //set current position 3579 $footer_y = $this->h - $this->footer_margin; 3580 if ($this->rtl) { 3581 $this->SetXY($this->original_rMargin, $footer_y); 3582 } else { 3583 $this->SetXY($this->original_lMargin, $footer_y); 3584 } 3585 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]); 3586 $this->Footer(); 3587 //restore position 3588 if ($this->rtl) { 3589 $this->SetXY($this->original_rMargin, $this->tMargin); 3590 } else { 3591 $this->SetXY($this->original_lMargin, $this->tMargin); 3592 } 3593 $this->_outRestoreGraphicsState(); 3594 $this->lasth = $lasth; 3595 $this->thead = $temp_thead; 3596 $this->theadMargins = $temp_theadMargins; 3597 } 3598 // restore graphic settings 3599 $this->setGraphicVars($gvars); 3600 $this->current_column = $gvars['current_column']; 3601 $this->num_columns = $gvars['num_columns']; 3602 // calculate footer length 3603 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1; 3604 $this->InFooter = false; 3605 } 3606 3607 /** 3608 * Check if we are on the page body (excluding page header and footer). 3609 * @return true if we are not in page header nor in page footer, false otherwise. 3610 * @protected 3611 * @since 5.9.091 (2011-06-15) 3612 */ 3613 protected function inPageBody() { 3614 return (($this->InHeader === false) AND ($this->InFooter === false)); 3615 } 3616 3617 /** 3618 * This method is used to render the table header on new page (if any). 3619 * @protected 3620 * @since 4.5.030 (2009-03-25) 3621 */ 3622 protected function setTableHeader() { 3623 if ($this->num_columns > 1) { 3624 // multi column mode 3625 return; 3626 } 3627 if (isset($this->theadMargins['top'])) { 3628 // restore the original top-margin 3629 $this->tMargin = $this->theadMargins['top']; 3630 $this->pagedim[$this->page]['tm'] = $this->tMargin; 3631 $this->y = $this->tMargin; 3632 } 3633 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) { 3634 // set margins 3635 $prev_lMargin = $this->lMargin; 3636 $prev_rMargin = $this->rMargin; 3637 $prev_cell_padding = $this->cell_padding; 3638 $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']); 3639 $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']); 3640 $this->cell_padding = $this->theadMargins['cell_padding']; 3641 if ($this->rtl) { 3642 $this->x = $this->w - $this->rMargin; 3643 } else { 3644 $this->x = $this->lMargin; 3645 } 3646 // account for special "cell" mode 3647 if ($this->theadMargins['cell']) { 3648 if ($this->rtl) { 3649 $this->x -= $this->cell_padding['R']; 3650 } else { 3651 $this->x += $this->cell_padding['L']; 3652 } 3653 } 3654 $gvars = $this->getGraphicVars(); 3655 if (!empty($this->theadMargins['gvars'])) { 3656 // set the correct graphic style 3657 $this->setGraphicVars($this->theadMargins['gvars']); 3658 $this->rMargin = $gvars['rMargin']; 3659 $this->lMargin = $gvars['lMargin']; 3660 } 3661 // print table header 3662 $this->writeHTML($this->thead, false, false, false, false, ''); 3663 $this->setGraphicVars($gvars); 3664 // set new top margin to skip the table headers 3665 if (!isset($this->theadMargins['top'])) { 3666 $this->theadMargins['top'] = $this->tMargin; 3667 } 3668 // store end of header position 3669 if (!isset($this->columns[0]['th'])) { 3670 $this->columns[0]['th'] = array(); 3671 } 3672 $this->columns[0]['th']['\''.$this->page.'\''] = $this->y; 3673 $this->tMargin = $this->y; 3674 $this->pagedim[$this->page]['tm'] = $this->tMargin; 3675 $this->lasth = 0; 3676 $this->lMargin = $prev_lMargin; 3677 $this->rMargin = $prev_rMargin; 3678 $this->cell_padding = $prev_cell_padding; 3679 } 3680 } 3681 3682 /** 3683 * Returns the current page number. 3684 * @return int page number 3685 * @public 3686 * @since 1.0 3687 * @see getAliasNbPages() 3688 */ 3689 public function PageNo() { 3690 return $this->page; 3691 } 3692 3693 /** 3694 * Returns the array of spot colors. 3695 * @return (array) Spot colors array. 3696 * @public 3697 * @since 6.0.038 (2013-09-30) 3698 */ 3699 public function getAllSpotColors() { 3700 return $this->spot_colors; 3701 } 3702 3703 /** 3704 * Defines a new spot color. 3705 * It can be expressed in RGB components or gray scale. 3706 * The method can be called before the first page is created and the value is retained from page to page. 3707 * @param $name (string) Full name of the spot color. 3708 * @param $c (float) Cyan color for CMYK. Value between 0 and 100. 3709 * @param $m (float) Magenta color for CMYK. Value between 0 and 100. 3710 * @param $y (float) Yellow color for CMYK. Value between 0 and 100. 3711 * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100. 3712 * @public 3713 * @since 4.0.024 (2008-09-12) 3714 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor() 3715 */ 3716 public function AddSpotColor($name, $c, $m, $y, $k) { 3717 if (!isset($this->spot_colors[$name])) { 3718 $i = (1 + count($this->spot_colors)); 3719 $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i); 3720 } 3721 } 3722 3723 /** 3724 * Set the spot color for the specified type ('draw', 'fill', 'text'). 3725 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3726 * @param $name (string) Name of the spot color. 3727 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3728 * @return (string) PDF color command. 3729 * @public 3730 * @since 5.9.125 (2011-10-03) 3731 */ 3732 public function setSpotColor($type, $name, $tint=100) { 3733 $spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors); 3734 if ($spotcolor === false) { 3735 $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.'); 3736 } 3737 $tint = (max(0, min(100, $tint)) / 100); 3738 $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']); 3739 switch ($type) { 3740 case 'draw': { 3741 $pdfcolor .= sprintf('CS %F SCN', $tint); 3742 $this->DrawColor = $pdfcolor; 3743 $this->strokecolor = $spotcolor; 3744 break; 3745 } 3746 case 'fill': { 3747 $pdfcolor .= sprintf('cs %F scn', $tint); 3748 $this->FillColor = $pdfcolor; 3749 $this->bgcolor = $spotcolor; 3750 break; 3751 } 3752 case 'text': { 3753 $pdfcolor .= sprintf('cs %F scn', $tint); 3754 $this->TextColor = $pdfcolor; 3755 $this->fgcolor = $spotcolor; 3756 break; 3757 } 3758 } 3759 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3760 if ($this->state == 2) { 3761 $this->_out($pdfcolor); 3762 } 3763 if ($this->inxobj) { 3764 // we are inside an XObject template 3765 $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name]; 3766 } 3767 return $pdfcolor; 3768 } 3769 3770 /** 3771 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders). 3772 * @param $name (string) Name of the spot color. 3773 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3774 * @public 3775 * @since 4.0.024 (2008-09-12) 3776 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor() 3777 */ 3778 public function SetDrawSpotColor($name, $tint=100) { 3779 $this->setSpotColor('draw', $name, $tint); 3780 } 3781 3782 /** 3783 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds). 3784 * @param $name (string) Name of the spot color. 3785 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3786 * @public 3787 * @since 4.0.024 (2008-09-12) 3788 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor() 3789 */ 3790 public function SetFillSpotColor($name, $tint=100) { 3791 $this->setSpotColor('fill', $name, $tint); 3792 } 3793 3794 /** 3795 * Defines the spot color used for text. 3796 * @param $name (string) Name of the spot color. 3797 * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3798 * @public 3799 * @since 4.0.024 (2008-09-12) 3800 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor() 3801 */ 3802 public function SetTextSpotColor($name, $tint=100) { 3803 $this->setSpotColor('text', $name, $tint); 3804 } 3805 3806 /** 3807 * Set the color array for the specified type ('draw', 'fill', 'text'). 3808 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3809 * The method can be called before the first page is created and the value is retained from page to page. 3810 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3811 * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values). 3812 * @param $ret (boolean) If true do not send the PDF command. 3813 * @return (string) The PDF command or empty string. 3814 * @public 3815 * @since 3.1.000 (2008-06-11) 3816 */ 3817 public function setColorArray($type, $color, $ret=false) { 3818 if (is_array($color)) { 3819 $color = array_values($color); 3820 // component: grey, RGB red or CMYK cyan 3821 $c = isset($color[0]) ? $color[0] : -1; 3822 // component: RGB green or CMYK magenta 3823 $m = isset($color[1]) ? $color[1] : -1; 3824 // component: RGB blue or CMYK yellow 3825 $y = isset($color[2]) ? $color[2] : -1; 3826 // component: CMYK black 3827 $k = isset($color[3]) ? $color[3] : -1; 3828 // color name 3829 $name = isset($color[4]) ? $color[4] : ''; 3830 if ($c >= 0) { 3831 return $this->setColor($type, $c, $m, $y, $k, $ret, $name); 3832 } 3833 } 3834 return ''; 3835 } 3836 3837 /** 3838 * Defines the color used for all drawing operations (lines, rectangles and cell borders). 3839 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3840 * The method can be called before the first page is created and the value is retained from page to page. 3841 * @param $color (array) Array of colors (1, 3 or 4 values). 3842 * @param $ret (boolean) If true do not send the PDF command. 3843 * @return string the PDF command 3844 * @public 3845 * @since 3.1.000 (2008-06-11) 3846 * @see SetDrawColor() 3847 */ 3848 public function SetDrawColorArray($color, $ret=false) { 3849 return $this->setColorArray('draw', $color, $ret); 3850 } 3851 3852 /** 3853 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 3854 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3855 * The method can be called before the first page is created and the value is retained from page to page. 3856 * @param $color (array) Array of colors (1, 3 or 4 values). 3857 * @param $ret (boolean) If true do not send the PDF command. 3858 * @public 3859 * @since 3.1.000 (2008-6-11) 3860 * @see SetFillColor() 3861 */ 3862 public function SetFillColorArray($color, $ret=false) { 3863 return $this->setColorArray('fill', $color, $ret); 3864 } 3865 3866 /** 3867 * Defines the color used for text. It can be expressed in RGB components or gray scale. 3868 * The method can be called before the first page is created and the value is retained from page to page. 3869 * @param $color (array) Array of colors (1, 3 or 4 values). 3870 * @param $ret (boolean) If true do not send the PDF command. 3871 * @public 3872 * @since 3.1.000 (2008-6-11) 3873 * @see SetFillColor() 3874 */ 3875 public function SetTextColorArray($color, $ret=false) { 3876 return $this->setColorArray('text', $color, $ret); 3877 } 3878 3879 /** 3880 * Defines the color used by the specified type ('draw', 'fill', 'text'). 3881 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3882 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 3883 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 3884 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 3885 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 3886 * @param $ret (boolean) If true do not send the command. 3887 * @param $name (string) spot color name (if any) 3888 * @return (string) The PDF command or empty string. 3889 * @public 3890 * @since 5.9.125 (2011-10-03) 3891 */ 3892 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 3893 // set default values 3894 if (!is_numeric($col1)) { 3895 $col1 = 0; 3896 } 3897 if (!is_numeric($col2)) { 3898 $col2 = -1; 3899 } 3900 if (!is_numeric($col3)) { 3901 $col3 = -1; 3902 } 3903 if (!is_numeric($col4)) { 3904 $col4 = -1; 3905 } 3906 // set color by case 3907 $suffix = ''; 3908 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 3909 // Grey scale 3910 $col1 = max(0, min(255, $col1)); 3911 $intcolor = array('G' => $col1); 3912 $pdfcolor = sprintf('%F ', ($col1 / 255)); 3913 $suffix = 'g'; 3914 } elseif ($col4 == -1) { 3915 // RGB 3916 $col1 = max(0, min(255, $col1)); 3917 $col2 = max(0, min(255, $col2)); 3918 $col3 = max(0, min(255, $col3)); 3919 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 3920 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255)); 3921 $suffix = 'rg'; 3922 } else { 3923 $col1 = max(0, min(100, $col1)); 3924 $col2 = max(0, min(100, $col2)); 3925 $col3 = max(0, min(100, $col3)); 3926 $col4 = max(0, min(100, $col4)); 3927 if (empty($name)) { 3928 // CMYK 3929 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 3930 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100)); 3931 $suffix = 'k'; 3932 } else { 3933 // SPOT COLOR 3934 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name); 3935 $this->AddSpotColor($name, $col1, $col2, $col3, $col4); 3936 $pdfcolor = $this->setSpotColor($type, $name, 100); 3937 } 3938 } 3939 switch ($type) { 3940 case 'draw': { 3941 $pdfcolor .= strtoupper($suffix); 3942 $this->DrawColor = $pdfcolor; 3943 $this->strokecolor = $intcolor; 3944 break; 3945 } 3946 case 'fill': { 3947 $pdfcolor .= $suffix; 3948 $this->FillColor = $pdfcolor; 3949 $this->bgcolor = $intcolor; 3950 break; 3951 } 3952 case 'text': { 3953 $pdfcolor .= $suffix; 3954 $this->TextColor = $pdfcolor; 3955 $this->fgcolor = $intcolor; 3956 break; 3957 } 3958 } 3959 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3960 if (($type != 'text') AND ($this->state == 2)) { 3961 if (!$ret) { 3962 $this->_out($pdfcolor); 3963 } 3964 return $pdfcolor; 3965 } 3966 return ''; 3967 } 3968 3969 /** 3970 * 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. 3971 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 3972 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 3973 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 3974 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 3975 * @param $ret (boolean) If true do not send the command. 3976 * @param $name (string) spot color name (if any) 3977 * @return string the PDF command 3978 * @public 3979 * @since 1.3 3980 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() 3981 */ 3982 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 3983 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name); 3984 } 3985 3986 /** 3987 * 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. 3988 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 3989 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 3990 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 3991 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 3992 * @param $ret (boolean) If true do not send the command. 3993 * @param $name (string) Spot color name (if any). 3994 * @return (string) The PDF command. 3995 * @public 3996 * @since 1.3 3997 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() 3998 */ 3999 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 4000 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name); 4001 } 4002 4003 /** 4004 * 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. 4005 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 4006 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 4007 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 4008 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 4009 * @param $ret (boolean) If true do not send the command. 4010 * @param $name (string) Spot color name (if any). 4011 * @return (string) Empty string. 4012 * @public 4013 * @since 1.3 4014 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() 4015 */ 4016 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 4017 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name); 4018 } 4019 4020 /** 4021 * Returns the length of a string in user unit. A font must be selected.<br> 4022 * @param $s (string) The string whose length is to be computed 4023 * @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. 4024 * @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. 4025 * @param $fontsize (float) Font size in points. The default value is the current size. 4026 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length. 4027 * @return mixed int total string length or array of characted widths 4028 * @author Nicola Asuni 4029 * @public 4030 * @since 1.2 4031 */ 4032 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 4033 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); 4034 } 4035 4036 /** 4037 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br> 4038 * @param $sa (string) The array of chars whose total length is to be computed 4039 * @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. 4040 * @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. 4041 * @param $fontsize (float) Font size in points. The default value is the current size. 4042 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length. 4043 * @return mixed int total string length or array of characted widths 4044 * @author Nicola Asuni 4045 * @public 4046 * @since 2.4.000 (2008-03-06) 4047 */ 4048 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 4049 // store current values 4050 if (!TCPDF_STATIC::empty_string($fontname)) { 4051 $prev_FontFamily = $this->FontFamily; 4052 $prev_FontStyle = $this->FontStyle; 4053 $prev_FontSizePt = $this->FontSizePt; 4054 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false); 4055 } 4056 // convert UTF-8 array to Latin1 if required 4057 if ($this->isunicode AND (!$this->isUnicodeFont())) { 4058 $sa = TCPDF_FONTS::UTF8ArrToLatin1Arr($sa); 4059 } 4060 $w = 0; // total width 4061 $wa = array(); // array of characters widths 4062 foreach ($sa as $ck => $char) { 4063 // character width 4064 $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)])); 4065 $wa[] = $cw; 4066 $w += $cw; 4067 } 4068 // restore previous values 4069 if (!TCPDF_STATIC::empty_string($fontname)) { 4070 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false); 4071 } 4072 if ($getarray) { 4073 return $wa; 4074 } 4075 return $w; 4076 } 4077 4078 /** 4079 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking). 4080 * @param $char (int) The char code whose length is to be returned 4081 * @param $notlast (boolean) If false ignore the font-spacing. 4082 * @return float char width 4083 * @author Nicola Asuni 4084 * @public 4085 * @since 2.4.000 (2008-03-06) 4086 */ 4087 public function GetCharWidth($char, $notlast=true) { 4088 // get raw width 4089 $chw = $this->getRawCharWidth($char); 4090 if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) { 4091 // increase/decrease font spacing 4092 $chw += $this->font_spacing; 4093 } 4094 if ($this->font_stretching != 100) { 4095 // fixed stretching mode 4096 $chw *= ($this->font_stretching / 100); 4097 } 4098 return $chw; 4099 } 4100 4101 /** 4102 * Returns the length of the char in user unit for the current font. 4103 * @param $char (int) The char code whose length is to be returned 4104 * @return float char width 4105 * @author Nicola Asuni 4106 * @public 4107 * @since 5.9.000 (2010-09-28) 4108 */ 4109 public function getRawCharWidth($char) { 4110 if ($char == 173) { 4111 // SHY character will not be printed 4112 return (0); 4113 } 4114 if (isset($this->CurrentFont['cw'][$char])) { 4115 $w = $this->CurrentFont['cw'][$char]; 4116 } elseif (isset($this->CurrentFont['dw'])) { 4117 // default width 4118 $w = $this->CurrentFont['dw']; 4119 } elseif (isset($this->CurrentFont['cw'][32])) { 4120 // default width 4121 $w = $this->CurrentFont['cw'][32]; 4122 } else { 4123 $w = 600; 4124 } 4125 return $this->getAbsFontMeasure($w); 4126 } 4127 4128 /** 4129 * Returns the numbero of characters in a string. 4130 * @param $s (string) The input string. 4131 * @return int number of characters 4132 * @public 4133 * @since 2.0.0001 (2008-01-07) 4134 */ 4135 public function GetNumChars($s) { 4136 if ($this->isUnicodeFont()) { 4137 return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont)); 4138 } 4139 return strlen($s); 4140 } 4141 4142 /** 4143 * Fill the list of available fonts ($this->fontlist). 4144 * @protected 4145 * @since 4.0.013 (2008-07-28) 4146 */ 4147 protected function getFontsList() { 4148 if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) { 4149 while (($file = readdir($fontsdir)) !== false) { 4150 if (substr($file, -4) == '.php') { 4151 array_push($this->fontlist, strtolower(basename($file, '.php'))); 4152 } 4153 } 4154 closedir($fontsdir); 4155 } 4156 } 4157 4158 /** 4159 * Imports a TrueType, Type1, core, or CID0 font and makes it available. 4160 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 4161 * 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. 4162 * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. 4163 * @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> 4164 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 4165 * @return array containing the font data, or false in case of error. 4166 * @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. 4167 * @public 4168 * @since 1.5 4169 * @see SetFont(), setFontSubsetting() 4170 */ 4171 public function AddFont($family, $style='', $fontfile='', $subset='default') { 4172 if ($subset === 'default') { 4173 $subset = $this->font_subsetting; 4174 } 4175 if ($this->pdfa_mode) { 4176 $subset = false; 4177 } 4178 if (TCPDF_STATIC::empty_string($family)) { 4179 if (!TCPDF_STATIC::empty_string($this->FontFamily)) { 4180 $family = $this->FontFamily; 4181 } else { 4182 $this->Error('Empty font family'); 4183 } 4184 } 4185 // move embedded styles on $style 4186 if (substr($family, -1) == 'I') { 4187 $style .= 'I'; 4188 $family = substr($family, 0, -1); 4189 } 4190 if (substr($family, -1) == 'B') { 4191 $style .= 'B'; 4192 $family = substr($family, 0, -1); 4193 } 4194 // normalize family name 4195 $family = strtolower($family); 4196 if ((!$this->isunicode) AND ($family == 'arial')) { 4197 $family = 'helvetica'; 4198 } 4199 if (($family == 'symbol') OR ($family == 'zapfdingbats')) { 4200 $style = ''; 4201 } 4202 if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) { 4203 // all fonts must be embedded 4204 $family = 'pdfa'.$family; 4205 } 4206 $tempstyle = strtoupper($style); 4207 $style = ''; 4208 // underline 4209 if (strpos($tempstyle, 'U') !== false) { 4210 $this->underline = true; 4211 } else { 4212 $this->underline = false; 4213 } 4214 // line-through (deleted) 4215 if (strpos($tempstyle, 'D') !== false) { 4216 $this->linethrough = true; 4217 } else { 4218 $this->linethrough = false; 4219 } 4220 // overline 4221 if (strpos($tempstyle, 'O') !== false) { 4222 $this->overline = true; 4223 } else { 4224 $this->overline = false; 4225 } 4226 // bold 4227 if (strpos($tempstyle, 'B') !== false) { 4228 $style .= 'B'; 4229 } 4230 // oblique 4231 if (strpos($tempstyle, 'I') !== false) { 4232 $style .= 'I'; 4233 } 4234 $bistyle = $style; 4235 $fontkey = $family.$style; 4236 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : ''); 4237 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style); 4238 // check if the font has been already added 4239 $fb = $this->getFontBuffer($fontkey); 4240 if ($fb !== false) { 4241 if ($this->inxobj) { 4242 // we are inside an XObject template 4243 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i']; 4244 } 4245 return $fontdata; 4246 } 4247 // get specified font directory (if any) 4248 $fontdir = false; 4249 if (!TCPDF_STATIC::empty_string($fontfile)) { 4250 $fontdir = dirname($fontfile); 4251 if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) { 4252 $fontdir = ''; 4253 } else { 4254 $fontdir .= '/'; 4255 } 4256 } 4257 // true when the font style variation is missing 4258 $missing_style = false; 4259 // search and include font file 4260 if (TCPDF_STATIC::empty_string($fontfile) OR (!@file_exists($fontfile))) { 4261 // build a standard filenames for specified font 4262 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php'; 4263 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir); 4264 if (TCPDF_STATIC::empty_string($fontfile)) { 4265 $missing_style = true; 4266 // try to remove the style part 4267 $tmp_fontfile = str_replace(' ', '', $family).'.php'; 4268 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir); 4269 } 4270 } 4271 // include font file 4272 if (!TCPDF_STATIC::empty_string($fontfile) AND (@file_exists($fontfile))) { 4273 include($fontfile); 4274 } else { 4275 $this->Error('Could not include font definition file: '.$family.''); 4276 } 4277 // check font parameters 4278 if ((!isset($type)) OR (!isset($cw))) { 4279 $this->Error('The font definition file has a bad format: '.$fontfile.''); 4280 } 4281 // SET default parameters 4282 if (!isset($file) OR TCPDF_STATIC::empty_string($file)) { 4283 $file = ''; 4284 } 4285 if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) { 4286 $enc = ''; 4287 } 4288 if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) { 4289 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0); 4290 $cidinfo['uni2cid'] = array(); 4291 } 4292 if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) { 4293 $ctg = ''; 4294 } 4295 if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) { 4296 $desc = array(); 4297 } 4298 if (!isset($up) OR TCPDF_STATIC::empty_string($up)) { 4299 $up = -100; 4300 } 4301 if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) { 4302 $ut = 50; 4303 } 4304 if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) { 4305 $cw = array(); 4306 } 4307 if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) { 4308 // set default width 4309 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) { 4310 $dw = $desc['MissingWidth']; 4311 } elseif (isset($cw[32])) { 4312 $dw = $cw[32]; 4313 } else { 4314 $dw = 600; 4315 } 4316 } 4317 ++$this->numfonts; 4318 if ($type == 'core') { 4319 $name = $this->CoreFonts[$fontkey]; 4320 $subset = false; 4321 } elseif (($type == 'TrueType') OR ($type == 'Type1')) { 4322 $subset = false; 4323 } elseif ($type == 'TrueTypeUnicode') { 4324 $enc = 'Identity-H'; 4325 } elseif ($type == 'cidfont0') { 4326 if ($this->pdfa_mode) { 4327 $this->Error('All fonts must be embedded in PDF/A mode!'); 4328 } 4329 } else { 4330 $this->Error('Unknow font type: '.$type.''); 4331 } 4332 // set name if unset 4333 if (!isset($name) OR empty($name)) { 4334 $name = $fontkey; 4335 } 4336 // create artificial font style variations if missing (only works with non-embedded fonts) 4337 if (($type != 'core') AND $missing_style) { 4338 // style variations 4339 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic'); 4340 $name .= $styles[$bistyle]; 4341 // artificial bold 4342 if (strpos($bistyle, 'B') !== false) { 4343 if (isset($desc['StemV'])) { 4344 // from normal to bold 4345 $desc['StemV'] = round($desc['StemV'] * 1.75); 4346 } else { 4347 // bold 4348 $desc['StemV'] = 123; 4349 } 4350 } 4351 // artificial italic 4352 if (strpos($bistyle, 'I') !== false) { 4353 if (isset($desc['ItalicAngle'])) { 4354 $desc['ItalicAngle'] -= 11; 4355 } else { 4356 $desc['ItalicAngle'] = -11; 4357 } 4358 if (isset($desc['Flags'])) { 4359 $desc['Flags'] |= 64; //bit 7 4360 } else { 4361 $desc['Flags'] = 64; 4362 } 4363 } 4364 } 4365 // check if the array of characters bounding boxes is defined 4366 if (!isset($cbbox)) { 4367 $cbbox = array(); 4368 } 4369 // initialize subsetchars 4370 $subsetchars = array_fill(0, 255, true); 4371 $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)); 4372 if ($this->inxobj) { 4373 // we are inside an XObject template 4374 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts; 4375 } 4376 if (isset($diff) AND (!empty($diff))) { 4377 //Search existing encodings 4378 $d = 0; 4379 $nb = count($this->diffs); 4380 for ($i=1; $i <= $nb; ++$i) { 4381 if ($this->diffs[$i] == $diff) { 4382 $d = $i; 4383 break; 4384 } 4385 } 4386 if ($d == 0) { 4387 $d = $nb + 1; 4388 $this->diffs[$d] = $diff; 4389 } 4390 $this->setFontSubBuffer($fontkey, 'diff', $d); 4391 } 4392 if (!TCPDF_STATIC::empty_string($file)) { 4393 if (!isset($this->FontFiles[$file])) { 4394 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) { 4395 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); 4396 } elseif ($type != 'core') { 4397 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); 4398 } 4399 } else { 4400 // update fontkeys that are sharing this font file 4401 $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset); 4402 if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) { 4403 $this->FontFiles[$file]['fontkeys'][] = $fontkey; 4404 } 4405 } 4406 } 4407 return $fontdata; 4408 } 4409 4410 /** 4411 * Sets the font used to print character strings. 4412 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). 4413 * The method can be called before the first page is created and the font is retained from page to page. 4414 * If you just wish to change the current font size, it is simpler to call SetFontSize(). 4415 * 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 /> 4416 * @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. 4417 * @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. 4418 * @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 4419 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 4420 * @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. 4421 * @param $out (boolean) if true output the font size command, otherwise only set the font properties. 4422 * @author Nicola Asuni 4423 * @public 4424 * @since 1.0 4425 * @see AddFont(), SetFontSize() 4426 */ 4427 public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) { 4428 //Select a font; size given in points 4429 if ($size === null) { 4430 $size = $this->FontSizePt; 4431 } 4432 if ($size < 0) { 4433 $size = 0; 4434 } 4435 // try to add font (if not already added) 4436 $fontdata = $this->AddFont($family, $style, $fontfile, $subset); 4437 $this->FontFamily = $fontdata['family']; 4438 $this->FontStyle = $fontdata['style']; 4439 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) { 4440 // save subset chars of the previous font 4441 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 4442 } 4443 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']); 4444 $this->SetFontSize($size, $out); 4445 } 4446 4447 /** 4448 * Defines the size of the current font. 4449 * @param $size (float) The font size in points. 4450 * @param $out (boolean) if true output the font size command, otherwise only set the font properties. 4451 * @public 4452 * @since 1.0 4453 * @see SetFont() 4454 */ 4455 public function SetFontSize($size, $out=true) { 4456 // font size in points 4457 $this->FontSizePt = $size; 4458 // font size in user units 4459 $this->FontSize = $size / $this->k; 4460 // calculate some font metrics 4461 if (isset($this->CurrentFont['desc']['FontBBox'])) { 4462 $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); 4463 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000); 4464 } else { 4465 $font_height = $size * 1.219; 4466 } 4467 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) { 4468 $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000); 4469 } 4470 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) { 4471 $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000); 4472 } 4473 if (!isset($font_ascent) AND !isset($font_descent)) { 4474 // core font 4475 $font_ascent = 0.76 * $font_height; 4476 $font_descent = $font_height - $font_ascent; 4477 } elseif (!isset($font_descent)) { 4478 $font_descent = $font_height - $font_ascent; 4479 } elseif (!isset($font_ascent)) { 4480 $font_ascent = $font_height - $font_descent; 4481 } 4482 $this->FontAscent = ($font_ascent / $this->k); 4483 $this->FontDescent = ($font_descent / $this->k); 4484 if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) { 4485 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 4486 } 4487 } 4488 4489 /** 4490 * Returns the bounding box of the current font in user units. 4491 * @return array 4492 * @public 4493 * @since 5.9.152 (2012-03-23) 4494 */ 4495 public function getFontBBox() { 4496 $fbbox = array(); 4497 if (isset($this->CurrentFont['desc']['FontBBox'])) { 4498 $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); 4499 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox); 4500 } else { 4501 // Find max width 4502 if (isset($this->CurrentFont['desc']['MaxWidth'])) { 4503 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth'])); 4504 } else { 4505 $maxw = 0; 4506 if (isset($this->CurrentFont['desc']['MissingWidth'])) { 4507 $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']); 4508 } 4509 if (isset($this->CurrentFont['desc']['AvgWidth'])) { 4510 $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']); 4511 } 4512 if (isset($this->CurrentFont['dw'])) { 4513 $maxw = max($maxw, $this->CurrentFont['dw']); 4514 } 4515 foreach ($this->CurrentFont['cw'] as $char => $w) { 4516 $maxw = max($maxw, $w); 4517 } 4518 if ($maxw == 0) { 4519 $maxw = 600; 4520 } 4521 $maxw = $this->getAbsFontMeasure($maxw); 4522 } 4523 $fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent); 4524 } 4525 return $fbbox; 4526 } 4527 4528 /** 4529 * Convert a relative font measure into absolute value. 4530 * @param $s (int) Font measure. 4531 * @return float Absolute measure. 4532 * @since 5.9.186 (2012-09-13) 4533 */ 4534 public function getAbsFontMeasure($s) { 4535 return ($s * $this->FontSize / 1000); 4536 } 4537 4538 /** 4539 * Returns the glyph bounding box of the specified character in the current font in user units. 4540 * @param $char (int) Input character code. 4541 * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined. 4542 * @since 5.9.186 (2012-09-13) 4543 */ 4544 public function getCharBBox($char) { 4545 $c = intval($char); 4546 if (isset($this->CurrentFont['cw'][$c])) { 4547 // glyph is defined ... use zero width & height for glyphs without outlines 4548 $result = array(0,0,0,0); 4549 if (isset($this->CurrentFont['cbbox'][$c])) { 4550 $result = $this->CurrentFont['cbbox'][$c]; 4551 } 4552 return array_map(array($this,'getAbsFontMeasure'), $result); 4553 } 4554 return false; 4555 } 4556 4557 /** 4558 * Return the font descent value 4559 * @param $font (string) font name 4560 * @param $style (string) font style 4561 * @param $size (float) The size (in points) 4562 * @return int font descent 4563 * @public 4564 * @author Nicola Asuni 4565 * @since 4.9.003 (2010-03-30) 4566 */ 4567 public function getFontDescent($font, $style='', $size=0) { 4568 $fontdata = $this->AddFont($font, $style); 4569 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4570 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) { 4571 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000); 4572 } else { 4573 $descent = (1.219 * 0.24 * $size); 4574 } 4575 return ($descent / $this->k); 4576 } 4577 4578 /** 4579 * Return the font ascent value. 4580 * @param $font (string) font name 4581 * @param $style (string) font style 4582 * @param $size (float) The size (in points) 4583 * @return int font ascent 4584 * @public 4585 * @author Nicola Asuni 4586 * @since 4.9.003 (2010-03-30) 4587 */ 4588 public function getFontAscent($font, $style='', $size=0) { 4589 $fontdata = $this->AddFont($font, $style); 4590 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4591 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) { 4592 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000); 4593 } else { 4594 $ascent = 1.219 * 0.76 * $size; 4595 } 4596 return ($ascent / $this->k); 4597 } 4598 4599 /** 4600 * Return true in the character is present in the specified font. 4601 * @param $char (mixed) Character to check (integer value or string) 4602 * @param $font (string) Font name (family name). 4603 * @param $style (string) Font style. 4604 * @return (boolean) true if the char is defined, false otherwise. 4605 * @public 4606 * @since 5.9.153 (2012-03-28) 4607 */ 4608 public function isCharDefined($char, $font='', $style='') { 4609 if (is_string($char)) { 4610 // get character code 4611 $char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont); 4612 $char = $char[0]; 4613 } 4614 if (TCPDF_STATIC::empty_string($font)) { 4615 if (TCPDF_STATIC::empty_string($style)) { 4616 return (isset($this->CurrentFont['cw'][intval($char)])); 4617 } 4618 $font = $this->FontFamily; 4619 } 4620 $fontdata = $this->AddFont($font, $style); 4621 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4622 return (isset($fontinfo['cw'][intval($char)])); 4623 } 4624 4625 /** 4626 * Replace missing font characters on selected font with specified substitutions. 4627 * @param $text (string) Text to process. 4628 * @param $font (string) Font name (family name). 4629 * @param $style (string) Font style. 4630 * @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. 4631 * @return (string) Processed text. 4632 * @public 4633 * @since 5.9.153 (2012-03-28) 4634 */ 4635 public function replaceMissingChars($text, $font='', $style='', $subs=array()) { 4636 if (empty($subs)) { 4637 return $text; 4638 } 4639 if (TCPDF_STATIC::empty_string($font)) { 4640 $font = $this->FontFamily; 4641 } 4642 $fontdata = $this->AddFont($font, $style); 4643 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4644 $uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont); 4645 foreach ($uniarr as $k => $chr) { 4646 if (!isset($fontinfo['cw'][$chr])) { 4647 // this character is missing on the selected font 4648 if (isset($subs[$chr])) { 4649 // we have available substitutions 4650 if (is_array($subs[$chr])) { 4651 foreach($subs[$chr] as $s) { 4652 if (isset($fontinfo['cw'][$s])) { 4653 $uniarr[$k] = $s; 4654 break; 4655 } 4656 } 4657 } elseif (isset($fontinfo['cw'][$subs[$chr]])) { 4658 $uniarr[$k] = $subs[$chr]; 4659 } 4660 } 4661 } 4662 } 4663 return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode)); 4664 } 4665 4666 /** 4667 * Defines the default monospaced font. 4668 * @param $font (string) Font name. 4669 * @public 4670 * @since 4.5.025 4671 */ 4672 public function SetDefaultMonospacedFont($font) { 4673 $this->default_monospaced_font = $font; 4674 } 4675 4676 /** 4677 * 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 /> 4678 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). 4679 * @public 4680 * @since 1.5 4681 * @see Cell(), Write(), Image(), Link(), SetLink() 4682 */ 4683 public function AddLink() { 4684 // create a new internal link 4685 $n = count($this->links) + 1; 4686 $this->links[$n] = array('p' => 0, 'y' => 0, 'f' => false); 4687 return $n; 4688 } 4689 4690 /** 4691 * Defines the page and position a link points to. 4692 * @param $link (int) The link identifier returned by AddLink() 4693 * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) 4694 * @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. 4695 * @public 4696 * @since 1.5 4697 * @see AddLink() 4698 */ 4699 public function SetLink($link, $y=0, $page=-1) { 4700 $fixed = false; 4701 if (!empty($page) AND ($page[0] == '*')) { 4702 $page = intval(substr($page, 1)); 4703 // this page number will not be changed when moving/add/deleting pages 4704 $fixed = true; 4705 } 4706 if ($page < 0) { 4707 $page = $this->page; 4708 } 4709 if ($y == -1) { 4710 $y = $this->y; 4711 } 4712 $this->links[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed); 4713 } 4714 4715 /** 4716 * Puts a link on a rectangular area of the page. 4717 * 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. 4718 * @param $x (float) Abscissa of the upper-left corner of the rectangle 4719 * @param $y (float) Ordinate of the upper-left corner of the rectangle 4720 * @param $w (float) Width of the rectangle 4721 * @param $h (float) Height of the rectangle 4722 * @param $link (mixed) URL or identifier returned by AddLink() 4723 * @param $spaces (int) number of spaces on the text to link 4724 * @public 4725 * @since 1.5 4726 * @see AddLink(), Annotation(), Cell(), Write(), Image() 4727 */ 4728 public function Link($x, $y, $w, $h, $link, $spaces=0) { 4729 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces); 4730 } 4731 4732 /** 4733 * Puts a markup annotation on a rectangular area of the page. 4734 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!! 4735 * @param $x (float) Abscissa of the upper-left corner of the rectangle 4736 * @param $y (float) Ordinate of the upper-left corner of the rectangle 4737 * @param $w (float) Width of the rectangle 4738 * @param $h (float) Height of the rectangle 4739 * @param $text (string) annotation text or alternate content 4740 * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7). 4741 * @param $spaces (int) number of spaces on the text to link 4742 * @public 4743 * @since 4.0.018 (2008-08-06) 4744 */ 4745 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) { 4746 if ($this->inxobj) { 4747 // store parameters for later use on template 4748 $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces); 4749 return; 4750 } 4751 if ($x === '') { 4752 $x = $this->x; 4753 } 4754 if ($y === '') { 4755 $y = $this->y; 4756 } 4757 // check page for no-write regions and adapt page margins if necessary 4758 list($x, $y) = $this->checkPageRegions($h, $x, $y); 4759 // recalculate coordinates to account for graphic transformations 4760 if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) { 4761 for ($i=$this->transfmatrix_key; $i > 0; --$i) { 4762 $maxid = count($this->transfmatrix[$i]) - 1; 4763 for ($j=$maxid; $j >= 0; --$j) { 4764 $ctm = $this->transfmatrix[$i][$j]; 4765 if (isset($ctm['a'])) { 4766 $x = $x * $this->k; 4767 $y = ($this->h - $y) * $this->k; 4768 $w = $w * $this->k; 4769 $h = $h * $this->k; 4770 // top left 4771 $xt = $x; 4772 $yt = $y; 4773 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4774 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4775 // top right 4776 $xt = $x + $w; 4777 $yt = $y; 4778 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4779 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4780 // bottom left 4781 $xt = $x; 4782 $yt = $y - $h; 4783 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4784 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4785 // bottom right 4786 $xt = $x + $w; 4787 $yt = $y - $h; 4788 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4789 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4790 // new coordinates (rectangle area) 4791 $x = min($x1, $x2, $x3, $x4); 4792 $y = max($y1, $y2, $y3, $y4); 4793 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k; 4794 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k; 4795 $x = $x / $this->k; 4796 $y = $this->h - ($y / $this->k); 4797 } 4798 } 4799 } 4800 } 4801 if ($this->page <= 0) { 4802 $page = 1; 4803 } else { 4804 $page = $this->page; 4805 } 4806 if (!isset($this->PageAnnots[$page])) { 4807 $this->PageAnnots[$page] = array(); 4808 } 4809 $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); 4810 if (!$this->pdfa_mode) { 4811 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS'])) 4812 AND (@file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS'])) 4813 AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) { 4814 $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']); 4815 } 4816 } 4817 // Add widgets annotation's icons 4818 if (isset($opt['mk']['i']) AND @file_exists($opt['mk']['i'])) { 4819 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true); 4820 } 4821 if (isset($opt['mk']['ri']) AND @file_exists($opt['mk']['ri'])) { 4822 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 4823 } 4824 if (isset($opt['mk']['ix']) AND @file_exists($opt['mk']['ix'])) { 4825 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 4826 } 4827 } 4828 4829 /** 4830 * Embedd the attached files. 4831 * @since 4.4.000 (2008-12-07) 4832 * @protected 4833 * @see Annotation() 4834 */ 4835 protected function _putEmbeddedFiles() { 4836 if ($this->pdfa_mode) { 4837 // embedded files are not allowed in PDF/A mode 4838 return; 4839 } 4840 reset($this->embeddedfiles); 4841 foreach ($this->embeddedfiles as $filename => $filedata) { 4842 $data = TCPDF_STATIC::fileGetContents($filedata['file']); 4843 if ($data !== FALSE) { 4844 $rawsize = strlen($data); 4845 if ($rawsize > 0) { 4846 // update name tree 4847 $this->efnames[$filename] = $filedata['f'].' 0 R'; 4848 // embedded file specification object 4849 $out = $this->_getobj($filedata['f'])."\n"; 4850 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>'; 4851 $out .= "\n".'endobj'; 4852 $this->_out($out); 4853 // embedded file object 4854 $filter = ''; 4855 if ($this->compress) { 4856 $data = gzcompress($data); 4857 $filter = ' /Filter /FlateDecode'; 4858 } 4859 $stream = $this->_getrawstream($data, $filedata['n']); 4860 $out = $this->_getobj($filedata['n'])."\n"; 4861 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>'; 4862 $out .= ' stream'."\n".$stream."\n".'endstream'; 4863 $out .= "\n".'endobj'; 4864 $this->_out($out); 4865 } 4866 } 4867 } 4868 } 4869 4870 /** 4871 * Prints a text cell at the specified position. 4872 * This method allows to place a string precisely on the page. 4873 * @param $x (float) Abscissa of the cell origin 4874 * @param $y (float) Ordinate of the cell origin 4875 * @param $txt (string) String to print 4876 * @param $fstroke (int) outline size in user units (false = disable) 4877 * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation). 4878 * @param $ffill (boolean) if true fills the text 4879 * @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))) 4880 * @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. 4881 * @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> 4882 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 4883 * @param $link (mixed) URL or identifier returned by AddLink(). 4884 * @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. 4885 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 4886 * @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> 4887 * @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> 4888 * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position. 4889 * @public 4890 * @since 1.0 4891 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell() 4892 */ 4893 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) { 4894 $textrendermode = $this->textrendermode; 4895 $textstrokewidth = $this->textstrokewidth; 4896 $this->setTextRenderingMode($fstroke, $ffill, $fclip); 4897 $this->SetXY($x, $y, $rtloff); 4898 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign); 4899 // restore previous rendering mode 4900 $this->textrendermode = $textrendermode; 4901 $this->textstrokewidth = $textstrokewidth; 4902 } 4903 4904 /** 4905 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 4906 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> 4907 * This method is called automatically and should not be called directly by the application. 4908 * @return boolean 4909 * @public 4910 * @since 1.4 4911 * @see SetAutoPageBreak() 4912 */ 4913 public function AcceptPageBreak() { 4914 if ($this->num_columns > 1) { 4915 // multi column mode 4916 if ($this->current_column < ($this->num_columns - 1)) { 4917 // go to next column 4918 $this->selectColumn($this->current_column + 1); 4919 } elseif ($this->AutoPageBreak) { 4920 // add a new page 4921 $this->AddPage(); 4922 // set first column 4923 $this->selectColumn(0); 4924 } 4925 // avoid page breaking from checkPageBreak() 4926 return false; 4927 } 4928 return $this->AutoPageBreak; 4929 } 4930 4931 /** 4932 * Add page if needed. 4933 * @param $h (float) Cell height. Default value: 0. 4934 * @param $y (mixed) starting y position, leave empty for current position. 4935 * @param $addpage (boolean) if true add a page, otherwise only return the true/false state 4936 * @return boolean true in case of page break, false otherwise. 4937 * @since 3.2.000 (2008-07-01) 4938 * @protected 4939 */ 4940 protected function checkPageBreak($h=0, $y='', $addpage=true) { 4941 if (TCPDF_STATIC::empty_string($y)) { 4942 $y = $this->y; 4943 } 4944 $current_page = $this->page; 4945 if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) { 4946 if ($addpage) { 4947 //Automatic page break 4948 $x = $this->x; 4949 $this->AddPage($this->CurOrientation); 4950 $this->y = $this->tMargin; 4951 $oldpage = $this->page - 1; 4952 if ($this->rtl) { 4953 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) { 4954 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']); 4955 } else { 4956 $this->x = $x; 4957 } 4958 } else { 4959 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 4960 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']); 4961 } else { 4962 $this->x = $x; 4963 } 4964 } 4965 } 4966 return true; 4967 } 4968 if ($current_page != $this->page) { 4969 // account for columns mode 4970 return true; 4971 } 4972 return false; 4973 } 4974 4975 /** 4976 * 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 /> 4977 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 4978 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 4979 * @param $h (float) Cell height. Default value: 0. 4980 * @param $txt (string) String to print. Default value: empty string. 4981 * @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))) 4982 * @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. 4983 * @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> 4984 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 4985 * @param $link (mixed) URL or identifier returned by AddLink(). 4986 * @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. 4987 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 4988 * @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> 4989 * @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> 4990 * @public 4991 * @since 1.0 4992 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() 4993 */ 4994 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') { 4995 $prev_cell_margin = $this->cell_margin; 4996 $prev_cell_padding = $this->cell_padding; 4997 $this->adjustCellPadding($border); 4998 if (!$ignore_min_height) { 4999 $min_cell_height = $this->getCellHeight($this->FontSize); 5000 if ($h < $min_cell_height) { 5001 $h = $min_cell_height; 5002 } 5003 } 5004 $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']); 5005 // apply text shadow if enabled 5006 if ($this->txtshadow['enabled']) { 5007 // save data 5008 $x = $this->x; 5009 $y = $this->y; 5010 $bc = $this->bgcolor; 5011 $fc = $this->fgcolor; 5012 $sc = $this->strokecolor; 5013 $alpha = $this->alpha; 5014 // print shadow 5015 $this->x += $this->txtshadow['depth_w']; 5016 $this->y += $this->txtshadow['depth_h']; 5017 $this->SetFillColorArray($this->txtshadow['color']); 5018 $this->SetTextColorArray($this->txtshadow['color']); 5019 $this->SetDrawColorArray($this->txtshadow['color']); 5020 if ($this->txtshadow['opacity'] != $alpha['CA']) { 5021 $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']); 5022 } 5023 if ($this->state == 2) { 5024 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); 5025 } 5026 //restore data 5027 $this->x = $x; 5028 $this->y = $y; 5029 $this->SetFillColorArray($bc); 5030 $this->SetTextColorArray($fc); 5031 $this->SetDrawColorArray($sc); 5032 if ($this->txtshadow['opacity'] != $alpha['CA']) { 5033 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']); 5034 } 5035 } 5036 if ($this->state == 2) { 5037 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); 5038 } 5039 $this->cell_padding = $prev_cell_padding; 5040 $this->cell_margin = $prev_cell_margin; 5041 } 5042 5043 /** 5044 * 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 /> 5045 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 5046 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 5047 * @param $h (float) Cell height. Default value: 0. 5048 * @param $txt (string) String to print. Default value: empty string. 5049 * @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))) 5050 * @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. 5051 * @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> 5052 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 5053 * @param $link (mixed) URL or identifier returned by AddLink(). 5054 * @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. 5055 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 5056 * @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> 5057 * @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> 5058 * @return string containing cell code 5059 * @protected 5060 * @since 1.0 5061 * @see Cell() 5062 */ 5063 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') { 5064 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space 5065 $txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt); 5066 $prev_cell_margin = $this->cell_margin; 5067 $prev_cell_padding = $this->cell_padding; 5068 $txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode); 5069 $rs = ''; //string to be returned 5070 $this->adjustCellPadding($border); 5071 if (!$ignore_min_height) { 5072 $min_cell_height = $this->getCellHeight($this->FontSize); 5073 if ($h < $min_cell_height) { 5074 $h = $min_cell_height; 5075 } 5076 } 5077 $k = $this->k; 5078 // check page for no-write regions and adapt page margins if necessary 5079 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); 5080 if ($this->rtl) { 5081 $x = $this->x - $this->cell_margin['R']; 5082 } else { 5083 $x = $this->x + $this->cell_margin['L']; 5084 } 5085 $y = $this->y + $this->cell_margin['T']; 5086 $prev_font_stretching = $this->font_stretching; 5087 $prev_font_spacing = $this->font_spacing; 5088 // cell vertical alignment 5089 switch ($calign) { 5090 case 'A': { 5091 // font top 5092 switch ($valign) { 5093 case 'T': { 5094 // top 5095 $y -= $this->cell_padding['T']; 5096 break; 5097 } 5098 case 'B': { 5099 // bottom 5100 $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent); 5101 break; 5102 } 5103 default: 5104 case 'C': 5105 case 'M': { 5106 // center 5107 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2); 5108 break; 5109 } 5110 } 5111 break; 5112 } 5113 case 'L': { 5114 // font baseline 5115 switch ($valign) { 5116 case 'T': { 5117 // top 5118 $y -= ($this->cell_padding['T'] + $this->FontAscent); 5119 break; 5120 } 5121 case 'B': { 5122 // bottom 5123 $y -= ($h - $this->cell_padding['B'] - $this->FontDescent); 5124 break; 5125 } 5126 default: 5127 case 'C': 5128 case 'M': { 5129 // center 5130 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2); 5131 break; 5132 } 5133 } 5134 break; 5135 } 5136 case 'D': { 5137 // font bottom 5138 switch ($valign) { 5139 case 'T': { 5140 // top 5141 $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent); 5142 break; 5143 } 5144 case 'B': { 5145 // bottom 5146 $y -= ($h - $this->cell_padding['B']); 5147 break; 5148 } 5149 default: 5150 case 'C': 5151 case 'M': { 5152 // center 5153 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2); 5154 break; 5155 } 5156 } 5157 break; 5158 } 5159 case 'B': { 5160 // cell bottom 5161 $y -= $h; 5162 break; 5163 } 5164 case 'C': 5165 case 'M': { 5166 // cell center 5167 $y -= ($h / 2); 5168 break; 5169 } 5170 default: 5171 case 'T': { 5172 // cell top 5173 break; 5174 } 5175 } 5176 // text vertical alignment 5177 switch ($valign) { 5178 case 'T': { 5179 // top 5180 $yt = $y + $this->cell_padding['T']; 5181 break; 5182 } 5183 case 'B': { 5184 // bottom 5185 $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent; 5186 break; 5187 } 5188 default: 5189 case 'C': 5190 case 'M': { 5191 // center 5192 $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2); 5193 break; 5194 } 5195 } 5196 $basefonty = $yt + $this->FontAscent; 5197 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 5198 if ($this->rtl) { 5199 $w = $x - $this->lMargin; 5200 } else { 5201 $w = $this->w - $this->rMargin - $x; 5202 } 5203 } 5204 $s = ''; 5205 // fill and borders 5206 if (is_string($border) AND (strlen($border) == 4)) { 5207 // full border 5208 $border = 1; 5209 } 5210 if ($fill OR ($border == 1)) { 5211 if ($fill) { 5212 $op = ($border == 1) ? 'B' : 'f'; 5213 } else { 5214 $op = 'S'; 5215 } 5216 if ($this->rtl) { 5217 $xk = (($x - $w) * $k); 5218 } else { 5219 $xk = ($x * $k); 5220 } 5221 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op); 5222 } 5223 // draw borders 5224 $s .= $this->getCellBorder($x, $y, $w, $h, $border); 5225 if ($txt != '') { 5226 $txt2 = $txt; 5227 if ($this->isunicode) { 5228 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 5229 $txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont); 5230 } else { 5231 $unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values 5232 $unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont); 5233 // replace thai chars (if any) 5234 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) { 5235 // number of chars 5236 $numchars = count($unicode); 5237 // po pla, for far, for fan 5238 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f); 5239 // do chada, to patak 5240 $lowtail = array(0x0e0e, 0x0e0f); 5241 // mai hun arkad, sara i, sara ii, sara ue, sara uee 5242 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37); 5243 // mai ek, mai tho, mai tri, mai chattawa, karan 5244 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c); 5245 // sara u, sara uu, pinthu 5246 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a); 5247 $output = array(); 5248 for ($i = 0; $i < $numchars; $i++) { 5249 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) { 5250 $ch0 = $unicode[$i]; 5251 $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0; 5252 $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0; 5253 $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0; 5254 if (in_array($ch0, $tonemark)) { 5255 if ($chn == 0x0e33) { 5256 // sara um 5257 if (in_array($ch1, $longtail)) { 5258 // tonemark at upper left 5259 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); 5260 } else { 5261 // tonemark at upper right (normal position) 5262 $output[] = $ch0; 5263 } 5264 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) { 5265 // tonemark at lower left 5266 $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48)); 5267 } elseif (in_array($ch1, $upvowel)) { 5268 if (in_array($ch2, $longtail)) { 5269 // tonemark at upper left 5270 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); 5271 } else { 5272 // tonemark at upper right (normal position) 5273 $output[] = $ch0; 5274 } 5275 } else { 5276 // tonemark at lower right 5277 $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48)); 5278 } 5279 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) { 5280 // add lower left nikhahit and sara aa 5281 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) { 5282 $output[] = 0xf711; 5283 $this->CurrentFont['subsetchars'][0xf711] = true; 5284 $output[] = 0x0e32; 5285 $this->CurrentFont['subsetchars'][0x0e32] = true; 5286 } else { 5287 $output[] = $ch0; 5288 } 5289 } elseif (in_array($ch1, $longtail)) { 5290 if ($ch0 == 0x0e31) { 5291 // lower left mai hun arkad 5292 $output[] = $this->replaceChar($ch0, 0xf710); 5293 } elseif (in_array($ch0, $upvowel)) { 5294 // lower left 5295 $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34)); 5296 } elseif ($ch0 == 0x0e47) { 5297 // lower left mai tai koo 5298 $output[] = $this->replaceChar($ch0, 0xf712); 5299 } else { 5300 // normal character 5301 $output[] = $ch0; 5302 } 5303 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) { 5304 // lower vowel 5305 $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38)); 5306 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) { 5307 // yo ying without lower part 5308 $output[] = $this->replaceChar($ch0, 0xf70f); 5309 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) { 5310 // tho santan without lower part 5311 $output[] = $this->replaceChar($ch0, 0xf700); 5312 } else { 5313 $output[] = $ch0; 5314 } 5315 } else { 5316 // non-thai character 5317 $output[] = $unicode[$i]; 5318 } 5319 } 5320 $unicode = $output; 5321 // update font subsetchars 5322 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 5323 } // end of K_THAI_TOPCHARS 5324 $txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false); 5325 } 5326 } 5327 $txt2 = TCPDF_STATIC::_escape($txt2); 5328 // get current text width (considering general font stretching and spacing) 5329 $txwidth = $this->GetStringWidth($txt); 5330 $width = $txwidth; 5331 // check for stretch mode 5332 if ($stretch > 0) { 5333 // calculate ratio between cell width and text width 5334 if ($width <= 0) { 5335 $ratio = 1; 5336 } else { 5337 $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width); 5338 } 5339 // check if stretching is required 5340 if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) { 5341 // the text will be stretched to fit cell width 5342 if ($stretch > 2) { 5343 // set new character spacing 5344 $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100)); 5345 } else { 5346 // set new horizontal stretching 5347 $this->font_stretching *= $ratio; 5348 } 5349 // recalculate text width (the text fills the entire cell) 5350 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 5351 // reset alignment 5352 $align = ''; 5353 } 5354 } 5355 if ($this->font_stretching != 100) { 5356 // apply font stretching 5357 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching); 5358 } 5359 if ($this->font_spacing != 0) { 5360 // increase/decrease font spacing 5361 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k)); 5362 } 5363 if ($this->ColorFlag AND ($this->textrendermode < 4)) { 5364 $s .= 'q '.$this->TextColor.' '; 5365 } 5366 // rendering mode 5367 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k)); 5368 // count number of spaces 5369 $ns = substr_count($txt, chr(32)); 5370 // Justification 5371 $spacewidth = 0; 5372 if (($align == 'J') AND ($ns > 0)) { 5373 if ($this->isUnicodeFont()) { 5374 // get string width without spaces 5375 $width = $this->GetStringWidth(str_replace(' ', '', $txt)); 5376 // calculate average space width 5377 $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1); 5378 if ($this->font_stretching != 100) { 5379 // word spacing is affected by stretching 5380 $spacewidth /= ($this->font_stretching / 100); 5381 } 5382 // set word position to be used with TJ operator 5383 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2); 5384 $unicode_justification = true; 5385 } else { 5386 // get string width 5387 $width = $txwidth; 5388 // new space width 5389 $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k; 5390 if ($this->font_stretching != 100) { 5391 // word spacing (Tw) is affected by stretching 5392 $spacewidth /= ($this->font_stretching / 100); 5393 } 5394 // set word spacing 5395 $rs .= sprintf('BT %F Tw ET ', $spacewidth); 5396 } 5397 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 5398 } 5399 // replace carriage return characters 5400 $txt2 = str_replace("\r", ' ', $txt2); 5401 switch ($align) { 5402 case 'C': { 5403 $dx = ($w - $width) / 2; 5404 break; 5405 } 5406 case 'R': { 5407 if ($this->rtl) { 5408 $dx = $this->cell_padding['R']; 5409 } else { 5410 $dx = $w - $width - $this->cell_padding['R']; 5411 } 5412 break; 5413 } 5414 case 'L': { 5415 if ($this->rtl) { 5416 $dx = $w - $width - $this->cell_padding['L']; 5417 } else { 5418 $dx = $this->cell_padding['L']; 5419 } 5420 break; 5421 } 5422 case 'J': 5423 default: { 5424 if ($this->rtl) { 5425 $dx = $this->cell_padding['R']; 5426 } else { 5427 $dx = $this->cell_padding['L']; 5428 } 5429 break; 5430 } 5431 } 5432 if ($this->rtl) { 5433 $xdx = $x - $dx - $width; 5434 } else { 5435 $xdx = $x + $dx; 5436 } 5437 $xdk = $xdx * $k; 5438 // print text 5439 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2); 5440 if (isset($uniblock)) { 5441 // print overlapping characters as separate string 5442 $xshift = 0; // horizontal shift 5443 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k); 5444 $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)); 5445 foreach ($uniblock as $uk => $uniarr) { 5446 if (($uk % 2) == 0) { 5447 // x space to skip 5448 if ($spacewidth != 0) { 5449 // justification shift 5450 $xshift += (count(array_keys($uniarr, 32)) * $spw); 5451 } 5452 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification 5453 } else { 5454 // character to print 5455 $topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false); 5456 $topchr = TCPDF_STATIC::_escape($topchr); 5457 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr); 5458 } 5459 } 5460 } 5461 if ($this->underline) { 5462 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width); 5463 } 5464 if ($this->linethrough) { 5465 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width); 5466 } 5467 if ($this->overline) { 5468 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width); 5469 } 5470 if ($this->ColorFlag AND ($this->textrendermode < 4)) { 5471 $s .= ' Q'; 5472 } 5473 if ($link) { 5474 $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns); 5475 } 5476 } 5477 // output cell 5478 if ($s) { 5479 // output cell 5480 $rs .= $s; 5481 if ($this->font_spacing != 0) { 5482 // reset font spacing mode 5483 $rs .= ' BT 0 Tc ET'; 5484 } 5485 if ($this->font_stretching != 100) { 5486 // reset font stretching mode 5487 $rs .= ' BT 100 Tz ET'; 5488 } 5489 } 5490 // reset word spacing 5491 if (!$this->isUnicodeFont() AND ($align == 'J')) { 5492 $rs .= ' BT 0 Tw ET'; 5493 } 5494 // reset stretching and spacing 5495 $this->font_stretching = $prev_font_stretching; 5496 $this->font_spacing = $prev_font_spacing; 5497 $this->lasth = $h; 5498 if ($ln > 0) { 5499 //Go to the beginning of the next line 5500 $this->y = $y + $h + $this->cell_margin['B']; 5501 if ($ln == 1) { 5502 if ($this->rtl) { 5503 $this->x = $this->w - $this->rMargin; 5504 } else { 5505 $this->x = $this->lMargin; 5506 } 5507 } 5508 } else { 5509 // go left or right by case 5510 if ($this->rtl) { 5511 $this->x = $x - $w - $this->cell_margin['L']; 5512 } else { 5513 $this->x = $x + $w + $this->cell_margin['R']; 5514 } 5515 } 5516 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n"; 5517 $rs = $gstyles.$rs; 5518 $this->cell_padding = $prev_cell_padding; 5519 $this->cell_margin = $prev_cell_margin; 5520 return $rs; 5521 } 5522 5523 /** 5524 * Replace a char if is defined on the current font. 5525 * @param $oldchar (int) Integer code (unicode) of the character to replace. 5526 * @param $newchar (int) Integer code (unicode) of the new character. 5527 * @return int the replaced char or the old char in case the new char i not defined 5528 * @protected 5529 * @since 5.9.167 (2012-06-22) 5530 */ 5531 protected function replaceChar($oldchar, $newchar) { 5532 if ($this->isCharDefined($newchar)) { 5533 // add the new char on the subset list 5534 $this->CurrentFont['subsetchars'][$newchar] = true; 5535 // return the new character 5536 return $newchar; 5537 } 5538 // return the old char 5539 return $oldchar; 5540 } 5541 5542 /** 5543 * Returns the code to draw the cell border 5544 * @param $x (float) X coordinate. 5545 * @param $y (float) Y coordinate. 5546 * @param $w (float) Cell width. 5547 * @param $h (float) Cell height. 5548 * @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))) 5549 * @return string containing cell border code 5550 * @protected 5551 * @see SetLineStyle() 5552 * @since 5.7.000 (2010-08-02) 5553 */ 5554 protected function getCellBorder($x, $y, $w, $h, $brd) { 5555 $s = ''; // string to be returned 5556 if (empty($brd)) { 5557 return $s; 5558 } 5559 if ($brd == 1) { 5560 $brd = array('LRTB' => true); 5561 } 5562 // calculate coordinates for border 5563 $k = $this->k; 5564 if ($this->rtl) { 5565 $xeL = ($x - $w) * $k; 5566 $xeR = $x * $k; 5567 } else { 5568 $xeL = $x * $k; 5569 $xeR = ($x + $w) * $k; 5570 } 5571 $yeL = (($this->h - ($y + $h)) * $k); 5572 $yeT = (($this->h - $y) * $k); 5573 $xeT = $xeL; 5574 $xeB = $xeR; 5575 $yeR = $yeT; 5576 $yeB = $yeL; 5577 if (is_string($brd)) { 5578 // convert string to array 5579 $slen = strlen($brd); 5580 $newbrd = array(); 5581 for ($i = 0; $i < $slen; ++$i) { 5582 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter'); 5583 } 5584 $brd = $newbrd; 5585 } 5586 if (isset($brd['mode'])) { 5587 $mode = $brd['mode']; 5588 unset($brd['mode']); 5589 } else { 5590 $mode = 'normal'; 5591 } 5592 foreach ($brd as $border => $style) { 5593 if (is_array($style) AND !empty($style)) { 5594 // apply border style 5595 $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '; 5596 $s .= $this->SetLineStyle($style, true)."\n"; 5597 } 5598 switch ($mode) { 5599 case 'ext': { 5600 $off = (($this->LineWidth / 2) * $k); 5601 $xL = $xeL - $off; 5602 $xR = $xeR + $off; 5603 $yT = $yeT + $off; 5604 $yL = $yeL - $off; 5605 $xT = $xL; 5606 $xB = $xR; 5607 $yR = $yT; 5608 $yB = $yL; 5609 $w += $this->LineWidth; 5610 $h += $this->LineWidth; 5611 break; 5612 } 5613 case 'int': { 5614 $off = ($this->LineWidth / 2) * $k; 5615 $xL = $xeL + $off; 5616 $xR = $xeR - $off; 5617 $yT = $yeT - $off; 5618 $yL = $yeL + $off; 5619 $xT = $xL; 5620 $xB = $xR; 5621 $yR = $yT; 5622 $yB = $yL; 5623 $w -= $this->LineWidth; 5624 $h -= $this->LineWidth; 5625 break; 5626 } 5627 case 'normal': 5628 default: { 5629 $xL = $xeL; 5630 $xT = $xeT; 5631 $xB = $xeB; 5632 $xR = $xeR; 5633 $yL = $yeL; 5634 $yT = $yeT; 5635 $yB = $yeB; 5636 $yR = $yeR; 5637 break; 5638 } 5639 } 5640 // draw borders by case 5641 if (strlen($border) == 4) { 5642 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k)); 5643 } elseif (strlen($border) == 3) { 5644 if (strpos($border,'B') === false) { // LTR 5645 $s .= sprintf('%F %F m ', $xL, $yL); 5646 $s .= sprintf('%F %F l ', $xT, $yT); 5647 $s .= sprintf('%F %F l ', $xR, $yR); 5648 $s .= sprintf('%F %F l ', $xB, $yB); 5649 $s .= 'S '; 5650 } elseif (strpos($border,'L') === false) { // TRB 5651 $s .= sprintf('%F %F m ', $xT, $yT); 5652 $s .= sprintf('%F %F l ', $xR, $yR); 5653 $s .= sprintf('%F %F l ', $xB, $yB); 5654 $s .= sprintf('%F %F l ', $xL, $yL); 5655 $s .= 'S '; 5656 } elseif (strpos($border,'T') === false) { // RBL 5657 $s .= sprintf('%F %F m ', $xR, $yR); 5658 $s .= sprintf('%F %F l ', $xB, $yB); 5659 $s .= sprintf('%F %F l ', $xL, $yL); 5660 $s .= sprintf('%F %F l ', $xT, $yT); 5661 $s .= 'S '; 5662 } elseif (strpos($border,'R') === false) { // BLT 5663 $s .= sprintf('%F %F m ', $xB, $yB); 5664 $s .= sprintf('%F %F l ', $xL, $yL); 5665 $s .= sprintf('%F %F l ', $xT, $yT); 5666 $s .= sprintf('%F %F l ', $xR, $yR); 5667 $s .= 'S '; 5668 } 5669 } elseif (strlen($border) == 2) { 5670 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT 5671 $s .= sprintf('%F %F m ', $xL, $yL); 5672 $s .= sprintf('%F %F l ', $xT, $yT); 5673 $s .= sprintf('%F %F l ', $xR, $yR); 5674 $s .= 'S '; 5675 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR 5676 $s .= sprintf('%F %F m ', $xT, $yT); 5677 $s .= sprintf('%F %F l ', $xR, $yR); 5678 $s .= sprintf('%F %F l ', $xB, $yB); 5679 $s .= 'S '; 5680 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB 5681 $s .= sprintf('%F %F m ', $xR, $yR); 5682 $s .= sprintf('%F %F l ', $xB, $yB); 5683 $s .= sprintf('%F %F l ', $xL, $yL); 5684 $s .= 'S '; 5685 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL 5686 $s .= sprintf('%F %F m ', $xB, $yB); 5687 $s .= sprintf('%F %F l ', $xL, $yL); 5688 $s .= sprintf('%F %F l ', $xT, $yT); 5689 $s .= 'S '; 5690 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR 5691 $s .= sprintf('%F %F m ', $xL, $yL); 5692 $s .= sprintf('%F %F l ', $xT, $yT); 5693 $s .= 'S '; 5694 $s .= sprintf('%F %F m ', $xR, $yR); 5695 $s .= sprintf('%F %F l ', $xB, $yB); 5696 $s .= 'S '; 5697 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB 5698 $s .= sprintf('%F %F m ', $xT, $yT); 5699 $s .= sprintf('%F %F l ', $xR, $yR); 5700 $s .= 'S '; 5701 $s .= sprintf('%F %F m ', $xB, $yB); 5702 $s .= sprintf('%F %F l ', $xL, $yL); 5703 $s .= 'S '; 5704 } 5705 } else { // strlen($border) == 1 5706 if (strpos($border,'L') !== false) { // L 5707 $s .= sprintf('%F %F m ', $xL, $yL); 5708 $s .= sprintf('%F %F l ', $xT, $yT); 5709 $s .= 'S '; 5710 } elseif (strpos($border,'T') !== false) { // T 5711 $s .= sprintf('%F %F m ', $xT, $yT); 5712 $s .= sprintf('%F %F l ', $xR, $yR); 5713 $s .= 'S '; 5714 } elseif (strpos($border,'R') !== false) { // R 5715 $s .= sprintf('%F %F m ', $xR, $yR); 5716 $s .= sprintf('%F %F l ', $xB, $yB); 5717 $s .= 'S '; 5718 } elseif (strpos($border,'B') !== false) { // B 5719 $s .= sprintf('%F %F m ', $xB, $yB); 5720 $s .= sprintf('%F %F l ', $xL, $yL); 5721 $s .= 'S '; 5722 } 5723 } 5724 if (is_array($style) AND !empty($style)) { 5725 // reset border style to previous value 5726 $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n"; 5727 } 5728 } 5729 return $s; 5730 } 5731 5732 /** 5733 * This method allows printing text with line breaks. 5734 * 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 /> 5735 * Text can be aligned, centered or justified. The cell block can be framed and the background painted. 5736 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 5737 * @param $h (float) Cell minimum height. The cell extends automatically if needed. 5738 * @param $txt (string) String to print 5739 * @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))) 5740 * @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> 5741 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 5742 * @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> 5743 * @param $x (float) x position in user units 5744 * @param $y (float) y position in user units 5745 * @param $reseth (boolean) if true reset the last cell height (default true). 5746 * @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. 5747 * @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. 5748 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width. 5749 * @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. 5750 * @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. 5751 * @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 equal to $h. 5752 * @return int Return the number of cells or 1 for html mode. 5753 * @public 5754 * @since 1.3 5755 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() 5756 */ 5757 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) { 5758 $prev_cell_margin = $this->cell_margin; 5759 $prev_cell_padding = $this->cell_padding; 5760 // adjust internal padding 5761 $this->adjustCellPadding($border); 5762 $mc_padding = $this->cell_padding; 5763 $mc_margin = $this->cell_margin; 5764 $this->cell_padding['T'] = 0; 5765 $this->cell_padding['B'] = 0; 5766 $this->setCellMargins(0, 0, 0, 0); 5767 if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) { 5768 // reset row height 5769 $this->resetLastH(); 5770 } 5771 if (!TCPDF_STATIC::empty_string($y)) { 5772 $this->SetY($y); 5773 } else { 5774 $y = $this->GetY(); 5775 } 5776 $resth = 0; 5777 if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) { 5778 // spit cell in more pages/columns 5779 $newh = ($this->PageBreakTrigger - $y); 5780 $resth = ($h - $newh); // cell to be printed on the next page/column 5781 $h = $newh; 5782 } 5783 // get current page number 5784 $startpage = $this->page; 5785 // get current column 5786 $startcolumn = $this->current_column; 5787 if (!TCPDF_STATIC::empty_string($x)) { 5788 $this->SetX($x); 5789 } else { 5790 $x = $this->GetX(); 5791 } 5792 // check page for no-write regions and adapt page margins if necessary 5793 list($x, $y) = $this->checkPageRegions(0, $x, $y); 5794 // apply margins 5795 $oy = $y + $mc_margin['T']; 5796 if ($this->rtl) { 5797 $ox = ($this->w - $x - $mc_margin['R']); 5798 } else { 5799 $ox = ($x + $mc_margin['L']); 5800 } 5801 $this->x = $ox; 5802 $this->y = $oy; 5803 // set width 5804 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 5805 if ($this->rtl) { 5806 $w = ($this->x - $this->lMargin - $mc_margin['L']); 5807 } else { 5808 $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']); 5809 } 5810 } 5811 // store original margin values 5812 $lMargin = $this->lMargin; 5813 $rMargin = $this->rMargin; 5814 if ($this->rtl) { 5815 $this->rMargin = ($this->w - $this->x); 5816 $this->lMargin = ($this->x - $w); 5817 } else { 5818 $this->lMargin = ($this->x); 5819 $this->rMargin = ($this->w - $this->x - $w); 5820 } 5821 $this->clMargin = $this->lMargin; 5822 $this->crMargin = $this->rMargin; 5823 if ($autopadding) { 5824 // add top padding 5825 $this->y += $mc_padding['T']; 5826 } 5827 if ($ishtml) { // ******* Write HTML text 5828 $this->writeHTML($txt, true, false, $reseth, true, $align); 5829 $nl = 1; 5830 } else { // ******* Write simple text 5831 $prev_FontSizePt = $this->FontSizePt; 5832 if ($fitcell) { 5833 // ajust height values 5834 $tobottom = ($this->h - $this->y - $this->bMargin - $this->cell_padding['T'] - $this->cell_padding['B']); 5835 $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom)); 5836 } 5837 // vertical alignment 5838 if ($maxh > 0) { 5839 // get text height 5840 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5841 if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt > 1)) { 5842 // try to reduce font size to fit text on cell (use a quick search algorithm) 5843 $fmin = 1; 5844 $fmax = $this->FontSizePt; 5845 $diff_epsilon = (1 / $this->k); // one point (min resolution) 5846 $maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations 5847 while ($maxit >= 0) { 5848 $fmid = (($fmax + $fmin) / 2); 5849 $this->SetFontSize($fmid, false); 5850 $this->resetLastH(); 5851 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5852 $diff = ($maxh - $text_height); 5853 if ($diff >= 0) { 5854 if ($diff <= $diff_epsilon) { 5855 break; 5856 } 5857 $fmin = $fmid; 5858 } else { 5859 $fmax = $fmid; 5860 } 5861 --$maxit; 5862 } 5863 if ($maxit < 0) { 5864 // premature exit, we get the minimum font value to fit the cell 5865 $this->SetFontSize($fmin); 5866 $this->resetLastH(); 5867 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5868 } else { 5869 $this->SetFontSize($fmid); 5870 $this->resetLastH(); 5871 } 5872 } 5873 if ($text_height < $maxh) { 5874 if ($valign == 'M') { 5875 // text vertically centered 5876 $this->y += (($maxh - $text_height) / 2); 5877 } elseif ($valign == 'B') { 5878 // text vertically aligned on bottom 5879 $this->y += ($maxh - $text_height); 5880 } 5881 } 5882 } 5883 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin); 5884 if ($fitcell) { 5885 // restore font size 5886 $this->SetFontSize($prev_FontSizePt); 5887 } 5888 } 5889 if ($autopadding) { 5890 // add bottom padding 5891 $this->y += $mc_padding['B']; 5892 } 5893 // Get end-of-text Y position 5894 $currentY = $this->y; 5895 // get latest page number 5896 $endpage = $this->page; 5897 if ($resth > 0) { 5898 $skip = ($endpage - $startpage); 5899 $tmpresth = $resth; 5900 while ($tmpresth > 0) { 5901 if ($skip <= 0) { 5902 // add a page (or trig AcceptPageBreak() for multicolumn mode) 5903 $this->checkPageBreak($this->PageBreakTrigger + 1); 5904 } 5905 if ($this->num_columns > 1) { 5906 $tmpresth -= ($this->h - $this->y - $this->bMargin); 5907 } else { 5908 $tmpresth -= ($this->h - $this->tMargin - $this->bMargin); 5909 } 5910 --$skip; 5911 } 5912 $currentY = $this->y; 5913 $endpage = $this->page; 5914 } 5915 // get latest column 5916 $endcolumn = $this->current_column; 5917 if ($this->num_columns == 0) { 5918 $this->num_columns = 1; 5919 } 5920 // disable page regions check 5921 $check_page_regions = $this->check_page_regions; 5922 $this->check_page_regions = false; 5923 // get border modes 5924 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 5925 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 5926 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 5927 // design borders around HTML cells. 5928 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 5929 $ccode = ''; 5930 $this->setPage($page); 5931 if ($this->num_columns < 2) { 5932 // single-column mode 5933 $this->SetX($x); 5934 $this->y = $this->tMargin; 5935 } 5936 // account for margin changes 5937 if ($page > $startpage) { 5938 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 5939 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 5940 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 5941 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 5942 } 5943 } 5944 if ($startpage == $endpage) { 5945 // single page 5946 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 5947 $this->selectColumn($column); 5948 if ($this->rtl) { 5949 $this->x -= $mc_margin['R']; 5950 } else { 5951 $this->x += $mc_margin['L']; 5952 } 5953 if ($startcolumn == $endcolumn) { // single column 5954 $cborder = $border; 5955 $h = max($h, ($currentY - $oy)); 5956 $this->y = $oy; 5957 } elseif ($column == $startcolumn) { // first column 5958 $cborder = $border_start; 5959 $this->y = $oy; 5960 $h = $this->h - $this->y - $this->bMargin; 5961 } elseif ($column == $endcolumn) { // end column 5962 $cborder = $border_end; 5963 $h = $currentY - $this->y; 5964 if ($resth > $h) { 5965 $h = $resth; 5966 } 5967 } else { // middle column 5968 $cborder = $border_middle; 5969 $h = $this->h - $this->y - $this->bMargin; 5970 $resth -= $h; 5971 } 5972 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 5973 } // end for each column 5974 } elseif ($page == $startpage) { // first page 5975 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 5976 $this->selectColumn($column); 5977 if ($this->rtl) { 5978 $this->x -= $mc_margin['R']; 5979 } else { 5980 $this->x += $mc_margin['L']; 5981 } 5982 if ($column == $startcolumn) { // first column 5983 $cborder = $border_start; 5984 $this->y = $oy; 5985 $h = $this->h - $this->y - $this->bMargin; 5986 } else { // middle column 5987 $cborder = $border_middle; 5988 $h = $this->h - $this->y - $this->bMargin; 5989 $resth -= $h; 5990 } 5991 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 5992 } // end for each column 5993 } elseif ($page == $endpage) { // last page 5994 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 5995 $this->selectColumn($column); 5996 if ($this->rtl) { 5997 $this->x -= $mc_margin['R']; 5998 } else { 5999 $this->x += $mc_margin['L']; 6000 } 6001 if ($column == $endcolumn) { 6002 // end column 6003 $cborder = $border_end; 6004 $h = $currentY - $this->y; 6005 if ($resth > $h) { 6006 $h = $resth; 6007 } 6008 } else { 6009 // middle column 6010 $cborder = $border_middle; 6011 $h = $this->h - $this->y - $this->bMargin; 6012 $resth -= $h; 6013 } 6014 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6015 } // end for each column 6016 } else { // middle page 6017 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 6018 $this->selectColumn($column); 6019 if ($this->rtl) { 6020 $this->x -= $mc_margin['R']; 6021 } else { 6022 $this->x += $mc_margin['L']; 6023 } 6024 $cborder = $border_middle; 6025 $h = $this->h - $this->y - $this->bMargin; 6026 $resth -= $h; 6027 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6028 } // end for each column 6029 } 6030 if ($cborder OR $fill) { 6031 $offsetlen = strlen($ccode); 6032 // draw border and fill 6033 if ($this->inxobj) { 6034 // we are inside an XObject template 6035 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 6036 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 6037 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 6038 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 6039 } else { 6040 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 6041 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 6042 } 6043 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 6044 $pstart = substr($pagebuff, 0, $pagemark); 6045 $pend = substr($pagebuff, $pagemark); 6046 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 6047 } else { 6048 if (end($this->transfmrk[$this->page]) !== false) { 6049 $pagemarkkey = key($this->transfmrk[$this->page]); 6050 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 6051 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; 6052 } elseif ($this->InFooter) { 6053 $pagemark = $this->footerpos[$this->page]; 6054 $this->footerpos[$this->page] += $offsetlen; 6055 } else { 6056 $pagemark = $this->intmrk[$this->page]; 6057 $this->intmrk[$this->page] += $offsetlen; 6058 } 6059 $pagebuff = $this->getPageBuffer($this->page); 6060 $pstart = substr($pagebuff, 0, $pagemark); 6061 $pend = substr($pagebuff, $pagemark); 6062 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 6063 } 6064 } 6065 } // end for each page 6066 // restore page regions check 6067 $this->check_page_regions = $check_page_regions; 6068 // Get end-of-cell Y position 6069 $currentY = $this->GetY(); 6070 // restore previous values 6071 if ($this->num_columns > 1) { 6072 $this->selectColumn(); 6073 } else { 6074 // restore original margins 6075 $this->lMargin = $lMargin; 6076 $this->rMargin = $rMargin; 6077 if ($this->page > $startpage) { 6078 // check for margin variations between pages (i.e. booklet mode) 6079 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']); 6080 $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']); 6081 if (($dl != 0) OR ($dr != 0)) { 6082 $this->lMargin += $dl; 6083 $this->rMargin += $dr; 6084 } 6085 } 6086 } 6087 if ($ln > 0) { 6088 //Go to the beginning of the next line 6089 $this->SetY($currentY + $mc_margin['B']); 6090 if ($ln == 2) { 6091 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); 6092 } 6093 } else { 6094 // go left or right by case 6095 $this->setPage($startpage); 6096 $this->y = $y; 6097 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); 6098 } 6099 $this->setContentMark(); 6100 $this->cell_padding = $prev_cell_padding; 6101 $this->cell_margin = $prev_cell_margin; 6102 $this->clMargin = $this->lMargin; 6103 $this->crMargin = $this->rMargin; 6104 return $nl; 6105 } 6106 6107 /** 6108 * This method return the estimated number of lines for print a simple text string using Multicell() method. 6109 * @param $txt (string) String for calculating his height 6110 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 6111 * @param $reseth (boolean) if true reset the last cell height (default false). 6112 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true). 6113 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding. 6114 * @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))) 6115 * @return float Return the minimal height needed for multicell method for printing the $txt param. 6116 * @author Alexander Escalona Fern\E1ndez, Nicola Asuni 6117 * @public 6118 * @since 4.5.011 6119 */ 6120 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { 6121 if ($txt === NULL) { 6122 return 0; 6123 } 6124 if ($txt === '') { 6125 // empty string 6126 return 1; 6127 } 6128 // adjust internal padding 6129 $prev_cell_padding = $this->cell_padding; 6130 $prev_lasth = $this->lasth; 6131 if (is_array($cellpadding)) { 6132 $this->cell_padding = $cellpadding; 6133 } 6134 $this->adjustCellPadding($border); 6135 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 6136 if ($this->rtl) { 6137 $w = $this->x - $this->lMargin; 6138 } else { 6139 $w = $this->w - $this->rMargin - $this->x; 6140 } 6141 } 6142 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 6143 if ($reseth) { 6144 // reset row height 6145 $this->resetLastH(); 6146 } 6147 $lines = 1; 6148 $sum = 0; 6149 $chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6150 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true); 6151 $length = count($chars); 6152 $lastSeparator = -1; 6153 for ($i = 0; $i < $length; ++$i) { 6154 $c = $chars[$i]; 6155 $charWidth = $charsWidth[$i]; 6156 if (($c != 160) 6157 AND (($c == 173) 6158 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode)) 6159 OR (($c == 45) 6160 AND ($i > 0) AND ($i < ($length - 1)) 6161 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode)) 6162 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode)) 6163 ) 6164 ) 6165 ) { 6166 $lastSeparator = $i; 6167 } 6168 if ((($sum + $charWidth) > $wmax) OR ($c == 10)) { 6169 ++$lines; 6170 if ($c == 10) { 6171 $lastSeparator = -1; 6172 $sum = 0; 6173 } elseif ($lastSeparator != -1) { 6174 $i = $lastSeparator; 6175 $lastSeparator = -1; 6176 $sum = 0; 6177 } else { 6178 $sum = $charWidth; 6179 } 6180 } else { 6181 $sum += $charWidth; 6182 } 6183 } 6184 if ($chars[($length - 1)] == 10) { 6185 --$lines; 6186 } 6187 $this->cell_padding = $prev_cell_padding; 6188 $this->lasth = $prev_lasth; 6189 return $lines; 6190 } 6191 6192 /** 6193 * This method return the estimated height needed for printing a simple text string using the Multicell() method. 6194 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique: 6195 * @pre 6196 * // store current object 6197 * $pdf->startTransaction(); 6198 * // store starting values 6199 * $start_y = $pdf->GetY(); 6200 * $start_page = $pdf->getPage(); 6201 * // call your printing functions with your parameters 6202 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6203 * $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); 6204 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6205 * // get the new Y 6206 * $end_y = $pdf->GetY(); 6207 * $end_page = $pdf->getPage(); 6208 * // calculate height 6209 * $height = 0; 6210 * if ($end_page == $start_page) { 6211 * $height = $end_y - $start_y; 6212 * } else { 6213 * for ($page=$start_page; $page <= $end_page; ++$page) { 6214 * $this->setPage($page); 6215 * if ($page == $start_page) { 6216 * // first page 6217 * $height = $this->h - $start_y - $this->bMargin; 6218 * } elseif ($page == $end_page) { 6219 * // last page 6220 * $height = $end_y - $this->tMargin; 6221 * } else { 6222 * $height = $this->h - $this->tMargin - $this->bMargin; 6223 * } 6224 * } 6225 * } 6226 * // restore previous object 6227 * $pdf = $pdf->rollbackTransaction(); 6228 * 6229 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 6230 * @param $txt (string) String for calculating his height 6231 * @param $reseth (boolean) if true reset the last cell height (default false). 6232 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true). 6233 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding. 6234 * @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))) 6235 * @return float Return the minimal height needed for multicell method for printing the $txt param. 6236 * @author Nicola Asuni, Alexander Escalona Fern\E1ndez 6237 * @public 6238 */ 6239 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { 6240 // adjust internal padding 6241 $prev_cell_padding = $this->cell_padding; 6242 $prev_lasth = $this->lasth; 6243 if (is_array($cellpadding)) { 6244 $this->cell_padding = $cellpadding; 6245 } 6246 $this->adjustCellPadding($border); 6247 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border); 6248 $height = $this->getCellHeight(($lines * $this->FontSize), $autopadding); 6249 $this->cell_padding = $prev_cell_padding; 6250 $this->lasth = $prev_lasth; 6251 return $height; 6252 } 6253 6254 /** 6255 * This method prints text from the current position.<br /> 6256 * @param $h (float) Line height 6257 * @param $txt (string) String to print 6258 * @param $link (mixed) URL or identifier returned by AddLink() 6259 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 6260 * @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> 6261 * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line. 6262 * @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. 6263 * @param $firstline (boolean) if true prints only the first line and return the remaining string. 6264 * @param $firstblock (boolean) if true the string is the starting of a line. 6265 * @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. 6266 * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode). 6267 * @param $margin (array) margin array of the parent container 6268 * @return mixed Return the number of cells or the remaining string if $firstline = true. 6269 * @public 6270 * @since 1.5 6271 */ 6272 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') { 6273 // check page for no-write regions and adapt page margins if necessary 6274 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); 6275 if (strlen($txt) == 0) { 6276 // fix empty text 6277 $txt = ' '; 6278 } 6279 if ($margin === '') { 6280 // set default margins 6281 $margin = $this->cell_margin; 6282 } 6283 // remove carriage returns 6284 $s = str_replace("\r", '', $txt); 6285 // check if string contains arabic text 6286 if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) { 6287 $arabic = true; 6288 } else { 6289 $arabic = false; 6290 } 6291 // check if string contains RTL text 6292 if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) { 6293 $rtlmode = true; 6294 } else { 6295 $rtlmode = false; 6296 } 6297 // get a char width 6298 $chrwidth = $this->GetCharWidth(46); // dot character 6299 // get array of unicode values 6300 $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont); 6301 // calculate maximum width for a single character on string 6302 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true); 6303 array_walk($chrw, array($this, 'getRawCharWidth')); 6304 $maxchwidth = max($chrw); 6305 // get array of chars 6306 $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode); 6307 // get the number of characters 6308 $nb = count($chars); 6309 // replacement for SHY character (minus symbol) 6310 $shy_replacement = 45; 6311 $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode); 6312 // widht for SHY replacement 6313 $shy_replacement_width = $this->GetCharWidth($shy_replacement); 6314 // page width 6315 $pw = $w = $this->w - $this->lMargin - $this->rMargin; 6316 // calculate remaining line width ($w) 6317 if ($this->rtl) { 6318 $w = $this->x - $this->lMargin; 6319 } else { 6320 $w = $this->w - $this->rMargin - $this->x; 6321 } 6322 // max column width 6323 $wmax = ($w - $wadj); 6324 if (!$firstline) { 6325 $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']); 6326 } 6327 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) { 6328 // the maximum width character do not fit on column 6329 return ''; 6330 } 6331 // minimum row height 6332 $row_height = max($h, $this->getCellHeight($this->FontSize)); 6333 // max Y 6334 $maxy = $this->y + $maxh - max($row_height, $h); 6335 $start_page = $this->page; 6336 $i = 0; // character position 6337 $j = 0; // current starting position 6338 $sep = -1; // position of the last blank space 6339 $prevsep = $sep; // previous separator 6340 $shy = false; // true if the last blank is a soft hypen (SHY) 6341 $prevshy = $shy; // previous shy mode 6342 $l = 0; // current string length 6343 $nl = 0; //number of lines 6344 $linebreak = false; 6345 $pc = 0; // previous character 6346 // for each character 6347 while ($i < $nb) { 6348 if (($maxh > 0) AND ($this->y > $maxy) ) { 6349 break; 6350 } 6351 //Get the current character 6352 $c = $chars[$i]; 6353 if ($c == 10) { // 10 = "\n" = new line 6354 //Explicit line break 6355 if ($align == 'J') { 6356 if ($this->rtl) { 6357 $talign = 'R'; 6358 } else { 6359 $talign = 'L'; 6360 } 6361 } else { 6362 $talign = $align; 6363 } 6364 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6365 if ($firstline) { 6366 $startx = $this->x; 6367 $tmparr = array_slice($chars, $j, ($i - $j)); 6368 if ($rtlmode) { 6369 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6370 } 6371 $linew = $this->GetArrStringWidth($tmparr); 6372 unset($tmparr); 6373 if ($this->rtl) { 6374 $this->endlinex = $startx - $linew; 6375 } else { 6376 $this->endlinex = $startx + $linew; 6377 } 6378 $w = $linew; 6379 $tmpcellpadding = $this->cell_padding; 6380 if ($maxh == 0) { 6381 $this->SetCellPadding(0); 6382 } 6383 } 6384 if ($firstblock AND $this->isRTLTextDir()) { 6385 $tmpstr = $this->stringRightTrim($tmpstr); 6386 } 6387 // Skip newlines at the beginning of a page or column 6388 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) { 6389 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch); 6390 } 6391 unset($tmpstr); 6392 if ($firstline) { 6393 $this->cell_padding = $tmpcellpadding; 6394 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6395 } 6396 ++$nl; 6397 $j = $i + 1; 6398 $l = 0; 6399 $sep = -1; 6400 $prevsep = $sep; 6401 $shy = false; 6402 // account for margin changes 6403 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { 6404 $this->AcceptPageBreak(); 6405 if ($this->rtl) { 6406 $this->x -= $margin['R']; 6407 } else { 6408 $this->x += $margin['L']; 6409 } 6410 $this->lMargin += $margin['L']; 6411 $this->rMargin += $margin['R']; 6412 } 6413 $w = $this->getRemainingWidth(); 6414 $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']); 6415 } else { 6416 // 160 is the non-breaking space. 6417 // 173 is SHY (Soft Hypen). 6418 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 6419 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 6420 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 6421 if (($c != 160) 6422 AND (($c == 173) 6423 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode)) 6424 OR (($c == 45) 6425 AND ($i < ($nb - 1)) 6426 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode)) 6427 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode)) 6428 ) 6429 ) 6430 ) { 6431 // update last blank space position 6432 $prevsep = $sep; 6433 $sep = $i; 6434 // check if is a SHY 6435 if (($c == 173) OR ($c == 45)) { 6436 $prevshy = $shy; 6437 $shy = true; 6438 if ($pc == 45) { 6439 $tmp_shy_replacement_width = 0; 6440 $tmp_shy_replacement_char = ''; 6441 } else { 6442 $tmp_shy_replacement_width = $shy_replacement_width; 6443 $tmp_shy_replacement_char = $shy_replacement_char; 6444 } 6445 } else { 6446 $shy = false; 6447 } 6448 } 6449 // update string length 6450 if ($this->isUnicodeFont() AND ($arabic)) { 6451 // with bidirectional algorithm some chars may be changed affecting the line length 6452 // *** very slow *** 6453 $l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont)); 6454 } else { 6455 $l += $this->GetCharWidth($c); 6456 } 6457 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) >= $wmax))) { 6458 if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) { 6459 $sep = $prevsep; 6460 $shy = $prevshy; 6461 } 6462 // we have reached the end of column 6463 if ($sep == -1) { 6464 // check if the line was already started 6465 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth))) 6466 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) { 6467 // print a void cell and go to next line 6468 $this->Cell($w, $h, '', 0, 1); 6469 $linebreak = true; 6470 if ($firstline) { 6471 return (TCPDF_FONTS::UniArrSubString($uchars, $j)); 6472 } 6473 } else { 6474 // truncate the word because do not fit on column 6475 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6476 if ($firstline) { 6477 $startx = $this->x; 6478 $tmparr = array_slice($chars, $j, ($i - $j)); 6479 if ($rtlmode) { 6480 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6481 } 6482 $linew = $this->GetArrStringWidth($tmparr); 6483 unset($tmparr); 6484 if ($this->rtl) { 6485 $this->endlinex = $startx - $linew; 6486 } else { 6487 $this->endlinex = $startx + $linew; 6488 } 6489 $w = $linew; 6490 $tmpcellpadding = $this->cell_padding; 6491 if ($maxh == 0) { 6492 $this->SetCellPadding(0); 6493 } 6494 } 6495 if ($firstblock AND $this->isRTLTextDir()) { 6496 $tmpstr = $this->stringRightTrim($tmpstr); 6497 } 6498 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 6499 unset($tmpstr); 6500 if ($firstline) { 6501 $this->cell_padding = $tmpcellpadding; 6502 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6503 } 6504 $j = $i; 6505 --$i; 6506 } 6507 } else { 6508 // word wrapping 6509 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) { 6510 $endspace = 1; 6511 } else { 6512 $endspace = 0; 6513 } 6514 // check the length of the next string 6515 $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)); 6516 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest)); 6517 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) { 6518 // truncate the word because do not fit on a full page width 6519 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6520 if ($firstline) { 6521 $startx = $this->x; 6522 $tmparr = array_slice($chars, $j, ($i - $j)); 6523 if ($rtlmode) { 6524 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6525 } 6526 $linew = $this->GetArrStringWidth($tmparr); 6527 unset($tmparr); 6528 if ($this->rtl) { 6529 $this->endlinex = ($startx - $linew); 6530 } else { 6531 $this->endlinex = ($startx + $linew); 6532 } 6533 $w = $linew; 6534 $tmpcellpadding = $this->cell_padding; 6535 if ($maxh == 0) { 6536 $this->SetCellPadding(0); 6537 } 6538 } 6539 if ($firstblock AND $this->isRTLTextDir()) { 6540 $tmpstr = $this->stringRightTrim($tmpstr); 6541 } 6542 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 6543 unset($tmpstr); 6544 if ($firstline) { 6545 $this->cell_padding = $tmpcellpadding; 6546 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6547 } 6548 $j = $i; 6549 --$i; 6550 } else { 6551 // word wrapping 6552 if ($shy) { 6553 // add hypen (minus symbol) at the end of the line 6554 $shy_width = $tmp_shy_replacement_width; 6555 if ($this->rtl) { 6556 $shy_char_left = $tmp_shy_replacement_char; 6557 $shy_char_right = ''; 6558 } else { 6559 $shy_char_left = ''; 6560 $shy_char_right = $tmp_shy_replacement_char; 6561 } 6562 } else { 6563 $shy_width = 0; 6564 $shy_char_left = ''; 6565 $shy_char_right = ''; 6566 } 6567 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace)); 6568 if ($firstline) { 6569 $startx = $this->x; 6570 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j)); 6571 if ($rtlmode) { 6572 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6573 } 6574 $linew = $this->GetArrStringWidth($tmparr); 6575 unset($tmparr); 6576 if ($this->rtl) { 6577 $this->endlinex = $startx - $linew - $shy_width; 6578 } else { 6579 $this->endlinex = $startx + $linew + $shy_width; 6580 } 6581 $w = $linew; 6582 $tmpcellpadding = $this->cell_padding; 6583 if ($maxh == 0) { 6584 $this->SetCellPadding(0); 6585 } 6586 } 6587 // print the line 6588 if ($firstblock AND $this->isRTLTextDir()) { 6589 $tmpstr = $this->stringRightTrim($tmpstr); 6590 } 6591 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch); 6592 unset($tmpstr); 6593 if ($firstline) { 6594 if ($chars[$sep] == 45) { 6595 $endspace += 1; 6596 } 6597 // return the remaining text 6598 $this->cell_padding = $tmpcellpadding; 6599 return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace))); 6600 } 6601 $i = $sep; 6602 $sep = -1; 6603 $shy = false; 6604 $j = ($i + 1); 6605 } 6606 } 6607 // account for margin changes 6608 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { 6609 $this->AcceptPageBreak(); 6610 if ($this->rtl) { 6611 $this->x -= $margin['R']; 6612 } else { 6613 $this->x += $margin['L']; 6614 } 6615 $this->lMargin += $margin['L']; 6616 $this->rMargin += $margin['R']; 6617 } 6618 $w = $this->getRemainingWidth(); 6619 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 6620 if ($linebreak) { 6621 $linebreak = false; 6622 } else { 6623 ++$nl; 6624 $l = 0; 6625 } 6626 } 6627 } 6628 // save last character 6629 $pc = $c; 6630 ++$i; 6631 } // end while i < nb 6632 // print last substring (if any) 6633 if ($l > 0) { 6634 switch ($align) { 6635 case 'J': 6636 case 'C': { 6637 $w = $w; 6638 break; 6639 } 6640 case 'L': { 6641 if ($this->rtl) { 6642 $w = $w; 6643 } else { 6644 $w = $l; 6645 } 6646 break; 6647 } 6648 case 'R': { 6649 if ($this->rtl) { 6650 $w = $l; 6651 } else { 6652 $w = $w; 6653 } 6654 break; 6655 } 6656 default: { 6657 $w = $l; 6658 break; 6659 } 6660 } 6661 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb); 6662 if ($firstline) { 6663 $startx = $this->x; 6664 $tmparr = array_slice($chars, $j, ($nb - $j)); 6665 if ($rtlmode) { 6666 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6667 } 6668 $linew = $this->GetArrStringWidth($tmparr); 6669 unset($tmparr); 6670 if ($this->rtl) { 6671 $this->endlinex = $startx - $linew; 6672 } else { 6673 $this->endlinex = $startx + $linew; 6674 } 6675 $w = $linew; 6676 $tmpcellpadding = $this->cell_padding; 6677 if ($maxh == 0) { 6678 $this->SetCellPadding(0); 6679 } 6680 } 6681 if ($firstblock AND $this->isRTLTextDir()) { 6682 $tmpstr = $this->stringRightTrim($tmpstr); 6683 } 6684 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch); 6685 unset($tmpstr); 6686 if ($firstline) { 6687 $this->cell_padding = $tmpcellpadding; 6688 return (TCPDF_FONTS::UniArrSubString($uchars, $nb)); 6689 } 6690 ++$nl; 6691 } 6692 if ($firstline) { 6693 return ''; 6694 } 6695 return $nl; 6696 } 6697 6698 /** 6699 * Returns the remaining width between the current position and margins. 6700 * @return int Return the remaining width 6701 * @protected 6702 */ 6703 protected function getRemainingWidth() { 6704 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y); 6705 if ($this->rtl) { 6706 return ($this->x - $this->lMargin); 6707 } else { 6708 return ($this->w - $this->rMargin - $this->x); 6709 } 6710 } 6711 6712 /** 6713 * Set the block dimensions accounting for page breaks and page/column fitting 6714 * @param $w (float) width 6715 * @param $h (float) height 6716 * @param $x (float) X coordinate 6717 * @param $y (float) Y coodiante 6718 * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions. 6719 * @return array($w, $h, $x, $y) 6720 * @protected 6721 * @since 5.5.009 (2010-07-05) 6722 */ 6723 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) { 6724 if ($w <= 0) { 6725 // set maximum width 6726 $w = ($this->w - $this->lMargin - $this->rMargin); 6727 if ($w <= 0) { 6728 $w = 1; 6729 } 6730 } 6731 if ($h <= 0) { 6732 // set maximum height 6733 $h = ($this->PageBreakTrigger - $this->tMargin); 6734 if ($h <= 0) { 6735 $h = 1; 6736 } 6737 } 6738 // resize the block to be vertically contained on a single page or single column 6739 if ($fitonpage OR $this->AutoPageBreak) { 6740 $ratio_wh = ($w / $h); 6741 if ($h > ($this->PageBreakTrigger - $this->tMargin)) { 6742 $h = $this->PageBreakTrigger - $this->tMargin; 6743 $w = ($h * $ratio_wh); 6744 } 6745 // resize the block to be horizontally contained on a single page or single column 6746 if ($fitonpage) { 6747 $maxw = ($this->w - $this->lMargin - $this->rMargin); 6748 if ($w > $maxw) { 6749 $w = $maxw; 6750 $h = ($w / $ratio_wh); 6751 } 6752 } 6753 } 6754 // Check whether we need a new page or new column first as this does not fit 6755 $prev_x = $this->x; 6756 $prev_y = $this->y; 6757 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) { 6758 $y = $this->y; 6759 if ($this->rtl) { 6760 $x += ($prev_x - $this->x); 6761 } else { 6762 $x += ($this->x - $prev_x); 6763 } 6764 $this->newline = true; 6765 } 6766 // resize the block to be contained on the remaining available page or column space 6767 if ($fitonpage) { 6768 $ratio_wh = ($w / $h); 6769 if (($y + $h) > $this->PageBreakTrigger) { 6770 $h = $this->PageBreakTrigger - $y; 6771 $w = ($h * $ratio_wh); 6772 } 6773 if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) { 6774 $w = $this->w - $this->rMargin - $x; 6775 $h = ($w / $ratio_wh); 6776 } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) { 6777 $w = $x - $this->lMargin; 6778 $h = ($w / $ratio_wh); 6779 } 6780 } 6781 return array($w, $h, $x, $y); 6782 } 6783 6784 /** 6785 * Puts an image in the page. 6786 * The upper-left corner must be given. 6787 * The dimensions can be specified in different ways:<ul> 6788 * <li>explicit width and height (expressed in user unit)</li> 6789 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li> 6790 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> 6791 * 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; 6792 * The format can be specified explicitly or inferred from the file extension.<br /> 6793 * It is possible to put a link on the image.<br /> 6794 * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> 6795 * @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'). 6796 * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL). 6797 * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL). 6798 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 6799 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 6800 * @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. 6801 * @param $link (mixed) URL or identifier returned by AddLink(). 6802 * @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> 6803 * @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). 6804 * @param $dpi (int) dot-per-inch resolution used on resize 6805 * @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> 6806 * @param $ismask (boolean) true if this image is a mask, false otherwise 6807 * @param $imgmask (mixed) image object returned by this function or false 6808 * @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))) 6809 * @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). 6810 * @param $hidden (boolean) If true do not display the image. 6811 * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions. 6812 * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned). 6813 * @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. 6814 * @return image information 6815 * @public 6816 * @since 1.1 6817 */ 6818 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()) { 6819 if ($this->state != 2) { 6820 return; 6821 } 6822 if (strcmp($x, '') === 0) { 6823 $x = $this->x; 6824 } 6825 if (strcmp($y, '') === 0) { 6826 $y = $this->y; 6827 } 6828 // check page for no-write regions and adapt page margins if necessary 6829 list($x, $y) = $this->checkPageRegions($h, $x, $y); 6830 $exurl = ''; // external streams 6831 $imsize = FALSE; 6832 // check if we are passing an image as file or string 6833 if ($file[0] === '@') { 6834 // image from string 6835 $imgdata = substr($file, 1); 6836 } else { // image file 6837 if ($file[0] === '*') { 6838 // image as external stream 6839 $file = substr($file, 1); 6840 $exurl = $file; 6841 } 6842 // check if is a local file 6843 if (!@file_exists($file)) { 6844 // try to encode spaces on filename 6845 $tfile = str_replace(' ', '%20', $file); 6846 if (@file_exists($tfile)) { 6847 $file = $tfile; 6848 } 6849 } 6850 if (($imsize = @getimagesize($file)) === FALSE) { 6851 if (in_array($file, $this->imagekeys)) { 6852 // get existing image data 6853 $info = $this->getImageBuffer($file); 6854 $imsize = array($info['w'], $info['h']); 6855 } elseif (strpos($file, '__tcpdf_'.$this->file_id.'_img') === FALSE) { 6856 $imgdata = TCPDF_STATIC::fileGetContents($file); 6857 } 6858 } 6859 } 6860 if (!empty($imgdata)) { 6861 // copy image to cache 6862 $original_file = $file; 6863 $file = TCPDF_STATIC::getObjFilename('img', $this->file_id); 6864 $fp = TCPDF_STATIC::fopenLocal($file, 'w'); 6865 if (!$fp) { 6866 $this->Error('Unable to write file: '.$file); 6867 } 6868 fwrite($fp, $imgdata); 6869 fclose($fp); 6870 unset($imgdata); 6871 $imsize = @getimagesize($file); 6872 if ($imsize === FALSE) { 6873 unlink($file); 6874 $file = $original_file; 6875 } 6876 } 6877 if ($imsize === FALSE) { 6878 if (($w > 0) AND ($h > 0)) { 6879 // get measures from specified data 6880 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 6881 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 6882 $imsize = array($pw, $ph); 6883 } else { 6884 $this->Error('[Image] Unable to get the size of the image: '.$file); 6885 } 6886 } 6887 // file hash 6888 $filehash = md5($file); 6889 // get original image width and height in pixels 6890 list($pixw, $pixh) = $imsize; 6891 // calculate image width and height on document 6892 if (($w <= 0) AND ($h <= 0)) { 6893 // convert image size to document unit 6894 $w = $this->pixelsToUnits($pixw); 6895 $h = $this->pixelsToUnits($pixh); 6896 } elseif ($w <= 0) { 6897 $w = $h * $pixw / $pixh; 6898 } elseif ($h <= 0) { 6899 $h = $w * $pixh / $pixw; 6900 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) { 6901 if (strlen($fitbox) !== 2) { 6902 // set default alignment 6903 $fitbox = '--'; 6904 } 6905 // scale image dimensions proportionally to fit within the ($w, $h) box 6906 if ((($w * $pixh) / ($h * $pixw)) < 1) { 6907 // store current height 6908 $oldh = $h; 6909 // calculate new height 6910 $h = $w * $pixh / $pixw; 6911 // height difference 6912 $hdiff = ($oldh - $h); 6913 // vertical alignment 6914 switch (strtoupper($fitbox[1])) { 6915 case 'T': { 6916 break; 6917 } 6918 case 'M': { 6919 $y += ($hdiff / 2); 6920 break; 6921 } 6922 case 'B': { 6923 $y += $hdiff; 6924 break; 6925 } 6926 } 6927 } else { 6928 // store current width 6929 $oldw = $w; 6930 // calculate new width 6931 $w = $h * $pixw / $pixh; 6932 // width difference 6933 $wdiff = ($oldw - $w); 6934 // horizontal alignment 6935 switch (strtoupper($fitbox[0])) { 6936 case 'L': { 6937 if ($this->rtl) { 6938 $x -= $wdiff; 6939 } 6940 break; 6941 } 6942 case 'C': { 6943 if ($this->rtl) { 6944 $x -= ($wdiff / 2); 6945 } else { 6946 $x += ($wdiff / 2); 6947 } 6948 break; 6949 } 6950 case 'R': { 6951 if (!$this->rtl) { 6952 $x += $wdiff; 6953 } 6954 break; 6955 } 6956 } 6957 } 6958 } 6959 // fit the image on available space 6960 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 6961 // calculate new minimum dimensions in pixels 6962 $neww = round($w * $this->k * $dpi / $this->dpi); 6963 $newh = round($h * $this->k * $dpi / $this->dpi); 6964 // check if resize is necessary (resize is used only to reduce the image) 6965 $newsize = ($neww * $newh); 6966 $pixsize = ($pixw * $pixh); 6967 if (intval($resize) == 2) { 6968 $resize = true; 6969 } elseif ($newsize >= $pixsize) { 6970 $resize = false; 6971 } 6972 // check if image has been already added on document 6973 $newimage = true; 6974 if (in_array($file, $this->imagekeys)) { 6975 $newimage = false; 6976 // get existing image data 6977 $info = $this->getImageBuffer($file); 6978 if (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) { 6979 // check if the newer image is larger 6980 $oldsize = ($info['w'] * $info['h']); 6981 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 6982 $newimage = true; 6983 } 6984 } 6985 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)) { 6986 // create temp image file (without alpha channel) 6987 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash; 6988 // create temp alpha file 6989 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash; 6990 // check for cached images 6991 if (in_array($tempfile_plain, $this->imagekeys)) { 6992 // get existing image data 6993 $info = $this->getImageBuffer($tempfile_plain); 6994 // check if the newer image is larger 6995 $oldsize = ($info['w'] * $info['h']); 6996 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 6997 $newimage = true; 6998 } else { 6999 $newimage = false; 7000 // embed mask image 7001 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 7002 // embed image, masked with previously embedded mask 7003 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 7004 } 7005 } 7006 } 7007 if ($newimage) { 7008 //First use of image, get info 7009 $type = strtolower($type); 7010 if ($type == '') { 7011 $type = TCPDF_IMAGES::getImageFileType($file, $imsize); 7012 } elseif ($type == 'jpg') { 7013 $type = 'jpeg'; 7014 } 7015 $mqr = TCPDF_STATIC::get_mqr(); 7016 TCPDF_STATIC::set_mqr(false); 7017 // Specific image handlers (defined on TCPDF_IMAGES CLASS) 7018 $mtd = '_parse'.$type; 7019 // GD image handler function 7020 $gdfunction = 'imagecreatefrom'.$type; 7021 $info = false; 7022 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) { 7023 // TCPDF image functions 7024 $info = TCPDF_IMAGES::$mtd($file); 7025 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) 7026 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) { 7027 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash); 7028 } 7029 } 7030 if (($info === false) AND function_exists($gdfunction)) { 7031 try { 7032 // GD library 7033 $img = $gdfunction($file); 7034 if ($img !== false) { 7035 if ($resize) { 7036 $imgr = imagecreatetruecolor($neww, $newh); 7037 if (($type == 'gif') OR ($type == 'png')) { 7038 $imgr = TCPDF_IMAGES::setGDImageTransparency($imgr, $img); 7039 } 7040 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 7041 $img = $imgr; 7042 } 7043 if (($type == 'gif') OR ($type == 'png')) { 7044 $info = TCPDF_IMAGES::_toPNG($img, TCPDF_STATIC::getObjFilename('img', $this->file_id)); 7045 } else { 7046 $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality, TCPDF_STATIC::getObjFilename('img', $this->file_id)); 7047 } 7048 } 7049 } catch(Exception $e) { 7050 $info = false; 7051 } 7052 } 7053 if (($info === false) AND extension_loaded('imagick')) { 7054 try { 7055 // ImageMagick library 7056 $img = new Imagick(); 7057 if ($type == 'svg') { 7058 if ($file[0] === '@') { 7059 // image from string 7060 $svgimg = substr($file, 1); 7061 } else { 7062 // get SVG file content 7063 $svgimg = TCPDF_STATIC::fileGetContents($file); 7064 } 7065 if ($svgimg !== FALSE) { 7066 // get width and height 7067 $regs = array(); 7068 if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) { 7069 $svgtag = $regs[1]; 7070 $tmp = array(); 7071 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { 7072 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 7073 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit; 7074 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1); 7075 } else { 7076 $ow = $w; 7077 } 7078 $tmp = array(); 7079 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { 7080 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 7081 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit; 7082 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1); 7083 } else { 7084 $oh = $h; 7085 } 7086 $tmp = array(); 7087 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) { 7088 $vbw = ($ow * $this->imgscale * $this->k); 7089 $vbh = ($oh * $this->imgscale * $this->k); 7090 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh); 7091 $svgtag = $vbox.$svgtag; 7092 } 7093 $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1); 7094 } 7095 $img->readImageBlob($svgimg); 7096 } 7097 } else { 7098 $img->readImage($file); 7099 } 7100 if ($resize) { 7101 $img->resizeImage($neww, $newh, 10, 1, false); 7102 } 7103 $img->setCompressionQuality($this->jpeg_quality); 7104 $img->setImageFormat('jpeg'); 7105 $tempname = TCPDF_STATIC::getObjFilename('img', $this->file_id); 7106 $img->writeImage($tempname); 7107 $info = TCPDF_IMAGES::_parsejpeg($tempname); 7108 unlink($tempname); 7109 $img->destroy(); 7110 } catch(Exception $e) { 7111 $info = false; 7112 } 7113 } 7114 if ($info === false) { 7115 // unable to process image 7116 return; 7117 } 7118 TCPDF_STATIC::set_mqr($mqr); 7119 if ($ismask) { 7120 // force grayscale 7121 $info['cs'] = 'DeviceGray'; 7122 } 7123 if ($imgmask !== false) { 7124 $info['masked'] = $imgmask; 7125 } 7126 if (!empty($exurl)) { 7127 $info['exurl'] = $exurl; 7128 } 7129 // array of alternative images 7130 $info['altimgs'] = $altimgs; 7131 // add image to document 7132 $info['i'] = $this->setImageBuffer($file, $info); 7133 } 7134 // set alignment 7135 $this->img_rb_y = $y + $h; 7136 // set alignment 7137 if ($this->rtl) { 7138 if ($palign == 'L') { 7139 $ximg = $this->lMargin; 7140 } elseif ($palign == 'C') { 7141 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 7142 } elseif ($palign == 'R') { 7143 $ximg = $this->w - $this->rMargin - $w; 7144 } else { 7145 $ximg = $x - $w; 7146 } 7147 $this->img_rb_x = $ximg; 7148 } else { 7149 if ($palign == 'L') { 7150 $ximg = $this->lMargin; 7151 } elseif ($palign == 'C') { 7152 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 7153 } elseif ($palign == 'R') { 7154 $ximg = $this->w - $this->rMargin - $w; 7155 } else { 7156 $ximg = $x; 7157 } 7158 $this->img_rb_x = $ximg + $w; 7159 } 7160 if ($ismask OR $hidden) { 7161 // image is not displayed 7162 return $info['i']; 7163 } 7164 $xkimg = $ximg * $this->k; 7165 if (!$alt) { 7166 // only non-alternative immages will be set 7167 $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'])); 7168 } 7169 if (!empty($border)) { 7170 $bx = $this->x; 7171 $by = $this->y; 7172 $this->x = $ximg; 7173 if ($this->rtl) { 7174 $this->x += $w; 7175 } 7176 $this->y = $y; 7177 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 7178 $this->x = $bx; 7179 $this->y = $by; 7180 } 7181 if ($link) { 7182 $this->Link($ximg, $y, $w, $h, $link, 0); 7183 } 7184 // set pointer to align the next text/objects 7185 switch($align) { 7186 case 'T': { 7187 $this->y = $y; 7188 $this->x = $this->img_rb_x; 7189 break; 7190 } 7191 case 'M': { 7192 $this->y = $y + round($h/2); 7193 $this->x = $this->img_rb_x; 7194 break; 7195 } 7196 case 'B': { 7197 $this->y = $this->img_rb_y; 7198 $this->x = $this->img_rb_x; 7199 break; 7200 } 7201 case 'N': { 7202 $this->SetY($this->img_rb_y); 7203 break; 7204 } 7205 default:{ 7206 break; 7207 } 7208 } 7209 $this->endlinex = $this->img_rb_x; 7210 if ($this->inxobj) { 7211 // we are inside an XObject template 7212 $this->xobjects[$this->xobjid]['images'][] = $info['i']; 7213 } 7214 return $info['i']; 7215 } 7216 7217 /** 7218 * Extract info from a PNG image with alpha channel using the Imagick or GD library. 7219 * @param $file (string) Name of the file containing the image. 7220 * @param $x (float) Abscissa of the upper-left corner. 7221 * @param $y (float) Ordinate of the upper-left corner. 7222 * @param $wpx (float) Original width of the image in pixels. 7223 * @param $hpx (float) original height of the image in pixels. 7224 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 7225 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 7226 * @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. 7227 * @param $link (mixed) URL or identifier returned by AddLink(). 7228 * @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> 7229 * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library). 7230 * @param $dpi (int) dot-per-inch resolution used on resize 7231 * @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> 7232 * @param $filehash (string) File hash used to build unique file names. 7233 * @author Nicola Asuni 7234 * @protected 7235 * @since 4.3.007 (2008-12-04) 7236 * @see Image() 7237 */ 7238 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') { 7239 // create temp images 7240 if (empty($filehash)) { 7241 $filehash = md5($file); 7242 } 7243 // create temp image file (without alpha channel) 7244 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash; 7245 // create temp alpha file 7246 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash; 7247 $parsed = false; 7248 $parse_error = ''; 7249 // ImageMagick extension 7250 if (($parsed === false) AND extension_loaded('imagick')) { 7251 try { 7252 // ImageMagick library 7253 $img = new Imagick(); 7254 $img->readImage($file); 7255 // clone image object 7256 $imga = TCPDF_STATIC::objclone($img); 7257 // extract alpha channel 7258 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) { 7259 $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT); 7260 } else { 7261 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE); 7262 $img->negateImage(true); 7263 } 7264 $img->setImageFormat('png'); 7265 $img->writeImage($tempfile_alpha); 7266 // remove alpha channel 7267 if (method_exists($imga, 'setImageMatte')) { 7268 $imga->setImageMatte(false); 7269 } else { 7270 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE)); 7271 } 7272 $imga->setImageFormat('png'); 7273 $imga->writeImage($tempfile_plain); 7274 $parsed = true; 7275 } catch (Exception $e) { 7276 // Imagemagick fails, try with GD 7277 $parse_error = 'Imagick library error: '.$e->getMessage(); 7278 } 7279 } 7280 // GD extension 7281 if (($parsed === false) AND function_exists('imagecreatefrompng')) { 7282 try { 7283 // generate images 7284 $img = imagecreatefrompng($file); 7285 $imgalpha = imagecreate($wpx, $hpx); 7286 // generate gray scale palette (0 -> 255) 7287 for ($c = 0; $c < 256; ++$c) { 7288 ImageColorAllocate($imgalpha, $c, $c, $c); 7289 } 7290 // extract alpha channel 7291 for ($xpx = 0; $xpx < $wpx; ++$xpx) { 7292 for ($ypx = 0; $ypx < $hpx; ++$ypx) { 7293 $color = imagecolorat($img, $xpx, $ypx); 7294 // get and correct gamma color 7295 $alpha = $this->getGDgamma($img, $color); 7296 imagesetpixel($imgalpha, $xpx, $ypx, $alpha); 7297 } 7298 } 7299 imagepng($imgalpha, $tempfile_alpha); 7300 imagedestroy($imgalpha); 7301 // extract image without alpha channel 7302 $imgplain = imagecreatetruecolor($wpx, $hpx); 7303 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx); 7304 imagepng($imgplain, $tempfile_plain); 7305 imagedestroy($imgplain); 7306 $parsed = true; 7307 } catch (Exception $e) { 7308 // GD fails 7309 $parse_error = 'GD library error: '.$e->getMessage(); 7310 } 7311 } 7312 if ($parsed === false) { 7313 if (empty($parse_error)) { 7314 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.'); 7315 } else { 7316 $this->Error($parse_error); 7317 } 7318 } 7319 // embed mask image 7320 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 7321 // embed image, masked with previously embedded mask 7322 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 7323 } 7324 7325 /** 7326 * Get the GD-corrected PNG gamma value from alpha color 7327 * @param $img (int) GD image Resource ID. 7328 * @param $c (int) alpha color 7329 * @protected 7330 * @since 4.3.007 (2008-12-04) 7331 */ 7332 protected function getGDgamma($img, $c) { 7333 if (!isset($this->gdgammacache['#'.$c])) { 7334 $colors = imagecolorsforindex($img, $c); 7335 // GD alpha is only 7 bit (0 -> 127) 7336 $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255); 7337 // correct gamma 7338 $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255); 7339 // store the latest values on cache to improve performances 7340 if (count($this->gdgammacache) > 8) { 7341 // remove one element from the cache array 7342 array_shift($this->gdgammacache); 7343 } 7344 } 7345 return $this->gdgammacache['#'.$c]; 7346 } 7347 7348 /** 7349 * Performs a line break. 7350 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. 7351 * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell. 7352 * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate 7353 * @public 7354 * @since 1.0 7355 * @see Cell() 7356 */ 7357 public function Ln($h='', $cell=false) { 7358 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'])) { 7359 // revove vertical space from the top of the column 7360 return; 7361 } 7362 if ($cell) { 7363 if ($this->rtl) { 7364 $cellpadding = $this->cell_padding['R']; 7365 } else { 7366 $cellpadding = $this->cell_padding['L']; 7367 } 7368 } else { 7369 $cellpadding = 0; 7370 } 7371 if ($this->rtl) { 7372 $this->x = $this->w - $this->rMargin - $cellpadding; 7373 } else { 7374 $this->x = $this->lMargin + $cellpadding; 7375 } 7376 if (is_string($h)) { 7377 $h = $this->lasth; 7378 } 7379 $this->y += $h; 7380 $this->newline = true; 7381 } 7382 7383 /** 7384 * Returns the relative X value of current position. 7385 * The value is relative to the left border for LTR languages and to the right border for RTL languages. 7386 * @return float 7387 * @public 7388 * @since 1.2 7389 * @see SetX(), GetY(), SetY() 7390 */ 7391 public function GetX() { 7392 //Get x position 7393 if ($this->rtl) { 7394 return ($this->w - $this->x); 7395 } else { 7396 return $this->x; 7397 } 7398 } 7399 7400 /** 7401 * Returns the absolute X value of current position. 7402 * @return float 7403 * @public 7404 * @since 1.2 7405 * @see SetX(), GetY(), SetY() 7406 */ 7407 public function GetAbsX() { 7408 return $this->x; 7409 } 7410 7411 /** 7412 * Returns the ordinate of the current position. 7413 * @return float 7414 * @public 7415 * @since 1.0 7416 * @see SetY(), GetX(), SetX() 7417 */ 7418 public function GetY() { 7419 return $this->y; 7420 } 7421 7422 /** 7423 * Defines the abscissa of the current position. 7424 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). 7425 * @param $x (float) The value of the abscissa in user units. 7426 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. 7427 * @public 7428 * @since 1.2 7429 * @see GetX(), GetY(), SetY(), SetXY() 7430 */ 7431 public function SetX($x, $rtloff=false) { 7432 $x = floatval($x); 7433 if (!$rtloff AND $this->rtl) { 7434 if ($x >= 0) { 7435 $this->x = $this->w - $x; 7436 } else { 7437 $this->x = abs($x); 7438 } 7439 } else { 7440 if ($x >= 0) { 7441 $this->x = $x; 7442 } else { 7443 $this->x = $this->w + $x; 7444 } 7445 } 7446 if ($this->x < 0) { 7447 $this->x = 0; 7448 } 7449 if ($this->x > $this->w) { 7450 $this->x = $this->w; 7451 } 7452 } 7453 7454 /** 7455 * Moves the current abscissa back to the left margin and sets the ordinate. 7456 * If the passed value is negative, it is relative to the bottom of the page. 7457 * @param $y (float) The value of the ordinate in user units. 7458 * @param $resetx (bool) if true (default) reset the X position. 7459 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. 7460 * @public 7461 * @since 1.0 7462 * @see GetX(), GetY(), SetY(), SetXY() 7463 */ 7464 public function SetY($y, $resetx=true, $rtloff=false) { 7465 $y = floatval($y); 7466 if ($resetx) { 7467 //reset x 7468 if (!$rtloff AND $this->rtl) { 7469 $this->x = $this->w - $this->rMargin; 7470 } else { 7471 $this->x = $this->lMargin; 7472 } 7473 } 7474 if ($y >= 0) { 7475 $this->y = $y; 7476 } else { 7477 $this->y = $this->h + $y; 7478 } 7479 if ($this->y < 0) { 7480 $this->y = 0; 7481 } 7482 if ($this->y > $this->h) { 7483 $this->y = $this->h; 7484 } 7485 } 7486 7487 /** 7488 * Defines the abscissa and ordinate of the current position. 7489 * If the passed values are negative, they are relative respectively to the right and bottom of the page. 7490 * @param $x (float) The value of the abscissa. 7491 * @param $y (float) The value of the ordinate. 7492 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. 7493 * @public 7494 * @since 1.2 7495 * @see SetX(), SetY() 7496 */ 7497 public function SetXY($x, $y, $rtloff=false) { 7498 $this->SetY($y, false, $rtloff); 7499 $this->SetX($x, $rtloff); 7500 } 7501 7502 /** 7503 * Set the absolute X coordinate of the current pointer. 7504 * @param $x (float) The value of the abscissa in user units. 7505 * @public 7506 * @since 5.9.186 (2012-09-13) 7507 * @see setAbsX(), setAbsY(), SetAbsXY() 7508 */ 7509 public function SetAbsX($x) { 7510 $this->x = floatval($x); 7511 } 7512 7513 /** 7514 * Set the absolute Y coordinate of the current pointer. 7515 * @param $y (float) (float) The value of the ordinate in user units. 7516 * @public 7517 * @since 5.9.186 (2012-09-13) 7518 * @see setAbsX(), setAbsY(), SetAbsXY() 7519 */ 7520 public function SetAbsY($y) { 7521 $this->y = floatval($y); 7522 } 7523 7524 /** 7525 * Set the absolute X and Y coordinates of the current pointer. 7526 * @param $x (float) The value of the abscissa in user units. 7527 * @param $y (float) (float) The value of the ordinate in user units. 7528 * @public 7529 * @since 5.9.186 (2012-09-13) 7530 * @see setAbsX(), setAbsY(), SetAbsXY() 7531 */ 7532 public function SetAbsXY($x, $y) { 7533 $this->SetAbsX($x); 7534 $this->SetAbsY($y); 7535 } 7536 7537 /** 7538 * Send the document to a given destination: string, local file or browser. 7539 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> 7540 * The method first calls Close() if necessary to terminate the document. 7541 * @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. 7542 * @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> 7543 * @return string 7544 * @public 7545 * @since 1.0 7546 * @see Close() 7547 */ 7548 public function Output($name='doc.pdf', $dest='I') { 7549 //Output PDF to some destination 7550 //Finish document if necessary 7551 if ($this->state < 3) { 7552 $this->Close(); 7553 } 7554 //Normalize parameters 7555 if (is_bool($dest)) { 7556 $dest = $dest ? 'D' : 'F'; 7557 } 7558 $dest = strtoupper($dest); 7559 if ($dest[0] != 'F') { 7560 $name = preg_replace('/[\s]+/', '_', $name); 7561 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name); 7562 } 7563 if ($this->sign) { 7564 // *** apply digital signature to the document *** 7565 // get the document content 7566 $pdfdoc = $this->getBuffer(); 7567 // remove last newline 7568 $pdfdoc = substr($pdfdoc, 0, -1); 7569 // remove filler space 7570 $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string); 7571 // define the ByteRange 7572 $byte_range = array(); 7573 $byte_range[0] = 0; 7574 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10; 7575 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2; 7576 $byte_range[3] = strlen($pdfdoc) - $byte_range[2]; 7577 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]); 7578 // replace the ByteRange 7579 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]); 7580 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange))); 7581 $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc); 7582 // write the document to a temporary folder 7583 $tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id); 7584 $f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb'); 7585 if (!$f) { 7586 $this->Error('Unable to create temporary file: '.$tempdoc); 7587 } 7588 $pdfdoc_length = strlen($pdfdoc); 7589 fwrite($f, $pdfdoc, $pdfdoc_length); 7590 fclose($f); 7591 // get digital signature via openssl library 7592 $tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id); 7593 if (empty($this->signature_data['extracerts'])) { 7594 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED); 7595 } else { 7596 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']); 7597 } 7598 // read signature 7599 $signature = file_get_contents($tempsign); 7600 // extract signature 7601 $signature = substr($signature, $pdfdoc_length); 7602 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); 7603 $tmparr = explode("\n\n", $signature); 7604 $signature = $tmparr[1]; 7605 // decode signature 7606 $signature = base64_decode(trim($signature)); 7607 // add TSA timestamp to signature 7608 $signature = $this->applyTSA($signature); 7609 // convert signature to hex 7610 $signature = current(unpack('H*', $signature)); 7611 $signature = str_pad($signature, $this->signature_max_length, '0'); 7612 // Add signature to the document 7613 $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]); 7614 $this->bufferlen = strlen($this->buffer); 7615 } 7616 switch($dest) { 7617 case 'I': { 7618 // Send PDF to the standard output 7619 if (ob_get_contents()) { 7620 $this->Error('Some data has already been output, can\'t send PDF file'); 7621 } 7622 if (php_sapi_name() != 'cli') { 7623 // send output to a browser 7624 header('Content-Type: application/pdf'); 7625 if (headers_sent()) { 7626 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7627 } 7628 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7629 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7630 header('Pragma: public'); 7631 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7632 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7633 header('Content-Disposition: inline; filename="'.basename($name).'"'); 7634 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen); 7635 } else { 7636 echo $this->getBuffer(); 7637 } 7638 break; 7639 } 7640 case 'D': { 7641 // download PDF as file 7642 if (ob_get_contents()) { 7643 $this->Error('Some data has already been output, can\'t send PDF file'); 7644 } 7645 header('Content-Description: File Transfer'); 7646 if (headers_sent()) { 7647 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7648 } 7649 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7650 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7651 header('Pragma: public'); 7652 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7653 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7654 // force download dialog 7655 if (strpos(php_sapi_name(), 'cgi') === false) { 7656 header('Content-Type: application/force-download'); 7657 header('Content-Type: application/octet-stream', false); 7658 header('Content-Type: application/download', false); 7659 header('Content-Type: application/pdf', false); 7660 } else { 7661 header('Content-Type: application/pdf'); 7662 } 7663 // use the Content-Disposition header to supply a recommended filename 7664 header('Content-Disposition: attachment; filename="'.basename($name).'"'); 7665 header('Content-Transfer-Encoding: binary'); 7666 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen); 7667 break; 7668 } 7669 case 'F': 7670 case 'FI': 7671 case 'FD': { 7672 // save PDF to a local file 7673 $f = TCPDF_STATIC::fopenLocal($name, 'wb'); 7674 if (!$f) { 7675 $this->Error('Unable to create output file: '.$name); 7676 } 7677 fwrite($f, $this->getBuffer(), $this->bufferlen); 7678 fclose($f); 7679 if ($dest == 'FI') { 7680 // send headers to browser 7681 header('Content-Type: application/pdf'); 7682 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7683 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7684 header('Pragma: public'); 7685 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7686 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7687 header('Content-Disposition: inline; filename="'.basename($name).'"'); 7688 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name)); 7689 } elseif ($dest == 'FD') { 7690 // send headers to browser 7691 if (ob_get_contents()) { 7692 $this->Error('Some data has already been output, can\'t send PDF file'); 7693 } 7694 header('Content-Description: File Transfer'); 7695 if (headers_sent()) { 7696 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7697 } 7698 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=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(file_get_contents($name), filesize($name)); 7715 } 7716 break; 7717 } 7718 case 'E': { 7719 // return PDF as base64 mime multi-part email attachment (RFC 2045) 7720 $retval = 'Content-Type: application/pdf;'."\r\n"; 7721 $retval .= ' name="'.$name.'"'."\r\n"; 7722 $retval .= 'Content-Transfer-Encoding: base64'."\r\n"; 7723 $retval .= 'Content-Disposition: attachment;'."\r\n"; 7724 $retval .= ' filename="'.$name.'"'."\r\n\r\n"; 7725 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n"); 7726 return $retval; 7727 } 7728 case 'S': { 7729 // returns PDF as a string 7730 return $this->getBuffer(); 7731 } 7732 default: { 7733 $this->Error('Incorrect output destination: '.$dest); 7734 } 7735 } 7736 return ''; 7737 } 7738 7739 /** 7740 * Unset all class variables except the following critical variables. 7741 * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables. 7742 * @param $preserve_objcopy (boolean) if true preserves the objcopy variable 7743 * @public 7744 * @since 4.5.016 (2009-02-24) 7745 */ 7746 public function _destroy($destroyall=false, $preserve_objcopy=false) { 7747 if ($destroyall AND !$preserve_objcopy) { 7748 // remove all temporary files 7749 $tmpfiles = glob(K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_*'); 7750 if (!empty($tmpfiles)) { 7751 array_map('unlink', $tmpfiles); 7752 } 7753 } 7754 $preserve = array( 7755 'file_id', 7756 'internal_encoding', 7757 'state', 7758 'bufferlen', 7759 'buffer', 7760 'cached_files', 7761 'sign', 7762 'signature_data', 7763 'signature_max_length', 7764 'byterange_string', 7765 'tsa_timestamp', 7766 'tsa_data' 7767 ); 7768 foreach (array_keys(get_object_vars($this)) as $val) { 7769 if ($destroyall OR !in_array($val, $preserve)) { 7770 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND ($val != 'file_id') AND isset($this->$val)) { 7771 unset($this->$val); 7772 } 7773 } 7774 } 7775 } 7776 7777 /** 7778 * Check for locale-related bug 7779 * @protected 7780 */ 7781 protected function _dochecks() { 7782 //Check for locale-related bug 7783 if (1.1 == 1) { 7784 $this->Error('Don\'t alter the locale before including class file'); 7785 } 7786 //Check for decimal separator 7787 if (sprintf('%.1F', 1.0) != '1.0') { 7788 setlocale(LC_NUMERIC, 'C'); 7789 } 7790 } 7791 7792 /** 7793 * Return an array containing variations for the basic page number alias. 7794 * @param $a (string) Base alias. 7795 * @return array of page number aliases 7796 * @protected 7797 */ 7798 protected function getInternalPageNumberAliases($a= '') { 7799 $alias = array(); 7800 // build array of Unicode + ASCII variants (the order is important) 7801 $alias = array('u' => array(), 'a' => array()); 7802 $u = '{'.$a.'}'; 7803 $alias['u'][] = TCPDF_STATIC::_escape($u); 7804 if ($this->isunicode) { 7805 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont)); 7806 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont)); 7807 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont)); 7808 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont)); 7809 } 7810 $alias['a'][] = TCPDF_STATIC::_escape($a); 7811 return $alias; 7812 } 7813 7814 /** 7815 * Return an array containing all internal page aliases. 7816 * @return array of page number aliases 7817 * @protected 7818 */ 7819 protected function getAllInternalPageNumberAliases() { 7820 $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); 7821 $pnalias = array(); 7822 foreach($basic_alias as $k => $a) { 7823 $pnalias[$k] = $this->getInternalPageNumberAliases($a); 7824 } 7825 return $pnalias; 7826 } 7827 7828 /** 7829 * Replace right shift page number aliases with spaces to correct right alignment. 7830 * This works perfectly only when using monospaced fonts. 7831 * @param $page (string) Page content. 7832 * @param $aliases (array) Array of page aliases. 7833 * @param $diff (int) initial difference to add. 7834 * @return replaced page content. 7835 * @protected 7836 */ 7837 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) { 7838 foreach ($aliases as $type => $alias) { 7839 foreach ($alias as $a) { 7840 // find position of compensation factor 7841 $startnum = (strpos($a, ':') + 1); 7842 $a = substr($a, 0, $startnum); 7843 if (($pos = strpos($page, $a)) !== false) { 7844 // end of alias 7845 $endnum = strpos($page, '}', $pos); 7846 // string to be replaced 7847 $aa = substr($page, $pos, ($endnum - $pos + 1)); 7848 // get compensation factor 7849 $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum)); 7850 $ratio = preg_replace('/[^0-9\.]/', '', $ratio); 7851 $ratio = floatval($ratio); 7852 if ($type == 'u') { 7853 $chrdiff = floor(($diff + 12) * $ratio); 7854 $shift = str_repeat(' ', $chrdiff); 7855 $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont); 7856 } else { 7857 $chrdiff = floor(($diff + 11) * $ratio); 7858 $shift = str_repeat(' ', $chrdiff); 7859 } 7860 $page = str_replace($aa, $shift, $page); 7861 } 7862 } 7863 } 7864 return $page; 7865 } 7866 7867 /** 7868 * Set page boxes to be included on page descriptions. 7869 * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'). 7870 * @protected 7871 */ 7872 protected function setPageBoxTypes($boxes) { 7873 $this->page_boxes = array(); 7874 foreach ($boxes as $box) { 7875 if (in_array($box, TCPDF_STATIC::$pageboxes)) { 7876 $this->page_boxes[] = $box; 7877 } 7878 } 7879 } 7880 7881 /** 7882 * Output pages (and replace page number aliases). 7883 * @protected 7884 */ 7885 protected function _putpages() { 7886 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 7887 // get internal aliases for page numbers 7888 $pnalias = $this->getAllInternalPageNumberAliases(); 7889 $num_pages = $this->numpages; 7890 $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1)); 7891 $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont); 7892 $ptp_num_chars = $this->GetNumChars($ptpa); 7893 $pagegroupnum = 0; 7894 $groupnum = 0; 7895 $ptgu = 1; 7896 $ptga = 1; 7897 $ptg_num_chars = 1; 7898 for ($n = 1; $n <= $num_pages; ++$n) { 7899 // get current page 7900 $temppage = $this->getPageBuffer($n); 7901 $pagelen = strlen($temppage); 7902 // set replacements for total pages number 7903 $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1)); 7904 $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont); 7905 $pnp_num_chars = $this->GetNumChars($pnpa); 7906 $pdiff = 0; // difference used for right shift alignment of page numbers 7907 $gdiff = 0; // difference used for right shift alignment of page group numbers 7908 if (!empty($this->pagegroups)) { 7909 if (isset($this->newpagegroup[$n])) { 7910 $pagegroupnum = 0; 7911 ++$groupnum; 7912 $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]); 7913 $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont); 7914 $ptg_num_chars = $this->GetNumChars($ptga); 7915 } 7916 ++$pagegroupnum; 7917 $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum); 7918 $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont); 7919 $png_num_chars = $this->GetNumChars($pnga); 7920 // replace page numbers 7921 $replace = array(); 7922 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']); 7923 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']); 7924 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']); 7925 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']); 7926 list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff); 7927 } 7928 // replace page numbers 7929 $replace = array(); 7930 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']); 7931 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']); 7932 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']); 7933 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']); 7934 list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff); 7935 // replace right shift alias 7936 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff)); 7937 // replace EPS marker 7938 $temppage = str_replace($this->epsmarker, '', $temppage); 7939 //Page 7940 $this->page_obj_id[$n] = $this->_newobj(); 7941 $out = '<<'; 7942 $out .= ' /Type /Page'; 7943 $out .= ' /Parent 1 0 R'; 7944 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 7945 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp); 7946 } 7947 $out .= ' /Resources 2 0 R'; 7948 foreach ($this->page_boxes as $box) { 7949 $out .= ' /'.$box; 7950 $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']); 7951 } 7952 if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) { 7953 $out .= ' /BoxColorInfo <<'; 7954 foreach ($this->page_boxes as $box) { 7955 if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) { 7956 $out .= ' /'.$box.' <<'; 7957 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) { 7958 $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C']; 7959 $out .= ' /C ['; 7960 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); 7961 $out .= ' ]'; 7962 } 7963 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) { 7964 $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k); 7965 } 7966 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) { 7967 $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S']; 7968 } 7969 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) { 7970 $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D']; 7971 $out .= ' /D ['; 7972 foreach ($dashes as $dash) { 7973 $out .= sprintf(' %F', ($dash * $this->k)); 7974 } 7975 $out .= ' ]'; 7976 } 7977 $out .= ' >>'; 7978 } 7979 } 7980 $out .= ' >>'; 7981 } 7982 $out .= ' /Contents '.($this->n + 1).' 0 R'; 7983 $out .= ' /Rotate '.$this->pagedim[$n]['Rotate']; 7984 if (!$this->pdfa_mode) { 7985 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>'; 7986 } 7987 if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) { 7988 // page transitions 7989 if (isset($this->pagedim[$n]['trans']['Dur'])) { 7990 $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur']; 7991 } 7992 $out .= ' /Trans <<'; 7993 $out .= ' /Type /Trans'; 7994 if (isset($this->pagedim[$n]['trans']['S'])) { 7995 $out .= ' /S /'.$this->pagedim[$n]['trans']['S']; 7996 } 7997 if (isset($this->pagedim[$n]['trans']['D'])) { 7998 $out .= ' /D '.$this->pagedim[$n]['trans']['D']; 7999 } 8000 if (isset($this->pagedim[$n]['trans']['Dm'])) { 8001 $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm']; 8002 } 8003 if (isset($this->pagedim[$n]['trans']['M'])) { 8004 $out .= ' /M /'.$this->pagedim[$n]['trans']['M']; 8005 } 8006 if (isset($this->pagedim[$n]['trans']['Di'])) { 8007 $out .= ' /Di '.$this->pagedim[$n]['trans']['Di']; 8008 } 8009 if (isset($this->pagedim[$n]['trans']['SS'])) { 8010 $out .= ' /SS '.$this->pagedim[$n]['trans']['SS']; 8011 } 8012 if (isset($this->pagedim[$n]['trans']['B'])) { 8013 $out .= ' /B '.$this->pagedim[$n]['trans']['B']; 8014 } 8015 $out .= ' >>'; 8016 } 8017 $out .= $this->_getannotsrefs($n); 8018 $out .= ' /PZ '.$this->pagedim[$n]['PZ']; 8019 $out .= ' >>'; 8020 $out .= "\n".'endobj'; 8021 $this->_out($out); 8022 //Page content 8023 $p = ($this->compress) ? gzcompress($temppage) : $temppage; 8024 $this->_newobj(); 8025 $p = $this->_getrawstream($p); 8026 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj'); 8027 } 8028 //Pages root 8029 $out = $this->_getobj(1)."\n"; 8030 $out .= '<< /Type /Pages /Kids ['; 8031 foreach($this->page_obj_id as $page_obj) { 8032 $out .= ' '.$page_obj.' 0 R'; 8033 } 8034 $out .= ' ] /Count '.$num_pages.' >>'; 8035 $out .= "\n".'endobj'; 8036 $this->_out($out); 8037 } 8038 8039 /** 8040 * Get references to page annotations. 8041 * @param $n (int) page number 8042 * @return string 8043 * @protected 8044 * @author Nicola Asuni 8045 * @since 5.0.010 (2010-05-17) 8046 */ 8047 protected function _getannotsrefs($n) { 8048 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) { 8049 return ''; 8050 } 8051 $out = ' /Annots ['; 8052 if (isset($this->PageAnnots[$n])) { 8053 foreach ($this->PageAnnots[$n] as $key => $val) { 8054 if (!in_array($val['n'], $this->radio_groups)) { 8055 $out .= ' '.$val['n'].' 0 R'; 8056 } 8057 } 8058 // add radiobutton groups 8059 if (isset($this->radiobutton_groups[$n])) { 8060 foreach ($this->radiobutton_groups[$n] as $key => $data) { 8061 if (isset($data['n'])) { 8062 $out .= ' '.$data['n'].' 0 R'; 8063 } 8064 } 8065 } 8066 } 8067 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) { 8068 // set reference for signature object 8069 $out .= ' '.$this->sig_obj_id.' 0 R'; 8070 } 8071 if (!empty($this->empty_signature_appearance)) { 8072 foreach ($this->empty_signature_appearance as $esa) { 8073 if ($esa['page'] == $n) { 8074 // set reference for empty signature objects 8075 $out .= ' '.$esa['objid'].' 0 R'; 8076 } 8077 } 8078 } 8079 $out .= ' ]'; 8080 return $out; 8081 } 8082 8083 /** 8084 * Output annotations objects for all pages. 8085 * !!! THIS METHOD IS NOT YET COMPLETED !!! 8086 * See section 12.5 of PDF 32000_2008 reference. 8087 * @protected 8088 * @author Nicola Asuni 8089 * @since 4.0.018 (2008-08-06) 8090 */ 8091 protected function _putannotsobjs() { 8092 // reset object counter 8093 for ($n=1; $n <= $this->numpages; ++$n) { 8094 if (isset($this->PageAnnots[$n])) { 8095 // set page annotations 8096 foreach ($this->PageAnnots[$n] as $key => $pl) { 8097 $annot_obj_id = $this->PageAnnots[$n][$key]['n']; 8098 // create annotation object for grouping radiobuttons 8099 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) { 8100 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n']; 8101 $annots = '<<'; 8102 $annots .= ' /Type /Annot'; 8103 $annots .= ' /Subtype /Widget'; 8104 $annots .= ' /Rect [0 0 0 0]'; 8105 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) { 8106 // read only 8107 $annots .= ' /F 68'; 8108 $annots .= ' /Ff 49153'; 8109 } else { 8110 $annots .= ' /F 4'; // default print for PDF/A 8111 $annots .= ' /Ff 49152'; 8112 } 8113 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id); 8114 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { 8115 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id); 8116 } 8117 $annots .= ' /FT /Btn'; 8118 $annots .= ' /Kids ['; 8119 $defval = ''; 8120 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) { 8121 if (isset($data['kid'])) { 8122 $annots .= ' '.$data['kid'].' 0 R'; 8123 if ($data['def'] !== 'Off') { 8124 $defval = $data['def']; 8125 } 8126 } 8127 } 8128 $annots .= ' ]'; 8129 if (!empty($defval)) { 8130 $annots .= ' /V /'.$defval; 8131 } 8132 $annots .= ' >>'; 8133 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj'); 8134 $this->form_obj_id[] = $radio_button_obj_id; 8135 // store object id to be used on Parent entry of Kids 8136 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id; 8137 } 8138 $formfield = false; 8139 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER); 8140 $a = $pl['x'] * $this->k; 8141 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k); 8142 $c = $pl['w'] * $this->k; 8143 $d = $pl['h'] * $this->k; 8144 $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d); 8145 // create new annotation object 8146 $annots = '<</Type /Annot'; 8147 $annots .= ' /Subtype /'.$pl['opt']['subtype']; 8148 $annots .= ' /Rect ['.$rect.']'; 8149 $ft = array('Btn', 'Tx', 'Ch', 'Sig'); 8150 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) { 8151 $annots .= ' /FT /'.$pl['opt']['ft']; 8152 $formfield = true; 8153 } 8154 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id); 8155 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R'; 8156 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id); 8157 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp); 8158 if (isset($pl['opt']['f'])) { 8159 $fval = 0; 8160 if (is_array($pl['opt']['f'])) { 8161 foreach ($pl['opt']['f'] as $f) { 8162 switch (strtolower($f)) { 8163 case 'invisible': { 8164 $fval += 1 << 0; 8165 break; 8166 } 8167 case 'hidden': { 8168 $fval += 1 << 1; 8169 break; 8170 } 8171 case 'print': { 8172 $fval += 1 << 2; 8173 break; 8174 } 8175 case 'nozoom': { 8176 $fval += 1 << 3; 8177 break; 8178 } 8179 case 'norotate': { 8180 $fval += 1 << 4; 8181 break; 8182 } 8183 case 'noview': { 8184 $fval += 1 << 5; 8185 break; 8186 } 8187 case 'readonly': { 8188 $fval += 1 << 6; 8189 break; 8190 } 8191 case 'locked': { 8192 $fval += 1 << 8; 8193 break; 8194 } 8195 case 'togglenoview': { 8196 $fval += 1 << 9; 8197 break; 8198 } 8199 case 'lockedcontents': { 8200 $fval += 1 << 10; 8201 break; 8202 } 8203 default: { 8204 break; 8205 } 8206 } 8207 } 8208 } else { 8209 $fval = intval($pl['opt']['f']); 8210 } 8211 } else { 8212 $fval = 4; 8213 } 8214 if ($this->pdfa_mode) { 8215 // force print flag for PDF/A mode 8216 $fval |= 4; 8217 } 8218 $annots .= ' /F '.intval($fval); 8219 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) { 8220 $annots .= ' /AS /'.$pl['opt']['as']; 8221 } 8222 if (isset($pl['opt']['ap'])) { 8223 // appearance stream 8224 $annots .= ' /AP <<'; 8225 if (is_array($pl['opt']['ap'])) { 8226 foreach ($pl['opt']['ap'] as $apmode => $apdef) { 8227 // $apmode can be: n = normal; r = rollover; d = down; 8228 $annots .= ' /'.strtoupper($apmode); 8229 if (is_array($apdef)) { 8230 $annots .= ' <<'; 8231 foreach ($apdef as $apstate => $stream) { 8232 // reference to XObject that define the appearance for this mode-state 8233 $apsobjid = $this->_putAPXObject($c, $d, $stream); 8234 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R'; 8235 } 8236 $annots .= ' >>'; 8237 } else { 8238 // reference to XObject that define the appearance for this mode 8239 $apsobjid = $this->_putAPXObject($c, $d, $apdef); 8240 $annots .= ' '.$apsobjid.' 0 R'; 8241 } 8242 } 8243 } else { 8244 $annots .= $pl['opt']['ap']; 8245 } 8246 $annots .= ' >>'; 8247 } 8248 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) { 8249 $annots .= ' /BS <<'; 8250 $annots .= ' /Type /Border'; 8251 if (isset($pl['opt']['bs']['w'])) { 8252 $annots .= ' /W '.intval($pl['opt']['bs']['w']); 8253 } 8254 $bstyles = array('S', 'D', 'B', 'I', 'U'); 8255 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) { 8256 $annots .= ' /S /'.$pl['opt']['bs']['s']; 8257 } 8258 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) { 8259 $annots .= ' /D ['; 8260 foreach ($pl['opt']['bs']['d'] as $cord) { 8261 $annots .= ' '.intval($cord); 8262 } 8263 $annots .= ']'; 8264 } 8265 $annots .= ' >>'; 8266 } else { 8267 $annots .= ' /Border ['; 8268 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) { 8269 $annots .= intval($pl['opt']['border'][0]).' '; 8270 $annots .= intval($pl['opt']['border'][1]).' '; 8271 $annots .= intval($pl['opt']['border'][2]); 8272 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) { 8273 $annots .= ' ['; 8274 foreach ($pl['opt']['border'][3] as $dash) { 8275 $annots .= intval($dash).' '; 8276 } 8277 $annots .= ']'; 8278 } 8279 } else { 8280 $annots .= '0 0 0'; 8281 } 8282 $annots .= ']'; 8283 } 8284 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) { 8285 $annots .= ' /BE <<'; 8286 $bstyles = array('S', 'C'); 8287 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) { 8288 $annots .= ' /S /'.$pl['opt']['bs']['s']; 8289 } else { 8290 $annots .= ' /S /S'; 8291 } 8292 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) { 8293 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']); 8294 } 8295 $annots .= '>>'; 8296 } 8297 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) { 8298 $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']); 8299 } 8300 //$annots .= ' /StructParent '; 8301 //$annots .= ' /OC '; 8302 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound'); 8303 if (in_array(strtolower($pl['opt']['subtype']), $markups)) { 8304 // this is a markup type 8305 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 8306 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id); 8307 } 8308 //$annots .= ' /Popup '; 8309 if (isset($pl['opt']['ca'])) { 8310 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca'])); 8311 } 8312 if (isset($pl['opt']['rc'])) { 8313 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); 8314 } 8315 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp); 8316 //$annots .= ' /IRT '; 8317 if (isset($pl['opt']['subj'])) { 8318 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id); 8319 } 8320 //$annots .= ' /RT '; 8321 //$annots .= ' /IT '; 8322 //$annots .= ' /ExData '; 8323 } 8324 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash'); 8325 // Annotation types 8326 switch (strtolower($pl['opt']['subtype'])) { 8327 case 'text': { 8328 if (isset($pl['opt']['open'])) { 8329 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false'); 8330 } 8331 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph'); 8332 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8333 $annots .= ' /Name /'.$pl['opt']['name']; 8334 } else { 8335 $annots .= ' /Name /Note'; 8336 } 8337 $statemodels = array('Marked', 'Review'); 8338 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) { 8339 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 8340 } else { 8341 $pl['opt']['statemodel'] = 'Marked'; 8342 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 8343 } 8344 if ($pl['opt']['statemodel'] == 'Marked') { 8345 $states = array('Accepted', 'Unmarked'); 8346 } else { 8347 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None'); 8348 } 8349 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) { 8350 $annots .= ' /State /'.$pl['opt']['state']; 8351 } else { 8352 if ($pl['opt']['statemodel'] == 'Marked') { 8353 $annots .= ' /State /Unmarked'; 8354 } else { 8355 $annots .= ' /State /None'; 8356 } 8357 } 8358 break; 8359 } 8360 case 'link': { 8361 if (is_string($pl['txt'])) { 8362 if ($pl['txt'][0] == '#') { 8363 // internal destination 8364 $annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1)); 8365 } elseif ($pl['txt'][0] == '%') { 8366 // embedded PDF file 8367 $filename = basename(substr($pl['txt'], 1)); 8368 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; 8369 } elseif ($pl['txt'][0] == '*') { 8370 // embedded generic file 8371 $filename = basename(substr($pl['txt'], 1)); 8372 $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});'; 8373 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>'; 8374 } else { 8375 $parsedUrl = parse_url($pl['txt']); 8376 if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) { 8377 // relative link to a PDF file 8378 $dest = '[0 /Fit]'; // default page 0 8379 if (!empty($parsedUrl['fragment'])) { 8380 // check for named destination 8381 $tmp = explode('=', $parsedUrl['fragment']); 8382 $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')'; 8383 } 8384 $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>'; 8385 } else { 8386 // external URI link 8387 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>'; 8388 } 8389 } 8390 } elseif (isset($this->links[$pl['txt']])) { 8391 // internal link ID 8392 $l = $this->links[$pl['txt']]; 8393 if (isset($this->page_obj_id[($l['p'])])) { 8394 $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))); 8395 } 8396 } 8397 $hmodes = array('N', 'I', 'O', 'P'); 8398 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) { 8399 $annots .= ' /H /'.$pl['opt']['h']; 8400 } else { 8401 $annots .= ' /H /I'; 8402 } 8403 //$annots .= ' /PA '; 8404 //$annots .= ' /Quadpoints '; 8405 break; 8406 } 8407 case 'freetext': { 8408 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 8409 $annots .= ' /DA ('.$pl['opt']['da'].')'; 8410 } 8411 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 8412 $annots .= ' /Q '.intval($pl['opt']['q']); 8413 } 8414 if (isset($pl['opt']['rc'])) { 8415 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); 8416 } 8417 if (isset($pl['opt']['ds'])) { 8418 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id); 8419 } 8420 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) { 8421 $annots .= ' /CL ['; 8422 foreach ($pl['opt']['cl'] as $cl) { 8423 $annots .= sprintf('%F ', $cl * $this->k); 8424 } 8425 $annots .= ']'; 8426 } 8427 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter'); 8428 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) { 8429 $annots .= ' /IT /'.$pl['opt']['it']; 8430 } 8431 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) { 8432 $l = $pl['opt']['rd'][0] * $this->k; 8433 $r = $pl['opt']['rd'][1] * $this->k; 8434 $t = $pl['opt']['rd'][2] * $this->k; 8435 $b = $pl['opt']['rd'][3] * $this->k; 8436 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']'; 8437 } 8438 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) { 8439 $annots .= ' /LE /'.$pl['opt']['le']; 8440 } 8441 break; 8442 } 8443 case 'line': { 8444 break; 8445 } 8446 case 'square': { 8447 break; 8448 } 8449 case 'circle': { 8450 break; 8451 } 8452 case 'polygon': { 8453 break; 8454 } 8455 case 'polyline': { 8456 break; 8457 } 8458 case 'highlight': { 8459 break; 8460 } 8461 case 'underline': { 8462 break; 8463 } 8464 case 'squiggly': { 8465 break; 8466 } 8467 case 'strikeout': { 8468 break; 8469 } 8470 case 'stamp': { 8471 break; 8472 } 8473 case 'caret': { 8474 break; 8475 } 8476 case 'ink': { 8477 break; 8478 } 8479 case 'popup': { 8480 break; 8481 } 8482 case 'fileattachment': { 8483 if ($this->pdfa_mode) { 8484 // embedded files are not allowed in PDF/A mode 8485 break; 8486 } 8487 if (!isset($pl['opt']['fs'])) { 8488 break; 8489 } 8490 $filename = basename($pl['opt']['fs']); 8491 if (isset($this->embeddedfiles[$filename]['f'])) { 8492 $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R'; 8493 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag'); 8494 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8495 $annots .= ' /Name /'.$pl['opt']['name']; 8496 } else { 8497 $annots .= ' /Name /PushPin'; 8498 } 8499 // index (zero-based) of the annotation in the Annots array of this page 8500 $this->embeddedfiles[$filename]['a'] = $key; 8501 } 8502 break; 8503 } 8504 case 'sound': { 8505 if (!isset($pl['opt']['fs'])) { 8506 break; 8507 } 8508 $filename = basename($pl['opt']['fs']); 8509 if (isset($this->embeddedfiles[$filename]['f'])) { 8510 // ... TO BE COMPLETED ... 8511 // /R /C /B /E /CO /CP 8512 $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R'; 8513 $iconsapp = array('Speaker', 'Mic'); 8514 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8515 $annots .= ' /Name /'.$pl['opt']['name']; 8516 } else { 8517 $annots .= ' /Name /Speaker'; 8518 } 8519 } 8520 break; 8521 } 8522 case 'movie': { 8523 break; 8524 } 8525 case 'widget': { 8526 $hmode = array('N', 'I', 'O', 'P', 'T'); 8527 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) { 8528 $annots .= ' /H /'.$pl['opt']['h']; 8529 } 8530 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) { 8531 $annots .= ' /MK <<'; 8532 if (isset($pl['opt']['mk']['r'])) { 8533 $annots .= ' /R '.$pl['opt']['mk']['r']; 8534 } 8535 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) { 8536 $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']); 8537 } 8538 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) { 8539 $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']); 8540 } 8541 if (isset($pl['opt']['mk']['ca'])) { 8542 $annots .= ' /CA '.$pl['opt']['mk']['ca']; 8543 } 8544 if (isset($pl['opt']['mk']['rc'])) { 8545 $annots .= ' /RC '.$pl['opt']['mk']['rc']; 8546 } 8547 if (isset($pl['opt']['mk']['ac'])) { 8548 $annots .= ' /AC '.$pl['opt']['mk']['ac']; 8549 } 8550 if (isset($pl['opt']['mk']['i'])) { 8551 $info = $this->getImageBuffer($pl['opt']['mk']['i']); 8552 if ($info !== false) { 8553 $annots .= ' /I '.$info['n'].' 0 R'; 8554 } 8555 } 8556 if (isset($pl['opt']['mk']['ri'])) { 8557 $info = $this->getImageBuffer($pl['opt']['mk']['ri']); 8558 if ($info !== false) { 8559 $annots .= ' /RI '.$info['n'].' 0 R'; 8560 } 8561 } 8562 if (isset($pl['opt']['mk']['ix'])) { 8563 $info = $this->getImageBuffer($pl['opt']['mk']['ix']); 8564 if ($info !== false) { 8565 $annots .= ' /IX '.$info['n'].' 0 R'; 8566 } 8567 } 8568 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) { 8569 $annots .= ' /IF <<'; 8570 $if_sw = array('A', 'B', 'S', 'N'); 8571 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) { 8572 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw']; 8573 } 8574 $if_s = array('A', 'P'); 8575 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) { 8576 $annots .= ' /S /'.$pl['opt']['mk']['if']['s']; 8577 } 8578 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) { 8579 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]); 8580 } 8581 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) { 8582 $annots .= ' /FB true'; 8583 } 8584 $annots .= '>>'; 8585 } 8586 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) { 8587 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']); 8588 } 8589 $annots .= '>>'; 8590 } // end MK 8591 // --- Entries for field dictionaries --- 8592 if (isset($this->radiobutton_groups[$n][$pl['txt']])) { 8593 // set parent 8594 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R'; 8595 } 8596 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 8597 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id); 8598 } 8599 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { 8600 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id); 8601 } 8602 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) { 8603 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id); 8604 } 8605 if (isset($pl['opt']['ff'])) { 8606 if (is_array($pl['opt']['ff'])) { 8607 // array of bit settings 8608 $flag = 0; 8609 foreach($pl['opt']['ff'] as $val) { 8610 $flag += 1 << ($val - 1); 8611 } 8612 } else { 8613 $flag = intval($pl['opt']['ff']); 8614 } 8615 $annots .= ' /Ff '.$flag; 8616 } 8617 if (isset($pl['opt']['maxlen'])) { 8618 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']); 8619 } 8620 if (isset($pl['opt']['v'])) { 8621 $annots .= ' /V'; 8622 if (is_array($pl['opt']['v'])) { 8623 foreach ($pl['opt']['v'] AS $optval) { 8624 if (is_float($optval)) { 8625 $optval = sprintf('%F', $optval); 8626 } 8627 $annots .= ' '.$optval; 8628 } 8629 } else { 8630 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id); 8631 } 8632 } 8633 if (isset($pl['opt']['dv'])) { 8634 $annots .= ' /DV'; 8635 if (is_array($pl['opt']['dv'])) { 8636 foreach ($pl['opt']['dv'] AS $optval) { 8637 if (is_float($optval)) { 8638 $optval = sprintf('%F', $optval); 8639 } 8640 $annots .= ' '.$optval; 8641 } 8642 } else { 8643 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id); 8644 } 8645 } 8646 if (isset($pl['opt']['rv'])) { 8647 $annots .= ' /RV'; 8648 if (is_array($pl['opt']['rv'])) { 8649 foreach ($pl['opt']['rv'] AS $optval) { 8650 if (is_float($optval)) { 8651 $optval = sprintf('%F', $optval); 8652 } 8653 $annots .= ' '.$optval; 8654 } 8655 } else { 8656 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id); 8657 } 8658 } 8659 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) { 8660 $annots .= ' /A << '.$pl['opt']['a'].' >>'; 8661 } 8662 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) { 8663 $annots .= ' /AA << '.$pl['opt']['aa'].' >>'; 8664 } 8665 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 8666 $annots .= ' /DA ('.$pl['opt']['da'].')'; 8667 } 8668 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 8669 $annots .= ' /Q '.intval($pl['opt']['q']); 8670 } 8671 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) { 8672 $annots .= ' /Opt ['; 8673 foreach($pl['opt']['opt'] AS $copt) { 8674 if (is_array($copt)) { 8675 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']'; 8676 } else { 8677 $annots .= ' '.$this->_textstring($copt, $annot_obj_id); 8678 } 8679 } 8680 $annots .= ']'; 8681 } 8682 if (isset($pl['opt']['ti'])) { 8683 $annots .= ' /TI '.intval($pl['opt']['ti']); 8684 } 8685 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) { 8686 $annots .= ' /I ['; 8687 foreach($pl['opt']['i'] AS $copt) { 8688 $annots .= intval($copt).' '; 8689 } 8690 $annots .= ']'; 8691 } 8692 break; 8693 } 8694 case 'screen': { 8695 break; 8696 } 8697 case 'printermark': { 8698 break; 8699 } 8700 case 'trapnet': { 8701 break; 8702 } 8703 case 'watermark': { 8704 break; 8705 } 8706 case '3d': { 8707 break; 8708 } 8709 default: { 8710 break; 8711 } 8712 } 8713 $annots .= '>>'; 8714 // create new annotation object 8715 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj'); 8716 if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) { 8717 // store reference of form object 8718 $this->form_obj_id[] = $annot_obj_id; 8719 } 8720 } 8721 } 8722 } // end for each page 8723 } 8724 8725 /** 8726 * Put appearance streams XObject used to define annotation's appearance states. 8727 * @param $w (int) annotation width 8728 * @param $h (int) annotation height 8729 * @param $stream (string) appearance stream 8730 * @return int object ID 8731 * @protected 8732 * @since 4.8.001 (2009-09-09) 8733 */ 8734 protected function _putAPXObject($w=0, $h=0, $stream='') { 8735 $stream = trim($stream); 8736 $out = $this->_getobj()."\n"; 8737 $this->xobjects['AX'.$this->n] = array('n' => $this->n); 8738 $out .= '<<'; 8739 $out .= ' /Type /XObject'; 8740 $out .= ' /Subtype /Form'; 8741 $out .= ' /FormType 1'; 8742 if ($this->compress) { 8743 $stream = gzcompress($stream); 8744 $out .= ' /Filter /FlateDecode'; 8745 } 8746 $rect = sprintf('%F %F', $w, $h); 8747 $out .= ' /BBox [0 0 '.$rect.']'; 8748 $out .= ' /Matrix [1 0 0 1 0 0]'; 8749 $out .= ' /Resources 2 0 R'; 8750 $stream = $this->_getrawstream($stream); 8751 $out .= ' /Length '.strlen($stream); 8752 $out .= ' >>'; 8753 $out .= ' stream'."\n".$stream."\n".'endstream'; 8754 $out .= "\n".'endobj'; 8755 $this->_out($out); 8756 return $this->n; 8757 } 8758 8759 /** 8760 * Output fonts. 8761 * @author Nicola Asuni 8762 * @protected 8763 */ 8764 protected function _putfonts() { 8765 $nf = $this->n; 8766 foreach ($this->diffs as $diff) { 8767 //Encodings 8768 $this->_newobj(); 8769 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj'); 8770 } 8771 $mqr = TCPDF_STATIC::get_mqr(); 8772 TCPDF_STATIC::set_mqr(false); 8773 foreach ($this->FontFiles as $file => $info) { 8774 // search and get font file to embedd 8775 $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']); 8776 if (!TCPDF_STATIC::empty_string($fontfile)) { 8777 $font = file_get_contents($fontfile); 8778 $compressed = (substr($file, -2) == '.z'); 8779 if ((!$compressed) AND (isset($info['length2']))) { 8780 $header = (ord($font[0]) == 128); 8781 if ($header) { 8782 // strip first binary header 8783 $font = substr($font, 6); 8784 } 8785 if ($header AND (ord($font[$info['length1']]) == 128)) { 8786 // strip second binary header 8787 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6)); 8788 } 8789 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) { 8790 if ($compressed) { 8791 // uncompress font 8792 $font = gzuncompress($font); 8793 } 8794 // merge subset characters 8795 $subsetchars = array(); // used chars 8796 foreach ($info['fontkeys'] as $fontkey) { 8797 $fontinfo = $this->getFontBuffer($fontkey); 8798 $subsetchars += $fontinfo['subsetchars']; 8799 } 8800 // rebuild a font subset 8801 $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars); 8802 // calculate new font length 8803 $info['length1'] = strlen($font); 8804 if ($compressed) { 8805 // recompress font 8806 $font = gzcompress($font); 8807 } 8808 } 8809 $this->_newobj(); 8810 $this->FontFiles[$file]['n'] = $this->n; 8811 $stream = $this->_getrawstream($font); 8812 $out = '<< /Length '.strlen($stream); 8813 if ($compressed) { 8814 $out .= ' /Filter /FlateDecode'; 8815 } 8816 $out .= ' /Length1 '.$info['length1']; 8817 if (isset($info['length2'])) { 8818 $out .= ' /Length2 '.$info['length2'].' /Length3 0'; 8819 } 8820 $out .= ' >>'; 8821 $out .= ' stream'."\n".$stream."\n".'endstream'; 8822 $out .= "\n".'endobj'; 8823 $this->_out($out); 8824 } 8825 } 8826 TCPDF_STATIC::set_mqr($mqr); 8827 foreach ($this->fontkeys as $k) { 8828 //Font objects 8829 $font = $this->getFontBuffer($k); 8830 $type = $font['type']; 8831 $name = $font['name']; 8832 if ($type == 'core') { 8833 // standard core font 8834 $out = $this->_getobj($this->font_obj_ids[$k])."\n"; 8835 $out .= '<</Type /Font'; 8836 $out .= ' /Subtype /Type1'; 8837 $out .= ' /BaseFont /'.$name; 8838 $out .= ' /Name /F'.$font['i']; 8839 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) { 8840 $out .= ' /Encoding /WinAnsiEncoding'; 8841 } 8842 if ($k == 'helvetica') { 8843 // add default font for annotations 8844 $this->annotation_fonts[$k] = $font['i']; 8845 } 8846 $out .= ' >>'; 8847 $out .= "\n".'endobj'; 8848 $this->_out($out); 8849 } elseif (($type == 'Type1') OR ($type == 'TrueType')) { 8850 // additional Type1 or TrueType font 8851 $out = $this->_getobj($this->font_obj_ids[$k])."\n"; 8852 $out .= '<</Type /Font'; 8853 $out .= ' /Subtype /'.$type; 8854 $out .= ' /BaseFont /'.$name; 8855 $out .= ' /Name /F'.$font['i']; 8856 $out .= ' /FirstChar 32 /LastChar 255'; 8857 $out .= ' /Widths '.($this->n + 1).' 0 R'; 8858 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R'; 8859 if ($font['enc']) { 8860 if (isset($font['diff'])) { 8861 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R'; 8862 } else { 8863 $out .= ' /Encoding /WinAnsiEncoding'; 8864 } 8865 } 8866 $out .= ' >>'; 8867 $out .= "\n".'endobj'; 8868 $this->_out($out); 8869 // Widths 8870 $this->_newobj(); 8871 $s = '['; 8872 for ($i = 32; $i < 256; ++$i) { 8873 if (isset($font['cw'][$i])) { 8874 $s .= $font['cw'][$i].' '; 8875 } else { 8876 $s .= $font['dw'].' '; 8877 } 8878 } 8879 $s .= ']'; 8880 $s .= "\n".'endobj'; 8881 $this->_out($s); 8882 //Descriptor 8883 $this->_newobj(); 8884 $s = '<</Type /FontDescriptor /FontName /'.$name; 8885 foreach ($font['desc'] as $fdk => $fdv) { 8886 if (is_float($fdv)) { 8887 $fdv = sprintf('%F', $fdv); 8888 } 8889 $s .= ' /'.$fdk.' '.$fdv.''; 8890 } 8891 if (!TCPDF_STATIC::empty_string($font['file'])) { 8892 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; 8893 } 8894 $s .= '>>'; 8895 $s .= "\n".'endobj'; 8896 $this->_out($s); 8897 } else { 8898 // additional types 8899 $mtd = '_put'.strtolower($type); 8900 if (!method_exists($this, $mtd)) { 8901 $this->Error('Unsupported font type: '.$type); 8902 } 8903 $this->$mtd($font); 8904 } 8905 } 8906 } 8907 8908 /** 8909 * Adds unicode fonts.<br> 8910 * Based on PDF Reference 1.3 (section 5) 8911 * @param $font (array) font data 8912 * @protected 8913 * @author Nicola Asuni 8914 * @since 1.52.0.TC005 (2005-01-05) 8915 */ 8916 protected function _puttruetypeunicode($font) { 8917 $fontname = ''; 8918 if ($font['subset']) { 8919 // change name for font subsetting 8920 $subtag = sprintf('%06u', $font['i']); 8921 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ'); 8922 $fontname .= $subtag.'+'; 8923 } 8924 $fontname .= $font['name']; 8925 // Type0 Font 8926 // A composite font composed of other fonts, organized hierarchically 8927 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; 8928 $out .= '<< /Type /Font'; 8929 $out .= ' /Subtype /Type0'; 8930 $out .= ' /BaseFont /'.$fontname; 8931 $out .= ' /Name /F'.$font['i']; 8932 $out .= ' /Encoding /'.$font['enc']; 8933 $out .= ' /ToUnicode '.($this->n + 1).' 0 R'; 8934 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]'; 8935 $out .= ' >>'; 8936 $out .= "\n".'endobj'; 8937 $this->_out($out); 8938 // ToUnicode map for Identity-H 8939 $stream = TCPDF_FONT_DATA::$uni_identity_h; 8940 // ToUnicode Object 8941 $this->_newobj(); 8942 $stream = ($this->compress) ? gzcompress($stream) : $stream; 8943 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 8944 $stream = $this->_getrawstream($stream); 8945 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj'); 8946 // CIDFontType2 8947 // A CIDFont whose glyph descriptions are based on TrueType font technology 8948 $oid = $this->_newobj(); 8949 $out = '<< /Type /Font'; 8950 $out .= ' /Subtype /CIDFontType2'; 8951 $out .= ' /BaseFont /'.$fontname; 8952 // A dictionary containing entries that define the character collection of the CIDFont. 8953 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); 8954 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); 8955 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 8956 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>'; 8957 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 8958 $out .= ' /DW '.$font['dw']; // default width 8959 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0); 8960 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) { 8961 $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R'; 8962 } 8963 $out .= ' >>'; 8964 $out .= "\n".'endobj'; 8965 $this->_out($out); 8966 // Font descriptor 8967 // A font descriptor describing the CIDFont default metrics other than its glyph widths 8968 $this->_newobj(); 8969 $out = '<< /Type /FontDescriptor'; 8970 $out .= ' /FontName /'.$fontname; 8971 foreach ($font['desc'] as $key => $value) { 8972 if (is_float($value)) { 8973 $value = sprintf('%F', $value); 8974 } 8975 $out .= ' /'.$key.' '.$value; 8976 } 8977 $fontdir = false; 8978 if (!TCPDF_STATIC::empty_string($font['file'])) { 8979 // A stream containing a TrueType font 8980 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'; 8981 $fontdir = $this->FontFiles[$font['file']]['fontdir']; 8982 } 8983 $out .= ' >>'; 8984 $out .= "\n".'endobj'; 8985 $this->_out($out); 8986 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) { 8987 $this->_newobj(); 8988 // Embed CIDToGIDMap 8989 // A specification of the mapping from CIDs to glyph indices 8990 // search and get CTG font file to embedd 8991 $ctgfile = strtolower($font['ctg']); 8992 // search and get ctg font file to embedd 8993 $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir); 8994 if (TCPDF_STATIC::empty_string($fontfile)) { 8995 $this->Error('Font file not found: '.$ctgfile); 8996 } 8997 $stream = $this->_getrawstream(file_get_contents($fontfile)); 8998 $out = '<< /Length '.strlen($stream).''; 8999 if (substr($fontfile, -2) == '.z') { // check file extension 9000 // Decompresses data encoded using the public-domain 9001 // zlib/deflate compression method, reproducing the 9002 // original text or binary data 9003 $out .= ' /Filter /FlateDecode'; 9004 } 9005 $out .= ' >>'; 9006 $out .= ' stream'."\n".$stream."\n".'endstream'; 9007 $out .= "\n".'endobj'; 9008 $this->_out($out); 9009 } 9010 } 9011 9012 /** 9013 * Output CID-0 fonts. 9014 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format 9015 * @param $font (array) font data 9016 * @protected 9017 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira 9018 * @since 3.2.000 (2008-06-23) 9019 */ 9020 protected function _putcidfont0($font) { 9021 $cidoffset = 0; 9022 if (!isset($font['cw'][1])) { 9023 $cidoffset = 31; 9024 } 9025 if (isset($font['cidinfo']['uni2cid'])) { 9026 // convert unicode to cid. 9027 $uni2cid = $font['cidinfo']['uni2cid']; 9028 $cw = array(); 9029 foreach ($font['cw'] as $uni => $width) { 9030 if (isset($uni2cid[$uni])) { 9031 $cw[($uni2cid[$uni] + $cidoffset)] = $width; 9032 } elseif ($uni < 256) { 9033 $cw[$uni] = $width; 9034 } // else unknown character 9035 } 9036 $font = array_merge($font, array('cw' => $cw)); 9037 } 9038 $name = $font['name']; 9039 $enc = $font['enc']; 9040 if ($enc) { 9041 $longname = $name.'-'.$enc; 9042 } else { 9043 $longname = $name; 9044 } 9045 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; 9046 $out .= '<</Type /Font'; 9047 $out .= ' /Subtype /Type0'; 9048 $out .= ' /BaseFont /'.$longname; 9049 $out .= ' /Name /F'.$font['i']; 9050 if ($enc) { 9051 $out .= ' /Encoding /'.$enc; 9052 } 9053 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]'; 9054 $out .= ' >>'; 9055 $out .= "\n".'endobj'; 9056 $this->_out($out); 9057 $oid = $this->_newobj(); 9058 $out = '<</Type /Font'; 9059 $out .= ' /Subtype /CIDFontType0'; 9060 $out .= ' /BaseFont /'.$name; 9061 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); 9062 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); 9063 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 9064 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>'; 9065 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 9066 $out .= ' /DW '.$font['dw']; 9067 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset); 9068 $out .= ' >>'; 9069 $out .= "\n".'endobj'; 9070 $this->_out($out); 9071 $this->_newobj(); 9072 $s = '<</Type /FontDescriptor /FontName /'.$name; 9073 foreach ($font['desc'] as $k => $v) { 9074 if ($k != 'Style') { 9075 if (is_float($v)) { 9076 $v = sprintf('%F', $v); 9077 } 9078 $s .= ' /'.$k.' '.$v.''; 9079 } 9080 } 9081 $s .= '>>'; 9082 $s .= "\n".'endobj'; 9083 $this->_out($s); 9084 } 9085 9086 /** 9087 * Output images. 9088 * @protected 9089 */ 9090 protected function _putimages() { 9091 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 9092 foreach ($this->imagekeys as $file) { 9093 $info = $this->getImageBuffer($file); 9094 // set object for alternate images array 9095 if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) { 9096 $altoid = $this->_newobj(); 9097 $out = '['; 9098 foreach ($info['altimgs'] as $altimage) { 9099 if (isset($this->xobjects['I'.$altimage[0]]['n'])) { 9100 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R'; 9101 $out .= ' /DefaultForPrinting'; 9102 if ($altimage[1] === true) { 9103 $out .= ' true'; 9104 } else { 9105 $out .= ' false'; 9106 } 9107 $out .= ' >>'; 9108 } 9109 } 9110 $out .= ' ]'; 9111 $out .= "\n".'endobj'; 9112 $this->_out($out); 9113 } 9114 // set image object 9115 $oid = $this->_newobj(); 9116 $this->xobjects['I'.$info['i']] = array('n' => $oid); 9117 $this->setImageSubBuffer($file, 'n', $this->n); 9118 $out = '<</Type /XObject'; 9119 $out .= ' /Subtype /Image'; 9120 $out .= ' /Width '.$info['w']; 9121 $out .= ' /Height '.$info['h']; 9122 if (array_key_exists('masked', $info)) { 9123 $out .= ' /SMask '.($this->n - 1).' 0 R'; 9124 } 9125 // set color space 9126 $icc = false; 9127 if (isset($info['icc']) AND ($info['icc'] !== false)) { 9128 // ICC Colour Space 9129 $icc = true; 9130 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]'; 9131 } elseif ($info['cs'] == 'Indexed') { 9132 // Indexed Colour Space 9133 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]'; 9134 } else { 9135 // Device Colour Space 9136 $out .= ' /ColorSpace /'.$info['cs']; 9137 } 9138 if ($info['cs'] == 'DeviceCMYK') { 9139 $out .= ' /Decode [1 0 1 0 1 0 1 0]'; 9140 } 9141 $out .= ' /BitsPerComponent '.$info['bpc']; 9142 if (isset($altoid) AND ($altoid > 0)) { 9143 // reference to alternate images dictionary 9144 $out .= ' /Alternates '.$altoid.' 0 R'; 9145 } 9146 if (isset($info['exurl']) AND !empty($info['exurl'])) { 9147 // external stream 9148 $out .= ' /Length 0'; 9149 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>'; 9150 if (isset($info['f'])) { 9151 $out .= ' /FFilter /'.$info['f']; 9152 } 9153 $out .= ' >>'; 9154 $out .= ' stream'."\n".'endstream'; 9155 } else { 9156 if (isset($info['f'])) { 9157 $out .= ' /Filter /'.$info['f']; 9158 } 9159 if (isset($info['parms'])) { 9160 $out .= ' '.$info['parms']; 9161 } 9162 if (isset($info['trns']) AND is_array($info['trns'])) { 9163 $trns = ''; 9164 $count_info = count($info['trns']); 9165 if ($info['cs'] == 'Indexed') { 9166 $maxval =(pow(2, $info['bpc']) - 1); 9167 for ($i = 0; $i < $count_info; ++$i) { 9168 if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) { 9169 // this is not a binary type mask @TODO: create a SMask 9170 $trns = ''; 9171 break; 9172 } elseif (empty($trns) AND ($info['trns'][$i] == 0)) { 9173 // store the first fully transparent value 9174 $trns .= $i.' '.$i.' '; 9175 } 9176 } 9177 } else { 9178 // grayscale or RGB 9179 for ($i = 0; $i < $count_info; ++$i) { 9180 if ($info['trns'][$i] == 0) { 9181 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; 9182 } 9183 } 9184 } 9185 // Colour Key Masking 9186 if (!empty($trns)) { 9187 $out .= ' /Mask ['.$trns.']'; 9188 } 9189 } 9190 $stream = $this->_getrawstream($info['data']); 9191 $out .= ' /Length '.strlen($stream).' >>'; 9192 $out .= ' stream'."\n".$stream."\n".'endstream'; 9193 } 9194 $out .= "\n".'endobj'; 9195 $this->_out($out); 9196 if ($icc) { 9197 // ICC colour profile 9198 $this->_newobj(); 9199 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc']; 9200 $icc = $this->_getrawstream($icc); 9201 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); 9202 } elseif ($info['cs'] == 'Indexed') { 9203 // colour palette 9204 $this->_newobj(); 9205 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; 9206 $pal = $this->_getrawstream($pal); 9207 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj'); 9208 } 9209 } 9210 } 9211 9212 /** 9213 * Output Form XObjects Templates. 9214 * @author Nicola Asuni 9215 * @since 5.8.017 (2010-08-24) 9216 * @protected 9217 * @see startTemplate(), endTemplate(), printTemplate() 9218 */ 9219 protected function _putxobjects() { 9220 foreach ($this->xobjects as $key => $data) { 9221 if (isset($data['outdata'])) { 9222 $stream = str_replace($this->epsmarker, '', trim($data['outdata'])); 9223 $out = $this->_getobj($data['n'])."\n"; 9224 $out .= '<<'; 9225 $out .= ' /Type /XObject'; 9226 $out .= ' /Subtype /Form'; 9227 $out .= ' /FormType 1'; 9228 if ($this->compress) { 9229 $stream = gzcompress($stream); 9230 $out .= ' /Filter /FlateDecode'; 9231 } 9232 $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)); 9233 $out .= ' /Matrix [1 0 0 1 0 0]'; 9234 $out .= ' /Resources <<'; 9235 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 9236 if (!$this->pdfa_mode) { 9237 // transparency 9238 if (isset($data['extgstates']) AND !empty($data['extgstates'])) { 9239 $out .= ' /ExtGState <<'; 9240 foreach ($data['extgstates'] as $k => $extgstate) { 9241 if (isset($this->extgstates[$k]['name'])) { 9242 $out .= ' /'.$this->extgstates[$k]['name']; 9243 } else { 9244 $out .= ' /GS'.$k; 9245 } 9246 $out .= ' '.$this->extgstates[$k]['n'].' 0 R'; 9247 } 9248 $out .= ' >>'; 9249 } 9250 if (isset($data['gradients']) AND !empty($data['gradients'])) { 9251 $gp = ''; 9252 $gs = ''; 9253 foreach ($data['gradients'] as $id => $grad) { 9254 // gradient patterns 9255 $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R'; 9256 // gradient shadings 9257 $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R'; 9258 } 9259 $out .= ' /Pattern <<'.$gp.' >>'; 9260 $out .= ' /Shading <<'.$gs.' >>'; 9261 } 9262 } 9263 // spot colors 9264 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) { 9265 $out .= ' /ColorSpace <<'; 9266 foreach ($data['spot_colors'] as $name => $color) { 9267 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R'; 9268 } 9269 $out .= ' >>'; 9270 } 9271 // fonts 9272 if (!empty($data['fonts'])) { 9273 $out .= ' /Font <<'; 9274 foreach ($data['fonts'] as $fontkey => $fontid) { 9275 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; 9276 } 9277 $out .= ' >>'; 9278 } 9279 // images or nested xobjects 9280 if (!empty($data['images']) OR !empty($data['xobjects'])) { 9281 $out .= ' /XObject <<'; 9282 foreach ($data['images'] as $imgid) { 9283 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R'; 9284 } 9285 foreach ($data['xobjects'] as $sub_id => $sub_objid) { 9286 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R'; 9287 } 9288 $out .= ' >>'; 9289 } 9290 $out .= ' >>'; //end resources 9291 if (isset($data['group']) AND ($data['group'] !== false)) { 9292 // set transparency group 9293 $out .= ' /Group << /Type /Group /S /Transparency'; 9294 if (is_array($data['group'])) { 9295 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) { 9296 $out .= ' /CS /'.$data['group']['CS']; 9297 } 9298 if (isset($data['group']['I'])) { 9299 $out .= ' /I /'.($data['group']['I']===true?'true':'false'); 9300 } 9301 if (isset($data['group']['K'])) { 9302 $out .= ' /K /'.($data['group']['K']===true?'true':'false'); 9303 } 9304 } 9305 $out .= ' >>'; 9306 } 9307 $stream = $this->_getrawstream($stream, $data['n']); 9308 $out .= ' /Length '.strlen($stream); 9309 $out .= ' >>'; 9310 $out .= ' stream'."\n".$stream."\n".'endstream'; 9311 $out .= "\n".'endobj'; 9312 $this->_out($out); 9313 } 9314 } 9315 } 9316 9317 /** 9318 * Output Spot Colors Resources. 9319 * @protected 9320 * @since 4.0.024 (2008-09-12) 9321 */ 9322 protected function _putspotcolors() { 9323 foreach ($this->spot_colors as $name => $color) { 9324 $this->_newobj(); 9325 $this->spot_colors[$name]['n'] = $this->n; 9326 $out = '[/Separation /'.str_replace(' ', '#20', $name); 9327 $out .= ' /DeviceCMYK <<'; 9328 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]'; 9329 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100)); 9330 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]'; 9331 $out .= "\n".'endobj'; 9332 $this->_out($out); 9333 } 9334 } 9335 9336 /** 9337 * Return XObjects Dictionary. 9338 * @return string XObjects dictionary 9339 * @protected 9340 * @since 5.8.014 (2010-08-23) 9341 */ 9342 protected function _getxobjectdict() { 9343 $out = ''; 9344 foreach ($this->xobjects as $id => $objid) { 9345 $out .= ' /'.$id.' '.$objid['n'].' 0 R'; 9346 } 9347 return $out; 9348 } 9349 9350 /** 9351 * Output Resources Dictionary. 9352 * @protected 9353 */ 9354 protected function _putresourcedict() { 9355 $out = $this->_getobj(2)."\n"; 9356 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 9357 $out .= ' /Font <<'; 9358 foreach ($this->fontkeys as $fontkey) { 9359 $font = $this->getFontBuffer($fontkey); 9360 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R'; 9361 } 9362 $out .= ' >>'; 9363 $out .= ' /XObject <<'; 9364 $out .= $this->_getxobjectdict(); 9365 $out .= ' >>'; 9366 // layers 9367 if (!empty($this->pdflayers)) { 9368 $out .= ' /Properties <<'; 9369 foreach ($this->pdflayers as $layer) { 9370 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R'; 9371 } 9372 $out .= ' >>'; 9373 } 9374 if (!$this->pdfa_mode) { 9375 // transparency 9376 if (isset($this->extgstates) AND !empty($this->extgstates)) { 9377 $out .= ' /ExtGState <<'; 9378 foreach ($this->extgstates as $k => $extgstate) { 9379 if (isset($extgstate['name'])) { 9380 $out .= ' /'.$extgstate['name']; 9381 } else { 9382 $out .= ' /GS'.$k; 9383 } 9384 $out .= ' '.$extgstate['n'].' 0 R'; 9385 } 9386 $out .= ' >>'; 9387 } 9388 if (isset($this->gradients) AND !empty($this->gradients)) { 9389 $gp = ''; 9390 $gs = ''; 9391 foreach ($this->gradients as $id => $grad) { 9392 // gradient patterns 9393 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R'; 9394 // gradient shadings 9395 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R'; 9396 } 9397 $out .= ' /Pattern <<'.$gp.' >>'; 9398 $out .= ' /Shading <<'.$gs.' >>'; 9399 } 9400 } 9401 // spot colors 9402 if (isset($this->spot_colors) AND !empty($this->spot_colors)) { 9403 $out .= ' /ColorSpace <<'; 9404 foreach ($this->spot_colors as $color) { 9405 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R'; 9406 } 9407 $out .= ' >>'; 9408 } 9409 $out .= ' >>'; 9410 $out .= "\n".'endobj'; 9411 $this->_out($out); 9412 } 9413 9414 /** 9415 * Output Resources. 9416 * @protected 9417 */ 9418 protected function _putresources() { 9419 $this->_putextgstates(); 9420 $this->_putocg(); 9421 $this->_putfonts(); 9422 $this->_putimages(); 9423 $this->_putspotcolors(); 9424 $this->_putshaders(); 9425 $this->_putxobjects(); 9426 $this->_putresourcedict(); 9427 $this->_putdests(); 9428 $this->_putEmbeddedFiles(); 9429 $this->_putannotsobjs(); 9430 $this->_putjavascript(); 9431 $this->_putbookmarks(); 9432 $this->_putencryption(); 9433 } 9434 9435 /** 9436 * Adds some Metadata information (Document Information Dictionary) 9437 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference) 9438 * @return int object id 9439 * @protected 9440 */ 9441 protected function _putinfo() { 9442 $oid = $this->_newobj(); 9443 $out = '<<'; 9444 // store current isunicode value 9445 $prev_isunicode = $this->isunicode; 9446 if ($this->docinfounicode) { 9447 $this->isunicode = true; 9448 } 9449 if (!TCPDF_STATIC::empty_string($this->title)) { 9450 // The document's title. 9451 $out .= ' /Title '.$this->_textstring($this->title, $oid); 9452 } 9453 if (!TCPDF_STATIC::empty_string($this->author)) { 9454 // The name of the person who created the document. 9455 $out .= ' /Author '.$this->_textstring($this->author, $oid); 9456 } 9457 if (!TCPDF_STATIC::empty_string($this->subject)) { 9458 // The subject of the document. 9459 $out .= ' /Subject '.$this->_textstring($this->subject, $oid); 9460 } 9461 if (!TCPDF_STATIC::empty_string($this->keywords)) { 9462 // Keywords associated with the document. 9463 $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid); 9464 } 9465 if (!TCPDF_STATIC::empty_string($this->creator)) { 9466 // 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. 9467 $out .= ' /Creator '.$this->_textstring($this->creator, $oid); 9468 } 9469 // restore previous isunicode value 9470 $this->isunicode = $prev_isunicode; 9471 // default producer 9472 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid); 9473 // The date and time the document was created, in human-readable form 9474 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp); 9475 // The date and time the document was most recently modified, in human-readable form 9476 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp); 9477 // A name object indicating whether the document has been modified to include trapping information 9478 $out .= ' /Trapped /False'; 9479 $out .= ' >>'; 9480 $out .= "\n".'endobj'; 9481 $this->_out($out); 9482 return $oid; 9483 } 9484 9485 /** 9486 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag. 9487 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method! 9488 * @param $xmp (string) Custom XMP data. 9489 * @since 5.9.128 (2011-10-06) 9490 * @public 9491 */ 9492 public function setExtraXMP($xmp) { 9493 $this->custom_xmp = $xmp; 9494 } 9495 9496 /** 9497 * Put XMP data object and return ID. 9498 * @return (int) The object ID. 9499 * @since 5.9.121 (2011-09-28) 9500 * @protected 9501 */ 9502 protected function _putXMP() { 9503 $oid = $this->_newobj(); 9504 // store current isunicode value 9505 $prev_isunicode = $this->isunicode; 9506 $this->isunicode = true; 9507 $prev_encrypted = $this->encrypted; 9508 $this->encrypted = false; 9509 // set XMP data 9510 $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n"; 9511 $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"; 9512 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n"; 9513 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n"; 9514 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n"; 9515 $xmp .= "\t\t\t".'<dc:title>'."\n"; 9516 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; 9517 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n"; 9518 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; 9519 $xmp .= "\t\t\t".'</dc:title>'."\n"; 9520 $xmp .= "\t\t\t".'<dc:creator>'."\n"; 9521 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n"; 9522 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n"; 9523 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n"; 9524 $xmp .= "\t\t\t".'</dc:creator>'."\n"; 9525 $xmp .= "\t\t\t".'<dc:description>'."\n"; 9526 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; 9527 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n"; 9528 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; 9529 $xmp .= "\t\t\t".'</dc:description>'."\n"; 9530 $xmp .= "\t\t\t".'<dc:subject>'."\n"; 9531 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; 9532 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n"; 9533 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; 9534 $xmp .= "\t\t\t".'</dc:subject>'."\n"; 9535 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9536 // convert doc creation date format 9537 $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp); 9538 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2); 9539 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2); 9540 $doccreationdate .= substr($dcdate, 14, 3).':'.substr($dcdate, 18, 2); 9541 $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate); 9542 // convert doc modification date format 9543 $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp); 9544 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2); 9545 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2); 9546 $docmoddate .= substr($dmdate, 14, 3).':'.substr($dmdate, 18, 2); 9547 $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate); 9548 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n"; 9549 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n"; 9550 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n"; 9551 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n"; 9552 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n"; 9553 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9554 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n"; 9555 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n"; 9556 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n"; 9557 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9558 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n"; 9559 $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); 9560 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n"; 9561 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n"; 9562 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9563 if ($this->pdfa_mode) { 9564 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n"; 9565 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n"; 9566 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n"; 9567 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9568 } 9569 // XMP extension schemas 9570 $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"; 9571 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n"; 9572 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; 9573 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9574 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n"; 9575 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n"; 9576 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n"; 9577 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9578 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9579 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n"; 9580 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n"; 9581 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n"; 9582 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 9583 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 9584 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9585 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9586 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n"; 9587 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n"; 9588 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n"; 9589 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9590 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 9591 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 9592 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9593 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9594 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n"; 9595 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n"; 9596 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n"; 9597 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 9598 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 9599 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9600 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9601 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n"; 9602 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n"; 9603 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n"; 9604 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9605 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9606 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9607 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n"; 9608 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n"; 9609 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; 9610 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9611 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9612 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9613 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n"; 9614 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n"; 9615 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; 9616 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9617 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 9618 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 9619 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9620 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; 9621 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n"; 9622 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9623 $xmp .= "\t".'</rdf:RDF>'."\n"; 9624 $xmp .= $this->custom_xmp; 9625 $xmp .= '</x:xmpmeta>'."\n"; 9626 $xmp .= '<?xpacket end="w"?>'; 9627 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj'; 9628 // restore previous isunicode value 9629 $this->isunicode = $prev_isunicode; 9630 $this->encrypted = $prev_encrypted; 9631 $this->_out($out); 9632 return $oid; 9633 } 9634 9635 /** 9636 * Output Catalog. 9637 * @return int object id 9638 * @protected 9639 */ 9640 protected function _putcatalog() { 9641 // put XMP 9642 $xmpobj = $this->_putXMP(); 9643 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile 9644 if ($this->pdfa_mode OR $this->force_srgb) { 9645 $iccobj = $this->_newobj(); 9646 $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc'); 9647 $filter = ''; 9648 if ($this->compress) { 9649 $filter = ' /Filter /FlateDecode'; 9650 $icc = gzcompress($icc); 9651 } 9652 $icc = $this->_getrawstream($icc); 9653 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); 9654 } 9655 // start catalog 9656 $oid = $this->_newobj(); 9657 $out = '<< /Type /Catalog'; 9658 $out .= ' /Version /'.$this->PDFVersion; 9659 //$out .= ' /Extensions <<>>'; 9660 $out .= ' /Pages 1 0 R'; 9661 //$out .= ' /PageLabels ' //...; 9662 $out .= ' /Names <<'; 9663 if ((!$this->pdfa_mode) AND !empty($this->n_js)) { 9664 $out .= ' /JavaScript '.$this->n_js; 9665 } 9666 if (!empty($this->efnames)) { 9667 $out .= ' /EmbeddedFiles <</Names ['; 9668 foreach ($this->efnames AS $fn => $fref) { 9669 $out .= ' '.$this->_datastring($fn).' '.$fref; 9670 } 9671 $out .= ' ]>>'; 9672 } 9673 $out .= ' >>'; 9674 if (!empty($this->dests)) { 9675 $out .= ' /Dests '.($this->n_dests).' 0 R'; 9676 } 9677 $out .= $this->_putviewerpreferences(); 9678 if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) { 9679 $out .= ' /PageLayout /'.$this->LayoutMode; 9680 } 9681 if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) { 9682 $out .= ' /PageMode /'.$this->PageMode; 9683 } 9684 if (count($this->outlines) > 0) { 9685 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R'; 9686 $out .= ' /PageMode /UseOutlines'; 9687 } 9688 //$out .= ' /Threads []'; 9689 if ($this->ZoomMode == 'fullpage') { 9690 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]'; 9691 } elseif ($this->ZoomMode == 'fullwidth') { 9692 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]'; 9693 } elseif ($this->ZoomMode == 'real') { 9694 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]'; 9695 } elseif (!is_string($this->ZoomMode)) { 9696 $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100)); 9697 } 9698 //$out .= ' /AA <<>>'; 9699 //$out .= ' /URI <<>>'; 9700 $out .= ' /Metadata '.$xmpobj.' 0 R'; 9701 //$out .= ' /StructTreeRoot <<>>'; 9702 //$out .= ' /MarkInfo <<>>'; 9703 if (isset($this->l['a_meta_language'])) { 9704 $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid); 9705 } 9706 //$out .= ' /SpiderInfo <<>>'; 9707 // set OutputIntent to sRGB IEC61966-2.1 if required 9708 if ($this->pdfa_mode OR $this->force_srgb) { 9709 $out .= ' /OutputIntents [<<'; 9710 $out .= ' /Type /OutputIntent'; 9711 $out .= ' /S /GTS_PDFA1'; 9712 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9713 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9714 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid); 9715 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9716 $out .= ' /DestOutputProfile '.$iccobj.' 0 R'; 9717 $out .= ' >>]'; 9718 } 9719 //$out .= ' /PieceInfo <<>>'; 9720 if (!empty($this->pdflayers)) { 9721 $lyrobjs = ''; 9722 $lyrobjs_off = ''; 9723 $lyrobjs_lock = ''; 9724 foreach ($this->pdflayers as $layer) { 9725 $layer_obj_ref = ' '.$layer['objid'].' 0 R'; 9726 $lyrobjs .= $layer_obj_ref; 9727 if ($layer['view'] === false) { 9728 $lyrobjs_off .= $layer_obj_ref; 9729 } 9730 if ($layer['lock']) { 9731 $lyrobjs_lock .= $layer_obj_ref; 9732 } 9733 } 9734 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']'; 9735 $out .= ' /D <<'; 9736 $out .= ' /Name '.$this->_textstring('Layers', $oid); 9737 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid); 9738 $out .= ' /BaseState /ON'; 9739 $out .= ' /OFF ['.$lyrobjs_off.']'; 9740 $out .= ' /Locked ['.$lyrobjs_lock.']'; 9741 $out .= ' /Intent /View'; 9742 $out .= ' /AS ['; 9743 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>'; 9744 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>'; 9745 $out .= ' ]'; 9746 $out .= ' /Order ['.$lyrobjs.']'; 9747 $out .= ' /ListMode /AllPages'; 9748 //$out .= ' /RBGroups ['..']'; 9749 //$out .= ' /Locked ['..']'; 9750 $out .= ' >>'; 9751 $out .= ' >>'; 9752 } 9753 // AcroForm 9754 if (!empty($this->form_obj_id) 9755 OR ($this->sign AND isset($this->signature_data['cert_type'])) 9756 OR !empty($this->empty_signature_appearance)) { 9757 $out .= ' /AcroForm <<'; 9758 $objrefs = ''; 9759 if ($this->sign AND isset($this->signature_data['cert_type'])) { 9760 // set reference for signature object 9761 $objrefs .= $this->sig_obj_id.' 0 R'; 9762 } 9763 if (!empty($this->empty_signature_appearance)) { 9764 foreach ($this->empty_signature_appearance as $esa) { 9765 // set reference for empty signature objects 9766 $objrefs .= ' '.$esa['objid'].' 0 R'; 9767 } 9768 } 9769 if (!empty($this->form_obj_id)) { 9770 foreach($this->form_obj_id as $objid) { 9771 $objrefs .= ' '.$objid.' 0 R'; 9772 } 9773 } 9774 $out .= ' /Fields ['.$objrefs.']'; 9775 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields. 9776 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 9777 $out .= ' /NeedAppearances false'; 9778 } 9779 if ($this->sign AND isset($this->signature_data['cert_type'])) { 9780 if ($this->signature_data['cert_type'] > 0) { 9781 $out .= ' /SigFlags 3'; 9782 } else { 9783 $out .= ' /SigFlags 1'; 9784 } 9785 } 9786 //$out .= ' /CO '; 9787 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) { 9788 $out .= ' /DR <<'; 9789 $out .= ' /Font <<'; 9790 foreach ($this->annotation_fonts as $fontkey => $fontid) { 9791 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; 9792 } 9793 $out .= ' >> >>'; 9794 } 9795 $font = $this->getFontBuffer('helvetica'); 9796 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)'; 9797 $out .= ' /Q '.(($this->rtl)?'2':'0'); 9798 //$out .= ' /XFA '; 9799 $out .= ' >>'; 9800 // signatures 9801 if ($this->sign AND isset($this->signature_data['cert_type']) 9802 AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) { 9803 if ($this->signature_data['cert_type'] > 0) { 9804 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>'; 9805 } else { 9806 $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>'; 9807 } 9808 } 9809 } 9810 //$out .= ' /Legal <<>>'; 9811 //$out .= ' /Requirements []'; 9812 //$out .= ' /Collection <<>>'; 9813 //$out .= ' /NeedsRendering true'; 9814 $out .= ' >>'; 9815 $out .= "\n".'endobj'; 9816 $this->_out($out); 9817 return $oid; 9818 } 9819 9820 /** 9821 * Output viewer preferences. 9822 * @return string for viewer preferences 9823 * @author Nicola asuni 9824 * @since 3.1.000 (2008-06-09) 9825 * @protected 9826 */ 9827 protected function _putviewerpreferences() { 9828 $vp = $this->viewer_preferences; 9829 $out = ' /ViewerPreferences <<'; 9830 if ($this->rtl) { 9831 $out .= ' /Direction /R2L'; 9832 } else { 9833 $out .= ' /Direction /L2R'; 9834 } 9835 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) { 9836 $out .= ' /HideToolbar true'; 9837 } 9838 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) { 9839 $out .= ' /HideMenubar true'; 9840 } 9841 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) { 9842 $out .= ' /HideWindowUI true'; 9843 } 9844 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) { 9845 $out .= ' /FitWindow true'; 9846 } 9847 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) { 9848 $out .= ' /CenterWindow true'; 9849 } 9850 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) { 9851 $out .= ' /DisplayDocTitle true'; 9852 } 9853 if (isset($vp['NonFullScreenPageMode'])) { 9854 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode']; 9855 } 9856 if (isset($vp['ViewArea'])) { 9857 $out .= ' /ViewArea /'.$vp['ViewArea']; 9858 } 9859 if (isset($vp['ViewClip'])) { 9860 $out .= ' /ViewClip /'.$vp['ViewClip']; 9861 } 9862 if (isset($vp['PrintArea'])) { 9863 $out .= ' /PrintArea /'.$vp['PrintArea']; 9864 } 9865 if (isset($vp['PrintClip'])) { 9866 $out .= ' /PrintClip /'.$vp['PrintClip']; 9867 } 9868 if (isset($vp['PrintScaling'])) { 9869 $out .= ' /PrintScaling /'.$vp['PrintScaling']; 9870 } 9871 if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) { 9872 $out .= ' /Duplex /'.$vp['Duplex']; 9873 } 9874 if (isset($vp['PickTrayByPDFSize'])) { 9875 if ($vp['PickTrayByPDFSize']) { 9876 $out .= ' /PickTrayByPDFSize true'; 9877 } else { 9878 $out .= ' /PickTrayByPDFSize false'; 9879 } 9880 } 9881 if (isset($vp['PrintPageRange'])) { 9882 $PrintPageRangeNum = ''; 9883 foreach ($vp['PrintPageRange'] as $k => $v) { 9884 $PrintPageRangeNum .= ' '.($v - 1).''; 9885 } 9886 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']'; 9887 } 9888 if (isset($vp['NumCopies'])) { 9889 $out .= ' /NumCopies '.intval($vp['NumCopies']); 9890 } 9891 $out .= ' >>'; 9892 return $out; 9893 } 9894 9895 /** 9896 * Output PDF File Header (7.5.2). 9897 * @protected 9898 */ 9899 protected function _putheader() { 9900 $this->_out('%PDF-'.$this->PDFVersion); 9901 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3)); 9902 } 9903 9904 /** 9905 * Output end of document (EOF). 9906 * @protected 9907 */ 9908 protected function _enddoc() { 9909 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) { 9910 // save subset chars of the previous font 9911 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 9912 } 9913 $this->state = 1; 9914 $this->_putheader(); 9915 $this->_putpages(); 9916 $this->_putresources(); 9917 // empty signature fields 9918 if (!empty($this->empty_signature_appearance)) { 9919 foreach ($this->empty_signature_appearance as $key => $esa) { 9920 // widget annotation for empty signature 9921 $out = $this->_getobj($esa['objid'])."\n"; 9922 $out .= '<< /Type /Annot'; 9923 $out .= ' /Subtype /Widget'; 9924 $out .= ' /Rect ['.$esa['rect'].']'; 9925 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page 9926 $out .= ' /F 4'; 9927 $out .= ' /FT /Sig'; 9928 $signame = $esa['name'].sprintf(' [%03d]', ($key + 1)); 9929 $out .= ' /T '.$this->_textstring($signame, $esa['objid']); 9930 $out .= ' /Ff 0'; 9931 $out .= ' >>'; 9932 $out .= "\n".'endobj'; 9933 $this->_out($out); 9934 } 9935 } 9936 // Signature 9937 if ($this->sign AND isset($this->signature_data['cert_type'])) { 9938 // widget annotation for signature 9939 $out = $this->_getobj($this->sig_obj_id)."\n"; 9940 $out .= '<< /Type /Annot'; 9941 $out .= ' /Subtype /Widget'; 9942 $out .= ' /Rect ['.$this->signature_appearance['rect'].']'; 9943 $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page 9944 $out .= ' /F 4'; 9945 $out .= ' /FT /Sig'; 9946 $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id); 9947 $out .= ' /Ff 0'; 9948 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R'; 9949 $out .= ' >>'; 9950 $out .= "\n".'endobj'; 9951 $this->_out($out); 9952 // signature 9953 $this->_putsignature(); 9954 } 9955 // Info 9956 $objid_info = $this->_putinfo(); 9957 // Catalog 9958 $objid_catalog = $this->_putcatalog(); 9959 // Cross-ref 9960 $o = $this->bufferlen; 9961 // XREF section 9962 $this->_out('xref'); 9963 $this->_out('0 '.($this->n + 1)); 9964 $this->_out('0000000000 65535 f '); 9965 $freegen = ($this->n + 2); 9966 for ($i=1; $i <= $this->n; ++$i) { 9967 if (!isset($this->offsets[$i]) AND ($i > 1)) { 9968 $this->_out(sprintf('0000000000 %05d f ', $freegen)); 9969 ++$freegen; 9970 } else { 9971 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 9972 } 9973 } 9974 // TRAILER 9975 $out = 'trailer'."\n"; 9976 $out .= '<<'; 9977 $out .= ' /Size '.($this->n + 1); 9978 $out .= ' /Root '.$objid_catalog.' 0 R'; 9979 $out .= ' /Info '.$objid_info.' 0 R'; 9980 if ($this->encrypted) { 9981 $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R'; 9982 } 9983 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]'; 9984 $out .= ' >>'; 9985 $this->_out($out); 9986 $this->_out('startxref'); 9987 $this->_out($o); 9988 $this->_out('%%EOF'); 9989 $this->state = 3; // end-of-doc 9990 } 9991 9992 /** 9993 * Initialize a new page. 9994 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 9995 * @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(). 9996 * @protected 9997 * @see getPageSizeFromFormat(), setPageFormat() 9998 */ 9999 protected function _beginpage($orientation='', $format='') { 10000 ++$this->page; 10001 $this->pageobjects[$this->page] = array(); 10002 $this->setPageBuffer($this->page, ''); 10003 // initialize array for graphics tranformation positions inside a page buffer 10004 $this->transfmrk[$this->page] = array(); 10005 $this->state = 2; 10006 if (TCPDF_STATIC::empty_string($orientation)) { 10007 if (isset($this->CurOrientation)) { 10008 $orientation = $this->CurOrientation; 10009 } elseif ($this->fwPt > $this->fhPt) { 10010 // landscape 10011 $orientation = 'L'; 10012 } else { 10013 // portrait 10014 $orientation = 'P'; 10015 } 10016 } 10017 if (TCPDF_STATIC::empty_string($format)) { 10018 $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)]; 10019 $this->setPageOrientation($orientation); 10020 } else { 10021 $this->setPageFormat($format, $orientation); 10022 } 10023 if ($this->rtl) { 10024 $this->x = $this->w - $this->rMargin; 10025 } else { 10026 $this->x = $this->lMargin; 10027 } 10028 $this->y = $this->tMargin; 10029 if (isset($this->newpagegroup[$this->page])) { 10030 // start a new group 10031 $this->currpagegroup = $this->newpagegroup[$this->page]; 10032 $this->pagegroups[$this->currpagegroup] = 1; 10033 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { 10034 ++$this->pagegroups[$this->currpagegroup]; 10035 } 10036 } 10037 10038 /** 10039 * Mark end of page. 10040 * @protected 10041 */ 10042 protected function _endpage() { 10043 $this->setVisibility('all'); 10044 $this->state = 1; 10045 } 10046 10047 /** 10048 * Begin a new object and return the object number. 10049 * @return int object number 10050 * @protected 10051 */ 10052 protected function _newobj() { 10053 $this->_out($this->_getobj()); 10054 return $this->n; 10055 } 10056 10057 /** 10058 * Return the starting object string for the selected object ID. 10059 * @param $objid (int) Object ID (leave empty to get a new ID). 10060 * @return string the starting object string 10061 * @protected 10062 * @since 5.8.009 (2010-08-20) 10063 */ 10064 protected function _getobj($objid='') { 10065 if ($objid === '') { 10066 ++$this->n; 10067 $objid = $this->n; 10068 } 10069 $this->offsets[$objid] = $this->bufferlen; 10070 $this->pageobjects[$this->page][] = $objid; 10071 return $objid.' 0 obj'; 10072 } 10073 10074 /** 10075 * Underline text. 10076 * @param $x (int) X coordinate 10077 * @param $y (int) Y coordinate 10078 * @param $txt (string) text to underline 10079 * @protected 10080 */ 10081 protected function _dounderline($x, $y, $txt) { 10082 $w = $this->GetStringWidth($txt); 10083 return $this->_dounderlinew($x, $y, $w); 10084 } 10085 10086 /** 10087 * Underline for rectangular text area. 10088 * @param $x (int) X coordinate 10089 * @param $y (int) Y coordinate 10090 * @param $w (int) width to underline 10091 * @protected 10092 * @since 4.8.008 (2009-09-29) 10093 */ 10094 protected function _dounderlinew($x, $y, $w) { 10095 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10096 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew); 10097 } 10098 10099 /** 10100 * Line through text. 10101 * @param $x (int) X coordinate 10102 * @param $y (int) Y coordinate 10103 * @param $txt (string) text to linethrough 10104 * @protected 10105 */ 10106 protected function _dolinethrough($x, $y, $txt) { 10107 $w = $this->GetStringWidth($txt); 10108 return $this->_dolinethroughw($x, $y, $w); 10109 } 10110 10111 /** 10112 * Line through for rectangular text area. 10113 * @param $x (int) X coordinate 10114 * @param $y (int) Y coordinate 10115 * @param $w (int) line length (width) 10116 * @protected 10117 * @since 4.9.008 (2009-09-29) 10118 */ 10119 protected function _dolinethroughw($x, $y, $w) { 10120 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10121 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew); 10122 } 10123 10124 /** 10125 * Overline text. 10126 * @param $x (int) X coordinate 10127 * @param $y (int) Y coordinate 10128 * @param $txt (string) text to overline 10129 * @protected 10130 * @since 4.9.015 (2010-04-19) 10131 */ 10132 protected function _dooverline($x, $y, $txt) { 10133 $w = $this->GetStringWidth($txt); 10134 return $this->_dooverlinew($x, $y, $w); 10135 } 10136 10137 /** 10138 * Overline for rectangular text area. 10139 * @param $x (int) X coordinate 10140 * @param $y (int) Y coordinate 10141 * @param $w (int) width to overline 10142 * @protected 10143 * @since 4.9.015 (2010-04-19) 10144 */ 10145 protected function _dooverlinew($x, $y, $w) { 10146 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10147 return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew); 10148 10149 } 10150 10151 /** 10152 * Format a data string for meta information 10153 * @param $s (string) data string to escape. 10154 * @param $n (int) object ID 10155 * @return string escaped string. 10156 * @protected 10157 */ 10158 protected function _datastring($s, $n=0) { 10159 if ($n == 0) { 10160 $n = $this->n; 10161 } 10162 $s = $this->_encrypt_data($n, $s); 10163 return '('. TCPDF_STATIC::_escape($s).')'; 10164 } 10165 10166 /** 10167 * Set the document creation timestamp 10168 * @param $time (mixed) Document creation timestamp in seconds or date-time string. 10169 * @public 10170 * @since 5.9.152 (2012-03-23) 10171 */ 10172 public function setDocCreationTimestamp($time) { 10173 if (is_string($time)) { 10174 $time = TCPDF_STATIC::getTimestamp($time); 10175 } 10176 $this->doc_creation_timestamp = intval($time); 10177 } 10178 10179 /** 10180 * Set the document modification timestamp 10181 * @param $time (mixed) Document modification timestamp in seconds or date-time string. 10182 * @public 10183 * @since 5.9.152 (2012-03-23) 10184 */ 10185 public function setDocModificationTimestamp($time) { 10186 if (is_string($time)) { 10187 $time = TCPDF_STATIC::getTimestamp($time); 10188 } 10189 $this->doc_modification_timestamp = intval($time); 10190 } 10191 10192 /** 10193 * Returns document creation timestamp in seconds. 10194 * @return (int) Creation timestamp in seconds. 10195 * @public 10196 * @since 5.9.152 (2012-03-23) 10197 */ 10198 public function getDocCreationTimestamp() { 10199 return $this->doc_creation_timestamp; 10200 } 10201 10202 /** 10203 * Returns document modification timestamp in seconds. 10204 * @return (int) Modfication timestamp in seconds. 10205 * @public 10206 * @since 5.9.152 (2012-03-23) 10207 */ 10208 public function getDocModificationTimestamp() { 10209 return $this->doc_modification_timestamp; 10210 } 10211 10212 /** 10213 * Returns a formatted date for meta information 10214 * @param $n (int) Object ID. 10215 * @param $timestamp (int) Timestamp to convert. 10216 * @return string escaped date string. 10217 * @protected 10218 * @since 4.6.028 (2009-08-25) 10219 */ 10220 protected function _datestring($n=0, $timestamp=0) { 10221 if ((empty($timestamp)) OR ($timestamp < 0)) { 10222 $timestamp = $this->doc_creation_timestamp; 10223 } 10224 return $this->_datastring('D:'.TCPDF_STATIC::getFormattedDate($timestamp), $n); 10225 } 10226 10227 /** 10228 * Format a text string for meta information 10229 * @param $s (string) string to escape. 10230 * @param $n (int) object ID 10231 * @return string escaped string. 10232 * @protected 10233 */ 10234 protected function _textstring($s, $n=0) { 10235 if ($this->isunicode) { 10236 //Convert string to UTF-16BE 10237 $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont); 10238 } 10239 return $this->_datastring($s, $n); 10240 } 10241 10242 /** 10243 * get raw output stream. 10244 * @param $s (string) string to output. 10245 * @param $n (int) object reference for encryption mode 10246 * @protected 10247 * @author Nicola Asuni 10248 * @since 5.5.000 (2010-06-22) 10249 */ 10250 protected function _getrawstream($s, $n=0) { 10251 if ($n <= 0) { 10252 // default to current object 10253 $n = $this->n; 10254 } 10255 return $this->_encrypt_data($n, $s); 10256 } 10257 10258 /** 10259 * Output a string to the document. 10260 * @param $s (string) string to output. 10261 * @protected 10262 */ 10263 protected function _out($s) { 10264 if ($this->state == 2) { 10265 if ($this->inxobj) { 10266 // we are inside an XObject template 10267 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n"; 10268 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { 10269 // puts data before page footer 10270 $pagebuff = $this->getPageBuffer($this->page); 10271 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); 10272 $footer = substr($pagebuff, -$this->footerlen[$this->page]); 10273 $this->setPageBuffer($this->page, $page.$s."\n".$footer); 10274 // update footer position 10275 $this->footerpos[$this->page] += strlen($s."\n"); 10276 } else { 10277 // set page data 10278 $this->setPageBuffer($this->page, $s."\n", true); 10279 } 10280 } elseif ($this->state > 0) { 10281 // set general data 10282 $this->setBuffer($s."\n"); 10283 } 10284 } 10285 10286 /** 10287 * Set header font. 10288 * @param $font (array) Array describing the basic font parameters: (family, style, size). 10289 * @public 10290 * @since 1.1 10291 */ 10292 public function setHeaderFont($font) { 10293 $this->header_font = $font; 10294 } 10295 10296 /** 10297 * Get header font. 10298 * @return array() Array describing the basic font parameters: (family, style, size). 10299 * @public 10300 * @since 4.0.012 (2008-07-24) 10301 */ 10302 public function getHeaderFont() { 10303 return $this->header_font; 10304 } 10305 10306 /** 10307 * Set footer font. 10308 * @param $font (array) Array describing the basic font parameters: (family, style, size). 10309 * @public 10310 * @since 1.1 10311 */ 10312 public function setFooterFont($font) { 10313 $this->footer_font = $font; 10314 } 10315 10316 /** 10317 * Get Footer font. 10318 * @return array() Array describing the basic font parameters: (family, style, size). 10319 * @public 10320 * @since 4.0.012 (2008-07-24) 10321 */ 10322 public function getFooterFont() { 10323 return $this->footer_font; 10324 } 10325 10326 /** 10327 * Set language array. 10328 * @param $language (array) 10329 * @public 10330 * @since 1.1 10331 */ 10332 public function setLanguageArray($language) { 10333 $this->l = $language; 10334 if (isset($this->l['a_meta_dir'])) { 10335 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; 10336 } else { 10337 $this->rtl = false; 10338 } 10339 } 10340 10341 /** 10342 * Returns the PDF data. 10343 * @public 10344 */ 10345 public function getPDFData() { 10346 if ($this->state < 3) { 10347 $this->Close(); 10348 } 10349 return $this->buffer; 10350 } 10351 10352 /** 10353 * Output anchor link. 10354 * @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>) 10355 * @param $name (string) link name 10356 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 10357 * @param $firstline (boolean) if true prints only the first line and return the remaining string. 10358 * @param $color (array) array of RGB text color 10359 * @param $style (string) font style (U, D, B, I) 10360 * @param $firstblock (boolean) if true the string is the starting of a line. 10361 * @return the number of cells used or the remaining text if $firstline = true; 10362 * @public 10363 */ 10364 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) { 10365 if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) { 10366 // convert url to internal link 10367 $lnkdata = explode(',', $url); 10368 if (isset($lnkdata[0]) ) { 10369 $page = substr($lnkdata[0], 1); 10370 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { 10371 $lnky = floatval($lnkdata[1]); 10372 } else { 10373 $lnky = 0; 10374 } 10375 $url = $this->AddLink(); 10376 $this->SetLink($url, $lnky, $page); 10377 } 10378 } 10379 // store current settings 10380 $prevcolor = $this->fgcolor; 10381 $prevstyle = $this->FontStyle; 10382 if (empty($color)) { 10383 $this->SetTextColorArray($this->htmlLinkColorArray); 10384 } else { 10385 $this->SetTextColorArray($color); 10386 } 10387 if ($style == -1) { 10388 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle); 10389 } else { 10390 $this->SetFont('', $this->FontStyle.$style); 10391 } 10392 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0); 10393 // restore settings 10394 $this->SetFont('', $prevstyle); 10395 $this->SetTextColorArray($prevcolor); 10396 return $ret; 10397 } 10398 10399 /** 10400 * Converts pixels to User's Units. 10401 * @param $px (int) pixels 10402 * @return float value in user's unit 10403 * @public 10404 * @see setImageScale(), getImageScale() 10405 */ 10406 public function pixelsToUnits($px) { 10407 return ($px / ($this->imgscale * $this->k)); 10408 } 10409 10410 /** 10411 * Reverse function for htmlentities. 10412 * Convert entities in UTF-8. 10413 * @param $text_to_convert (string) Text to convert. 10414 * @return string converted text string 10415 * @public 10416 */ 10417 public function unhtmlentities($text_to_convert) { 10418 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); 10419 } 10420 10421 // ENCRYPTION METHODS ---------------------------------- 10422 10423 /** 10424 * Compute encryption key depending on object number where the encrypted data is stored. 10425 * This is used for all strings and streams without crypt filter specifier. 10426 * @param $n (int) object number 10427 * @return int object key 10428 * @protected 10429 * @author Nicola Asuni 10430 * @since 2.0.000 (2008-01-02) 10431 */ 10432 protected function _objectkey($n) { 10433 $objkey = $this->encryptdata['key'].pack('VXxx', $n); 10434 if ($this->encryptdata['mode'] == 2) { // AES-128 10435 // AES padding 10436 $objkey .= "\x73\x41\x6C\x54"; // sAlT 10437 } 10438 $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5)); 10439 $objkey = substr($objkey, 0, 16); 10440 return $objkey; 10441 } 10442 10443 /** 10444 * Encrypt the input string. 10445 * @param $n (int) object number 10446 * @param $s (string) data string to encrypt 10447 * @return encrypted string 10448 * @protected 10449 * @author Nicola Asuni 10450 * @since 5.0.005 (2010-05-11) 10451 */ 10452 protected function _encrypt_data($n, $s) { 10453 if (!$this->encrypted) { 10454 return $s; 10455 } 10456 switch ($this->encryptdata['mode']) { 10457 case 0: // RC4-40 10458 case 1: { // RC4-128 10459 $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c); 10460 break; 10461 } 10462 case 2: { // AES-128 10463 $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s); 10464 break; 10465 } 10466 case 3: { // AES-256 10467 $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s); 10468 break; 10469 } 10470 } 10471 return $s; 10472 } 10473 10474 /** 10475 * Put encryption on PDF document. 10476 * @protected 10477 * @author Nicola Asuni 10478 * @since 2.0.000 (2008-01-02) 10479 */ 10480 protected function _putencryption() { 10481 if (!$this->encrypted) { 10482 return; 10483 } 10484 $this->encryptdata['objid'] = $this->_newobj(); 10485 $out = '<<'; 10486 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) { 10487 $this->encryptdata['Filter'] = 'Standard'; 10488 } 10489 $out .= ' /Filter /'.$this->encryptdata['Filter']; 10490 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) { 10491 $out .= ' /SubFilter /'.$this->encryptdata['SubFilter']; 10492 } 10493 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) { 10494 $this->encryptdata['V'] = 1; 10495 } 10496 // V is a code specifying the algorithm to be used in encrypting and decrypting the document 10497 $out .= ' /V '.$this->encryptdata['V']; 10498 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) { 10499 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256 10500 $out .= ' /Length '.$this->encryptdata['Length']; 10501 } else { 10502 $out .= ' /Length 40'; 10503 } 10504 if ($this->encryptdata['V'] >= 4) { 10505 if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) { 10506 $this->encryptdata['StmF'] = 'Identity'; 10507 } 10508 if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) { 10509 // The name of the crypt filter that shall be used when decrypting all strings in the document. 10510 $this->encryptdata['StrF'] = 'Identity'; 10511 } 10512 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries. 10513 if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) { 10514 $out .= ' /CF <<'; 10515 $out .= ' /'.$this->encryptdata['StmF'].' <<'; 10516 $out .= ' /Type /CryptFilter'; 10517 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) { 10518 // The method used 10519 $out .= ' /CFM /'.$this->encryptdata['CF']['CFM']; 10520 if ($this->encryptdata['pubkey']) { 10521 $out .= ' /Recipients ['; 10522 foreach ($this->encryptdata['Recipients'] as $rec) { 10523 $out .= ' <'.$rec.'>'; 10524 } 10525 $out .= ' ]'; 10526 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { 10527 $out .= ' /EncryptMetadata false'; 10528 } else { 10529 $out .= ' /EncryptMetadata true'; 10530 } 10531 } 10532 } else { 10533 $out .= ' /CFM /None'; 10534 } 10535 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) { 10536 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter. 10537 $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent']; 10538 } else { 10539 $out .= ' /AuthEvent /DocOpen'; 10540 } 10541 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) { 10542 // The bit length of the encryption key. 10543 $out .= ' /Length '.$this->encryptdata['CF']['Length']; 10544 } 10545 $out .= ' >> >>'; 10546 } 10547 // The name of the crypt filter that shall be used by default when decrypting streams. 10548 $out .= ' /StmF /'.$this->encryptdata['StmF']; 10549 // The name of the crypt filter that shall be used when decrypting all strings in the document. 10550 $out .= ' /StrF /'.$this->encryptdata['StrF']; 10551 if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) { 10552 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier. 10553 $out .= ' /EFF /'.$this->encryptdata['']; 10554 } 10555 } 10556 // Additional encryption dictionary entries for the standard security handler 10557 if ($this->encryptdata['pubkey']) { 10558 if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) { 10559 $out .= ' /Recipients ['; 10560 foreach ($this->encryptdata['Recipients'] as $rec) { 10561 $out .= ' <'.$rec.'>'; 10562 } 10563 $out .= ' ]'; 10564 } 10565 } else { 10566 $out .= ' /R'; 10567 if ($this->encryptdata['V'] == 5) { // AES-256 10568 $out .= ' 5'; 10569 $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')'; 10570 $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')'; 10571 $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')'; 10572 } elseif ($this->encryptdata['V'] == 4) { // AES-128 10573 $out .= ' 4'; 10574 } elseif ($this->encryptdata['V'] < 2) { // RC-40 10575 $out .= ' 2'; 10576 } else { // RC-128 10577 $out .= ' 3'; 10578 } 10579 $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')'; 10580 $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')'; 10581 $out .= ' /P '.$this->encryptdata['P']; 10582 if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) { 10583 $out .= ' /EncryptMetadata false'; 10584 } else { 10585 $out .= ' /EncryptMetadata true'; 10586 } 10587 } 10588 $out .= ' >>'; 10589 $out .= "\n".'endobj'; 10590 $this->_out($out); 10591 } 10592 10593 /** 10594 * Compute U value (used for encryption) 10595 * @return string U value 10596 * @protected 10597 * @since 2.0.000 (2008-01-02) 10598 * @author Nicola Asuni 10599 */ 10600 protected function _Uvalue() { 10601 if ($this->encryptdata['mode'] == 0) { // RC4-40 10602 return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c); 10603 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128 10604 $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']); 10605 $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c); 10606 $len = strlen($tmp); 10607 for ($i = 1; $i <= 19; ++$i) { 10608 $ek = ''; 10609 for ($j = 0; $j < $len; ++$j) { 10610 $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i); 10611 } 10612 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c); 10613 } 10614 $enc .= str_repeat("\x00", 16); 10615 return substr($enc, 0, 32); 10616 } elseif ($this->encryptdata['mode'] == 3) { // AES-256 10617 $seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed()); 10618 // User Validation Salt 10619 $this->encryptdata['UVS'] = substr($seed, 0, 8); 10620 // User Key Salt 10621 $this->encryptdata['UKS'] = substr($seed, 8, 16); 10622 return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS']; 10623 } 10624 } 10625 10626 /** 10627 * Compute UE value (used for encryption) 10628 * @return string UE value 10629 * @protected 10630 * @since 5.9.006 (2010-10-19) 10631 * @author Nicola Asuni 10632 */ 10633 protected function _UEvalue() { 10634 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true); 10635 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']); 10636 } 10637 10638 /** 10639 * Compute O value (used for encryption) 10640 * @return string O value 10641 * @protected 10642 * @since 2.0.000 (2008-01-02) 10643 * @author Nicola Asuni 10644 */ 10645 protected function _Ovalue() { 10646 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128 10647 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']); 10648 if ($this->encryptdata['mode'] > 0) { 10649 for ($i = 0; $i < 50; ++$i) { 10650 $tmp = TCPDF_STATIC::_md5_16($tmp); 10651 } 10652 } 10653 $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8)); 10654 $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c); 10655 if ($this->encryptdata['mode'] > 0) { 10656 $len = strlen($owner_key); 10657 for ($i = 1; $i <= 19; ++$i) { 10658 $ek = ''; 10659 for ($j = 0; $j < $len; ++$j) { 10660 $ek .= chr(ord($owner_key[$j]) ^ $i); 10661 } 10662 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c); 10663 } 10664 } 10665 return $enc; 10666 } elseif ($this->encryptdata['mode'] == 3) { // AES-256 10667 $seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed()); 10668 // Owner Validation Salt 10669 $this->encryptdata['OVS'] = substr($seed, 0, 8); 10670 // Owner Key Salt 10671 $this->encryptdata['OKS'] = substr($seed, 8, 16); 10672 return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS']; 10673 } 10674 } 10675 10676 /** 10677 * Compute OE value (used for encryption) 10678 * @return string OE value 10679 * @protected 10680 * @since 5.9.006 (2010-10-19) 10681 * @author Nicola Asuni 10682 */ 10683 protected function _OEvalue() { 10684 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true); 10685 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']); 10686 } 10687 10688 /** 10689 * Convert password for AES-256 encryption mode 10690 * @param $password (string) password 10691 * @return string password 10692 * @protected 10693 * @since 5.9.006 (2010-10-19) 10694 * @author Nicola Asuni 10695 */ 10696 protected function _fixAES256Password($password) { 10697 $psw = ''; // password to be returned 10698 $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont); 10699 foreach ($psw_array as $c) { 10700 $psw .= TCPDF_FONTS::unichr($c, $this->isunicode); 10701 } 10702 return substr($psw, 0, 127); 10703 } 10704 10705 /** 10706 * Compute encryption key 10707 * @protected 10708 * @since 2.0.000 (2008-01-02) 10709 * @author Nicola Asuni 10710 */ 10711 protected function _generateencryptionkey() { 10712 $keybytelen = ($this->encryptdata['Length'] / 8); 10713 if (!$this->encryptdata['pubkey']) { // standard mode 10714 if ($this->encryptdata['mode'] == 3) { // AES-256 10715 // generate 256 bit random key 10716 $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen); 10717 // truncate passwords 10718 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']); 10719 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']); 10720 // Compute U value 10721 $this->encryptdata['U'] = $this->_Uvalue(); 10722 // Compute UE value 10723 $this->encryptdata['UE'] = $this->_UEvalue(); 10724 // Compute O value 10725 $this->encryptdata['O'] = $this->_Ovalue(); 10726 // Compute OE value 10727 $this->encryptdata['OE'] = $this->_OEvalue(); 10728 // Compute P value 10729 $this->encryptdata['P'] = $this->encryptdata['protection']; 10730 // Computing the encryption dictionary's Perms (permissions) value 10731 $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3 10732 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7 10733 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8 10734 $perms .= 'F'; 10735 } else { 10736 $perms .= 'T'; 10737 } 10738 $perms .= 'adb'; // bytes 9-11 10739 $perms .= 'nick'; // bytes 12-15 10740 $this->encryptdata['perms'] = TCPDF_STATIC::_AESnopad($this->encryptdata['key'], $perms); 10741 } else { // RC4-40, RC4-128, AES-128 10742 // Pad passwords 10743 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32); 10744 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32); 10745 // Compute O value 10746 $this->encryptdata['O'] = $this->_Ovalue(); 10747 // get default permissions (reverse byte order) 10748 $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); 10749 // Compute encryption key 10750 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']); 10751 if ($this->encryptdata['mode'] > 0) { 10752 for ($i = 0; $i < 50; ++$i) { 10753 $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen)); 10754 } 10755 } 10756 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen); 10757 // Compute U value 10758 $this->encryptdata['U'] = $this->_Uvalue(); 10759 // Compute P value 10760 $this->encryptdata['P'] = $this->encryptdata['protection']; 10761 } 10762 } else { // Public-Key mode 10763 // random 20-byte seed 10764 $seed = sha1(TCPDF_STATIC::getRandomSeed(), true); 10765 $recipient_bytes = ''; 10766 foreach ($this->encryptdata['pubkeys'] as $pubkey) { 10767 // for each public certificate 10768 if (isset($pubkey['p'])) { 10769 $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']); 10770 } else { 10771 $pkprotection = $this->encryptdata['protection']; 10772 } 10773 // get default permissions (reverse byte order) 10774 $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection); 10775 // envelope data 10776 $envelope = $seed.$pkpermissions; 10777 // write the envelope data to a temporary file 10778 $tempkeyfile = TCPDF_STATIC::getObjFilename('key', $this->file_id); 10779 $f = TCPDF_STATIC::fopenLocal($tempkeyfile, 'wb'); 10780 if (!$f) { 10781 $this->Error('Unable to create temporary key file: '.$tempkeyfile); 10782 } 10783 $envelope_length = strlen($envelope); 10784 fwrite($f, $envelope, $envelope_length); 10785 fclose($f); 10786 $tempencfile = TCPDF_STATIC::getObjFilename('enc', $this->file_id); 10787 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) { 10788 $this->Error('Unable to encrypt the file: '.$tempkeyfile); 10789 } 10790 // read encryption signature 10791 $signature = file_get_contents($tempencfile, false, null, $envelope_length); 10792 // extract signature 10793 $signature = substr($signature, strpos($signature, 'Content-Disposition')); 10794 $tmparr = explode("\n\n", $signature); 10795 $signature = trim($tmparr[1]); 10796 unset($tmparr); 10797 // decode signature 10798 $signature = base64_decode($signature); 10799 // convert signature to hex 10800 $hexsignature = current(unpack('H*', $signature)); 10801 // store signature on recipients array 10802 $this->encryptdata['Recipients'][] = $hexsignature; 10803 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array 10804 $recipient_bytes .= $signature; 10805 } 10806 // calculate encryption key 10807 if ($this->encryptdata['mode'] == 3) { // AES-256 10808 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen); 10809 } else { // RC4-40, RC4-128, AES-128 10810 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen); 10811 } 10812 } 10813 } 10814 10815 /** 10816 * Set document protection 10817 * Remark: the protection against modification is for people who have the full Acrobat product. 10818 * 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. 10819 * 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. 10820 * @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> 10821 * @param $user_pass (String) user password. Empty by default. 10822 * @param $owner_pass (String) owner password. If not specified, a random value is used. 10823 * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit. 10824 * @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'))) 10825 * @public 10826 * @since 2.0.000 (2008-01-02) 10827 * @author Nicola Asuni 10828 */ 10829 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) { 10830 if ($this->pdfa_mode) { 10831 // encryption is not allowed in PDF/A mode 10832 return; 10833 } 10834 $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode); 10835 if (($pubkeys !== null) AND (is_array($pubkeys))) { 10836 // public-key mode 10837 $this->encryptdata['pubkeys'] = $pubkeys; 10838 if ($mode == 0) { 10839 // public-Key Security requires at least 128 bit 10840 $mode = 1; 10841 } 10842 if (!function_exists('openssl_pkcs7_encrypt')) { 10843 $this->Error('Public-Key Security requires openssl library.'); 10844 } 10845 // Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec) 10846 $this->encryptdata['pubkey'] = true; 10847 $this->encryptdata['Filter'] = 'Adobe.PubSec'; 10848 $this->encryptdata['StmF'] = 'DefaultCryptFilter'; 10849 $this->encryptdata['StrF'] = 'DefaultCryptFilter'; 10850 } else { 10851 // standard mode (password mode) 10852 $this->encryptdata['pubkey'] = false; 10853 $this->encryptdata['Filter'] = 'Standard'; 10854 $this->encryptdata['StmF'] = 'StdCF'; 10855 $this->encryptdata['StrF'] = 'StdCF'; 10856 } 10857 if ($mode > 1) { // AES 10858 if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) { 10859 $this->Error('AES encryption requires openssl or mcrypt extension (http://www.php.net/manual/en/mcrypt.requirements.php).'); 10860 } 10861 if (extension_loaded('openssl') && !in_array('aes-256-cbc', openssl_get_cipher_methods())) { 10862 $this->Error('AES encryption requires openssl/aes-256-cbc cypher.'); 10863 } 10864 if (extension_loaded('mcrypt') && mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) { 10865 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.'); 10866 } 10867 if (($mode == 3) AND !function_exists('hash')) { 10868 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2. 10869 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).'); 10870 } 10871 } 10872 if ($owner_pass === null) { 10873 $owner_pass = md5(TCPDF_STATIC::getRandomSeed()); 10874 } 10875 $this->encryptdata['user_password'] = $user_pass; 10876 $this->encryptdata['owner_password'] = $owner_pass; 10877 $this->encryptdata['mode'] = $mode; 10878 switch ($mode) { 10879 case 0: { // RC4 40 bit 10880 $this->encryptdata['V'] = 1; 10881 $this->encryptdata['Length'] = 40; 10882 $this->encryptdata['CF']['CFM'] = 'V2'; 10883 break; 10884 } 10885 case 1: { // RC4 128 bit 10886 $this->encryptdata['V'] = 2; 10887 $this->encryptdata['Length'] = 128; 10888 $this->encryptdata['CF']['CFM'] = 'V2'; 10889 if ($this->encryptdata['pubkey']) { 10890 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4'; 10891 $this->encryptdata['Recipients'] = array(); 10892 } 10893 break; 10894 } 10895 case 2: { // AES 128 bit 10896 $this->encryptdata['V'] = 4; 10897 $this->encryptdata['Length'] = 128; 10898 $this->encryptdata['CF']['CFM'] = 'AESV2'; 10899 $this->encryptdata['CF']['Length'] = 128; 10900 if ($this->encryptdata['pubkey']) { 10901 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; 10902 $this->encryptdata['Recipients'] = array(); 10903 } 10904 break; 10905 } 10906 case 3: { // AES 256 bit 10907 $this->encryptdata['V'] = 5; 10908 $this->encryptdata['Length'] = 256; 10909 $this->encryptdata['CF']['CFM'] = 'AESV3'; 10910 $this->encryptdata['CF']['Length'] = 256; 10911 if ($this->encryptdata['pubkey']) { 10912 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; 10913 $this->encryptdata['Recipients'] = array(); 10914 } 10915 break; 10916 } 10917 } 10918 $this->encrypted = true; 10919 $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id); 10920 $this->_generateencryptionkey(); 10921 } 10922 10923 // END OF ENCRYPTION FUNCTIONS ------------------------- 10924 10925 // START TRANSFORMATIONS SECTION ----------------------- 10926 10927 /** 10928 * Starts a 2D tranformation saving current graphic state. 10929 * This function must be called before scaling, mirroring, translation, rotation and skewing. 10930 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 10931 * @public 10932 * @since 2.1.000 (2008-01-07) 10933 * @see StartTransform(), StopTransform() 10934 */ 10935 public function StartTransform() { 10936 if ($this->state != 2) { 10937 return; 10938 } 10939 $this->_outSaveGraphicsState(); 10940 if ($this->inxobj) { 10941 // we are inside an XObject template 10942 $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']); 10943 } else { 10944 $this->transfmrk[$this->page][] = $this->pagelen[$this->page]; 10945 } 10946 ++$this->transfmatrix_key; 10947 $this->transfmatrix[$this->transfmatrix_key] = array(); 10948 } 10949 10950 /** 10951 * Stops a 2D tranformation restoring previous graphic state. 10952 * This function must be called after scaling, mirroring, translation, rotation and skewing. 10953 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 10954 * @public 10955 * @since 2.1.000 (2008-01-07) 10956 * @see StartTransform(), StopTransform() 10957 */ 10958 public function StopTransform() { 10959 if ($this->state != 2) { 10960 return; 10961 } 10962 $this->_outRestoreGraphicsState(); 10963 if (isset($this->transfmatrix[$this->transfmatrix_key])) { 10964 array_pop($this->transfmatrix[$this->transfmatrix_key]); 10965 --$this->transfmatrix_key; 10966 } 10967 if ($this->inxobj) { 10968 // we are inside an XObject template 10969 array_pop($this->xobjects[$this->xobjid]['transfmrk']); 10970 } else { 10971 array_pop($this->transfmrk[$this->page]); 10972 } 10973 } 10974 /** 10975 * Horizontal Scaling. 10976 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed. 10977 * @param $x (int) abscissa of the scaling center. Default is current x position 10978 * @param $y (int) ordinate of the scaling center. Default is current y position 10979 * @public 10980 * @since 2.1.000 (2008-01-07) 10981 * @see StartTransform(), StopTransform() 10982 */ 10983 public function ScaleX($s_x, $x='', $y='') { 10984 $this->Scale($s_x, 100, $x, $y); 10985 } 10986 10987 /** 10988 * Vertical Scaling. 10989 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed. 10990 * @param $x (int) abscissa of the scaling center. Default is current x position 10991 * @param $y (int) ordinate of the scaling center. Default is current y position 10992 * @public 10993 * @since 2.1.000 (2008-01-07) 10994 * @see StartTransform(), StopTransform() 10995 */ 10996 public function ScaleY($s_y, $x='', $y='') { 10997 $this->Scale(100, $s_y, $x, $y); 10998 } 10999 11000 /** 11001 * Vertical and horizontal proportional Scaling. 11002 * @param $s (float) scaling factor for width and height as percent. 0 is not allowed. 11003 * @param $x (int) abscissa of the scaling center. Default is current x position 11004 * @param $y (int) ordinate of the scaling center. Default is current y position 11005 * @public 11006 * @since 2.1.000 (2008-01-07) 11007 * @see StartTransform(), StopTransform() 11008 */ 11009 public function ScaleXY($s, $x='', $y='') { 11010 $this->Scale($s, $s, $x, $y); 11011 } 11012 11013 /** 11014 * Vertical and horizontal non-proportional Scaling. 11015 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed. 11016 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed. 11017 * @param $x (int) abscissa of the scaling center. Default is current x position 11018 * @param $y (int) ordinate of the scaling center. Default is current y position 11019 * @public 11020 * @since 2.1.000 (2008-01-07) 11021 * @see StartTransform(), StopTransform() 11022 */ 11023 public function Scale($s_x, $s_y, $x='', $y='') { 11024 if ($x === '') { 11025 $x = $this->x; 11026 } 11027 if ($y === '') { 11028 $y = $this->y; 11029 } 11030 if (($s_x == 0) OR ($s_y == 0)) { 11031 $this->Error('Please do not use values equal to zero for scaling'); 11032 } 11033 $y = ($this->h - $y) * $this->k; 11034 $x *= $this->k; 11035 //calculate elements of transformation matrix 11036 $s_x /= 100; 11037 $s_y /= 100; 11038 $tm = array(); 11039 $tm[0] = $s_x; 11040 $tm[1] = 0; 11041 $tm[2] = 0; 11042 $tm[3] = $s_y; 11043 $tm[4] = $x * (1 - $s_x); 11044 $tm[5] = $y * (1 - $s_y); 11045 //scale the coordinate system 11046 $this->Transform($tm); 11047 } 11048 11049 /** 11050 * Horizontal Mirroring. 11051 * @param $x (int) abscissa of the point. Default is current x position 11052 * @public 11053 * @since 2.1.000 (2008-01-07) 11054 * @see StartTransform(), StopTransform() 11055 */ 11056 public function MirrorH($x='') { 11057 $this->Scale(-100, 100, $x); 11058 } 11059 11060 /** 11061 * Verical Mirroring. 11062 * @param $y (int) ordinate of the point. Default is current y position 11063 * @public 11064 * @since 2.1.000 (2008-01-07) 11065 * @see StartTransform(), StopTransform() 11066 */ 11067 public function MirrorV($y='') { 11068 $this->Scale(100, -100, '', $y); 11069 } 11070 11071 /** 11072 * Point reflection mirroring. 11073 * @param $x (int) abscissa of the point. Default is current x position 11074 * @param $y (int) ordinate of the point. Default is current y position 11075 * @public 11076 * @since 2.1.000 (2008-01-07) 11077 * @see StartTransform(), StopTransform() 11078 */ 11079 public function MirrorP($x='',$y='') { 11080 $this->Scale(-100, -100, $x, $y); 11081 } 11082 11083 /** 11084 * Reflection against a straight line through point (x, y) with the gradient angle (angle). 11085 * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line). 11086 * @param $x (int) abscissa of the point. Default is current x position 11087 * @param $y (int) ordinate of the point. Default is current y position 11088 * @public 11089 * @since 2.1.000 (2008-01-07) 11090 * @see StartTransform(), StopTransform() 11091 */ 11092 public function MirrorL($angle=0, $x='',$y='') { 11093 $this->Scale(-100, 100, $x, $y); 11094 $this->Rotate(-2*($angle-90), $x, $y); 11095 } 11096 11097 /** 11098 * Translate graphic object horizontally. 11099 * @param $t_x (int) movement to the right (or left for RTL) 11100 * @public 11101 * @since 2.1.000 (2008-01-07) 11102 * @see StartTransform(), StopTransform() 11103 */ 11104 public function TranslateX($t_x) { 11105 $this->Translate($t_x, 0); 11106 } 11107 11108 /** 11109 * Translate graphic object vertically. 11110 * @param $t_y (int) movement to the bottom 11111 * @public 11112 * @since 2.1.000 (2008-01-07) 11113 * @see StartTransform(), StopTransform() 11114 */ 11115 public function TranslateY($t_y) { 11116 $this->Translate(0, $t_y); 11117 } 11118 11119 /** 11120 * Translate graphic object horizontally and vertically. 11121 * @param $t_x (int) movement to the right 11122 * @param $t_y (int) movement to the bottom 11123 * @public 11124 * @since 2.1.000 (2008-01-07) 11125 * @see StartTransform(), StopTransform() 11126 */ 11127 public function Translate($t_x, $t_y) { 11128 //calculate elements of transformation matrix 11129 $tm = array(); 11130 $tm[0] = 1; 11131 $tm[1] = 0; 11132 $tm[2] = 0; 11133 $tm[3] = 1; 11134 $tm[4] = $t_x * $this->k; 11135 $tm[5] = -$t_y * $this->k; 11136 //translate the coordinate system 11137 $this->Transform($tm); 11138 } 11139 11140 /** 11141 * Rotate object. 11142 * @param $angle (float) angle in degrees for counter-clockwise rotation 11143 * @param $x (int) abscissa of the rotation center. Default is current x position 11144 * @param $y (int) ordinate of the rotation center. Default is current y position 11145 * @public 11146 * @since 2.1.000 (2008-01-07) 11147 * @see StartTransform(), StopTransform() 11148 */ 11149 public function Rotate($angle, $x='', $y='') { 11150 if ($x === '') { 11151 $x = $this->x; 11152 } 11153 if ($y === '') { 11154 $y = $this->y; 11155 } 11156 $y = ($this->h - $y) * $this->k; 11157 $x *= $this->k; 11158 //calculate elements of transformation matrix 11159 $tm = array(); 11160 $tm[0] = cos(deg2rad($angle)); 11161 $tm[1] = sin(deg2rad($angle)); 11162 $tm[2] = -$tm[1]; 11163 $tm[3] = $tm[0]; 11164 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x); 11165 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x); 11166 //rotate the coordinate system around ($x,$y) 11167 $this->Transform($tm); 11168 } 11169 11170 /** 11171 * Skew horizontally. 11172 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right) 11173 * @param $x (int) abscissa of the skewing center. default is current x position 11174 * @param $y (int) ordinate of the skewing center. default is current y position 11175 * @public 11176 * @since 2.1.000 (2008-01-07) 11177 * @see StartTransform(), StopTransform() 11178 */ 11179 public function SkewX($angle_x, $x='', $y='') { 11180 $this->Skew($angle_x, 0, $x, $y); 11181 } 11182 11183 /** 11184 * Skew vertically. 11185 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 11186 * @param $x (int) abscissa of the skewing center. default is current x position 11187 * @param $y (int) ordinate of the skewing center. default is current y position 11188 * @public 11189 * @since 2.1.000 (2008-01-07) 11190 * @see StartTransform(), StopTransform() 11191 */ 11192 public function SkewY($angle_y, $x='', $y='') { 11193 $this->Skew(0, $angle_y, $x, $y); 11194 } 11195 11196 /** 11197 * Skew. 11198 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right) 11199 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 11200 * @param $x (int) abscissa of the skewing center. default is current x position 11201 * @param $y (int) ordinate of the skewing center. default is current y position 11202 * @public 11203 * @since 2.1.000 (2008-01-07) 11204 * @see StartTransform(), StopTransform() 11205 */ 11206 public function Skew($angle_x, $angle_y, $x='', $y='') { 11207 if ($x === '') { 11208 $x = $this->x; 11209 } 11210 if ($y === '') { 11211 $y = $this->y; 11212 } 11213 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) { 11214 $this->Error('Please use values between -90 and +90 degrees for Skewing.'); 11215 } 11216 $x *= $this->k; 11217 $y = ($this->h - $y) * $this->k; 11218 //calculate elements of transformation matrix 11219 $tm = array(); 11220 $tm[0] = 1; 11221 $tm[1] = tan(deg2rad($angle_y)); 11222 $tm[2] = tan(deg2rad($angle_x)); 11223 $tm[3] = 1; 11224 $tm[4] = -$tm[2] * $y; 11225 $tm[5] = -$tm[1] * $x; 11226 //skew the coordinate system 11227 $this->Transform($tm); 11228 } 11229 11230 /** 11231 * Apply graphic transformations. 11232 * @param $tm (array) transformation matrix 11233 * @protected 11234 * @since 2.1.000 (2008-01-07) 11235 * @see StartTransform(), StopTransform() 11236 */ 11237 protected function Transform($tm) { 11238 if ($this->state != 2) { 11239 return; 11240 } 11241 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); 11242 // add tranformation matrix 11243 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]); 11244 // update transformation mark 11245 if ($this->inxobj) { 11246 // we are inside an XObject template 11247 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 11248 $key = key($this->xobjects[$this->xobjid]['transfmrk']); 11249 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']); 11250 } 11251 } elseif (end($this->transfmrk[$this->page]) !== false) { 11252 $key = key($this->transfmrk[$this->page]); 11253 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page]; 11254 } 11255 } 11256 11257 // END TRANSFORMATIONS SECTION ------------------------- 11258 11259 // START GRAPHIC FUNCTIONS SECTION --------------------- 11260 // The following section is based on the code provided by David Hernandez Sanz 11261 11262 /** 11263 * 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. 11264 * @param $width (float) The width. 11265 * @public 11266 * @since 1.0 11267 * @see Line(), Rect(), Cell(), MultiCell() 11268 */ 11269 public function SetLineWidth($width) { 11270 //Set line width 11271 $this->LineWidth = $width; 11272 $this->linestyleWidth = sprintf('%F w', ($width * $this->k)); 11273 if ($this->state == 2) { 11274 $this->_out($this->linestyleWidth); 11275 } 11276 } 11277 11278 /** 11279 * Returns the current the line width. 11280 * @return int Line width 11281 * @public 11282 * @since 2.1.000 (2008-01-07) 11283 * @see Line(), SetLineWidth() 11284 */ 11285 public function GetLineWidth() { 11286 return $this->LineWidth; 11287 } 11288 11289 /** 11290 * Set line style. 11291 * @param $style (array) Line style. Array with keys among the following: 11292 * <ul> 11293 * <li>width (float): Width of the line in user units.</li> 11294 * <li>cap (string): Type of cap to put on the line. Possible values are: 11295 * butt, round, square. The difference between "square" and "butt" is that 11296 * "square" projects a flat end past the end of the line.</li> 11297 * <li>join (string): Type of join. Possible values are: miter, round, 11298 * bevel.</li> 11299 * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with 11300 * series of length values, which are the lengths of the on and off dashes. 11301 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, 11302 * 1 off, 2 on, 1 off, ...</li> 11303 * <li>phase (integer): Modifier on the dash pattern which is used to shift 11304 * the point at which the pattern starts.</li> 11305 * <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> 11306 * </ul> 11307 * @param $ret (boolean) if true do not send the command. 11308 * @return string the PDF command 11309 * @public 11310 * @since 2.1.000 (2008-01-08) 11311 */ 11312 public function SetLineStyle($style, $ret=false) { 11313 $s = ''; // string to be returned 11314 if (!is_array($style)) { 11315 return; 11316 } 11317 if (isset($style['width'])) { 11318 $this->LineWidth = $style['width']; 11319 $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k)); 11320 $s .= $this->linestyleWidth.' '; 11321 } 11322 if (isset($style['cap'])) { 11323 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2); 11324 if (isset($ca[$style['cap']])) { 11325 $this->linestyleCap = $ca[$style['cap']].' J'; 11326 $s .= $this->linestyleCap.' '; 11327 } 11328 } 11329 if (isset($style['join'])) { 11330 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); 11331 if (isset($ja[$style['join']])) { 11332 $this->linestyleJoin = $ja[$style['join']].' j'; 11333 $s .= $this->linestyleJoin.' '; 11334 } 11335 } 11336 if (isset($style['dash'])) { 11337 $dash_string = ''; 11338 if ($style['dash']) { 11339 if (preg_match('/^.+,/', $style['dash']) > 0) { 11340 $tab = explode(',', $style['dash']); 11341 } else { 11342 $tab = array($style['dash']); 11343 } 11344 $dash_string = ''; 11345 foreach ($tab as $i => $v) { 11346 if ($i) { 11347 $dash_string .= ' '; 11348 } 11349 $dash_string .= sprintf('%F', $v); 11350 } 11351 } 11352 if (!isset($style['phase']) OR !$style['dash']) { 11353 $style['phase'] = 0; 11354 } 11355 $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']); 11356 $s .= $this->linestyleDash.' '; 11357 } 11358 if (isset($style['color'])) { 11359 $s .= $this->SetDrawColorArray($style['color'], true).' '; 11360 } 11361 if (!$ret AND ($this->state == 2)) { 11362 $this->_out($s); 11363 } 11364 return $s; 11365 } 11366 11367 /** 11368 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment. 11369 * @param $x (float) Abscissa of point. 11370 * @param $y (float) Ordinate of point. 11371 * @protected 11372 * @since 2.1.000 (2008-01-08) 11373 */ 11374 protected function _outPoint($x, $y) { 11375 if ($this->state == 2) { 11376 $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k))); 11377 } 11378 } 11379 11380 /** 11381 * Append a straight line segment from the current point to the point (x, y). 11382 * The new current point shall be (x, y). 11383 * @param $x (float) Abscissa of end point. 11384 * @param $y (float) Ordinate of end point. 11385 * @protected 11386 * @since 2.1.000 (2008-01-08) 11387 */ 11388 protected function _outLine($x, $y) { 11389 if ($this->state == 2) { 11390 $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k))); 11391 } 11392 } 11393 11394 /** 11395 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space. 11396 * @param $x (float) Abscissa of upper-left corner. 11397 * @param $y (float) Ordinate of upper-left corner. 11398 * @param $w (float) Width. 11399 * @param $h (float) Height. 11400 * @param $op (string) options 11401 * @protected 11402 * @since 2.1.000 (2008-01-08) 11403 */ 11404 protected function _outRect($x, $y, $w, $h, $op) { 11405 if ($this->state == 2) { 11406 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op)); 11407 } 11408 } 11409 11410 /** 11411 * 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. 11412 * The new current point shall be (x3, y3). 11413 * @param $x1 (float) Abscissa of control point 1. 11414 * @param $y1 (float) Ordinate of control point 1. 11415 * @param $x2 (float) Abscissa of control point 2. 11416 * @param $y2 (float) Ordinate of control point 2. 11417 * @param $x3 (float) Abscissa of end point. 11418 * @param $y3 (float) Ordinate of end point. 11419 * @protected 11420 * @since 2.1.000 (2008-01-08) 11421 */ 11422 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { 11423 if ($this->state == 2) { 11424 $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))); 11425 } 11426 } 11427 11428 /** 11429 * 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. 11430 * The new current point shall be (x3, y3). 11431 * @param $x2 (float) Abscissa of control point 2. 11432 * @param $y2 (float) Ordinate of control point 2. 11433 * @param $x3 (float) Abscissa of end point. 11434 * @param $y3 (float) Ordinate of end point. 11435 * @protected 11436 * @since 4.9.019 (2010-04-26) 11437 */ 11438 protected function _outCurveV($x2, $y2, $x3, $y3) { 11439 if ($this->state == 2) { 11440 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k))); 11441 } 11442 } 11443 11444 /** 11445 * 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. 11446 * The new current point shall be (x3, y3). 11447 * @param $x1 (float) Abscissa of control point 1. 11448 * @param $y1 (float) Ordinate of control point 1. 11449 * @param $x3 (float) Abscissa of end point. 11450 * @param $y3 (float) Ordinate of end point. 11451 * @protected 11452 * @since 2.1.000 (2008-01-08) 11453 */ 11454 protected function _outCurveY($x1, $y1, $x3, $y3) { 11455 if ($this->state == 2) { 11456 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k))); 11457 } 11458 } 11459 11460 /** 11461 * Draws a line between two points. 11462 * @param $x1 (float) Abscissa of first point. 11463 * @param $y1 (float) Ordinate of first point. 11464 * @param $x2 (float) Abscissa of second point. 11465 * @param $y2 (float) Ordinate of second point. 11466 * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array). 11467 * @public 11468 * @since 1.0 11469 * @see SetLineWidth(), SetDrawColor(), SetLineStyle() 11470 */ 11471 public function Line($x1, $y1, $x2, $y2, $style=array()) { 11472 if ($this->state != 2) { 11473 return; 11474 } 11475 if (is_array($style)) { 11476 $this->SetLineStyle($style); 11477 } 11478 $this->_outPoint($x1, $y1); 11479 $this->_outLine($x2, $y2); 11480 $this->_out('S'); 11481 } 11482 11483 /** 11484 * Draws a rectangle. 11485 * @param $x (float) Abscissa of upper-left corner. 11486 * @param $y (float) Ordinate of upper-left corner. 11487 * @param $w (float) Width. 11488 * @param $h (float) Height. 11489 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11490 * @param $border_style (array) Border style of rectangle. Array with keys among the following: 11491 * <ul> 11492 * <li>all: Line style of all borders. Array like for SetLineStyle().</li> 11493 * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li> 11494 * </ul> 11495 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array). 11496 * @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). 11497 * @public 11498 * @since 1.0 11499 * @see SetLineStyle() 11500 */ 11501 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) { 11502 if ($this->state != 2) { 11503 return; 11504 } 11505 if (empty($style)) { 11506 $style = 'S'; 11507 } 11508 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) { 11509 // set background color 11510 $this->SetFillColorArray($fill_color); 11511 } 11512 if (!empty($border_style)) { 11513 if (isset($border_style['all']) AND !empty($border_style['all'])) { 11514 //set global style for border 11515 $this->SetLineStyle($border_style['all']); 11516 $border_style = array(); 11517 } else { 11518 // remove stroke operator from style 11519 $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*' ); 11520 if (isset($opnostroke[$style])) { 11521 $style = $opnostroke[$style]; 11522 } 11523 } 11524 } 11525 if (!empty($style)) { 11526 $op = TCPDF_STATIC::getPathPaintOperator($style); 11527 $this->_outRect($x, $y, $w, $h, $op); 11528 } 11529 if (!empty($border_style)) { 11530 $border_style2 = array(); 11531 foreach ($border_style as $line => $value) { 11532 $length = strlen($line); 11533 for ($i = 0; $i < $length; ++$i) { 11534 $border_style2[$line[$i]] = $value; 11535 } 11536 } 11537 $border_style = $border_style2; 11538 if (isset($border_style['L']) AND $border_style['L']) { 11539 $this->Line($x, $y, $x, $y + $h, $border_style['L']); 11540 } 11541 if (isset($border_style['T']) AND $border_style['T']) { 11542 $this->Line($x, $y, $x + $w, $y, $border_style['T']); 11543 } 11544 if (isset($border_style['R']) AND $border_style['R']) { 11545 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']); 11546 } 11547 if (isset($border_style['B']) AND $border_style['B']) { 11548 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']); 11549 } 11550 } 11551 } 11552 11553 /** 11554 * Draws a Bezier curve. 11555 * The Bezier curve is a tangent to the line between the control points at 11556 * either end of the curve. 11557 * @param $x0 (float) Abscissa of start point. 11558 * @param $y0 (float) Ordinate of start point. 11559 * @param $x1 (float) Abscissa of control point 1. 11560 * @param $y1 (float) Ordinate of control point 1. 11561 * @param $x2 (float) Abscissa of control point 2. 11562 * @param $y2 (float) Ordinate of control point 2. 11563 * @param $x3 (float) Abscissa of end point. 11564 * @param $y3 (float) Ordinate of end point. 11565 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11566 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array). 11567 * @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). 11568 * @public 11569 * @see SetLineStyle() 11570 * @since 2.1.000 (2008-01-08) 11571 */ 11572 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) { 11573 if ($this->state != 2) { 11574 return; 11575 } 11576 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11577 $this->SetFillColorArray($fill_color); 11578 } 11579 $op = TCPDF_STATIC::getPathPaintOperator($style); 11580 if ($line_style) { 11581 $this->SetLineStyle($line_style); 11582 } 11583 $this->_outPoint($x0, $y0); 11584 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 11585 $this->_out($op); 11586 } 11587 11588 /** 11589 * Draws a poly-Bezier curve. 11590 * Each Bezier curve segment is a tangent to the line between the control points at 11591 * either end of the curve. 11592 * @param $x0 (float) Abscissa of start point. 11593 * @param $y0 (float) Ordinate of start point. 11594 * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3). 11595 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11596 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array). 11597 * @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). 11598 * @public 11599 * @see SetLineStyle() 11600 * @since 3.0008 (2008-05-12) 11601 */ 11602 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) { 11603 if ($this->state != 2) { 11604 return; 11605 } 11606 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11607 $this->SetFillColorArray($fill_color); 11608 } 11609 $op = TCPDF_STATIC::getPathPaintOperator($style); 11610 if ($op == 'f') { 11611 $line_style = array(); 11612 } 11613 if ($line_style) { 11614 $this->SetLineStyle($line_style); 11615 } 11616 $this->_outPoint($x0, $y0); 11617 foreach ($segments as $segment) { 11618 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment; 11619 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 11620 } 11621 $this->_out($op); 11622 } 11623 11624 /** 11625 * Draws an ellipse. 11626 * An ellipse is formed from n Bezier curves. 11627 * @param $x0 (float) Abscissa of center point. 11628 * @param $y0 (float) Ordinate of center point. 11629 * @param $rx (float) Horizontal radius. 11630 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0. 11631 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0. 11632 * @param $astart: (float) Angle start of draw line. Default value: 0. 11633 * @param $afinish: (float) Angle finish of draw line. Default value: 360. 11634 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11635 * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array). 11636 * @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). 11637 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse. 11638 * @author Nicola Asuni 11639 * @public 11640 * @since 2.1.000 (2008-01-08) 11641 */ 11642 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 11643 if ($this->state != 2) { 11644 return; 11645 } 11646 if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) { 11647 $ry = $rx; 11648 } 11649 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11650 $this->SetFillColorArray($fill_color); 11651 } 11652 $op = TCPDF_STATIC::getPathPaintOperator($style); 11653 if ($op == 'f') { 11654 $line_style = array(); 11655 } 11656 if ($line_style) { 11657 $this->SetLineStyle($line_style); 11658 } 11659 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false); 11660 $this->_out($op); 11661 } 11662 11663 /** 11664 * Append an elliptical arc to the current path. 11665 * An ellipse is formed from n Bezier curves. 11666 * @param $xc (float) Abscissa of center point. 11667 * @param $yc (float) Ordinate of center point. 11668 * @param $rx (float) Horizontal radius. 11669 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0. 11670 * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0. 11671 * @param $angs: (float) Angle start of draw line. Default value: 0. 11672 * @param $angf: (float) Angle finish of draw line. Default value: 360. 11673 * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors). 11674 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse. 11675 * @param $startpoint (boolean) if true output a starting point. 11676 * @param $ccw (boolean) if true draws in counter-clockwise. 11677 * @param $svg (boolean) if true the angles are in svg mode (already calculated). 11678 * @return array bounding box coordinates (x min, y min, x max, y max) 11679 * @author Nicola Asuni 11680 * @protected 11681 * @since 4.9.019 (2010-04-26) 11682 */ 11683 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) { 11684 if (($rx <= 0) OR ($ry < 0)) { 11685 return; 11686 } 11687 $k = $this->k; 11688 if ($nc < 2) { 11689 $nc = 2; 11690 } 11691 $xmin = 2147483647; 11692 $ymin = 2147483647; 11693 $xmax = 0; 11694 $ymax = 0; 11695 if ($pie) { 11696 // center of the arc 11697 $this->_outPoint($xc, $yc); 11698 } 11699 $xang = deg2rad((float) $xang); 11700 $angs = deg2rad((float) $angs); 11701 $angf = deg2rad((float) $angf); 11702 if ($svg) { 11703 $as = $angs; 11704 $af = $angf; 11705 } else { 11706 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx)); 11707 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx)); 11708 } 11709 if ($as < 0) { 11710 $as += (2 * M_PI); 11711 } 11712 if ($af < 0) { 11713 $af += (2 * M_PI); 11714 } 11715 if ($ccw AND ($as > $af)) { 11716 // reverse rotation 11717 $as -= (2 * M_PI); 11718 } elseif (!$ccw AND ($as < $af)) { 11719 // reverse rotation 11720 $af -= (2 * M_PI); 11721 } 11722 $total_angle = ($af - $as); 11723 if ($nc < 2) { 11724 $nc = 2; 11725 } 11726 // total arcs to draw 11727 $nc *= (2 * abs($total_angle) / M_PI); 11728 $nc = round($nc) + 1; 11729 // angle of each arc 11730 $arcang = ($total_angle / $nc); 11731 // center point in PDF coordinates 11732 $x0 = $xc; 11733 $y0 = ($this->h - $yc); 11734 // starting angle 11735 $ang = $as; 11736 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3); 11737 $cos_xang = cos($xang); 11738 $sin_xang = sin($xang); 11739 $cos_ang = cos($ang); 11740 $sin_ang = sin($ang); 11741 // first arc point 11742 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 11743 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 11744 // first Bezier control point 11745 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 11746 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 11747 if ($pie) { 11748 // line from center to arc starting point 11749 $this->_outLine($px1, $this->h - $py1); 11750 } elseif ($startpoint) { 11751 // arc starting point 11752 $this->_outPoint($px1, $this->h - $py1); 11753 } 11754 // draw arcs 11755 for ($i = 1; $i <= $nc; ++$i) { 11756 // starting angle 11757 $ang = $as + ($i * $arcang); 11758 if ($i == $nc) { 11759 $ang = $af; 11760 } 11761 $cos_ang = cos($ang); 11762 $sin_ang = sin($ang); 11763 // second arc point 11764 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 11765 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 11766 // second Bezier control point 11767 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 11768 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 11769 // draw arc 11770 $cx1 = ($px1 + $qx1); 11771 $cy1 = ($this->h - ($py1 + $qy1)); 11772 $cx2 = ($px2 - $qx2); 11773 $cy2 = ($this->h - ($py2 - $qy2)); 11774 $cx3 = $px2; 11775 $cy3 = ($this->h - $py2); 11776 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3); 11777 // get bounding box coordinates 11778 $xmin = min($xmin, $cx1, $cx2, $cx3); 11779 $ymin = min($ymin, $cy1, $cy2, $cy3); 11780 $xmax = max($xmax, $cx1, $cx2, $cx3); 11781 $ymax = max($ymax, $cy1, $cy2, $cy3); 11782 // move to next point 11783 $px1 = $px2; 11784 $py1 = $py2; 11785 $qx1 = $qx2; 11786 $qy1 = $qy2; 11787 } 11788 if ($pie) { 11789 $this->_outLine($xc, $yc); 11790 // get bounding box coordinates 11791 $xmin = min($xmin, $xc); 11792 $ymin = min($ymin, $yc); 11793 $xmax = max($xmax, $xc); 11794 $ymax = max($ymax, $yc); 11795 } 11796 return array($xmin, $ymin, $xmax, $ymax); 11797 } 11798 11799 /** 11800 * Draws a circle. 11801 * A circle is formed from n Bezier curves. 11802 * @param $x0 (float) Abscissa of center point. 11803 * @param $y0 (float) Ordinate of center point. 11804 * @param $r (float) Radius. 11805 * @param $angstr: (float) Angle start of draw line. Default value: 0. 11806 * @param $angend: (float) Angle finish of draw line. Default value: 360. 11807 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11808 * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array). 11809 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). 11810 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle. 11811 * @public 11812 * @since 2.1.000 (2008-01-08) 11813 */ 11814 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 11815 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc); 11816 } 11817 11818 /** 11819 * Draws a polygonal line 11820 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 11821 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11822 * @param $line_style (array) Line style of polygon. Array with keys among the following: 11823 * <ul> 11824 * <li>all: Line style of all lines. Array like for SetLineStyle().</li> 11825 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li> 11826 * </ul> 11827 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 11828 * @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). 11829 * @since 4.8.003 (2009-09-15) 11830 * @public 11831 */ 11832 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) { 11833 $this->Polygon($p, $style, $line_style, $fill_color, false); 11834 } 11835 11836 /** 11837 * Draws a polygon. 11838 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 11839 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11840 * @param $line_style (array) Line style of polygon. Array with keys among the following: 11841 * <ul> 11842 * <li>all: Line style of all lines. Array like for SetLineStyle().</li> 11843 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li> 11844 * </ul> 11845 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 11846 * @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). 11847 * @param $closed (boolean) if true the polygon is closes, otherwise will remain open 11848 * @public 11849 * @since 2.1.000 (2008-01-08) 11850 */ 11851 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) { 11852 if ($this->state != 2) { 11853 return; 11854 } 11855 $nc = count($p); // number of coordinates 11856 $np = $nc / 2; // number of points 11857 if ($closed) { 11858 // close polygon by adding the first 2 points at the end (one line) 11859 for ($i = 0; $i < 4; ++$i) { 11860 $p[$nc + $i] = $p[$i]; 11861 } 11862 // copy style for the last added line 11863 if (isset($line_style[0])) { 11864 $line_style[$np] = $line_style[0]; 11865 } 11866 $nc += 4; 11867 } 11868 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11869 $this->SetFillColorArray($fill_color); 11870 } 11871 $op = TCPDF_STATIC::getPathPaintOperator($style); 11872 if ($op == 'f') { 11873 $line_style = array(); 11874 } 11875 $draw = true; 11876 if ($line_style) { 11877 if (isset($line_style['all'])) { 11878 $this->SetLineStyle($line_style['all']); 11879 } else { 11880 $draw = false; 11881 if ($op == 'B') { 11882 // draw fill 11883 $op = 'f'; 11884 $this->_outPoint($p[0], $p[1]); 11885 for ($i = 2; $i < $nc; $i = $i + 2) { 11886 $this->_outLine($p[$i], $p[$i + 1]); 11887 } 11888 $this->_out($op); 11889 } 11890 // draw outline 11891 $this->_outPoint($p[0], $p[1]); 11892 for ($i = 2; $i < $nc; $i = $i + 2) { 11893 $line_num = ($i / 2) - 1; 11894 if (isset($line_style[$line_num])) { 11895 if ($line_style[$line_num] != 0) { 11896 if (is_array($line_style[$line_num])) { 11897 $this->_out('S'); 11898 $this->SetLineStyle($line_style[$line_num]); 11899 $this->_outPoint($p[$i - 2], $p[$i - 1]); 11900 $this->_outLine($p[$i], $p[$i + 1]); 11901 $this->_out('S'); 11902 $this->_outPoint($p[$i], $p[$i + 1]); 11903 } else { 11904 $this->_outLine($p[$i], $p[$i + 1]); 11905 } 11906 } 11907 } else { 11908 $this->_outLine($p[$i], $p[$i + 1]); 11909 } 11910 } 11911 $this->_out($op); 11912 } 11913 } 11914 if ($draw) { 11915 $this->_outPoint($p[0], $p[1]); 11916 for ($i = 2; $i < $nc; $i = $i + 2) { 11917 $this->_outLine($p[$i], $p[$i + 1]); 11918 } 11919 $this->_out($op); 11920 } 11921 } 11922 11923 /** 11924 * Draws a regular polygon. 11925 * @param $x0 (float) Abscissa of center point. 11926 * @param $y0 (float) Ordinate of center point. 11927 * @param $r: (float) Radius of inscribed circle. 11928 * @param $ns (integer) Number of sides. 11929 * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0. 11930 * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false. 11931 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11932 * @param $line_style (array) Line style of polygon sides. Array with keys among the following: 11933 * <ul> 11934 * <li>all: Line style of all sides. Array like for SetLineStyle().</li> 11935 * <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li> 11936 * </ul> 11937 * If a key is not present or is null, not draws the side. Default value is 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 $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are: 11940 * <ul> 11941 * <li>D or empty string: Draw (default).</li> 11942 * <li>F: Fill.</li> 11943 * <li>DF or FD: Draw and fill.</li> 11944 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 11945 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 11946 * </ul> 11947 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array). 11948 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 11949 * @public 11950 * @since 2.1.000 (2008-01-08) 11951 */ 11952 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()) { 11953 if (3 > $ns) { 11954 $ns = 3; 11955 } 11956 if ($draw_circle) { 11957 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 11958 } 11959 $p = array(); 11960 for ($i = 0; $i < $ns; ++$i) { 11961 $a = $angle + ($i * 360 / $ns); 11962 $a_rad = deg2rad((float) $a); 11963 $p[] = $x0 + ($r * sin($a_rad)); 11964 $p[] = $y0 + ($r * cos($a_rad)); 11965 } 11966 $this->Polygon($p, $style, $line_style, $fill_color); 11967 } 11968 11969 /** 11970 * Draws a star polygon 11971 * @param $x0 (float) Abscissa of center point. 11972 * @param $y0 (float) Ordinate of center point. 11973 * @param $r (float) Radius of inscribed circle. 11974 * @param $nv (integer) Number of vertices. 11975 * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon). 11976 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0. 11977 * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false. 11978 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11979 * @param $line_style (array) Line style of polygon sides. Array with keys among the following: 11980 * <ul> 11981 * <li>all: Line style of all sides. Array like for 11982 * SetLineStyle().</li> 11983 * <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li> 11984 * </ul> 11985 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 11986 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). 11987 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are: 11988 * <ul> 11989 * <li>D or empty string: Draw (default).</li> 11990 * <li>F: Fill.</li> 11991 * <li>DF or FD: Draw and fill.</li> 11992 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 11993 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 11994 * </ul> 11995 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array). 11996 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 11997 * @public 11998 * @since 2.1.000 (2008-01-08) 11999 */ 12000 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()) { 12001 if ($nv < 2) { 12002 $nv = 2; 12003 } 12004 if ($draw_circle) { 12005 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 12006 } 12007 $p2 = array(); 12008 $visited = array(); 12009 for ($i = 0; $i < $nv; ++$i) { 12010 $a = $angle + ($i * 360 / $nv); 12011 $a_rad = deg2rad((float) $a); 12012 $p2[] = $x0 + ($r * sin($a_rad)); 12013 $p2[] = $y0 + ($r * cos($a_rad)); 12014 $visited[] = false; 12015 } 12016 $p = array(); 12017 $i = 0; 12018 do { 12019 $p[] = $p2[$i * 2]; 12020 $p[] = $p2[($i * 2) + 1]; 12021 $visited[$i] = true; 12022 $i += $ng; 12023 $i %= $nv; 12024 } while (!$visited[$i]); 12025 $this->Polygon($p, $style, $line_style, $fill_color); 12026 } 12027 12028 /** 12029 * Draws a rounded rectangle. 12030 * @param $x (float) Abscissa of upper-left corner. 12031 * @param $y (float) Ordinate of upper-left corner. 12032 * @param $w (float) Width. 12033 * @param $h (float) Height. 12034 * @param $r (float) the radius of the circle used to round off the corners of the rectangle. 12035 * @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"). 12036 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12037 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array). 12038 * @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). 12039 * @public 12040 * @since 2.1.000 (2008-01-08) 12041 */ 12042 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 12043 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color); 12044 } 12045 12046 /** 12047 * Draws a rounded rectangle. 12048 * @param $x (float) Abscissa of upper-left corner. 12049 * @param $y (float) Ordinate of upper-left corner. 12050 * @param $w (float) Width. 12051 * @param $h (float) Height. 12052 * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle. 12053 * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle. 12054 * @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"). 12055 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12056 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array). 12057 * @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). 12058 * @public 12059 * @since 4.9.019 (2010-04-22) 12060 */ 12061 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 12062 if ($this->state != 2) { 12063 return; 12064 } 12065 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) { 12066 // Not rounded 12067 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); 12068 return; 12069 } 12070 // Rounded 12071 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 12072 $this->SetFillColorArray($fill_color); 12073 } 12074 $op = TCPDF_STATIC::getPathPaintOperator($style); 12075 if ($op == 'f') { 12076 $border_style = array(); 12077 } 12078 if ($border_style) { 12079 $this->SetLineStyle($border_style); 12080 } 12081 $MyArc = 4 / 3 * (sqrt(2) - 1); 12082 $this->_outPoint($x + $rx, $y); 12083 $xc = $x + $w - $rx; 12084 $yc = $y + $ry; 12085 $this->_outLine($xc, $y); 12086 if ($round_corner[0]) { 12087 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc); 12088 } else { 12089 $this->_outLine($x + $w, $y); 12090 } 12091 $xc = $x + $w - $rx; 12092 $yc = $y + $h - $ry; 12093 $this->_outLine($x + $w, $yc); 12094 if ($round_corner[1]) { 12095 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry); 12096 } else { 12097 $this->_outLine($x + $w, $y + $h); 12098 } 12099 $xc = $x + $rx; 12100 $yc = $y + $h - $ry; 12101 $this->_outLine($xc, $y + $h); 12102 if ($round_corner[2]) { 12103 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc); 12104 } else { 12105 $this->_outLine($x, $y + $h); 12106 } 12107 $xc = $x + $rx; 12108 $yc = $y + $ry; 12109 $this->_outLine($x, $yc); 12110 if ($round_corner[3]) { 12111 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry); 12112 } else { 12113 $this->_outLine($x, $y); 12114 $this->_outLine($x + $rx, $y); 12115 } 12116 $this->_out($op); 12117 } 12118 12119 /** 12120 * Draws a grahic arrow. 12121 * @param $x0 (float) Abscissa of first point. 12122 * @param $y0 (float) Ordinate of first point. 12123 * @param $x1 (float) Abscissa of second point. 12124 * @param $y1 (float) Ordinate of second point. 12125 * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead) 12126 * @param $arm_size (float) length of arrowhead arms 12127 * @param $arm_angle (int) angle between an arm and the shaft 12128 * @author Piotr Galecki, Nicola Asuni, Andy Meier 12129 * @since 4.6.018 (2009-07-10) 12130 */ 12131 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) { 12132 // getting arrow direction angle 12133 // 0 deg angle is when both arms go along X axis. angle grows clockwise. 12134 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1)); 12135 if ($dir_angle < 0) { 12136 $dir_angle += (2 * M_PI); 12137 } 12138 $arm_angle = deg2rad($arm_angle); 12139 $sx1 = $x1; 12140 $sy1 = $y1; 12141 if ($head_style > 0) { 12142 // calculate the stopping point for the arrow shaft 12143 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle)); 12144 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle)); 12145 } 12146 // main arrow line / shaft 12147 $this->Line($x0, $y0, $sx1, $sy1); 12148 // left arrowhead arm tip 12149 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle)); 12150 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle)); 12151 // right arrowhead arm tip 12152 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle)); 12153 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle)); 12154 $mode = 'D'; 12155 $style = array(); 12156 switch ($head_style) { 12157 case 0: { 12158 // draw only arrowhead arms 12159 $mode = 'D'; 12160 $style = array(1, 1, 0); 12161 break; 12162 } 12163 case 1: { 12164 // draw closed arrowhead, but no fill 12165 $mode = 'D'; 12166 break; 12167 } 12168 case 2: { 12169 // closed and filled arrowhead 12170 $mode = 'DF'; 12171 break; 12172 } 12173 case 3: { 12174 // filled arrowhead 12175 $mode = 'F'; 12176 break; 12177 } 12178 } 12179 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array()); 12180 } 12181 12182 // END GRAPHIC FUNCTIONS SECTION ----------------------- 12183 12184 /** 12185 * Add a Named Destination. 12186 * NOTE: destination names are unique, so only last entry will be saved. 12187 * @param $name (string) Destination name. 12188 * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;). 12189 * @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. 12190 * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;). 12191 * @return (string) Stripped named destination identifier or false in case of error. 12192 * @public 12193 * @author Christian Deligant, Nicola Asuni 12194 * @since 5.9.097 (2011-06-23) 12195 */ 12196 public function setDestination($name, $y=-1, $page='', $x=-1) { 12197 // remove unsupported characters 12198 $name = TCPDF_STATIC::encodeNameObject($name); 12199 if (TCPDF_STATIC::empty_string($name)) { 12200 return false; 12201 } 12202 if ($y == -1) { 12203 $y = $this->GetY(); 12204 } elseif ($y < 0) { 12205 $y = 0; 12206 } elseif ($y > $this->h) { 12207 $y = $this->h; 12208 } 12209 if ($x == -1) { 12210 $x = $this->GetX(); 12211 } elseif ($x < 0) { 12212 $x = 0; 12213 } elseif ($x > $this->w) { 12214 $x = $this->w; 12215 } 12216 $fixed = false; 12217 if (!empty($page) AND ($page[0] == '*')) { 12218 $page = intval(substr($page, 1)); 12219 // this page number will not be changed when moving/add/deleting pages 12220 $fixed = true; 12221 } 12222 if (empty($page)) { 12223 $page = $this->PageNo(); 12224 if (empty($page)) { 12225 return; 12226 } 12227 } 12228 $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed); 12229 return $name; 12230 } 12231 12232 /** 12233 * Return the Named Destination array. 12234 * @return (array) Named Destination array. 12235 * @public 12236 * @author Nicola Asuni 12237 * @since 5.9.097 (2011-06-23) 12238 */ 12239 public function getDestination() { 12240 return $this->dests; 12241 } 12242 12243 /** 12244 * Insert Named Destinations. 12245 * @protected 12246 * @author Johannes G\FCntert, Nicola Asuni 12247 * @since 5.9.098 (2011-06-23) 12248 */ 12249 protected function _putdests() { 12250 if (empty($this->dests)) { 12251 return; 12252 } 12253 $this->n_dests = $this->_newobj(); 12254 $out = ' <<'; 12255 foreach($this->dests as $name => $o) { 12256 $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))); 12257 } 12258 $out .= ' >>'; 12259 $out .= "\n".'endobj'; 12260 $this->_out($out); 12261 } 12262 12263 /** 12264 * Adds a bookmark - alias for Bookmark(). 12265 * @param $txt (string) Bookmark description. 12266 * @param $level (int) Bookmark level (minimum value is 0). 12267 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;). 12268 * @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. 12269 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. 12270 * @param $color (array) RGB color array (values from 0 to 255). 12271 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). 12272 * @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). 12273 * @public 12274 */ 12275 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { 12276 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link); 12277 } 12278 12279 /** 12280 * Adds a bookmark. 12281 * @param $txt (string) Bookmark description. 12282 * @param $level (int) Bookmark level (minimum value is 0). 12283 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;). 12284 * @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. 12285 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. 12286 * @param $color (array) RGB color array (values from 0 to 255). 12287 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). 12288 * @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). 12289 * @public 12290 * @since 2.1.002 (2008-02-12) 12291 */ 12292 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { 12293 if ($level < 0) { 12294 $level = 0; 12295 } 12296 if (isset($this->outlines[0])) { 12297 $lastoutline = end($this->outlines); 12298 $maxlevel = $lastoutline['l'] + 1; 12299 } else { 12300 $maxlevel = 0; 12301 } 12302 if ($level > $maxlevel) { 12303 $level = $maxlevel; 12304 } 12305 if ($y == -1) { 12306 $y = $this->GetY(); 12307 } elseif ($y < 0) { 12308 $y = 0; 12309 } elseif ($y > $this->h) { 12310 $y = $this->h; 12311 } 12312 if ($x == -1) { 12313 $x = $this->GetX(); 12314 } elseif ($x < 0) { 12315 $x = 0; 12316 } elseif ($x > $this->w) { 12317 $x = $this->w; 12318 } 12319 $fixed = false; 12320 if (!empty($page) AND ($page[0] == '*')) { 12321 $page = intval(substr($page, 1)); 12322 // this page number will not be changed when moving/add/deleting pages 12323 $fixed = true; 12324 } 12325 if (empty($page)) { 12326 $page = $this->PageNo(); 12327 if (empty($page)) { 12328 return; 12329 } 12330 } 12331 $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link); 12332 } 12333 12334 /** 12335 * Sort bookmarks for page and key. 12336 * @protected 12337 * @since 5.9.119 (2011-09-19) 12338 */ 12339 protected function sortBookmarks() { 12340 // get sorting columns 12341 $outline_p = array(); 12342 $outline_y = array(); 12343 foreach ($this->outlines as $key => $row) { 12344 $outline_p[$key] = $row['p']; 12345 $outline_k[$key] = $key; 12346 } 12347 // sort outlines by page and original position 12348 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines); 12349 } 12350 12351 /** 12352 * Create a bookmark PDF string. 12353 * @protected 12354 * @author Olivier Plathey, Nicola Asuni 12355 * @since 2.1.002 (2008-02-12) 12356 */ 12357 protected function _putbookmarks() { 12358 $nb = count($this->outlines); 12359 if ($nb == 0) { 12360 return; 12361 } 12362 // sort bookmarks 12363 $this->sortBookmarks(); 12364 $lru = array(); 12365 $level = 0; 12366 foreach ($this->outlines as $i => $o) { 12367 if ($o['l'] > 0) { 12368 $parent = $lru[($o['l'] - 1)]; 12369 //Set parent and last pointers 12370 $this->outlines[$i]['parent'] = $parent; 12371 $this->outlines[$parent]['last'] = $i; 12372 if ($o['l'] > $level) { 12373 //Level increasing: set first pointer 12374 $this->outlines[$parent]['first'] = $i; 12375 } 12376 } else { 12377 $this->outlines[$i]['parent'] = $nb; 12378 } 12379 if (($o['l'] <= $level) AND ($i > 0)) { 12380 //Set prev and next pointers 12381 $prev = $lru[$o['l']]; 12382 $this->outlines[$prev]['next'] = $i; 12383 $this->outlines[$i]['prev'] = $prev; 12384 } 12385 $lru[$o['l']] = $i; 12386 $level = $o['l']; 12387 } 12388 //Outline items 12389 $n = $this->n + 1; 12390 $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'; 12391 foreach ($this->outlines as $i => $o) { 12392 $oid = $this->_newobj(); 12393 // covert HTML title to string 12394 $title = preg_replace($nltags, "\n", $o['t']); 12395 $title = preg_replace("/[\r]+/si", '', $title); 12396 $title = preg_replace("/[\n]+/si", "\n", $title); 12397 $title = strip_tags($title); 12398 $title = $this->stringTrim($title); 12399 $out = '<</Title '.$this->_textstring($title, $oid); 12400 $out .= ' /Parent '.($n + $o['parent']).' 0 R'; 12401 if (isset($o['prev'])) { 12402 $out .= ' /Prev '.($n + $o['prev']).' 0 R'; 12403 } 12404 if (isset($o['next'])) { 12405 $out .= ' /Next '.($n + $o['next']).' 0 R'; 12406 } 12407 if (isset($o['first'])) { 12408 $out .= ' /First '.($n + $o['first']).' 0 R'; 12409 } 12410 if (isset($o['last'])) { 12411 $out .= ' /Last '.($n + $o['last']).' 0 R'; 12412 } 12413 if (isset($o['u']) AND !empty($o['u'])) { 12414 // link 12415 if (is_string($o['u'])) { 12416 if ($o['u'][0] == '#') { 12417 // internal destination 12418 $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1)); 12419 } elseif ($o['u'][0] == '%') { 12420 // embedded PDF file 12421 $filename = basename(substr($o['u'], 1)); 12422 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; 12423 } elseif ($o['u'][0] == '*') { 12424 // embedded generic file 12425 $filename = basename(substr($o['u'], 1)); 12426 $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});'; 12427 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>'; 12428 } else { 12429 // external URI link 12430 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>'; 12431 } 12432 } elseif (isset($this->links[$o['u']])) { 12433 // internal link ID 12434 $l = $this->links[$o['u']]; 12435 if (isset($this->page_obj_id[($l['p'])])) { 12436 $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))); 12437 } 12438 } 12439 } elseif (isset($this->page_obj_id[($o['p'])])) { 12440 // link to a page 12441 $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))); 12442 } 12443 // set font style 12444 $style = 0; 12445 if (!empty($o['s'])) { 12446 // bold 12447 if (strpos($o['s'], 'B') !== false) { 12448 $style |= 2; 12449 } 12450 // oblique 12451 if (strpos($o['s'], 'I') !== false) { 12452 $style |= 1; 12453 } 12454 } 12455 $out .= sprintf(' /F %d', $style); 12456 // set bookmark color 12457 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) { 12458 $color = array_values($o['c']); 12459 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); 12460 } else { 12461 // black 12462 $out .= ' /C [0.0 0.0 0.0]'; 12463 } 12464 $out .= ' /Count 0'; // normally closed item 12465 $out .= ' >>'; 12466 $out .= "\n".'endobj'; 12467 $this->_out($out); 12468 } 12469 //Outline root 12470 $this->OutlineRoot = $this->_newobj(); 12471 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj'); 12472 } 12473 12474 // --- JAVASCRIPT ------------------------------------------------------ 12475 12476 /** 12477 * Adds a javascript 12478 * @param $script (string) Javascript code 12479 * @public 12480 * @author Johannes G\FCntert, Nicola Asuni 12481 * @since 2.1.002 (2008-02-12) 12482 */ 12483 public function IncludeJS($script) { 12484 $this->javascript .= $script; 12485 } 12486 12487 /** 12488 * Adds a javascript object and return object ID 12489 * @param $script (string) Javascript code 12490 * @param $onload (boolean) if true executes this object when opening the document 12491 * @return int internal object ID 12492 * @public 12493 * @author Nicola Asuni 12494 * @since 4.8.000 (2009-09-07) 12495 */ 12496 public function addJavascriptObject($script, $onload=false) { 12497 if ($this->pdfa_mode) { 12498 // javascript is not allowed in PDF/A mode 12499 return false; 12500 } 12501 ++$this->n; 12502 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload); 12503 return $this->n; 12504 } 12505 12506 /** 12507 * Create a javascript PDF string. 12508 * @protected 12509 * @author Johannes G\FCntert, Nicola Asuni 12510 * @since 2.1.002 (2008-02-12) 12511 */ 12512 protected function _putjavascript() { 12513 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) { 12514 return; 12515 } 12516 if (strpos($this->javascript, 'this.addField') > 0) { 12517 if (!$this->ur['enabled']) { 12518 //$this->setUserRights(); 12519 } 12520 // the following two lines are used to avoid form fields duplication after saving 12521 // The addField method only works when releasing user rights (UR3) 12522 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1); 12523 $jsb = "getField('tcpdfdocsaved').value='saved';"; 12524 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb; 12525 } 12526 // name tree for javascript 12527 $this->n_js = '<< /Names ['; 12528 if (!empty($this->javascript)) { 12529 $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R'; 12530 } 12531 if (!empty($this->js_objects)) { 12532 foreach ($this->js_objects as $key => $val) { 12533 if ($val['onload']) { 12534 $this->n_js .= ' (JS'.$key.') '.$key.' 0 R'; 12535 } 12536 } 12537 } 12538 $this->n_js .= ' ] >>'; 12539 // default Javascript object 12540 if (!empty($this->javascript)) { 12541 $obj_id = $this->_newobj(); 12542 $out = '<< /S /JavaScript'; 12543 $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id); 12544 $out .= ' >>'; 12545 $out .= "\n".'endobj'; 12546 $this->_out($out); 12547 } 12548 // additional Javascript objects 12549 if (!empty($this->js_objects)) { 12550 foreach ($this->js_objects as $key => $val) { 12551 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj'; 12552 $this->_out($out); 12553 } 12554 } 12555 } 12556 12557 /** 12558 * Adds a javascript form field. 12559 * @param $type (string) field type 12560 * @param $name (string) field name 12561 * @param $x (int) horizontal position 12562 * @param $y (int) vertical position 12563 * @param $w (int) width 12564 * @param $h (int) height 12565 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12566 * @protected 12567 * @author Denis Van Nuffelen, Nicola Asuni 12568 * @since 2.1.002 (2008-02-12) 12569 */ 12570 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) { 12571 if ($this->rtl) { 12572 $x = $x - $w; 12573 } 12574 // the followind avoid fields duplication after saving the document 12575 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {"; 12576 $k = $this->k; 12577 $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"; 12578 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n"; 12579 while (list($key, $val) = each($prop)) { 12580 if (strcmp(substr($key, -5), 'Color') == 0) { 12581 $val = TCPDF_COLORS::_JScolor($val); 12582 } else { 12583 $val = "'".$val."'"; 12584 } 12585 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n"; 12586 } 12587 if ($this->rtl) { 12588 $this->x -= $w; 12589 } else { 12590 $this->x += $w; 12591 } 12592 $this->javascript .= '}'; 12593 } 12594 12595 // --- FORM FIELDS ----------------------------------------------------- 12596 12597 12598 12599 /** 12600 * Set default properties for form fields. 12601 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12602 * @public 12603 * @author Nicola Asuni 12604 * @since 4.8.000 (2009-09-06) 12605 */ 12606 public function setFormDefaultProp($prop=array()) { 12607 $this->default_form_prop = $prop; 12608 } 12609 12610 /** 12611 * Return the default properties for form fields. 12612 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12613 * @public 12614 * @author Nicola Asuni 12615 * @since 4.8.000 (2009-09-06) 12616 */ 12617 public function getFormDefaultProp() { 12618 return $this->default_form_prop; 12619 } 12620 12621 /** 12622 * Creates a text field 12623 * @param $name (string) field name 12624 * @param $w (float) Width of the rectangle 12625 * @param $h (float) Height of the rectangle 12626 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12627 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 12628 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12629 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12630 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 12631 * @public 12632 * @author Nicola Asuni 12633 * @since 4.8.000 (2009-09-07) 12634 */ 12635 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 12636 if ($x === '') { 12637 $x = $this->x; 12638 } 12639 if ($y === '') { 12640 $y = $this->y; 12641 } 12642 // check page for no-write regions and adapt page margins if necessary 12643 list($x, $y) = $this->checkPageRegions($h, $x, $y); 12644 if ($js) { 12645 $this->_addfield('text', $name, $x, $y, $w, $h, $prop); 12646 return; 12647 } 12648 // get default style 12649 $prop = array_merge($this->getFormDefaultProp(), $prop); 12650 // get annotation data 12651 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12652 // set default appearance stream 12653 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 12654 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 12655 $popt['da'] = $fontstyle; 12656 // build appearance stream 12657 $popt['ap'] = array(); 12658 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 12659 $text = ''; 12660 if (isset($prop['value']) AND !empty($prop['value'])) { 12661 $text = $prop['value']; 12662 } elseif (isset($opt['v']) AND !empty($opt['v'])) { 12663 $text = $opt['v']; 12664 } 12665 $tmpid = $this->startTemplate($w, $h, false); 12666 $align = ''; 12667 if (isset($popt['q'])) { 12668 switch ($popt['q']) { 12669 case 0: { 12670 $align = 'L'; 12671 break; 12672 } 12673 case 1: { 12674 $align = 'C'; 12675 break; 12676 } 12677 case 2: { 12678 $align = 'R'; 12679 break; 12680 } 12681 default: { 12682 $align = ''; 12683 break; 12684 } 12685 } 12686 } 12687 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 12688 $this->endTemplate(); 12689 --$this->n; 12690 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 12691 unset($this->xobjects[$tmpid]); 12692 $popt['ap']['n'] .= 'Q EMC'; 12693 // merge options 12694 $opt = array_merge($popt, $opt); 12695 // remove some conflicting options 12696 unset($opt['bs']); 12697 // set remaining annotation data 12698 $opt['Subtype'] = 'Widget'; 12699 $opt['ft'] = 'Tx'; 12700 $opt['t'] = $name; 12701 // Additional annotation's parameters (check _putannotsobj() method): 12702 //$opt['f'] 12703 //$opt['as'] 12704 //$opt['bs'] 12705 //$opt['be'] 12706 //$opt['c'] 12707 //$opt['border'] 12708 //$opt['h'] 12709 //$opt['mk']; 12710 //$opt['mk']['r'] 12711 //$opt['mk']['bc']; 12712 //$opt['mk']['bg']; 12713 unset($opt['mk']['ca']); 12714 unset($opt['mk']['rc']); 12715 unset($opt['mk']['ac']); 12716 unset($opt['mk']['i']); 12717 unset($opt['mk']['ri']); 12718 unset($opt['mk']['ix']); 12719 unset($opt['mk']['if']); 12720 //$opt['mk']['if']['sw']; 12721 //$opt['mk']['if']['s']; 12722 //$opt['mk']['if']['a']; 12723 //$opt['mk']['if']['fb']; 12724 unset($opt['mk']['tp']); 12725 //$opt['tu'] 12726 //$opt['tm'] 12727 //$opt['ff'] 12728 //$opt['v'] 12729 //$opt['dv'] 12730 //$opt['a'] 12731 //$opt['aa'] 12732 //$opt['q'] 12733 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 12734 if ($this->rtl) { 12735 $this->x -= $w; 12736 } else { 12737 $this->x += $w; 12738 } 12739 } 12740 12741 /** 12742 * Creates a RadioButton field. 12743 * @param $name (string) Field name. 12744 * @param $w (int) Width of the radio button. 12745 * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12746 * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference. 12747 * @param $onvalue (string) Value to be returned if selected. 12748 * @param $checked (boolean) Define the initial state. 12749 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12750 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12751 * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered). 12752 * @public 12753 * @author Nicola Asuni 12754 * @since 4.8.000 (2009-09-07) 12755 */ 12756 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) { 12757 if ($x === '') { 12758 $x = $this->x; 12759 } 12760 if ($y === '') { 12761 $y = $this->y; 12762 } 12763 // check page for no-write regions and adapt page margins if necessary 12764 list($x, $y) = $this->checkPageRegions($w, $x, $y); 12765 if ($js) { 12766 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop); 12767 return; 12768 } 12769 if (TCPDF_STATIC::empty_string($onvalue)) { 12770 $onvalue = 'On'; 12771 } 12772 if ($checked) { 12773 $defval = $onvalue; 12774 } else { 12775 $defval = 'Off'; 12776 } 12777 // set font 12778 $font = 'zapfdingbats'; 12779 if ($this->pdfa_mode) { 12780 // all fonts must be embedded 12781 $font = 'pdfa'.$font; 12782 } 12783 $this->AddFont($font); 12784 $tmpfont = $this->getFontBuffer($font); 12785 // set data for parent group 12786 if (!isset($this->radiobutton_groups[$this->page])) { 12787 $this->radiobutton_groups[$this->page] = array(); 12788 } 12789 if (!isset($this->radiobutton_groups[$this->page][$name])) { 12790 $this->radiobutton_groups[$this->page][$name] = array(); 12791 ++$this->n; 12792 $this->radiobutton_groups[$this->page][$name]['n'] = $this->n; 12793 $this->radio_groups[] = $this->n; 12794 } 12795 $kid = ($this->n + 1); 12796 // save object ID to be added on Kids entry on parent object 12797 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval); 12798 // get default style 12799 $prop = array_merge($this->getFormDefaultProp(), $prop); 12800 $prop['NoToggleToOff'] = 'true'; 12801 $prop['Radio'] = 'true'; 12802 $prop['borderStyle'] = 'inset'; 12803 // get annotation data 12804 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12805 // set additional default options 12806 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; 12807 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); 12808 $popt['da'] = $fontstyle; 12809 // build appearance stream 12810 $popt['ap'] = array(); 12811 $popt['ap']['n'] = array(); 12812 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k); 12813 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); 12814 $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); 12815 $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); 12816 if (!isset($popt['mk'])) { 12817 $popt['mk'] = array(); 12818 } 12819 $popt['mk']['ca'] = '(l)'; 12820 // merge options 12821 $opt = array_merge($popt, $opt); 12822 // set remaining annotation data 12823 $opt['Subtype'] = 'Widget'; 12824 $opt['ft'] = 'Btn'; 12825 if ($checked) { 12826 $opt['v'] = array('/'.$onvalue); 12827 $opt['as'] = $onvalue; 12828 } else { 12829 $opt['as'] = 'Off'; 12830 } 12831 // store readonly flag 12832 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) { 12833 $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false; 12834 } 12835 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64); 12836 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 12837 if ($this->rtl) { 12838 $this->x -= $w; 12839 } else { 12840 $this->x += $w; 12841 } 12842 } 12843 12844 /** 12845 * Creates a List-box field 12846 * @param $name (string) field name 12847 * @param $w (int) width 12848 * @param $h (int) height 12849 * @param $values (array) array containing the list of values. 12850 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12851 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 12852 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12853 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12854 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 12855 * @public 12856 * @author Nicola Asuni 12857 * @since 4.8.000 (2009-09-07) 12858 */ 12859 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 12860 if ($x === '') { 12861 $x = $this->x; 12862 } 12863 if ($y === '') { 12864 $y = $this->y; 12865 } 12866 // check page for no-write regions and adapt page margins if necessary 12867 list($x, $y) = $this->checkPageRegions($h, $x, $y); 12868 if ($js) { 12869 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop); 12870 $s = ''; 12871 foreach ($values as $value) { 12872 if (is_array($value)) { 12873 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']'; 12874 } else { 12875 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']'; 12876 } 12877 } 12878 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n"; 12879 return; 12880 } 12881 // get default style 12882 $prop = array_merge($this->getFormDefaultProp(), $prop); 12883 // get annotation data 12884 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12885 // set additional default values 12886 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 12887 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 12888 $popt['da'] = $fontstyle; 12889 // build appearance stream 12890 $popt['ap'] = array(); 12891 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 12892 $text = ''; 12893 foreach($values as $item) { 12894 if (is_array($item)) { 12895 $text .= $item[1]."\n"; 12896 } else { 12897 $text .= $item."\n"; 12898 } 12899 } 12900 $tmpid = $this->startTemplate($w, $h, false); 12901 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 12902 $this->endTemplate(); 12903 --$this->n; 12904 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 12905 unset($this->xobjects[$tmpid]); 12906 $popt['ap']['n'] .= 'Q EMC'; 12907 // merge options 12908 $opt = array_merge($popt, $opt); 12909 // set remaining annotation data 12910 $opt['Subtype'] = 'Widget'; 12911 $opt['ft'] = 'Ch'; 12912 $opt['t'] = $name; 12913 $opt['opt'] = $values; 12914 unset($opt['mk']['ca']); 12915 unset($opt['mk']['rc']); 12916 unset($opt['mk']['ac']); 12917 unset($opt['mk']['i']); 12918 unset($opt['mk']['ri']); 12919 unset($opt['mk']['ix']); 12920 unset($opt['mk']['if']); 12921 unset($opt['mk']['tp']); 12922 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 12923 if ($this->rtl) { 12924 $this->x -= $w; 12925 } else { 12926 $this->x += $w; 12927 } 12928 } 12929 12930 /** 12931 * Creates a Combo-box field 12932 * @param $name (string) field name 12933 * @param $w (int) width 12934 * @param $h (int) height 12935 * @param $values (array) array containing the list of values. 12936 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12937 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 12938 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12939 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12940 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 12941 * @public 12942 * @author Nicola Asuni 12943 * @since 4.8.000 (2009-09-07) 12944 */ 12945 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 12946 if ($x === '') { 12947 $x = $this->x; 12948 } 12949 if ($y === '') { 12950 $y = $this->y; 12951 } 12952 // check page for no-write regions and adapt page margins if necessary 12953 list($x, $y) = $this->checkPageRegions($h, $x, $y); 12954 if ($js) { 12955 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop); 12956 $s = ''; 12957 foreach ($values as $value) { 12958 if (is_array($value)) { 12959 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']'; 12960 } else { 12961 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']'; 12962 } 12963 } 12964 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n"; 12965 return; 12966 } 12967 // get default style 12968 $prop = array_merge($this->getFormDefaultProp(), $prop); 12969 $prop['Combo'] = true; 12970 // get annotation data 12971 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12972 // set additional default options 12973 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 12974 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 12975 $popt['da'] = $fontstyle; 12976 // build appearance stream 12977 $popt['ap'] = array(); 12978 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 12979 $text = ''; 12980 foreach($values as $item) { 12981 if (is_array($item)) { 12982 $text .= $item[1]."\n"; 12983 } else { 12984 $text .= $item."\n"; 12985 } 12986 } 12987 $tmpid = $this->startTemplate($w, $h, false); 12988 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 12989 $this->endTemplate(); 12990 --$this->n; 12991 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 12992 unset($this->xobjects[$tmpid]); 12993 $popt['ap']['n'] .= 'Q EMC'; 12994 // merge options 12995 $opt = array_merge($popt, $opt); 12996 // set remaining annotation data 12997 $opt['Subtype'] = 'Widget'; 12998 $opt['ft'] = 'Ch'; 12999 $opt['t'] = $name; 13000 $opt['opt'] = $values; 13001 unset($opt['mk']['ca']); 13002 unset($opt['mk']['rc']); 13003 unset($opt['mk']['ac']); 13004 unset($opt['mk']['i']); 13005 unset($opt['mk']['ri']); 13006 unset($opt['mk']['ix']); 13007 unset($opt['mk']['if']); 13008 unset($opt['mk']['tp']); 13009 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 13010 if ($this->rtl) { 13011 $this->x -= $w; 13012 } else { 13013 $this->x += $w; 13014 } 13015 } 13016 13017 /** 13018 * Creates a CheckBox field 13019 * @param $name (string) field name 13020 * @param $w (int) width 13021 * @param $checked (boolean) define the initial state. 13022 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 13023 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 13024 * @param $onvalue (string) value to be returned if selected. 13025 * @param $x (float) Abscissa of the upper-left corner of the rectangle 13026 * @param $y (float) Ordinate of the upper-left corner of the rectangle 13027 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 13028 * @public 13029 * @author Nicola Asuni 13030 * @since 4.8.000 (2009-09-07) 13031 */ 13032 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) { 13033 if ($x === '') { 13034 $x = $this->x; 13035 } 13036 if ($y === '') { 13037 $y = $this->y; 13038 } 13039 // check page for no-write regions and adapt page margins if necessary 13040 list($x, $y) = $this->checkPageRegions($w, $x, $y); 13041 if ($js) { 13042 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop); 13043 return; 13044 } 13045 if (!isset($prop['value'])) { 13046 $prop['value'] = array('Yes'); 13047 } 13048 // get default style 13049 $prop = array_merge($this->getFormDefaultProp(), $prop); 13050 $prop['borderStyle'] = 'inset'; 13051 // get annotation data 13052 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13053 // set additional default options 13054 $font = 'zapfdingbats'; 13055 if ($this->pdfa_mode) { 13056 // all fonts must be embedded 13057 $font = 'pdfa'.$font; 13058 } 13059 $this->AddFont($font); 13060 $tmpfont = $this->getFontBuffer($font); 13061 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; 13062 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); 13063 $popt['da'] = $fontstyle; 13064 // build appearance stream 13065 $popt['ap'] = array(); 13066 $popt['ap']['n'] = array(); 13067 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k); 13068 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); 13069 $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); 13070 $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); 13071 // merge options 13072 $opt = array_merge($popt, $opt); 13073 // set remaining annotation data 13074 $opt['Subtype'] = 'Widget'; 13075 $opt['ft'] = 'Btn'; 13076 $opt['t'] = $name; 13077 if (TCPDF_STATIC::empty_string($onvalue)) { 13078 $onvalue = 'Yes'; 13079 } 13080 $opt['opt'] = array($onvalue); 13081 if ($checked) { 13082 $opt['v'] = array('/Yes'); 13083 $opt['as'] = 'Yes'; 13084 } else { 13085 $opt['v'] = array('/Off'); 13086 $opt['as'] = 'Off'; 13087 } 13088 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 13089 if ($this->rtl) { 13090 $this->x -= $w; 13091 } else { 13092 $this->x += $w; 13093 } 13094 } 13095 13096 /** 13097 * Creates a button field 13098 * @param $name (string) field name 13099 * @param $w (int) width 13100 * @param $h (int) height 13101 * @param $caption (string) caption. 13102 * @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. 13103 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 13104 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 13105 * @param $x (float) Abscissa of the upper-left corner of the rectangle 13106 * @param $y (float) Ordinate of the upper-left corner of the rectangle 13107 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 13108 * @public 13109 * @author Nicola Asuni 13110 * @since 4.8.000 (2009-09-07) 13111 */ 13112 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 13113 if ($x === '') { 13114 $x = $this->x; 13115 } 13116 if ($y === '') { 13117 $y = $this->y; 13118 } 13119 // check page for no-write regions and adapt page margins if necessary 13120 list($x, $y) = $this->checkPageRegions($h, $x, $y); 13121 if ($js) { 13122 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop); 13123 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n"; 13124 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n"; 13125 $this->javascript .= 'f'.$name.".highlight='push';\n"; 13126 $this->javascript .= 'f'.$name.".print=false;\n"; 13127 return; 13128 } 13129 // get default style 13130 $prop = array_merge($this->getFormDefaultProp(), $prop); 13131 $prop['Pushbutton'] = 'true'; 13132 $prop['highlight'] = 'push'; 13133 $prop['display'] = 'display.noPrint'; 13134 // get annotation data 13135 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13136 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 13137 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 13138 $popt['da'] = $fontstyle; 13139 // build appearance stream 13140 $popt['ap'] = array(); 13141 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 13142 $tmpid = $this->startTemplate($w, $h, false); 13143 $bw = (2 / $this->k); // border width 13144 $border = array( 13145 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 13146 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)), 13147 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 13148 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51))); 13149 $this->SetFillColor(204); 13150 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M'); 13151 $this->endTemplate(); 13152 --$this->n; 13153 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 13154 unset($this->xobjects[$tmpid]); 13155 $popt['ap']['n'] .= 'Q EMC'; 13156 // set additional default options 13157 if (!isset($popt['mk'])) { 13158 $popt['mk'] = array(); 13159 } 13160 $ann_obj_id = ($this->n + 1); 13161 if (!empty($action) AND !is_array($action)) { 13162 $ann_obj_id = ($this->n + 2); 13163 } 13164 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id); 13165 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id); 13166 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id); 13167 // merge options 13168 $opt = array_merge($popt, $opt); 13169 // set remaining annotation data 13170 $opt['Subtype'] = 'Widget'; 13171 $opt['ft'] = 'Btn'; 13172 $opt['t'] = $caption; 13173 $opt['v'] = $name; 13174 if (!empty($action)) { 13175 if (is_array($action)) { 13176 // form action options as on section 12.7.5 of PDF32000_2008. 13177 $opt['aa'] = '/D <<'; 13178 $bmode = array('SubmitForm', 'ResetForm', 'ImportData'); 13179 foreach ($action AS $key => $val) { 13180 if (($key == 'S') AND in_array($val, $bmode)) { 13181 $opt['aa'] .= ' /S /'.$val; 13182 } elseif (($key == 'F') AND (!empty($val))) { 13183 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id); 13184 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) { 13185 $opt['aa'] .= ' /Fields ['; 13186 foreach ($val AS $field) { 13187 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id); 13188 } 13189 $opt['aa'] .= ']'; 13190 } elseif (($key == 'Flags')) { 13191 $ff = 0; 13192 if (is_array($val)) { 13193 foreach ($val AS $flag) { 13194 switch ($flag) { 13195 case 'Include/Exclude': { 13196 $ff += 1 << 0; 13197 break; 13198 } 13199 case 'IncludeNoValueFields': { 13200 $ff += 1 << 1; 13201 break; 13202 } 13203 case 'ExportFormat': { 13204 $ff += 1 << 2; 13205 break; 13206 } 13207 case 'GetMethod': { 13208 $ff += 1 << 3; 13209 break; 13210 } 13211 case 'SubmitCoordinates': { 13212 $ff += 1 << 4; 13213 break; 13214 } 13215 case 'XFDF': { 13216 $ff += 1 << 5; 13217 break; 13218 } 13219 case 'IncludeAppendSaves': { 13220 $ff += 1 << 6; 13221 break; 13222 } 13223 case 'IncludeAnnotations': { 13224 $ff += 1 << 7; 13225 break; 13226 } 13227 case 'SubmitPDF': { 13228 $ff += 1 << 8; 13229 break; 13230 } 13231 case 'CanonicalFormat': { 13232 $ff += 1 << 9; 13233 break; 13234 } 13235 case 'ExclNonUserAnnots': { 13236 $ff += 1 << 10; 13237 break; 13238 } 13239 case 'ExclFKey': { 13240 $ff += 1 << 11; 13241 break; 13242 } 13243 case 'EmbedForm': { 13244 $ff += 1 << 13; 13245 break; 13246 } 13247 } 13248 } 13249 } else { 13250 $ff = intval($val); 13251 } 13252 $opt['aa'] .= ' /Flags '.$ff; 13253 } 13254 } 13255 $opt['aa'] .= ' >>'; 13256 } else { 13257 // Javascript action or raw action command 13258 $js_obj_id = $this->addJavascriptObject($action); 13259 $opt['aa'] = '/D '.$js_obj_id.' 0 R'; 13260 } 13261 } 13262 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 13263 if ($this->rtl) { 13264 $this->x -= $w; 13265 } else { 13266 $this->x += $w; 13267 } 13268 } 13269 13270 // --- END FORMS FIELDS ------------------------------------------------ 13271 13272 /** 13273 * Add certification signature (DocMDP or UR3) 13274 * You can set only one signature type 13275 * @protected 13276 * @author Nicola Asuni 13277 * @since 4.6.008 (2009-05-07) 13278 */ 13279 protected function _putsignature() { 13280 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) { 13281 return; 13282 } 13283 $sigobjid = ($this->sig_obj_id + 1); 13284 $out = $this->_getobj($sigobjid)."\n"; 13285 $out .= '<< /Type /Sig'; 13286 $out .= ' /Filter /Adobe.PPKLite'; 13287 $out .= ' /SubFilter /adbe.pkcs7.detached'; 13288 $out .= ' '.TCPDF_STATIC::$byterange_string; 13289 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>'; 13290 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 13291 $out .= ' /Reference ['; // array of signature reference dictionaries 13292 $out .= ' << /Type /SigRef'; 13293 if ($this->signature_data['cert_type'] > 0) { 13294 $out .= ' /TransformMethod /DocMDP'; 13295 $out .= ' /TransformParams <<'; 13296 $out .= ' /Type /TransformParams'; 13297 $out .= ' /P '.$this->signature_data['cert_type']; 13298 $out .= ' /V /1.2'; 13299 } else { 13300 $out .= ' /TransformMethod /UR3'; 13301 $out .= ' /TransformParams <<'; 13302 $out .= ' /Type /TransformParams'; 13303 $out .= ' /V /2.2'; 13304 if (!TCPDF_STATIC::empty_string($this->ur['document'])) { 13305 $out .= ' /Document['.$this->ur['document'].']'; 13306 } 13307 if (!TCPDF_STATIC::empty_string($this->ur['form'])) { 13308 $out .= ' /Form['.$this->ur['form'].']'; 13309 } 13310 if (!TCPDF_STATIC::empty_string($this->ur['signature'])) { 13311 $out .= ' /Signature['.$this->ur['signature'].']'; 13312 } 13313 if (!TCPDF_STATIC::empty_string($this->ur['annots'])) { 13314 $out .= ' /Annots['.$this->ur['annots'].']'; 13315 } 13316 if (!TCPDF_STATIC::empty_string($this->ur['ef'])) { 13317 $out .= ' /EF['.$this->ur['ef'].']'; 13318 } 13319 if (!TCPDF_STATIC::empty_string($this->ur['formex'])) { 13320 $out .= ' /FormEX['.$this->ur['formex'].']'; 13321 } 13322 } 13323 $out .= ' >>'; // close TransformParams 13324 // optional digest data (values must be calculated and replaced later) 13325 //$out .= ' /Data ********** 0 R'; 13326 //$out .= ' /DigestMethod/MD5'; 13327 //$out .= ' /DigestLocation[********** 34]'; 13328 //$out .= ' /DigestValue<********************************>'; 13329 $out .= ' >>'; 13330 $out .= ' ]'; // end of reference 13331 } 13332 if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) { 13333 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid); 13334 } 13335 if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) { 13336 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid); 13337 } 13338 if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) { 13339 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid); 13340 } 13341 if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) { 13342 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid); 13343 } 13344 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp); 13345 $out .= ' >>'; 13346 $out .= "\n".'endobj'; 13347 $this->_out($out); 13348 } 13349 13350 /** 13351 * Set User's Rights for PDF Reader 13352 * WARNING: This is experimental and currently do not work. 13353 * Check the PDF Reference 8.7.1 Transform Methods, 13354 * Table 8.105 Entries in the UR transform parameters dictionary 13355 * @param $enable (boolean) if true enable user's rights on PDF reader 13356 * @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. 13357 * @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. 13358 * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 13359 * @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. 13360 * @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 13361 Names specifying additional embedded-files-related usage rights for the document. 13362 * @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. 13363 * @public 13364 * @author Nicola Asuni 13365 * @since 2.9.000 (2008-03-26) 13366 */ 13367 public function setUserRights( 13368 $enable=true, 13369 $document='/FullSave', 13370 $annots='/Create/Delete/Modify/Copy/Import/Export', 13371 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', 13372 $signature='/Modify', 13373 $ef='/Create/Delete/Modify/Import', 13374 $formex='') { 13375 $this->ur['enabled'] = $enable; 13376 $this->ur['document'] = $document; 13377 $this->ur['annots'] = $annots; 13378 $this->ur['form'] = $form; 13379 $this->ur['signature'] = $signature; 13380 $this->ur['ef'] = $ef; 13381 $this->ur['formex'] = $formex; 13382 if (!$this->sign) { 13383 $this->setSignature('', '', '', '', 0, array()); 13384 } 13385 } 13386 13387 /** 13388 * Enable document signature (requires the OpenSSL Library). 13389 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader. 13390 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt 13391 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 13392 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes 13393 * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://') 13394 * @param $private_key (mixed) private key (string or filename prefixed with 'file://') 13395 * @param $private_key_password (string) password 13396 * @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. 13397 * @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. 13398 * @param $info (array) array of option information: Name, Location, Reason, ContactInfo. 13399 * @param $approval (string) Enable approval signature eg. for PDF incremental update 13400 * @public 13401 * @author Nicola Asuni 13402 * @since 4.6.005 (2009-04-24) 13403 */ 13404 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') { 13405 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt 13406 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 13407 // to convert pfx certificate to pem: openssl 13408 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes 13409 $this->sign = true; 13410 ++$this->n; 13411 $this->sig_obj_id = $this->n; // signature widget 13412 ++$this->n; // signature object ($this->sig_obj_id + 1) 13413 $this->signature_data = array(); 13414 if (strlen($signing_cert) == 0) { 13415 $this->Error('Please provide a certificate file and password!'); 13416 } 13417 if (strlen($private_key) == 0) { 13418 $private_key = $signing_cert; 13419 } 13420 $this->signature_data['signcert'] = $signing_cert; 13421 $this->signature_data['privkey'] = $private_key; 13422 $this->signature_data['password'] = $private_key_password; 13423 $this->signature_data['extracerts'] = $extracerts; 13424 $this->signature_data['cert_type'] = $cert_type; 13425 $this->signature_data['info'] = $info; 13426 $this->signature_data['approval'] = $approval; 13427 } 13428 13429 /** 13430 * Set the digital signature appearance (a cliccable rectangle area to get signature properties) 13431 * @param $x (float) Abscissa of the upper-left corner. 13432 * @param $y (float) Ordinate of the upper-left corner. 13433 * @param $w (float) Width of the signature area. 13434 * @param $h (float) Height of the signature area. 13435 * @param $page (int) option page number (if < 0 the current page is used). 13436 * @param $name (string) Name of the signature. 13437 * @public 13438 * @author Nicola Asuni 13439 * @since 5.3.011 (2010-06-17) 13440 */ 13441 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13442 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name); 13443 } 13444 13445 /** 13446 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties) 13447 * @param $x (float) Abscissa of the upper-left corner. 13448 * @param $y (float) Ordinate of the upper-left corner. 13449 * @param $w (float) Width of the signature area. 13450 * @param $h (float) Height of the signature area. 13451 * @param $page (int) option page number (if < 0 the current page is used). 13452 * @param $name (string) Name of the signature. 13453 * @public 13454 * @author Nicola Asuni 13455 * @since 5.9.101 (2011-07-06) 13456 */ 13457 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13458 ++$this->n; 13459 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name); 13460 } 13461 13462 /** 13463 * Get the array that defines the signature appearance (page and rectangle coordinates). 13464 * @param $x (float) Abscissa of the upper-left corner. 13465 * @param $y (float) Ordinate of the upper-left corner. 13466 * @param $w (float) Width of the signature area. 13467 * @param $h (float) Height of the signature area. 13468 * @param $page (int) option page number (if < 0 the current page is used). 13469 * @param $name (string) Name of the signature. 13470 * @return (array) Array defining page and rectangle coordinates of signature appearance. 13471 * @protected 13472 * @author Nicola Asuni 13473 * @since 5.9.101 (2011-07-06) 13474 */ 13475 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13476 $sigapp = array(); 13477 if (($page < 1) OR ($page > $this->numpages)) { 13478 $sigapp['page'] = $this->page; 13479 } else { 13480 $sigapp['page'] = intval($page); 13481 } 13482 if (empty($name)) { 13483 $sigapp['name'] = 'Signature'; 13484 } else { 13485 $sigapp['name'] = $name; 13486 } 13487 $a = $x * $this->k; 13488 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k); 13489 $c = $w * $this->k; 13490 $d = $h * $this->k; 13491 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d)); 13492 return $sigapp; 13493 } 13494 13495 /** 13496 * Enable document timestamping (requires the OpenSSL Library). 13497 * The trusted timestamping improve document security that means that no one should be able to change the document once it has been recorded. 13498 * Use with digital signature only! 13499 * @param $tsa_host (string) Time Stamping Authority (TSA) server (prefixed with 'https://') 13500 * @param $tsa_username (string) Specifies the username for TSA authorization (optional) OR specifies the TSA authorization PEM file (see: example_66.php, optional) 13501 * @param $tsa_password (string) Specifies the password for TSA authorization (optional) 13502 * @param $tsa_cert (string) Specifies the location of TSA certificate for authorization (optional for cURL) 13503 * @public 13504 * @author Richard Stockinger 13505 * @since 6.0.090 (2014-06-16) 13506 */ 13507 public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') { 13508 $this->tsa_data = array(); 13509 if (!function_exists('curl_init')) { 13510 $this->Error('Please enable cURL PHP extension!'); 13511 } 13512 if (strlen($tsa_host) == 0) { 13513 $this->Error('Please specify the host of Time Stamping Authority (TSA)!'); 13514 } 13515 $this->tsa_data['tsa_host'] = $tsa_host; 13516 if (is_file($tsa_username)) { 13517 $this->tsa_data['tsa_auth'] = $tsa_username; 13518 } else { 13519 $this->tsa_data['tsa_username'] = $tsa_username; 13520 } 13521 $this->tsa_data['tsa_password'] = $tsa_password; 13522 $this->tsa_data['tsa_cert'] = $tsa_cert; 13523 $this->tsa_timestamp = true; 13524 } 13525 13526 /** 13527 * NOT YET IMPLEMENTED 13528 * Request TSA for a timestamp 13529 * @param $signature (string) Digital signature as binary string 13530 * @return (string) Timestamped digital signature 13531 * @protected 13532 * @author Richard Stockinger 13533 * @since 6.0.090 (2014-06-16) 13534 */ 13535 protected function applyTSA($signature) { 13536 if (!$this->tsa_timestamp) { 13537 return $signature; 13538 } 13539 //@TODO: implement this feature 13540 return $signature; 13541 } 13542 13543 /** 13544 * Create a new page group. 13545 * NOTE: call this function before calling AddPage() 13546 * @param $page (int) starting group page (leave empty for next page). 13547 * @public 13548 * @since 3.0.000 (2008-03-27) 13549 */ 13550 public function startPageGroup($page='') { 13551 if (empty($page)) { 13552 $page = $this->page + 1; 13553 } 13554 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1; 13555 } 13556 13557 /** 13558 * Set the starting page number. 13559 * @param $num (int) Starting page number. 13560 * @since 5.9.093 (2011-06-16) 13561 * @public 13562 */ 13563 public function setStartingPageNumber($num=1) { 13564 $this->starting_page_number = max(0, intval($num)); 13565 } 13566 13567 /** 13568 * Returns the string alias used right align page numbers. 13569 * If the current font is unicode type, the returned string wil contain an additional open curly brace. 13570 * @return string 13571 * @since 5.9.099 (2011-06-27) 13572 * @public 13573 */ 13574 public function getAliasRightShift() { 13575 // calculate aproximatively the ratio between widths of aliases and replacements. 13576 $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}'; 13577 $rep = str_repeat(' ', $this->GetNumChars($ref)); 13578 $wrep = $this->GetStringWidth($rep); 13579 if ($wrep > 0) { 13580 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep)); 13581 } else { 13582 $wdiff = 1; 13583 } 13584 $sdiff = sprintf('%F', $wdiff); 13585 $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}'; 13586 if ($this->isUnicodeFont()) { 13587 $alias = '{'.$alias; 13588 } 13589 return $alias; 13590 } 13591 13592 /** 13593 * Returns the string alias used for the total number of pages. 13594 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13595 * This alias will be replaced by the total number of pages in the document. 13596 * @return string 13597 * @since 4.0.018 (2008-08-08) 13598 * @public 13599 */ 13600 public function getAliasNbPages() { 13601 if ($this->isUnicodeFont()) { 13602 return '{'.TCPDF_STATIC::$alias_tot_pages.'}'; 13603 } 13604 return TCPDF_STATIC::$alias_tot_pages; 13605 } 13606 13607 /** 13608 * Returns the string alias used for the page number. 13609 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13610 * This alias will be replaced by the page number. 13611 * @return string 13612 * @since 4.5.000 (2009-01-02) 13613 * @public 13614 */ 13615 public function getAliasNumPage() { 13616 if ($this->isUnicodeFont()) { 13617 return '{'.TCPDF_STATIC::$alias_num_page.'}'; 13618 } 13619 return TCPDF_STATIC::$alias_num_page; 13620 } 13621 13622 /** 13623 * Return the alias for the total number of pages in the current page group. 13624 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13625 * This alias will be replaced by the total number of pages in this group. 13626 * @return alias of the current page group 13627 * @public 13628 * @since 3.0.000 (2008-03-27) 13629 */ 13630 public function getPageGroupAlias() { 13631 if ($this->isUnicodeFont()) { 13632 return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}'; 13633 } 13634 return TCPDF_STATIC::$alias_group_tot_pages; 13635 } 13636 13637 /** 13638 * Return the alias for the page number on the current page group. 13639 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13640 * This alias will be replaced by the page number (relative to the belonging group). 13641 * @return alias of the current page group 13642 * @public 13643 * @since 4.5.000 (2009-01-02) 13644 */ 13645 public function getPageNumGroupAlias() { 13646 if ($this->isUnicodeFont()) { 13647 return '{'.TCPDF_STATIC::$alias_group_num_page.'}'; 13648 } 13649 return TCPDF_STATIC::$alias_group_num_page; 13650 } 13651 13652 /** 13653 * Return the current page in the group. 13654 * @return current page in the group 13655 * @public 13656 * @since 3.0.000 (2008-03-27) 13657 */ 13658 public function getGroupPageNo() { 13659 return $this->pagegroups[$this->currpagegroup]; 13660 } 13661 13662 /** 13663 * Returns the current group page number formatted as a string. 13664 * @public 13665 * @since 4.3.003 (2008-11-18) 13666 * @see PaneNo(), formatPageNumber() 13667 */ 13668 public function getGroupPageNoFormatted() { 13669 return TCPDF_STATIC::formatPageNumber($this->getGroupPageNo()); 13670 } 13671 13672 /** 13673 * Returns the current page number formatted as a string. 13674 * @public 13675 * @since 4.2.005 (2008-11-06) 13676 * @see PaneNo(), formatPageNumber() 13677 */ 13678 public function PageNoFormatted() { 13679 return TCPDF_STATIC::formatPageNumber($this->PageNo()); 13680 } 13681 13682 /** 13683 * Put pdf layers. 13684 * @protected 13685 * @since 3.0.000 (2008-03-27) 13686 */ 13687 protected function _putocg() { 13688 if (empty($this->pdflayers)) { 13689 return; 13690 } 13691 foreach ($this->pdflayers as $key => $layer) { 13692 $this->pdflayers[$key]['objid'] = $this->_newobj(); 13693 $out = '<< /Type /OCG'; 13694 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']); 13695 $out .= ' /Usage <<'; 13696 if (isset($layer['print']) AND ($layer['print'] !== NULL)) { 13697 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>'; 13698 } 13699 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>'; 13700 $out .= ' >> >>'; 13701 $out .= "\n".'endobj'; 13702 $this->_out($out); 13703 } 13704 } 13705 13706 /** 13707 * Start a new pdf layer. 13708 * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name. 13709 * @param $print (boolean|null) Set to TRUE to print this layer, FALSE to not print and NULL to not set this option 13710 * @param $view (boolean) Set to true to view this layer. 13711 * @param $lock (boolean) If true lock the layer 13712 * @public 13713 * @since 5.9.102 (2011-07-13) 13714 */ 13715 public function startLayer($name='', $print=true, $view=true, $lock=true) { 13716 if ($this->state != 2) { 13717 return; 13718 } 13719 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1)); 13720 if (empty($name)) { 13721 $name = $layer; 13722 } else { 13723 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name); 13724 } 13725 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock); 13726 $this->openMarkedContent = true; 13727 $this->_out('/OC /'.$layer.' BDC'); 13728 } 13729 13730 /** 13731 * End the current PDF layer. 13732 * @public 13733 * @since 5.9.102 (2011-07-13) 13734 */ 13735 public function endLayer() { 13736 if ($this->state != 2) { 13737 return; 13738 } 13739 if ($this->openMarkedContent) { 13740 // close existing open marked-content layer 13741 $this->_out('EMC'); 13742 $this->openMarkedContent = false; 13743 } 13744 } 13745 13746 /** 13747 * Set the visibility of the successive elements. 13748 * This can be useful, for instance, to put a background 13749 * image or color that will show on screen but won't print. 13750 * @param $v (string) visibility mode. Legal values are: all, print, screen or view. 13751 * @public 13752 * @since 3.0.000 (2008-03-27) 13753 */ 13754 public function setVisibility($v) { 13755 if ($this->state != 2) { 13756 return; 13757 } 13758 $this->endLayer(); 13759 switch($v) { 13760 case 'print': { 13761 $this->startLayer('Print', true, false); 13762 break; 13763 } 13764 case 'view': 13765 case 'screen': { 13766 $this->startLayer('View', false, true); 13767 break; 13768 } 13769 case 'all': { 13770 $this->_out(''); 13771 break; 13772 } 13773 default: { 13774 $this->Error('Incorrect visibility: '.$v); 13775 break; 13776 } 13777 } 13778 } 13779 13780 /** 13781 * Add transparency parameters to the current extgstate 13782 * @param $parms (array) parameters 13783 * @return the number of extgstates 13784 * @protected 13785 * @since 3.0.000 (2008-03-27) 13786 */ 13787 protected function addExtGState($parms) { 13788 if ($this->pdfa_mode) { 13789 // transparencies are not allowed in PDF/A mode 13790 return; 13791 } 13792 // check if this ExtGState already exist 13793 foreach ($this->extgstates as $i => $ext) { 13794 if ($ext['parms'] == $parms) { 13795 if ($this->inxobj) { 13796 // we are inside an XObject template 13797 $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext; 13798 } 13799 // return reference to existing ExtGState 13800 return $i; 13801 } 13802 } 13803 $n = (count($this->extgstates) + 1); 13804 $this->extgstates[$n] = array('parms' => $parms); 13805 if ($this->inxobj) { 13806 // we are inside an XObject template 13807 $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n]; 13808 } 13809 return $n; 13810 } 13811 13812 /** 13813 * Add an extgstate 13814 * @param $gs (array) extgstate 13815 * @protected 13816 * @since 3.0.000 (2008-03-27) 13817 */ 13818 protected function setExtGState($gs) { 13819 if ($this->pdfa_mode OR ($this->state != 2)) { 13820 // transparency is not allowed in PDF/A mode 13821 return; 13822 } 13823 $this->_out(sprintf('/GS%d gs', $gs)); 13824 } 13825 13826 /** 13827 * Put extgstates for object transparency 13828 * @protected 13829 * @since 3.0.000 (2008-03-27) 13830 */ 13831 protected function _putextgstates() { 13832 foreach ($this->extgstates as $i => $ext) { 13833 $this->extgstates[$i]['n'] = $this->_newobj(); 13834 $out = '<< /Type /ExtGState'; 13835 foreach ($ext['parms'] as $k => $v) { 13836 if (is_float($v)) { 13837 $v = sprintf('%F', $v); 13838 } elseif ($v === true) { 13839 $v = 'true'; 13840 } elseif ($v === false) { 13841 $v = 'false'; 13842 } 13843 $out .= ' /'.$k.' '.$v; 13844 } 13845 $out .= ' >>'; 13846 $out .= "\n".'endobj'; 13847 $this->_out($out); 13848 } 13849 } 13850 13851 /** 13852 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations. 13853 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 13854 * @param $stroking (boolean) If true apply overprint for stroking operations. 13855 * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking. 13856 * @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). 13857 * @public 13858 * @since 5.9.152 (2012-03-23) 13859 */ 13860 public function setOverprint($stroking=true, $nonstroking='', $mode=0) { 13861 if ($this->state != 2) { 13862 return; 13863 } 13864 $stroking = $stroking ? true : false; 13865 if (TCPDF_STATIC::empty_string($nonstroking)) { 13866 // default value if not set 13867 $nonstroking = $stroking; 13868 } else { 13869 $nonstroking = $nonstroking ? true : false; 13870 } 13871 if (($mode != 0) AND ($mode != 1)) { 13872 $mode = 0; 13873 } 13874 $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode); 13875 $gs = $this->addExtGState($this->overprint); 13876 $this->setExtGState($gs); 13877 } 13878 13879 /** 13880 * Get the overprint mode array (OP, op, OPM). 13881 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 13882 * @return array. 13883 * @public 13884 * @since 5.9.152 (2012-03-23) 13885 */ 13886 public function getOverprint() { 13887 return $this->overprint; 13888 } 13889 13890 /** 13891 * Set alpha for stroking (CA) and non-stroking (ca) operations. 13892 * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque). 13893 * @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 13894 * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque). 13895 * @param $ais (boolean) 13896 * @public 13897 * @since 3.0.000 (2008-03-27) 13898 */ 13899 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) { 13900 if ($this->pdfa_mode) { 13901 // transparency is not allowed in PDF/A mode 13902 return; 13903 } 13904 $stroking = floatval($stroking); 13905 if (TCPDF_STATIC::empty_string($nonstroking)) { 13906 // default value if not set 13907 $nonstroking = $stroking; 13908 } else { 13909 $nonstroking = floatval($nonstroking); 13910 } 13911 if ($bm[0] == '/') { 13912 // remove trailing slash 13913 $bm = substr($bm, 1); 13914 } 13915 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) { 13916 $bm = 'Normal'; 13917 } 13918 $ais = $ais ? true : false; 13919 $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais); 13920 $gs = $this->addExtGState($this->alpha); 13921 $this->setExtGState($gs); 13922 } 13923 13924 /** 13925 * Get the alpha mode array (CA, ca, BM, AIS). 13926 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 13927 * @return array. 13928 * @public 13929 * @since 5.9.152 (2012-03-23) 13930 */ 13931 public function getAlpha() { 13932 return $this->alpha; 13933 } 13934 13935 /** 13936 * Set the default JPEG compression quality (1-100) 13937 * @param $quality (int) JPEG quality, integer between 1 and 100 13938 * @public 13939 * @since 3.0.000 (2008-03-27) 13940 */ 13941 public function setJPEGQuality($quality) { 13942 if (($quality < 1) OR ($quality > 100)) { 13943 $quality = 75; 13944 } 13945 $this->jpeg_quality = intval($quality); 13946 } 13947 13948 /** 13949 * Set the default number of columns in a row for HTML tables. 13950 * @param $cols (int) number of columns 13951 * @public 13952 * @since 3.0.014 (2008-06-04) 13953 */ 13954 public function setDefaultTableColumns($cols=4) { 13955 $this->default_table_columns = intval($cols); 13956 } 13957 13958 /** 13959 * Set the height of the cell (line height) respect the font height. 13960 * @param $h (int) cell proportion respect font height (typical value = 1.25). 13961 * @public 13962 * @since 3.0.014 (2008-06-04) 13963 */ 13964 public function setCellHeightRatio($h) { 13965 $this->cell_height_ratio = $h; 13966 } 13967 13968 /** 13969 * return the height of cell repect font height. 13970 * @public 13971 * @since 4.0.012 (2008-07-24) 13972 */ 13973 public function getCellHeightRatio() { 13974 return $this->cell_height_ratio; 13975 } 13976 13977 /** 13978 * Set the PDF version (check PDF reference for valid values). 13979 * @param $version (string) PDF document version. 13980 * @public 13981 * @since 3.1.000 (2008-06-09) 13982 */ 13983 public function setPDFVersion($version='1.7') { 13984 if ($this->pdfa_mode) { 13985 // PDF/A mode 13986 $this->PDFVersion = '1.4'; 13987 } else { 13988 $this->PDFVersion = $version; 13989 } 13990 } 13991 13992 /** 13993 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print. 13994 * (see Section 8.1 of PDF reference, "Viewer Preferences"). 13995 * <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> 13996 * @param $preferences (array) array of options. 13997 * @author Nicola Asuni 13998 * @public 13999 * @since 3.1.000 (2008-06-09) 14000 */ 14001 public function setViewerPreferences($preferences) { 14002 $this->viewer_preferences = $preferences; 14003 } 14004 14005 /** 14006 * Paints color transition registration bars 14007 * @param $x (float) abscissa of the top left corner of the rectangle. 14008 * @param $y (float) ordinate of the top left corner of the rectangle. 14009 * @param $w (float) width of the rectangle. 14010 * @param $h (float) height of the rectangle. 14011 * @param $transition (boolean) if true prints tcolor transitions to white. 14012 * @param $vertical (boolean) if true prints bar vertically. 14013 * @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. 14014 * @author Nicola Asuni 14015 * @since 4.9.000 (2010-03-26) 14016 * @public 14017 */ 14018 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') { 14019 if (strpos($colors, 'ALLSPOT') !== false) { 14020 // expand spot colors 14021 $spot_colors = ''; 14022 foreach ($this->spot_colors as $spot_color_name => $v) { 14023 $spot_colors .= ','.$spot_color_name; 14024 } 14025 if (!empty($spot_colors)) { 14026 $spot_colors = substr($spot_colors, 1); 14027 $colors = str_replace('ALLSPOT', $spot_colors, $colors); 14028 } else { 14029 $colors = str_replace('ALLSPOT', 'NONE', $colors); 14030 } 14031 } 14032 $bars = explode(',', $colors); 14033 $numbars = count($bars); // number of bars to print 14034 if ($numbars <= 0) { 14035 return; 14036 } 14037 // set bar measures 14038 if ($vertical) { 14039 $coords = array(0, 0, 0, 1); 14040 $wb = $w / $numbars; // bar width 14041 $hb = $h; // bar height 14042 $xd = $wb; // delta x 14043 $yd = 0; // delta y 14044 } else { 14045 $coords = array(1, 0, 0, 0); 14046 $wb = $w; // bar width 14047 $hb = $h / $numbars; // bar height 14048 $xd = 0; // delta x 14049 $yd = $hb; // delta y 14050 } 14051 $xb = $x; 14052 $yb = $y; 14053 foreach ($bars as $col) { 14054 switch ($col) { 14055 // set transition colors 14056 case 'A': { // BLACK (GRAYSCALE) 14057 $col_a = array(255); 14058 $col_b = array(0); 14059 break; 14060 } 14061 case 'W': { // WHITE (GRAYSCALE) 14062 $col_a = array(0); 14063 $col_b = array(255); 14064 break; 14065 } 14066 case 'R': { // RED (RGB) 14067 $col_a = array(255,255,255); 14068 $col_b = array(255,0,0); 14069 break; 14070 } 14071 case 'G': { // GREEN (RGB) 14072 $col_a = array(255,255,255); 14073 $col_b = array(0,255,0); 14074 break; 14075 } 14076 case 'B': { // BLUE (RGB) 14077 $col_a = array(255,255,255); 14078 $col_b = array(0,0,255); 14079 break; 14080 } 14081 case 'C': { // CYAN (CMYK) 14082 $col_a = array(0,0,0,0); 14083 $col_b = array(100,0,0,0); 14084 break; 14085 } 14086 case 'M': { // MAGENTA (CMYK) 14087 $col_a = array(0,0,0,0); 14088 $col_b = array(0,100,0,0); 14089 break; 14090 } 14091 case 'Y': { // YELLOW (CMYK) 14092 $col_a = array(0,0,0,0); 14093 $col_b = array(0,0,100,0); 14094 break; 14095 } 14096 case 'K': { // KEY - BLACK (CMYK) 14097 $col_a = array(0,0,0,0); 14098 $col_b = array(0,0,0,100); 14099 break; 14100 } 14101 case 'RGB': { // BLACK REGISTRATION (RGB) 14102 $col_a = array(255,255,255); 14103 $col_b = array(0,0,0); 14104 break; 14105 } 14106 case 'CMYK': { // BLACK REGISTRATION (CMYK) 14107 $col_a = array(0,0,0,0); 14108 $col_b = array(100,100,100,100); 14109 break; 14110 } 14111 case 'ALL': { // SPOT COLOR REGISTRATION 14112 $col_a = array(0,0,0,0,'None'); 14113 $col_b = array(100,100,100,100,'All'); 14114 break; 14115 } 14116 case 'NONE': { // SKIP THIS COLOR 14117 $col_a = array(0,0,0,0,'None'); 14118 $col_b = array(0,0,0,0,'None'); 14119 break; 14120 } 14121 default: { // SPECIFIC SPOT COLOR NAME 14122 $col_a = array(0,0,0,0,'None'); 14123 $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors); 14124 if ($col_b === false) { 14125 // in case of error defaults to the registration color 14126 $col_b = array(100,100,100,100,'All'); 14127 } 14128 break; 14129 } 14130 } 14131 if ($col != 'NONE') { 14132 if ($transition) { 14133 // color gradient 14134 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords); 14135 } else { 14136 $this->SetFillColorArray($col_b); 14137 // colored rectangle 14138 $this->Rect($xb, $yb, $wb, $hb, 'F', array()); 14139 } 14140 $xb += $xd; 14141 $yb += $yd; 14142 } 14143 } 14144 } 14145 14146 /** 14147 * Paints crop marks. 14148 * @param $x (float) abscissa of the crop mark center. 14149 * @param $y (float) ordinate of the crop mark center. 14150 * @param $w (float) width of the crop mark. 14151 * @param $h (float) height of the crop mark. 14152 * @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. 14153 * @param $color (array) crop mark color (default spot registration color). 14154 * @author Nicola Asuni 14155 * @since 4.9.000 (2010-03-26) 14156 * @public 14157 */ 14158 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) { 14159 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color)); 14160 $type = strtoupper($type); 14161 $type = preg_replace('/[^A-Z\-\,]*/', '', $type); 14162 // split type in single components 14163 $type = str_replace('-', ',', $type); 14164 $type = str_replace('TL', 'T,L', $type); 14165 $type = str_replace('TR', 'T,R', $type); 14166 $type = str_replace('BL', 'F,L', $type); 14167 $type = str_replace('BR', 'F,R', $type); 14168 $type = str_replace('A', 'T,L', $type); 14169 $type = str_replace('B', 'T,R', $type); 14170 $type = str_replace('T,RO', 'BO', $type); 14171 $type = str_replace('C', 'F,L', $type); 14172 $type = str_replace('D', 'F,R', $type); 14173 $crops = explode(',', strtoupper($type)); 14174 // remove duplicates 14175 $crops = array_unique($crops); 14176 $dw = ($w / 4); // horizontal space to leave before the intersection point 14177 $dh = ($h / 4); // vertical space to leave before the intersection point 14178 foreach ($crops as $crop) { 14179 switch ($crop) { 14180 case 'T': 14181 case 'TOP': { 14182 $x1 = $x; 14183 $y1 = ($y - $h); 14184 $x2 = $x; 14185 $y2 = ($y - $dh); 14186 break; 14187 } 14188 case 'F': 14189 case 'BOTTOM': { 14190 $x1 = $x; 14191 $y1 = ($y + $dh); 14192 $x2 = $x; 14193 $y2 = ($y + $h); 14194 break; 14195 } 14196 case 'L': 14197 case 'LEFT': { 14198 $x1 = ($x - $w); 14199 $y1 = $y; 14200 $x2 = ($x - $dw); 14201 $y2 = $y; 14202 break; 14203 } 14204 case 'R': 14205 case 'RIGHT': { 14206 $x1 = ($x + $dw); 14207 $y1 = $y; 14208 $x2 = ($x + $w); 14209 $y2 = $y; 14210 break; 14211 } 14212 } 14213 $this->Line($x1, $y1, $x2, $y2); 14214 } 14215 } 14216 14217 /** 14218 * Paints a registration mark 14219 * @param $x (float) abscissa of the registration mark center. 14220 * @param $y (float) ordinate of the registration mark center. 14221 * @param $r (float) radius of the crop mark. 14222 * @param $double (boolean) if true print two concentric crop marks. 14223 * @param $cola (array) crop mark color (default spot registration color 'All'). 14224 * @param $colb (array) second crop mark color (default spot registration color 'None'). 14225 * @author Nicola Asuni 14226 * @since 4.9.000 (2010-03-26) 14227 * @public 14228 */ 14229 public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) { 14230 $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola); 14231 $this->SetFillColorArray($cola); 14232 $this->PieSector($x, $y, $r, 90, 180, 'F'); 14233 $this->PieSector($x, $y, $r, 270, 360, 'F'); 14234 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); 14235 if ($double) { 14236 $ri = $r * 0.5; 14237 $this->SetFillColorArray($colb); 14238 $this->PieSector($x, $y, $ri, 90, 180, 'F'); 14239 $this->PieSector($x, $y, $ri, 270, 360, 'F'); 14240 $this->SetFillColorArray($cola); 14241 $this->PieSector($x, $y, $ri, 0, 90, 'F'); 14242 $this->PieSector($x, $y, $ri, 180, 270, 'F'); 14243 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8); 14244 } 14245 } 14246 14247 /** 14248 * Paints a CMYK registration mark 14249 * @param $x (float) abscissa of the registration mark center. 14250 * @param $y (float) ordinate of the registration mark center. 14251 * @param $r (float) radius of the crop mark. 14252 * @author Nicola Asuni 14253 * @since 6.0.038 (2013-09-30) 14254 * @public 14255 */ 14256 public function registrationMarkCMYK($x, $y, $r) { 14257 // line width 14258 $lw = max((0.5 / $this->k),($r / 8)); 14259 // internal radius 14260 $ri = ($r * 0.6); 14261 // external radius 14262 $re = ($r * 1.3); 14263 // Cyan 14264 $this->SetFillColorArray(array(100,0,0,0)); 14265 $this->PieSector($x, $y, $ri, 270, 360, 'F'); 14266 // Magenta 14267 $this->SetFillColorArray(array(0,100,0,0)); 14268 $this->PieSector($x, $y, $ri, 0, 90, 'F'); 14269 // Yellow 14270 $this->SetFillColorArray(array(0,0,100,0)); 14271 $this->PieSector($x, $y, $ri, 90, 180, 'F'); 14272 // Key - black 14273 $this->SetFillColorArray(array(0,0,0,100)); 14274 $this->PieSector($x, $y, $ri, 180, 270, 'F'); 14275 // registration color 14276 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All')); 14277 $this->SetFillColorArray(array(100,100,100,100,'All')); 14278 // external circle 14279 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); 14280 // cross lines 14281 $this->Line($x, ($y - $re), $x, ($y - $ri)); 14282 $this->Line($x, ($y + $ri), $x, ($y + $re)); 14283 $this->Line(($x - $re), $y, ($x - $ri), $y); 14284 $this->Line(($x + $ri), $y, ($x + $re), $y); 14285 } 14286 14287 /** 14288 * Paints a linear colour gradient. 14289 * @param $x (float) abscissa of the top left corner of the rectangle. 14290 * @param $y (float) ordinate of the top left corner of the rectangle. 14291 * @param $w (float) width of the rectangle. 14292 * @param $h (float) height of the rectangle. 14293 * @param $col1 (array) first color (Grayscale, RGB or CMYK components). 14294 * @param $col2 (array) second color (Grayscale, RGB or CMYK components). 14295 * @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). 14296 * @author Andreas W\FCrmser, Nicola Asuni 14297 * @since 3.1.000 (2008-06-09) 14298 * @public 14299 */ 14300 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) { 14301 $this->Clip($x, $y, $w, $h); 14302 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 14303 } 14304 14305 /** 14306 * Paints a radial colour gradient. 14307 * @param $x (float) abscissa of the top left corner of the rectangle. 14308 * @param $y (float) ordinate of the top left corner of the rectangle. 14309 * @param $w (float) width of the rectangle. 14310 * @param $h (float) height of the rectangle. 14311 * @param $col1 (array) first color (Grayscale, RGB or CMYK components). 14312 * @param $col2 (array) second color (Grayscale, RGB or CMYK components). 14313 * @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. 14314 * @author Andreas W\FCrmser, Nicola Asuni 14315 * @since 3.1.000 (2008-06-09) 14316 * @public 14317 */ 14318 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) { 14319 $this->Clip($x, $y, $w, $h); 14320 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 14321 } 14322 14323 /** 14324 * Paints a coons patch mesh. 14325 * @param $x (float) abscissa of the top left corner of the rectangle. 14326 * @param $y (float) ordinate of the top left corner of the rectangle. 14327 * @param $w (float) width of the rectangle. 14328 * @param $h (float) height of the rectangle. 14329 * @param $col1 (array) first color (lower left corner) (RGB components). 14330 * @param $col2 (array) second color (lower right corner) (RGB components). 14331 * @param $col3 (array) third color (upper right corner) (RGB components). 14332 * @param $col4 (array) fourth color (upper left corner) (RGB components). 14333 * @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> 14334 * @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 14335 * @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 14336 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts. 14337 * @author Andreas W\FCrmser, Nicola Asuni 14338 * @since 3.1.000 (2008-06-09) 14339 * @public 14340 */ 14341 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) { 14342 if ($this->pdfa_mode OR ($this->state != 2)) { 14343 return; 14344 } 14345 $this->Clip($x, $y, $w, $h); 14346 $n = count($this->gradients) + 1; 14347 $this->gradients[$n] = array(); 14348 $this->gradients[$n]['type'] = 6; //coons patch mesh 14349 $this->gradients[$n]['coords'] = array(); 14350 $this->gradients[$n]['antialias'] = $antialias; 14351 $this->gradients[$n]['colors'] = array(); 14352 $this->gradients[$n]['transparency'] = false; 14353 //check the coords array if it is the simple array or the multi patch array 14354 if (!isset($coords[0]['f'])) { 14355 //simple array -> convert to multi patch array 14356 if (!isset($col1[1])) { 14357 $col1[1] = $col1[2] = $col1[0]; 14358 } 14359 if (!isset($col2[1])) { 14360 $col2[1] = $col2[2] = $col2[0]; 14361 } 14362 if (!isset($col3[1])) { 14363 $col3[1] = $col3[2] = $col3[0]; 14364 } 14365 if (!isset($col4[1])) { 14366 $col4[1] = $col4[2] = $col4[0]; 14367 } 14368 $patch_array[0]['f'] = 0; 14369 $patch_array[0]['points'] = $coords; 14370 $patch_array[0]['colors'][0]['r'] = $col1[0]; 14371 $patch_array[0]['colors'][0]['g'] = $col1[1]; 14372 $patch_array[0]['colors'][0]['b'] = $col1[2]; 14373 $patch_array[0]['colors'][1]['r'] = $col2[0]; 14374 $patch_array[0]['colors'][1]['g'] = $col2[1]; 14375 $patch_array[0]['colors'][1]['b'] = $col2[2]; 14376 $patch_array[0]['colors'][2]['r'] = $col3[0]; 14377 $patch_array[0]['colors'][2]['g'] = $col3[1]; 14378 $patch_array[0]['colors'][2]['b'] = $col3[2]; 14379 $patch_array[0]['colors'][3]['r'] = $col4[0]; 14380 $patch_array[0]['colors'][3]['g'] = $col4[1]; 14381 $patch_array[0]['colors'][3]['b'] = $col4[2]; 14382 } else { 14383 //multi patch array 14384 $patch_array = $coords; 14385 } 14386 $bpcd = 65535; //16 bits per coordinate 14387 //build the data stream 14388 $this->gradients[$n]['stream'] = ''; 14389 $count_patch = count($patch_array); 14390 for ($i=0; $i < $count_patch; ++$i) { 14391 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit 14392 $count_points = count($patch_array[$i]['points']); 14393 for ($j=0; $j < $count_points; ++$j) { 14394 //each point as 16 bit 14395 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd; 14396 if ($patch_array[$i]['points'][$j] < 0) { 14397 $patch_array[$i]['points'][$j] = 0; 14398 } 14399 if ($patch_array[$i]['points'][$j] > $bpcd) { 14400 $patch_array[$i]['points'][$j] = $bpcd; 14401 } 14402 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256)); 14403 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256)); 14404 } 14405 $count_cols = count($patch_array[$i]['colors']); 14406 for ($j=0; $j < $count_cols; ++$j) { 14407 //each color component as 8 bit 14408 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']); 14409 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']); 14410 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']); 14411 } 14412 } 14413 //paint the gradient 14414 $this->_out('/Sh'.$n.' sh'); 14415 //restore previous Graphic State 14416 $this->_outRestoreGraphicsState(); 14417 if ($this->inxobj) { 14418 // we are inside an XObject template 14419 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; 14420 } 14421 } 14422 14423 /** 14424 * Set a rectangular clipping area. 14425 * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode). 14426 * @param $y (float) ordinate of the top left corner of the rectangle. 14427 * @param $w (float) width of the rectangle. 14428 * @param $h (float) height of the rectangle. 14429 * @author Andreas W\FCrmser, Nicola Asuni 14430 * @since 3.1.000 (2008-06-09) 14431 * @protected 14432 */ 14433 protected function Clip($x, $y, $w, $h) { 14434 if ($this->state != 2) { 14435 return; 14436 } 14437 if ($this->rtl) { 14438 $x = $this->w - $x - $w; 14439 } 14440 //save current Graphic State 14441 $s = 'q'; 14442 //set clipping area 14443 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k); 14444 //set up transformation matrix for gradient 14445 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k); 14446 $this->_out($s); 14447 } 14448 14449 /** 14450 * Output gradient. 14451 * @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) 14452 * @param $coords (array) array of coordinates. 14453 * @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). 14454 * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value. 14455 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts. 14456 * @author Nicola Asuni 14457 * @since 3.1.000 (2008-06-09) 14458 * @public 14459 */ 14460 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) { 14461 if ($this->pdfa_mode OR ($this->state != 2)) { 14462 return; 14463 } 14464 $n = count($this->gradients) + 1; 14465 $this->gradients[$n] = array(); 14466 $this->gradients[$n]['type'] = $type; 14467 $this->gradients[$n]['coords'] = $coords; 14468 $this->gradients[$n]['antialias'] = $antialias; 14469 $this->gradients[$n]['colors'] = array(); 14470 $this->gradients[$n]['transparency'] = false; 14471 // color space 14472 $numcolspace = count($stops[0]['color']); 14473 $bcolor = array_values($background); 14474 switch($numcolspace) { 14475 case 5: // SPOT 14476 case 4: { // CMYK 14477 $this->gradients[$n]['colspace'] = 'DeviceCMYK'; 14478 if (!empty($background)) { 14479 $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100); 14480 } 14481 break; 14482 } 14483 case 3: { // RGB 14484 $this->gradients[$n]['colspace'] = 'DeviceRGB'; 14485 if (!empty($background)) { 14486 $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255); 14487 } 14488 break; 14489 } 14490 case 1: { // GRAY SCALE 14491 $this->gradients[$n]['colspace'] = 'DeviceGray'; 14492 if (!empty($background)) { 14493 $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255); 14494 } 14495 break; 14496 } 14497 } 14498 $num_stops = count($stops); 14499 $last_stop_id = $num_stops - 1; 14500 foreach ($stops as $key => $stop) { 14501 $this->gradients[$n]['colors'][$key] = array(); 14502 // offset represents a location along the gradient vector 14503 if (isset($stop['offset'])) { 14504 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset']; 14505 } else { 14506 if ($key == 0) { 14507 $this->gradients[$n]['colors'][$key]['offset'] = 0; 14508 } elseif ($key == $last_stop_id) { 14509 $this->gradients[$n]['colors'][$key]['offset'] = 1; 14510 } else { 14511 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key); 14512 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep; 14513 } 14514 } 14515 if (isset($stop['opacity'])) { 14516 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity']; 14517 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) { 14518 $this->gradients[$n]['transparency'] = true; 14519 } 14520 } else { 14521 $this->gradients[$n]['colors'][$key]['opacity'] = 1; 14522 } 14523 // exponent for the exponential interpolation function 14524 if (isset($stop['exponent'])) { 14525 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent']; 14526 } else { 14527 $this->gradients[$n]['colors'][$key]['exponent'] = 1; 14528 } 14529 // set colors 14530 $color = array_values($stop['color']); 14531 switch($numcolspace) { 14532 case 5: // SPOT 14533 case 4: { // CMYK 14534 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100); 14535 break; 14536 } 14537 case 3: { // RGB 14538 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255); 14539 break; 14540 } 14541 case 1: { // GRAY SCALE 14542 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255); 14543 break; 14544 } 14545 } 14546 } 14547 if ($this->gradients[$n]['transparency']) { 14548 // paint luminosity gradient 14549 $this->_out('/TGS'.$n.' gs'); 14550 } 14551 //paint the gradient 14552 $this->_out('/Sh'.$n.' sh'); 14553 //restore previous Graphic State 14554 $this->_outRestoreGraphicsState(); 14555 if ($this->inxobj) { 14556 // we are inside an XObject template 14557 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; 14558 } 14559 } 14560 14561 /** 14562 * Output gradient shaders. 14563 * @author Nicola Asuni 14564 * @since 3.1.000 (2008-06-09) 14565 * @protected 14566 */ 14567 function _putshaders() { 14568 if ($this->pdfa_mode) { 14569 return; 14570 } 14571 $idt = count($this->gradients); //index for transparency gradients 14572 foreach ($this->gradients as $id => $grad) { 14573 if (($grad['type'] == 2) OR ($grad['type'] == 3)) { 14574 $fc = $this->_newobj(); 14575 $out = '<<'; 14576 $out .= ' /FunctionType 3'; 14577 $out .= ' /Domain [0 1]'; 14578 $functions = ''; 14579 $bounds = ''; 14580 $encode = ''; 14581 $i = 1; 14582 $num_cols = count($grad['colors']); 14583 $lastcols = $num_cols - 1; 14584 for ($i = 1; $i < $num_cols; ++$i) { 14585 $functions .= ($fc + $i).' 0 R '; 14586 if ($i < $lastcols) { 14587 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']); 14588 } 14589 $encode .= '0 1 '; 14590 } 14591 $out .= ' /Functions ['.trim($functions).']'; 14592 $out .= ' /Bounds ['.trim($bounds).']'; 14593 $out .= ' /Encode ['.trim($encode).']'; 14594 $out .= ' >>'; 14595 $out .= "\n".'endobj'; 14596 $this->_out($out); 14597 for ($i = 1; $i < $num_cols; ++$i) { 14598 $this->_newobj(); 14599 $out = '<<'; 14600 $out .= ' /FunctionType 2'; 14601 $out .= ' /Domain [0 1]'; 14602 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']'; 14603 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']'; 14604 $out .= ' /N '.$grad['colors'][$i]['exponent']; 14605 $out .= ' >>'; 14606 $out .= "\n".'endobj'; 14607 $this->_out($out); 14608 } 14609 // set transparency functions 14610 if ($grad['transparency']) { 14611 $ft = $this->_newobj(); 14612 $out = '<<'; 14613 $out .= ' /FunctionType 3'; 14614 $out .= ' /Domain [0 1]'; 14615 $functions = ''; 14616 $i = 1; 14617 $num_cols = count($grad['colors']); 14618 for ($i = 1; $i < $num_cols; ++$i) { 14619 $functions .= ($ft + $i).' 0 R '; 14620 } 14621 $out .= ' /Functions ['.trim($functions).']'; 14622 $out .= ' /Bounds ['.trim($bounds).']'; 14623 $out .= ' /Encode ['.trim($encode).']'; 14624 $out .= ' >>'; 14625 $out .= "\n".'endobj'; 14626 $this->_out($out); 14627 for ($i = 1; $i < $num_cols; ++$i) { 14628 $this->_newobj(); 14629 $out = '<<'; 14630 $out .= ' /FunctionType 2'; 14631 $out .= ' /Domain [0 1]'; 14632 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']'; 14633 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']'; 14634 $out .= ' /N '.$grad['colors'][$i]['exponent']; 14635 $out .= ' >>'; 14636 $out .= "\n".'endobj'; 14637 $this->_out($out); 14638 } 14639 } 14640 } 14641 // set shading object 14642 $this->_newobj(); 14643 $out = '<< /ShadingType '.$grad['type']; 14644 if (isset($grad['colspace'])) { 14645 $out .= ' /ColorSpace /'.$grad['colspace']; 14646 } else { 14647 $out .= ' /ColorSpace /DeviceRGB'; 14648 } 14649 if (isset($grad['background']) AND !empty($grad['background'])) { 14650 $out .= ' /Background ['.$grad['background'].']'; 14651 } 14652 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) { 14653 $out .= ' /AntiAlias true'; 14654 } 14655 if ($grad['type'] == 2) { 14656 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]); 14657 $out .= ' /Domain [0 1]'; 14658 $out .= ' /Function '.$fc.' 0 R'; 14659 $out .= ' /Extend [true true]'; 14660 $out .= ' >>'; 14661 } elseif ($grad['type'] == 3) { 14662 //x0, y0, r0, x1, y1, r1 14663 //at this this time radius of inner circle is 0 14664 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]); 14665 $out .= ' /Domain [0 1]'; 14666 $out .= ' /Function '.$fc.' 0 R'; 14667 $out .= ' /Extend [true true]'; 14668 $out .= ' >>'; 14669 } elseif ($grad['type'] == 6) { 14670 $out .= ' /BitsPerCoordinate 16'; 14671 $out .= ' /BitsPerComponent 8'; 14672 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]'; 14673 $out .= ' /BitsPerFlag 8'; 14674 $stream = $this->_getrawstream($grad['stream']); 14675 $out .= ' /Length '.strlen($stream); 14676 $out .= ' >>'; 14677 $out .= ' stream'."\n".$stream."\n".'endstream'; 14678 } 14679 $out .= "\n".'endobj'; 14680 $this->_out($out); 14681 if ($grad['transparency']) { 14682 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out); 14683 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency); 14684 } 14685 $this->gradients[$id]['id'] = $this->n; 14686 // set pattern object 14687 $this->_newobj(); 14688 $out = '<< /Type /Pattern /PatternType 2'; 14689 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R'; 14690 $out .= ' >>'; 14691 $out .= "\n".'endobj'; 14692 $this->_out($out); 14693 $this->gradients[$id]['pattern'] = $this->n; 14694 // set shading and pattern for transparency mask 14695 if ($grad['transparency']) { 14696 // luminosity pattern 14697 $idgs = $id + $idt; 14698 $this->_newobj(); 14699 $this->_out($shading_transparency); 14700 $this->gradients[$idgs]['id'] = $this->n; 14701 $this->_newobj(); 14702 $out = '<< /Type /Pattern /PatternType 2'; 14703 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R'; 14704 $out .= ' >>'; 14705 $out .= "\n".'endobj'; 14706 $this->_out($out); 14707 $this->gradients[$idgs]['pattern'] = $this->n; 14708 // luminosity XObject 14709 $oid = $this->_newobj(); 14710 $this->xobjects['LX'.$oid] = array('n' => $oid); 14711 $filter = ''; 14712 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q'; 14713 if ($this->compress) { 14714 $filter = ' /Filter /FlateDecode'; 14715 $stream = gzcompress($stream); 14716 } 14717 $stream = $this->_getrawstream($stream); 14718 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter; 14719 $out .= ' /Length '.strlen($stream); 14720 $rect = sprintf('%F %F', $this->wPt, $this->hPt); 14721 $out .= ' /BBox [0 0 '.$rect.']'; 14722 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>'; 14723 $out .= ' /Resources <<'; 14724 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>'; 14725 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>'; 14726 $out .= ' >>'; 14727 $out .= ' >> '; 14728 $out .= ' stream'."\n".$stream."\n".'endstream'; 14729 $out .= "\n".'endobj'; 14730 $this->_out($out); 14731 // SMask 14732 $this->_newobj(); 14733 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj'; 14734 $this->_out($out); 14735 // ExtGState 14736 $this->_newobj(); 14737 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj'; 14738 $this->_out($out); 14739 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id); 14740 } 14741 } 14742 } 14743 14744 /** 14745 * Draw the sector of a circle. 14746 * It can be used for instance to render pie charts. 14747 * @param $xc (float) abscissa of the center. 14748 * @param $yc (float) ordinate of the center. 14749 * @param $r (float) radius. 14750 * @param $a (float) start angle (in degrees). 14751 * @param $b (float) end angle (in degrees). 14752 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 14753 * @param $cw: (float) indicates whether to go clockwise (default: true). 14754 * @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. 14755 * @author Maxime Delorme, Nicola Asuni 14756 * @since 3.1.000 (2008-06-09) 14757 * @public 14758 */ 14759 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) { 14760 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o); 14761 } 14762 14763 /** 14764 * Draw the sector of an ellipse. 14765 * It can be used for instance to render pie charts. 14766 * @param $xc (float) abscissa of the center. 14767 * @param $yc (float) ordinate of the center. 14768 * @param $rx (float) the x-axis radius. 14769 * @param $ry (float) the y-axis radius. 14770 * @param $a (float) start angle (in degrees). 14771 * @param $b (float) end angle (in degrees). 14772 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 14773 * @param $cw: (float) indicates whether to go clockwise. 14774 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). 14775 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc. 14776 * @author Maxime Delorme, Nicola Asuni 14777 * @since 3.1.000 (2008-06-09) 14778 * @public 14779 */ 14780 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) { 14781 if ($this->state != 2) { 14782 return; 14783 } 14784 if ($this->rtl) { 14785 $xc = ($this->w - $xc); 14786 } 14787 $op = TCPDF_STATIC::getPathPaintOperator($style); 14788 if ($op == 'f') { 14789 $line_style = array(); 14790 } 14791 if ($cw) { 14792 $d = $b; 14793 $b = (360 - $a + $o); 14794 $a = (360 - $d + $o); 14795 } else { 14796 $b += $o; 14797 $a += $o; 14798 } 14799 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc); 14800 $this->_out($op); 14801 } 14802 14803 /** 14804 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files. 14805 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library. 14806 * Only vector drawing is supported, not text or bitmap. 14807 * 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). 14808 * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string. 14809 * @param $x (float) Abscissa of the upper-left corner. 14810 * @param $y (float) Ordinate of the upper-left corner. 14811 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 14812 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 14813 * @param $link (mixed) URL or identifier returned by AddLink(). 14814 * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true. 14815 * @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> 14816 * @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> 14817 * @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))) 14818 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions. 14819 * @param $fixoutvals (boolean) if true remove values outside the bounding box. 14820 * @author Valentin Schmidt, Nicola Asuni 14821 * @since 3.1.000 (2008-06-09) 14822 * @public 14823 */ 14824 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) { 14825 if ($this->state != 2) { 14826 return; 14827 } 14828 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { 14829 // convert EPS to raster image using GD or ImageMagick libraries 14830 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 14831 } 14832 if ($x === '') { 14833 $x = $this->x; 14834 } 14835 if ($y === '') { 14836 $y = $this->y; 14837 } 14838 // check page for no-write regions and adapt page margins if necessary 14839 list($x, $y) = $this->checkPageRegions($h, $x, $y); 14840 $k = $this->k; 14841 if ($file[0] === '@') { // image from string 14842 $data = substr($file, 1); 14843 } else { // EPS/AI file 14844 $data = TCPDF_STATIC::fileGetContents($file); 14845 } 14846 if ($data === FALSE) { 14847 $this->Error('EPS file not found: '.$file); 14848 } 14849 $regs = array(); 14850 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!) 14851 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator 14852 if (count($regs) > 1) { 14853 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0" 14854 if (strpos($version_str, 'Adobe Illustrator') !== false) { 14855 $versexp = explode(' ', $version_str); 14856 $version = (float)array_pop($versexp); 14857 if ($version >= 9) { 14858 $this->Error('This version of Adobe Illustrator file is not supported: '.$file); 14859 } 14860 } 14861 } 14862 // strip binary bytes in front of PS-header 14863 $start = strpos($data, '%!PS-Adobe'); 14864 if ($start > 0) { 14865 $data = substr($data, $start); 14866 } 14867 // find BoundingBox params 14868 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs); 14869 if (count($regs) > 1) { 14870 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1])); 14871 } else { 14872 $this->Error('No BoundingBox found in EPS/AI file: '.$file); 14873 } 14874 $start = strpos($data, '%%EndSetup'); 14875 if ($start === false) { 14876 $start = strpos($data, '%%EndProlog'); 14877 } 14878 if ($start === false) { 14879 $start = strpos($data, '%%BoundingBox'); 14880 } 14881 $data = substr($data, $start); 14882 $end = strpos($data, '%%PageTrailer'); 14883 if ($end===false) { 14884 $end = strpos($data, 'showpage'); 14885 } 14886 if ($end) { 14887 $data = substr($data, 0, $end); 14888 } 14889 // calculate image width and height on document 14890 if (($w <= 0) AND ($h <= 0)) { 14891 $w = ($x2 - $x1) / $k; 14892 $h = ($y2 - $y1) / $k; 14893 } elseif ($w <= 0) { 14894 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k)); 14895 } elseif ($h <= 0) { 14896 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k)); 14897 } 14898 // fit the image on available space 14899 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 14900 if ($this->rasterize_vector_images) { 14901 // convert EPS to raster image using GD or ImageMagick libraries 14902 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 14903 } 14904 // set scaling factors 14905 $scale_x = $w / (($x2 - $x1) / $k); 14906 $scale_y = $h / (($y2 - $y1) / $k); 14907 // set alignment 14908 $this->img_rb_y = $y + $h; 14909 // set alignment 14910 if ($this->rtl) { 14911 if ($palign == 'L') { 14912 $ximg = $this->lMargin; 14913 } elseif ($palign == 'C') { 14914 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 14915 } elseif ($palign == 'R') { 14916 $ximg = $this->w - $this->rMargin - $w; 14917 } else { 14918 $ximg = $x - $w; 14919 } 14920 $this->img_rb_x = $ximg; 14921 } else { 14922 if ($palign == 'L') { 14923 $ximg = $this->lMargin; 14924 } elseif ($palign == 'C') { 14925 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 14926 } elseif ($palign == 'R') { 14927 $ximg = $this->w - $this->rMargin - $w; 14928 } else { 14929 $ximg = $x; 14930 } 14931 $this->img_rb_x = $ximg + $w; 14932 } 14933 if ($useBoundingBox) { 14934 $dx = $ximg * $k - $x1; 14935 $dy = $y * $k - $y1; 14936 } else { 14937 $dx = $ximg * $k; 14938 $dy = $y * $k; 14939 } 14940 // save the current graphic state 14941 $this->_out('q'.$this->epsmarker); 14942 // translate 14943 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1)))); 14944 // scale 14945 if (isset($scale_x)) { 14946 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y))); 14947 } 14948 // handle pc/unix/mac line endings 14949 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY); 14950 $u=0; 14951 $cnt = count($lines); 14952 for ($i=0; $i < $cnt; ++$i) { 14953 $line = $lines[$i]; 14954 if (($line == '') OR ($line[0] == '%')) { 14955 continue; 14956 } 14957 $len = strlen($line); 14958 // check for spot color names 14959 $color_name = ''; 14960 if (strcasecmp('x', substr(trim($line), -1)) == 0) { 14961 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) { 14962 // extract spot color name 14963 $color_name = $matches[0]; 14964 // remove color name from string 14965 $line = str_replace(' '.$color_name, '', $line); 14966 // remove pharentesis from color name 14967 $color_name = substr($color_name, 1, -1); 14968 } 14969 } 14970 $chunks = explode(' ', $line); 14971 $cmd = trim(array_pop($chunks)); 14972 // RGB 14973 if (($cmd == 'Xa') OR ($cmd == 'XA')) { 14974 $b = array_pop($chunks); 14975 $g = array_pop($chunks); 14976 $r = array_pop($chunks); 14977 $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! 14978 continue; 14979 } 14980 $skip = false; 14981 if ($fixoutvals) { 14982 // check for values outside the bounding box 14983 switch ($cmd) { 14984 case 'm': 14985 case 'l': 14986 case 'L': { 14987 // skip values outside bounding box 14988 foreach ($chunks as $key => $val) { 14989 if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) { 14990 $skip = true; 14991 } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) { 14992 $skip = true; 14993 } 14994 } 14995 } 14996 } 14997 } 14998 switch ($cmd) { 14999 case 'm': 15000 case 'l': 15001 case 'v': 15002 case 'y': 15003 case 'c': 15004 case 'k': 15005 case 'K': 15006 case 'g': 15007 case 'G': 15008 case 's': 15009 case 'S': 15010 case 'J': 15011 case 'j': 15012 case 'w': 15013 case 'M': 15014 case 'd': 15015 case 'n': { 15016 if ($skip) { 15017 break; 15018 } 15019 $this->_out($line); 15020 break; 15021 } 15022 case 'x': {// custom fill color 15023 if (empty($color_name)) { 15024 // CMYK color 15025 list($col_c, $col_m, $col_y, $col_k) = $chunks; 15026 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k'); 15027 } else { 15028 // Spot Color (CMYK + tint) 15029 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; 15030 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); 15031 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t)); 15032 $this->_out($color_cmd); 15033 } 15034 break; 15035 } 15036 case 'X': { // custom stroke color 15037 if (empty($color_name)) { 15038 // CMYK color 15039 list($col_c, $col_m, $col_y, $col_k) = $chunks; 15040 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K'); 15041 } else { 15042 // Spot Color (CMYK + tint) 15043 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; 15044 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); 15045 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t)); 15046 $this->_out($color_cmd); 15047 } 15048 break; 15049 } 15050 case 'Y': 15051 case 'N': 15052 case 'V': 15053 case 'L': 15054 case 'C': { 15055 if ($skip) { 15056 break; 15057 } 15058 $line[($len - 1)] = strtolower($cmd); 15059 $this->_out($line); 15060 break; 15061 } 15062 case 'b': 15063 case 'B': { 15064 $this->_out($cmd . '*'); 15065 break; 15066 } 15067 case 'f': 15068 case 'F': { 15069 if ($u > 0) { 15070 $isU = false; 15071 $max = min(($i + 5), $cnt); 15072 for ($j = ($i + 1); $j < $max; ++$j) { 15073 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U'))); 15074 } 15075 if ($isU) { 15076 $this->_out('f*'); 15077 } 15078 } else { 15079 $this->_out('f*'); 15080 } 15081 break; 15082 } 15083 case '*u': { 15084 ++$u; 15085 break; 15086 } 15087 case '*U': { 15088 --$u; 15089 break; 15090 } 15091 } 15092 } 15093 // restore previous graphic state 15094 $this->_out($this->epsmarker.'Q'); 15095 if (!empty($border)) { 15096 $bx = $this->x; 15097 $by = $this->y; 15098 $this->x = $ximg; 15099 if ($this->rtl) { 15100 $this->x += $w; 15101 } 15102 $this->y = $y; 15103 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 15104 $this->x = $bx; 15105 $this->y = $by; 15106 } 15107 if ($link) { 15108 $this->Link($ximg, $y, $w, $h, $link, 0); 15109 } 15110 // set pointer to align the next text/objects 15111 switch($align) { 15112 case 'T':{ 15113 $this->y = $y; 15114 $this->x = $this->img_rb_x; 15115 break; 15116 } 15117 case 'M':{ 15118 $this->y = $y + round($h/2); 15119 $this->x = $this->img_rb_x; 15120 break; 15121 } 15122 case 'B':{ 15123 $this->y = $this->img_rb_y; 15124 $this->x = $this->img_rb_x; 15125 break; 15126 } 15127 case 'N':{ 15128 $this->SetY($this->img_rb_y); 15129 break; 15130 } 15131 default:{ 15132 break; 15133 } 15134 } 15135 $this->endlinex = $this->img_rb_x; 15136 } 15137 15138 /** 15139 * Set document barcode. 15140 * @param $bc (string) barcode 15141 * @public 15142 */ 15143 public function setBarcode($bc='') { 15144 $this->barcode = $bc; 15145 } 15146 15147 /** 15148 * Get current barcode. 15149 * @return string 15150 * @public 15151 * @since 4.0.012 (2008-07-24) 15152 */ 15153 public function getBarcode() { 15154 return $this->barcode; 15155 } 15156 15157 /** 15158 * Print a Linear Barcode. 15159 * @param $code (string) code to print 15160 * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats). 15161 * @param $x (int) x position in user units (empty string = current x position) 15162 * @param $y (int) y position in user units (empty string = current y position) 15163 * @param $w (int) width in user units (empty string = remaining page width) 15164 * @param $h (int) height in user units (empty string = remaining page height) 15165 * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm) 15166 * @param $style (array) array of options:<ul> 15167 * <li>boolean $style['border'] if true prints a border</li> 15168 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li> 15169 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li> 15170 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li> 15171 * <li>array $style['fgcolor'] color array for bars and text</li> 15172 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li> 15173 * <li>boolean $style['text'] if true prints text below the barcode</li> 15174 * <li>string $style['label'] override default label</li> 15175 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li> 15176 * <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> 15177 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li> 15178 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li> 15179 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li> 15180 * <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> 15181 * <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> 15182 * @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> 15183 * @author Nicola Asuni 15184 * @since 3.1.000 (2008-06-09) 15185 * @public 15186 */ 15187 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') { 15188 if (TCPDF_STATIC::empty_string(trim($code))) { 15189 return; 15190 } 15191 require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php'); 15192 // save current graphic settings 15193 $gvars = $this->getGraphicVars(); 15194 // create new barcode object 15195 $barcodeobj = new TCPDFBarcode($code, $type); 15196 $arrcode = $barcodeobj->getBarcodeArray(); 15197 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) { 15198 $this->Error('Error in 1D barcode string'); 15199 } 15200 if ($arrcode['maxh'] <= 0) { 15201 $arrcode['maxh'] = 1; 15202 } 15203 // set default values 15204 if (!isset($style['position'])) { 15205 $style['position'] = ''; 15206 } elseif ($style['position'] == 'S') { 15207 // keep this for backward compatibility 15208 $style['position'] = ''; 15209 $style['stretch'] = true; 15210 } 15211 if (!isset($style['fitwidth'])) { 15212 if (!isset($style['stretch'])) { 15213 $style['fitwidth'] = true; 15214 } else { 15215 $style['fitwidth'] = false; 15216 } 15217 } 15218 if ($style['fitwidth']) { 15219 // disable stretch 15220 $style['stretch'] = false; 15221 } 15222 if (!isset($style['stretch'])) { 15223 if (($w === '') OR ($w <= 0)) { 15224 $style['stretch'] = false; 15225 } else { 15226 $style['stretch'] = true; 15227 } 15228 } 15229 if (!isset($style['fgcolor'])) { 15230 $style['fgcolor'] = array(0,0,0); // default black 15231 } 15232 if (!isset($style['bgcolor'])) { 15233 $style['bgcolor'] = false; // default transparent 15234 } 15235 if (!isset($style['border'])) { 15236 $style['border'] = false; 15237 } 15238 $fontsize = 0; 15239 if (!isset($style['text'])) { 15240 $style['text'] = false; 15241 } 15242 if ($style['text'] AND isset($style['font'])) { 15243 if (isset($style['fontsize'])) { 15244 $fontsize = $style['fontsize']; 15245 } 15246 $this->SetFont($style['font'], '', $fontsize); 15247 } 15248 if (!isset($style['stretchtext'])) { 15249 $style['stretchtext'] = 4; 15250 } 15251 if ($x === '') { 15252 $x = $this->x; 15253 } 15254 if ($y === '') { 15255 $y = $this->y; 15256 } 15257 // check page for no-write regions and adapt page margins if necessary 15258 list($x, $y) = $this->checkPageRegions($h, $x, $y); 15259 if (($w === '') OR ($w <= 0)) { 15260 if ($this->rtl) { 15261 $w = $x - $this->lMargin; 15262 } else { 15263 $w = $this->w - $this->rMargin - $x; 15264 } 15265 } 15266 // padding 15267 if (!isset($style['padding'])) { 15268 $padding = 0; 15269 } elseif ($style['padding'] === 'auto') { 15270 $padding = 10 * ($w / ($arrcode['maxw'] + 20)); 15271 } else { 15272 $padding = floatval($style['padding']); 15273 } 15274 // horizontal padding 15275 if (!isset($style['hpadding'])) { 15276 $hpadding = $padding; 15277 } elseif ($style['hpadding'] === 'auto') { 15278 $hpadding = 10 * ($w / ($arrcode['maxw'] + 20)); 15279 } else { 15280 $hpadding = floatval($style['hpadding']); 15281 } 15282 // vertical padding 15283 if (!isset($style['vpadding'])) { 15284 $vpadding = $padding; 15285 } elseif ($style['vpadding'] === 'auto') { 15286 $vpadding = ($hpadding / 2); 15287 } else { 15288 $vpadding = floatval($style['vpadding']); 15289 } 15290 // calculate xres (single bar width) 15291 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw']; 15292 if ($style['stretch']) { 15293 $xres = $max_xres; 15294 } else { 15295 if (TCPDF_STATIC::empty_string($xres)) { 15296 $xres = (0.141 * $this->k); // default bar width = 0.4 mm 15297 } 15298 if ($xres > $max_xres) { 15299 // correct xres to fit on $w 15300 $xres = $max_xres; 15301 } 15302 if ((isset($style['padding']) AND ($style['padding'] === 'auto')) 15303 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) { 15304 $hpadding = 10 * $xres; 15305 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) { 15306 $vpadding = ($hpadding / 2); 15307 } 15308 } 15309 } 15310 if ($style['fitwidth']) { 15311 $wold = $w; 15312 $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding)); 15313 if (isset($style['cellfitalign'])) { 15314 switch ($style['cellfitalign']) { 15315 case 'L': { 15316 if ($this->rtl) { 15317 $x -= ($wold - $w); 15318 } 15319 break; 15320 } 15321 case 'R': { 15322 if (!$this->rtl) { 15323 $x += ($wold - $w); 15324 } 15325 break; 15326 } 15327 case 'C': { 15328 if ($this->rtl) { 15329 $x -= (($wold - $w) / 2); 15330 } else { 15331 $x += (($wold - $w) / 2); 15332 } 15333 break; 15334 } 15335 default : { 15336 break; 15337 } 15338 } 15339 } 15340 } 15341 $text_height = $this->getCellHeight($fontsize / $this->k); 15342 // height 15343 if (($h === '') OR ($h <= 0)) { 15344 // set default height 15345 $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height; 15346 } 15347 $barh = $h - $text_height - (2 * $vpadding); 15348 if ($barh <=0) { 15349 // try to reduce font or padding to fit barcode on available height 15350 if ($text_height > $h) { 15351 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio)); 15352 $text_height = $this->getCellHeight($fontsize / $this->k); 15353 $this->SetFont($style['font'], '', $fontsize); 15354 } 15355 if ($vpadding > 0) { 15356 $vpadding = (($h - $text_height) / 4); 15357 } 15358 $barh = $h - $text_height - (2 * $vpadding); 15359 } 15360 // fit the barcode on available space 15361 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); 15362 // set alignment 15363 $this->img_rb_y = $y + $h; 15364 // set alignment 15365 if ($this->rtl) { 15366 if ($style['position'] == 'L') { 15367 $xpos = $this->lMargin; 15368 } elseif ($style['position'] == 'C') { 15369 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15370 } elseif ($style['position'] == 'R') { 15371 $xpos = $this->w - $this->rMargin - $w; 15372 } else { 15373 $xpos = $x - $w; 15374 } 15375 $this->img_rb_x = $xpos; 15376 } else { 15377 if ($style['position'] == 'L') { 15378 $xpos = $this->lMargin; 15379 } elseif ($style['position'] == 'C') { 15380 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15381 } elseif ($style['position'] == 'R') { 15382 $xpos = $this->w - $this->rMargin - $w; 15383 } else { 15384 $xpos = $x; 15385 } 15386 $this->img_rb_x = $xpos + $w; 15387 } 15388 $xpos_rect = $xpos; 15389 if (!isset($style['align'])) { 15390 $style['align'] = 'C'; 15391 } 15392 switch ($style['align']) { 15393 case 'L': { 15394 $xpos = $xpos_rect + $hpadding; 15395 break; 15396 } 15397 case 'R': { 15398 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding; 15399 break; 15400 } 15401 case 'C': 15402 default : { 15403 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2); 15404 break; 15405 } 15406 } 15407 $xpos_text = $xpos; 15408 // barcode is always printed in LTR direction 15409 $tempRTL = $this->rtl; 15410 $this->rtl = false; 15411 // print background color 15412 if ($style['bgcolor']) { 15413 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 15414 } elseif ($style['border']) { 15415 $this->Rect($xpos_rect, $y, $w, $h, 'D'); 15416 } 15417 // set foreground color 15418 $this->SetDrawColorArray($style['fgcolor']); 15419 $this->SetTextColorArray($style['fgcolor']); 15420 // print bars 15421 foreach ($arrcode['bcode'] as $k => $v) { 15422 $bw = ($v['w'] * $xres); 15423 if ($v['t']) { 15424 // draw a vertical bar 15425 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']); 15426 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']); 15427 } 15428 $xpos += $bw; 15429 } 15430 // print text 15431 if ($style['text']) { 15432 if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) { 15433 $label = $style['label']; 15434 } else { 15435 $label = $code; 15436 } 15437 $txtwidth = ($arrcode['maxw'] * $xres); 15438 if ($this->GetStringWidth($label) > $txtwidth) { 15439 $style['stretchtext'] = 2; 15440 } 15441 // print text 15442 $this->x = $xpos_text; 15443 $this->y = $y + $vpadding + $barh; 15444 $cellpadding = $this->cell_padding; 15445 $this->SetCellPadding(0); 15446 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T'); 15447 $this->cell_padding = $cellpadding; 15448 } 15449 // restore original direction 15450 $this->rtl = $tempRTL; 15451 // restore previous settings 15452 $this->setGraphicVars($gvars); 15453 // set pointer to align the next text/objects 15454 switch($align) { 15455 case 'T':{ 15456 $this->y = $y; 15457 $this->x = $this->img_rb_x; 15458 break; 15459 } 15460 case 'M':{ 15461 $this->y = $y + round($h / 2); 15462 $this->x = $this->img_rb_x; 15463 break; 15464 } 15465 case 'B':{ 15466 $this->y = $this->img_rb_y; 15467 $this->x = $this->img_rb_x; 15468 break; 15469 } 15470 case 'N':{ 15471 $this->SetY($this->img_rb_y); 15472 break; 15473 } 15474 default:{ 15475 break; 15476 } 15477 } 15478 $this->endlinex = $this->img_rb_x; 15479 } 15480 15481 /** 15482 * Print 2D Barcode. 15483 * @param $code (string) code to print 15484 * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats). 15485 * @param $x (int) x position in user units 15486 * @param $y (int) y position in user units 15487 * @param $w (int) width in user units 15488 * @param $h (int) height in user units 15489 * @param $style (array) array of options:<ul> 15490 * <li>boolean $style['border'] if true prints a border around the barcode</li> 15491 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li> 15492 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li> 15493 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li> 15494 * <li>int $style['module_width'] width of a single module in points</li> 15495 * <li>int $style['module_height'] height of a single module in points</li> 15496 * <li>array $style['fgcolor'] color array for bars and text</li> 15497 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li> 15498 * <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> 15499 * <li>$style['module_height'] height of a single module in points</li></ul> 15500 * @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> 15501 * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio 15502 * @author Nicola Asuni 15503 * @since 4.5.037 (2009-04-07) 15504 * @public 15505 */ 15506 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) { 15507 if (TCPDF_STATIC::empty_string(trim($code))) { 15508 return; 15509 } 15510 require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php'); 15511 // save current graphic settings 15512 $gvars = $this->getGraphicVars(); 15513 // create new barcode object 15514 $barcodeobj = new TCPDF2DBarcode($code, $type); 15515 $arrcode = $barcodeobj->getBarcodeArray(); 15516 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)) { 15517 $this->Error('Error in 2D barcode string'); 15518 } 15519 // set default values 15520 if (!isset($style['position'])) { 15521 $style['position'] = ''; 15522 } 15523 if (!isset($style['fgcolor'])) { 15524 $style['fgcolor'] = array(0,0,0); // default black 15525 } 15526 if (!isset($style['bgcolor'])) { 15527 $style['bgcolor'] = false; // default transparent 15528 } 15529 if (!isset($style['border'])) { 15530 $style['border'] = false; 15531 } 15532 // padding 15533 if (!isset($style['padding'])) { 15534 $style['padding'] = 0; 15535 } elseif ($style['padding'] === 'auto') { 15536 $style['padding'] = 4; 15537 } 15538 if (!isset($style['hpadding'])) { 15539 $style['hpadding'] = $style['padding']; 15540 } elseif ($style['hpadding'] === 'auto') { 15541 $style['hpadding'] = 4; 15542 } 15543 if (!isset($style['vpadding'])) { 15544 $style['vpadding'] = $style['padding']; 15545 } elseif ($style['vpadding'] === 'auto') { 15546 $style['vpadding'] = 4; 15547 } 15548 $hpad = (2 * $style['hpadding']); 15549 $vpad = (2 * $style['vpadding']); 15550 // cell (module) dimension 15551 if (!isset($style['module_width'])) { 15552 $style['module_width'] = 1; // width of a single module in points 15553 } 15554 if (!isset($style['module_height'])) { 15555 $style['module_height'] = 1; // height of a single module in points 15556 } 15557 if ($x === '') { 15558 $x = $this->x; 15559 } 15560 if ($y === '') { 15561 $y = $this->y; 15562 } 15563 // check page for no-write regions and adapt page margins if necessary 15564 list($x, $y) = $this->checkPageRegions($h, $x, $y); 15565 // number of barcode columns and rows 15566 $rows = $arrcode['num_rows']; 15567 $cols = $arrcode['num_cols']; 15568 if (($rows <= 0) || ($cols <= 0)){ 15569 $this->Error('Error in 2D barcode string'); 15570 } 15571 // module width and height 15572 $mw = $style['module_width']; 15573 $mh = $style['module_height']; 15574 if (($mw <= 0) OR ($mh <= 0)) { 15575 $this->Error('Error in 2D barcode string'); 15576 } 15577 // get max dimensions 15578 if ($this->rtl) { 15579 $maxw = $x - $this->lMargin; 15580 } else { 15581 $maxw = $this->w - $this->rMargin - $x; 15582 } 15583 $maxh = ($this->h - $this->tMargin - $this->bMargin); 15584 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad)); 15585 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad)); 15586 if (!$distort) { 15587 if (($maxw * $ratioHW) > $maxh) { 15588 $maxw = $maxh * $ratioWH; 15589 } 15590 if (($maxh * $ratioWH) > $maxw) { 15591 $maxh = $maxw * $ratioHW; 15592 } 15593 } 15594 // set maximum dimensions 15595 if ($w > $maxw) { 15596 $w = $maxw; 15597 } 15598 if ($h > $maxh) { 15599 $h = $maxh; 15600 } 15601 // set dimensions 15602 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) { 15603 $w = ($cols + $hpad) * ($mw / $this->k); 15604 $h = ($rows + $vpad) * ($mh / $this->k); 15605 } elseif (($w === '') OR ($w <= 0)) { 15606 $w = $h * $ratioWH; 15607 } elseif (($h === '') OR ($h <= 0)) { 15608 $h = $w * $ratioHW; 15609 } 15610 // barcode size (excluding padding) 15611 $bw = ($w * $cols) / ($cols + $hpad); 15612 $bh = ($h * $rows) / ($rows + $vpad); 15613 // dimension of single barcode cell unit 15614 $cw = $bw / $cols; 15615 $ch = $bh / $rows; 15616 if (!$distort) { 15617 if (($cw / $ch) > ($mw / $mh)) { 15618 // correct horizontal distortion 15619 $cw = $ch * $mw / $mh; 15620 $bw = $cw * $cols; 15621 $style['hpadding'] = ($w - $bw) / (2 * $cw); 15622 } else { 15623 // correct vertical distortion 15624 $ch = $cw * $mh / $mw; 15625 $bh = $ch * $rows; 15626 $style['vpadding'] = ($h - $bh) / (2 * $ch); 15627 } 15628 } 15629 // fit the barcode on available space 15630 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); 15631 // set alignment 15632 $this->img_rb_y = $y + $h; 15633 // set alignment 15634 if ($this->rtl) { 15635 if ($style['position'] == 'L') { 15636 $xpos = $this->lMargin; 15637 } elseif ($style['position'] == 'C') { 15638 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15639 } elseif ($style['position'] == 'R') { 15640 $xpos = $this->w - $this->rMargin - $w; 15641 } else { 15642 $xpos = $x - $w; 15643 } 15644 $this->img_rb_x = $xpos; 15645 } else { 15646 if ($style['position'] == 'L') { 15647 $xpos = $this->lMargin; 15648 } elseif ($style['position'] == 'C') { 15649 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15650 } elseif ($style['position'] == 'R') { 15651 $xpos = $this->w - $this->rMargin - $w; 15652 } else { 15653 $xpos = $x; 15654 } 15655 $this->img_rb_x = $xpos + $w; 15656 } 15657 $xstart = $xpos + ($style['hpadding'] * $cw); 15658 $ystart = $y + ($style['vpadding'] * $ch); 15659 // barcode is always printed in LTR direction 15660 $tempRTL = $this->rtl; 15661 $this->rtl = false; 15662 // print background color 15663 if ($style['bgcolor']) { 15664 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 15665 } elseif ($style['border']) { 15666 $this->Rect($xpos, $y, $w, $h, 'D'); 15667 } 15668 // set foreground color 15669 $this->SetDrawColorArray($style['fgcolor']); 15670 // print barcode cells 15671 // for each row 15672 for ($r = 0; $r < $rows; ++$r) { 15673 $xr = $xstart; 15674 // for each column 15675 for ($c = 0; $c < $cols; ++$c) { 15676 if ($arrcode['bcode'][$r][$c] == 1) { 15677 // draw a single barcode cell 15678 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']); 15679 } 15680 $xr += $cw; 15681 } 15682 $ystart += $ch; 15683 } 15684 // restore original direction 15685 $this->rtl = $tempRTL; 15686 // restore previous settings 15687 $this->setGraphicVars($gvars); 15688 // set pointer to align the next text/objects 15689 switch($align) { 15690 case 'T':{ 15691 $this->y = $y; 15692 $this->x = $this->img_rb_x; 15693 break; 15694 } 15695 case 'M':{ 15696 $this->y = $y + round($h/2); 15697 $this->x = $this->img_rb_x; 15698 break; 15699 } 15700 case 'B':{ 15701 $this->y = $this->img_rb_y; 15702 $this->x = $this->img_rb_x; 15703 break; 15704 } 15705 case 'N':{ 15706 $this->SetY($this->img_rb_y); 15707 break; 15708 } 15709 default:{ 15710 break; 15711 } 15712 } 15713 $this->endlinex = $this->img_rb_x; 15714 } 15715 15716 /** 15717 * Returns an array containing current margins: 15718 * <ul> 15719 <li>$ret['left'] = left margin</li> 15720 <li>$ret['right'] = right margin</li> 15721 <li>$ret['top'] = top margin</li> 15722 <li>$ret['bottom'] = bottom margin</li> 15723 <li>$ret['header'] = header margin</li> 15724 <li>$ret['footer'] = footer margin</li> 15725 <li>$ret['cell'] = cell padding array</li> 15726 <li>$ret['padding_left'] = cell left padding</li> 15727 <li>$ret['padding_top'] = cell top padding</li> 15728 <li>$ret['padding_right'] = cell right padding</li> 15729 <li>$ret['padding_bottom'] = cell bottom padding</li> 15730 * </ul> 15731 * @return array containing all margins measures 15732 * @public 15733 * @since 3.2.000 (2008-06-23) 15734 */ 15735 public function getMargins() { 15736 $ret = array( 15737 'left' => $this->lMargin, 15738 'right' => $this->rMargin, 15739 'top' => $this->tMargin, 15740 'bottom' => $this->bMargin, 15741 'header' => $this->header_margin, 15742 'footer' => $this->footer_margin, 15743 'cell' => $this->cell_padding, 15744 'padding_left' => $this->cell_padding['L'], 15745 'padding_top' => $this->cell_padding['T'], 15746 'padding_right' => $this->cell_padding['R'], 15747 'padding_bottom' => $this->cell_padding['B'] 15748 ); 15749 return $ret; 15750 } 15751 15752 /** 15753 * Returns an array containing original margins: 15754 * <ul> 15755 <li>$ret['left'] = left margin</li> 15756 <li>$ret['right'] = right margin</li> 15757 * </ul> 15758 * @return array containing all margins measures 15759 * @public 15760 * @since 4.0.012 (2008-07-24) 15761 */ 15762 public function getOriginalMargins() { 15763 $ret = array( 15764 'left' => $this->original_lMargin, 15765 'right' => $this->original_rMargin 15766 ); 15767 return $ret; 15768 } 15769 15770 /** 15771 * Returns the current font size. 15772 * @return current font size 15773 * @public 15774 * @since 3.2.000 (2008-06-23) 15775 */ 15776 public function getFontSize() { 15777 return $this->FontSize; 15778 } 15779 15780 /** 15781 * Returns the current font size in points unit. 15782 * @return current font size in points unit 15783 * @public 15784 * @since 3.2.000 (2008-06-23) 15785 */ 15786 public function getFontSizePt() { 15787 return $this->FontSizePt; 15788 } 15789 15790 /** 15791 * Returns the current font family name. 15792 * @return string current font family name 15793 * @public 15794 * @since 4.3.008 (2008-12-05) 15795 */ 15796 public function getFontFamily() { 15797 return $this->FontFamily; 15798 } 15799 15800 /** 15801 * Returns the current font style. 15802 * @return string current font style 15803 * @public 15804 * @since 4.3.008 (2008-12-05) 15805 */ 15806 public function getFontStyle() { 15807 return $this->FontStyle; 15808 } 15809 15810 /** 15811 * Cleanup HTML code (requires HTML Tidy library). 15812 * @param $html (string) htmlcode to fix 15813 * @param $default_css (string) CSS commands to add 15814 * @param $tagvs (array) parameters for setHtmlVSpace method 15815 * @param $tidy_options (array) options for tidy_parse_string function 15816 * @return string XHTML code cleaned up 15817 * @author Nicola Asuni 15818 * @public 15819 * @since 5.9.017 (2010-11-16) 15820 * @see setHtmlVSpace() 15821 */ 15822 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') { 15823 return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces); 15824 } 15825 15826 /** 15827 * Returns the border width from CSS property 15828 * @param $width (string) border width 15829 * @return int with in user units 15830 * @protected 15831 * @since 5.7.000 (2010-08-02) 15832 */ 15833 protected function getCSSBorderWidth($width) { 15834 if ($width == 'thin') { 15835 $width = (2 / $this->k); 15836 } elseif ($width == 'medium') { 15837 $width = (4 / $this->k); 15838 } elseif ($width == 'thick') { 15839 $width = (6 / $this->k); 15840 } else { 15841 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false); 15842 } 15843 return $width; 15844 } 15845 15846 /** 15847 * Returns the border dash style from CSS property 15848 * @param $style (string) border style to convert 15849 * @return int sash style (return -1 in case of none or hidden border) 15850 * @protected 15851 * @since 5.7.000 (2010-08-02) 15852 */ 15853 protected function getCSSBorderDashStyle($style) { 15854 switch (strtolower($style)) { 15855 case 'none': 15856 case 'hidden': { 15857 $dash = -1; 15858 break; 15859 } 15860 case 'dotted': { 15861 $dash = 1; 15862 break; 15863 } 15864 case 'dashed': { 15865 $dash = 3; 15866 break; 15867 } 15868 case 'double': 15869 case 'groove': 15870 case 'ridge': 15871 case 'inset': 15872 case 'outset': 15873 case 'solid': 15874 default: { 15875 $dash = 0; 15876 break; 15877 } 15878 } 15879 return $dash; 15880 } 15881 15882 /** 15883 * Returns the border style array from CSS border properties 15884 * @param $cssborder (string) border properties 15885 * @return array containing border properties 15886 * @protected 15887 * @since 5.7.000 (2010-08-02) 15888 */ 15889 protected function getCSSBorderStyle($cssborder) { 15890 $bprop = preg_split('/[\s]+/', trim($cssborder)); 15891 $border = array(); // value to be returned 15892 switch (count($bprop)) { 15893 case 3: { 15894 $width = $bprop[0]; 15895 $style = $bprop[1]; 15896 $color = $bprop[2]; 15897 break; 15898 } 15899 case 2: { 15900 $width = 'medium'; 15901 $style = $bprop[0]; 15902 $color = $bprop[1]; 15903 break; 15904 } 15905 case 1: { 15906 $width = 'medium'; 15907 $style = $bprop[0]; 15908 $color = 'black'; 15909 break; 15910 } 15911 default: { 15912 $width = 'medium'; 15913 $style = 'solid'; 15914 $color = 'black'; 15915 break; 15916 } 15917 } 15918 if ($style == 'none') { 15919 return array(); 15920 } 15921 $border['cap'] = 'square'; 15922 $border['join'] = 'miter'; 15923 $border['dash'] = $this->getCSSBorderDashStyle($style); 15924 if ($border['dash'] < 0) { 15925 return array(); 15926 } 15927 $border['width'] = $this->getCSSBorderWidth($width); 15928 $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors); 15929 return $border; 15930 } 15931 15932 /** 15933 * Get the internal Cell padding from CSS attribute. 15934 * @param $csspadding (string) padding properties 15935 * @param $width (float) width of the containing element 15936 * @return array of cell paddings 15937 * @public 15938 * @since 5.9.000 (2010-10-04) 15939 */ 15940 public function getCSSPadding($csspadding, $width=0) { 15941 $padding = preg_split('/[\s]+/', trim($csspadding)); 15942 $cell_padding = array(); // value to be returned 15943 switch (count($padding)) { 15944 case 4: { 15945 $cell_padding['T'] = $padding[0]; 15946 $cell_padding['R'] = $padding[1]; 15947 $cell_padding['B'] = $padding[2]; 15948 $cell_padding['L'] = $padding[3]; 15949 break; 15950 } 15951 case 3: { 15952 $cell_padding['T'] = $padding[0]; 15953 $cell_padding['R'] = $padding[1]; 15954 $cell_padding['B'] = $padding[2]; 15955 $cell_padding['L'] = $padding[1]; 15956 break; 15957 } 15958 case 2: { 15959 $cell_padding['T'] = $padding[0]; 15960 $cell_padding['R'] = $padding[1]; 15961 $cell_padding['B'] = $padding[0]; 15962 $cell_padding['L'] = $padding[1]; 15963 break; 15964 } 15965 case 1: { 15966 $cell_padding['T'] = $padding[0]; 15967 $cell_padding['R'] = $padding[0]; 15968 $cell_padding['B'] = $padding[0]; 15969 $cell_padding['L'] = $padding[0]; 15970 break; 15971 } 15972 default: { 15973 return $this->cell_padding; 15974 } 15975 } 15976 if ($width == 0) { 15977 $width = $this->w - $this->lMargin - $this->rMargin; 15978 } 15979 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false); 15980 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false); 15981 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false); 15982 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false); 15983 return $cell_padding; 15984 } 15985 15986 /** 15987 * Get the internal Cell margin from CSS attribute. 15988 * @param $cssmargin (string) margin properties 15989 * @param $width (float) width of the containing element 15990 * @return array of cell margins 15991 * @public 15992 * @since 5.9.000 (2010-10-04) 15993 */ 15994 public function getCSSMargin($cssmargin, $width=0) { 15995 $margin = preg_split('/[\s]+/', trim($cssmargin)); 15996 $cell_margin = array(); // value to be returned 15997 switch (count($margin)) { 15998 case 4: { 15999 $cell_margin['T'] = $margin[0]; 16000 $cell_margin['R'] = $margin[1]; 16001 $cell_margin['B'] = $margin[2]; 16002 $cell_margin['L'] = $margin[3]; 16003 break; 16004 } 16005 case 3: { 16006 $cell_margin['T'] = $margin[0]; 16007 $cell_margin['R'] = $margin[1]; 16008 $cell_margin['B'] = $margin[2]; 16009 $cell_margin['L'] = $margin[1]; 16010 break; 16011 } 16012 case 2: { 16013 $cell_margin['T'] = $margin[0]; 16014 $cell_margin['R'] = $margin[1]; 16015 $cell_margin['B'] = $margin[0]; 16016 $cell_margin['L'] = $margin[1]; 16017 break; 16018 } 16019 case 1: { 16020 $cell_margin['T'] = $margin[0]; 16021 $cell_margin['R'] = $margin[0]; 16022 $cell_margin['B'] = $margin[0]; 16023 $cell_margin['L'] = $margin[0]; 16024 break; 16025 } 16026 default: { 16027 return $this->cell_margin; 16028 } 16029 } 16030 if ($width == 0) { 16031 $width = $this->w - $this->lMargin - $this->rMargin; 16032 } 16033 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false); 16034 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false); 16035 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false); 16036 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false); 16037 return $cell_margin; 16038 } 16039 16040 /** 16041 * Get the border-spacing from CSS attribute. 16042 * @param $cssbspace (string) border-spacing CSS properties 16043 * @param $width (float) width of the containing element 16044 * @return array of border spacings 16045 * @public 16046 * @since 5.9.010 (2010-10-27) 16047 */ 16048 public function getCSSBorderMargin($cssbspace, $width=0) { 16049 $space = preg_split('/[\s]+/', trim($cssbspace)); 16050 $border_spacing = array(); // value to be returned 16051 switch (count($space)) { 16052 case 2: { 16053 $border_spacing['H'] = $space[0]; 16054 $border_spacing['V'] = $space[1]; 16055 break; 16056 } 16057 case 1: { 16058 $border_spacing['H'] = $space[0]; 16059 $border_spacing['V'] = $space[0]; 16060 break; 16061 } 16062 default: { 16063 return array('H' => 0, 'V' => 0); 16064 } 16065 } 16066 if ($width == 0) { 16067 $width = $this->w - $this->lMargin - $this->rMargin; 16068 } 16069 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false); 16070 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false); 16071 return $border_spacing; 16072 } 16073 16074 /** 16075 * Returns the letter-spacing value from CSS value 16076 * @param $spacing (string) letter-spacing value 16077 * @param $parent (float) font spacing (tracking) value of the parent element 16078 * @return float quantity to increases or decreases the space between characters in a text. 16079 * @protected 16080 * @since 5.9.000 (2010-10-02) 16081 */ 16082 protected function getCSSFontSpacing($spacing, $parent=0) { 16083 $val = 0; // value to be returned 16084 $spacing = trim($spacing); 16085 switch ($spacing) { 16086 case 'normal': { 16087 $val = 0; 16088 break; 16089 } 16090 case 'inherit': { 16091 if ($parent == 'normal') { 16092 $val = 0; 16093 } else { 16094 $val = $parent; 16095 } 16096 break; 16097 } 16098 default: { 16099 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false); 16100 } 16101 } 16102 return $val; 16103 } 16104 16105 /** 16106 * Returns the percentage of font stretching from CSS value 16107 * @param $stretch (string) stretch mode 16108 * @param $parent (float) stretch value of the parent element 16109 * @return float font stretching percentage 16110 * @protected 16111 * @since 5.9.000 (2010-10-02) 16112 */ 16113 protected function getCSSFontStretching($stretch, $parent=100) { 16114 $val = 100; // value to be returned 16115 $stretch = trim($stretch); 16116 switch ($stretch) { 16117 case 'ultra-condensed': { 16118 $val = 40; 16119 break; 16120 } 16121 case 'extra-condensed': { 16122 $val = 55; 16123 break; 16124 } 16125 case 'condensed': { 16126 $val = 70; 16127 break; 16128 } 16129 case 'semi-condensed': { 16130 $val = 85; 16131 break; 16132 } 16133 case 'normal': { 16134 $val = 100; 16135 break; 16136 } 16137 case 'semi-expanded': { 16138 $val = 115; 16139 break; 16140 } 16141 case 'expanded': { 16142 $val = 130; 16143 break; 16144 } 16145 case 'extra-expanded': { 16146 $val = 145; 16147 break; 16148 } 16149 case 'ultra-expanded': { 16150 $val = 160; 16151 break; 16152 } 16153 case 'wider': { 16154 $val = ($parent + 10); 16155 break; 16156 } 16157 case 'narrower': { 16158 $val = ($parent - 10); 16159 break; 16160 } 16161 case 'inherit': { 16162 if ($parent == 'normal') { 16163 $val = 100; 16164 } else { 16165 $val = $parent; 16166 } 16167 break; 16168 } 16169 default: { 16170 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false); 16171 } 16172 } 16173 return $val; 16174 } 16175 16176 /** 16177 * Convert HTML string containing font size value to points 16178 * @param $val (string) String containing font size value and unit. 16179 * @param $refsize (float) Reference font size in points. 16180 * @param $parent_size (float) Parent font size in points. 16181 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). 16182 * @return float value in points 16183 * @public 16184 */ 16185 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') { 16186 $refsize = TCPDF_FONTS::getFontRefSize($refsize); 16187 $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize); 16188 switch ($val) { 16189 case 'xx-small': { 16190 $size = ($refsize - 4); 16191 break; 16192 } 16193 case 'x-small': { 16194 $size = ($refsize - 3); 16195 break; 16196 } 16197 case 'small': { 16198 $size = ($refsize - 2); 16199 break; 16200 } 16201 case 'medium': { 16202 $size = $refsize; 16203 break; 16204 } 16205 case 'large': { 16206 $size = ($refsize + 2); 16207 break; 16208 } 16209 case 'x-large': { 16210 $size = ($refsize + 4); 16211 break; 16212 } 16213 case 'xx-large': { 16214 $size = ($refsize + 6); 16215 break; 16216 } 16217 case 'smaller': { 16218 $size = ($parent_size - 3); 16219 break; 16220 } 16221 case 'larger': { 16222 $size = ($parent_size + 3); 16223 break; 16224 } 16225 default: { 16226 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true); 16227 } 16228 } 16229 return $size; 16230 } 16231 16232 /** 16233 * Returns the HTML DOM array. 16234 * @param $html (string) html code 16235 * @return array 16236 * @protected 16237 * @since 3.2.000 (2008-06-20) 16238 */ 16239 protected function getHtmlDomArray($html) { 16240 // array of CSS styles ( selector => properties). 16241 $css = array(); 16242 // get CSS array defined at previous call 16243 $matches = array(); 16244 if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) { 16245 if (isset($matches[1][0])) { 16246 $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true)); 16247 } 16248 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html); 16249 } 16250 // extract external CSS files 16251 $matches = array(); 16252 if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) { 16253 foreach ($matches[1] as $key => $link) { 16254 $type = array(); 16255 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) { 16256 $type = array(); 16257 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type); 16258 // get 'all' and 'print' media, other media types are discarded 16259 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 16260 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { 16261 $type = array(); 16262 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) { 16263 // read CSS data file 16264 $cssdata = TCPDF_STATIC::fileGetContents(trim($type[1])); 16265 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) { 16266 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata)); 16267 } 16268 } 16269 } 16270 } 16271 } 16272 } 16273 // extract style tags 16274 $matches = array(); 16275 if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) { 16276 foreach ($matches[1] as $key => $media) { 16277 $type = array(); 16278 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type); 16279 // get 'all' and 'print' media, other media types are discarded 16280 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 16281 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { 16282 $cssdata = $matches[2][$key]; 16283 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata)); 16284 } 16285 } 16286 } 16287 // create a special tag to contain the CSS array (used for table content) 16288 $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>'; 16289 // remove head and style blocks 16290 $html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html); 16291 $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html); 16292 // define block tags 16293 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td'); 16294 // define self-closing tags 16295 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta'); 16296 // remove all unsupported tags (the line below lists all supported tags) 16297 $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>'); 16298 //replace some blank characters 16299 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag 16300 $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); 16301 $html = preg_replace('@(\r\n|\r)@', "\n", $html); 16302 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); 16303 $html = strtr($html, $repTable); 16304 $offset = 0; 16305 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) { 16306 $html_a = substr($html, 0, $offset); 16307 $html_b = substr($html, $offset, ($pos - $offset + 6)); 16308 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) { 16309 // preserve newlines on <pre> tag 16310 $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b); 16311 } 16312 while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) { 16313 // preserve spaces on <pre> tag 16314 $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2 \\3</pre>", $html_b); 16315 } 16316 $html = $html_a.$html_b.substr($html, $pos + 6); 16317 $offset = strlen($html_a.$html_b); 16318 } 16319 $offset = 0; 16320 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) { 16321 $html_a = substr($html, 0, $offset); 16322 $html_b = substr($html, $offset, ($pos - $offset + 11)); 16323 while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) { 16324 // preserve newlines on <textarea> tag 16325 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b); 16326 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b); 16327 } 16328 $html = $html_a.$html_b.substr($html, $pos + 11); 16329 $offset = strlen($html_a.$html_b); 16330 } 16331 $html = preg_replace('/([\s]*)<option/si', '<option', $html); 16332 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html); 16333 $offset = 0; 16334 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) { 16335 $html_a = substr($html, 0, $offset); 16336 $html_b = substr($html, $offset, ($pos - $offset + 9)); 16337 while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) { 16338 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b); 16339 $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b); 16340 } 16341 $html = $html_a.$html_b.substr($html, $pos + 9); 16342 $offset = strlen($html_a.$html_b); 16343 } 16344 if (preg_match("'</select'si", $html)) { 16345 $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html); 16346 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html); 16347 } 16348 $html = str_replace("\n", ' ', $html); 16349 // restore textarea newlines 16350 $html = str_replace('<TBR>', "\n", $html); 16351 // remove extra spaces from code 16352 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html); 16353 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html); 16354 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html); 16355 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html); 16356 $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); 16357 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html); 16358 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html); 16359 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html); 16360 $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1> \\2', $html); 16361 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html); 16362 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag 16363 $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html); 16364 $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1> </li>', $html); 16365 $html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1"> </font><img', $html); 16366 $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1> ', $html); // preserve some spaces 16367 $html = preg_replace('/[\s]<\/([^\>]*)>/', ' </\\1>', $html); // preserve some spaces 16368 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment 16369 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment 16370 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space 16371 // trim string 16372 $html = $this->stringTrim($html); 16373 // fix br tag after li 16374 $html = preg_replace('/<li><br([^\>]*)>/', '<li> <br\\1>', $html); 16375 // fix first image tag alignment 16376 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1); 16377 // pattern for generic tag 16378 $tagpattern = '/(<[^>]+>)/'; 16379 // explodes the string 16380 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 16381 // count elements 16382 $maxel = count($a); 16383 $elkey = 0; 16384 $key = 0; 16385 // create an array of elements 16386 $dom = array(); 16387 $dom[$key] = array(); 16388 // set inheritable properties fot the first void element 16389 // 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 16390 $dom[$key]['tag'] = false; 16391 $dom[$key]['block'] = false; 16392 $dom[$key]['value'] = ''; 16393 $dom[$key]['parent'] = 0; 16394 $dom[$key]['hide'] = false; 16395 $dom[$key]['fontname'] = $this->FontFamily; 16396 $dom[$key]['fontstyle'] = $this->FontStyle; 16397 $dom[$key]['fontsize'] = $this->FontSizePt; 16398 $dom[$key]['font-stretch'] = $this->font_stretching; 16399 $dom[$key]['letter-spacing'] = $this->font_spacing; 16400 $dom[$key]['stroke'] = $this->textstrokewidth; 16401 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0); 16402 $dom[$key]['clip'] = ($this->textrendermode > 3); 16403 $dom[$key]['line-height'] = $this->cell_height_ratio; 16404 $dom[$key]['bgcolor'] = false; 16405 $dom[$key]['fgcolor'] = $this->fgcolor; // color 16406 $dom[$key]['strokecolor'] = $this->strokecolor; 16407 $dom[$key]['align'] = ''; 16408 $dom[$key]['listtype'] = ''; 16409 $dom[$key]['text-indent'] = 0; 16410 $dom[$key]['text-transform'] = ''; 16411 $dom[$key]['border'] = array(); 16412 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr'; 16413 $thead = false; // true when we are inside the THEAD tag 16414 ++$key; 16415 $level = array(); 16416 array_push($level, 0); // root 16417 while ($elkey < $maxel) { 16418 $dom[$key] = array(); 16419 $element = $a[$elkey]; 16420 $dom[$key]['elkey'] = $elkey; 16421 if (preg_match($tagpattern, $element)) { 16422 // html tag 16423 $element = substr($element, 1, -1); 16424 // get tag name 16425 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); 16426 $tagname = strtolower($tag[1]); 16427 // check if we are inside a table header 16428 if ($tagname == 'thead') { 16429 if ($element[0] == '/') { 16430 $thead = false; 16431 } else { 16432 $thead = true; 16433 } 16434 ++$elkey; 16435 continue; 16436 } 16437 $dom[$key]['tag'] = true; 16438 $dom[$key]['value'] = $tagname; 16439 if (in_array($dom[$key]['value'], $blocktags)) { 16440 $dom[$key]['block'] = true; 16441 } else { 16442 $dom[$key]['block'] = false; 16443 } 16444 if ($element[0] == '/') { 16445 // *** closing html tag 16446 $dom[$key]['opening'] = false; 16447 $dom[$key]['parent'] = end($level); 16448 array_pop($level); 16449 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide']; 16450 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname']; 16451 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle']; 16452 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize']; 16453 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch']; 16454 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing']; 16455 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke']; 16456 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill']; 16457 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip']; 16458 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height']; 16459 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor']; 16460 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor']; 16461 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor']; 16462 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align']; 16463 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform']; 16464 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir']; 16465 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) { 16466 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype']; 16467 } 16468 // set the number of columns in table tag 16469 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) { 16470 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols']; 16471 } 16472 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 16473 $dom[($dom[$key]['parent'])]['content'] = $csstagarray; 16474 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) { 16475 $dom[($dom[$key]['parent'])]['content'] .= stripslashes($a[$dom[$i]['elkey']]); 16476 } 16477 $key = $i; 16478 // mark nested tables 16479 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']); 16480 // remove thead sections from nested tables 16481 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']); 16482 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']); 16483 } 16484 // store header rows on a new table 16485 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) { 16486 if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) { 16487 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']]; 16488 } 16489 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) { 16490 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']]; 16491 } 16492 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) { 16493 $dom[($dom[$key]['parent'])]['attribute'] = array(); 16494 } 16495 // header elements must be always contained in a single page 16496 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true'; 16497 } 16498 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) { 16499 // remove the nobr attributes from the table header 16500 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']); 16501 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>'; 16502 } 16503 } else { 16504 // *** opening or self-closing html tag 16505 $dom[$key]['opening'] = true; 16506 $dom[$key]['parent'] = end($level); 16507 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) { 16508 // self-closing tag 16509 $dom[$key]['self'] = true; 16510 } else { 16511 // opening tag 16512 array_push($level, $key); 16513 $dom[$key]['self'] = false; 16514 } 16515 // copy some values from parent 16516 $parentkey = 0; 16517 if ($key > 0) { 16518 $parentkey = $dom[$key]['parent']; 16519 $dom[$key]['hide'] = $dom[$parentkey]['hide']; 16520 $dom[$key]['fontname'] = $dom[$parentkey]['fontname']; 16521 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle']; 16522 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize']; 16523 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch']; 16524 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing']; 16525 $dom[$key]['stroke'] = $dom[$parentkey]['stroke']; 16526 $dom[$key]['fill'] = $dom[$parentkey]['fill']; 16527 $dom[$key]['clip'] = $dom[$parentkey]['clip']; 16528 $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; 16529 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor']; 16530 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor']; 16531 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor']; 16532 $dom[$key]['align'] = $dom[$parentkey]['align']; 16533 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 16534 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 16535 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform']; 16536 $dom[$key]['border'] = array(); 16537 $dom[$key]['dir'] = $dom[$parentkey]['dir']; 16538 } 16539 // get attributes 16540 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER); 16541 $dom[$key]['attribute'] = array(); // reset attribute array 16542 while (list($id, $name) = each($attr_array[1])) { 16543 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; 16544 } 16545 if (!empty($css)) { 16546 // merge CSS style to current style 16547 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css); 16548 $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']); 16549 } 16550 // split style attributes 16551 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) { 16552 // get style attributes 16553 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); 16554 $dom[$key]['style'] = array(); // reset style attribute array 16555 while (list($id, $name) = each($style_array[1])) { 16556 // in case of duplicate attribute the last replace the previous 16557 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); 16558 } 16559 // --- get some style attributes --- 16560 // text direction 16561 if (isset($dom[$key]['style']['direction'])) { 16562 $dom[$key]['dir'] = $dom[$key]['style']['direction']; 16563 } 16564 // display 16565 if (isset($dom[$key]['style']['display'])) { 16566 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none'); 16567 } 16568 // font family 16569 if (isset($dom[$key]['style']['font-family'])) { 16570 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']); 16571 } 16572 // list-style-type 16573 if (isset($dom[$key]['style']['list-style-type'])) { 16574 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type'])); 16575 if ($dom[$key]['listtype'] == 'inherit') { 16576 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 16577 } 16578 } 16579 // text-indent 16580 if (isset($dom[$key]['style']['text-indent'])) { 16581 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']); 16582 if ($dom[$key]['text-indent'] == 'inherit') { 16583 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 16584 } 16585 } 16586 // text-transform 16587 if (isset($dom[$key]['style']['text-transform'])) { 16588 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform']; 16589 } 16590 // font size 16591 if (isset($dom[$key]['style']['font-size'])) { 16592 $fsize = trim($dom[$key]['style']['font-size']); 16593 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt'); 16594 } 16595 // font-stretch 16596 if (isset($dom[$key]['style']['font-stretch'])) { 16597 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']); 16598 } 16599 // letter-spacing 16600 if (isset($dom[$key]['style']['letter-spacing'])) { 16601 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']); 16602 } 16603 // line-height (internally is the cell height ratio) 16604 if (isset($dom[$key]['style']['line-height'])) { 16605 $lineheight = trim($dom[$key]['style']['line-height']); 16606 switch ($lineheight) { 16607 // A normal line height. This is default 16608 case 'normal': { 16609 $dom[$key]['line-height'] = $dom[0]['line-height']; 16610 break; 16611 } 16612 case 'inherit': { 16613 $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; 16614 } 16615 default: { 16616 if (is_numeric($lineheight)) { 16617 // convert to percentage of font height 16618 $lineheight = ($lineheight * 100).'%'; 16619 } 16620 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true); 16621 if (substr($lineheight, -1) !== '%') { 16622 if ($dom[$key]['fontsize'] <= 0) { 16623 $dom[$key]['line-height'] = 1; 16624 } else { 16625 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']); 16626 } 16627 } 16628 } 16629 } 16630 } 16631 // font style 16632 if (isset($dom[$key]['style']['font-weight'])) { 16633 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') { 16634 if (strpos($dom[$key]['fontstyle'], 'B') !== false) { 16635 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']); 16636 } 16637 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') { 16638 $dom[$key]['fontstyle'] .= 'B'; 16639 } 16640 } 16641 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) { 16642 $dom[$key]['fontstyle'] .= 'I'; 16643 } 16644 // font color 16645 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) { 16646 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors); 16647 } elseif ($dom[$key]['value'] == 'a') { 16648 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; 16649 } 16650 // background color 16651 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) { 16652 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors); 16653 } 16654 // text-decoration 16655 if (isset($dom[$key]['style']['text-decoration'])) { 16656 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration'])); 16657 foreach ($decors as $dec) { 16658 $dec = trim($dec); 16659 if (!TCPDF_STATIC::empty_string($dec)) { 16660 if ($dec[0] == 'u') { 16661 // underline 16662 $dom[$key]['fontstyle'] .= 'U'; 16663 } elseif ($dec[0] == 'l') { 16664 // line-through 16665 $dom[$key]['fontstyle'] .= 'D'; 16666 } elseif ($dec[0] == 'o') { 16667 // overline 16668 $dom[$key]['fontstyle'] .= 'O'; 16669 } 16670 } 16671 } 16672 } elseif ($dom[$key]['value'] == 'a') { 16673 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; 16674 } 16675 // check for width attribute 16676 if (isset($dom[$key]['style']['width'])) { 16677 $dom[$key]['width'] = $dom[$key]['style']['width']; 16678 } 16679 // check for height attribute 16680 if (isset($dom[$key]['style']['height'])) { 16681 $dom[$key]['height'] = $dom[$key]['style']['height']; 16682 } 16683 // check for text alignment 16684 if (isset($dom[$key]['style']['text-align'])) { 16685 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]); 16686 } 16687 // check for CSS border properties 16688 if (isset($dom[$key]['style']['border'])) { 16689 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']); 16690 if (!empty($borderstyle)) { 16691 $dom[$key]['border']['LTRB'] = $borderstyle; 16692 } 16693 } 16694 if (isset($dom[$key]['style']['border-color'])) { 16695 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color'])); 16696 if (isset($brd_colors[3])) { 16697 $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors); 16698 } 16699 if (isset($brd_colors[1])) { 16700 $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors); 16701 } 16702 if (isset($brd_colors[0])) { 16703 $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors); 16704 } 16705 if (isset($brd_colors[2])) { 16706 $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors); 16707 } 16708 } 16709 if (isset($dom[$key]['style']['border-width'])) { 16710 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width'])); 16711 if (isset($brd_widths[3])) { 16712 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]); 16713 } 16714 if (isset($brd_widths[1])) { 16715 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]); 16716 } 16717 if (isset($brd_widths[0])) { 16718 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]); 16719 } 16720 if (isset($brd_widths[2])) { 16721 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]); 16722 } 16723 } 16724 if (isset($dom[$key]['style']['border-style'])) { 16725 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style'])); 16726 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) { 16727 $dom[$key]['border']['L']['cap'] = 'square'; 16728 $dom[$key]['border']['L']['join'] = 'miter'; 16729 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]); 16730 if ($dom[$key]['border']['L']['dash'] < 0) { 16731 $dom[$key]['border']['L'] = array(); 16732 } 16733 } 16734 if (isset($brd_styles[1])) { 16735 $dom[$key]['border']['R']['cap'] = 'square'; 16736 $dom[$key]['border']['R']['join'] = 'miter'; 16737 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]); 16738 if ($dom[$key]['border']['R']['dash'] < 0) { 16739 $dom[$key]['border']['R'] = array(); 16740 } 16741 } 16742 if (isset($brd_styles[0])) { 16743 $dom[$key]['border']['T']['cap'] = 'square'; 16744 $dom[$key]['border']['T']['join'] = 'miter'; 16745 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]); 16746 if ($dom[$key]['border']['T']['dash'] < 0) { 16747 $dom[$key]['border']['T'] = array(); 16748 } 16749 } 16750 if (isset($brd_styles[2])) { 16751 $dom[$key]['border']['B']['cap'] = 'square'; 16752 $dom[$key]['border']['B']['join'] = 'miter'; 16753 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]); 16754 if ($dom[$key]['border']['B']['dash'] < 0) { 16755 $dom[$key]['border']['B'] = array(); 16756 } 16757 } 16758 } 16759 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom'); 16760 foreach ($cellside as $bsk => $bsv) { 16761 if (isset($dom[$key]['style']['border-'.$bsv])) { 16762 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]); 16763 if (!empty($borderstyle)) { 16764 $dom[$key]['border'][$bsk] = $borderstyle; 16765 } 16766 } 16767 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) { 16768 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors); 16769 } 16770 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) { 16771 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']); 16772 } 16773 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) { 16774 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']); 16775 if ($dom[$key]['border'][$bsk]['dash'] < 0) { 16776 $dom[$key]['border'][$bsk] = array(); 16777 } 16778 } 16779 } 16780 // check for CSS padding properties 16781 if (isset($dom[$key]['style']['padding'])) { 16782 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']); 16783 } else { 16784 $dom[$key]['padding'] = $this->cell_padding; 16785 } 16786 foreach ($cellside as $psk => $psv) { 16787 if (isset($dom[$key]['style']['padding-'.$psv])) { 16788 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false); 16789 } 16790 } 16791 // check for CSS margin properties 16792 if (isset($dom[$key]['style']['margin'])) { 16793 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']); 16794 } else { 16795 $dom[$key]['margin'] = $this->cell_margin; 16796 } 16797 foreach ($cellside as $psk => $psv) { 16798 if (isset($dom[$key]['style']['margin-'.$psv])) { 16799 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false); 16800 } 16801 } 16802 // check for CSS border-spacing properties 16803 if (isset($dom[$key]['style']['border-spacing'])) { 16804 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']); 16805 } 16806 // page-break-inside 16807 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) { 16808 $dom[$key]['attribute']['nobr'] = 'true'; 16809 } 16810 // page-break-before 16811 if (isset($dom[$key]['style']['page-break-before'])) { 16812 if ($dom[$key]['style']['page-break-before'] == 'always') { 16813 $dom[$key]['attribute']['pagebreak'] = 'true'; 16814 } elseif ($dom[$key]['style']['page-break-before'] == 'left') { 16815 $dom[$key]['attribute']['pagebreak'] = 'left'; 16816 } elseif ($dom[$key]['style']['page-break-before'] == 'right') { 16817 $dom[$key]['attribute']['pagebreak'] = 'right'; 16818 } 16819 } 16820 // page-break-after 16821 if (isset($dom[$key]['style']['page-break-after'])) { 16822 if ($dom[$key]['style']['page-break-after'] == 'always') { 16823 $dom[$key]['attribute']['pagebreakafter'] = 'true'; 16824 } elseif ($dom[$key]['style']['page-break-after'] == 'left') { 16825 $dom[$key]['attribute']['pagebreakafter'] = 'left'; 16826 } elseif ($dom[$key]['style']['page-break-after'] == 'right') { 16827 $dom[$key]['attribute']['pagebreakafter'] = 'right'; 16828 } 16829 } 16830 } 16831 if (isset($dom[$key]['attribute']['display'])) { 16832 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none'); 16833 } 16834 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) { 16835 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black'); 16836 if (!empty($borderstyle)) { 16837 $dom[$key]['border']['LTRB'] = $borderstyle; 16838 } 16839 } 16840 // check for font tag 16841 if ($dom[$key]['value'] == 'font') { 16842 // font family 16843 if (isset($dom[$key]['attribute']['face'])) { 16844 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']); 16845 } 16846 // font size 16847 if (isset($dom[$key]['attribute']['size'])) { 16848 if ($key > 0) { 16849 if ($dom[$key]['attribute']['size'][0] == '+') { 16850 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1)); 16851 } elseif ($dom[$key]['attribute']['size'][0] == '-') { 16852 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1)); 16853 } else { 16854 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 16855 } 16856 } else { 16857 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 16858 } 16859 } 16860 } 16861 // force natural alignment for lists 16862 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) 16863 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) { 16864 if ($this->rtl) { 16865 $dom[$key]['align'] = 'R'; 16866 } else { 16867 $dom[$key]['align'] = 'L'; 16868 } 16869 } 16870 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) { 16871 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { 16872 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO; 16873 } 16874 } 16875 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) { 16876 $dom[$key]['fontstyle'] .= 'B'; 16877 } 16878 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) { 16879 $dom[$key]['fontstyle'] .= 'I'; 16880 } 16881 if ($dom[$key]['value'] == 'u') { 16882 $dom[$key]['fontstyle'] .= 'U'; 16883 } 16884 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) { 16885 $dom[$key]['fontstyle'] .= 'D'; 16886 } 16887 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) { 16888 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; 16889 } 16890 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) { 16891 $dom[$key]['fontname'] = $this->default_monospaced_font; 16892 } 16893 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)) { 16894 // headings h1, h2, h3, h4, h5, h6 16895 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { 16896 $headsize = (4 - intval($dom[$key]['value']{1})) * 2; 16897 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize; 16898 } 16899 if (!isset($dom[$key]['style']['font-weight'])) { 16900 $dom[$key]['fontstyle'] .= 'B'; 16901 } 16902 } 16903 if (($dom[$key]['value'] == 'table')) { 16904 $dom[$key]['rows'] = 0; // number of rows 16905 $dom[$key]['trids'] = array(); // IDs of TR elements 16906 $dom[$key]['thead'] = ''; // table header rows 16907 } 16908 if (($dom[$key]['value'] == 'tr')) { 16909 $dom[$key]['cols'] = 0; 16910 if ($thead) { 16911 $dom[$key]['thead'] = true; 16912 // rows on thead block are printed as a separate table 16913 } else { 16914 $dom[$key]['thead'] = false; 16915 // store the number of rows on table element 16916 ++$dom[($dom[$key]['parent'])]['rows']; 16917 // store the TR elements IDs on table element 16918 array_push($dom[($dom[$key]['parent'])]['trids'], $key); 16919 } 16920 } 16921 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) { 16922 if (isset($dom[$key]['attribute']['colspan'])) { 16923 $colspan = intval($dom[$key]['attribute']['colspan']); 16924 } else { 16925 $colspan = 1; 16926 } 16927 $dom[$key]['attribute']['colspan'] = $colspan; 16928 $dom[($dom[$key]['parent'])]['cols'] += $colspan; 16929 } 16930 // text direction 16931 if (isset($dom[$key]['attribute']['dir'])) { 16932 $dom[$key]['dir'] = $dom[$key]['attribute']['dir']; 16933 } 16934 // set foreground color attribute 16935 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) { 16936 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors); 16937 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) { 16938 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; 16939 } 16940 // set background color attribute 16941 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) { 16942 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors); 16943 } 16944 // set stroke color attribute 16945 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) { 16946 $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors); 16947 } 16948 // check for width attribute 16949 if (isset($dom[$key]['attribute']['width'])) { 16950 $dom[$key]['width'] = $dom[$key]['attribute']['width']; 16951 } 16952 // check for height attribute 16953 if (isset($dom[$key]['attribute']['height'])) { 16954 $dom[$key]['height'] = $dom[$key]['attribute']['height']; 16955 } 16956 // check for text alignment 16957 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) { 16958 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]); 16959 } 16960 // check for text rendering mode (the following attributes do not exist in HTML) 16961 if (isset($dom[$key]['attribute']['stroke'])) { 16962 // font stroke width 16963 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true); 16964 } 16965 if (isset($dom[$key]['attribute']['fill'])) { 16966 // font fill 16967 if ($dom[$key]['attribute']['fill'] == 'true') { 16968 $dom[$key]['fill'] = true; 16969 } else { 16970 $dom[$key]['fill'] = false; 16971 } 16972 } 16973 if (isset($dom[$key]['attribute']['clip'])) { 16974 // clipping mode 16975 if ($dom[$key]['attribute']['clip'] == 'true') { 16976 $dom[$key]['clip'] = true; 16977 } else { 16978 $dom[$key]['clip'] = false; 16979 } 16980 } 16981 } // end opening tag 16982 } else { 16983 // text 16984 $dom[$key]['tag'] = false; 16985 $dom[$key]['block'] = false; 16986 $dom[$key]['parent'] = end($level); 16987 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir']; 16988 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) { 16989 // text-transform for unicode requires mb_convert_case (Multibyte String Functions) 16990 if (function_exists('mb_convert_case')) { 16991 $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER); 16992 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) { 16993 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding); 16994 } 16995 } elseif (!$this->isunicode) { 16996 switch ($dom[$dom[$key]['parent']]['text-transform']) { 16997 case 'capitalize': { 16998 $element = ucwords(strtolower($element)); 16999 break; 17000 } 17001 case 'uppercase': { 17002 $element = strtoupper($element); 17003 break; 17004 } 17005 case 'lowercase': { 17006 $element = strtolower($element); 17007 break; 17008 } 17009 } 17010 } 17011 } 17012 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element)); 17013 } 17014 ++$elkey; 17015 ++$key; 17016 } 17017 return $dom; 17018 } 17019 17020 /** 17021 * Returns the string used to find spaces 17022 * @return string 17023 * @protected 17024 * @author Nicola Asuni 17025 * @since 4.8.024 (2010-01-15) 17026 */ 17027 protected function getSpaceString() { 17028 $spacestr = chr(32); 17029 if ($this->isUnicodeFont()) { 17030 $spacestr = chr(0).chr(32); 17031 } 17032 return $spacestr; 17033 } 17034 17035 /** 17036 * Return an hash code used to ensure that the serialized data has been generated by this TCPDF instance. 17037 * @param $data (string) serialized data 17038 * @return string 17039 * @public static 17040 */ 17041 protected function getHashForTCPDFtagParams($data) { 17042 return md5(strlen($data).$this->file_id.$data); 17043 } 17044 17045 /** 17046 * Serialize an array of parameters to be used with TCPDF tag in HTML code. 17047 * @param $data (array) parameters array 17048 * @return string containing serialized data 17049 * @public static 17050 */ 17051 public function serializeTCPDFtagParameters($data) { 17052 $encoded = urlencode(json_encode($data)); 17053 return $this->getHashForTCPDFtagParams($encoded).$encoded; 17054 } 17055 17056 /** 17057 * Unserialize parameters to be used with TCPDF tag in HTML code. 17058 * @param $data (string) serialized data 17059 * @return array containing unserialized data 17060 * @protected static 17061 */ 17062 protected function unserializeTCPDFtagParameters($data) { 17063 $hash = substr($data, 0, 32); 17064 $encoded = substr($data, 32); 17065 if ($hash != $this->getHashForTCPDFtagParams($encoded)) { 17066 $this->Error('Invalid parameters'); 17067 } 17068 return json_decode(urldecode($encoded), true); 17069 } 17070 17071 /** 17072 * Prints a cell (rectangular area) with optional borders, background color and html text string. 17073 * 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 /> 17074 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 17075 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. 17076 * 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 17077 * NOTE: all the HTML attributes must be enclosed in double-quote. 17078 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 17079 * @param $h (float) Cell minimum height. The cell extends automatically if needed. 17080 * @param $x (float) upper-left corner X coordinate 17081 * @param $y (float) upper-left corner Y coordinate 17082 * @param $html (string) html text to print. Default value: empty string. 17083 * @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))) 17084 * @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> 17085 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 17086 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 17087 * @param $reseth (boolean) if true reset the last cell height (default true). 17088 * @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> 17089 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width. 17090 * @see Multicell(), writeHTML() 17091 * @public 17092 */ 17093 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) { 17094 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false); 17095 } 17096 17097 /** 17098 * Allows to preserve some HTML formatting (limited support).<br /> 17099 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. 17100 * 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 17101 * NOTE: all the HTML attributes must be enclosed in double-quote. 17102 * @param $html (string) text to display 17103 * @param $ln (boolean) if true add a new line after text (default = true) 17104 * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false). 17105 * @param $reseth (boolean) if true reset the last cell height (default false). 17106 * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false). 17107 * @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> 17108 * @public 17109 */ 17110 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') { 17111 $gvars = $this->getGraphicVars(); 17112 // store current values 17113 $prev_cell_margin = $this->cell_margin; 17114 $prev_cell_padding = $this->cell_padding; 17115 $prevPage = $this->page; 17116 $prevlMargin = $this->lMargin; 17117 $prevrMargin = $this->rMargin; 17118 $curfontname = $this->FontFamily; 17119 $curfontstyle = $this->FontStyle; 17120 $curfontsize = $this->FontSizePt; 17121 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize); 17122 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize); 17123 $curfontstretcing = $this->font_stretching; 17124 $curfonttracking = $this->font_spacing; 17125 $this->newline = true; 17126 $newline = true; 17127 $startlinepage = $this->page; 17128 $minstartliney = $this->y; 17129 $maxbottomliney = 0; 17130 $startlinex = $this->x; 17131 $startliney = $this->y; 17132 $yshift = 0; 17133 $loop = 0; 17134 $curpos = 0; 17135 $this_method_vars = array(); 17136 $undo = false; 17137 $fontaligned = false; 17138 $reverse_dir = false; // true when the text direction is reversed 17139 $this->premode = false; 17140 if ($this->inxobj) { 17141 // we are inside an XObject template 17142 $pask = count($this->xobjects[$this->xobjid]['annotations']); 17143 } elseif (isset($this->PageAnnots[$this->page])) { 17144 $pask = count($this->PageAnnots[$this->page]); 17145 } else { 17146 $pask = 0; 17147 } 17148 if ($this->inxobj) { 17149 // we are inside an XObject template 17150 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); 17151 } elseif (!$this->InFooter) { 17152 if (isset($this->footerlen[$this->page])) { 17153 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 17154 } else { 17155 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 17156 } 17157 $startlinepos = $this->footerpos[$this->page]; 17158 } else { 17159 // we are inside the footer 17160 $startlinepos = $this->pagelen[$this->page]; 17161 } 17162 $lalign = $align; 17163 $plalign = $align; 17164 if ($this->rtl) { 17165 $w = $this->x - $this->lMargin; 17166 } else { 17167 $w = $this->w - $this->rMargin - $this->x; 17168 } 17169 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']); 17170 if ($cell) { 17171 if ($this->rtl) { 17172 $this->x -= $this->cell_padding['R']; 17173 $this->lMargin += $this->cell_padding['R']; 17174 } else { 17175 $this->x += $this->cell_padding['L']; 17176 $this->rMargin += $this->cell_padding['L']; 17177 } 17178 } 17179 if ($this->customlistindent >= 0) { 17180 $this->listindent = $this->customlistindent; 17181 } else { 17182 $this->listindent = $this->GetStringWidth('000000'); 17183 } 17184 $this->listindentlevel = 0; 17185 // save previous states 17186 $prev_cell_height_ratio = $this->cell_height_ratio; 17187 $prev_listnum = $this->listnum; 17188 $prev_listordered = $this->listordered; 17189 $prev_listcount = $this->listcount; 17190 $prev_lispacer = $this->lispacer; 17191 $this->listnum = 0; 17192 $this->listordered = array(); 17193 $this->listcount = array(); 17194 $this->lispacer = ''; 17195 if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) { 17196 // reset row height 17197 $this->resetLastH(); 17198 } 17199 $dom = $this->getHtmlDomArray($html); 17200 $maxel = count($dom); 17201 $key = 0; 17202 while ($key < $maxel) { 17203 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) { 17204 // store the node key 17205 $hidden_node_key = $key; 17206 if ($dom[$key]['self']) { 17207 // skip just this self-closing tag 17208 ++$key; 17209 } else { 17210 // skip this and all children tags 17211 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) { 17212 // skip hidden objects 17213 ++$key; 17214 } 17215 ++$key; 17216 } 17217 } 17218 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) { 17219 // check for pagebreak 17220 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) { 17221 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17222 $this->checkPageBreak($this->PageBreakTrigger + 1); 17223 $this->htmlvspace = ($this->PageBreakTrigger + 1); 17224 } 17225 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 17226 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 17227 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17228 $this->checkPageBreak($this->PageBreakTrigger + 1); 17229 $this->htmlvspace = ($this->PageBreakTrigger + 1); 17230 } 17231 } 17232 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) { 17233 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { 17234 $dom[$key]['attribute']['nobr'] = false; 17235 } else { 17236 // store current object 17237 $this->startTransaction(); 17238 // save this method vars 17239 $this_method_vars['html'] = $html; 17240 $this_method_vars['ln'] = $ln; 17241 $this_method_vars['fill'] = $fill; 17242 $this_method_vars['reseth'] = $reseth; 17243 $this_method_vars['cell'] = $cell; 17244 $this_method_vars['align'] = $align; 17245 $this_method_vars['gvars'] = $gvars; 17246 $this_method_vars['prevPage'] = $prevPage; 17247 $this_method_vars['prev_cell_margin'] = $prev_cell_margin; 17248 $this_method_vars['prev_cell_padding'] = $prev_cell_padding; 17249 $this_method_vars['prevlMargin'] = $prevlMargin; 17250 $this_method_vars['prevrMargin'] = $prevrMargin; 17251 $this_method_vars['curfontname'] = $curfontname; 17252 $this_method_vars['curfontstyle'] = $curfontstyle; 17253 $this_method_vars['curfontsize'] = $curfontsize; 17254 $this_method_vars['curfontascent'] = $curfontascent; 17255 $this_method_vars['curfontdescent'] = $curfontdescent; 17256 $this_method_vars['curfontstretcing'] = $curfontstretcing; 17257 $this_method_vars['curfonttracking'] = $curfonttracking; 17258 $this_method_vars['minstartliney'] = $minstartliney; 17259 $this_method_vars['maxbottomliney'] = $maxbottomliney; 17260 $this_method_vars['yshift'] = $yshift; 17261 $this_method_vars['startlinepage'] = $startlinepage; 17262 $this_method_vars['startlinepos'] = $startlinepos; 17263 $this_method_vars['startlinex'] = $startlinex; 17264 $this_method_vars['startliney'] = $startliney; 17265 $this_method_vars['newline'] = $newline; 17266 $this_method_vars['loop'] = $loop; 17267 $this_method_vars['curpos'] = $curpos; 17268 $this_method_vars['pask'] = $pask; 17269 $this_method_vars['lalign'] = $lalign; 17270 $this_method_vars['plalign'] = $plalign; 17271 $this_method_vars['w'] = $w; 17272 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio; 17273 $this_method_vars['prev_listnum'] = $prev_listnum; 17274 $this_method_vars['prev_listordered'] = $prev_listordered; 17275 $this_method_vars['prev_listcount'] = $prev_listcount; 17276 $this_method_vars['prev_lispacer'] = $prev_lispacer; 17277 $this_method_vars['fontaligned'] = $fontaligned; 17278 $this_method_vars['key'] = $key; 17279 $this_method_vars['dom'] = $dom; 17280 } 17281 } 17282 // print THEAD block 17283 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) { 17284 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) { 17285 $this->inthead = true; 17286 // print table header (thead) 17287 $this->writeHTML($this->thead, false, false, false, false, ''); 17288 // check if we are on a new page or on a new column 17289 if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) { 17290 // we are on a new page or on a new column and the total object height is less than the available vertical space. 17291 // restore previous object 17292 $this->rollbackTransaction(true); 17293 // restore previous values 17294 foreach ($this_method_vars as $vkey => $vval) { 17295 $$vkey = $vval; 17296 } 17297 // disable table header 17298 $tmp_thead = $this->thead; 17299 $this->thead = ''; 17300 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17301 $pre_y = $this->y; 17302 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 17303 // fix for multicolumn mode 17304 $startliney = $this->y; 17305 } 17306 $this->start_transaction_page = $this->page; 17307 $this->start_transaction_y = $this->y; 17308 // restore table header 17309 $this->thead = $tmp_thead; 17310 // fix table border properties 17311 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) { 17312 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px'); 17313 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) { 17314 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V']; 17315 } else { 17316 $tmp_cellspacing = 0; 17317 } 17318 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page; 17319 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column; 17320 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing; 17321 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']); 17322 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset; 17323 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset; 17324 // print table header (thead) 17325 $this->writeHTML($this->thead, false, false, false, false, ''); 17326 } 17327 } 17328 // move $key index forward to skip THEAD block 17329 while ( ($key < $maxel) AND (!( 17330 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead'])) 17331 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) { 17332 ++$key; 17333 } 17334 } 17335 if ($dom[$key]['tag'] OR ($key == 0)) { 17336 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) { 17337 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L'; 17338 } 17339 // vertically align image in line 17340 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) { 17341 // get image height 17342 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px'); 17343 $autolinebreak = false; 17344 if (!empty($dom[$key]['width'])) { 17345 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false); 17346 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R'])) 17347 AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L']))) 17348 OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) { 17349 // add automatic line break 17350 $autolinebreak = true; 17351 $this->Ln('', $cell); 17352 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) { 17353 // go back to evaluate this line break 17354 --$key; 17355 } 17356 } 17357 } 17358 if (!$autolinebreak) { 17359 if ($this->inPageBody()) { 17360 $pre_y = $this->y; 17361 // check for page break 17362 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) { 17363 // fix for multicolumn mode 17364 $startliney = $this->y; 17365 } 17366 } 17367 if ($this->page > $startlinepage) { 17368 // fix line splitted over two pages 17369 if (isset($this->footerlen[$startlinepage])) { 17370 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17371 } 17372 // line to be moved one page forward 17373 $pagebuff = $this->getPageBuffer($startlinepage); 17374 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 17375 $tstart = substr($pagebuff, 0, $startlinepos); 17376 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 17377 // remove line from previous page 17378 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 17379 $pagebuff = $this->getPageBuffer($this->page); 17380 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 17381 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 17382 // add line start to current page 17383 $yshift = ($minstartliney - $this->y); 17384 if ($fontaligned) { 17385 $yshift += ($curfontsize / $this->k); 17386 } 17387 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k)); 17388 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 17389 // shift the annotations and links 17390 if (isset($this->PageAnnots[$this->page])) { 17391 $next_pask = count($this->PageAnnots[$this->page]); 17392 } else { 17393 $next_pask = 0; 17394 } 17395 if (isset($this->PageAnnots[$startlinepage])) { 17396 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 17397 if ($pak >= $pask) { 17398 $this->PageAnnots[$this->page][] = $pac; 17399 unset($this->PageAnnots[$startlinepage][$pak]); 17400 $npak = count($this->PageAnnots[$this->page]) - 1; 17401 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 17402 } 17403 } 17404 } 17405 $pask = $next_pask; 17406 $startlinepos = $this->cntmrk[$this->page]; 17407 $startlinepage = $this->page; 17408 $startliney = $this->y; 17409 $this->newline = false; 17410 } 17411 $this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh); 17412 $minstartliney = min($this->y, $minstartliney); 17413 $maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k)); 17414 } 17415 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) { 17416 // account for different font size 17417 $pfontname = $curfontname; 17418 $pfontstyle = $curfontstyle; 17419 $pfontsize = $curfontsize; 17420 $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname); 17421 $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle); 17422 $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize); 17423 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize); 17424 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize); 17425 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize) 17426 OR ($this->cell_height_ratio != $dom[$key]['line-height']) 17427 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) { 17428 if (($key < ($maxel - 1)) AND ( 17429 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) 17430 OR ($this->cell_height_ratio != $dom[$key]['line-height']) 17431 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) 17432 AND ($fontsize >= 0) AND ($curfontsize >= 0) 17433 AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname))) 17434 )) { 17435 if ($this->page > $startlinepage) { 17436 // fix lines splitted over two pages 17437 if (isset($this->footerlen[$startlinepage])) { 17438 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17439 } 17440 // line to be moved one page forward 17441 $pagebuff = $this->getPageBuffer($startlinepage); 17442 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 17443 $tstart = substr($pagebuff, 0, $startlinepos); 17444 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 17445 // remove line start from previous page 17446 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 17447 $pagebuff = $this->getPageBuffer($this->page); 17448 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 17449 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 17450 // add line start to current page 17451 $yshift = ($minstartliney - $this->y); 17452 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k)); 17453 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 17454 // shift the annotations and links 17455 if (isset($this->PageAnnots[$this->page])) { 17456 $next_pask = count($this->PageAnnots[$this->page]); 17457 } else { 17458 $next_pask = 0; 17459 } 17460 if (isset($this->PageAnnots[$startlinepage])) { 17461 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 17462 if ($pak >= $pask) { 17463 $this->PageAnnots[$this->page][] = $pac; 17464 unset($this->PageAnnots[$startlinepage][$pak]); 17465 $npak = count($this->PageAnnots[$this->page]) - 1; 17466 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 17467 } 17468 } 17469 } 17470 $pask = $next_pask; 17471 $startlinepos = $this->cntmrk[$this->page]; 17472 $startlinepage = $this->page; 17473 $startliney = $this->y; 17474 } 17475 if (!isset($dom[$key]['line-height'])) { 17476 $dom[$key]['line-height'] = $this->cell_height_ratio; 17477 } 17478 if (!$dom[$key]['block']) { 17479 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']))) { 17480 $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2; 17481 } 17482 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) { 17483 $current_line_align_data = array($key, $minstartliney, $maxbottomliney); 17484 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)))) { 17485 $minstartliney = min($this->y, $line_align_data[1]); 17486 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]); 17487 } else { 17488 $minstartliney = min($this->y, $minstartliney); 17489 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney); 17490 } 17491 $line_align_data = $current_line_align_data; 17492 } 17493 } 17494 $this->cell_height_ratio = $dom[$key]['line-height']; 17495 $fontaligned = true; 17496 } 17497 $this->SetFont($fontname, $fontstyle, $fontsize); 17498 // reset row height 17499 $this->resetLastH(); 17500 $curfontname = $fontname; 17501 $curfontstyle = $fontstyle; 17502 $curfontsize = $fontsize; 17503 $curfontascent = $fontascent; 17504 $curfontdescent = $fontdescent; 17505 } 17506 } 17507 // set text rendering mode 17508 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth; 17509 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0); 17510 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3); 17511 $this->setTextRenderingMode($textstroke, $textfill, $textclip); 17512 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) { 17513 $this->setFontStretching($dom[$key]['font-stretch']); 17514 } 17515 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) { 17516 $this->setFontSpacing($dom[$key]['letter-spacing']); 17517 } 17518 if (($plalign == 'J') AND $dom[$key]['block']) { 17519 $plalign = ''; 17520 } 17521 // get current position on page buffer 17522 $curpos = $this->pagelen[$startlinepage]; 17523 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) { 17524 $this->SetFillColorArray($dom[$key]['bgcolor']); 17525 $wfill = true; 17526 } else { 17527 $wfill = $fill | false; 17528 } 17529 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) { 17530 $this->SetTextColorArray($dom[$key]['fgcolor']); 17531 } 17532 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) { 17533 $this->SetDrawColorArray($dom[$key]['strokecolor']); 17534 } 17535 if (isset($dom[$key]['align'])) { 17536 $lalign = $dom[$key]['align']; 17537 } 17538 if (TCPDF_STATIC::empty_string($lalign)) { 17539 $lalign = $align; 17540 } 17541 } 17542 // align lines 17543 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) { 17544 $newline = true; 17545 $fontaligned = false; 17546 // we are at the beginning of a new line 17547 if (isset($startlinex)) { 17548 $yshift = ($minstartliney - $startliney); 17549 if (($yshift > 0) OR ($this->page > $startlinepage)) { 17550 $yshift = 0; 17551 } 17552 $t_x = 0; 17553 // the last line must be shifted to be aligned as requested 17554 $linew = abs($this->endlinex - $startlinex); 17555 if ($this->inxobj) { 17556 // we are inside an XObject template 17557 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); 17558 if (isset($opentagpos)) { 17559 $midpos = $opentagpos; 17560 } else { 17561 $midpos = 0; 17562 } 17563 if ($midpos > 0) { 17564 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); 17565 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); 17566 } else { 17567 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); 17568 $pend = ''; 17569 } 17570 } else { 17571 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 17572 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 17573 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17574 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 17575 } elseif (isset($opentagpos)) { 17576 $midpos = $opentagpos; 17577 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 17578 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17579 $midpos = $this->footerpos[$startlinepage]; 17580 } else { 17581 $midpos = 0; 17582 } 17583 if ($midpos > 0) { 17584 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 17585 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 17586 } else { 17587 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 17588 $pend = ''; 17589 } 17590 } 17591 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { 17592 // calculate shifting amount 17593 $tw = $w; 17594 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) { 17595 $tw += $this->cell_padding['R']; 17596 } 17597 if ($this->lMargin != $prevlMargin) { 17598 $tw += ($prevlMargin - $this->lMargin); 17599 } 17600 if ($this->rMargin != $prevrMargin) { 17601 $tw += ($prevrMargin - $this->rMargin); 17602 } 17603 $one_space_width = $this->GetStringWidth(chr(32)); 17604 $no = 0; // number of spaces on a line contained on a single block 17605 if ($this->isRTLTextDir()) { // RTL 17606 // remove left space if exist 17607 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[('); 17608 if ($pos1 > 0) { 17609 $pos1 = intval($pos1); 17610 if ($this->isUnicodeFont()) { 17611 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32))); 17612 $spacelen = 2; 17613 } else { 17614 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32))); 17615 $spacelen = 1; 17616 } 17617 if ($pos1 == $pos2) { 17618 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); 17619 if (substr($pmid, $pos1, 4) == '[()]') { 17620 $linew -= $one_space_width; 17621 } elseif ($pos1 == strpos($pmid, '[(')) { 17622 $no = 1; 17623 } 17624 } 17625 } 17626 } else { // LTR 17627 // remove right space if exist 17628 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]'); 17629 if ($pos1 > 0) { 17630 $pos1 = intval($pos1); 17631 if ($this->isUnicodeFont()) { 17632 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2; 17633 $spacelen = 2; 17634 } else { 17635 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1; 17636 $spacelen = 1; 17637 } 17638 if ($pos1 == $pos2) { 17639 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); 17640 $linew -= $one_space_width; 17641 } 17642 } 17643 } 17644 $mdiff = ($tw - $linew); 17645 if ($plalign == 'C') { 17646 if ($this->rtl) { 17647 $t_x = -($mdiff / 2); 17648 } else { 17649 $t_x = ($mdiff / 2); 17650 } 17651 } elseif ($plalign == 'R') { 17652 // right alignment on LTR document 17653 $t_x = $mdiff; 17654 } elseif ($plalign == 'L') { 17655 // left alignment on RTL document 17656 $t_x = -$mdiff; 17657 } elseif (($plalign == 'J') AND ($plalign == $lalign)) { 17658 // Justification 17659 if ($this->isRTLTextDir()) { 17660 // align text on the left 17661 $t_x = -$mdiff; 17662 } 17663 $ns = 0; // number of spaces 17664 $pmidtemp = $pmid; 17665 // escape special characters 17666 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 17667 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 17668 // search spaces 17669 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) { 17670 $spacestr = $this->getSpaceString(); 17671 $maxkk = count($lnstring[1]) - 1; 17672 for ($kk=0; $kk <= $maxkk; ++$kk) { 17673 // restore special characters 17674 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]); 17675 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]); 17676 // store number of spaces on the strings 17677 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr); 17678 // count total spaces on line 17679 $ns += $lnstring[2][$kk]; 17680 $lnstring[3][$kk] = $ns; 17681 } 17682 if ($ns == 0) { 17683 $ns = 1; 17684 } 17685 // calculate additional space to add to each existing space 17686 $spacewidth = ($mdiff / ($ns - $no)) * $this->k; 17687 if ($this->FontSize <= 0) { 17688 $this->FontSize = 1; 17689 } 17690 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize; 17691 if ($this->font_spacing != 0) { 17692 // fixed spacing mode 17693 $osw = -1000 * $this->font_spacing / $this->FontSize; 17694 $spacewidthu += $osw; 17695 } 17696 $nsmax = $ns; 17697 $ns = 0; 17698 reset($lnstring); 17699 $offset = 0; 17700 $strcount = 0; 17701 $prev_epsposbeg = 0; 17702 $textpos = 0; 17703 if ($this->isRTLTextDir()) { 17704 $textpos = $this->wPt; 17705 } 17706 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) { 17707 // check if we are inside a string section '[( ... )]' 17708 $stroffset = strpos($pmid, '[(', $offset); 17709 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) { 17710 // set offset to the end of string section 17711 $offset = strpos($pmid, ')]', $stroffset); 17712 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) { 17713 $offset = strpos($pmid, ')]', ($offset + 1)); 17714 } 17715 if ($offset === false) { 17716 $this->Error('HTML Justification: malformed PDF code.'); 17717 } 17718 continue; 17719 } 17720 if ($this->isRTLTextDir()) { 17721 $spacew = ($spacewidth * ($nsmax - $ns)); 17722 } else { 17723 $spacew = ($spacewidth * $ns); 17724 } 17725 $offset = $strpiece[2][1] + strlen($strpiece[2][0]); 17726 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset); 17727 if ($epsposend !== null) { 17728 $epsposend += strlen($this->epsmarker.'Q'); 17729 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); 17730 if ($epsposbeg === null) { 17731 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6)); 17732 $prev_epsposbeg = $epsposbeg; 17733 } 17734 if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) { 17735 // shift EPS images 17736 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew); 17737 $pmid_b = substr($pmid, 0, $epsposbeg); 17738 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg)); 17739 $pmid_e = substr($pmid, $epsposend); 17740 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e; 17741 $offset = $epsposend; 17742 continue; 17743 } 17744 } 17745 $currentxpos = 0; 17746 // shift blocks of code 17747 switch ($strpiece[2][0]) { 17748 case 'Td': 17749 case 'cm': 17750 case 'm': 17751 case 'l': { 17752 // get current X position 17753 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); 17754 if (!isset($xmatches[1])) { 17755 break; 17756 } 17757 $currentxpos = $xmatches[1]; 17758 $textpos = $currentxpos; 17759 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) { 17760 $ns = $lnstring[3][$strcount]; 17761 if ($this->isRTLTextDir()) { 17762 $spacew = ($spacewidth * ($nsmax - $ns)); 17763 } 17764 ++$strcount; 17765 } 17766 // justify block 17767 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) { 17768 $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4]; 17769 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17770 unset($pmatch, $newpmid); 17771 } 17772 break; 17773 } 17774 case 're': { 17775 // justify block 17776 if (!TCPDF_STATIC::empty_string($this->lispacer)) { 17777 $this->lispacer = ''; 17778 continue; 17779 } 17780 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches); 17781 if (!isset($xmatches[1])) { 17782 break; 17783 } 17784 $currentxpos = $xmatches[1]; 17785 $x_diff = 0; 17786 $w_diff = 0; 17787 if ($this->isRTLTextDir()) { // RTL 17788 if ($currentxpos < $textpos) { 17789 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount])); 17790 $w_diff = ($spacewidth * $lnstring[2][$strcount]); 17791 } else { 17792 if ($strcount > 0) { 17793 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)])); 17794 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); 17795 } 17796 } 17797 } else { // LTR 17798 if ($currentxpos > $textpos) { 17799 if ($strcount > 0) { 17800 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]); 17801 } 17802 $w_diff = ($spacewidth * $lnstring[2][$strcount]); 17803 } else { 17804 if ($strcount > 1) { 17805 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]); 17806 } 17807 if ($strcount > 0) { 17808 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); 17809 } 17810 } 17811 } 17812 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) { 17813 $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff)); 17814 $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff)); 17815 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6]; 17816 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17817 unset($pmatch, $newpmid, $newx, $neww); 17818 } 17819 break; 17820 } 17821 case 'c': { 17822 // get current X position 17823 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); 17824 if (!isset($xmatches[1])) { 17825 break; 17826 } 17827 $currentxpos = $xmatches[1]; 17828 // justify block 17829 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) { 17830 $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew)); 17831 $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew)); 17832 $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew)); 17833 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8]; 17834 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17835 unset($pmatch, $newpmid, $newx1, $newx2, $newx3); 17836 } 17837 break; 17838 } 17839 } 17840 // shift the annotations and links 17841 $cxpos = ($currentxpos / $this->k); 17842 $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps); 17843 if ($this->inxobj) { 17844 // we are inside an XObject template 17845 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 17846 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 17847 if ($cxpos > $lmpos) { 17848 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k); 17849 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17850 } else { 17851 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17852 } 17853 break; 17854 } 17855 } 17856 } elseif (isset($this->PageAnnots[$this->page])) { 17857 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 17858 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 17859 if ($cxpos > $lmpos) { 17860 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k); 17861 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17862 } else { 17863 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17864 } 17865 break; 17866 } 17867 } 17868 } 17869 } // end of while 17870 // remove markers 17871 $pmid = str_replace('x*#!#*x', '', $pmid); 17872 if ($this->isUnicodeFont()) { 17873 // multibyte characters 17874 $spacew = $spacewidthu; 17875 if ($this->font_stretching != 100) { 17876 // word spacing is affected by stretching 17877 $spacew /= ($this->font_stretching / 100); 17878 } 17879 // escape special characters 17880 $pos = 0; 17881 $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid); 17882 $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid); 17883 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) { 17884 foreach($pamatch[0] as $pk => $pmatch) { 17885 $replace = $pamatch[1][$pk]; 17886 $replace = str_replace('#!#OP#!#', '(', $replace); 17887 $replace = str_replace('#!#CP#!#', ')', $replace); 17888 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]'; 17889 $pos = strpos($pmid, $pmatch, $pos); 17890 if ($pos !== FALSE) { 17891 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch)); 17892 } 17893 ++$pos; 17894 } 17895 unset($pamatch); 17896 } 17897 if ($this->inxobj) { 17898 // we are inside an XObject template 17899 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend; 17900 } else { 17901 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend); 17902 } 17903 $endlinepos = strlen($pstart."\n".$pmid."\n"); 17904 } else { 17905 // non-unicode (single-byte characters) 17906 if ($this->font_stretching != 100) { 17907 // word spacing (Tw) is affected by stretching 17908 $spacewidth /= ($this->font_stretching / 100); 17909 } 17910 $rs = sprintf('%F Tw', $spacewidth); 17911 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid); 17912 if ($this->inxobj) { 17913 // we are inside an XObject template 17914 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend; 17915 } else { 17916 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend); 17917 } 17918 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n"); 17919 } 17920 } 17921 } // end of J 17922 } // end if $startlinex 17923 if (($t_x != 0) OR ($yshift < 0)) { 17924 // shift the line 17925 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k)); 17926 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; 17927 $endlinepos = strlen($pstart); 17928 if ($this->inxobj) { 17929 // we are inside an XObject template 17930 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; 17931 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 17932 if ($pak >= $pask) { 17933 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; 17934 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; 17935 } 17936 } 17937 } else { 17938 $this->setPageBuffer($startlinepage, $pstart.$pend); 17939 // shift the annotations and links 17940 if (isset($this->PageAnnots[$this->page])) { 17941 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 17942 if ($pak >= $pask) { 17943 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 17944 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 17945 } 17946 } 17947 } 17948 } 17949 $this->y -= $yshift; 17950 } 17951 } 17952 $pbrk = $this->checkPageBreak($this->lasth); 17953 $this->newline = false; 17954 $startlinex = $this->x; 17955 $startliney = $this->y; 17956 if ($dom[$dom[$key]['parent']]['value'] == 'sup') { 17957 $startliney -= ((0.3 * $this->FontSizePt) / $this->k); 17958 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') { 17959 $startliney -= (($this->FontSizePt / 0.7) / $this->k); 17960 } else { 17961 $minstartliney = $startliney; 17962 $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k)); 17963 } 17964 $startlinepage = $this->page; 17965 if (isset($endlinepos) AND (!$pbrk)) { 17966 $startlinepos = $endlinepos; 17967 } else { 17968 if ($this->inxobj) { 17969 // we are inside an XObject template 17970 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); 17971 } elseif (!$this->InFooter) { 17972 if (isset($this->footerlen[$this->page])) { 17973 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 17974 } else { 17975 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 17976 } 17977 $startlinepos = $this->footerpos[$this->page]; 17978 } else { 17979 $startlinepos = $this->pagelen[$this->page]; 17980 } 17981 } 17982 unset($endlinepos); 17983 $plalign = $lalign; 17984 if (isset($this->PageAnnots[$this->page])) { 17985 $pask = count($this->PageAnnots[$this->page]); 17986 } else { 17987 $pask = 0; 17988 } 17989 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table') 17990 AND (isset($this->emptypagemrk[$this->page])) 17991 AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) { 17992 $this->SetFont($fontname, $fontstyle, $fontsize); 17993 if ($wfill) { 17994 $this->SetFillColorArray($this->bgcolor); 17995 } 17996 } 17997 } // end newline 17998 if (isset($opentagpos)) { 17999 unset($opentagpos); 18000 } 18001 if ($dom[$key]['tag']) { 18002 if ($dom[$key]['opening']) { 18003 // get text indentation (if any) 18004 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) { 18005 $this->textindent = $dom[$key]['text-indent']; 18006 $this->newline = true; 18007 } 18008 // table 18009 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) { 18010 // available page width 18011 if ($this->rtl) { 18012 $wtmp = $this->x - $this->lMargin; 18013 } else { 18014 $wtmp = $this->w - $this->rMargin - $this->x; 18015 } 18016 // get cell spacing 18017 if (isset($dom[$key]['attribute']['cellspacing'])) { 18018 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px'); 18019 $cellspacing = array('H' => $clsp, 'V' => $clsp); 18020 } elseif (isset($dom[$key]['border-spacing'])) { 18021 $cellspacing = $dom[$key]['border-spacing']; 18022 } else { 18023 $cellspacing = array('H' => 0, 'V' => 0); 18024 } 18025 // table width 18026 if (isset($dom[$key]['width'])) { 18027 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); 18028 } else { 18029 $table_width = $wtmp; 18030 } 18031 $table_width -= (2 * $cellspacing['H']); 18032 if (!$this->inthead) { 18033 $this->y += $cellspacing['V']; 18034 } 18035 if ($this->rtl) { 18036 $cellspacingx = -$cellspacing['H']; 18037 } else { 18038 $cellspacingx = $cellspacing['H']; 18039 } 18040 // total table width without cellspaces 18041 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1))); 18042 // minimum column width 18043 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']); 18044 // array of custom column widths 18045 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width); 18046 } 18047 // table row 18048 if ($dom[$key]['value'] == 'tr') { 18049 // reset column counter 18050 $colid = 0; 18051 } 18052 // table cell 18053 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 18054 $trid = $dom[$key]['parent']; 18055 $table_el = $dom[$trid]['parent']; 18056 if (!isset($dom[$table_el]['cols'])) { 18057 $dom[$table_el]['cols'] = $dom[$trid]['cols']; 18058 } 18059 // store border info 18060 $tdborder = 0; 18061 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) { 18062 $tdborder = $dom[$key]['border']; 18063 } 18064 $colspan = intval($dom[$key]['attribute']['colspan']); 18065 if ($colspan <= 0) { 18066 $colspan = 1; 18067 } 18068 $old_cell_padding = $this->cell_padding; 18069 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { 18070 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px'); 18071 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd); 18072 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) { 18073 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding']; 18074 } else { 18075 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0); 18076 } 18077 $this->cell_padding = $current_cell_padding; 18078 if (isset($dom[$key]['height'])) { 18079 // minimum cell height 18080 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px'); 18081 } else { 18082 $cellh = 0; 18083 } 18084 if (isset($dom[$key]['content'])) { 18085 $cell_content = $dom[$key]['content']; 18086 } else { 18087 $cell_content = ' '; 18088 } 18089 $tagtype = $dom[$key]['value']; 18090 $parentid = $key; 18091 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) { 18092 // move $key index forward 18093 ++$key; 18094 } 18095 if (!isset($dom[$trid]['startpage'])) { 18096 $dom[$trid]['startpage'] = $this->page; 18097 } else { 18098 $this->setPage($dom[$trid]['startpage']); 18099 } 18100 if (!isset($dom[$trid]['startcolumn'])) { 18101 $dom[$trid]['startcolumn'] = $this->current_column; 18102 } elseif ($this->current_column != $dom[$trid]['startcolumn']) { 18103 $tmpx = $this->x; 18104 $this->selectColumn($dom[$trid]['startcolumn']); 18105 $this->x = $tmpx; 18106 } 18107 if (!isset($dom[$trid]['starty'])) { 18108 $dom[$trid]['starty'] = $this->y; 18109 } else { 18110 $this->y = $dom[$trid]['starty']; 18111 } 18112 if (!isset($dom[$trid]['startx'])) { 18113 $dom[$trid]['startx'] = $this->x; 18114 $this->x += $cellspacingx; 18115 } else { 18116 $this->x += ($cellspacingx / 2); 18117 } 18118 if (isset($dom[$parentid]['attribute']['rowspan'])) { 18119 $rowspan = intval($dom[$parentid]['attribute']['rowspan']); 18120 } else { 18121 $rowspan = 1; 18122 } 18123 // skip row-spanned cells started on the previous rows 18124 if (isset($dom[$table_el]['rowspans'])) { 18125 $rsk = 0; 18126 $rskmax = count($dom[$table_el]['rowspans']); 18127 while ($rsk < $rskmax) { 18128 $trwsp = $dom[$table_el]['rowspans'][$rsk]; 18129 $rsstartx = $trwsp['startx']; 18130 $rsendx = $trwsp['endx']; 18131 // account for margin changes 18132 if ($trwsp['startpage'] < $this->page) { 18133 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) { 18134 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']); 18135 $rsstartx -= $dl; 18136 $rsendx -= $dl; 18137 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) { 18138 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']); 18139 $rsstartx += $dl; 18140 $rsendx += $dl; 18141 } 18142 } 18143 if (($trwsp['rowspan'] > 0) 18144 AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps)) 18145 AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps)) 18146 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) { 18147 // set the starting X position of the current cell 18148 $this->x = $rsendx + $cellspacingx; 18149 // increment column indicator 18150 $colid += $trwsp['colspan']; 18151 if (($trwsp['rowspan'] == 1) 18152 AND (isset($dom[$trid]['endy'])) 18153 AND (isset($dom[$trid]['endpage'])) 18154 AND (isset($dom[$trid]['endcolumn'])) 18155 AND ($trwsp['endpage'] == $dom[$trid]['endpage']) 18156 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { 18157 // set ending Y position for row 18158 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 18159 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy']; 18160 } 18161 $rsk = 0; 18162 } else { 18163 ++$rsk; 18164 } 18165 } 18166 } 18167 if (isset($dom[$parentid]['width'])) { 18168 // user specified width 18169 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px'); 18170 $tmpcw = ($cellw / $colspan); 18171 for ($i = 0; $i < $colspan; ++$i) { 18172 $table_colwidths[($colid + $i)] = $tmpcw; 18173 } 18174 } else { 18175 // inherit column width 18176 $cellw = 0; 18177 for ($i = 0; $i < $colspan; ++$i) { 18178 $cellw += (isset($table_colwidths[($colid + $i)]) ? $table_colwidths[($colid + $i)] : 0); 18179 } 18180 } 18181 $cellw += (($colspan - 1) * $cellspacing['H']); 18182 // increment column indicator 18183 $colid += $colspan; 18184 // add rowspan information to table element 18185 if ($rowspan > 1) { 18186 $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)); 18187 } 18188 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x)); 18189 if ($rowspan > 1) { 18190 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1); 18191 } 18192 // push background colors 18193 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) { 18194 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor']; 18195 } 18196 // store border info 18197 if (isset($tdborder) AND !empty($tdborder)) { 18198 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder; 18199 } 18200 $prevLastH = $this->lasth; 18201 // store some info for multicolumn mode 18202 if ($this->rtl) { 18203 $this->colxshift['x'] = $this->w - $this->x - $this->rMargin; 18204 } else { 18205 $this->colxshift['x'] = $this->x - $this->lMargin; 18206 } 18207 $this->colxshift['s'] = $cellspacing; 18208 $this->colxshift['p'] = $current_cell_padding; 18209 // ****** write the cell content ****** 18210 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false); 18211 // restore some values 18212 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 18213 $this->lasth = $prevLastH; 18214 $this->cell_padding = $old_cell_padding; 18215 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x; 18216 // update the end of row position 18217 if ($rowspan <= 1) { 18218 if (isset($dom[$trid]['endy'])) { 18219 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) { 18220 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']); 18221 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) { 18222 $dom[$trid]['endy'] = $this->y; 18223 } 18224 } else { 18225 $dom[$trid]['endy'] = $this->y; 18226 } 18227 if (isset($dom[$trid]['endpage'])) { 18228 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']); 18229 } else { 18230 $dom[$trid]['endpage'] = $this->page; 18231 } 18232 if (isset($dom[$trid]['endcolumn'])) { 18233 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']); 18234 } else { 18235 $dom[$trid]['endcolumn'] = $this->current_column; 18236 } 18237 } else { 18238 // account for row-spanned cells 18239 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x; 18240 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y; 18241 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page; 18242 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column; 18243 } 18244 if (isset($dom[$table_el]['rowspans'])) { 18245 // update endy and endpage on rowspanned cells 18246 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 18247 if ($trwsp['rowspan'] > 0) { 18248 if (isset($dom[$trid]['endpage'])) { 18249 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { 18250 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 18251 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) { 18252 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy']; 18253 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage']; 18254 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn']; 18255 } else { 18256 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm']; 18257 } 18258 } 18259 } 18260 } 18261 } 18262 $this->x += ($cellspacingx / 2); 18263 } else { 18264 // opening tag (or self-closing tag) 18265 if (!isset($opentagpos)) { 18266 if ($this->inxobj) { 18267 // we are inside an XObject template 18268 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']); 18269 } elseif (!$this->InFooter) { 18270 if (isset($this->footerlen[$this->page])) { 18271 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 18272 } else { 18273 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 18274 } 18275 $opentagpos = $this->footerpos[$this->page]; 18276 } 18277 } 18278 $dom = $this->openHTMLTagHandler($dom, $key, $cell); 18279 } 18280 } else { // closing tag 18281 $prev_numpages = $this->numpages; 18282 $old_bordermrk = $this->bordermrk[$this->page]; 18283 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney); 18284 if ($this->bordermrk[$this->page] > $old_bordermrk) { 18285 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk); 18286 } 18287 if ($prev_numpages > $this->numpages) { 18288 $startlinepage = $this->page; 18289 } 18290 } 18291 } elseif (strlen($dom[$key]['value']) > 0) { 18292 // print list-item 18293 if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) { 18294 $this->SetFont($pfontname, $pfontstyle, $pfontsize); 18295 $this->resetLastH(); 18296 $minstartliney = $this->y; 18297 $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize)); 18298 if (is_numeric($pfontsize) AND ($pfontsize > 0)) { 18299 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize); 18300 } 18301 $this->SetFont($curfontname, $curfontstyle, $curfontsize); 18302 $this->resetLastH(); 18303 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) { 18304 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize); 18305 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize); 18306 $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2; 18307 $minstartliney = min($this->y, $minstartliney); 18308 $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney); 18309 } 18310 } 18311 // text 18312 $this->htmlvspace = 0; 18313 if ((!$this->premode) AND $this->isRTLTextDir()) { 18314 // reverse spaces order 18315 $lsp = ''; // left spaces 18316 $rsp = ''; // right spaces 18317 if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { 18318 $lsp = $matches[1]; 18319 } 18320 if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { 18321 $rsp = $matches[1]; 18322 } 18323 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp; 18324 } 18325 if ($newline) { 18326 if (!$this->premode) { 18327 $prelen = strlen($dom[$key]['value']); 18328 if ($this->isRTLTextDir()) { 18329 // right trim except non-breaking space 18330 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']); 18331 } else { 18332 // left trim except non-breaking space 18333 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']); 18334 } 18335 $postlen = strlen($dom[$key]['value']); 18336 if (($postlen == 0) AND ($prelen > 0)) { 18337 $dom[$key]['trimmed_space'] = true; 18338 } 18339 } 18340 $newline = false; 18341 $firstblock = true; 18342 } else { 18343 $firstblock = false; 18344 // replace empty multiple spaces string with a single space 18345 $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']); 18346 } 18347 $strrest = ''; 18348 if ($this->rtl) { 18349 $this->x -= $this->textindent; 18350 } else { 18351 $this->x += $this->textindent; 18352 } 18353 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) { 18354 $strlinelen = $this->GetStringWidth($dom[$key]['value']); 18355 if (!empty($this->HREF) AND (isset($this->HREF['url']))) { 18356 // HTML <a> Link 18357 $hrefcolor = ''; 18358 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) { 18359 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor']; 18360 } 18361 $hrefstyle = -1; 18362 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) { 18363 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle']; 18364 } 18365 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true); 18366 } else { 18367 $wadj = 0; // space to leave for block continuity 18368 if ($this->rtl) { 18369 $cwa = ($this->x - $this->lMargin); 18370 } else { 18371 $cwa = ($this->w - $this->rMargin - $this->x); 18372 } 18373 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) { 18374 // check the next text blocks for continuity 18375 $nkey = ($key + 1); 18376 $write_block = true; 18377 $same_textdir = true; 18378 $tmp_fontname = $this->FontFamily; 18379 $tmp_fontstyle = $this->FontStyle; 18380 $tmp_fontsize = $this->FontSizePt; 18381 while ($write_block AND isset($dom[$nkey])) { 18382 if ($dom[$nkey]['tag']) { 18383 if ($dom[$nkey]['block']) { 18384 // end of block 18385 $write_block = false; 18386 } 18387 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily; 18388 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle; 18389 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt; 18390 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']); 18391 } else { 18392 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']); 18393 if (isset($nextstr[0]) AND $same_textdir) { 18394 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize); 18395 if (isset($nextstr[1])) { 18396 $write_block = false; 18397 } 18398 } 18399 } 18400 ++$nkey; 18401 } 18402 } 18403 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) { 18404 $wadj = 0; 18405 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']); 18406 $numblks = count($nextstr); 18407 if ($numblks > 1) { 18408 // try to split on blank spaces 18409 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)])); 18410 } else { 18411 // set the entire block on new line 18412 $wadj = $this->GetStringWidth($nextstr[0]); 18413 } 18414 } 18415 // check for reversed text direction 18416 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) { 18417 // LTR text on RTL direction or RTL text on LTR direction 18418 $reverse_dir = true; 18419 $this->rtl = !$this->rtl; 18420 $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems 18421 if ($this->rtl) { 18422 $this->x += $revshift; 18423 } else { 18424 $this->x -= $revshift; 18425 } 18426 $xws = $this->x; 18427 } 18428 // ****** write only until the end of the line and get the rest ****** 18429 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj); 18430 // restore default direction 18431 if ($reverse_dir AND ($wadj == 0)) { 18432 $this->x = $xws; 18433 $this->rtl = !$this->rtl; 18434 $reverse_dir = false; 18435 } 18436 } 18437 } 18438 $this->textindent = 0; 18439 if (strlen($strrest) > 0) { 18440 // store the remaining string on the previous $key position 18441 $this->newline = true; 18442 if ($strrest == $dom[$key]['value']) { 18443 // used to avoid infinite loop 18444 ++$loop; 18445 } else { 18446 $loop = 0; 18447 } 18448 $dom[$key]['value'] = $strrest; 18449 if ($cell) { 18450 if ($this->rtl) { 18451 $this->x -= $this->cell_padding['R']; 18452 } else { 18453 $this->x += $this->cell_padding['L']; 18454 } 18455 } 18456 if ($loop < 3) { 18457 --$key; 18458 } 18459 } else { 18460 $loop = 0; 18461 // add the positive font spacing of the last character (if any) 18462 if ($this->font_spacing > 0) { 18463 if ($this->rtl) { 18464 $this->x -= $this->font_spacing; 18465 } else { 18466 $this->x += $this->font_spacing; 18467 } 18468 } 18469 } 18470 } 18471 ++$key; 18472 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')) { 18473 // check if we are on a new page or on a new column 18474 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) { 18475 // we are on a new page or on a new column and the total object height is less than the available vertical space. 18476 // restore previous object 18477 $this->rollbackTransaction(true); 18478 // restore previous values 18479 foreach ($this_method_vars as $vkey => $vval) { 18480 $$vkey = $vval; 18481 } 18482 if (!empty($dom[$key]['thead'])) { 18483 $this->inthead = true; 18484 } 18485 // add a page (or trig AcceptPageBreak() for multicolumn mode) 18486 $pre_y = $this->y; 18487 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 18488 $startliney = $this->y; 18489 } 18490 $undo = true; // avoid infinite loop 18491 } else { 18492 $undo = false; 18493 } 18494 } 18495 } // end for each $key 18496 // align the last line 18497 if (isset($startlinex)) { 18498 $yshift = ($minstartliney - $startliney); 18499 if (($yshift > 0) OR ($this->page > $startlinepage)) { 18500 $yshift = 0; 18501 } 18502 $t_x = 0; 18503 // the last line must be shifted to be aligned as requested 18504 $linew = abs($this->endlinex - $startlinex); 18505 if ($this->inxobj) { 18506 // we are inside an XObject template 18507 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); 18508 if (isset($opentagpos)) { 18509 $midpos = $opentagpos; 18510 } else { 18511 $midpos = 0; 18512 } 18513 if ($midpos > 0) { 18514 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); 18515 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); 18516 } else { 18517 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); 18518 $pend = ''; 18519 } 18520 } else { 18521 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 18522 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 18523 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 18524 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 18525 } elseif (isset($opentagpos)) { 18526 $midpos = $opentagpos; 18527 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 18528 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 18529 $midpos = $this->footerpos[$startlinepage]; 18530 } else { 18531 $midpos = 0; 18532 } 18533 if ($midpos > 0) { 18534 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 18535 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 18536 } else { 18537 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 18538 $pend = ''; 18539 } 18540 } 18541 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { 18542 // calculate shifting amount 18543 $tw = $w; 18544 if ($this->lMargin != $prevlMargin) { 18545 $tw += ($prevlMargin - $this->lMargin); 18546 } 18547 if ($this->rMargin != $prevrMargin) { 18548 $tw += ($prevrMargin - $this->rMargin); 18549 } 18550 $one_space_width = $this->GetStringWidth(chr(32)); 18551 $no = 0; // number of spaces on a line contained on a single block 18552 if ($this->isRTLTextDir()) { // RTL 18553 // remove left space if exist 18554 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[('); 18555 if ($pos1 > 0) { 18556 $pos1 = intval($pos1); 18557 if ($this->isUnicodeFont()) { 18558 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32))); 18559 $spacelen = 2; 18560 } else { 18561 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32))); 18562 $spacelen = 1; 18563 } 18564 if ($pos1 == $pos2) { 18565 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); 18566 if (substr($pmid, $pos1, 4) == '[()]') { 18567 $linew -= $one_space_width; 18568 } elseif ($pos1 == strpos($pmid, '[(')) { 18569 $no = 1; 18570 } 18571 } 18572 } 18573 } else { // LTR 18574 // remove right space if exist 18575 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]'); 18576 if ($pos1 > 0) { 18577 $pos1 = intval($pos1); 18578 if ($this->isUnicodeFont()) { 18579 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2; 18580 $spacelen = 2; 18581 } else { 18582 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1; 18583 $spacelen = 1; 18584 } 18585 if ($pos1 == $pos2) { 18586 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); 18587 $linew -= $one_space_width; 18588 } 18589 } 18590 } 18591 $mdiff = ($tw - $linew); 18592 if ($plalign == 'C') { 18593 if ($this->rtl) { 18594 $t_x = -($mdiff / 2); 18595 } else { 18596 $t_x = ($mdiff / 2); 18597 } 18598 } elseif ($plalign == 'R') { 18599 // right alignment on LTR document 18600 $t_x = $mdiff; 18601 } elseif ($plalign == 'L') { 18602 // left alignment on RTL document 18603 $t_x = -$mdiff; 18604 } 18605 } // end if startlinex 18606 if (($t_x != 0) OR ($yshift < 0)) { 18607 // shift the line 18608 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k)); 18609 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; 18610 $endlinepos = strlen($pstart); 18611 if ($this->inxobj) { 18612 // we are inside an XObject template 18613 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; 18614 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 18615 if ($pak >= $pask) { 18616 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; 18617 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; 18618 } 18619 } 18620 } else { 18621 $this->setPageBuffer($startlinepage, $pstart.$pend); 18622 // shift the annotations and links 18623 if (isset($this->PageAnnots[$this->page])) { 18624 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 18625 if ($pak >= $pask) { 18626 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 18627 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 18628 } 18629 } 18630 } 18631 } 18632 $this->y -= $yshift; 18633 $yshift = 0; 18634 } 18635 } 18636 // restore previous values 18637 $this->setGraphicVars($gvars); 18638 if ($this->num_columns > 1) { 18639 $this->selectColumn(); 18640 } elseif ($this->page > $prevPage) { 18641 $this->lMargin = $this->pagedim[$this->page]['olm']; 18642 $this->rMargin = $this->pagedim[$this->page]['orm']; 18643 } 18644 // restore previous list state 18645 $this->cell_height_ratio = $prev_cell_height_ratio; 18646 $this->listnum = $prev_listnum; 18647 $this->listordered = $prev_listordered; 18648 $this->listcount = $prev_listcount; 18649 $this->lispacer = $prev_lispacer; 18650 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) { 18651 $this->Ln($this->lasth); 18652 if (($this->y < $maxbottomliney) AND ($startlinepage == $this->page)) { 18653 $this->y = $maxbottomliney; 18654 } 18655 } 18656 unset($dom); 18657 } 18658 18659 /** 18660 * Process opening tags. 18661 * @param $dom (array) html dom array 18662 * @param $key (int) current element id 18663 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 18664 * @return $dom array 18665 * @protected 18666 */ 18667 protected function openHTMLTagHandler($dom, $key, $cell) { 18668 $tag = $dom[$key]; 18669 $parent = $dom[($dom[$key]['parent'])]; 18670 $firsttag = ($key == 1); 18671 // check for text direction attribute 18672 if (isset($tag['dir'])) { 18673 $this->setTempRTL($tag['dir']); 18674 } else { 18675 $this->tmprtl = false; 18676 } 18677 if ($tag['block']) { 18678 $hbz = 0; // distance from y to line bottom 18679 $hb = 0; // vertical space between block tags 18680 // calculate vertical space for block tags 18681 if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) { 18682 $cur_h = $this->tagvspaces[$tag['value']][0]['h']; 18683 } elseif (isset($tag['fontsize'])) { 18684 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k); 18685 } else { 18686 $cur_h = $this->getCellHeight($this->FontSize); 18687 } 18688 if (isset($this->tagvspaces[$tag['value']][0]['n'])) { 18689 $on = $this->tagvspaces[$tag['value']][0]['n']; 18690 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 18691 $on = 0.6; 18692 } else { 18693 $on = 1; 18694 } 18695 if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) { 18696 $hb = 0; 18697 } else { 18698 $hb = ($on * $cur_h); 18699 } 18700 if (($this->htmlvspace <= 0) AND ($on > 0)) { 18701 if (isset($parent['fontsize'])) { 18702 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); 18703 } else { 18704 $hbz = $this->getCellHeight($this->FontSize); 18705 } 18706 } 18707 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) { 18708 // fix vertical space after table 18709 $hbz = 0; 18710 } 18711 // closing vertical space 18712 $hbc = 0; 18713 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { 18714 $pre_h = $this->tagvspaces[$tag['value']][1]['h']; 18715 } elseif (isset($parent['fontsize'])) { 18716 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k); 18717 } else { 18718 $pre_h = $this->getCellHeight($this->FontSize); 18719 } 18720 if (isset($this->tagvspaces[$tag['value']][1]['n'])) { 18721 $cn = $this->tagvspaces[$tag['value']][1]['n']; 18722 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 18723 $cn = 0.6; 18724 } else { 18725 $cn = 1; 18726 } 18727 if (isset($this->tagvspaces[$tag['value']][1])) { 18728 $hbc = ($cn * $pre_h); 18729 } 18730 } 18731 // Opening tag 18732 switch($tag['value']) { 18733 case 'table': { 18734 $cp = 0; 18735 $cs = 0; 18736 $dom[$key]['rowspans'] = array(); 18737 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) { 18738 $this->htmlvspace = 0; 18739 // set table header 18740 if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) { 18741 // set table header 18742 $this->thead = $dom[$key]['thead']; 18743 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) { 18744 $this->theadMargins = array(); 18745 $this->theadMargins['cell_padding'] = $this->cell_padding; 18746 $this->theadMargins['lmargin'] = $this->lMargin; 18747 $this->theadMargins['rmargin'] = $this->rMargin; 18748 $this->theadMargins['page'] = $this->page; 18749 $this->theadMargins['cell'] = $cell; 18750 $this->theadMargins['gvars'] = $this->getGraphicVars(); 18751 } 18752 } 18753 } 18754 // store current margins and page 18755 $dom[$key]['old_cell_padding'] = $this->cell_padding; 18756 if (isset($tag['attribute']['cellpadding'])) { 18757 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px'); 18758 $this->SetCellPadding($pad); 18759 } elseif (isset($tag['padding'])) { 18760 $this->cell_padding = $tag['padding']; 18761 } 18762 if (isset($tag['attribute']['cellspacing'])) { 18763 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 18764 } elseif (isset($tag['border-spacing'])) { 18765 $cs = $tag['border-spacing']['V']; 18766 } 18767 $prev_y = $this->y; 18768 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) { 18769 $this->inthead = true; 18770 // add a page (or trig AcceptPageBreak() for multicolumn mode) 18771 $this->checkPageBreak($this->PageBreakTrigger + 1); 18772 } 18773 break; 18774 } 18775 case 'tr': { 18776 // array of columns positions 18777 $dom[$key]['cellpos'] = array(); 18778 break; 18779 } 18780 case 'hr': { 18781 if ((isset($tag['height'])) AND ($tag['height'] != '')) { 18782 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px'); 18783 } else { 18784 $hrHeight = $this->GetLineWidth(); 18785 } 18786 $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag); 18787 $x = $this->GetX(); 18788 $y = $this->GetY(); 18789 $wtmp = $this->w - $this->lMargin - $this->rMargin; 18790 if ($cell) { 18791 $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']); 18792 } 18793 if ((isset($tag['width'])) AND ($tag['width'] != '')) { 18794 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px'); 18795 } else { 18796 $hrWidth = $wtmp; 18797 } 18798 $prevlinewidth = $this->GetLineWidth(); 18799 $this->SetLineWidth($hrHeight); 18800 $this->Line($x, $y, $x + $hrWidth, $y); 18801 $this->SetLineWidth($prevlinewidth); 18802 $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)])); 18803 break; 18804 } 18805 case 'a': { 18806 if (array_key_exists('href', $tag['attribute'])) { 18807 $this->HREF['url'] = $tag['attribute']['href']; 18808 } 18809 break; 18810 } 18811 case 'img': { 18812 if (!empty($tag['attribute']['src'])) { 18813 if ($tag['attribute']['src'][0] === '@') { 18814 // data stream 18815 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1)); 18816 $type = ''; 18817 } else { 18818 // get image type 18819 $type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']); 18820 } 18821 if (!isset($tag['width'])) { 18822 $tag['width'] = 0; 18823 } 18824 if (!isset($tag['height'])) { 18825 $tag['height'] = 0; 18826 } 18827 //if (!isset($tag['attribute']['align'])) { 18828 // the only alignment supported is "bottom" 18829 // further development is required for other modes. 18830 $tag['attribute']['align'] = 'bottom'; 18831 //} 18832 switch($tag['attribute']['align']) { 18833 case 'top': { 18834 $align = 'T'; 18835 break; 18836 } 18837 case 'middle': { 18838 $align = 'M'; 18839 break; 18840 } 18841 case 'bottom': { 18842 $align = 'B'; 18843 break; 18844 } 18845 default: { 18846 $align = 'B'; 18847 break; 18848 } 18849 } 18850 $prevy = $this->y; 18851 $xpos = $this->x; 18852 $imglink = ''; 18853 if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) { 18854 $imglink = $this->HREF['url']; 18855 if ($imglink[0] == '#') { 18856 // convert url to internal link 18857 $lnkdata = explode(',', $imglink); 18858 if (isset($lnkdata[0])) { 18859 $page = intval(substr($lnkdata[0], 1)); 18860 if (empty($page) OR ($page <= 0)) { 18861 $page = $this->page; 18862 } 18863 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { 18864 $lnky = floatval($lnkdata[1]); 18865 } else { 18866 $lnky = 0; 18867 } 18868 $imglink = $this->AddLink(); 18869 $this->SetLink($imglink, $lnky, $page); 18870 } 18871 } 18872 } 18873 $border = 0; 18874 if (isset($tag['border']) AND !empty($tag['border'])) { 18875 // currently only support 1 (frame) or a combination of 'LTRB' 18876 $border = $tag['border']; 18877 } 18878 $iw = ''; 18879 if (isset($tag['width'])) { 18880 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false); 18881 } 18882 $ih = ''; 18883 if (isset($tag['height'])) { 18884 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false); 18885 } 18886 if (($type == 'eps') OR ($type == 'ai')) { 18887 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true); 18888 } elseif ($type == 'svg') { 18889 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true); 18890 } else { 18891 $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true); 18892 } 18893 switch($align) { 18894 case 'T': { 18895 $this->y = $prevy; 18896 break; 18897 } 18898 case 'M': { 18899 $this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2); 18900 break; 18901 } 18902 case 'B': { 18903 $this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio)); 18904 break; 18905 } 18906 } 18907 } 18908 break; 18909 } 18910 case 'dl': { 18911 ++$this->listnum; 18912 if ($this->listnum == 1) { 18913 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 18914 } else { 18915 $this->addHTMLVertSpace(0, 0, $cell, $firsttag); 18916 } 18917 break; 18918 } 18919 case 'dt': { 18920 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 18921 break; 18922 } 18923 case 'dd': { 18924 if ($this->rtl) { 18925 $this->rMargin += $this->listindent; 18926 } else { 18927 $this->lMargin += $this->listindent; 18928 } 18929 ++$this->listindentlevel; 18930 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 18931 break; 18932 } 18933 case 'ul': 18934 case 'ol': { 18935 ++$this->listnum; 18936 if ($tag['value'] == 'ol') { 18937 $this->listordered[$this->listnum] = true; 18938 } else { 18939 $this->listordered[$this->listnum] = false; 18940 } 18941 if (isset($tag['attribute']['start'])) { 18942 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1; 18943 } else { 18944 $this->listcount[$this->listnum] = 0; 18945 } 18946 if ($this->rtl) { 18947 $this->rMargin += $this->listindent; 18948 $this->x -= $this->listindent; 18949 } else { 18950 $this->lMargin += $this->listindent; 18951 $this->x += $this->listindent; 18952 } 18953 ++$this->listindentlevel; 18954 if ($this->listnum == 1) { 18955 if ($key > 1) { 18956 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 18957 } 18958 } else { 18959 $this->addHTMLVertSpace(0, 0, $cell, $firsttag); 18960 } 18961 break; 18962 } 18963 case 'li': { 18964 if ($key > 2) { 18965 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 18966 } 18967 if ($this->listordered[$this->listnum]) { 18968 // ordered item 18969 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) { 18970 $this->lispacer = $parent['attribute']['type']; 18971 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) { 18972 $this->lispacer = $parent['listtype']; 18973 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) { 18974 $this->lispacer = $this->lisymbol; 18975 } else { 18976 $this->lispacer = '#'; 18977 } 18978 ++$this->listcount[$this->listnum]; 18979 if (isset($tag['attribute']['value'])) { 18980 $this->listcount[$this->listnum] = intval($tag['attribute']['value']); 18981 } 18982 } else { 18983 // unordered item 18984 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) { 18985 $this->lispacer = $parent['attribute']['type']; 18986 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) { 18987 $this->lispacer = $parent['listtype']; 18988 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) { 18989 $this->lispacer = $this->lisymbol; 18990 } else { 18991 $this->lispacer = '!'; 18992 } 18993 } 18994 break; 18995 } 18996 case 'blockquote': { 18997 if ($this->rtl) { 18998 $this->rMargin += $this->listindent; 18999 } else { 19000 $this->lMargin += $this->listindent; 19001 } 19002 ++$this->listindentlevel; 19003 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19004 break; 19005 } 19006 case 'br': { 19007 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19008 break; 19009 } 19010 case 'div': { 19011 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19012 break; 19013 } 19014 case 'p': { 19015 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19016 break; 19017 } 19018 case 'pre': { 19019 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19020 $this->premode = true; 19021 break; 19022 } 19023 case 'sup': { 19024 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k)); 19025 break; 19026 } 19027 case 'sub': { 19028 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k)); 19029 break; 19030 } 19031 case 'h1': 19032 case 'h2': 19033 case 'h3': 19034 case 'h4': 19035 case 'h5': 19036 case 'h6': { 19037 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19038 break; 19039 } 19040 // Form fields (since 4.8.000 - 2009-09-07) 19041 case 'form': { 19042 if (isset($tag['attribute']['action'])) { 19043 $this->form_action = $tag['attribute']['action']; 19044 } else { 19045 $this->Error('Please explicitly set action attribute path!'); 19046 } 19047 if (isset($tag['attribute']['enctype'])) { 19048 $this->form_enctype = $tag['attribute']['enctype']; 19049 } else { 19050 $this->form_enctype = 'application/x-www-form-urlencoded'; 19051 } 19052 if (isset($tag['attribute']['method'])) { 19053 $this->form_mode = $tag['attribute']['method']; 19054 } else { 19055 $this->form_mode = 'post'; 19056 } 19057 break; 19058 } 19059 case 'input': { 19060 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19061 $name = $tag['attribute']['name']; 19062 } else { 19063 break; 19064 } 19065 $prop = array(); 19066 $opt = array(); 19067 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) { 19068 $prop['readonly'] = true; 19069 } 19070 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) { 19071 $value = $tag['attribute']['value']; 19072 } 19073 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) { 19074 $opt['maxlen'] = intval($tag['attribute']['maxlength']); 19075 } 19076 $h = $this->getCellHeight($this->FontSize); 19077 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) { 19078 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2; 19079 } else { 19080 $w = $h; 19081 } 19082 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) { 19083 $checked = true; 19084 } else { 19085 $checked = false; 19086 } 19087 if (isset($tag['align'])) { 19088 switch ($tag['align']) { 19089 case 'C': { 19090 $opt['q'] = 1; 19091 break; 19092 } 19093 case 'R': { 19094 $opt['q'] = 2; 19095 break; 19096 } 19097 case 'L': 19098 default: { 19099 break; 19100 } 19101 } 19102 } 19103 switch ($tag['attribute']['type']) { 19104 case 'text': { 19105 if (isset($value)) { 19106 $opt['v'] = $value; 19107 } 19108 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19109 break; 19110 } 19111 case 'password': { 19112 if (isset($value)) { 19113 $opt['v'] = $value; 19114 } 19115 $prop['password'] = 'true'; 19116 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19117 break; 19118 } 19119 case 'checkbox': { 19120 if (!isset($value)) { 19121 break; 19122 } 19123 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false); 19124 break; 19125 } 19126 case 'radio': { 19127 if (!isset($value)) { 19128 break; 19129 } 19130 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false); 19131 break; 19132 } 19133 case 'submit': { 19134 if (!isset($value)) { 19135 $value = 'submit'; 19136 } 19137 $w = $this->GetStringWidth($value) * 1.5; 19138 $h *= 1.6; 19139 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19140 $action = array(); 19141 $action['S'] = 'SubmitForm'; 19142 $action['F'] = $this->form_action; 19143 if ($this->form_enctype != 'FDF') { 19144 $action['Flags'] = array('ExportFormat'); 19145 } 19146 if ($this->form_mode == 'get') { 19147 $action['Flags'] = array('GetMethod'); 19148 } 19149 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false); 19150 break; 19151 } 19152 case 'reset': { 19153 if (!isset($value)) { 19154 $value = 'reset'; 19155 } 19156 $w = $this->GetStringWidth($value) * 1.5; 19157 $h *= 1.6; 19158 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19159 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false); 19160 break; 19161 } 19162 case 'file': { 19163 $prop['fileSelect'] = 'true'; 19164 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19165 if (!isset($value)) { 19166 $value = '*'; 19167 } 19168 $w = $this->GetStringWidth($value) * 2; 19169 $h *= 1.2; 19170 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19171 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();'; 19172 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19173 break; 19174 } 19175 case 'hidden': { 19176 if (isset($value)) { 19177 $opt['v'] = $value; 19178 } 19179 $opt['f'] = array('invisible', 'hidden'); 19180 $this->TextField($name, 0, 0, $prop, $opt, '', '', false); 19181 break; 19182 } 19183 case 'image': { 19184 // THIS TYPE MUST BE FIXED 19185 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) { 19186 $img = $tag['attribute']['src']; 19187 } else { 19188 break; 19189 } 19190 $value = 'img'; 19191 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false)); 19192 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 19193 $jsaction = $tag['attribute']['onclick']; 19194 } else { 19195 $jsaction = ''; 19196 } 19197 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19198 break; 19199 } 19200 case 'button': { 19201 if (!isset($value)) { 19202 $value = ' '; 19203 } 19204 $w = $this->GetStringWidth($value) * 1.5; 19205 $h *= 1.6; 19206 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19207 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 19208 $jsaction = $tag['attribute']['onclick']; 19209 } else { 19210 $jsaction = ''; 19211 } 19212 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19213 break; 19214 } 19215 } 19216 break; 19217 } 19218 case 'textarea': { 19219 $prop = array(); 19220 $opt = array(); 19221 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) { 19222 $prop['readonly'] = true; 19223 } 19224 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19225 $name = $tag['attribute']['name']; 19226 } else { 19227 break; 19228 } 19229 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) { 19230 $opt['v'] = $tag['attribute']['value']; 19231 } 19232 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) { 19233 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2; 19234 } else { 19235 $w = 40; 19236 } 19237 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) { 19238 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize); 19239 } else { 19240 $h = 10; 19241 } 19242 $prop['multiline'] = 'true'; 19243 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19244 break; 19245 } 19246 case 'select': { 19247 $h = $this->getCellHeight($this->FontSize); 19248 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) { 19249 $h *= ($tag['attribute']['size'] + 1); 19250 } 19251 $prop = array(); 19252 $opt = array(); 19253 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19254 $name = $tag['attribute']['name']; 19255 } else { 19256 break; 19257 } 19258 $w = 0; 19259 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) { 19260 $options = explode('#!NwL!#', $tag['attribute']['opt']); 19261 $values = array(); 19262 foreach ($options as $val) { 19263 if (strpos($val, '#!TaB!#') !== false) { 19264 $opts = explode('#!TaB!#', $val); 19265 $values[] = $opts; 19266 $w = max($w, $this->GetStringWidth($opts[1])); 19267 } else { 19268 $values[] = $val; 19269 $w = max($w, $this->GetStringWidth($val)); 19270 } 19271 } 19272 } else { 19273 break; 19274 } 19275 $w *= 2; 19276 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) { 19277 $prop['multipleSelection'] = 'true'; 19278 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false); 19279 } else { 19280 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false); 19281 } 19282 break; 19283 } 19284 case 'tcpdf': { 19285 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) { 19286 // Special tag used to call TCPDF methods 19287 if (isset($tag['attribute']['method'])) { 19288 $tcpdf_method = $tag['attribute']['method']; 19289 if (method_exists($this, $tcpdf_method)) { 19290 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) { 19291 $params = $this->unserializeTCPDFtagParameters($tag['attribute']['params']); 19292 call_user_func_array(array($this, $tcpdf_method), $params); 19293 } else { 19294 $this->$tcpdf_method(); 19295 } 19296 $this->newline = true; 19297 } 19298 } 19299 } 19300 break; 19301 } 19302 default: { 19303 break; 19304 } 19305 } 19306 // define tags that support borders and background colors 19307 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table'); 19308 if (in_array($tag['value'], $bordertags)) { 19309 // set border 19310 $dom[$key]['borderposition'] = $this->getBorderStartPosition(); 19311 } 19312 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) { 19313 $pba = $dom[$key]['attribute']['pagebreakafter']; 19314 // check for pagebreak 19315 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 19316 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19317 $this->checkPageBreak($this->PageBreakTrigger + 1); 19318 } 19319 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 19320 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 19321 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19322 $this->checkPageBreak($this->PageBreakTrigger + 1); 19323 } 19324 } 19325 return $dom; 19326 } 19327 19328 /** 19329 * Process closing tags. 19330 * @param $dom (array) html dom array 19331 * @param $key (int) current element id 19332 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 19333 * @param $maxbottomliney (int) maximum y value of current line 19334 * @return $dom array 19335 * @protected 19336 */ 19337 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) { 19338 $tag = $dom[$key]; 19339 $parent = $dom[($dom[$key]['parent'])]; 19340 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker'))); 19341 $in_table_head = false; 19342 // maximum x position (used to draw borders) 19343 if ($this->rtl) { 19344 $xmax = $this->w; 19345 } else { 19346 $xmax = 0; 19347 } 19348 if ($tag['block']) { 19349 $hbz = 0; // distance from y to line bottom 19350 $hb = 0; // vertical space between block tags 19351 // calculate vertical space for block tags 19352 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { 19353 $pre_h = $this->tagvspaces[$tag['value']][1]['h']; 19354 } elseif (isset($parent['fontsize'])) { 19355 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k); 19356 } else { 19357 $pre_h = $this->getCellHeight($this->FontSize); 19358 } 19359 if (isset($this->tagvspaces[$tag['value']][1]['n'])) { 19360 $cn = $this->tagvspaces[$tag['value']][1]['n']; 19361 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 19362 $cn = 0.6; 19363 } else { 19364 $cn = 1; 19365 } 19366 if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) { 19367 $hb = 0; 19368 } else { 19369 $hb = ($cn * $pre_h); 19370 } 19371 if ($maxbottomliney > $this->PageBreakTrigger) { 19372 $hbz = $this->getCellHeight($this->FontSize); 19373 } elseif ($this->y < $maxbottomliney) { 19374 $hbz = ($maxbottomliney - $this->y); 19375 } 19376 } 19377 // Closing tag 19378 switch($tag['value']) { 19379 case 'tr': { 19380 $table_el = $dom[($dom[$key]['parent'])]['parent']; 19381 if (!isset($parent['endy'])) { 19382 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 19383 $parent['endy'] = $this->y; 19384 } 19385 if (!isset($parent['endpage'])) { 19386 $dom[($dom[$key]['parent'])]['endpage'] = $this->page; 19387 $parent['endpage'] = $this->page; 19388 } 19389 if (!isset($parent['endcolumn'])) { 19390 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column; 19391 $parent['endcolumn'] = $this->current_column; 19392 } 19393 // update row-spanned cells 19394 if (isset($dom[$table_el]['rowspans'])) { 19395 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19396 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1; 19397 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19398 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) { 19399 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']); 19400 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) { 19401 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 19402 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 19403 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; 19404 } 19405 } 19406 } 19407 // report new endy and endpage to the rowspanned cells 19408 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19409 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19410 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']); 19411 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 19412 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']); 19413 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; 19414 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']); 19415 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 19416 } 19417 } 19418 // update remaining rowspanned cells 19419 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19420 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19421 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage']; 19422 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn']; 19423 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy']; 19424 } 19425 } 19426 } 19427 $prev_page = $this->page; 19428 $this->setPage($dom[($dom[$key]['parent'])]['endpage']); 19429 if ($this->num_columns > 1) { 19430 if (($prev_page < $this->page) 19431 AND ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1))) 19432 OR ($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']))) { 19433 // page jump 19434 $this->selectColumn(0); 19435 $dom[($dom[$key]['parent'])]['endcolumn'] = 0; 19436 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 19437 } else { 19438 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']); 19439 $this->y = $dom[($dom[$key]['parent'])]['endy']; 19440 } 19441 } else { 19442 $this->y = $dom[($dom[$key]['parent'])]['endy']; 19443 } 19444 if (isset($dom[$table_el]['attribute']['cellspacing'])) { 19445 $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px'); 19446 } elseif (isset($dom[$table_el]['border-spacing'])) { 19447 $this->y += $dom[$table_el]['border-spacing']['V']; 19448 } 19449 $this->Ln(0, $cell); 19450 if ($this->current_column == $parent['startcolumn']) { 19451 $this->x = $parent['startx']; 19452 } 19453 // account for booklet mode 19454 if ($this->page > $parent['startpage']) { 19455 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) { 19456 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']); 19457 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) { 19458 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']); 19459 } 19460 } 19461 break; 19462 } 19463 case 'tablehead': 19464 // closing tag used for the thead part 19465 $in_table_head = true; 19466 $this->inthead = false; 19467 case 'table': { 19468 $table_el = $parent; 19469 // set default border 19470 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) { 19471 // set default border 19472 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0))); 19473 } else { 19474 $border = 0; 19475 } 19476 $default_border = $border; 19477 // fix bottom line alignment of last line before page break 19478 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) { 19479 // update row-spanned cells 19480 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 19481 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 19482 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) { 19483 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey; 19484 } 19485 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) { 19486 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1; 19487 } 19488 } 19489 } 19490 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) { 19491 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm']; 19492 $dom[$prevtrkey]['endy'] = $pgendy; 19493 // update row-spanned cells 19494 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 19495 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 19496 if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) { 19497 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy; 19498 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1; 19499 } 19500 } 19501 } 19502 } 19503 $prevtrkey = $trkey; 19504 $table_el = $dom[($dom[$key]['parent'])]; 19505 } 19506 // for each row 19507 if (count($table_el['trids']) > 0) { 19508 unset($xmax); 19509 } 19510 foreach ($table_el['trids'] as $j => $trkey) { 19511 $parent = $dom[$trkey]; 19512 if (!isset($xmax)) { 19513 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx']; 19514 } 19515 // for each cell on the row 19516 foreach ($parent['cellpos'] as $k => $cellpos) { 19517 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) { 19518 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx']; 19519 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx']; 19520 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy']; 19521 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage']; 19522 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage']; 19523 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn']; 19524 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn']; 19525 } else { 19526 $endy = $parent['endy']; 19527 $startpage = $parent['startpage']; 19528 $endpage = $parent['endpage']; 19529 $startcolumn = $parent['startcolumn']; 19530 $endcolumn = $parent['endcolumn']; 19531 } 19532 if ($this->num_columns == 0) { 19533 $this->num_columns = 1; 19534 } 19535 if (isset($cellpos['border'])) { 19536 $border = $cellpos['border']; 19537 } 19538 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { 19539 $this->SetFillColorArray($cellpos['bgcolor']); 19540 $fill = true; 19541 } else { 19542 $fill = false; 19543 } 19544 $x = $cellpos['startx']; 19545 $y = $parent['starty']; 19546 $starty = $y; 19547 $w = abs($cellpos['endx'] - $cellpos['startx']); 19548 // get border modes 19549 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 19550 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 19551 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 19552 // design borders around HTML cells. 19553 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 19554 $ccode = ''; 19555 $this->setPage($page); 19556 if ($this->num_columns < 2) { 19557 // single-column mode 19558 $this->x = $x; 19559 $this->y = $this->tMargin; 19560 } 19561 // account for margin changes 19562 if ($page > $startpage) { 19563 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 19564 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 19565 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 19566 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 19567 } 19568 } 19569 if ($startpage == $endpage) { // single page 19570 $deltacol = 0; 19571 $deltath = 0; 19572 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 19573 $this->selectColumn($column); 19574 if ($startcolumn == $endcolumn) { // single column 19575 $cborder = $border; 19576 $h = $endy - $parent['starty']; 19577 $this->y = $y; 19578 $this->x = $x; 19579 } elseif ($column == $startcolumn) { // first column 19580 $cborder = $border_start; 19581 $this->y = $starty; 19582 $this->x = $x; 19583 $h = $this->h - $this->y - $this->bMargin; 19584 if ($this->rtl) { 19585 $deltacol = $this->x + $this->rMargin - $this->w; 19586 } else { 19587 $deltacol = $this->x - $this->lMargin; 19588 } 19589 } elseif ($column == $endcolumn) { // end column 19590 $cborder = $border_end; 19591 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19592 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19593 } 19594 $this->x += $deltacol; 19595 $h = $endy - $this->y; 19596 } else { // middle column 19597 $cborder = $border_middle; 19598 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19599 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19600 } 19601 $this->x += $deltacol; 19602 $h = $this->h - $this->y - $this->bMargin; 19603 } 19604 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19605 } // end for each column 19606 } elseif ($page == $startpage) { // first page 19607 $deltacol = 0; 19608 $deltath = 0; 19609 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 19610 $this->selectColumn($column); 19611 if ($column == $startcolumn) { // first column 19612 $cborder = $border_start; 19613 $this->y = $starty; 19614 $this->x = $x; 19615 $h = $this->h - $this->y - $this->bMargin; 19616 if ($this->rtl) { 19617 $deltacol = $this->x + $this->rMargin - $this->w; 19618 } else { 19619 $deltacol = $this->x - $this->lMargin; 19620 } 19621 } else { // middle column 19622 $cborder = $border_middle; 19623 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19624 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19625 } 19626 $this->x += $deltacol; 19627 $h = $this->h - $this->y - $this->bMargin; 19628 } 19629 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19630 } // end for each column 19631 } elseif ($page == $endpage) { // last page 19632 $deltacol = 0; 19633 $deltath = 0; 19634 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 19635 $this->selectColumn($column); 19636 if ($column == $endcolumn) { // end column 19637 $cborder = $border_end; 19638 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19639 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19640 } 19641 $this->x += $deltacol; 19642 $h = $endy - $this->y; 19643 } else { // middle column 19644 $cborder = $border_middle; 19645 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19646 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19647 } 19648 $this->x += $deltacol; 19649 $h = $this->h - $this->y - $this->bMargin; 19650 } 19651 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19652 } // end for each column 19653 } else { // middle page 19654 $deltacol = 0; 19655 $deltath = 0; 19656 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 19657 $this->selectColumn($column); 19658 $cborder = $border_middle; 19659 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19660 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19661 } 19662 $this->x += $deltacol; 19663 $h = $this->h - $this->y - $this->bMargin; 19664 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19665 } // end for each column 19666 } 19667 if (!empty($cborder) OR !empty($fill)) { 19668 $offsetlen = strlen($ccode); 19669 // draw border and fill 19670 if ($this->inxobj) { 19671 // we are inside an XObject template 19672 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 19673 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 19674 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 19675 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 19676 } else { 19677 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 19678 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 19679 } 19680 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 19681 $pstart = substr($pagebuff, 0, $pagemark); 19682 $pend = substr($pagebuff, $pagemark); 19683 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 19684 } else { 19685 // draw border and fill 19686 if (end($this->transfmrk[$this->page]) !== false) { 19687 $pagemarkkey = key($this->transfmrk[$this->page]); 19688 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 19689 } elseif ($this->InFooter) { 19690 $pagemark = $this->footerpos[$this->page]; 19691 } else { 19692 $pagemark = $this->intmrk[$this->page]; 19693 } 19694 $pagebuff = $this->getPageBuffer($this->page); 19695 $pstart = substr($pagebuff, 0, $pagemark); 19696 $pend = substr($pagebuff, $pagemark); 19697 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 19698 } 19699 } 19700 } // end for each page 19701 // restore default border 19702 $border = $default_border; 19703 } // end for each cell on the row 19704 if (isset($table_el['attribute']['cellspacing'])) { 19705 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px'); 19706 } elseif (isset($table_el['border-spacing'])) { 19707 $this->y += $table_el['border-spacing']['V']; 19708 } 19709 $this->Ln(0, $cell); 19710 $this->x = $parent['startx']; 19711 if ($endpage > $startpage) { 19712 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) { 19713 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']); 19714 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) { 19715 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']); 19716 } 19717 } 19718 } 19719 if (!$in_table_head) { // we are not inside a thead section 19720 $this->cell_padding = $table_el['old_cell_padding']; 19721 // reset row height 19722 $this->resetLastH(); 19723 if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) { 19724 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]); 19725 if (($plendiff > 0) AND ($plendiff < 60)) { 19726 $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff); 19727 if (substr($pagediff, 0, 5) == 'BT /F') { 19728 // the difference is only a font setting 19729 $plendiff = 0; 19730 } 19731 } 19732 if ($plendiff == 0) { 19733 // remove last blank page 19734 $this->deletePage($this->numpages); 19735 } 19736 } 19737 if (isset($this->theadMargins['top'])) { 19738 // restore top margin 19739 $this->tMargin = $this->theadMargins['top']; 19740 } 19741 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) { 19742 // reset main table header 19743 $this->thead = ''; 19744 $this->theadMargins = array(); 19745 $this->pagedim[$this->page]['tm'] = $this->tMargin; 19746 } 19747 } 19748 $parent = $table_el; 19749 break; 19750 } 19751 case 'a': { 19752 $this->HREF = ''; 19753 break; 19754 } 19755 case 'sup': { 19756 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k)); 19757 break; 19758 } 19759 case 'sub': { 19760 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k)); 19761 break; 19762 } 19763 case 'div': { 19764 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19765 break; 19766 } 19767 case 'blockquote': { 19768 if ($this->rtl) { 19769 $this->rMargin -= $this->listindent; 19770 } else { 19771 $this->lMargin -= $this->listindent; 19772 } 19773 --$this->listindentlevel; 19774 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19775 break; 19776 } 19777 case 'p': { 19778 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19779 break; 19780 } 19781 case 'pre': { 19782 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19783 $this->premode = false; 19784 break; 19785 } 19786 case 'dl': { 19787 --$this->listnum; 19788 if ($this->listnum <= 0) { 19789 $this->listnum = 0; 19790 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19791 } else { 19792 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19793 } 19794 $this->resetLastH(); 19795 break; 19796 } 19797 case 'dt': { 19798 $this->lispacer = ''; 19799 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19800 break; 19801 } 19802 case 'dd': { 19803 $this->lispacer = ''; 19804 if ($this->rtl) { 19805 $this->rMargin -= $this->listindent; 19806 } else { 19807 $this->lMargin -= $this->listindent; 19808 } 19809 --$this->listindentlevel; 19810 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19811 break; 19812 } 19813 case 'ul': 19814 case 'ol': { 19815 --$this->listnum; 19816 $this->lispacer = ''; 19817 if ($this->rtl) { 19818 $this->rMargin -= $this->listindent; 19819 } else { 19820 $this->lMargin -= $this->listindent; 19821 } 19822 --$this->listindentlevel; 19823 if ($this->listnum <= 0) { 19824 $this->listnum = 0; 19825 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19826 } else { 19827 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19828 } 19829 $this->resetLastH(); 19830 break; 19831 } 19832 case 'li': { 19833 $this->lispacer = ''; 19834 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19835 break; 19836 } 19837 case 'h1': 19838 case 'h2': 19839 case 'h3': 19840 case 'h4': 19841 case 'h5': 19842 case 'h6': { 19843 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19844 break; 19845 } 19846 // Form fields (since 4.8.000 - 2009-09-07) 19847 case 'form': { 19848 $this->form_action = ''; 19849 $this->form_enctype = 'application/x-www-form-urlencoded'; 19850 break; 19851 } 19852 default : { 19853 break; 19854 } 19855 } 19856 // draw border and background (if any) 19857 $this->drawHTMLTagBorder($parent, $xmax); 19858 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) { 19859 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter']; 19860 // check for pagebreak 19861 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 19862 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19863 $this->checkPageBreak($this->PageBreakTrigger + 1); 19864 } 19865 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 19866 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 19867 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19868 $this->checkPageBreak($this->PageBreakTrigger + 1); 19869 } 19870 } 19871 $this->tmprtl = false; 19872 return $dom; 19873 } 19874 19875 /** 19876 * Add vertical spaces if needed. 19877 * @param $hbz (string) Distance between current y and line bottom. 19878 * @param $hb (string) The height of the break. 19879 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 19880 * @param $firsttag (boolean) set to true when the tag is the first. 19881 * @param $lasttag (boolean) set to true when the tag is the last. 19882 * @protected 19883 */ 19884 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) { 19885 if ($firsttag) { 19886 $this->Ln(0, $cell); 19887 $this->htmlvspace = 0; 19888 return; 19889 } 19890 if ($lasttag) { 19891 $this->Ln($hbz, $cell); 19892 $this->htmlvspace = 0; 19893 return; 19894 } 19895 if ($hb < $this->htmlvspace) { 19896 $hd = 0; 19897 } else { 19898 $hd = $hb - $this->htmlvspace; 19899 $this->htmlvspace = $hb; 19900 } 19901 $this->Ln(($hbz + $hd), $cell); 19902 } 19903 19904 /** 19905 * Return the starting coordinates to draw an html border 19906 * @return array containing top-left border coordinates 19907 * @protected 19908 * @since 5.7.000 (2010-08-03) 19909 */ 19910 protected function getBorderStartPosition() { 19911 if ($this->rtl) { 19912 $xmax = $this->lMargin; 19913 } else { 19914 $xmax = $this->w - $this->rMargin; 19915 } 19916 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax); 19917 } 19918 19919 /** 19920 * Draw an HTML block border and fill 19921 * @param $tag (array) array of tag properties. 19922 * @param $xmax (int) end X coordinate for border. 19923 * @protected 19924 * @since 5.7.000 (2010-08-03) 19925 */ 19926 protected function drawHTMLTagBorder($tag, $xmax) { 19927 if (!isset($tag['borderposition'])) { 19928 // nothing to draw 19929 return; 19930 } 19931 $prev_x = $this->x; 19932 $prev_y = $this->y; 19933 $prev_lasth = $this->lasth; 19934 $border = 0; 19935 $fill = false; 19936 $this->lasth = 0; 19937 if (isset($tag['border']) AND !empty($tag['border'])) { 19938 // get border style 19939 $border = $tag['border']; 19940 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) { 19941 // border for table header 19942 $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 19943 } 19944 } 19945 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) { 19946 // get background color 19947 $old_bgcolor = $this->bgcolor; 19948 $this->SetFillColorArray($tag['bgcolor']); 19949 $fill = true; 19950 } 19951 if (!$border AND !$fill) { 19952 // nothing to draw 19953 return; 19954 } 19955 if (isset($tag['attribute']['cellspacing'])) { 19956 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 19957 $cellspacing = array('H' => $clsp, 'V' => $clsp); 19958 } elseif (isset($tag['border-spacing'])) { 19959 $cellspacing = $tag['border-spacing']; 19960 } else { 19961 $cellspacing = array('H' => 0, 'V' => 0); 19962 } 19963 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) { 19964 // draw the border externally respect the sqare edge. 19965 $border['mode'] = 'ext'; 19966 } 19967 if ($this->rtl) { 19968 if ($xmax >= $tag['borderposition']['x']) { 19969 $xmax = $tag['borderposition']['xmax']; 19970 } 19971 $w = ($tag['borderposition']['x'] - $xmax); 19972 } else { 19973 if ($xmax <= $tag['borderposition']['x']) { 19974 $xmax = $tag['borderposition']['xmax']; 19975 } 19976 $w = ($xmax - $tag['borderposition']['x']); 19977 } 19978 if ($w <= 0) { 19979 return; 19980 } 19981 $w += $cellspacing['H']; 19982 $startpage = $tag['borderposition']['page']; 19983 $startcolumn = $tag['borderposition']['column']; 19984 $x = $tag['borderposition']['x']; 19985 $y = $tag['borderposition']['y']; 19986 $endpage = $this->page; 19987 $starty = $tag['borderposition']['y'] - $cellspacing['V']; 19988 $currentY = $this->y; 19989 $this->x = $x; 19990 // get latest column 19991 $endcolumn = $this->current_column; 19992 if ($this->num_columns == 0) { 19993 $this->num_columns = 1; 19994 } 19995 // get border modes 19996 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 19997 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 19998 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 19999 // temporary disable page regions 20000 $temp_page_regions = $this->page_regions; 20001 $this->page_regions = array(); 20002 // design borders around HTML cells. 20003 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 20004 $ccode = ''; 20005 $this->setPage($page); 20006 if ($this->num_columns < 2) { 20007 // single-column mode 20008 $this->x = $x; 20009 $this->y = $this->tMargin; 20010 } 20011 // account for margin changes 20012 if ($page > $startpage) { 20013 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 20014 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 20015 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 20016 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 20017 } 20018 } 20019 if ($startpage == $endpage) { 20020 // single page 20021 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 20022 $this->selectColumn($column); 20023 if ($startcolumn == $endcolumn) { // single column 20024 $cborder = $border; 20025 $h = ($currentY - $y) + $cellspacing['V']; 20026 $this->y = $starty; 20027 } elseif ($column == $startcolumn) { // first column 20028 $cborder = $border_start; 20029 $this->y = $starty; 20030 $h = $this->h - $this->y - $this->bMargin; 20031 } elseif ($column == $endcolumn) { // end column 20032 $cborder = $border_end; 20033 $h = $currentY - $this->y; 20034 } else { // middle column 20035 $cborder = $border_middle; 20036 $h = $this->h - $this->y - $this->bMargin; 20037 } 20038 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20039 } // end for each column 20040 } elseif ($page == $startpage) { // first page 20041 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 20042 $this->selectColumn($column); 20043 if ($column == $startcolumn) { // first column 20044 $cborder = $border_start; 20045 $this->y = $starty; 20046 $h = $this->h - $this->y - $this->bMargin; 20047 } else { // middle column 20048 $cborder = $border_middle; 20049 $h = $this->h - $this->y - $this->bMargin; 20050 } 20051 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20052 } // end for each column 20053 } elseif ($page == $endpage) { // last page 20054 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 20055 $this->selectColumn($column); 20056 if ($column == $endcolumn) { 20057 // end column 20058 $cborder = $border_end; 20059 $h = $currentY - $this->y; 20060 } else { 20061 // middle column 20062 $cborder = $border_middle; 20063 $h = $this->h - $this->y - $this->bMargin; 20064 } 20065 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20066 } // end for each column 20067 } else { // middle page 20068 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 20069 $this->selectColumn($column); 20070 $cborder = $border_middle; 20071 $h = $this->h - $this->y - $this->bMargin; 20072 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20073 } // end for each column 20074 } 20075 if ($cborder OR $fill) { 20076 $offsetlen = strlen($ccode); 20077 // draw border and fill 20078 if ($this->inxobj) { 20079 // we are inside an XObject template 20080 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 20081 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 20082 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 20083 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 20084 } else { 20085 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 20086 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 20087 } 20088 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 20089 $pstart = substr($pagebuff, 0, $pagemark); 20090 $pend = substr($pagebuff, $pagemark); 20091 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 20092 } else { 20093 if (end($this->transfmrk[$this->page]) !== false) { 20094 $pagemarkkey = key($this->transfmrk[$this->page]); 20095 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 20096 } elseif ($this->InFooter) { 20097 $pagemark = $this->footerpos[$this->page]; 20098 } else { 20099 $pagemark = $this->intmrk[$this->page]; 20100 } 20101 $pagebuff = $this->getPageBuffer($this->page); 20102 $pstart = substr($pagebuff, 0, $pagemark); 20103 $pend = substr($pagebuff, $pagemark); 20104 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 20105 $this->bordermrk[$this->page] += $offsetlen; 20106 $this->cntmrk[$this->page] += $offsetlen; 20107 } 20108 } 20109 } // end for each page 20110 // restore page regions 20111 $this->page_regions = $temp_page_regions; 20112 if (isset($old_bgcolor)) { 20113 // restore background color 20114 $this->SetFillColorArray($old_bgcolor); 20115 } 20116 // restore pointer position 20117 $this->x = $prev_x; 20118 $this->y = $prev_y; 20119 $this->lasth = $prev_lasth; 20120 } 20121 20122 /** 20123 * Set the default bullet to be used as LI bullet symbol 20124 * @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') 20125 * @public 20126 * @since 4.0.028 (2008-09-26) 20127 */ 20128 public function setLIsymbol($symbol='!') { 20129 // check for custom image symbol 20130 if (substr($symbol, 0, 4) == 'img|') { 20131 $this->lisymbol = $symbol; 20132 return; 20133 } 20134 $symbol = strtolower($symbol); 20135 $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'); 20136 if (in_array($symbol, $valid_symbols)) { 20137 $this->lisymbol = $symbol; 20138 } else { 20139 $this->lisymbol = ''; 20140 } 20141 } 20142 20143 /** 20144 * Set the booklet mode for double-sided pages. 20145 * @param $booklet (boolean) true set the booklet mode on, false otherwise. 20146 * @param $inner (float) Inner page margin. 20147 * @param $outer (float) Outer page margin. 20148 * @public 20149 * @since 4.2.000 (2008-10-29) 20150 */ 20151 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) { 20152 $this->booklet = $booklet; 20153 if ($inner >= 0) { 20154 $this->lMargin = $inner; 20155 } 20156 if ($outer >= 0) { 20157 $this->rMargin = $outer; 20158 } 20159 } 20160 20161 /** 20162 * Swap the left and right margins. 20163 * @param $reverse (boolean) if true swap left and right margins. 20164 * @protected 20165 * @since 4.2.000 (2008-10-29) 20166 */ 20167 protected function swapMargins($reverse=true) { 20168 if ($reverse) { 20169 // swap left and right margins 20170 $mtemp = $this->original_lMargin; 20171 $this->original_lMargin = $this->original_rMargin; 20172 $this->original_rMargin = $mtemp; 20173 $deltam = $this->original_lMargin - $this->original_rMargin; 20174 $this->lMargin += $deltam; 20175 $this->rMargin -= $deltam; 20176 } 20177 } 20178 20179 /** 20180 * Set the vertical spaces for HTML tags. 20181 * The array must have the following structure (example): 20182 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1))); 20183 * The first array level contains the tag names, 20184 * the second level contains 0 for opening tags or 1 for closing tags, 20185 * the third level contains the vertical space unit (h) and the number spaces to add (n). 20186 * If the h parameter is not specified, default values are used. 20187 * @param $tagvs (array) array of tags and relative vertical spaces. 20188 * @public 20189 * @since 4.2.001 (2008-10-30) 20190 */ 20191 public function setHtmlVSpace($tagvs) { 20192 $this->tagvspaces = $tagvs; 20193 } 20194 20195 /** 20196 * Set custom width for list indentation. 20197 * @param $width (float) width of the indentation. Use negative value to disable it. 20198 * @public 20199 * @since 4.2.007 (2008-11-12) 20200 */ 20201 public function setListIndentWidth($width) { 20202 return $this->customlistindent = floatval($width); 20203 } 20204 20205 /** 20206 * Set the top/bottom cell sides to be open or closed when the cell cross the page. 20207 * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page. 20208 * @public 20209 * @since 4.2.010 (2008-11-14) 20210 */ 20211 public function setOpenCell($isopen) { 20212 $this->opencell = $isopen; 20213 } 20214 20215 /** 20216 * Set the color and font style for HTML links. 20217 * @param $color (array) RGB array of colors 20218 * @param $fontstyle (string) additional font styles to add 20219 * @public 20220 * @since 4.4.003 (2008-12-09) 20221 */ 20222 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') { 20223 $this->htmlLinkColorArray = $color; 20224 $this->htmlLinkFontStyle = $fontstyle; 20225 } 20226 20227 /** 20228 * Convert HTML string containing value and unit of measure to user's units or points. 20229 * @param $htmlval (string) String containing values and unit. 20230 * @param $refsize (string) Reference value in points. 20231 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). 20232 * @param $points (boolean) If true returns points, otherwise returns value in user's units. 20233 * @return float value in user's unit or point if $points=true 20234 * @public 20235 * @since 4.4.004 (2008-12-10) 20236 */ 20237 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) { 20238 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt'); 20239 $retval = 0; 20240 $value = 0; 20241 $unit = 'px'; 20242 if ($points) { 20243 $k = 1; 20244 } else { 20245 $k = $this->k; 20246 } 20247 if (in_array($defaultunit, $supportedunits)) { 20248 $unit = $defaultunit; 20249 } 20250 if (is_numeric($htmlval)) { 20251 $value = floatval($htmlval); 20252 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) { 20253 $value = floatval($mnum[1]); 20254 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) { 20255 if (in_array($munit[1], $supportedunits)) { 20256 $unit = $munit[1]; 20257 } 20258 } 20259 } 20260 switch ($unit) { 20261 // percentage 20262 case '%': { 20263 $retval = (($value * $refsize) / 100); 20264 break; 20265 } 20266 // relative-size 20267 case 'em': { 20268 $retval = ($value * $refsize); 20269 break; 20270 } 20271 // height of lower case 'x' (about half the font-size) 20272 case 'ex': { 20273 $retval = ($value * ($refsize / 2)); 20274 break; 20275 } 20276 // absolute-size 20277 case 'in': { 20278 $retval = (($value * $this->dpi) / $k); 20279 break; 20280 } 20281 // centimeters 20282 case 'cm': { 20283 $retval = (($value / 2.54 * $this->dpi) / $k); 20284 break; 20285 } 20286 // millimeters 20287 case 'mm': { 20288 $retval = (($value / 25.4 * $this->dpi) / $k); 20289 break; 20290 } 20291 // one pica is 12 points 20292 case 'pc': { 20293 $retval = (($value * 12) / $k); 20294 break; 20295 } 20296 // points 20297 case 'pt': { 20298 $retval = ($value / $k); 20299 break; 20300 } 20301 // pixels 20302 case 'px': { 20303 $retval = $this->pixelsToUnits($value); 20304 if ($points) { 20305 $retval *= $this->k; 20306 } 20307 break; 20308 } 20309 } 20310 return $retval; 20311 } 20312 20313 /** 20314 * Output an HTML list bullet or ordered item symbol 20315 * @param $listdepth (int) list nesting level 20316 * @param $listtype (string) type of list 20317 * @param $size (float) current font size 20318 * @protected 20319 * @since 4.4.004 (2008-12-10) 20320 */ 20321 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) { 20322 if ($this->state != 2) { 20323 return; 20324 } 20325 $size /= $this->k; 20326 $fill = ''; 20327 $bgcolor = $this->bgcolor; 20328 $color = $this->fgcolor; 20329 $strokecolor = $this->strokecolor; 20330 $width = 0; 20331 $textitem = ''; 20332 $tmpx = $this->x; 20333 $lspace = $this->GetStringWidth(' '); 20334 if ($listtype == '^') { 20335 // special symbol used for avoid justification of rect bullet 20336 $this->lispacer = ''; 20337 return; 20338 } elseif ($listtype == '!') { 20339 // set default list type for unordered list 20340 $deftypes = array('disc', 'circle', 'square'); 20341 $listtype = $deftypes[($listdepth - 1) % 3]; 20342 } elseif ($listtype == '#') { 20343 // set default list type for ordered list 20344 $listtype = 'decimal'; 20345 } elseif (substr($listtype, 0, 4) == 'img|') { 20346 // custom image type ('img|type|width|height|image.ext') 20347 $img = explode('|', $listtype); 20348 $listtype = 'img'; 20349 } 20350 switch ($listtype) { 20351 // unordered types 20352 case 'none': { 20353 break; 20354 } 20355 case 'disc': { 20356 $r = $size / 6; 20357 $lspace += (2 * $r); 20358 if ($this->rtl) { 20359 $this->x += $lspace; 20360 } else { 20361 $this->x -= $lspace; 20362 } 20363 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8); 20364 break; 20365 } 20366 case 'circle': { 20367 $r = $size / 6; 20368 $lspace += (2 * $r); 20369 if ($this->rtl) { 20370 $this->x += $lspace; 20371 } else { 20372 $this->x -= $lspace; 20373 } 20374 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor; 20375 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color); 20376 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8); 20377 $this->_out($prev_line_style); // restore line settings 20378 break; 20379 } 20380 case 'square': { 20381 $l = $size / 3; 20382 $lspace += $l; 20383 if ($this->rtl) {; 20384 $this->x += $lspace; 20385 } else { 20386 $this->x -= $lspace; 20387 } 20388 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color); 20389 break; 20390 } 20391 case 'img': { 20392 // 1=>type, 2=>width, 3=>height, 4=>image.ext 20393 $lspace += $img[2]; 20394 if ($this->rtl) {; 20395 $this->x += $lspace; 20396 } else { 20397 $this->x -= $lspace; 20398 } 20399 $imgtype = strtolower($img[1]); 20400 $prev_y = $this->y; 20401 switch ($imgtype) { 20402 case 'svg': { 20403 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false); 20404 break; 20405 } 20406 case 'ai': 20407 case 'eps': { 20408 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false); 20409 break; 20410 } 20411 default: { 20412 $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); 20413 break; 20414 } 20415 } 20416 $this->y = $prev_y; 20417 break; 20418 } 20419 // ordered types 20420 // $this->listcount[$this->listnum]; 20421 // $textitem 20422 case '1': 20423 case 'decimal': { 20424 $textitem = $this->listcount[$this->listnum]; 20425 break; 20426 } 20427 case 'decimal-leading-zero': { 20428 $textitem = sprintf('%02d', $this->listcount[$this->listnum]); 20429 break; 20430 } 20431 case 'i': 20432 case 'lower-roman': { 20433 $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum])); 20434 break; 20435 } 20436 case 'I': 20437 case 'upper-roman': { 20438 $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]); 20439 break; 20440 } 20441 case 'a': 20442 case 'lower-alpha': 20443 case 'lower-latin': { 20444 $textitem = chr(97 + $this->listcount[$this->listnum] - 1); 20445 break; 20446 } 20447 case 'A': 20448 case 'upper-alpha': 20449 case 'upper-latin': { 20450 $textitem = chr(65 + $this->listcount[$this->listnum] - 1); 20451 break; 20452 } 20453 case 'lower-greek': { 20454 $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode); 20455 break; 20456 } 20457 /* 20458 // Types to be implemented (special handling) 20459 case 'hebrew': { 20460 break; 20461 } 20462 case 'armenian': { 20463 break; 20464 } 20465 case 'georgian': { 20466 break; 20467 } 20468 case 'cjk-ideographic': { 20469 break; 20470 } 20471 case 'hiragana': { 20472 break; 20473 } 20474 case 'katakana': { 20475 break; 20476 } 20477 case 'hiragana-iroha': { 20478 break; 20479 } 20480 case 'katakana-iroha': { 20481 break; 20482 } 20483 */ 20484 default: { 20485 $textitem = $this->listcount[$this->listnum]; 20486 } 20487 } 20488 if (!TCPDF_STATIC::empty_string($textitem)) { 20489 // Check whether we need a new page or new column 20490 $prev_y = $this->y; 20491 $h = $this->getCellHeight($this->FontSize); 20492 if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) { 20493 $tmpx = $this->x; 20494 } 20495 // print ordered item 20496 if ($this->rtl) { 20497 $textitem = '.'.$textitem; 20498 } else { 20499 $textitem = $textitem.'.'; 20500 } 20501 $lspace += $this->GetStringWidth($textitem); 20502 if ($this->rtl) { 20503 $this->x += $lspace; 20504 } else { 20505 $this->x -= $lspace; 20506 } 20507 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false); 20508 } 20509 $this->x = $tmpx; 20510 $this->lispacer = '^'; 20511 // restore colors 20512 $this->SetFillColorArray($bgcolor); 20513 $this->SetDrawColorArray($strokecolor); 20514 $this->SettextColorArray($color); 20515 } 20516 20517 /** 20518 * Returns current graphic variables as array. 20519 * @return array of graphic variables 20520 * @protected 20521 * @since 4.2.010 (2008-11-14) 20522 */ 20523 protected function getGraphicVars() { 20524 $grapvars = array( 20525 'FontFamily' => $this->FontFamily, 20526 'FontStyle' => $this->FontStyle, 20527 'FontSizePt' => $this->FontSizePt, 20528 'rMargin' => $this->rMargin, 20529 'lMargin' => $this->lMargin, 20530 'cell_padding' => $this->cell_padding, 20531 'cell_margin' => $this->cell_margin, 20532 'LineWidth' => $this->LineWidth, 20533 'linestyleWidth' => $this->linestyleWidth, 20534 'linestyleCap' => $this->linestyleCap, 20535 'linestyleJoin' => $this->linestyleJoin, 20536 'linestyleDash' => $this->linestyleDash, 20537 'textrendermode' => $this->textrendermode, 20538 'textstrokewidth' => $this->textstrokewidth, 20539 'DrawColor' => $this->DrawColor, 20540 'FillColor' => $this->FillColor, 20541 'TextColor' => $this->TextColor, 20542 'ColorFlag' => $this->ColorFlag, 20543 'bgcolor' => $this->bgcolor, 20544 'fgcolor' => $this->fgcolor, 20545 'htmlvspace' => $this->htmlvspace, 20546 'listindent' => $this->listindent, 20547 'listindentlevel' => $this->listindentlevel, 20548 'listnum' => $this->listnum, 20549 'listordered' => $this->listordered, 20550 'listcount' => $this->listcount, 20551 'lispacer' => $this->lispacer, 20552 'cell_height_ratio' => $this->cell_height_ratio, 20553 'font_stretching' => $this->font_stretching, 20554 'font_spacing' => $this->font_spacing, 20555 'alpha' => $this->alpha, 20556 // extended 20557 'lasth' => $this->lasth, 20558 'tMargin' => $this->tMargin, 20559 'bMargin' => $this->bMargin, 20560 'AutoPageBreak' => $this->AutoPageBreak, 20561 'PageBreakTrigger' => $this->PageBreakTrigger, 20562 'x' => $this->x, 20563 'y' => $this->y, 20564 'w' => $this->w, 20565 'h' => $this->h, 20566 'wPt' => $this->wPt, 20567 'hPt' => $this->hPt, 20568 'fwPt' => $this->fwPt, 20569 'fhPt' => $this->fhPt, 20570 'page' => $this->page, 20571 'current_column' => $this->current_column, 20572 'num_columns' => $this->num_columns 20573 ); 20574 return $grapvars; 20575 } 20576 20577 /** 20578 * Set graphic variables. 20579 * @param $gvars (array) array of graphic variablesto restore 20580 * @param $extended (boolean) if true restore extended graphic variables 20581 * @protected 20582 * @since 4.2.010 (2008-11-14) 20583 */ 20584 protected function setGraphicVars($gvars, $extended=false) { 20585 if ($this->state != 2) { 20586 return; 20587 } 20588 $this->FontFamily = $gvars['FontFamily']; 20589 $this->FontStyle = $gvars['FontStyle']; 20590 $this->FontSizePt = $gvars['FontSizePt']; 20591 $this->rMargin = $gvars['rMargin']; 20592 $this->lMargin = $gvars['lMargin']; 20593 $this->cell_padding = $gvars['cell_padding']; 20594 $this->cell_margin = $gvars['cell_margin']; 20595 $this->LineWidth = $gvars['LineWidth']; 20596 $this->linestyleWidth = $gvars['linestyleWidth']; 20597 $this->linestyleCap = $gvars['linestyleCap']; 20598 $this->linestyleJoin = $gvars['linestyleJoin']; 20599 $this->linestyleDash = $gvars['linestyleDash']; 20600 $this->textrendermode = $gvars['textrendermode']; 20601 $this->textstrokewidth = $gvars['textstrokewidth']; 20602 $this->DrawColor = $gvars['DrawColor']; 20603 $this->FillColor = $gvars['FillColor']; 20604 $this->TextColor = $gvars['TextColor']; 20605 $this->ColorFlag = $gvars['ColorFlag']; 20606 $this->bgcolor = $gvars['bgcolor']; 20607 $this->fgcolor = $gvars['fgcolor']; 20608 $this->htmlvspace = $gvars['htmlvspace']; 20609 $this->listindent = $gvars['listindent']; 20610 $this->listindentlevel = $gvars['listindentlevel']; 20611 $this->listnum = $gvars['listnum']; 20612 $this->listordered = $gvars['listordered']; 20613 $this->listcount = $gvars['listcount']; 20614 $this->lispacer = $gvars['lispacer']; 20615 $this->cell_height_ratio = $gvars['cell_height_ratio']; 20616 $this->font_stretching = $gvars['font_stretching']; 20617 $this->font_spacing = $gvars['font_spacing']; 20618 $this->alpha = $gvars['alpha']; 20619 if ($extended) { 20620 // restore extended values 20621 $this->lasth = $gvars['lasth']; 20622 $this->tMargin = $gvars['tMargin']; 20623 $this->bMargin = $gvars['bMargin']; 20624 $this->AutoPageBreak = $gvars['AutoPageBreak']; 20625 $this->PageBreakTrigger = $gvars['PageBreakTrigger']; 20626 $this->x = $gvars['x']; 20627 $this->y = $gvars['y']; 20628 $this->w = $gvars['w']; 20629 $this->h = $gvars['h']; 20630 $this->wPt = $gvars['wPt']; 20631 $this->hPt = $gvars['hPt']; 20632 $this->fwPt = $gvars['fwPt']; 20633 $this->fhPt = $gvars['fhPt']; 20634 $this->page = $gvars['page']; 20635 $this->current_column = $gvars['current_column']; 20636 $this->num_columns = $gvars['num_columns']; 20637 } 20638 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.''); 20639 if (!TCPDF_STATIC::empty_string($this->FontFamily)) { 20640 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 20641 } 20642 } 20643 20644 /** 20645 * Outputs the "save graphics state" operator 'q' 20646 * @protected 20647 */ 20648 protected function _outSaveGraphicsState() { 20649 $this->_out('q'); 20650 } 20651 20652 /** 20653 * Outputs the "restore graphics state" operator 'Q' 20654 * @protected 20655 */ 20656 protected function _outRestoreGraphicsState() { 20657 $this->_out('Q'); 20658 } 20659 20660 /** 20661 * Set buffer content (always append data). 20662 * @param $data (string) data 20663 * @protected 20664 * @since 4.5.000 (2009-01-02) 20665 */ 20666 protected function setBuffer($data) { 20667 $this->bufferlen += strlen($data); 20668 $this->buffer .= $data; 20669 } 20670 20671 /** 20672 * Replace the buffer content 20673 * @param $data (string) data 20674 * @protected 20675 * @since 5.5.000 (2010-06-22) 20676 */ 20677 protected function replaceBuffer($data) { 20678 $this->bufferlen = strlen($data); 20679 $this->buffer = $data; 20680 } 20681 20682 /** 20683 * Get buffer content. 20684 * @return string buffer content 20685 * @protected 20686 * @since 4.5.000 (2009-01-02) 20687 */ 20688 protected function getBuffer() { 20689 return $this->buffer; 20690 } 20691 20692 /** 20693 * Set page buffer content. 20694 * @param $page (int) page number 20695 * @param $data (string) page data 20696 * @param $append (boolean) if true append data, false replace. 20697 * @protected 20698 * @since 4.5.000 (2008-12-31) 20699 */ 20700 protected function setPageBuffer($page, $data, $append=false) { 20701 if ($append) { 20702 $this->pages[$page] .= $data; 20703 } else { 20704 $this->pages[$page] = $data; 20705 } 20706 if ($append AND isset($this->pagelen[$page])) { 20707 $this->pagelen[$page] += strlen($data); 20708 } else { 20709 $this->pagelen[$page] = strlen($data); 20710 } 20711 } 20712 20713 /** 20714 * Get page buffer content. 20715 * @param $page (int) page number 20716 * @return string page buffer content or false in case of error 20717 * @protected 20718 * @since 4.5.000 (2008-12-31) 20719 */ 20720 protected function getPageBuffer($page) { 20721 if (isset($this->pages[$page])) { 20722 return $this->pages[$page]; 20723 } 20724 return false; 20725 } 20726 20727 /** 20728 * Set image buffer content. 20729 * @param $image (string) image key 20730 * @param $data (array) image data 20731 * @return int image index number 20732 * @protected 20733 * @since 4.5.000 (2008-12-31) 20734 */ 20735 protected function setImageBuffer($image, $data) { 20736 if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) { 20737 $this->imagekeys[$this->numimages] = $image; 20738 $data['i'] = $this->numimages; 20739 ++$this->numimages; 20740 } 20741 $this->images[$image] = $data; 20742 return $data['i']; 20743 } 20744 20745 /** 20746 * Set image buffer content for a specified sub-key. 20747 * @param $image (string) image key 20748 * @param $key (string) image sub-key 20749 * @param $data (array) image data 20750 * @protected 20751 * @since 4.5.000 (2008-12-31) 20752 */ 20753 protected function setImageSubBuffer($image, $key, $data) { 20754 if (!isset($this->images[$image])) { 20755 $this->setImageBuffer($image, array()); 20756 } 20757 $this->images[$image][$key] = $data; 20758 } 20759 20760 /** 20761 * Get image buffer content. 20762 * @param $image (string) image key 20763 * @return string image buffer content or false in case of error 20764 * @protected 20765 * @since 4.5.000 (2008-12-31) 20766 */ 20767 protected function getImageBuffer($image) { 20768 if (isset($this->images[$image])) { 20769 return $this->images[$image]; 20770 } 20771 return false; 20772 } 20773 20774 /** 20775 * Set font buffer content. 20776 * @param $font (string) font key 20777 * @param $data (array) font data 20778 * @protected 20779 * @since 4.5.000 (2009-01-02) 20780 */ 20781 protected function setFontBuffer($font, $data) { 20782 $this->fonts[$font] = $data; 20783 if (!in_array($font, $this->fontkeys)) { 20784 $this->fontkeys[] = $font; 20785 // store object ID for current font 20786 ++$this->n; 20787 $this->font_obj_ids[$font] = $this->n; 20788 $this->setFontSubBuffer($font, 'n', $this->n); 20789 } 20790 } 20791 20792 /** 20793 * Set font buffer content. 20794 * @param $font (string) font key 20795 * @param $key (string) font sub-key 20796 * @param $data (array) font data 20797 * @protected 20798 * @since 4.5.000 (2009-01-02) 20799 */ 20800 protected function setFontSubBuffer($font, $key, $data) { 20801 if (!isset($this->fonts[$font])) { 20802 $this->setFontBuffer($font, array()); 20803 } 20804 $this->fonts[$font][$key] = $data; 20805 } 20806 20807 /** 20808 * Get font buffer content. 20809 * @param $font (string) font key 20810 * @return string font buffer content or false in case of error 20811 * @protected 20812 * @since 4.5.000 (2009-01-02) 20813 */ 20814 protected function getFontBuffer($font) { 20815 if (isset($this->fonts[$font])) { 20816 return $this->fonts[$font]; 20817 } 20818 return false; 20819 } 20820 20821 /** 20822 * Move a page to a previous position. 20823 * @param $frompage (int) number of the source page 20824 * @param $topage (int) number of the destination page (must be less than $frompage) 20825 * @return true in case of success, false in case of error. 20826 * @public 20827 * @since 4.5.000 (2009-01-02) 20828 */ 20829 public function movePage($frompage, $topage) { 20830 if (($frompage > $this->numpages) OR ($frompage <= $topage)) { 20831 return false; 20832 } 20833 if ($frompage == $this->page) { 20834 // close the page before moving it 20835 $this->endPage(); 20836 } 20837 // move all page-related states 20838 $tmppage = $this->getPageBuffer($frompage); 20839 $tmppagedim = $this->pagedim[$frompage]; 20840 $tmppagelen = $this->pagelen[$frompage]; 20841 $tmpintmrk = $this->intmrk[$frompage]; 20842 $tmpbordermrk = $this->bordermrk[$frompage]; 20843 $tmpcntmrk = $this->cntmrk[$frompage]; 20844 $tmppageobjects = $this->pageobjects[$frompage]; 20845 if (isset($this->footerpos[$frompage])) { 20846 $tmpfooterpos = $this->footerpos[$frompage]; 20847 } 20848 if (isset($this->footerlen[$frompage])) { 20849 $tmpfooterlen = $this->footerlen[$frompage]; 20850 } 20851 if (isset($this->transfmrk[$frompage])) { 20852 $tmptransfmrk = $this->transfmrk[$frompage]; 20853 } 20854 if (isset($this->PageAnnots[$frompage])) { 20855 $tmpannots = $this->PageAnnots[$frompage]; 20856 } 20857 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { 20858 for ($i = $frompage; $i > $topage; --$i) { 20859 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) { 20860 --$this->pagegroups[$this->newpagegroup[$i]]; 20861 break; 20862 } 20863 } 20864 for ($i = $topage; $i > 0; --$i) { 20865 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) { 20866 ++$this->pagegroups[$this->newpagegroup[$i]]; 20867 break; 20868 } 20869 } 20870 } 20871 for ($i = $frompage; $i > $topage; --$i) { 20872 $j = $i - 1; 20873 // shift pages down 20874 $this->setPageBuffer($i, $this->getPageBuffer($j)); 20875 $this->pagedim[$i] = $this->pagedim[$j]; 20876 $this->pagelen[$i] = $this->pagelen[$j]; 20877 $this->intmrk[$i] = $this->intmrk[$j]; 20878 $this->bordermrk[$i] = $this->bordermrk[$j]; 20879 $this->cntmrk[$i] = $this->cntmrk[$j]; 20880 $this->pageobjects[$i] = $this->pageobjects[$j]; 20881 if (isset($this->footerpos[$j])) { 20882 $this->footerpos[$i] = $this->footerpos[$j]; 20883 } elseif (isset($this->footerpos[$i])) { 20884 unset($this->footerpos[$i]); 20885 } 20886 if (isset($this->footerlen[$j])) { 20887 $this->footerlen[$i] = $this->footerlen[$j]; 20888 } elseif (isset($this->footerlen[$i])) { 20889 unset($this->footerlen[$i]); 20890 } 20891 if (isset($this->transfmrk[$j])) { 20892 $this->transfmrk[$i] = $this->transfmrk[$j]; 20893 } elseif (isset($this->transfmrk[$i])) { 20894 unset($this->transfmrk[$i]); 20895 } 20896 if (isset($this->PageAnnots[$j])) { 20897 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 20898 } elseif (isset($this->PageAnnots[$i])) { 20899 unset($this->PageAnnots[$i]); 20900 } 20901 if (isset($this->newpagegroup[$j])) { 20902 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 20903 unset($this->newpagegroup[$j]); 20904 } 20905 if ($this->currpagegroup == $j) { 20906 $this->currpagegroup = $i; 20907 } 20908 } 20909 $this->setPageBuffer($topage, $tmppage); 20910 $this->pagedim[$topage] = $tmppagedim; 20911 $this->pagelen[$topage] = $tmppagelen; 20912 $this->intmrk[$topage] = $tmpintmrk; 20913 $this->bordermrk[$topage] = $tmpbordermrk; 20914 $this->cntmrk[$topage] = $tmpcntmrk; 20915 $this->pageobjects[$topage] = $tmppageobjects; 20916 if (isset($tmpfooterpos)) { 20917 $this->footerpos[$topage] = $tmpfooterpos; 20918 } elseif (isset($this->footerpos[$topage])) { 20919 unset($this->footerpos[$topage]); 20920 } 20921 if (isset($tmpfooterlen)) { 20922 $this->footerlen[$topage] = $tmpfooterlen; 20923 } elseif (isset($this->footerlen[$topage])) { 20924 unset($this->footerlen[$topage]); 20925 } 20926 if (isset($tmptransfmrk)) { 20927 $this->transfmrk[$topage] = $tmptransfmrk; 20928 } elseif (isset($this->transfmrk[$topage])) { 20929 unset($this->transfmrk[$topage]); 20930 } 20931 if (isset($tmpannots)) { 20932 $this->PageAnnots[$topage] = $tmpannots; 20933 } elseif (isset($this->PageAnnots[$topage])) { 20934 unset($this->PageAnnots[$topage]); 20935 } 20936 // adjust outlines 20937 $tmpoutlines = $this->outlines; 20938 foreach ($tmpoutlines as $key => $outline) { 20939 if (!$outline['f']) { 20940 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) { 20941 $this->outlines[$key]['p'] = ($outline['p'] + 1); 20942 } elseif ($outline['p'] == $frompage) { 20943 $this->outlines[$key]['p'] = $topage; 20944 } 20945 } 20946 } 20947 // adjust dests 20948 $tmpdests = $this->dests; 20949 foreach ($tmpdests as $key => $dest) { 20950 if (!$dest['f']) { 20951 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) { 20952 $this->dests[$key]['p'] = ($dest['p'] + 1); 20953 } elseif ($dest['p'] == $frompage) { 20954 $this->dests[$key]['p'] = $topage; 20955 } 20956 } 20957 } 20958 // adjust links 20959 $tmplinks = $this->links; 20960 foreach ($tmplinks as $key => $link) { 20961 if (!$link['f']) { 20962 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) { 20963 $this->links[$key]['p'] = ($link['p'] + 1); 20964 } elseif ($link['p'] == $frompage) { 20965 $this->links[$key]['p'] = $topage; 20966 } 20967 } 20968 } 20969 // adjust javascript 20970 $jfrompage = $frompage; 20971 $jtopage = $topage; 20972 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) { 20973 foreach($pamatch[0] as $pk => $pmatch) { 20974 $pagenum = intval($pamatch[3][$pk]) + 1; 20975 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) { 20976 $newpage = ($pagenum + 1); 20977 } elseif ($pagenum == $jfrompage) { 20978 $newpage = $jtopage; 20979 } else { 20980 $newpage = $pagenum; 20981 } 20982 --$newpage; 20983 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage; 20984 $this->javascript = str_replace($pmatch, $newjs, $this->javascript); 20985 } 20986 unset($pamatch); 20987 } 20988 // return to last page 20989 $this->lastPage(true); 20990 return true; 20991 } 20992 20993 /** 20994 * Remove the specified page. 20995 * @param $page (int) page to remove 20996 * @return true in case of success, false in case of error. 20997 * @public 20998 * @since 4.6.004 (2009-04-23) 20999 */ 21000 public function deletePage($page) { 21001 if (($page < 1) OR ($page > $this->numpages)) { 21002 return false; 21003 } 21004 // delete current page 21005 unset($this->pages[$page]); 21006 unset($this->pagedim[$page]); 21007 unset($this->pagelen[$page]); 21008 unset($this->intmrk[$page]); 21009 unset($this->bordermrk[$page]); 21010 unset($this->cntmrk[$page]); 21011 foreach ($this->pageobjects[$page] as $oid) { 21012 if (isset($this->offsets[$oid])){ 21013 unset($this->offsets[$oid]); 21014 } 21015 } 21016 unset($this->pageobjects[$page]); 21017 if (isset($this->footerpos[$page])) { 21018 unset($this->footerpos[$page]); 21019 } 21020 if (isset($this->footerlen[$page])) { 21021 unset($this->footerlen[$page]); 21022 } 21023 if (isset($this->transfmrk[$page])) { 21024 unset($this->transfmrk[$page]); 21025 } 21026 if (isset($this->PageAnnots[$page])) { 21027 unset($this->PageAnnots[$page]); 21028 } 21029 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { 21030 for ($i = $page; $i > 0; --$i) { 21031 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) { 21032 --$this->pagegroups[$this->newpagegroup[$i]]; 21033 break; 21034 } 21035 } 21036 } 21037 if (isset($this->pageopen[$page])) { 21038 unset($this->pageopen[$page]); 21039 } 21040 if ($page < $this->numpages) { 21041 // update remaining pages 21042 for ($i = $page; $i < $this->numpages; ++$i) { 21043 $j = $i + 1; 21044 // shift pages 21045 $this->setPageBuffer($i, $this->getPageBuffer($j)); 21046 $this->pagedim[$i] = $this->pagedim[$j]; 21047 $this->pagelen[$i] = $this->pagelen[$j]; 21048 $this->intmrk[$i] = $this->intmrk[$j]; 21049 $this->bordermrk[$i] = $this->bordermrk[$j]; 21050 $this->cntmrk[$i] = $this->cntmrk[$j]; 21051 $this->pageobjects[$i] = $this->pageobjects[$j]; 21052 if (isset($this->footerpos[$j])) { 21053 $this->footerpos[$i] = $this->footerpos[$j]; 21054 } elseif (isset($this->footerpos[$i])) { 21055 unset($this->footerpos[$i]); 21056 } 21057 if (isset($this->footerlen[$j])) { 21058 $this->footerlen[$i] = $this->footerlen[$j]; 21059 } elseif (isset($this->footerlen[$i])) { 21060 unset($this->footerlen[$i]); 21061 } 21062 if (isset($this->transfmrk[$j])) { 21063 $this->transfmrk[$i] = $this->transfmrk[$j]; 21064 } elseif (isset($this->transfmrk[$i])) { 21065 unset($this->transfmrk[$i]); 21066 } 21067 if (isset($this->PageAnnots[$j])) { 21068 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 21069 } elseif (isset($this->PageAnnots[$i])) { 21070 unset($this->PageAnnots[$i]); 21071 } 21072 if (isset($this->newpagegroup[$j])) { 21073 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 21074 unset($this->newpagegroup[$j]); 21075 } 21076 if ($this->currpagegroup == $j) { 21077 $this->currpagegroup = $i; 21078 } 21079 if (isset($this->pageopen[$j])) { 21080 $this->pageopen[$i] = $this->pageopen[$j]; 21081 } elseif (isset($this->pageopen[$i])) { 21082 unset($this->pageopen[$i]); 21083 } 21084 } 21085 // remove last page 21086 unset($this->pages[$this->numpages]); 21087 unset($this->pagedim[$this->numpages]); 21088 unset($this->pagelen[$this->numpages]); 21089 unset($this->intmrk[$this->numpages]); 21090 unset($this->bordermrk[$this->numpages]); 21091 unset($this->cntmrk[$this->numpages]); 21092 foreach ($this->pageobjects[$this->numpages] as $oid) { 21093 if (isset($this->offsets[$oid])){ 21094 unset($this->offsets[$oid]); 21095 } 21096 } 21097 unset($this->pageobjects[$this->numpages]); 21098 if (isset($this->footerpos[$this->numpages])) { 21099 unset($this->footerpos[$this->numpages]); 21100 } 21101 if (isset($this->footerlen[$this->numpages])) { 21102 unset($this->footerlen[$this->numpages]); 21103 } 21104 if (isset($this->transfmrk[$this->numpages])) { 21105 unset($this->transfmrk[$this->numpages]); 21106 } 21107 if (isset($this->PageAnnots[$this->numpages])) { 21108 unset($this->PageAnnots[$this->numpages]); 21109 } 21110 if (isset($this->newpagegroup[$this->numpages])) { 21111 unset($this->newpagegroup[$this->numpages]); 21112 } 21113 if ($this->currpagegroup == $this->numpages) { 21114 $this->currpagegroup = ($this->numpages - 1); 21115 } 21116 if (isset($this->pagegroups[$this->numpages])) { 21117 unset($this->pagegroups[$this->numpages]); 21118 } 21119 if (isset($this->pageopen[$this->numpages])) { 21120 unset($this->pageopen[$this->numpages]); 21121 } 21122 } 21123 --$this->numpages; 21124 $this->page = $this->numpages; 21125 // adjust outlines 21126 $tmpoutlines = $this->outlines; 21127 foreach ($tmpoutlines as $key => $outline) { 21128 if (!$outline['f']) { 21129 if ($outline['p'] > $page) { 21130 $this->outlines[$key]['p'] = $outline['p'] - 1; 21131 } elseif ($outline['p'] == $page) { 21132 unset($this->outlines[$key]); 21133 } 21134 } 21135 } 21136 // adjust dests 21137 $tmpdests = $this->dests; 21138 foreach ($tmpdests as $key => $dest) { 21139 if (!$dest['f']) { 21140 if ($dest['p'] > $page) { 21141 $this->dests[$key]['p'] = $dest['p'] - 1; 21142 } elseif ($dest['p'] == $page) { 21143 unset($this->dests[$key]); 21144 } 21145 } 21146 } 21147 // adjust links 21148 $tmplinks = $this->links; 21149 foreach ($tmplinks as $key => $link) { 21150 if (!$link['f']) { 21151 if ($link['p'] > $page) { 21152 $this->links[$key]['p'] = $link['p'] - 1; 21153 } elseif ($link['p'] == $page) { 21154 unset($this->links[$key]); 21155 } 21156 } 21157 } 21158 // adjust javascript 21159 $jpage = $page; 21160 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) { 21161 foreach($pamatch[0] as $pk => $pmatch) { 21162 $pagenum = intval($pamatch[3][$pk]) + 1; 21163 if ($pagenum >= $jpage) { 21164 $newpage = ($pagenum - 1); 21165 } elseif ($pagenum == $jpage) { 21166 $newpage = 1; 21167 } else { 21168 $newpage = $pagenum; 21169 } 21170 --$newpage; 21171 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage; 21172 $this->javascript = str_replace($pmatch, $newjs, $this->javascript); 21173 } 21174 unset($pamatch); 21175 } 21176 // return to last page 21177 if ($this->numpages > 0) { 21178 $this->lastPage(true); 21179 } 21180 return true; 21181 } 21182 21183 /** 21184 * Clone the specified page to a new page. 21185 * @param $page (int) number of page to copy (0 = current page) 21186 * @return true in case of success, false in case of error. 21187 * @public 21188 * @since 4.9.015 (2010-04-20) 21189 */ 21190 public function copyPage($page=0) { 21191 if ($page == 0) { 21192 // default value 21193 $page = $this->page; 21194 } 21195 if (($page < 1) OR ($page > $this->numpages)) { 21196 return false; 21197 } 21198 // close the last page 21199 $this->endPage(); 21200 // copy all page-related states 21201 ++$this->numpages; 21202 $this->page = $this->numpages; 21203 $this->setPageBuffer($this->page, $this->getPageBuffer($page)); 21204 $this->pagedim[$this->page] = $this->pagedim[$page]; 21205 $this->pagelen[$this->page] = $this->pagelen[$page]; 21206 $this->intmrk[$this->page] = $this->intmrk[$page]; 21207 $this->bordermrk[$this->page] = $this->bordermrk[$page]; 21208 $this->cntmrk[$this->page] = $this->cntmrk[$page]; 21209 $this->pageobjects[$this->page] = $this->pageobjects[$page]; 21210 $this->pageopen[$this->page] = false; 21211 if (isset($this->footerpos[$page])) { 21212 $this->footerpos[$this->page] = $this->footerpos[$page]; 21213 } 21214 if (isset($this->footerlen[$page])) { 21215 $this->footerlen[$this->page] = $this->footerlen[$page]; 21216 } 21217 if (isset($this->transfmrk[$page])) { 21218 $this->transfmrk[$this->page] = $this->transfmrk[$page]; 21219 } 21220 if (isset($this->PageAnnots[$page])) { 21221 $this->PageAnnots[$this->page] = $this->PageAnnots[$page]; 21222 } 21223 if (isset($this->newpagegroup[$page])) { 21224 // start a new group 21225 $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1; 21226 $this->currpagegroup = $this->newpagegroup[$this->page]; 21227 $this->pagegroups[$this->currpagegroup] = 1; 21228 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { 21229 ++$this->pagegroups[$this->currpagegroup]; 21230 } 21231 // copy outlines 21232 $tmpoutlines = $this->outlines; 21233 foreach ($tmpoutlines as $key => $outline) { 21234 if ($outline['p'] == $page) { 21235 $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']); 21236 } 21237 } 21238 // copy links 21239 $tmplinks = $this->links; 21240 foreach ($tmplinks as $key => $link) { 21241 if ($link['p'] == $page) { 21242 $this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']); 21243 } 21244 } 21245 // return to last page 21246 $this->lastPage(true); 21247 return true; 21248 } 21249 21250 /** 21251 * Output a Table of Content Index (TOC). 21252 * This method must be called after all Bookmarks were set. 21253 * Before calling this method you have to open the page using the addTOCPage() method. 21254 * After calling this method you have to call endTOCPage() to close the TOC page. 21255 * You can override this method to achieve different styles. 21256 * @param $page (int) page number where this TOC should be inserted (leave empty for current page). 21257 * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment). 21258 * @param $filler (string) string used to fill the space between text and page number. 21259 * @param $toc_name (string) name to use for TOC bookmark. 21260 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic. 21261 * @param $color (array) RGB color array for bookmark title (values from 0 to 255). 21262 * @public 21263 * @author Nicola Asuni 21264 * @since 4.5.000 (2009-01-02) 21265 * @see addTOCPage(), endTOCPage(), addHTMLTOC() 21266 */ 21267 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) { 21268 $fontsize = $this->FontSizePt; 21269 $fontfamily = $this->FontFamily; 21270 $fontstyle = $this->FontStyle; 21271 $w = $this->w - $this->lMargin - $this->rMargin; 21272 $spacer = $this->GetStringWidth(chr(32)) * 4; 21273 $lmargin = $this->lMargin; 21274 $rmargin = $this->rMargin; 21275 $x_start = $this->GetX(); 21276 $page_first = $this->page; 21277 $current_page = $this->page; 21278 $page_fill_start = false; 21279 $page_fill_end = false; 21280 $current_column = $this->current_column; 21281 if (TCPDF_STATIC::empty_string($numbersfont)) { 21282 $numbersfont = $this->default_monospaced_font; 21283 } 21284 if (TCPDF_STATIC::empty_string($filler)) { 21285 $filler = ' '; 21286 } 21287 if (TCPDF_STATIC::empty_string($page)) { 21288 $gap = ' '; 21289 } else { 21290 $gap = ''; 21291 if ($page < 1) { 21292 $page = 1; 21293 } 21294 } 21295 $this->SetFont($numbersfont, $fontstyle, $fontsize); 21296 $numwidth = $this->GetStringWidth('00000'); 21297 $maxpage = 0; //used for pages on attached documents 21298 foreach ($this->outlines as $key => $outline) { 21299 // check for extra pages (used for attachments) 21300 if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) { 21301 $outline['p'] += ($this->page - $page_first); 21302 } 21303 if ($this->rtl) { 21304 $aligntext = 'R'; 21305 $alignnum = 'L'; 21306 } else { 21307 $aligntext = 'L'; 21308 $alignnum = 'R'; 21309 } 21310 if ($outline['l'] == 0) { 21311 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize); 21312 } else { 21313 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']); 21314 } 21315 $this->SetTextColorArray($outline['c']); 21316 // check for page break 21317 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize)); 21318 // set margins and X position 21319 if (($this->page == $current_page) AND ($this->current_column == $current_column)) { 21320 $this->lMargin = $lmargin; 21321 $this->rMargin = $rmargin; 21322 } else { 21323 if ($this->current_column != $current_column) { 21324 if ($this->rtl) { 21325 $x_start = $this->w - $this->columns[$this->current_column]['x']; 21326 } else { 21327 $x_start = $this->columns[$this->current_column]['x']; 21328 } 21329 } 21330 $lmargin = $this->lMargin; 21331 $rmargin = $this->rMargin; 21332 $current_page = $this->page; 21333 $current_column = $this->current_column; 21334 } 21335 $this->SetX($x_start); 21336 $indent = ($spacer * $outline['l']); 21337 if ($this->rtl) { 21338 $this->x -= $indent; 21339 $this->rMargin = $this->w - $this->x; 21340 } else { 21341 $this->x += $indent; 21342 $this->lMargin = $this->x; 21343 } 21344 $link = $this->AddLink(); 21345 $this->SetLink($link, $outline['y'], $outline['p']); 21346 // write the text 21347 if ($this->rtl) { 21348 $txt = ' '.$outline['t']; 21349 } else { 21350 $txt = $outline['t'].' '; 21351 } 21352 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, ''); 21353 if ($this->rtl) { 21354 $tw = $this->x - $this->lMargin; 21355 } else { 21356 $tw = $this->w - $this->rMargin - $this->x; 21357 } 21358 $this->SetFont($numbersfont, $fontstyle, $fontsize); 21359 if (TCPDF_STATIC::empty_string($page)) { 21360 $pagenum = $outline['p']; 21361 } else { 21362 // placemark to be replaced with the correct number 21363 $pagenum = '{#'.($outline['p']).'}'; 21364 if ($this->isUnicodeFont()) { 21365 $pagenum = '{'.$pagenum.'}'; 21366 } 21367 $maxpage = max($maxpage, $outline['p']); 21368 } 21369 $fw = ($tw - $this->GetStringWidth($pagenum.$filler)); 21370 $wfiller = $this->GetStringWidth($filler); 21371 if ($wfiller > 0) { 21372 $numfills = floor($fw / $wfiller); 21373 } else { 21374 $numfills = 0; 21375 } 21376 if ($numfills > 0) { 21377 $rowfill = str_repeat($filler, $numfills); 21378 } else { 21379 $rowfill = ''; 21380 } 21381 if ($this->rtl) { 21382 $pagenum = $pagenum.$gap.$rowfill; 21383 } else { 21384 $pagenum = $rowfill.$gap.$pagenum; 21385 } 21386 // write the number 21387 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0); 21388 } 21389 $page_last = $this->getPage(); 21390 $numpages = ($page_last - $page_first + 1); 21391 // account for booklet mode 21392 if ($this->booklet) { 21393 // check if a blank page is required before TOC 21394 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0)); 21395 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start))); 21396 if ($page_fill_start) { 21397 // add a page at the end (to be moved before TOC) 21398 $this->addPage(); 21399 ++$page_last; 21400 ++$numpages; 21401 } 21402 if ($page_fill_end) { 21403 // add a page at the end 21404 $this->addPage(); 21405 ++$page_last; 21406 ++$numpages; 21407 } 21408 } 21409 $maxpage = max($maxpage, $page_last); 21410 if (!TCPDF_STATIC::empty_string($page)) { 21411 for ($p = $page_first; $p <= $page_last; ++$p) { 21412 // get page data 21413 $temppage = $this->getPageBuffer($p); 21414 for ($n = 1; $n <= $maxpage; ++$n) { 21415 // update page numbers 21416 $a = '{#'.$n.'}'; 21417 // get page number aliases 21418 $pnalias = $this->getInternalPageNumberAliases($a); 21419 // calculate replacement number 21420 if (($n >= $page) AND ($n <= $this->numpages)) { 21421 $np = $n + $numpages; 21422 } else { 21423 $np = $n; 21424 } 21425 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1)); 21426 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont); 21427 // replace aliases with numbers 21428 foreach ($pnalias['u'] as $u) { 21429 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' ')))); 21430 if ($this->rtl) { 21431 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont); 21432 } else { 21433 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu; 21434 } 21435 $temppage = str_replace($u, $nr, $temppage); 21436 } 21437 foreach ($pnalias['a'] as $a) { 21438 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' ')))); 21439 if ($this->rtl) { 21440 $nr = $na.' '.$sfill; 21441 } else { 21442 $nr = $sfill.' '.$na; 21443 } 21444 $temppage = str_replace($a, $nr, $temppage); 21445 } 21446 } 21447 // save changes 21448 $this->setPageBuffer($p, $temppage); 21449 } 21450 // move pages 21451 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); 21452 if ($page_fill_start) { 21453 $this->movePage($page_last, $page_first); 21454 } 21455 for ($i = 0; $i < $numpages; ++$i) { 21456 $this->movePage($page_last, $page); 21457 } 21458 } 21459 } 21460 21461 /** 21462 * Output a Table Of Content Index (TOC) using HTML templates. 21463 * This method must be called after all Bookmarks were set. 21464 * Before calling this method you have to open the page using the addTOCPage() method. 21465 * After calling this method you have to call endTOCPage() to close the TOC page. 21466 * @param $page (int) page number where this TOC should be inserted (leave empty for current page). 21467 * @param $toc_name (string) name to use for TOC bookmark. 21468 * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number. 21469 * @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) 21470 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic. 21471 * @param $color (array) RGB color array for title (values from 0 to 255). 21472 * @public 21473 * @author Nicola Asuni 21474 * @since 5.0.001 (2010-05-06) 21475 * @see addTOCPage(), endTOCPage(), addTOC() 21476 */ 21477 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) { 21478 $filler = ' '; 21479 $prev_htmlLinkColorArray = $this->htmlLinkColorArray; 21480 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle; 21481 // set new style for link 21482 $this->htmlLinkColorArray = array(); 21483 $this->htmlLinkFontStyle = ''; 21484 $page_first = $this->getPage(); 21485 $page_fill_start = false; 21486 $page_fill_end = false; 21487 // get the font type used for numbers in each template 21488 $current_font = $this->FontFamily; 21489 foreach ($templates as $level => $html) { 21490 $dom = $this->getHtmlDomArray($html); 21491 foreach ($dom as $key => $value) { 21492 if ($value['value'] == '#TOC_PAGE_NUMBER#') { 21493 $this->SetFont($dom[($key - 1)]['fontname']); 21494 $templates['F'.$level] = $this->isUnicodeFont(); 21495 } 21496 } 21497 } 21498 $this->SetFont($current_font); 21499 $maxpage = 0; //used for pages on attached documents 21500 foreach ($this->outlines as $key => $outline) { 21501 // get HTML template 21502 $row = $templates[$outline['l']]; 21503 if (TCPDF_STATIC::empty_string($page)) { 21504 $pagenum = $outline['p']; 21505 } else { 21506 // placemark to be replaced with the correct number 21507 $pagenum = '{#'.($outline['p']).'}'; 21508 if ($templates['F'.$outline['l']]) { 21509 $pagenum = '{'.$pagenum.'}'; 21510 } 21511 $maxpage = max($maxpage, $outline['p']); 21512 } 21513 // replace templates with current values 21514 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row); 21515 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row); 21516 // add link to page 21517 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>'; 21518 // write bookmark entry 21519 $this->writeHTML($row, false, false, true, false, ''); 21520 } 21521 // restore link styles 21522 $this->htmlLinkColorArray = $prev_htmlLinkColorArray; 21523 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle; 21524 // move TOC page and replace numbers 21525 $page_last = $this->getPage(); 21526 $numpages = ($page_last - $page_first + 1); 21527 // account for booklet mode 21528 if ($this->booklet) { 21529 // check if a blank page is required before TOC 21530 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0)); 21531 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start))); 21532 if ($page_fill_start) { 21533 // add a page at the end (to be moved before TOC) 21534 $this->addPage(); 21535 ++$page_last; 21536 ++$numpages; 21537 } 21538 if ($page_fill_end) { 21539 // add a page at the end 21540 $this->addPage(); 21541 ++$page_last; 21542 ++$numpages; 21543 } 21544 } 21545 $maxpage = max($maxpage, $page_last); 21546 if (!TCPDF_STATIC::empty_string($page)) { 21547 for ($p = $page_first; $p <= $page_last; ++$p) { 21548 // get page data 21549 $temppage = $this->getPageBuffer($p); 21550 for ($n = 1; $n <= $maxpage; ++$n) { 21551 // update page numbers 21552 $a = '{#'.$n.'}'; 21553 // get page number aliases 21554 $pnalias = $this->getInternalPageNumberAliases($a); 21555 // calculate replacement number 21556 if ($n >= $page) { 21557 $np = $n + $numpages; 21558 } else { 21559 $np = $n; 21560 } 21561 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1)); 21562 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont); 21563 // replace aliases with numbers 21564 foreach ($pnalias['u'] as $u) { 21565 if ($correct_align) { 21566 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' '))); 21567 if ($this->rtl) { 21568 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont); 21569 } else { 21570 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu; 21571 } 21572 } else { 21573 $nr = $nu; 21574 } 21575 $temppage = str_replace($u, $nr, $temppage); 21576 } 21577 foreach ($pnalias['a'] as $a) { 21578 if ($correct_align) { 21579 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' '))); 21580 if ($this->rtl) { 21581 $nr = $na.' '.$sfill; 21582 } else { 21583 $nr = $sfill.' '.$na; 21584 } 21585 } else { 21586 $nr = $na; 21587 } 21588 $temppage = str_replace($a, $nr, $temppage); 21589 } 21590 } 21591 // save changes 21592 $this->setPageBuffer($p, $temppage); 21593 } 21594 // move pages 21595 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); 21596 if ($page_fill_start) { 21597 $this->movePage($page_last, $page_first); 21598 } 21599 for ($i = 0; $i < $numpages; ++$i) { 21600 $this->movePage($page_last, $page); 21601 } 21602 } 21603 } 21604 21605 /** 21606 * Stores a copy of the current TCPDF object used for undo operation. 21607 * @public 21608 * @since 4.5.029 (2009-03-19) 21609 */ 21610 public function startTransaction() { 21611 if (isset($this->objcopy)) { 21612 // remove previous copy 21613 $this->commitTransaction(); 21614 } 21615 // record current page number and Y position 21616 $this->start_transaction_page = $this->page; 21617 $this->start_transaction_y = $this->y; 21618 // clone current object 21619 $this->objcopy = TCPDF_STATIC::objclone($this); 21620 } 21621 21622 /** 21623 * Delete the copy of the current TCPDF object used for undo operation. 21624 * @public 21625 * @since 4.5.029 (2009-03-19) 21626 */ 21627 public function commitTransaction() { 21628 if (isset($this->objcopy)) { 21629 $this->objcopy->_destroy(true, true); 21630 unset($this->objcopy); 21631 } 21632 } 21633 21634 /** 21635 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction(). 21636 * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value. 21637 * @return TCPDF object. 21638 * @public 21639 * @since 4.5.029 (2009-03-19) 21640 */ 21641 public function rollbackTransaction($self=false) { 21642 if (isset($this->objcopy)) { 21643 $this->_destroy(true, true); 21644 if ($self) { 21645 $objvars = get_object_vars($this->objcopy); 21646 foreach ($objvars as $key => $value) { 21647 $this->$key = $value; 21648 } 21649 } 21650 return $this->objcopy; 21651 } 21652 return $this; 21653 } 21654 21655 // --- MULTI COLUMNS METHODS ----------------------- 21656 21657 /** 21658 * Set multiple columns of the same size 21659 * @param $numcols (int) number of columns (set to zero to disable columns mode) 21660 * @param $width (int) column width 21661 * @param $y (int) column starting Y position (leave empty for current Y position) 21662 * @public 21663 * @since 4.9.001 (2010-03-28) 21664 */ 21665 public function setEqualColumns($numcols=0, $width=0, $y='') { 21666 $this->columns = array(); 21667 if ($numcols < 2) { 21668 $numcols = 0; 21669 $this->columns = array(); 21670 } else { 21671 // maximum column width 21672 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols; 21673 if (($width == 0) OR ($width > $maxwidth)) { 21674 $width = $maxwidth; 21675 } 21676 if (TCPDF_STATIC::empty_string($y)) { 21677 $y = $this->y; 21678 } 21679 // space between columns 21680 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1)); 21681 // fill the columns array (with, space, starting Y position) 21682 for ($i = 0; $i < $numcols; ++$i) { 21683 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y); 21684 } 21685 } 21686 $this->num_columns = $numcols; 21687 $this->current_column = 0; 21688 $this->column_start_page = $this->page; 21689 $this->selectColumn(0); 21690 } 21691 21692 /** 21693 * Remove columns and reset page margins. 21694 * @public 21695 * @since 5.9.072 (2011-04-26) 21696 */ 21697 public function resetColumns() { 21698 $this->lMargin = $this->original_lMargin; 21699 $this->rMargin = $this->original_rMargin; 21700 $this->setEqualColumns(); 21701 } 21702 21703 /** 21704 * Set columns array. 21705 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position). 21706 * @param $columns (array) 21707 * @public 21708 * @since 4.9.001 (2010-03-28) 21709 */ 21710 public function setColumnsArray($columns) { 21711 $this->columns = $columns; 21712 $this->num_columns = count($columns); 21713 $this->current_column = 0; 21714 $this->column_start_page = $this->page; 21715 $this->selectColumn(0); 21716 } 21717 21718 /** 21719 * Set position at a given column 21720 * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column. 21721 * @public 21722 * @since 4.9.001 (2010-03-28) 21723 */ 21724 public function selectColumn($col='') { 21725 if (is_string($col)) { 21726 $col = $this->current_column; 21727 } elseif ($col >= $this->num_columns) { 21728 $col = 0; 21729 } 21730 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 21731 $enable_thead = false; 21732 if ($this->num_columns > 1) { 21733 if ($col != $this->current_column) { 21734 // move Y pointer at the top of the column 21735 if ($this->column_start_page == $this->page) { 21736 $this->y = $this->columns[$col]['y']; 21737 } else { 21738 $this->y = $this->tMargin; 21739 } 21740 // Avoid to write table headers more than once 21741 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) { 21742 $enable_thead = true; 21743 $this->maxselcol['page'] = $this->page; 21744 $this->maxselcol['column'] = $col; 21745 } 21746 } 21747 $xshift = $this->colxshift; 21748 // set X position of the current column by case 21749 $listindent = ($this->listindentlevel * $this->listindent); 21750 // calculate column X position 21751 $colpos = 0; 21752 for ($i = 0; $i < $col; ++$i) { 21753 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']); 21754 } 21755 if ($this->rtl) { 21756 $x = $this->w - $this->original_rMargin - $colpos; 21757 $this->rMargin = ($this->w - $x + $listindent); 21758 $this->lMargin = ($x - $this->columns[$col]['w']); 21759 $this->x = $x - $listindent; 21760 } else { 21761 $x = $this->original_lMargin + $colpos; 21762 $this->lMargin = ($x + $listindent); 21763 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']); 21764 $this->x = $x + $listindent; 21765 } 21766 $this->columns[$col]['x'] = $x; 21767 } 21768 $this->current_column = $col; 21769 // fix for HTML mode 21770 $this->newline = true; 21771 // print HTML table header (if any) 21772 if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) { 21773 if ($enable_thead) { 21774 // print table header 21775 $this->writeHTML($this->thead, false, false, false, false, ''); 21776 $this->y += $xshift['s']['V']; 21777 // store end of header position 21778 if (!isset($this->columns[$col]['th'])) { 21779 $this->columns[$col]['th'] = array(); 21780 } 21781 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y; 21782 $this->lasth = 0; 21783 } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) { 21784 $this->y = $this->columns[$col]['th']['\''.$this->page.'\'']; 21785 } 21786 } 21787 // account for an html table cell over multiple columns 21788 if ($this->rtl) { 21789 $this->rMargin += $xshift['x']; 21790 $this->x -= ($xshift['x'] + $xshift['p']['R']); 21791 } else { 21792 $this->lMargin += $xshift['x']; 21793 $this->x += $xshift['x'] + $xshift['p']['L']; 21794 } 21795 } 21796 21797 /** 21798 * Return the current column number 21799 * @return int current column number 21800 * @public 21801 * @since 5.5.011 (2010-07-08) 21802 */ 21803 public function getColumn() { 21804 return $this->current_column; 21805 } 21806 21807 /** 21808 * Return the current number of columns. 21809 * @return int number of columns 21810 * @public 21811 * @since 5.8.018 (2010-08-25) 21812 */ 21813 public function getNumberOfColumns() { 21814 return $this->num_columns; 21815 } 21816 21817 /** 21818 * Set Text rendering mode. 21819 * @param $stroke (int) outline size in user units (0 = disable). 21820 * @param $fill (boolean) if true fills the text (default). 21821 * @param $clip (boolean) if true activate clipping mode 21822 * @public 21823 * @since 4.9.008 (2009-04-02) 21824 */ 21825 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) { 21826 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode 21827 // convert text rendering parameters 21828 if ($stroke < 0) { 21829 $stroke = 0; 21830 } 21831 if ($fill === true) { 21832 if ($stroke > 0) { 21833 if ($clip === true) { 21834 // Fill, then stroke text and add to path for clipping 21835 $textrendermode = 6; 21836 } else { 21837 // Fill, then stroke text 21838 $textrendermode = 2; 21839 } 21840 $textstrokewidth = $stroke; 21841 } else { 21842 if ($clip === true) { 21843 // Fill text and add to path for clipping 21844 $textrendermode = 4; 21845 } else { 21846 // Fill text 21847 $textrendermode = 0; 21848 } 21849 } 21850 } else { 21851 if ($stroke > 0) { 21852 if ($clip === true) { 21853 // Stroke text and add to path for clipping 21854 $textrendermode = 5; 21855 } else { 21856 // Stroke text 21857 $textrendermode = 1; 21858 } 21859 $textstrokewidth = $stroke; 21860 } else { 21861 if ($clip === true) { 21862 // Add text to path for clipping 21863 $textrendermode = 7; 21864 } else { 21865 // Neither fill nor stroke text (invisible) 21866 $textrendermode = 3; 21867 } 21868 } 21869 } 21870 $this->textrendermode = $textrendermode; 21871 $this->textstrokewidth = $stroke; 21872 } 21873 21874 /** 21875 * Set parameters for drop shadow effect for text. 21876 * @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. 21877 * @since 5.9.174 (2012-07-25) 21878 * @public 21879 */ 21880 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) { 21881 if (isset($params['enabled'])) { 21882 $this->txtshadow['enabled'] = $params['enabled']?true:false; 21883 } else { 21884 $this->txtshadow['enabled'] = false; 21885 } 21886 if (isset($params['depth_w'])) { 21887 $this->txtshadow['depth_w'] = floatval($params['depth_w']); 21888 } else { 21889 $this->txtshadow['depth_w'] = 0; 21890 } 21891 if (isset($params['depth_h'])) { 21892 $this->txtshadow['depth_h'] = floatval($params['depth_h']); 21893 } else { 21894 $this->txtshadow['depth_h'] = 0; 21895 } 21896 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) { 21897 $this->txtshadow['color'] = $params['color']; 21898 } else { 21899 $this->txtshadow['color'] = $this->strokecolor; 21900 } 21901 if (isset($params['opacity'])) { 21902 $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity']))); 21903 } else { 21904 $this->txtshadow['opacity'] = 1; 21905 } 21906 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'))) { 21907 $this->txtshadow['blend_mode'] = $params['blend_mode']; 21908 } else { 21909 $this->txtshadow['blend_mode'] = 'Normal'; 21910 } 21911 if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) { 21912 $this->txtshadow['enabled'] = false; 21913 } 21914 } 21915 21916 /** 21917 * Return the text shadow parameters array. 21918 * @return Array of parameters. 21919 * @since 5.9.174 (2012-07-25) 21920 * @public 21921 */ 21922 public function getTextShadow() { 21923 return $this->txtshadow; 21924 } 21925 21926 /** 21927 * Returns an array of chars containing soft hyphens. 21928 * @param $word (array) array of chars 21929 * @param $patterns (array) Array of hypenation patterns. 21930 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algorithm. 21931 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens. 21932 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens. 21933 * @param $charmin (int) Minimum word length to apply the hyphenation algorithm. 21934 * @param $charmax (int) Maximum length of broken piece of word. 21935 * @return array text with soft hyphens 21936 * @author Nicola Asuni 21937 * @since 4.9.012 (2010-04-12) 21938 * @protected 21939 */ 21940 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 21941 $hyphenword = array(); // hyphens positions 21942 $numchars = count($word); 21943 if ($numchars <= $charmin) { 21944 return $word; 21945 } 21946 $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode); 21947 // some words will be returned as-is 21948 $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})(\]?)$/'; 21949 if (preg_match($pattern, $word_string) > 0) { 21950 // email 21951 return $word; 21952 } 21953 $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})(\]?)$/'; 21954 if (preg_match($pattern, $word_string) > 0) { 21955 // URL 21956 return $word; 21957 } 21958 if (isset($dictionary[$word_string])) { 21959 return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont); 21960 } 21961 // surround word with '_' characters 21962 $tmpword = array_merge(array(46), $word, array(46)); 21963 $tmpnumchars = $numchars + 2; 21964 $maxpos = $tmpnumchars - 1; 21965 for ($pos = 0; $pos < $maxpos; ++$pos) { 21966 $imax = min(($tmpnumchars - $pos), $charmax); 21967 for ($i = 1; $i <= $imax; ++$i) { 21968 $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode)); 21969 if (isset($patterns[$subword])) { 21970 $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont); 21971 $pattern_length = count($pattern); 21972 $digits = 1; 21973 for ($j = 0; $j < $pattern_length; ++$j) { 21974 // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid) 21975 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) { 21976 if ($j == 0) { 21977 $zero = $pos - 1; 21978 } else { 21979 $zero = $pos + $j - $digits; 21980 } 21981 // get hyphenation level 21982 $level = ($pattern[$j] - 48); 21983 // if two levels from two different patterns match at the same point, the higher one is selected. 21984 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) { 21985 $hyphenword[$zero] = $level; 21986 } 21987 ++$digits; 21988 } 21989 } 21990 } 21991 } 21992 } 21993 $inserted = 0; 21994 $maxpos = $numchars - $rightmin; 21995 for ($i = $leftmin; $i <= $maxpos; ++$i) { 21996 // only odd levels indicate allowed hyphenation points 21997 if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) { 21998 // 173 = soft hyphen character 21999 array_splice($word, $i + $inserted, 0, 173); 22000 ++$inserted; 22001 } 22002 } 22003 return $word; 22004 } 22005 22006 /** 22007 * Returns text with soft hyphens. 22008 * @param $text (string) text to process 22009 * @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/ 22010 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algorithm. 22011 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens. 22012 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens. 22013 * @param $charmin (int) Minimum word length to apply the hyphenation algorithm. 22014 * @param $charmax (int) Maximum length of broken piece of word. 22015 * @return array text with soft hyphens 22016 * @author Nicola Asuni 22017 * @since 4.9.012 (2010-04-12) 22018 * @public 22019 */ 22020 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 22021 $text = $this->unhtmlentities($text); 22022 $word = array(); // last word 22023 $txtarr = array(); // text to be returned 22024 $intag = false; // true if we are inside an HTML tag 22025 $skip = false; // true to skip hyphenation 22026 if (!is_array($patterns)) { 22027 $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns); 22028 } 22029 // get array of characters 22030 $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont); 22031 // for each char 22032 foreach ($unichars as $char) { 22033 if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') { 22034 // letter character 22035 $word[] = $char; 22036 } else { 22037 // other type of character 22038 if (!TCPDF_STATIC::empty_string($word)) { 22039 // hypenate the word 22040 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 22041 $word = array(); 22042 } 22043 $txtarr[] = $char; 22044 if (chr($char) == '<') { 22045 // we are inside an HTML tag 22046 $intag = true; 22047 } elseif ($intag AND (chr($char) == '>')) { 22048 // end of HTML tag 22049 $intag = false; 22050 // check for style tag 22051 $expected = array(115, 116, 121, 108, 101); // = 'style' 22052 $current = array_slice($txtarr, -6, 5); // last 5 chars 22053 $compare = array_diff($expected, $current); 22054 if (empty($compare)) { 22055 // check if it is a closing tag 22056 $expected = array(47); // = '/' 22057 $current = array_slice($txtarr, -7, 1); 22058 $compare = array_diff($expected, $current); 22059 if (empty($compare)) { 22060 // closing style tag 22061 $skip = false; 22062 } else { 22063 // opening style tag 22064 $skip = true; 22065 } 22066 } 22067 } 22068 } 22069 } 22070 if (!TCPDF_STATIC::empty_string($word)) { 22071 // hypenate the word 22072 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 22073 } 22074 // convert char array to string and return 22075 return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode); 22076 } 22077 22078 /** 22079 * Enable/disable rasterization of vector images using ImageMagick library. 22080 * @param $mode (boolean) if true enable rasterization, false otherwise. 22081 * @public 22082 * @since 5.0.000 (2010-04-27) 22083 */ 22084 public function setRasterizeVectorImages($mode) { 22085 $this->rasterize_vector_images = $mode; 22086 } 22087 22088 /** 22089 * Enable or disable default option for font subsetting. 22090 * @param $enable (boolean) if true enable font subsetting by default. 22091 * @author Nicola Asuni 22092 * @public 22093 * @since 5.3.002 (2010-06-07) 22094 */ 22095 public function setFontSubsetting($enable=true) { 22096 if ($this->pdfa_mode) { 22097 $this->font_subsetting = false; 22098 } else { 22099 $this->font_subsetting = $enable ? true : false; 22100 } 22101 } 22102 22103 /** 22104 * Return the default option for font subsetting. 22105 * @return boolean default font subsetting state. 22106 * @author Nicola Asuni 22107 * @public 22108 * @since 5.3.002 (2010-06-07) 22109 */ 22110 public function getFontSubsetting() { 22111 return $this->font_subsetting; 22112 } 22113 22114 /** 22115 * Left trim the input string 22116 * @param $str (string) string to trim 22117 * @param $replace (string) string that replace spaces. 22118 * @return left trimmed string 22119 * @author Nicola Asuni 22120 * @public 22121 * @since 5.8.000 (2010-08-11) 22122 */ 22123 public function stringLeftTrim($str, $replace='') { 22124 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str); 22125 } 22126 22127 /** 22128 * Right trim the input string 22129 * @param $str (string) string to trim 22130 * @param $replace (string) string that replace spaces. 22131 * @return right trimmed string 22132 * @author Nicola Asuni 22133 * @public 22134 * @since 5.8.000 (2010-08-11) 22135 */ 22136 public function stringRightTrim($str, $replace='') { 22137 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str); 22138 } 22139 22140 /** 22141 * Trim the input string 22142 * @param $str (string) string to trim 22143 * @param $replace (string) string that replace spaces. 22144 * @return trimmed string 22145 * @author Nicola Asuni 22146 * @public 22147 * @since 5.8.000 (2010-08-11) 22148 */ 22149 public function stringTrim($str, $replace='') { 22150 $str = $this->stringLeftTrim($str, $replace); 22151 $str = $this->stringRightTrim($str, $replace); 22152 return $str; 22153 } 22154 22155 /** 22156 * Return true if the current font is unicode type. 22157 * @return true for unicode font, false otherwise. 22158 * @author Nicola Asuni 22159 * @public 22160 * @since 5.8.002 (2010-08-14) 22161 */ 22162 public function isUnicodeFont() { 22163 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')); 22164 } 22165 22166 /** 22167 * Return normalized font name 22168 * @param $fontfamily (string) property string containing font family names 22169 * @return string normalized font name 22170 * @author Nicola Asuni 22171 * @public 22172 * @since 5.8.004 (2010-08-17) 22173 */ 22174 public function getFontFamilyName($fontfamily) { 22175 // remove spaces and symbols 22176 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily)); 22177 // extract all font names 22178 $fontslist = preg_split('/[,]/', $fontfamily); 22179 // find first valid font name 22180 foreach ($fontslist as $font) { 22181 // replace font variations 22182 $font = preg_replace('/regular$/', '', $font); 22183 $font = preg_replace('/italic$/', 'I', $font); 22184 $font = preg_replace('/oblique$/', 'I', $font); 22185 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font); 22186 // replace common family names and core fonts 22187 $pattern = array(); 22188 $replacement = array(); 22189 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/'; 22190 $replacement[] = 'times'; 22191 $pattern[] = '/^sansserif/'; 22192 $replacement[] = 'helvetica'; 22193 $pattern[] = '/^monospace/'; 22194 $replacement[] = 'courier'; 22195 $font = preg_replace($pattern, $replacement, $font); 22196 if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) { 22197 return $font; 22198 } 22199 } 22200 // return current font as default 22201 return $this->CurrentFont['fontkey']; 22202 } 22203 22204 /** 22205 * Start a new XObject Template. 22206 * 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). 22207 * 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. 22208 * Note: X,Y coordinates will be reset to 0,0. 22209 * @param $w (int) Template width in user units (empty string or zero = page width less margins). 22210 * @param $h (int) Template height in user units (empty string or zero = page height less margins). 22211 * @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). 22212 * @return int the XObject Template ID in case of success or false in case of error. 22213 * @author Nicola Asuni 22214 * @public 22215 * @since 5.8.017 (2010-08-24) 22216 * @see endTemplate(), printTemplate() 22217 */ 22218 public function startTemplate($w=0, $h=0, $group=false) { 22219 if ($this->inxobj) { 22220 // we are already inside an XObject template 22221 return false; 22222 } 22223 $this->inxobj = true; 22224 ++$this->n; 22225 // XObject ID 22226 $this->xobjid = 'XT'.$this->n; 22227 // object ID 22228 $this->xobjects[$this->xobjid] = array('n' => $this->n); 22229 // store current graphic state 22230 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars(); 22231 // initialize data 22232 $this->xobjects[$this->xobjid]['intmrk'] = 0; 22233 $this->xobjects[$this->xobjid]['transfmrk'] = array(); 22234 $this->xobjects[$this->xobjid]['outdata'] = ''; 22235 $this->xobjects[$this->xobjid]['xobjects'] = array(); 22236 $this->xobjects[$this->xobjid]['images'] = array(); 22237 $this->xobjects[$this->xobjid]['fonts'] = array(); 22238 $this->xobjects[$this->xobjid]['annotations'] = array(); 22239 $this->xobjects[$this->xobjid]['extgstates'] = array(); 22240 $this->xobjects[$this->xobjid]['gradients'] = array(); 22241 $this->xobjects[$this->xobjid]['spot_colors'] = array(); 22242 // set new environment 22243 $this->num_columns = 1; 22244 $this->current_column = 0; 22245 $this->SetAutoPageBreak(false); 22246 if (($w === '') OR ($w <= 0)) { 22247 $w = $this->w - $this->lMargin - $this->rMargin; 22248 } 22249 if (($h === '') OR ($h <= 0)) { 22250 $h = $this->h - $this->tMargin - $this->bMargin; 22251 } 22252 $this->xobjects[$this->xobjid]['x'] = 0; 22253 $this->xobjects[$this->xobjid]['y'] = 0; 22254 $this->xobjects[$this->xobjid]['w'] = $w; 22255 $this->xobjects[$this->xobjid]['h'] = $h; 22256 $this->w = $w; 22257 $this->h = $h; 22258 $this->wPt = $this->w * $this->k; 22259 $this->hPt = $this->h * $this->k; 22260 $this->fwPt = $this->wPt; 22261 $this->fhPt = $this->hPt; 22262 $this->x = 0; 22263 $this->y = 0; 22264 $this->lMargin = 0; 22265 $this->rMargin = 0; 22266 $this->tMargin = 0; 22267 $this->bMargin = 0; 22268 // set group mode 22269 $this->xobjects[$this->xobjid]['group'] = $group; 22270 return $this->xobjid; 22271 } 22272 22273 /** 22274 * End the current XObject Template started with startTemplate() and restore the previous graphic state. 22275 * 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). 22276 * 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. 22277 * @return int the XObject Template ID in case of success or false in case of error. 22278 * @author Nicola Asuni 22279 * @public 22280 * @since 5.8.017 (2010-08-24) 22281 * @see startTemplate(), printTemplate() 22282 */ 22283 public function endTemplate() { 22284 if (!$this->inxobj) { 22285 // we are not inside a template 22286 return false; 22287 } 22288 $this->inxobj = false; 22289 // restore previous graphic state 22290 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true); 22291 return $this->xobjid; 22292 } 22293 22294 /** 22295 * Print an XObject Template. 22296 * You can print an XObject Template inside the currently opened Template. 22297 * 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). 22298 * 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. 22299 * @param $id (string) The ID of XObject Template to print. 22300 * @param $x (int) X position in user units (empty string = current x position) 22301 * @param $y (int) Y position in user units (empty string = current y position) 22302 * @param $w (int) Width in user units (zero = remaining page width) 22303 * @param $h (int) Height in user units (zero = remaining page height) 22304 * @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> 22305 * @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> 22306 * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions. 22307 * @author Nicola Asuni 22308 * @public 22309 * @since 5.8.017 (2010-08-24) 22310 * @see startTemplate(), endTemplate() 22311 */ 22312 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) { 22313 if ($this->state != 2) { 22314 return; 22315 } 22316 if (!isset($this->xobjects[$id])) { 22317 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!'); 22318 } 22319 if ($this->inxobj) { 22320 if ($id == $this->xobjid) { 22321 // close current template 22322 $this->endTemplate(); 22323 } else { 22324 // use the template as resource for the template currently opened 22325 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id]; 22326 } 22327 } 22328 // set default values 22329 if ($x === '') { 22330 $x = $this->x; 22331 } 22332 if ($y === '') { 22333 $y = $this->y; 22334 } 22335 // check page for no-write regions and adapt page margins if necessary 22336 list($x, $y) = $this->checkPageRegions($h, $x, $y); 22337 $ow = $this->xobjects[$id]['w']; 22338 if ($ow <= 0) { 22339 $ow = 1; 22340 } 22341 $oh = $this->xobjects[$id]['h']; 22342 if ($oh <= 0) { 22343 $oh = 1; 22344 } 22345 // calculate template width and height on document 22346 if (($w <= 0) AND ($h <= 0)) { 22347 $w = $ow; 22348 $h = $oh; 22349 } elseif ($w <= 0) { 22350 $w = $h * $ow / $oh; 22351 } elseif ($h <= 0) { 22352 $h = $w * $oh / $ow; 22353 } 22354 // fit the template on available space 22355 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 22356 // set page alignment 22357 $rb_y = $y + $h; 22358 // set alignment 22359 if ($this->rtl) { 22360 if ($palign == 'L') { 22361 $xt = $this->lMargin; 22362 } elseif ($palign == 'C') { 22363 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22364 } elseif ($palign == 'R') { 22365 $xt = $this->w - $this->rMargin - $w; 22366 } else { 22367 $xt = $x - $w; 22368 } 22369 $rb_x = $xt; 22370 } else { 22371 if ($palign == 'L') { 22372 $xt = $this->lMargin; 22373 } elseif ($palign == 'C') { 22374 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22375 } elseif ($palign == 'R') { 22376 $xt = $this->w - $this->rMargin - $w; 22377 } else { 22378 $xt = $x; 22379 } 22380 $rb_x = $xt + $w; 22381 } 22382 // print XObject Template + Transformation matrix 22383 $this->StartTransform(); 22384 // translate and scale 22385 $sx = ($w / $ow); 22386 $sy = ($h / $oh); 22387 $tm = array(); 22388 $tm[0] = $sx; 22389 $tm[1] = 0; 22390 $tm[2] = 0; 22391 $tm[3] = $sy; 22392 $tm[4] = $xt * $this->k; 22393 $tm[5] = ($this->h - $h - $y) * $this->k; 22394 $this->Transform($tm); 22395 // set object 22396 $this->_out('/'.$id.' Do'); 22397 $this->StopTransform(); 22398 // add annotations 22399 if (!empty($this->xobjects[$id]['annotations'])) { 22400 foreach ($this->xobjects[$id]['annotations'] as $annot) { 22401 // transform original coordinates 22402 $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k))); 22403 $ax = ($coordlt[4] / $this->k); 22404 $ay = ($this->h - $h - ($coordlt[5] / $this->k)); 22405 $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k))); 22406 $aw = ($coordrb[4] / $this->k) - $ax; 22407 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay; 22408 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']); 22409 } 22410 } 22411 // set pointer to align the next text/objects 22412 switch($align) { 22413 case 'T': { 22414 $this->y = $y; 22415 $this->x = $rb_x; 22416 break; 22417 } 22418 case 'M': { 22419 $this->y = $y + round($h/2); 22420 $this->x = $rb_x; 22421 break; 22422 } 22423 case 'B': { 22424 $this->y = $rb_y; 22425 $this->x = $rb_x; 22426 break; 22427 } 22428 case 'N': { 22429 $this->SetY($rb_y); 22430 break; 22431 } 22432 default:{ 22433 break; 22434 } 22435 } 22436 } 22437 22438 /** 22439 * Set the percentage of character stretching. 22440 * @param $perc (int) percentage of stretching (100 = no stretching) 22441 * @author Nicola Asuni 22442 * @public 22443 * @since 5.9.000 (2010-09-29) 22444 */ 22445 public function setFontStretching($perc=100) { 22446 $this->font_stretching = $perc; 22447 } 22448 22449 /** 22450 * Get the percentage of character stretching. 22451 * @return float stretching value 22452 * @author Nicola Asuni 22453 * @public 22454 * @since 5.9.000 (2010-09-29) 22455 */ 22456 public function getFontStretching() { 22457 return $this->font_stretching; 22458 } 22459 22460 /** 22461 * Set the amount to increase or decrease the space between characters in a text. 22462 * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing) 22463 * @author Nicola Asuni 22464 * @public 22465 * @since 5.9.000 (2010-09-29) 22466 */ 22467 public function setFontSpacing($spacing=0) { 22468 $this->font_spacing = $spacing; 22469 } 22470 22471 /** 22472 * Get the amount to increase or decrease the space between characters in a text. 22473 * @return int font spacing (tracking) value 22474 * @author Nicola Asuni 22475 * @public 22476 * @since 5.9.000 (2010-09-29) 22477 */ 22478 public function getFontSpacing() { 22479 return $this->font_spacing; 22480 } 22481 22482 /** 22483 * Return an array of no-write page regions 22484 * @return array of no-write page regions 22485 * @author Nicola Asuni 22486 * @public 22487 * @since 5.9.003 (2010-10-13) 22488 * @see setPageRegions(), addPageRegion() 22489 */ 22490 public function getPageRegions() { 22491 return $this->page_regions; 22492 } 22493 22494 /** 22495 * Set no-write regions on page. 22496 * 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. 22497 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22498 * You can set multiple regions for the same page. 22499 * @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. 22500 * @author Nicola Asuni 22501 * @public 22502 * @since 5.9.003 (2010-10-13) 22503 * @see addPageRegion(), getPageRegions() 22504 */ 22505 public function setPageRegions($regions=array()) { 22506 // empty current regions array 22507 $this->page_regions = array(); 22508 // add regions 22509 foreach ($regions as $data) { 22510 $this->addPageRegion($data); 22511 } 22512 } 22513 22514 /** 22515 * Add a single no-write region on selected page. 22516 * 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. 22517 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22518 * You can set multiple regions for the same page. 22519 * @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). 22520 * @author Nicola Asuni 22521 * @public 22522 * @since 5.9.003 (2010-10-13) 22523 * @see setPageRegions(), getPageRegions() 22524 */ 22525 public function addPageRegion($region) { 22526 if (!isset($region['page']) OR empty($region['page'])) { 22527 $region['page'] = $this->page; 22528 } 22529 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0) 22530 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb']) 22531 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) { 22532 $this->page_regions[] = $region; 22533 } 22534 } 22535 22536 /** 22537 * Remove a single no-write region. 22538 * @param $key (int) region key 22539 * @author Nicola Asuni 22540 * @public 22541 * @since 5.9.003 (2010-10-13) 22542 * @see setPageRegions(), getPageRegions() 22543 */ 22544 public function removePageRegion($key) { 22545 if (isset($this->page_regions[$key])) { 22546 unset($this->page_regions[$key]); 22547 } 22548 } 22549 22550 /** 22551 * Check page for no-write regions and adapt current coordinates and page margins if necessary. 22552 * 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. 22553 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22554 * @param $h (float) height of the text/image/object to print in user units 22555 * @param $x (float) current X coordinate in user units 22556 * @param $y (float) current Y coordinate in user units 22557 * @return array($x, $y) 22558 * @author Nicola Asuni 22559 * @protected 22560 * @since 5.9.003 (2010-10-13) 22561 */ 22562 protected function checkPageRegions($h, $x, $y) { 22563 // set default values 22564 if ($x === '') { 22565 $x = $this->x; 22566 } 22567 if ($y === '') { 22568 $y = $this->y; 22569 } 22570 if (!$this->check_page_regions OR empty($this->page_regions)) { 22571 // no page regions defined 22572 return array($x, $y); 22573 } 22574 if (empty($h)) { 22575 $h = $this->getCellHeight($this->FontSize); 22576 } 22577 // check for page break 22578 if ($this->checkPageBreak($h, $y)) { 22579 // the content will be printed on a new page 22580 $x = $this->x; 22581 $y = $this->y; 22582 } 22583 if ($this->num_columns > 1) { 22584 if ($this->rtl) { 22585 $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']); 22586 } else { 22587 $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']); 22588 } 22589 } else { 22590 if ($this->rtl) { 22591 $this->lMargin = max($this->clMargin, $this->original_lMargin); 22592 } else { 22593 $this->rMargin = max($this->crMargin, $this->original_rMargin); 22594 } 22595 } 22596 // adjust coordinates and page margins 22597 foreach ($this->page_regions as $regid => $regdata) { 22598 if ($regdata['page'] == $this->page) { 22599 // check region boundaries 22600 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) { 22601 // Y is inside the region 22602 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient 22603 $yt = max($y, $regdata['yt']); 22604 $yb = min(($yt + $h), $regdata['yb']); 22605 $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt']; 22606 $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt']; 22607 if ($regdata['side'] == 'L') { // left side 22608 $new_margin = max($xt, $xb); 22609 if ($this->lMargin < $new_margin) { 22610 if ($this->rtl) { 22611 // adjust left page margin 22612 $this->lMargin = max(0, $new_margin); 22613 } 22614 if ($x < $new_margin) { 22615 // adjust x position 22616 $x = $new_margin; 22617 if ($new_margin > ($this->w - $this->rMargin)) { 22618 // adjust y position 22619 $y = $regdata['yb'] - $h; 22620 } 22621 } 22622 } 22623 } elseif ($regdata['side'] == 'R') { // right side 22624 $new_margin = min($xt, $xb); 22625 if (($this->w - $this->rMargin) > $new_margin) { 22626 if (!$this->rtl) { 22627 // adjust right page margin 22628 $this->rMargin = max(0, ($this->w - $new_margin)); 22629 } 22630 if ($x > $new_margin) { 22631 // adjust x position 22632 $x = $new_margin; 22633 if ($new_margin > $this->lMargin) { 22634 // adjust y position 22635 $y = $regdata['yb'] - $h; 22636 } 22637 } 22638 } 22639 } 22640 } 22641 } 22642 } 22643 return array($x, $y); 22644 } 22645 22646 // --- SVG METHODS --------------------------------------------------------- 22647 22648 /** 22649 * Embedd a Scalable Vector Graphics (SVG) image. 22650 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library. 22651 * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string. 22652 * @param $x (float) Abscissa of the upper-left corner. 22653 * @param $y (float) Ordinate of the upper-left corner. 22654 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 22655 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 22656 * @param $link (mixed) URL or identifier returned by AddLink(). 22657 * @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. 22658 * @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> 22659 * @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))) 22660 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions. 22661 * @author Nicola Asuni 22662 * @since 5.0.000 (2010-05-02) 22663 * @public 22664 */ 22665 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) { 22666 if ($this->state != 2) { 22667 return; 22668 } 22669 // reset SVG vars 22670 $this->svggradients = array(); 22671 $this->svggradientid = 0; 22672 $this->svgdefsmode = false; 22673 $this->svgdefs = array(); 22674 $this->svgclipmode = false; 22675 $this->svgclippaths = array(); 22676 $this->svgcliptm = array(); 22677 $this->svgclipid = 0; 22678 $this->svgtext = ''; 22679 $this->svgtextmode = array(); 22680 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { 22681 // convert SVG to raster image using GD or ImageMagick libraries 22682 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 22683 } 22684 if ($file[0] === '@') { // image from string 22685 $this->svgdir = ''; 22686 $svgdata = substr($file, 1); 22687 } else { // SVG file 22688 $this->svgdir = dirname($file); 22689 $svgdata = TCPDF_STATIC::fileGetContents($file); 22690 } 22691 if ($svgdata === FALSE) { 22692 $this->Error('SVG file not found: '.$file); 22693 } 22694 if ($x === '') { 22695 $x = $this->x; 22696 } 22697 if ($y === '') { 22698 $y = $this->y; 22699 } 22700 // check page for no-write regions and adapt page margins if necessary 22701 list($x, $y) = $this->checkPageRegions($h, $x, $y); 22702 $k = $this->k; 22703 $ox = 0; 22704 $oy = 0; 22705 $ow = $w; 22706 $oh = $h; 22707 $aspect_ratio_align = 'xMidYMid'; 22708 $aspect_ratio_ms = 'meet'; 22709 $regs = array(); 22710 // get original image width and height 22711 preg_match('/<svg([^\>]*)>/si', $svgdata, $regs); 22712 if (isset($regs[1]) AND !empty($regs[1])) { 22713 $tmp = array(); 22714 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22715 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 22716 } 22717 $tmp = array(); 22718 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22719 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 22720 } 22721 $tmp = array(); 22722 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22723 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 22724 } 22725 $tmp = array(); 22726 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22727 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 22728 } 22729 $tmp = array(); 22730 $view_box = array(); 22731 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) { 22732 if (count($tmp) == 5) { 22733 array_shift($tmp); 22734 foreach ($tmp as $key => $val) { 22735 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 22736 } 22737 $ox = $view_box[0]; 22738 $oy = $view_box[1]; 22739 } 22740 // get aspect ratio 22741 $tmp = array(); 22742 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22743 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]); 22744 switch (count($aspect_ratio)) { 22745 case 3: { 22746 $aspect_ratio_align = $aspect_ratio[1]; 22747 $aspect_ratio_ms = $aspect_ratio[2]; 22748 break; 22749 } 22750 case 2: { 22751 $aspect_ratio_align = $aspect_ratio[0]; 22752 $aspect_ratio_ms = $aspect_ratio[1]; 22753 break; 22754 } 22755 case 1: { 22756 $aspect_ratio_align = $aspect_ratio[0]; 22757 $aspect_ratio_ms = 'meet'; 22758 break; 22759 } 22760 } 22761 } 22762 } 22763 } 22764 if ($ow <= 0) { 22765 $ow = 1; 22766 } 22767 if ($oh <= 0) { 22768 $oh = 1; 22769 } 22770 // calculate image width and height on document 22771 if (($w <= 0) AND ($h <= 0)) { 22772 // convert image size to document unit 22773 $w = $ow; 22774 $h = $oh; 22775 } elseif ($w <= 0) { 22776 $w = $h * $ow / $oh; 22777 } elseif ($h <= 0) { 22778 $h = $w * $oh / $ow; 22779 } 22780 // fit the image on available space 22781 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 22782 if ($this->rasterize_vector_images) { 22783 // convert SVG to raster image using GD or ImageMagick libraries 22784 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 22785 } 22786 // set alignment 22787 $this->img_rb_y = $y + $h; 22788 // set alignment 22789 if ($this->rtl) { 22790 if ($palign == 'L') { 22791 $ximg = $this->lMargin; 22792 } elseif ($palign == 'C') { 22793 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22794 } elseif ($palign == 'R') { 22795 $ximg = $this->w - $this->rMargin - $w; 22796 } else { 22797 $ximg = $x - $w; 22798 } 22799 $this->img_rb_x = $ximg; 22800 } else { 22801 if ($palign == 'L') { 22802 $ximg = $this->lMargin; 22803 } elseif ($palign == 'C') { 22804 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22805 } elseif ($palign == 'R') { 22806 $ximg = $this->w - $this->rMargin - $w; 22807 } else { 22808 $ximg = $x; 22809 } 22810 $this->img_rb_x = $ximg + $w; 22811 } 22812 // store current graphic vars 22813 $gvars = $this->getGraphicVars(); 22814 // store SVG position and scale factors 22815 $svgoffset_x = ($ximg - $ox) * $this->k; 22816 $svgoffset_y = -($y - $oy) * $this->k; 22817 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) { 22818 $ow = $view_box[2]; 22819 $oh = $view_box[3]; 22820 } else { 22821 if ($ow <= 0) { 22822 $ow = $w; 22823 } 22824 if ($oh <= 0) { 22825 $oh = $h; 22826 } 22827 } 22828 $svgscale_x = $w / $ow; 22829 $svgscale_y = $h / $oh; 22830 // scaling and alignment 22831 if ($aspect_ratio_align != 'none') { 22832 // store current scaling values 22833 $svgscale_old_x = $svgscale_x; 22834 $svgscale_old_y = $svgscale_y; 22835 // force uniform scaling 22836 if ($aspect_ratio_ms == 'slice') { 22837 // the entire viewport is covered by the viewBox 22838 if ($svgscale_x > $svgscale_y) { 22839 $svgscale_y = $svgscale_x; 22840 } elseif ($svgscale_x < $svgscale_y) { 22841 $svgscale_x = $svgscale_y; 22842 } 22843 } else { // meet 22844 // the entire viewBox is visible within the viewport 22845 if ($svgscale_x < $svgscale_y) { 22846 $svgscale_y = $svgscale_x; 22847 } elseif ($svgscale_x > $svgscale_y) { 22848 $svgscale_x = $svgscale_y; 22849 } 22850 } 22851 // correct X alignment 22852 switch (substr($aspect_ratio_align, 1, 3)) { 22853 case 'Min': { 22854 // do nothing 22855 break; 22856 } 22857 case 'Max': { 22858 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x)); 22859 break; 22860 } 22861 default: 22862 case 'Mid': { 22863 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2); 22864 break; 22865 } 22866 } 22867 // correct Y alignment 22868 switch (substr($aspect_ratio_align, 5)) { 22869 case 'Min': { 22870 // do nothing 22871 break; 22872 } 22873 case 'Max': { 22874 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y)); 22875 break; 22876 } 22877 default: 22878 case 'Mid': { 22879 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2); 22880 break; 22881 } 22882 } 22883 } 22884 // store current page break mode 22885 $page_break_mode = $this->AutoPageBreak; 22886 $page_break_margin = $this->getBreakMargin(); 22887 $cell_padding = $this->cell_padding; 22888 $this->SetCellPadding(0); 22889 $this->SetAutoPageBreak(false); 22890 // save the current graphic state 22891 $this->_out('q'.$this->epsmarker); 22892 // set initial clipping mask 22893 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array()); 22894 // scale and translate 22895 $e = $ox * $this->k * (1 - $svgscale_x); 22896 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y); 22897 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y))); 22898 // creates a new XML parser to be used by the other XML functions 22899 $this->parser = xml_parser_create('UTF-8'); 22900 // the following function allows to use parser inside object 22901 xml_set_object($this->parser, $this); 22902 // disable case-folding for this XML parser 22903 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 22904 // sets the element handler functions for the XML parser 22905 xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler'); 22906 // sets the character data handler function for the XML parser 22907 xml_set_character_data_handler($this->parser, 'segSVGContentHandler'); 22908 // start parsing an XML document 22909 if (!xml_parse($this->parser, $svgdata)) { 22910 $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)); 22911 $this->Error($error_message); 22912 } 22913 // free this XML parser 22914 xml_parser_free($this->parser); 22915 // restore previous graphic state 22916 $this->_out($this->epsmarker.'Q'); 22917 // restore graphic vars 22918 $this->setGraphicVars($gvars); 22919 $this->lasth = $gvars['lasth']; 22920 if (!empty($border)) { 22921 $bx = $this->x; 22922 $by = $this->y; 22923 $this->x = $ximg; 22924 if ($this->rtl) { 22925 $this->x += $w; 22926 } 22927 $this->y = $y; 22928 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 22929 $this->x = $bx; 22930 $this->y = $by; 22931 } 22932 if ($link) { 22933 $this->Link($ximg, $y, $w, $h, $link, 0); 22934 } 22935 // set pointer to align the next text/objects 22936 switch($align) { 22937 case 'T':{ 22938 $this->y = $y; 22939 $this->x = $this->img_rb_x; 22940 break; 22941 } 22942 case 'M':{ 22943 $this->y = $y + round($h/2); 22944 $this->x = $this->img_rb_x; 22945 break; 22946 } 22947 case 'B':{ 22948 $this->y = $this->img_rb_y; 22949 $this->x = $this->img_rb_x; 22950 break; 22951 } 22952 case 'N':{ 22953 $this->SetY($this->img_rb_y); 22954 break; 22955 } 22956 default:{ 22957 // restore pointer to starting position 22958 $this->x = $gvars['x']; 22959 $this->y = $gvars['y']; 22960 $this->page = $gvars['page']; 22961 $this->current_column = $gvars['current_column']; 22962 $this->tMargin = $gvars['tMargin']; 22963 $this->bMargin = $gvars['bMargin']; 22964 $this->w = $gvars['w']; 22965 $this->h = $gvars['h']; 22966 $this->wPt = $gvars['wPt']; 22967 $this->hPt = $gvars['hPt']; 22968 $this->fwPt = $gvars['fwPt']; 22969 $this->fhPt = $gvars['fhPt']; 22970 break; 22971 } 22972 } 22973 $this->endlinex = $this->img_rb_x; 22974 // restore page break 22975 $this->SetAutoPageBreak($page_break_mode, $page_break_margin); 22976 $this->cell_padding = $cell_padding; 22977 } 22978 22979 /** 22980 * Convert SVG transformation matrix to PDF. 22981 * @param $tm (array) original SVG transformation matrix 22982 * @return array transformation matrix 22983 * @protected 22984 * @since 5.0.000 (2010-05-02) 22985 */ 22986 protected function convertSVGtMatrix($tm) { 22987 $a = $tm[0]; 22988 $b = -$tm[1]; 22989 $c = -$tm[2]; 22990 $d = $tm[3]; 22991 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k; 22992 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k; 22993 $x = 0; 22994 $y = $this->h * $this->k; 22995 $e = ($x * (1 - $a)) - ($y * $c) + $e; 22996 $f = ($y * (1 - $d)) - ($x * $b) + $f; 22997 return array($a, $b, $c, $d, $e, $f); 22998 } 22999 23000 /** 23001 * Apply SVG graphic transformation matrix. 23002 * @param $tm (array) original SVG transformation matrix 23003 * @protected 23004 * @since 5.0.000 (2010-05-02) 23005 */ 23006 protected function SVGTransform($tm) { 23007 $this->Transform($this->convertSVGtMatrix($tm)); 23008 } 23009 23010 /** 23011 * Apply the requested SVG styles (*** TO BE COMPLETED ***) 23012 * @param $svgstyle (array) array of SVG styles to apply 23013 * @param $prevsvgstyle (array) array of previous SVG style 23014 * @param $x (int) X origin of the bounding box 23015 * @param $y (int) Y origin of the bounding box 23016 * @param $w (int) width of the bounding box 23017 * @param $h (int) height of the bounding box 23018 * @param $clip_function (string) clip function 23019 * @param $clip_params (array) array of parameters for clipping function 23020 * @return object style 23021 * @author Nicola Asuni 23022 * @since 5.0.000 (2010-05-02) 23023 * @protected 23024 */ 23025 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) { 23026 if ($this->state != 2) { 23027 return; 23028 } 23029 $objstyle = ''; 23030 $minlen = (0.01 / $this->k); // minimum acceptable length 23031 if (!isset($svgstyle['opacity'])) { 23032 return $objstyle; 23033 } 23034 // clip-path 23035 $regs = array(); 23036 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) { 23037 $clip_path = $this->svgclippaths[$regs[1]]; 23038 foreach ($clip_path as $cp) { 23039 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']); 23040 } 23041 } 23042 // opacity 23043 if ($svgstyle['opacity'] != 1) { 23044 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false); 23045 } 23046 // color 23047 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors); 23048 $this->SetFillColorArray($fill_color); 23049 // text color 23050 $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors); 23051 $this->SetTextColorArray($text_color); 23052 // clip 23053 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) { 23054 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0); 23055 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0); 23056 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0); 23057 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0); 23058 $cx = $x + $left; 23059 $cy = $y + $top; 23060 $cw = $w - $left - $right; 23061 $ch = $h - $top - $bottom; 23062 if ($svgstyle['clip-rule'] == 'evenodd') { 23063 $clip_rule = 'CNZ'; 23064 } else { 23065 $clip_rule = 'CEO'; 23066 } 23067 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array()); 23068 } 23069 // fill 23070 $regs = array(); 23071 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) { 23072 // gradient 23073 $gradient = $this->svggradients[$regs[1]]; 23074 if (isset($gradient['xref'])) { 23075 // reference to another gradient definition 23076 $newgradient = $this->svggradients[$gradient['xref']]; 23077 $newgradient['coords'] = $gradient['coords']; 23078 $newgradient['mode'] = $gradient['mode']; 23079 $newgradient['type'] = $gradient['type']; 23080 $newgradient['gradientUnits'] = $gradient['gradientUnits']; 23081 if (isset($gradient['gradientTransform'])) { 23082 $newgradient['gradientTransform'] = $gradient['gradientTransform']; 23083 } 23084 $gradient = $newgradient; 23085 } 23086 //save current Graphic State 23087 $this->_outSaveGraphicsState(); 23088 //set clipping area 23089 if (!empty($clip_function) AND method_exists($this, $clip_function)) { 23090 $bbox = call_user_func_array(array($this, $clip_function), $clip_params); 23091 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) { 23092 list($x, $y, $w, $h) = $bbox; 23093 } 23094 } 23095 if ($gradient['mode'] == 'measure') { 23096 if (!isset($gradient['coords'][4])) { 23097 $gradient['coords'][4] = 0.5; 23098 } 23099 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) { 23100 $gtm = $gradient['gradientTransform']; 23101 // apply transformation matrix 23102 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4]; 23103 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5]; 23104 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4]; 23105 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5]; 23106 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2)); 23107 $gradient['coords'][0] = $xa; 23108 $gradient['coords'][1] = $ya; 23109 $gradient['coords'][2] = $xb; 23110 $gradient['coords'][3] = $yb; 23111 $gradient['coords'][4] = $r; 23112 } 23113 // convert SVG coordinates to user units 23114 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false); 23115 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false); 23116 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false); 23117 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false); 23118 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false); 23119 if ($w <= $minlen) { 23120 $w = $minlen; 23121 } 23122 if ($h <= $minlen) { 23123 $h = $minlen; 23124 } 23125 // shift units 23126 if ($gradient['gradientUnits'] == 'objectBoundingBox') { 23127 // convert to SVG coordinate system 23128 $gradient['coords'][0] += $x; 23129 $gradient['coords'][1] += $y; 23130 $gradient['coords'][2] += $x; 23131 $gradient['coords'][3] += $y; 23132 } 23133 // calculate percentages 23134 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w); 23135 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h); 23136 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w); 23137 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h); 23138 $gradient['coords'][4] /= $w; 23139 } elseif ($gradient['mode'] == 'percentage') { 23140 foreach($gradient['coords'] as $key => $val) { 23141 $gradient['coords'][$key] = (intval($val) / 100); 23142 if ($val < 0) { 23143 $gradient['coords'][$key] = 0; 23144 } elseif ($val > 1) { 23145 $gradient['coords'][$key] = 1; 23146 } 23147 } 23148 } 23149 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) { 23150 // single color (no shading) 23151 $gradient['coords'][0] = 1; 23152 $gradient['coords'][1] = 0; 23153 $gradient['coords'][2] = 0.999; 23154 $gradient['coords'][3] = 0; 23155 } 23156 // swap Y coordinates 23157 $tmp = $gradient['coords'][1]; 23158 $gradient['coords'][1] = $gradient['coords'][3]; 23159 $gradient['coords'][3] = $tmp; 23160 // set transformation map for gradient 23161 $cy = ($this->h - $y); 23162 if ($gradient['type'] == 3) { 23163 // circular gradient 23164 $cy -= ($gradient['coords'][1] * ($w + $h)); 23165 $h = $w = max($w, $h); 23166 } else { 23167 $cy -= $h; 23168 } 23169 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k))); 23170 if (count($gradient['stops']) > 1) { 23171 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false); 23172 } 23173 } elseif ($svgstyle['fill'] != 'none') { 23174 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors); 23175 if ($svgstyle['fill-opacity'] != 1) { 23176 $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false); 23177 } 23178 $this->SetFillColorArray($fill_color); 23179 if ($svgstyle['fill-rule'] == 'evenodd') { 23180 $objstyle .= 'F*'; 23181 } else { 23182 $objstyle .= 'F'; 23183 } 23184 } 23185 // stroke 23186 if ($svgstyle['stroke'] != 'none') { 23187 if ($svgstyle['stroke-opacity'] != 1) { 23188 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false); 23189 } elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) { 23190 $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false); 23191 } 23192 $stroke_style = array( 23193 'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors), 23194 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false), 23195 'cap' => $svgstyle['stroke-linecap'], 23196 'join' => $svgstyle['stroke-linejoin'] 23197 ); 23198 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) { 23199 $stroke_style['dash'] = $svgstyle['stroke-dasharray']; 23200 } 23201 $this->SetLineStyle($stroke_style); 23202 $objstyle .= 'D'; 23203 } 23204 // font 23205 $regs = array(); 23206 if (!empty($svgstyle['font'])) { 23207 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) { 23208 $font_family = $this->getFontFamilyName($regs[1]); 23209 } else { 23210 $font_family = $svgstyle['font-family']; 23211 } 23212 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23213 $font_size = trim($regs[1]); 23214 } else { 23215 $font_size = $svgstyle['font-size']; 23216 } 23217 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23218 $font_style = trim($regs[1]); 23219 } else { 23220 $font_style = $svgstyle['font-style']; 23221 } 23222 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23223 $font_weight = trim($regs[1]); 23224 } else { 23225 $font_weight = $svgstyle['font-weight']; 23226 } 23227 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23228 $font_stretch = trim($regs[1]); 23229 } else { 23230 $font_stretch = $svgstyle['font-stretch']; 23231 } 23232 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23233 $font_spacing = trim($regs[1]); 23234 } else { 23235 $font_spacing = $svgstyle['letter-spacing']; 23236 } 23237 } else { 23238 $font_family = $this->getFontFamilyName($svgstyle['font-family']); 23239 $font_size = $svgstyle['font-size']; 23240 $font_style = $svgstyle['font-style']; 23241 $font_weight = $svgstyle['font-weight']; 23242 $font_stretch = $svgstyle['font-stretch']; 23243 $font_spacing = $svgstyle['letter-spacing']; 23244 } 23245 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit); 23246 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']); 23247 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']); 23248 switch ($font_style) { 23249 case 'italic': { 23250 $font_style = 'I'; 23251 break; 23252 } 23253 case 'oblique': { 23254 $font_style = 'I'; 23255 break; 23256 } 23257 default: 23258 case 'normal': { 23259 $font_style = ''; 23260 break; 23261 } 23262 } 23263 switch ($font_weight) { 23264 case 'bold': 23265 case 'bolder': { 23266 $font_style .= 'B'; 23267 break; 23268 } 23269 case 'normal': { 23270 if ((substr($font_family, -1) == 'I') AND (substr($font_family, -2, 1) == 'B')) { 23271 $font_family = substr($font_family, 0, -2).'I'; 23272 } elseif (substr($font_family, -1) == 'B') { 23273 $font_family = substr($font_family, 0, -1); 23274 } 23275 break; 23276 } 23277 } 23278 switch ($svgstyle['text-decoration']) { 23279 case 'underline': { 23280 $font_style .= 'U'; 23281 break; 23282 } 23283 case 'overline': { 23284 $font_style .= 'O'; 23285 break; 23286 } 23287 case 'line-through': { 23288 $font_style .= 'D'; 23289 break; 23290 } 23291 default: 23292 case 'none': { 23293 break; 23294 } 23295 } 23296 $this->SetFont($font_family, $font_style, $font_size); 23297 $this->setFontStretching($font_stretch); 23298 $this->setFontSpacing($font_spacing); 23299 return $objstyle; 23300 } 23301 23302 /** 23303 * Draws an SVG path 23304 * @param $d (string) attribute d of the path SVG element 23305 * @param $style (string) Style of rendering. Possible values are: 23306 * <ul> 23307 * <li>D or empty string: Draw (default).</li> 23308 * <li>F: Fill.</li> 23309 * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li> 23310 * <li>DF or FD: Draw and fill.</li> 23311 * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li> 23312 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 23313 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 23314 * </ul> 23315 * @return array of container box measures (x, y, w, h) 23316 * @author Nicola Asuni 23317 * @since 5.0.000 (2010-05-02) 23318 * @protected 23319 */ 23320 protected function SVGPath($d, $style='') { 23321 if ($this->state != 2) { 23322 return; 23323 } 23324 // set fill/stroke style 23325 $op = TCPDF_STATIC::getPathPaintOperator($style, ''); 23326 if (empty($op)) { 23327 return; 23328 } 23329 $paths = array(); 23330 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d); 23331 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER); 23332 $x = 0; 23333 $y = 0; 23334 $x1 = 0; 23335 $y1 = 0; 23336 $x2 = 0; 23337 $y2 = 0; 23338 $xmin = 2147483647; 23339 $xmax = 0; 23340 $ymin = 2147483647; 23341 $ymax = 0; 23342 $relcoord = false; 23343 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point) 23344 $firstcmd = true; // used to print first point 23345 // draw curve pieces 23346 foreach ($paths as $key => $val) { 23347 // get curve type 23348 $cmd = trim($val[1]); 23349 if (strtolower($cmd) == $cmd) { 23350 // use relative coordinated instead of absolute 23351 $relcoord = true; 23352 $xoffset = $x; 23353 $yoffset = $y; 23354 } else { 23355 $relcoord = false; 23356 $xoffset = 0; 23357 $yoffset = 0; 23358 } 23359 $params = array(); 23360 if (isset($val[2])) { 23361 // get curve parameters 23362 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2])); 23363 $params = array(); 23364 foreach ($rawparams as $ck => $cp) { 23365 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false); 23366 if (abs($params[$ck]) < $minlen) { 23367 // approximate little values to zero 23368 $params[$ck] = 0; 23369 } 23370 } 23371 } 23372 // store current origin point 23373 $x0 = $x; 23374 $y0 = $y; 23375 switch (strtoupper($cmd)) { 23376 case 'M': { // moveto 23377 foreach ($params as $ck => $cp) { 23378 if (($ck % 2) == 0) { 23379 $x = $cp + $xoffset; 23380 } else { 23381 $y = $cp + $yoffset; 23382 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23383 if ($ck == 1) { 23384 $this->_outPoint($x, $y); 23385 $firstcmd = false; 23386 } else { 23387 $this->_outLine($x, $y); 23388 } 23389 $x0 = $x; 23390 $y0 = $y; 23391 } 23392 $xmin = min($xmin, $x); 23393 $ymin = min($ymin, $y); 23394 $xmax = max($xmax, $x); 23395 $ymax = max($ymax, $y); 23396 if ($relcoord) { 23397 $xoffset = $x; 23398 $yoffset = $y; 23399 } 23400 } 23401 } 23402 break; 23403 } 23404 case 'L': { // lineto 23405 foreach ($params as $ck => $cp) { 23406 if (($ck % 2) == 0) { 23407 $x = $cp + $xoffset; 23408 } else { 23409 $y = $cp + $yoffset; 23410 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23411 $this->_outLine($x, $y); 23412 $x0 = $x; 23413 $y0 = $y; 23414 } 23415 $xmin = min($xmin, $x); 23416 $ymin = min($ymin, $y); 23417 $xmax = max($xmax, $x); 23418 $ymax = max($ymax, $y); 23419 if ($relcoord) { 23420 $xoffset = $x; 23421 $yoffset = $y; 23422 } 23423 } 23424 } 23425 break; 23426 } 23427 case 'H': { // horizontal lineto 23428 foreach ($params as $ck => $cp) { 23429 $x = $cp + $xoffset; 23430 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23431 $this->_outLine($x, $y); 23432 $x0 = $x; 23433 $y0 = $y; 23434 } 23435 $xmin = min($xmin, $x); 23436 $xmax = max($xmax, $x); 23437 if ($relcoord) { 23438 $xoffset = $x; 23439 } 23440 } 23441 break; 23442 } 23443 case 'V': { // vertical lineto 23444 foreach ($params as $ck => $cp) { 23445 $y = $cp + $yoffset; 23446 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23447 $this->_outLine($x, $y); 23448 $x0 = $x; 23449 $y0 = $y; 23450 } 23451 $ymin = min($ymin, $y); 23452 $ymax = max($ymax, $y); 23453 if ($relcoord) { 23454 $yoffset = $y; 23455 } 23456 } 23457 break; 23458 } 23459 case 'C': { // curveto 23460 foreach ($params as $ck => $cp) { 23461 $params[$ck] = $cp; 23462 if ((($ck + 1) % 6) == 0) { 23463 $x1 = $params[($ck - 5)] + $xoffset; 23464 $y1 = $params[($ck - 4)] + $yoffset; 23465 $x2 = $params[($ck - 3)] + $xoffset; 23466 $y2 = $params[($ck - 2)] + $yoffset; 23467 $x = $params[($ck - 1)] + $xoffset; 23468 $y = $params[($ck)] + $yoffset; 23469 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 23470 $xmin = min($xmin, $x, $x1, $x2); 23471 $ymin = min($ymin, $y, $y1, $y2); 23472 $xmax = max($xmax, $x, $x1, $x2); 23473 $ymax = max($ymax, $y, $y1, $y2); 23474 if ($relcoord) { 23475 $xoffset = $x; 23476 $yoffset = $y; 23477 } 23478 } 23479 } 23480 break; 23481 } 23482 case 'S': { // shorthand/smooth curveto 23483 foreach ($params as $ck => $cp) { 23484 $params[$ck] = $cp; 23485 if ((($ck + 1) % 4) == 0) { 23486 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) { 23487 $x1 = (2 * $x) - $x2; 23488 $y1 = (2 * $y) - $y2; 23489 } else { 23490 $x1 = $x; 23491 $y1 = $y; 23492 } 23493 $x2 = $params[($ck - 3)] + $xoffset; 23494 $y2 = $params[($ck - 2)] + $yoffset; 23495 $x = $params[($ck - 1)] + $xoffset; 23496 $y = $params[($ck)] + $yoffset; 23497 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 23498 $xmin = min($xmin, $x, $x1, $x2); 23499 $ymin = min($ymin, $y, $y1, $y2); 23500 $xmax = max($xmax, $x, $x1, $x2); 23501 $ymax = max($ymax, $y, $y1, $y2); 23502 if ($relcoord) { 23503 $xoffset = $x; 23504 $yoffset = $y; 23505 } 23506 } 23507 } 23508 break; 23509 } 23510 case 'Q': { // quadratic Bezier curveto 23511 foreach ($params as $ck => $cp) { 23512 $params[$ck] = $cp; 23513 if ((($ck + 1) % 4) == 0) { 23514 // convert quadratic points to cubic points 23515 $x1 = $params[($ck - 3)] + $xoffset; 23516 $y1 = $params[($ck - 2)] + $yoffset; 23517 $xa = ($x + (2 * $x1)) / 3; 23518 $ya = ($y + (2 * $y1)) / 3; 23519 $x = $params[($ck - 1)] + $xoffset; 23520 $y = $params[($ck)] + $yoffset; 23521 $xb = ($x + (2 * $x1)) / 3; 23522 $yb = ($y + (2 * $y1)) / 3; 23523 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 23524 $xmin = min($xmin, $x, $xa, $xb); 23525 $ymin = min($ymin, $y, $ya, $yb); 23526 $xmax = max($xmax, $x, $xa, $xb); 23527 $ymax = max($ymax, $y, $ya, $yb); 23528 if ($relcoord) { 23529 $xoffset = $x; 23530 $yoffset = $y; 23531 } 23532 } 23533 } 23534 break; 23535 } 23536 case 'T': { // shorthand/smooth quadratic Bezier curveto 23537 foreach ($params as $ck => $cp) { 23538 $params[$ck] = $cp; 23539 if (($ck % 2) != 0) { 23540 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) { 23541 $x1 = (2 * $x) - $x1; 23542 $y1 = (2 * $y) - $y1; 23543 } else { 23544 $x1 = $x; 23545 $y1 = $y; 23546 } 23547 // convert quadratic points to cubic points 23548 $xa = ($x + (2 * $x1)) / 3; 23549 $ya = ($y + (2 * $y1)) / 3; 23550 $x = $params[($ck - 1)] + $xoffset; 23551 $y = $params[($ck)] + $yoffset; 23552 $xb = ($x + (2 * $x1)) / 3; 23553 $yb = ($y + (2 * $y1)) / 3; 23554 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 23555 $xmin = min($xmin, $x, $xa, $xb); 23556 $ymin = min($ymin, $y, $ya, $yb); 23557 $xmax = max($xmax, $x, $xa, $xb); 23558 $ymax = max($ymax, $y, $ya, $yb); 23559 if ($relcoord) { 23560 $xoffset = $x; 23561 $yoffset = $y; 23562 } 23563 } 23564 } 23565 break; 23566 } 23567 case 'A': { // elliptical arc 23568 foreach ($params as $ck => $cp) { 23569 $params[$ck] = $cp; 23570 if ((($ck + 1) % 7) == 0) { 23571 $x0 = $x; 23572 $y0 = $y; 23573 $rx = abs($params[($ck - 6)]); 23574 $ry = abs($params[($ck - 5)]); 23575 $ang = -$rawparams[($ck - 4)]; 23576 $angle = deg2rad($ang); 23577 $fa = $rawparams[($ck - 3)]; // large-arc-flag 23578 $fs = $rawparams[($ck - 2)]; // sweep-flag 23579 $x = $params[($ck - 1)] + $xoffset; 23580 $y = $params[$ck] + $yoffset; 23581 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) { 23582 // endpoints are almost identical 23583 $xmin = min($xmin, $x); 23584 $ymin = min($ymin, $y); 23585 $xmax = max($xmax, $x); 23586 $ymax = max($ymax, $y); 23587 } else { 23588 $cos_ang = cos($angle); 23589 $sin_ang = sin($angle); 23590 $a = (($x0 - $x) / 2); 23591 $b = (($y0 - $y) / 2); 23592 $xa = ($a * $cos_ang) - ($b * $sin_ang); 23593 $ya = ($a * $sin_ang) + ($b * $cos_ang); 23594 $rx2 = $rx * $rx; 23595 $ry2 = $ry * $ry; 23596 $xa2 = $xa * $xa; 23597 $ya2 = $ya * $ya; 23598 $delta = ($xa2 / $rx2) + ($ya2 / $ry2); 23599 if ($delta > 1) { 23600 $rx *= sqrt($delta); 23601 $ry *= sqrt($delta); 23602 $rx2 = $rx * $rx; 23603 $ry2 = $ry * $ry; 23604 } 23605 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2)); 23606 if ($numerator < 0) { 23607 $root = 0; 23608 } else { 23609 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2))); 23610 } 23611 if ($fa == $fs){ 23612 $root *= -1; 23613 } 23614 $cax = $root * (($rx * $ya) / $ry); 23615 $cay = -$root * (($ry * $xa) / $rx); 23616 // coordinates of ellipse center 23617 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2); 23618 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2); 23619 // get angles 23620 $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry)); 23621 $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry)); 23622 if (($fs == 0) AND ($dang > 0)) { 23623 $dang -= (2 * M_PI); 23624 } elseif (($fs == 1) AND ($dang < 0)) { 23625 $dang += (2 * M_PI); 23626 } 23627 $angf = $angs - $dang; 23628 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) { 23629 // reverse angles 23630 $tmp = $angs; 23631 $angs = $angf; 23632 $angf = $tmp; 23633 } 23634 $angs = round(rad2deg($angs), 6); 23635 $angf = round(rad2deg($angf), 6); 23636 // covent angles to positive values 23637 if (($angs < 0) AND ($angf < 0)) { 23638 $angs += 360; 23639 $angf += 360; 23640 } 23641 $pie = false; 23642 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) { 23643 $pie = true; 23644 } 23645 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true); 23646 $xmin = min($xmin, $x, $axmin); 23647 $ymin = min($ymin, $y, $aymin); 23648 $xmax = max($xmax, $x, $axmax); 23649 $ymax = max($ymax, $y, $aymax); 23650 } 23651 if ($relcoord) { 23652 $xoffset = $x; 23653 $yoffset = $y; 23654 } 23655 } 23656 } 23657 break; 23658 } 23659 case 'Z': { 23660 $this->_out('h'); 23661 break; 23662 } 23663 } 23664 $firstcmd = false; 23665 } // end foreach 23666 if (!empty($op)) { 23667 $this->_out($op); 23668 } 23669 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin)); 23670 } 23671 23672 /** 23673 * Return the tag name without the namespace 23674 * @param $name (string) Tag name 23675 * @protected 23676 */ 23677 protected function removeTagNamespace($name) { 23678 if(strpos($name, ':') !== false) { 23679 $parts = explode(':', $name); 23680 return $parts[(sizeof($parts) - 1)]; 23681 } 23682 return $name; 23683 } 23684 23685 /** 23686 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***) 23687 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 23688 * @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. 23689 * @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. 23690 * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix). 23691 * @author Nicola Asuni 23692 * @since 5.0.000 (2010-05-02) 23693 * @protected 23694 */ 23695 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) { 23696 $name = $this->removeTagNamespace($name); 23697 // check if we are in clip mode 23698 if ($this->svgclipmode) { 23699 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]); 23700 return; 23701 } 23702 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) { 23703 if (isset($attribs['id'])) { 23704 $attribs['child_elements'] = array(); 23705 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs); 23706 return; 23707 } 23708 if (end($this->svgdefs) !== FALSE) { 23709 $last_svgdefs_id = key($this->svgdefs); 23710 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) { 23711 $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1); 23712 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs); 23713 return; 23714 } 23715 } 23716 return; 23717 } 23718 $clipping = false; 23719 if ($parser == 'clip-path') { 23720 // set clipping mode 23721 $clipping = true; 23722 } 23723 // get styling properties 23724 $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style 23725 $svgstyle = $this->svgstyles[0]; // set default style 23726 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) { 23727 // default fill attribute for clipping 23728 $attribs['fill'] = 'none'; 23729 } 23730 if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) { 23731 // fix style for regular expression 23732 $attribs['style'] = ';'.$attribs['style']; 23733 } 23734 foreach ($prev_svgstyle as $key => $val) { 23735 if (in_array($key, TCPDF_IMAGES::$svginheritprop)) { 23736 // inherit previous value 23737 $svgstyle[$key] = $val; 23738 } 23739 if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) { 23740 // specific attribute settings 23741 if ($attribs[$key] == 'inherit') { 23742 $svgstyle[$key] = $val; 23743 } else { 23744 $svgstyle[$key] = $attribs[$key]; 23745 } 23746 } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) { 23747 // CSS style syntax 23748 $attrval = array(); 23749 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) { 23750 if ($attrval[1] == 'inherit') { 23751 $svgstyle[$key] = $val; 23752 } else { 23753 $svgstyle[$key] = $attrval[1]; 23754 } 23755 } 23756 } 23757 } 23758 // transformation matrix 23759 if (!empty($ctm)) { 23760 $tm = $ctm; 23761 } else { 23762 $tm = array(1,0,0,1,0,0); 23763 } 23764 if (isset($attribs['transform']) AND !empty($attribs['transform'])) { 23765 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform'])); 23766 } 23767 $svgstyle['transfmatrix'] = $tm; 23768 $invisible = false; 23769 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) { 23770 // the current graphics element is invisible (nothing is painted) 23771 $invisible = true; 23772 } 23773 // process tag 23774 switch($name) { 23775 case 'defs': { 23776 $this->svgdefsmode = true; 23777 break; 23778 } 23779 // clipPath 23780 case 'clipPath': { 23781 if ($invisible) { 23782 break; 23783 } 23784 $this->svgclipmode = true; 23785 if (!isset($attribs['id'])) { 23786 $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1); 23787 } 23788 $this->svgclipid = $attribs['id']; 23789 $this->svgclippaths[$this->svgclipid] = array(); 23790 $this->svgcliptm[$this->svgclipid] = $tm; 23791 break; 23792 } 23793 case 'svg': { 23794 // start of SVG object 23795 if(++$this->svg_tag_depth <= 1) { 23796 break; 23797 } 23798 // inner SVG 23799 array_push($this->svgstyles, $svgstyle); 23800 $this->StartTransform(); 23801 $svgX = (isset($attribs['x'])?$attribs['x']:0); 23802 $svgY = (isset($attribs['y'])?$attribs['y']:0); 23803 $svgW = (isset($attribs['width'])?$attribs['width']:0); 23804 $svgH = (isset($attribs['height'])?$attribs['height']:0); 23805 // set x, y position using transform matrix 23806 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY)); 23807 $this->SVGTransform($tm); 23808 // set clipping for width and height 23809 $x = 0; 23810 $y = 0; 23811 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w); 23812 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h); 23813 // draw clipping rect 23814 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array()); 23815 // parse viewbox, calculate extra transformation matrix 23816 if (isset($attribs['viewBox'])) { 23817 $tmp = array(); 23818 preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp); 23819 $tmp = $tmp[0]; 23820 if (sizeof($tmp) == 4) { 23821 $vx = $tmp[0]; 23822 $vy = $tmp[1]; 23823 $vw = $tmp[2]; 23824 $vh = $tmp[3]; 23825 // get aspect ratio 23826 $tmp = array(); 23827 $aspectX = 'xMid'; 23828 $aspectY = 'YMid'; 23829 $fit = 'meet'; 23830 if (isset($attribs['preserveAspectRatio'])) { 23831 if($attribs['preserveAspectRatio'] == 'none') { 23832 $fit = 'none'; 23833 } else { 23834 preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp); 23835 $tmp = $tmp[0]; 23836 if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) { 23837 $aspectX = substr($tmp[0], 0, 4); 23838 $aspectY = substr($tmp[0], 4, 4); 23839 $fit = $tmp[1]; 23840 } 23841 } 23842 } 23843 $wr = ($svgW / $vw); 23844 $hr = ($svgH / $vh); 23845 $ax = $ay = 0; 23846 if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) { 23847 if ($aspectX == 'xMax') { 23848 $ax = (($vw * ($wr / $hr)) - $vw); 23849 } 23850 if ($aspectX == 'xMid') { 23851 $ax = ((($vw * ($wr / $hr)) - $vw) / 2); 23852 } 23853 $wr = $hr; 23854 } elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) { 23855 if ($aspectY == 'YMax') { 23856 $ay = (($vh * ($hr / $wr)) - $vh); 23857 } 23858 if ($aspectY == 'YMid') { 23859 $ay = ((($vh * ($hr / $wr)) - $vh) / 2); 23860 } 23861 $hr = $wr; 23862 } 23863 $newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY)); 23864 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, $newtm); 23865 $this->SVGTransform($tm); 23866 } 23867 } 23868 $this->setSVGStyles($svgstyle, $prev_svgstyle); 23869 break; 23870 } 23871 case 'g': { 23872 // group together related graphics elements 23873 array_push($this->svgstyles, $svgstyle); 23874 $this->StartTransform(); 23875 $x = (isset($attribs['x'])?$attribs['x']:0); 23876 $y = (isset($attribs['y'])?$attribs['y']:0); 23877 $w = 1;//(isset($attribs['width'])?$attribs['width']:1); 23878 $h = 1;//(isset($attribs['height'])?$attribs['height']:1); 23879 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y)); 23880 $this->SVGTransform($tm); 23881 $this->setSVGStyles($svgstyle, $prev_svgstyle); 23882 break; 23883 } 23884 case 'linearGradient': { 23885 if ($this->pdfa_mode) { 23886 break; 23887 } 23888 if (!isset($attribs['id'])) { 23889 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); 23890 } 23891 $this->svggradientid = $attribs['id']; 23892 $this->svggradients[$this->svggradientid] = array(); 23893 $this->svggradients[$this->svggradientid]['type'] = 2; 23894 $this->svggradients[$this->svggradientid]['stops'] = array(); 23895 if (isset($attribs['gradientUnits'])) { 23896 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 23897 } else { 23898 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 23899 } 23900 //$attribs['spreadMethod'] 23901 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2']))) 23902 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%')) 23903 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%')) 23904 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%')) 23905 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) { 23906 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 23907 } else { 23908 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 23909 } 23910 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0'); 23911 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0'); 23912 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100'); 23913 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0'); 23914 if (isset($attribs['gradientTransform'])) { 23915 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); 23916 } 23917 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2); 23918 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 23919 // gradient is defined on another place 23920 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 23921 } 23922 break; 23923 } 23924 case 'radialGradient': { 23925 if ($this->pdfa_mode) { 23926 break; 23927 } 23928 if (!isset($attribs['id'])) { 23929 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); 23930 } 23931 $this->svggradientid = $attribs['id']; 23932 $this->svggradients[$this->svggradientid] = array(); 23933 $this->svggradients[$this->svggradientid]['type'] = 3; 23934 $this->svggradients[$this->svggradientid]['stops'] = array(); 23935 if (isset($attribs['gradientUnits'])) { 23936 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 23937 } else { 23938 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 23939 } 23940 //$attribs['spreadMethod'] 23941 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy']))) 23942 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%')) 23943 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) { 23944 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 23945 } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) { 23946 $this->svggradients[$this->svggradientid]['mode'] = 'ratio'; 23947 } else { 23948 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 23949 } 23950 $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5); 23951 $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5); 23952 $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx); 23953 $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy); 23954 $r = (isset($attribs['r']) ? $attribs['r'] : 0.5); 23955 if (isset($attribs['gradientTransform'])) { 23956 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); 23957 } 23958 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r); 23959 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 23960 // gradient is defined on another place 23961 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 23962 } 23963 break; 23964 } 23965 case 'stop': { 23966 // gradient stops 23967 if (substr($attribs['offset'], -1) == '%') { 23968 $offset = floatval(substr($attribs['offset'], -1)) / 100; 23969 } else { 23970 $offset = floatval($attribs['offset']); 23971 if ($offset > 1) { 23972 $offset /= 100; 23973 } 23974 } 23975 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black'; 23976 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1; 23977 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity); 23978 break; 23979 } 23980 // paths 23981 case 'path': { 23982 if ($invisible) { 23983 break; 23984 } 23985 if (isset($attribs['d'])) { 23986 $d = trim($attribs['d']); 23987 if (!empty($d)) { 23988 $x = (isset($attribs['x'])?$attribs['x']:0); 23989 $y = (isset($attribs['y'])?$attribs['y']:0); 23990 $w = (isset($attribs['width'])?$attribs['width']:1); 23991 $h = (isset($attribs['height'])?$attribs['height']:1); 23992 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y)); 23993 if ($clipping) { 23994 $this->SVGTransform($tm); 23995 $this->SVGPath($d, 'CNZ'); 23996 } else { 23997 $this->StartTransform(); 23998 $this->SVGTransform($tm); 23999 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ')); 24000 if (!empty($obstyle)) { 24001 $this->SVGPath($d, $obstyle); 24002 } 24003 $this->StopTransform(); 24004 } 24005 } 24006 } 24007 break; 24008 } 24009 // shapes 24010 case 'rect': { 24011 if ($invisible) { 24012 break; 24013 } 24014 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 24015 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 24016 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 24017 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 24018 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); 24019 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx); 24020 if ($clipping) { 24021 $this->SVGTransform($tm); 24022 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array()); 24023 } else { 24024 $this->StartTransform(); 24025 $this->SVGTransform($tm); 24026 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ')); 24027 if (!empty($obstyle)) { 24028 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array()); 24029 } 24030 $this->StopTransform(); 24031 } 24032 break; 24033 } 24034 case 'circle': { 24035 if ($invisible) { 24036 break; 24037 } 24038 $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0); 24039 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); 24040 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); 24041 $x = ($cx - $r); 24042 $y = ($cy - $r); 24043 $w = (2 * $r); 24044 $h = $w; 24045 if ($clipping) { 24046 $this->SVGTransform($tm); 24047 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8); 24048 } else { 24049 $this->StartTransform(); 24050 $this->SVGTransform($tm); 24051 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ')); 24052 if (!empty($obstyle)) { 24053 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8); 24054 } 24055 $this->StopTransform(); 24056 } 24057 break; 24058 } 24059 case 'ellipse': { 24060 if ($invisible) { 24061 break; 24062 } 24063 $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0); 24064 $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0); 24065 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); 24066 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); 24067 $x = ($cx - $rx); 24068 $y = ($cy - $ry); 24069 $w = (2 * $rx); 24070 $h = (2 * $ry); 24071 if ($clipping) { 24072 $this->SVGTransform($tm); 24073 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); 24074 } else { 24075 $this->StartTransform(); 24076 $this->SVGTransform($tm); 24077 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ')); 24078 if (!empty($obstyle)) { 24079 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8); 24080 } 24081 $this->StopTransform(); 24082 } 24083 break; 24084 } 24085 case 'line': { 24086 if ($invisible) { 24087 break; 24088 } 24089 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0); 24090 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0); 24091 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0); 24092 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0); 24093 $x = $x1; 24094 $y = $y1; 24095 $w = abs($x2 - $x1); 24096 $h = abs($y2 - $y1); 24097 if (!$clipping) { 24098 $this->StartTransform(); 24099 $this->SVGTransform($tm); 24100 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2)); 24101 $this->Line($x1, $y1, $x2, $y2); 24102 $this->StopTransform(); 24103 } 24104 break; 24105 } 24106 case 'polyline': 24107 case 'polygon': { 24108 if ($invisible) { 24109 break; 24110 } 24111 $points = (isset($attribs['points'])?$attribs['points']:'0 0'); 24112 $points = trim($points); 24113 // note that point may use a complex syntax not covered here 24114 $points = preg_split('/[\,\s]+/si', $points); 24115 if (count($points) < 4) { 24116 break; 24117 } 24118 $p = array(); 24119 $xmin = 2147483647; 24120 $xmax = 0; 24121 $ymin = 2147483647; 24122 $ymax = 0; 24123 foreach ($points as $key => $val) { 24124 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 24125 if (($key % 2) == 0) { 24126 // X coordinate 24127 $xmin = min($xmin, $p[$key]); 24128 $xmax = max($xmax, $p[$key]); 24129 } else { 24130 // Y coordinate 24131 $ymin = min($ymin, $p[$key]); 24132 $ymax = max($ymax, $p[$key]); 24133 } 24134 } 24135 $x = $xmin; 24136 $y = $ymin; 24137 $w = ($xmax - $xmin); 24138 $h = ($ymax - $ymin); 24139 if ($name == 'polyline') { 24140 $this->StartTransform(); 24141 $this->SVGTransform($tm); 24142 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); 24143 if (!empty($obstyle)) { 24144 $this->PolyLine($p, $obstyle, array(), array()); 24145 } 24146 $this->StopTransform(); 24147 } else { // polygon 24148 if ($clipping) { 24149 $this->SVGTransform($tm); 24150 $this->Polygon($p, 'CNZ', array(), array(), true); 24151 } else { 24152 $this->StartTransform(); 24153 $this->SVGTransform($tm); 24154 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ')); 24155 if (!empty($obstyle)) { 24156 $this->Polygon($p, $obstyle, array(), array(), true); 24157 } 24158 $this->StopTransform(); 24159 } 24160 } 24161 break; 24162 } 24163 // image 24164 case 'image': { 24165 if ($invisible) { 24166 break; 24167 } 24168 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) { 24169 break; 24170 } 24171 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 24172 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 24173 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 24174 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 24175 $img = $attribs['xlink:href']; 24176 if (!$clipping) { 24177 $this->StartTransform(); 24178 $this->SVGTransform($tm); 24179 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h); 24180 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) { 24181 // embedded image encoded as base64 24182 $img = '@'.base64_decode(substr($img, strlen($m[0]))); 24183 } else { 24184 // fix image path 24185 if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) { 24186 // replace relative path with full server path 24187 $img = $this->svgdir.'/'.$img; 24188 } 24189 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { 24190 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']); 24191 if (($findroot === false) OR ($findroot > 1)) { 24192 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { 24193 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img; 24194 } else { 24195 $img = $_SERVER['DOCUMENT_ROOT'].$img; 24196 } 24197 } 24198 } 24199 $img = urldecode($img); 24200 $testscrtype = @parse_url($img); 24201 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { 24202 // convert URL to server path 24203 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img); 24204 } 24205 } 24206 // get image type 24207 $imgtype = TCPDF_IMAGES::getImageFileType($img); 24208 if (($imgtype == 'eps') OR ($imgtype == 'ai')) { 24209 $this->ImageEps($img, $x, $y, $w, $h); 24210 } elseif ($imgtype == 'svg') { 24211 // store SVG vars 24212 $svggradients = $this->svggradients; 24213 $svggradientid = $this->svggradientid; 24214 $svgdefsmode = $this->svgdefsmode; 24215 $svgdefs = $this->svgdefs; 24216 $svgclipmode = $this->svgclipmode; 24217 $svgclippaths = $this->svgclippaths; 24218 $svgcliptm = $this->svgcliptm; 24219 $svgclipid = $this->svgclipid; 24220 $svgtext = $this->svgtext; 24221 $svgtextmode = $this->svgtextmode; 24222 $this->ImageSVG($img, $x, $y, $w, $h); 24223 // restore SVG vars 24224 $this->svggradients = $svggradients; 24225 $this->svggradientid = $svggradientid; 24226 $this->svgdefsmode = $svgdefsmode; 24227 $this->svgdefs = $svgdefs; 24228 $this->svgclipmode = $svgclipmode; 24229 $this->svgclippaths = $svgclippaths; 24230 $this->svgcliptm = $svgcliptm; 24231 $this->svgclipid = $svgclipid; 24232 $this->svgtext = $svgtext; 24233 $this->svgtextmode = $svgtextmode; 24234 } else { 24235 $this->Image($img, $x, $y, $w, $h); 24236 } 24237 $this->StopTransform(); 24238 } 24239 break; 24240 } 24241 // text 24242 case 'text': 24243 case 'tspan': { 24244 if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) { 24245 // @TODO: unsupported feature 24246 } 24247 // only basic support - advanced features must be implemented 24248 $this->svgtextmode['invisible'] = $invisible; 24249 if ($invisible) { 24250 break; 24251 } 24252 array_push($this->svgstyles, $svgstyle); 24253 if (isset($attribs['x'])) { 24254 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false); 24255 } elseif ($name == 'tspan') { 24256 $x = $this->x; 24257 } else { 24258 $x = 0; 24259 } 24260 if (isset($attribs['dx'])) { 24261 $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false); 24262 } 24263 if (isset($attribs['y'])) { 24264 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false); 24265 } elseif ($name == 'tspan') { 24266 $y = $this->y; 24267 } else { 24268 $y = 0; 24269 } 24270 if (isset($attribs['dy'])) { 24271 $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false); 24272 } 24273 $svgstyle['text-color'] = $svgstyle['fill']; 24274 $this->svgtext = ''; 24275 if (isset($svgstyle['text-anchor'])) { 24276 $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor']; 24277 } else { 24278 $this->svgtextmode['text-anchor'] = 'start'; 24279 } 24280 if (isset($svgstyle['direction'])) { 24281 if ($svgstyle['direction'] == 'rtl') { 24282 $this->svgtextmode['rtl'] = true; 24283 } else { 24284 $this->svgtextmode['rtl'] = false; 24285 } 24286 } else { 24287 $this->svgtextmode['rtl'] = false; 24288 } 24289 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) { 24290 $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false); 24291 } else { 24292 $this->svgtextmode['stroke'] = false; 24293 } 24294 $this->StartTransform(); 24295 $this->SVGTransform($tm); 24296 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1); 24297 $this->x = $x; 24298 $this->y = $y; 24299 break; 24300 } 24301 // use 24302 case 'use': { 24303 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 24304 $svgdefid = substr($attribs['xlink:href'], 1); 24305 if (isset($this->svgdefs[$svgdefid])) { 24306 $use = $this->svgdefs[$svgdefid]; 24307 if (isset($attribs['xlink:href'])) { 24308 unset($attribs['xlink:href']); 24309 } 24310 if (isset($attribs['id'])) { 24311 unset($attribs['id']); 24312 } 24313 if (isset($use['attribs']['x']) AND isset($attribs['x'])) { 24314 $attribs['x'] += $use['attribs']['x']; 24315 } 24316 if (isset($use['attribs']['y']) AND isset($attribs['y'])) { 24317 $attribs['y'] += $use['attribs']['y']; 24318 } 24319 if (empty($attribs['style'])) { 24320 $attribs['style'] = ''; 24321 } 24322 if (!empty($use['attribs']['style'])) { 24323 // merge styles 24324 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']); 24325 } 24326 $attribs = array_merge($use['attribs'], $attribs); 24327 $this->startSVGElementHandler($parser, $use['name'], $attribs); 24328 return; 24329 } 24330 } 24331 break; 24332 } 24333 default: { 24334 break; 24335 } 24336 } // end of switch 24337 // process child elements 24338 if (!empty($attribs['child_elements'])) { 24339 $child_elements = $attribs['child_elements']; 24340 unset($attribs['child_elements']); 24341 foreach($child_elements as $child_element) { 24342 if (empty($child_element['attribs']['closing_tag'])) { 24343 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']); 24344 } else { 24345 if (isset($child_element['attribs']['content'])) { 24346 $this->svgtext = $child_element['attribs']['content']; 24347 } 24348 $this->endSVGElementHandler('child-tag', $child_element['name']); 24349 } 24350 } 24351 } 24352 } 24353 24354 /** 24355 * Sets the closing SVG element handler function for the XML parser. 24356 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 24357 * @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. 24358 * @author Nicola Asuni 24359 * @since 5.0.000 (2010-05-02) 24360 * @protected 24361 */ 24362 protected function endSVGElementHandler($parser, $name) { 24363 $name = $this->removeTagNamespace($name); 24364 if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {; 24365 if (end($this->svgdefs) !== FALSE) { 24366 $last_svgdefs_id = key($this->svgdefs); 24367 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) { 24368 foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) { 24369 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) { 24370 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext)); 24371 return; 24372 } 24373 } 24374 if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) { 24375 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext)); 24376 return; 24377 } 24378 } 24379 } 24380 return; 24381 } 24382 switch($name) { 24383 case 'defs': { 24384 $this->svgdefsmode = false; 24385 break; 24386 } 24387 // clipPath 24388 case 'clipPath': { 24389 $this->svgclipmode = false; 24390 break; 24391 } 24392 case 'svg': { 24393 if (--$this->svg_tag_depth <= 0) { 24394 break; 24395 } 24396 } 24397 case 'g': { 24398 // ungroup: remove last style from array 24399 array_pop($this->svgstyles); 24400 $this->StopTransform(); 24401 break; 24402 } 24403 case 'text': 24404 case 'tspan': { 24405 if ($this->svgtextmode['invisible']) { 24406 // This implementation must be fixed to following the rule: 24407 // 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. 24408 break; 24409 } 24410 // print text 24411 $text = $this->svgtext; 24412 //$text = $this->stringTrim($text); 24413 $textlen = $this->GetStringWidth($text); 24414 if ($this->svgtextmode['text-anchor'] != 'start') { 24415 // check if string is RTL text 24416 if ($this->svgtextmode['text-anchor'] == 'end') { 24417 if ($this->svgtextmode['rtl']) { 24418 $this->x += $textlen; 24419 } else { 24420 $this->x -= $textlen; 24421 } 24422 } elseif ($this->svgtextmode['text-anchor'] == 'middle') { 24423 if ($this->svgtextmode['rtl']) { 24424 $this->x += ($textlen / 2); 24425 } else { 24426 $this->x -= ($textlen / 2); 24427 } 24428 } 24429 } 24430 $textrendermode = $this->textrendermode; 24431 $textstrokewidth = $this->textstrokewidth; 24432 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false); 24433 if ($name == 'text') { 24434 // store current coordinates 24435 $tmpx = $this->x; 24436 $tmpy = $this->y; 24437 } 24438 // print the text 24439 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T'); 24440 if ($name == 'text') { 24441 // restore coordinates 24442 $this->x = $tmpx; 24443 $this->y = $tmpy; 24444 } 24445 // restore previous rendering mode 24446 $this->textrendermode = $textrendermode; 24447 $this->textstrokewidth = $textstrokewidth; 24448 $this->svgtext = ''; 24449 $this->StopTransform(); 24450 if (!$this->svgdefsmode) { 24451 array_pop($this->svgstyles); 24452 } 24453 break; 24454 } 24455 default: { 24456 break; 24457 } 24458 } 24459 } 24460 24461 /** 24462 * Sets the character data handler function for the XML parser. 24463 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 24464 * @param $data (string) The second parameter, data, contains the character data as a string. 24465 * @author Nicola Asuni 24466 * @since 5.0.000 (2010-05-02) 24467 * @protected 24468 */ 24469 protected function segSVGContentHandler($parser, $data) { 24470 $this->svgtext .= $data; 24471 } 24472 24473 // --- END SVG METHODS ----------------------------------------------------- 24474 24475 } // END OF TCPDF CLASS 24476 24477 //============================================================+ 24478 // END OF FILE 24479 //============================================================+
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |