1<?php 2//============================================================+ 3// File name : tcpdf.php 4// Version : 6.3.2 5// Begin : 2002-08-03 6// Last Update : 2019-09-20 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-2019 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 ImageMagick (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 ImageMagick (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.3.2 108 */ 109 110// TCPDF configuration 111require_once(dirname(__FILE__).'/tcpdf_autoconfig.php'); 112// TCPDF static font methods and data 113require_once(dirname(__FILE__).'/include/tcpdf_font_data.php'); 114// TCPDF static font methods and data 115require_once(dirname(__FILE__).'/include/tcpdf_fonts.php'); 116// TCPDF static color methods and data 117require_once(dirname(__FILE__).'/include/tcpdf_colors.php'); 118// TCPDF static image methods and data 119require_once(dirname(__FILE__).'/include/tcpdf_images.php'); 120// TCPDF static methods and data 121require_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.3.2 132 * @author Nicola Asuni - info@tecnick.com 133 * @IgnoreAnnotation("protected") 134 * @IgnoreAnnotation("public") 135 * @IgnoreAnnotation("pre") 136 */ 137class TCPDF { 138 139 // Protected properties 140 141 /** 142 * Current page number. 143 * @protected 144 */ 145 protected $page; 146 147 /** 148 * Current object number. 149 * @protected 150 */ 151 protected $n; 152 153 /** 154 * Array of object offsets. 155 * @protected 156 */ 157 protected $offsets = array(); 158 159 /** 160 * Array of object IDs for each page. 161 * @protected 162 */ 163 protected $pageobjects = array(); 164 165 /** 166 * Buffer holding in-memory PDF. 167 * @protected 168 */ 169 protected $buffer; 170 171 /** 172 * Array containing pages. 173 * @protected 174 */ 175 protected $pages = array(); 176 177 /** 178 * Current document state. 179 * @protected 180 */ 181 protected $state; 182 183 /** 184 * Compression flag. 185 * @protected 186 */ 187 protected $compress; 188 189 /** 190 * Current page orientation (P = Portrait, L = Landscape). 191 * @protected 192 */ 193 protected $CurOrientation; 194 195 /** 196 * Page dimensions. 197 * @protected 198 */ 199 protected $pagedim = array(); 200 201 /** 202 * Scale factor (number of points in user unit). 203 * @protected 204 */ 205 protected $k; 206 207 /** 208 * Width of page format in points. 209 * @protected 210 */ 211 protected $fwPt; 212 213 /** 214 * Height of page format in points. 215 * @protected 216 */ 217 protected $fhPt; 218 219 /** 220 * Current width of page in points. 221 * @protected 222 */ 223 protected $wPt; 224 225 /** 226 * Current height of page in points. 227 * @protected 228 */ 229 protected $hPt; 230 231 /** 232 * Current width of page in user unit. 233 * @protected 234 */ 235 protected $w; 236 237 /** 238 * Current height of page in user unit. 239 * @protected 240 */ 241 protected $h; 242 243 /** 244 * Left margin. 245 * @protected 246 */ 247 protected $lMargin; 248 249 /** 250 * Right margin. 251 * @protected 252 */ 253 protected $rMargin; 254 255 /** 256 * Cell left margin (used by regions). 257 * @protected 258 */ 259 protected $clMargin; 260 261 /** 262 * Cell right margin (used by regions). 263 * @protected 264 */ 265 protected $crMargin; 266 267 /** 268 * Top margin. 269 * @protected 270 */ 271 protected $tMargin; 272 273 /** 274 * Page break margin. 275 * @protected 276 */ 277 protected $bMargin; 278 279 /** 280 * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left). 281 * @since 5.9.000 (2010-10-03) 282 * @protected 283 */ 284 protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0); 285 286 /** 287 * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left). 288 * @since 5.9.000 (2010-10-04) 289 * @protected 290 */ 291 protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0); 292 293 /** 294 * Current horizontal position in user unit for cell positioning. 295 * @protected 296 */ 297 protected $x; 298 299 /** 300 * Current vertical position in user unit for cell positioning. 301 * @protected 302 */ 303 protected $y; 304 305 /** 306 * Height of last cell printed. 307 * @protected 308 */ 309 protected $lasth; 310 311 /** 312 * Line width in user unit. 313 * @protected 314 */ 315 protected $LineWidth; 316 317 /** 318 * Array of standard font names. 319 * @protected 320 */ 321 protected $CoreFonts; 322 323 /** 324 * Array of used fonts. 325 * @protected 326 */ 327 protected $fonts = array(); 328 329 /** 330 * Array of font files. 331 * @protected 332 */ 333 protected $FontFiles = array(); 334 335 /** 336 * Array of encoding differences. 337 * @protected 338 */ 339 protected $diffs = array(); 340 341 /** 342 * Array of used images. 343 * @protected 344 */ 345 protected $images = array(); 346 347 /** 348 * Depth of the svg tag, to keep track if the svg tag is a subtag or the root tag. 349 * @protected 350 */ 351 protected $svg_tag_depth = 0; 352 353 /** 354 * Array of Annotations in pages. 355 * @protected 356 */ 357 protected $PageAnnots = array(); 358 359 /** 360 * Array of internal links. 361 * @protected 362 */ 363 protected $links = array(); 364 365 /** 366 * Current font family. 367 * @protected 368 */ 369 protected $FontFamily; 370 371 /** 372 * Current font style. 373 * @protected 374 */ 375 protected $FontStyle; 376 377 /** 378 * Current font ascent (distance between font top and baseline). 379 * @protected 380 * @since 2.8.000 (2007-03-29) 381 */ 382 protected $FontAscent; 383 384 /** 385 * Current font descent (distance between font bottom and baseline). 386 * @protected 387 * @since 2.8.000 (2007-03-29) 388 */ 389 protected $FontDescent; 390 391 /** 392 * Underlining flag. 393 * @protected 394 */ 395 protected $underline; 396 397 /** 398 * Overlining flag. 399 * @protected 400 */ 401 protected $overline; 402 403 /** 404 * Current font info. 405 * @protected 406 */ 407 protected $CurrentFont; 408 409 /** 410 * Current font size in points. 411 * @protected 412 */ 413 protected $FontSizePt; 414 415 /** 416 * Current font size in user unit. 417 * @protected 418 */ 419 protected $FontSize; 420 421 /** 422 * Commands for drawing color. 423 * @protected 424 */ 425 protected $DrawColor; 426 427 /** 428 * Commands for filling color. 429 * @protected 430 */ 431 protected $FillColor; 432 433 /** 434 * Commands for text color. 435 * @protected 436 */ 437 protected $TextColor; 438 439 /** 440 * Indicates whether fill and text colors are different. 441 * @protected 442 */ 443 protected $ColorFlag; 444 445 /** 446 * Automatic page breaking. 447 * @protected 448 */ 449 protected $AutoPageBreak; 450 451 /** 452 * Threshold used to trigger page breaks. 453 * @protected 454 */ 455 protected $PageBreakTrigger; 456 457 /** 458 * Flag set when processing page header. 459 * @protected 460 */ 461 protected $InHeader = false; 462 463 /** 464 * Flag set when processing page footer. 465 * @protected 466 */ 467 protected $InFooter = false; 468 469 /** 470 * Zoom display mode. 471 * @protected 472 */ 473 protected $ZoomMode; 474 475 /** 476 * Layout display mode. 477 * @protected 478 */ 479 protected $LayoutMode; 480 481 /** 482 * If true set the document information dictionary in Unicode. 483 * @protected 484 */ 485 protected $docinfounicode = true; 486 487 /** 488 * Document title. 489 * @protected 490 */ 491 protected $title = ''; 492 493 /** 494 * Document subject. 495 * @protected 496 */ 497 protected $subject = ''; 498 499 /** 500 * Document author. 501 * @protected 502 */ 503 protected $author = ''; 504 505 /** 506 * Document keywords. 507 * @protected 508 */ 509 protected $keywords = ''; 510 511 /** 512 * Document creator. 513 * @protected 514 */ 515 protected $creator = ''; 516 517 /** 518 * Starting page number. 519 * @protected 520 */ 521 protected $starting_page_number = 1; 522 523 /** 524 * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image. 525 * @since 2002-07-31 526 * @author Nicola Asuni 527 * @protected 528 */ 529 protected $img_rb_x; 530 531 /** 532 * The right-bottom corner Y coordinate of last inserted image. 533 * @since 2002-07-31 534 * @author Nicola Asuni 535 * @protected 536 */ 537 protected $img_rb_y; 538 539 /** 540 * Adjusting factor to convert pixels to user units. 541 * @since 2004-06-14 542 * @author Nicola Asuni 543 * @protected 544 */ 545 protected $imgscale = 1; 546 547 /** 548 * Boolean flag set to true when the input text is unicode (require unicode fonts). 549 * @since 2005-01-02 550 * @author Nicola Asuni 551 * @protected 552 */ 553 protected $isunicode = false; 554 555 /** 556 * PDF version. 557 * @since 1.5.3 558 * @protected 559 */ 560 protected $PDFVersion = '1.7'; 561 562 /** 563 * ID of the stored default header template (-1 = not set). 564 * @protected 565 */ 566 protected $header_xobjid = false; 567 568 /** 569 * If true reset the Header Xobject template at each page 570 * @protected 571 */ 572 protected $header_xobj_autoreset = false; 573 574 /** 575 * Minimum distance between header and top page margin. 576 * @protected 577 */ 578 protected $header_margin; 579 580 /** 581 * Minimum distance between footer and bottom page margin. 582 * @protected 583 */ 584 protected $footer_margin; 585 586 /** 587 * Original left margin value. 588 * @protected 589 * @since 1.53.0.TC013 590 */ 591 protected $original_lMargin; 592 593 /** 594 * Original right margin value. 595 * @protected 596 * @since 1.53.0.TC013 597 */ 598 protected $original_rMargin; 599 600 /** 601 * Default font used on page header. 602 * @protected 603 */ 604 protected $header_font; 605 606 /** 607 * Default font used on page footer. 608 * @protected 609 */ 610 protected $footer_font; 611 612 /** 613 * Language templates. 614 * @protected 615 */ 616 protected $l; 617 618 /** 619 * Barcode to print on page footer (only if set). 620 * @protected 621 */ 622 protected $barcode = false; 623 624 /** 625 * Boolean flag to print/hide page header. 626 * @protected 627 */ 628 protected $print_header = true; 629 630 /** 631 * Boolean flag to print/hide page footer. 632 * @protected 633 */ 634 protected $print_footer = true; 635 636 /** 637 * Header image logo. 638 * @protected 639 */ 640 protected $header_logo = ''; 641 642 /** 643 * Width of header image logo in user units. 644 * @protected 645 */ 646 protected $header_logo_width = 30; 647 648 /** 649 * Title to be printed on default page header. 650 * @protected 651 */ 652 protected $header_title = ''; 653 654 /** 655 * String to pring on page header after title. 656 * @protected 657 */ 658 protected $header_string = ''; 659 660 /** 661 * Color for header text (RGB array). 662 * @since 5.9.174 (2012-07-25) 663 * @protected 664 */ 665 protected $header_text_color = array(0,0,0); 666 667 /** 668 * Color for header line (RGB array). 669 * @since 5.9.174 (2012-07-25) 670 * @protected 671 */ 672 protected $header_line_color = array(0,0,0); 673 674 /** 675 * Color for footer text (RGB array). 676 * @since 5.9.174 (2012-07-25) 677 * @protected 678 */ 679 protected $footer_text_color = array(0,0,0); 680 681 /** 682 * Color for footer line (RGB array). 683 * @since 5.9.174 (2012-07-25) 684 * @protected 685 */ 686 protected $footer_line_color = array(0,0,0); 687 688 /** 689 * Text shadow data array. 690 * @since 5.9.174 (2012-07-25) 691 * @protected 692 */ 693 protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal'); 694 695 /** 696 * Default number of columns for html table. 697 * @protected 698 */ 699 protected $default_table_columns = 4; 700 701 // variables for html parser 702 703 /** 704 * HTML PARSER: array to store current link and rendering styles. 705 * @protected 706 */ 707 protected $HREF = array(); 708 709 /** 710 * List of available fonts on filesystem. 711 * @protected 712 */ 713 protected $fontlist = array(); 714 715 /** 716 * Current foreground color. 717 * @protected 718 */ 719 protected $fgcolor; 720 721 /** 722 * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise. 723 * @protected 724 */ 725 protected $listordered = array(); 726 727 /** 728 * HTML PARSER: array count list items on nested lists. 729 * @protected 730 */ 731 protected $listcount = array(); 732 733 /** 734 * HTML PARSER: current list nesting level. 735 * @protected 736 */ 737 protected $listnum = 0; 738 739 /** 740 * HTML PARSER: indent amount for lists. 741 * @protected 742 */ 743 protected $listindent = 0; 744 745 /** 746 * HTML PARSER: current list indententation level. 747 * @protected 748 */ 749 protected $listindentlevel = 0; 750 751 /** 752 * Current background color. 753 * @protected 754 */ 755 protected $bgcolor; 756 757 /** 758 * Temporary font size in points. 759 * @protected 760 */ 761 protected $tempfontsize = 10; 762 763 /** 764 * Spacer string for LI tags. 765 * @protected 766 */ 767 protected $lispacer = ''; 768 769 /** 770 * Default encoding. 771 * @protected 772 * @since 1.53.0.TC010 773 */ 774 protected $encoding = 'UTF-8'; 775 776 /** 777 * Boolean flag to indicate if the document language is Right-To-Left. 778 * @protected 779 * @since 2.0.000 780 */ 781 protected $rtl = false; 782 783 /** 784 * Boolean flag used to force RTL or LTR string direction. 785 * @protected 786 * @since 2.0.000 787 */ 788 protected $tmprtl = false; 789 790 // --- Variables used for document encryption: 791 792 /** 793 * IBoolean flag indicating whether document is protected. 794 * @protected 795 * @since 2.0.000 (2008-01-02) 796 */ 797 protected $encrypted; 798 799 /** 800 * Array containing encryption settings. 801 * @protected 802 * @since 5.0.005 (2010-05-11) 803 */ 804 protected $encryptdata = array(); 805 806 /** 807 * Last RC4 key encrypted (cached for optimisation). 808 * @protected 809 * @since 2.0.000 (2008-01-02) 810 */ 811 protected $last_enc_key; 812 813 /** 814 * Last RC4 computed key. 815 * @protected 816 * @since 2.0.000 (2008-01-02) 817 */ 818 protected $last_enc_key_c; 819 820 /** 821 * File ID (used on document trailer). 822 * @protected 823 * @since 5.0.005 (2010-05-12) 824 */ 825 protected $file_id; 826 827 // --- bookmark --- 828 829 /** 830 * Outlines for bookmark. 831 * @protected 832 * @since 2.1.002 (2008-02-12) 833 */ 834 protected $outlines = array(); 835 836 /** 837 * Outline root for bookmark. 838 * @protected 839 * @since 2.1.002 (2008-02-12) 840 */ 841 protected $OutlineRoot; 842 843 // --- javascript and form --- 844 845 /** 846 * Javascript code. 847 * @protected 848 * @since 2.1.002 (2008-02-12) 849 */ 850 protected $javascript = ''; 851 852 /** 853 * Javascript counter. 854 * @protected 855 * @since 2.1.002 (2008-02-12) 856 */ 857 protected $n_js; 858 859 /** 860 * line through state 861 * @protected 862 * @since 2.8.000 (2008-03-19) 863 */ 864 protected $linethrough; 865 866 /** 867 * Array with additional document-wide usage rights for the document. 868 * @protected 869 * @since 5.8.014 (2010-08-23) 870 */ 871 protected $ur = array(); 872 873 /** 874 * DPI (Dot Per Inch) Document Resolution (do not change). 875 * @protected 876 * @since 3.0.000 (2008-03-27) 877 */ 878 protected $dpi = 72; 879 880 /** 881 * Array of page numbers were a new page group was started (the page numbers are the keys of the array). 882 * @protected 883 * @since 3.0.000 (2008-03-27) 884 */ 885 protected $newpagegroup = array(); 886 887 /** 888 * Array that contains the number of pages in each page group. 889 * @protected 890 * @since 3.0.000 (2008-03-27) 891 */ 892 protected $pagegroups = array(); 893 894 /** 895 * Current page group number. 896 * @protected 897 * @since 3.0.000 (2008-03-27) 898 */ 899 protected $currpagegroup = 0; 900 901 /** 902 * Array of transparency objects and parameters. 903 * @protected 904 * @since 3.0.000 (2008-03-27) 905 */ 906 protected $extgstates; 907 908 /** 909 * Set the default JPEG compression quality (1-100). 910 * @protected 911 * @since 3.0.000 (2008-03-27) 912 */ 913 protected $jpeg_quality; 914 915 /** 916 * Default cell height ratio. 917 * @protected 918 * @since 3.0.014 (2008-05-23) 919 */ 920 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO; 921 922 /** 923 * PDF viewer preferences. 924 * @protected 925 * @since 3.1.000 (2008-06-09) 926 */ 927 protected $viewer_preferences; 928 929 /** 930 * A name object specifying how the document should be displayed when opened. 931 * @protected 932 * @since 3.1.000 (2008-06-09) 933 */ 934 protected $PageMode; 935 936 /** 937 * Array for storing gradient information. 938 * @protected 939 * @since 3.1.000 (2008-06-09) 940 */ 941 protected $gradients = array(); 942 943 /** 944 * Array used to store positions inside the pages buffer (keys are the page numbers). 945 * @protected 946 * @since 3.2.000 (2008-06-26) 947 */ 948 protected $intmrk = array(); 949 950 /** 951 * Array used to store positions inside the pages buffer (keys are the page numbers). 952 * @protected 953 * @since 5.7.000 (2010-08-03) 954 */ 955 protected $bordermrk = array(); 956 957 /** 958 * Array used to store page positions to track empty pages (keys are the page numbers). 959 * @protected 960 * @since 5.8.007 (2010-08-18) 961 */ 962 protected $emptypagemrk = array(); 963 964 /** 965 * Array used to store content positions inside the pages buffer (keys are the page numbers). 966 * @protected 967 * @since 4.6.021 (2009-07-20) 968 */ 969 protected $cntmrk = array(); 970 971 /** 972 * Array used to store footer positions of each page. 973 * @protected 974 * @since 3.2.000 (2008-07-01) 975 */ 976 protected $footerpos = array(); 977 978 /** 979 * Array used to store footer length of each page. 980 * @protected 981 * @since 4.0.014 (2008-07-29) 982 */ 983 protected $footerlen = array(); 984 985 /** 986 * Boolean flag to indicate if a new line is created. 987 * @protected 988 * @since 3.2.000 (2008-07-01) 989 */ 990 protected $newline = true; 991 992 /** 993 * End position of the latest inserted line. 994 * @protected 995 * @since 3.2.000 (2008-07-01) 996 */ 997 protected $endlinex = 0; 998 999 /** 1000 * PDF string for width value of the last line. 1001 * @protected 1002 * @since 4.0.006 (2008-07-16) 1003 */ 1004 protected $linestyleWidth = ''; 1005 1006 /** 1007 * PDF string for CAP value of the last line. 1008 * @protected 1009 * @since 4.0.006 (2008-07-16) 1010 */ 1011 protected $linestyleCap = '0 J'; 1012 1013 /** 1014 * PDF string for join value of the last line. 1015 * @protected 1016 * @since 4.0.006 (2008-07-16) 1017 */ 1018 protected $linestyleJoin = '0 j'; 1019 1020 /** 1021 * PDF string for dash value of the last line. 1022 * @protected 1023 * @since 4.0.006 (2008-07-16) 1024 */ 1025 protected $linestyleDash = '[] 0 d'; 1026 1027 /** 1028 * Boolean flag to indicate if marked-content sequence is open. 1029 * @protected 1030 * @since 4.0.013 (2008-07-28) 1031 */ 1032 protected $openMarkedContent = false; 1033 1034 /** 1035 * Count the latest inserted vertical spaces on HTML. 1036 * @protected 1037 * @since 4.0.021 (2008-08-24) 1038 */ 1039 protected $htmlvspace = 0; 1040 1041 /** 1042 * Array of Spot colors. 1043 * @protected 1044 * @since 4.0.024 (2008-09-12) 1045 */ 1046 protected $spot_colors = array(); 1047 1048 /** 1049 * Symbol used for HTML unordered list items. 1050 * @protected 1051 * @since 4.0.028 (2008-09-26) 1052 */ 1053 protected $lisymbol = ''; 1054 1055 /** 1056 * String used to mark the beginning and end of EPS image blocks. 1057 * @protected 1058 * @since 4.1.000 (2008-10-18) 1059 */ 1060 protected $epsmarker = 'x#!#EPS#!#x'; 1061 1062 /** 1063 * Array of transformation matrix. 1064 * @protected 1065 * @since 4.2.000 (2008-10-29) 1066 */ 1067 protected $transfmatrix = array(); 1068 1069 /** 1070 * Current key for transformation matrix. 1071 * @protected 1072 * @since 4.8.005 (2009-09-17) 1073 */ 1074 protected $transfmatrix_key = 0; 1075 1076 /** 1077 * Booklet mode for double-sided pages. 1078 * @protected 1079 * @since 4.2.000 (2008-10-29) 1080 */ 1081 protected $booklet = false; 1082 1083 /** 1084 * Epsilon value used for float calculations. 1085 * @protected 1086 * @since 4.2.000 (2008-10-29) 1087 */ 1088 protected $feps = 0.005; 1089 1090 /** 1091 * Array used for custom vertical spaces for HTML tags. 1092 * @protected 1093 * @since 4.2.001 (2008-10-30) 1094 */ 1095 protected $tagvspaces = array(); 1096 1097 /** 1098 * HTML PARSER: custom indent amount for lists. Negative value means disabled. 1099 * @protected 1100 * @since 4.2.007 (2008-11-12) 1101 */ 1102 protected $customlistindent = -1; 1103 1104 /** 1105 * Boolean flag to indicate if the border of the cell sides that cross the page should be removed. 1106 * @protected 1107 * @since 4.2.010 (2008-11-14) 1108 */ 1109 protected $opencell = true; 1110 1111 /** 1112 * Array of files to embedd. 1113 * @protected 1114 * @since 4.4.000 (2008-12-07) 1115 */ 1116 protected $embeddedfiles = array(); 1117 1118 /** 1119 * Boolean flag to indicate if we are inside a PRE tag. 1120 * @protected 1121 * @since 4.4.001 (2008-12-08) 1122 */ 1123 protected $premode = false; 1124 1125 /** 1126 * Array used to store positions of graphics transformation blocks inside the page buffer. 1127 * keys are the page numbers 1128 * @protected 1129 * @since 4.4.002 (2008-12-09) 1130 */ 1131 protected $transfmrk = array(); 1132 1133 /** 1134 * Default color for html links. 1135 * @protected 1136 * @since 4.4.003 (2008-12-09) 1137 */ 1138 protected $htmlLinkColorArray = array(0, 0, 255); 1139 1140 /** 1141 * Default font style to add to html links. 1142 * @protected 1143 * @since 4.4.003 (2008-12-09) 1144 */ 1145 protected $htmlLinkFontStyle = 'U'; 1146 1147 /** 1148 * Counts the number of pages. 1149 * @protected 1150 * @since 4.5.000 (2008-12-31) 1151 */ 1152 protected $numpages = 0; 1153 1154 /** 1155 * Array containing page lengths in bytes. 1156 * @protected 1157 * @since 4.5.000 (2008-12-31) 1158 */ 1159 protected $pagelen = array(); 1160 1161 /** 1162 * Counts the number of pages. 1163 * @protected 1164 * @since 4.5.000 (2008-12-31) 1165 */ 1166 protected $numimages = 0; 1167 1168 /** 1169 * Store the image keys. 1170 * @protected 1171 * @since 4.5.000 (2008-12-31) 1172 */ 1173 protected $imagekeys = array(); 1174 1175 /** 1176 * Length of the buffer in bytes. 1177 * @protected 1178 * @since 4.5.000 (2008-12-31) 1179 */ 1180 protected $bufferlen = 0; 1181 1182 /** 1183 * Counts the number of fonts. 1184 * @protected 1185 * @since 4.5.000 (2009-01-02) 1186 */ 1187 protected $numfonts = 0; 1188 1189 /** 1190 * Store the font keys. 1191 * @protected 1192 * @since 4.5.000 (2009-01-02) 1193 */ 1194 protected $fontkeys = array(); 1195 1196 /** 1197 * Store the font object IDs. 1198 * @protected 1199 * @since 4.8.001 (2009-09-09) 1200 */ 1201 protected $font_obj_ids = array(); 1202 1203 /** 1204 * Store the fage status (true when opened, false when closed). 1205 * @protected 1206 * @since 4.5.000 (2009-01-02) 1207 */ 1208 protected $pageopen = array(); 1209 1210 /** 1211 * Default monospace font. 1212 * @protected 1213 * @since 4.5.025 (2009-03-10) 1214 */ 1215 protected $default_monospaced_font = 'courier'; 1216 1217 /** 1218 * Cloned copy of the current class object. 1219 * @protected 1220 * @since 4.5.029 (2009-03-19) 1221 */ 1222 protected $objcopy; 1223 1224 /** 1225 * Array used to store the lengths of cache files. 1226 * @protected 1227 * @since 4.5.029 (2009-03-19) 1228 */ 1229 protected $cache_file_length = array(); 1230 1231 /** 1232 * Table header content to be repeated on each new page. 1233 * @protected 1234 * @since 4.5.030 (2009-03-20) 1235 */ 1236 protected $thead = ''; 1237 1238 /** 1239 * Margins used for table header. 1240 * @protected 1241 * @since 4.5.030 (2009-03-20) 1242 */ 1243 protected $theadMargins = array(); 1244 1245 /** 1246 * Boolean flag to enable document digital signature. 1247 * @protected 1248 * @since 4.6.005 (2009-04-24) 1249 */ 1250 protected $sign = false; 1251 1252 /** 1253 * Digital signature data. 1254 * @protected 1255 * @since 4.6.005 (2009-04-24) 1256 */ 1257 protected $signature_data = array(); 1258 1259 /** 1260 * Digital signature max length. 1261 * @protected 1262 * @since 4.6.005 (2009-04-24) 1263 */ 1264 protected $signature_max_length = 11742; 1265 1266 /** 1267 * Data for digital signature appearance. 1268 * @protected 1269 * @since 5.3.011 (2010-06-16) 1270 */ 1271 protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); 1272 1273 /** 1274 * Array of empty digital signature appearances. 1275 * @protected 1276 * @since 5.9.101 (2011-07-06) 1277 */ 1278 protected $empty_signature_appearance = array(); 1279 1280 /** 1281 * Boolean flag to enable document timestamping with TSA. 1282 * @protected 1283 * @since 6.0.085 (2014-06-19) 1284 */ 1285 protected $tsa_timestamp = false; 1286 1287 /** 1288 * Timestamping data. 1289 * @protected 1290 * @since 6.0.085 (2014-06-19) 1291 */ 1292 protected $tsa_data = array(); 1293 1294 /** 1295 * Regular expression used to find blank characters (required for word-wrapping). 1296 * @protected 1297 * @since 4.6.006 (2009-04-28) 1298 */ 1299 protected $re_spaces = '/[^\S\xa0]/'; 1300 1301 /** 1302 * Array of $re_spaces parts. 1303 * @protected 1304 * @since 5.5.011 (2010-07-09) 1305 */ 1306 protected $re_space = array('p' => '[^\S\xa0]', 'm' => ''); 1307 1308 /** 1309 * Digital signature object ID. 1310 * @protected 1311 * @since 4.6.022 (2009-06-23) 1312 */ 1313 protected $sig_obj_id = 0; 1314 1315 /** 1316 * ID of page objects. 1317 * @protected 1318 * @since 4.7.000 (2009-08-29) 1319 */ 1320 protected $page_obj_id = array(); 1321 1322 /** 1323 * List of form annotations IDs. 1324 * @protected 1325 * @since 4.8.000 (2009-09-07) 1326 */ 1327 protected $form_obj_id = array(); 1328 1329 /** 1330 * 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. 1331 * @protected 1332 * @since 4.8.000 (2009-09-07) 1333 */ 1334 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 1335 1336 /** 1337 * Javascript objects array. 1338 * @protected 1339 * @since 4.8.000 (2009-09-07) 1340 */ 1341 protected $js_objects = array(); 1342 1343 /** 1344 * Current form action (used during XHTML rendering). 1345 * @protected 1346 * @since 4.8.000 (2009-09-07) 1347 */ 1348 protected $form_action = ''; 1349 1350 /** 1351 * Current form encryption type (used during XHTML rendering). 1352 * @protected 1353 * @since 4.8.000 (2009-09-07) 1354 */ 1355 protected $form_enctype = 'application/x-www-form-urlencoded'; 1356 1357 /** 1358 * Current method to submit forms. 1359 * @protected 1360 * @since 4.8.000 (2009-09-07) 1361 */ 1362 protected $form_mode = 'post'; 1363 1364 /** 1365 * List of fonts used on form fields (fontname => fontkey). 1366 * @protected 1367 * @since 4.8.001 (2009-09-09) 1368 */ 1369 protected $annotation_fonts = array(); 1370 1371 /** 1372 * List of radio buttons parent objects. 1373 * @protected 1374 * @since 4.8.001 (2009-09-09) 1375 */ 1376 protected $radiobutton_groups = array(); 1377 1378 /** 1379 * List of radio group objects IDs. 1380 * @protected 1381 * @since 4.8.001 (2009-09-09) 1382 */ 1383 protected $radio_groups = array(); 1384 1385 /** 1386 * Text indentation value (used for text-indent CSS attribute). 1387 * @protected 1388 * @since 4.8.006 (2009-09-23) 1389 */ 1390 protected $textindent = 0; 1391 1392 /** 1393 * Store page number when startTransaction() is called. 1394 * @protected 1395 * @since 4.8.006 (2009-09-23) 1396 */ 1397 protected $start_transaction_page = 0; 1398 1399 /** 1400 * Store Y position when startTransaction() is called. 1401 * @protected 1402 * @since 4.9.001 (2010-03-28) 1403 */ 1404 protected $start_transaction_y = 0; 1405 1406 /** 1407 * True when we are printing the thead section on a new page. 1408 * @protected 1409 * @since 4.8.027 (2010-01-25) 1410 */ 1411 protected $inthead = false; 1412 1413 /** 1414 * Array of column measures (width, space, starting Y position). 1415 * @protected 1416 * @since 4.9.001 (2010-03-28) 1417 */ 1418 protected $columns = array(); 1419 1420 /** 1421 * Number of colums. 1422 * @protected 1423 * @since 4.9.001 (2010-03-28) 1424 */ 1425 protected $num_columns = 1; 1426 1427 /** 1428 * Current column number. 1429 * @protected 1430 * @since 4.9.001 (2010-03-28) 1431 */ 1432 protected $current_column = 0; 1433 1434 /** 1435 * Starting page for columns. 1436 * @protected 1437 * @since 4.9.001 (2010-03-28) 1438 */ 1439 protected $column_start_page = 0; 1440 1441 /** 1442 * Maximum page and column selected. 1443 * @protected 1444 * @since 5.8.000 (2010-08-11) 1445 */ 1446 protected $maxselcol = array('page' => 0, 'column' => 0); 1447 1448 /** 1449 * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding. 1450 * @protected 1451 * @since 5.8.000 (2010-08-11) 1452 */ 1453 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 1454 1455 /** 1456 * 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. 1457 * @protected 1458 * @since 4.9.008 (2010-04-03) 1459 */ 1460 protected $textrendermode = 0; 1461 1462 /** 1463 * Text stroke width in doc units. 1464 * @protected 1465 * @since 4.9.008 (2010-04-03) 1466 */ 1467 protected $textstrokewidth = 0; 1468 1469 /** 1470 * Current stroke color. 1471 * @protected 1472 * @since 4.9.008 (2010-04-03) 1473 */ 1474 protected $strokecolor; 1475 1476 /** 1477 * Default unit of measure for document. 1478 * @protected 1479 * @since 5.0.000 (2010-04-22) 1480 */ 1481 protected $pdfunit = 'mm'; 1482 1483 /** 1484 * Boolean flag true when we are on TOC (Table Of Content) page. 1485 * @protected 1486 */ 1487 protected $tocpage = false; 1488 1489 /** 1490 * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library. 1491 * @protected 1492 * @since 5.0.000 (2010-04-26) 1493 */ 1494 protected $rasterize_vector_images = false; 1495 1496 /** 1497 * Boolean flag: if true enables font subsetting by default. 1498 * @protected 1499 * @since 5.3.002 (2010-06-07) 1500 */ 1501 protected $font_subsetting = true; 1502 1503 /** 1504 * Array of default graphic settings. 1505 * @protected 1506 * @since 5.5.008 (2010-07-02) 1507 */ 1508 protected $default_graphic_vars = array(); 1509 1510 /** 1511 * Array of XObjects. 1512 * @protected 1513 * @since 5.8.014 (2010-08-23) 1514 */ 1515 protected $xobjects = array(); 1516 1517 /** 1518 * Boolean value true when we are inside an XObject. 1519 * @protected 1520 * @since 5.8.017 (2010-08-24) 1521 */ 1522 protected $inxobj = false; 1523 1524 /** 1525 * Current XObject ID. 1526 * @protected 1527 * @since 5.8.017 (2010-08-24) 1528 */ 1529 protected $xobjid = ''; 1530 1531 /** 1532 * Percentage of character stretching. 1533 * @protected 1534 * @since 5.9.000 (2010-09-29) 1535 */ 1536 protected $font_stretching = 100; 1537 1538 /** 1539 * Increases or decreases the space between characters in a text by the specified amount (tracking). 1540 * @protected 1541 * @since 5.9.000 (2010-09-29) 1542 */ 1543 protected $font_spacing = 0; 1544 1545 /** 1546 * Array of no-write regions. 1547 * ('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) 1548 * @protected 1549 * @since 5.9.003 (2010-10-14) 1550 */ 1551 protected $page_regions = array(); 1552 1553 /** 1554 * Boolean value true when page region check is active. 1555 * @protected 1556 */ 1557 protected $check_page_regions = true; 1558 1559 /** 1560 * Array of PDF layers data. 1561 * @protected 1562 * @since 5.9.102 (2011-07-13) 1563 */ 1564 protected $pdflayers = array(); 1565 1566 /** 1567 * A dictionary of names and corresponding destinations (Dests key on document Catalog). 1568 * @protected 1569 * @since 5.9.097 (2011-06-23) 1570 */ 1571 protected $dests = array(); 1572 1573 /** 1574 * Object ID for Named Destinations 1575 * @protected 1576 * @since 5.9.097 (2011-06-23) 1577 */ 1578 protected $n_dests; 1579 1580 /** 1581 * Embedded Files Names 1582 * @protected 1583 * @since 5.9.204 (2013-01-23) 1584 */ 1585 protected $efnames = array(); 1586 1587 /** 1588 * Directory used for the last SVG image. 1589 * @protected 1590 * @since 5.0.000 (2010-05-05) 1591 */ 1592 protected $svgdir = ''; 1593 1594 /** 1595 * Deafult unit of measure for SVG. 1596 * @protected 1597 * @since 5.0.000 (2010-05-02) 1598 */ 1599 protected $svgunit = 'px'; 1600 1601 /** 1602 * Array of SVG gradients. 1603 * @protected 1604 * @since 5.0.000 (2010-05-02) 1605 */ 1606 protected $svggradients = array(); 1607 1608 /** 1609 * ID of last SVG gradient. 1610 * @protected 1611 * @since 5.0.000 (2010-05-02) 1612 */ 1613 protected $svggradientid = 0; 1614 1615 /** 1616 * Boolean value true when in SVG defs group. 1617 * @protected 1618 * @since 5.0.000 (2010-05-02) 1619 */ 1620 protected $svgdefsmode = false; 1621 1622 /** 1623 * Array of SVG defs. 1624 * @protected 1625 * @since 5.0.000 (2010-05-02) 1626 */ 1627 protected $svgdefs = array(); 1628 1629 /** 1630 * Boolean value true when in SVG clipPath tag. 1631 * @protected 1632 * @since 5.0.000 (2010-04-26) 1633 */ 1634 protected $svgclipmode = false; 1635 1636 /** 1637 * Array of SVG clipPath commands. 1638 * @protected 1639 * @since 5.0.000 (2010-05-02) 1640 */ 1641 protected $svgclippaths = array(); 1642 1643 /** 1644 * Array of SVG clipPath tranformation matrix. 1645 * @protected 1646 * @since 5.8.022 (2010-08-31) 1647 */ 1648 protected $svgcliptm = array(); 1649 1650 /** 1651 * ID of last SVG clipPath. 1652 * @protected 1653 * @since 5.0.000 (2010-05-02) 1654 */ 1655 protected $svgclipid = 0; 1656 1657 /** 1658 * SVG text. 1659 * @protected 1660 * @since 5.0.000 (2010-05-02) 1661 */ 1662 protected $svgtext = ''; 1663 1664 /** 1665 * SVG text properties. 1666 * @protected 1667 * @since 5.8.013 (2010-08-23) 1668 */ 1669 protected $svgtextmode = array(); 1670 1671 /** 1672 * Array of SVG properties. 1673 * @protected 1674 * @since 5.0.000 (2010-05-02) 1675 */ 1676 protected $svgstyles = array(array( 1677 'alignment-baseline' => 'auto', 1678 'baseline-shift' => 'baseline', 1679 'clip' => 'auto', 1680 'clip-path' => 'none', 1681 'clip-rule' => 'nonzero', 1682 'color' => 'black', 1683 'color-interpolation' => 'sRGB', 1684 'color-interpolation-filters' => 'linearRGB', 1685 'color-profile' => 'auto', 1686 'color-rendering' => 'auto', 1687 'cursor' => 'auto', 1688 'direction' => 'ltr', 1689 'display' => 'inline', 1690 'dominant-baseline' => 'auto', 1691 'enable-background' => 'accumulate', 1692 'fill' => 'black', 1693 'fill-opacity' => 1, 1694 'fill-rule' => 'nonzero', 1695 'filter' => 'none', 1696 'flood-color' => 'black', 1697 'flood-opacity' => 1, 1698 'font' => '', 1699 'font-family' => 'helvetica', 1700 'font-size' => 'medium', 1701 'font-size-adjust' => 'none', 1702 'font-stretch' => 'normal', 1703 'font-style' => 'normal', 1704 'font-variant' => 'normal', 1705 'font-weight' => 'normal', 1706 'glyph-orientation-horizontal' => '0deg', 1707 'glyph-orientation-vertical' => 'auto', 1708 'image-rendering' => 'auto', 1709 'kerning' => 'auto', 1710 'letter-spacing' => 'normal', 1711 'lighting-color' => 'white', 1712 'marker' => '', 1713 'marker-end' => 'none', 1714 'marker-mid' => 'none', 1715 'marker-start' => 'none', 1716 'mask' => 'none', 1717 'opacity' => 1, 1718 'overflow' => 'auto', 1719 'pointer-events' => 'visiblePainted', 1720 'shape-rendering' => 'auto', 1721 'stop-color' => 'black', 1722 'stop-opacity' => 1, 1723 'stroke' => 'none', 1724 'stroke-dasharray' => 'none', 1725 'stroke-dashoffset' => 0, 1726 'stroke-linecap' => 'butt', 1727 'stroke-linejoin' => 'miter', 1728 'stroke-miterlimit' => 4, 1729 'stroke-opacity' => 1, 1730 'stroke-width' => 1, 1731 'text-anchor' => 'start', 1732 'text-decoration' => 'none', 1733 'text-rendering' => 'auto', 1734 'unicode-bidi' => 'normal', 1735 'visibility' => 'visible', 1736 'word-spacing' => 'normal', 1737 'writing-mode' => 'lr-tb', 1738 'text-color' => 'black', 1739 'transfmatrix' => array(1, 0, 0, 1, 0, 0) 1740 )); 1741 1742 /** 1743 * If true force sRGB color profile for all document. 1744 * @protected 1745 * @since 5.9.121 (2011-09-28) 1746 */ 1747 protected $force_srgb = false; 1748 1749 /** 1750 * If true set the document to PDF/A mode. 1751 * @protected 1752 * @since 5.9.121 (2011-09-27) 1753 */ 1754 protected $pdfa_mode = false; 1755 1756 /** 1757 * version of PDF/A mode (1 - 3). 1758 * @protected 1759 * @since 6.2.26 (2019-03-12) 1760 */ 1761 protected $pdfa_version = 1; 1762 1763 /** 1764 * Document creation date-time 1765 * @protected 1766 * @since 5.9.152 (2012-03-22) 1767 */ 1768 protected $doc_creation_timestamp; 1769 1770 /** 1771 * Document modification date-time 1772 * @protected 1773 * @since 5.9.152 (2012-03-22) 1774 */ 1775 protected $doc_modification_timestamp; 1776 1777 /** 1778 * Custom XMP data. 1779 * @protected 1780 * @since 5.9.128 (2011-10-06) 1781 */ 1782 protected $custom_xmp = ''; 1783 1784 /** 1785 * Custom XMP RDF data. 1786 * @protected 1787 * @since 6.3.0 (2019-09-19) 1788 */ 1789 protected $custom_xmp_rdf = ''; 1790 1791 /** 1792 * Overprint mode array. 1793 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 1794 * @protected 1795 * @since 5.9.152 (2012-03-23) 1796 */ 1797 protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0); 1798 1799 /** 1800 * Alpha mode array. 1801 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 1802 * @protected 1803 * @since 5.9.152 (2012-03-23) 1804 */ 1805 protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false); 1806 1807 /** 1808 * Define the page boundaries boxes to be set on document. 1809 * @protected 1810 * @since 5.9.152 (2012-03-23) 1811 */ 1812 protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); 1813 1814 /** 1815 * If true print TCPDF meta link. 1816 * @protected 1817 * @since 5.9.152 (2012-03-23) 1818 */ 1819 protected $tcpdflink = true; 1820 1821 /** 1822 * Cache array for computed GD gamma values. 1823 * @protected 1824 * @since 5.9.1632 (2012-06-05) 1825 */ 1826 protected $gdgammacache = array(); 1827 1828 /** 1829 * Cache array for file content 1830 * @protected 1831 * @var array 1832 * @sinde 6.3.5 (2020-09-28) 1833 */ 1834 protected $fileContentCache = array(); 1835 1836 /** 1837 * Whether to allow local file path in image html tags, when prefixed with file:// 1838 * 1839 * @var bool 1840 * @protected 1841 * @since 6.4 (2020-07-23) 1842 */ 1843 protected $allowLocalFiles = false; 1844 1845 //------------------------------------------------------------ 1846 // METHODS 1847 //------------------------------------------------------------ 1848 1849 /** 1850 * This is the class constructor. 1851 * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes). 1852 * 1853 * @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> 1854 * @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. 1855 * @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(). 1856 * @param $unicode (boolean) TRUE means that the input text is unicode (default = true) 1857 * @param $encoding (string) Charset encoding (used only when converting back html entities); default is UTF-8. 1858 * @param $diskcache (boolean) DEPRECATED FEATURE 1859 * @param $pdfa (integer) If not false, set the document to PDF/A mode and the good version (1 or 3). 1860 * @public 1861 * @see getPageSizeFromFormat(), setPageFormat() 1862 */ 1863 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) { 1864 // set file ID for trailer 1865 $serformat = (is_array($format) ? json_encode($format) : $format); 1866 $this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding)); 1867 $this->font_obj_ids = array(); 1868 $this->page_obj_id = array(); 1869 $this->form_obj_id = array(); 1870 1871 // set pdf/a mode 1872 if ($pdfa != false) { 1873 $this->pdfa_mode = true; 1874 $this->pdfa_version = $pdfa; // 1 or 3 1875 } else 1876 $this->pdfa_mode = false; 1877 1878 $this->force_srgb = false; 1879 // set language direction 1880 $this->rtl = false; 1881 $this->tmprtl = false; 1882 // some checks 1883 $this->_dochecks(); 1884 // initialization of properties 1885 $this->isunicode = $unicode; 1886 $this->page = 0; 1887 $this->transfmrk[0] = array(); 1888 $this->pagedim = array(); 1889 $this->n = 2; 1890 $this->buffer = ''; 1891 $this->pages = array(); 1892 $this->state = 0; 1893 $this->fonts = array(); 1894 $this->FontFiles = array(); 1895 $this->diffs = array(); 1896 $this->images = array(); 1897 $this->links = array(); 1898 $this->gradients = array(); 1899 $this->InFooter = false; 1900 $this->lasth = 0; 1901 $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica'; 1902 $this->FontStyle = ''; 1903 $this->FontSizePt = 12; 1904 $this->underline = false; 1905 $this->overline = false; 1906 $this->linethrough = false; 1907 $this->DrawColor = '0 G'; 1908 $this->FillColor = '0 g'; 1909 $this->TextColor = '0 g'; 1910 $this->ColorFlag = false; 1911 $this->pdflayers = array(); 1912 // encryption values 1913 $this->encrypted = false; 1914 $this->last_enc_key = ''; 1915 // standard Unicode fonts 1916 $this->CoreFonts = array( 1917 'courier'=>'Courier', 1918 'courierB'=>'Courier-Bold', 1919 'courierI'=>'Courier-Oblique', 1920 'courierBI'=>'Courier-BoldOblique', 1921 'helvetica'=>'Helvetica', 1922 'helveticaB'=>'Helvetica-Bold', 1923 'helveticaI'=>'Helvetica-Oblique', 1924 'helveticaBI'=>'Helvetica-BoldOblique', 1925 'times'=>'Times-Roman', 1926 'timesB'=>'Times-Bold', 1927 'timesI'=>'Times-Italic', 1928 'timesBI'=>'Times-BoldItalic', 1929 'symbol'=>'Symbol', 1930 'zapfdingbats'=>'ZapfDingbats' 1931 ); 1932 // set scale factor 1933 $this->setPageUnit($unit); 1934 // set page format and orientation 1935 $this->setPageFormat($format, $orientation); 1936 // page margins (1 cm) 1937 $margin = 28.35 / $this->k; 1938 $this->SetMargins($margin, $margin); 1939 $this->clMargin = $this->lMargin; 1940 $this->crMargin = $this->rMargin; 1941 // internal cell padding 1942 $cpadding = $margin / 10; 1943 $this->setCellPaddings($cpadding, 0, $cpadding, 0); 1944 // cell margins 1945 $this->setCellMargins(0, 0, 0, 0); 1946 // line width (0.2 mm) 1947 $this->LineWidth = 0.57 / $this->k; 1948 $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k)); 1949 $this->linestyleCap = '0 J'; 1950 $this->linestyleJoin = '0 j'; 1951 $this->linestyleDash = '[] 0 d'; 1952 // automatic page break 1953 $this->SetAutoPageBreak(true, (2 * $margin)); 1954 // full width display mode 1955 $this->SetDisplayMode('fullwidth'); 1956 // compression 1957 $this->SetCompression(); 1958 // set default PDF version number 1959 $this->setPDFVersion(); 1960 $this->tcpdflink = true; 1961 $this->encoding = $encoding; 1962 $this->HREF = array(); 1963 $this->getFontsList(); 1964 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0); 1965 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0); 1966 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255); 1967 $this->extgstates = array(); 1968 $this->setTextShadow(); 1969 // signature 1970 $this->sign = false; 1971 $this->tsa_timestamp = false; 1972 $this->tsa_data = array(); 1973 $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature'); 1974 $this->empty_signature_appearance = array(); 1975 // user's rights 1976 $this->ur['enabled'] = false; 1977 $this->ur['document'] = '/FullSave'; 1978 $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export'; 1979 $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate'; 1980 $this->ur['signature'] = '/Modify'; 1981 $this->ur['ef'] = '/Create/Delete/Modify/Import'; 1982 $this->ur['formex'] = ''; 1983 // set default JPEG quality 1984 $this->jpeg_quality = 75; 1985 // initialize some settings 1986 TCPDF_FONTS::utf8Bidi(array(), '', false, $this->isunicode, $this->CurrentFont); 1987 // set default font 1988 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 1989 $this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt)); 1990 $this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt)); 1991 // check if PCRE Unicode support is enabled 1992 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) { 1993 // PCRE unicode support is turned ON 1994 // \s : any whitespace character 1995 // \p{Z} : any separator 1996 // \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words. 1997 // \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0) 1998 //$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u'); 1999 $this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u'); 2000 } else { 2001 // PCRE unicode support is turned OFF 2002 $this->setSpacesRE('/[^\S\xa0]/'); 2003 } 2004 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 2005 // set document creation and modification timestamp 2006 $this->doc_creation_timestamp = time(); 2007 $this->doc_modification_timestamp = $this->doc_creation_timestamp; 2008 // get default graphic vars 2009 $this->default_graphic_vars = $this->getGraphicVars(); 2010 $this->header_xobj_autoreset = false; 2011 $this->custom_xmp = ''; 2012 $this->custom_xmp_rdf = ''; 2013 // Call cleanup method after script execution finishes or exit() is called. 2014 // NOTE: This will not be executed if the process is killed with a SIGTERM or SIGKILL signal. 2015 register_shutdown_function(array($this, '_destroy'), true); 2016 } 2017 2018 /** 2019 * Default destructor. 2020 * @public 2021 * @since 1.53.0.TC016 2022 */ 2023 public function __destruct() { 2024 // cleanup 2025 $this->_destroy(true); 2026 } 2027 2028 /** 2029 * Set the units of measure for the document. 2030 * @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. 2031 * @public 2032 * @since 3.0.015 (2008-06-06) 2033 */ 2034 public function setPageUnit($unit) { 2035 $unit = strtolower($unit); 2036 //Set scale factor 2037 switch ($unit) { 2038 // points 2039 case 'px': 2040 case 'pt': { 2041 $this->k = 1; 2042 break; 2043 } 2044 // millimeters 2045 case 'mm': { 2046 $this->k = $this->dpi / 25.4; 2047 break; 2048 } 2049 // centimeters 2050 case 'cm': { 2051 $this->k = $this->dpi / 2.54; 2052 break; 2053 } 2054 // inches 2055 case 'in': { 2056 $this->k = $this->dpi; 2057 break; 2058 } 2059 // unsupported unit 2060 default : { 2061 $this->Error('Incorrect unit: '.$unit); 2062 break; 2063 } 2064 } 2065 $this->pdfunit = $unit; 2066 if (isset($this->CurOrientation)) { 2067 $this->setPageOrientation($this->CurOrientation); 2068 } 2069 } 2070 2071 /** 2072 * Change the format of the current page 2073 * @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> 2074 * <li>['format'] = page format name (one of the above);</li> 2075 * <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> 2076 * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li> 2077 * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li> 2078 * <li>['MediaBox']['llx'] : lower-left x coordinate</li> 2079 * <li>['MediaBox']['lly'] : lower-left y coordinate</li> 2080 * <li>['MediaBox']['urx'] : upper-right x coordinate</li> 2081 * <li>['MediaBox']['ury'] : upper-right y coordinate</li> 2082 * <li>['CropBox'] : the visible region of default user space:</li> 2083 * <li>['CropBox']['llx'] : lower-left x coordinate</li> 2084 * <li>['CropBox']['lly'] : lower-left y coordinate</li> 2085 * <li>['CropBox']['urx'] : upper-right x coordinate</li> 2086 * <li>['CropBox']['ury'] : upper-right y coordinate</li> 2087 * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li> 2088 * <li>['BleedBox']['llx'] : lower-left x coordinate</li> 2089 * <li>['BleedBox']['lly'] : lower-left y coordinate</li> 2090 * <li>['BleedBox']['urx'] : upper-right x coordinate</li> 2091 * <li>['BleedBox']['ury'] : upper-right y coordinate</li> 2092 * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li> 2093 * <li>['TrimBox']['llx'] : lower-left x coordinate</li> 2094 * <li>['TrimBox']['lly'] : lower-left y coordinate</li> 2095 * <li>['TrimBox']['urx'] : upper-right x coordinate</li> 2096 * <li>['TrimBox']['ury'] : upper-right y coordinate</li> 2097 * <li>['ArtBox'] : the extent of the page's meaningful content:</li> 2098 * <li>['ArtBox']['llx'] : lower-left x coordinate</li> 2099 * <li>['ArtBox']['lly'] : lower-left y coordinate</li> 2100 * <li>['ArtBox']['urx'] : upper-right x coordinate</li> 2101 * <li>['ArtBox']['ury'] : upper-right y coordinate</li> 2102 * <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> 2103 * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li> 2104 * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li> 2105 * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li> 2106 * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li> 2107 * <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> 2108 * <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> 2109 * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li> 2110 * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li> 2111 * <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> 2112 * <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> 2113 * <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> 2114 * <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> 2115 * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li> 2116 * </ul> 2117 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul> 2118 * <li>P or Portrait (default)</li> 2119 * <li>L or Landscape</li> 2120 * <li>'' (empty string) for automatic orientation</li> 2121 * </ul> 2122 * @protected 2123 * @since 3.0.015 (2008-06-06) 2124 * @see getPageSizeFromFormat() 2125 */ 2126 protected function setPageFormat($format, $orientation='P') { 2127 if (!empty($format) AND isset($this->pagedim[$this->page])) { 2128 // remove inherited values 2129 unset($this->pagedim[$this->page]); 2130 } 2131 if (is_string($format)) { 2132 // get page measures from format name 2133 $pf = TCPDF_STATIC::getPageSizeFromFormat($format); 2134 $this->fwPt = $pf[0]; 2135 $this->fhPt = $pf[1]; 2136 } else { 2137 // the boundaries of the physical medium on which the page shall be displayed or printed 2138 if (isset($format['MediaBox'])) { 2139 $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); 2140 $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k); 2141 $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k); 2142 } else { 2143 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) { 2144 $pf = array(($format[0] * $this->k), ($format[1] * $this->k)); 2145 } else { 2146 if (!isset($format['format'])) { 2147 // default value 2148 $format['format'] = 'A4'; 2149 } 2150 $pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']); 2151 } 2152 $this->fwPt = $pf[0]; 2153 $this->fhPt = $pf[1]; 2154 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim); 2155 } 2156 // the visible region of default user space 2157 if (isset($format['CropBox'])) { 2158 $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); 2159 } 2160 // the region to which the contents of the page shall be clipped when output in a production environment 2161 if (isset($format['BleedBox'])) { 2162 $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); 2163 } 2164 // the intended dimensions of the finished page after trimming 2165 if (isset($format['TrimBox'])) { 2166 $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); 2167 } 2168 // the page's meaningful content (including potential white space) 2169 if (isset($format['ArtBox'])) { 2170 $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); 2171 } 2172 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries 2173 if (isset($format['BoxColorInfo'])) { 2174 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo']; 2175 } 2176 if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) { 2177 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. 2178 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']); 2179 } 2180 if (isset($format['PZ'])) { 2181 // The page's preferred zoom (magnification) factor 2182 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']); 2183 } 2184 if (isset($format['trans'])) { 2185 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation 2186 if (isset($format['trans']['Dur'])) { 2187 // The page's display duration 2188 $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']); 2189 } 2190 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade'); 2191 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) { 2192 // The transition style that shall be used when moving to this page from another during a presentation 2193 $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S']; 2194 $valid_effect = array('Split', 'Blinds'); 2195 $valid_vals = array('H', 'V'); 2196 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) { 2197 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm']; 2198 } 2199 $valid_effect = array('Split', 'Box', 'Fly'); 2200 $valid_vals = array('I', 'O'); 2201 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) { 2202 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M']; 2203 } 2204 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push'); 2205 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) { 2206 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe')) 2207 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter')) 2208 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) { 2209 $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']); 2210 } 2211 } 2212 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) { 2213 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']); 2214 } 2215 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) { 2216 $this->pagedim[$this->page]['trans']['B'] = 'true'; 2217 } 2218 } else { 2219 $this->pagedim[$this->page]['trans']['S'] = 'R'; 2220 } 2221 if (isset($format['trans']['D'])) { 2222 // The duration of the transition effect, in seconds 2223 $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']); 2224 } else { 2225 $this->pagedim[$this->page]['trans']['D'] = 1; 2226 } 2227 } 2228 } 2229 $this->setPageOrientation($orientation); 2230 } 2231 2232 /** 2233 * Set page orientation. 2234 * @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> 2235 * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off. 2236 * @param $bottommargin (float) bottom margin of the page. 2237 * @public 2238 * @since 3.0.015 (2008-06-06) 2239 */ 2240 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') { 2241 if (!isset($this->pagedim[$this->page]['MediaBox'])) { 2242 // the boundaries of the physical medium on which the page shall be displayed or printed 2243 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim); 2244 } 2245 if (!isset($this->pagedim[$this->page]['CropBox'])) { 2246 // the visible region of default user space 2247 $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); 2248 } 2249 if (!isset($this->pagedim[$this->page]['BleedBox'])) { 2250 // the region to which the contents of the page shall be clipped when output in a production environment 2251 $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); 2252 } 2253 if (!isset($this->pagedim[$this->page]['TrimBox'])) { 2254 // the intended dimensions of the finished page after trimming 2255 $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); 2256 } 2257 if (!isset($this->pagedim[$this->page]['ArtBox'])) { 2258 // the page's meaningful content (including potential white space) 2259 $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); 2260 } 2261 if (!isset($this->pagedim[$this->page]['Rotate'])) { 2262 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. 2263 $this->pagedim[$this->page]['Rotate'] = 0; 2264 } 2265 if (!isset($this->pagedim[$this->page]['PZ'])) { 2266 // The page's preferred zoom (magnification) factor 2267 $this->pagedim[$this->page]['PZ'] = 1; 2268 } 2269 if ($this->fwPt > $this->fhPt) { 2270 // landscape 2271 $default_orientation = 'L'; 2272 } else { 2273 // portrait 2274 $default_orientation = 'P'; 2275 } 2276 $valid_orientations = array('P', 'L'); 2277 if (empty($orientation)) { 2278 $orientation = $default_orientation; 2279 } else { 2280 $orientation = strtoupper($orientation[0]); 2281 } 2282 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) { 2283 $this->CurOrientation = $orientation; 2284 $this->wPt = $this->fhPt; 2285 $this->hPt = $this->fwPt; 2286 } else { 2287 $this->CurOrientation = $default_orientation; 2288 $this->wPt = $this->fwPt; 2289 $this->hPt = $this->fhPt; 2290 } 2291 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){ 2292 // swap X and Y coordinates (change page orientation) 2293 $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim); 2294 } 2295 $this->w = ($this->wPt / $this->k); 2296 $this->h = ($this->hPt / $this->k); 2297 if (TCPDF_STATIC::empty_string($autopagebreak)) { 2298 if (isset($this->AutoPageBreak)) { 2299 $autopagebreak = $this->AutoPageBreak; 2300 } else { 2301 $autopagebreak = true; 2302 } 2303 } 2304 if (TCPDF_STATIC::empty_string($bottommargin)) { 2305 if (isset($this->bMargin)) { 2306 $bottommargin = $this->bMargin; 2307 } else { 2308 // default value = 2 cm 2309 $bottommargin = 2 * 28.35 / $this->k; 2310 } 2311 } 2312 $this->SetAutoPageBreak($autopagebreak, $bottommargin); 2313 // store page dimensions 2314 $this->pagedim[$this->page]['w'] = $this->wPt; 2315 $this->pagedim[$this->page]['h'] = $this->hPt; 2316 $this->pagedim[$this->page]['wk'] = $this->w; 2317 $this->pagedim[$this->page]['hk'] = $this->h; 2318 $this->pagedim[$this->page]['tm'] = $this->tMargin; 2319 $this->pagedim[$this->page]['bm'] = $bottommargin; 2320 $this->pagedim[$this->page]['lm'] = $this->lMargin; 2321 $this->pagedim[$this->page]['rm'] = $this->rMargin; 2322 $this->pagedim[$this->page]['pb'] = $autopagebreak; 2323 $this->pagedim[$this->page]['or'] = $this->CurOrientation; 2324 $this->pagedim[$this->page]['olm'] = $this->original_lMargin; 2325 $this->pagedim[$this->page]['orm'] = $this->original_rMargin; 2326 } 2327 2328 /** 2329 * Set regular expression to detect withespaces or word separators. 2330 * The pattern delimiter must be the forward-slash character "/". 2331 * Some example patterns are: 2332 * <pre> 2333 * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/" 2334 * Unicode and PCRE unicode support: "/(?!\xa0)[\s\p{Z}]/u" 2335 * Unicode and PCRE unicode support in Chinese mode: "/(?!\xa0)[\s\p{Z}\p{Lo}]/u" 2336 * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"): 2337 * \s : any whitespace character 2338 * \p{Z} : any separator 2339 * \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words. 2340 * \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0) 2341 * </pre> 2342 * @param $re (string) regular expression (leave empty for default). 2343 * @public 2344 * @since 4.6.016 (2009-06-15) 2345 */ 2346 public function setSpacesRE($re='/[^\S\xa0]/') { 2347 $this->re_spaces = $re; 2348 $re_parts = explode('/', $re); 2349 // get pattern parts 2350 $this->re_space = array(); 2351 if (isset($re_parts[1]) AND !empty($re_parts[1])) { 2352 $this->re_space['p'] = $re_parts[1]; 2353 } else { 2354 $this->re_space['p'] = '[\s]'; 2355 } 2356 // set pattern modifiers 2357 if (isset($re_parts[2]) AND !empty($re_parts[2])) { 2358 $this->re_space['m'] = $re_parts[2]; 2359 } else { 2360 $this->re_space['m'] = ''; 2361 } 2362 } 2363 2364 /** 2365 * Enable or disable Right-To-Left language mode 2366 * @param $enable (Boolean) if true enable Right-To-Left language mode. 2367 * @param $resetx (Boolean) if true reset the X position on direction change. 2368 * @public 2369 * @since 2.0.000 (2008-01-03) 2370 */ 2371 public function setRTL($enable, $resetx=true) { 2372 $enable = $enable ? true : false; 2373 $resetx = ($resetx AND ($enable != $this->rtl)); 2374 $this->rtl = $enable; 2375 $this->tmprtl = false; 2376 if ($resetx) { 2377 $this->Ln(0); 2378 } 2379 } 2380 2381 /** 2382 * Return the RTL status 2383 * @return boolean 2384 * @public 2385 * @since 4.0.012 (2008-07-24) 2386 */ 2387 public function getRTL() { 2388 return $this->rtl; 2389 } 2390 2391 /** 2392 * Force temporary RTL language direction 2393 * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL 2394 * @public 2395 * @since 2.1.000 (2008-01-09) 2396 */ 2397 public function setTempRTL($mode) { 2398 $newmode = false; 2399 switch (strtoupper($mode)) { 2400 case 'LTR': 2401 case 'L': { 2402 if ($this->rtl) { 2403 $newmode = 'L'; 2404 } 2405 break; 2406 } 2407 case 'RTL': 2408 case 'R': { 2409 if (!$this->rtl) { 2410 $newmode = 'R'; 2411 } 2412 break; 2413 } 2414 case false: 2415 default: { 2416 $newmode = false; 2417 break; 2418 } 2419 } 2420 $this->tmprtl = $newmode; 2421 } 2422 2423 /** 2424 * Return the current temporary RTL status 2425 * @return boolean 2426 * @public 2427 * @since 4.8.014 (2009-11-04) 2428 */ 2429 public function isRTLTextDir() { 2430 return ($this->rtl OR ($this->tmprtl == 'R')); 2431 } 2432 2433 /** 2434 * Set the last cell height. 2435 * @param $h (float) cell height. 2436 * @author Nicola Asuni 2437 * @public 2438 * @since 1.53.0.TC034 2439 */ 2440 public function setLastH($h) { 2441 $this->lasth = $h; 2442 } 2443 2444 /** 2445 * Return the cell height 2446 * @param $fontsize (int) Font size in internal units 2447 * @param $padding (boolean) If true add cell padding 2448 * @public 2449 */ 2450 public function getCellHeight($fontsize, $padding=TRUE) { 2451 $height = ($fontsize * $this->cell_height_ratio); 2452 if ($padding) { 2453 $height += ($this->cell_padding['T'] + $this->cell_padding['B']); 2454 } 2455 return round($height, 6); 2456 } 2457 2458 /** 2459 * Reset the last cell height. 2460 * @public 2461 * @since 5.9.000 (2010-10-03) 2462 */ 2463 public function resetLastH() { 2464 $this->lasth = $this->getCellHeight($this->FontSize); 2465 } 2466 2467 /** 2468 * Get the last cell height. 2469 * @return last cell height 2470 * @public 2471 * @since 4.0.017 (2008-08-05) 2472 */ 2473 public function getLastH() { 2474 return $this->lasth; 2475 } 2476 2477 /** 2478 * Set the adjusting factor to convert pixels to user units. 2479 * @param $scale (float) adjusting factor to convert pixels to user units. 2480 * @author Nicola Asuni 2481 * @public 2482 * @since 1.5.2 2483 */ 2484 public function setImageScale($scale) { 2485 $this->imgscale = $scale; 2486 } 2487 2488 /** 2489 * Returns the adjusting factor to convert pixels to user units. 2490 * @return float adjusting factor to convert pixels to user units. 2491 * @author Nicola Asuni 2492 * @public 2493 * @since 1.5.2 2494 */ 2495 public function getImageScale() { 2496 return $this->imgscale; 2497 } 2498 2499 /** 2500 * Returns an array of page dimensions: 2501 * <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> 2502 * @param $pagenum (int) page number (empty = current page) 2503 * @return array of page dimensions. 2504 * @author Nicola Asuni 2505 * @public 2506 * @since 4.5.027 (2009-03-16) 2507 */ 2508 public function getPageDimensions($pagenum='') { 2509 if (empty($pagenum)) { 2510 $pagenum = $this->page; 2511 } 2512 return $this->pagedim[$pagenum]; 2513 } 2514 2515 /** 2516 * Returns the page width in units. 2517 * @param $pagenum (int) page number (empty = current page) 2518 * @return int page width. 2519 * @author Nicola Asuni 2520 * @public 2521 * @since 1.5.2 2522 * @see getPageDimensions() 2523 */ 2524 public function getPageWidth($pagenum='') { 2525 if (empty($pagenum)) { 2526 return $this->w; 2527 } 2528 return $this->pagedim[$pagenum]['w']; 2529 } 2530 2531 /** 2532 * Returns the page height in units. 2533 * @param $pagenum (int) page number (empty = current page) 2534 * @return int page height. 2535 * @author Nicola Asuni 2536 * @public 2537 * @since 1.5.2 2538 * @see getPageDimensions() 2539 */ 2540 public function getPageHeight($pagenum='') { 2541 if (empty($pagenum)) { 2542 return $this->h; 2543 } 2544 return $this->pagedim[$pagenum]['h']; 2545 } 2546 2547 /** 2548 * Returns the page break margin. 2549 * @param $pagenum (int) page number (empty = current page) 2550 * @return int page break margin. 2551 * @author Nicola Asuni 2552 * @public 2553 * @since 1.5.2 2554 * @see getPageDimensions() 2555 */ 2556 public function getBreakMargin($pagenum='') { 2557 if (empty($pagenum)) { 2558 return $this->bMargin; 2559 } 2560 return $this->pagedim[$pagenum]['bm']; 2561 } 2562 2563 /** 2564 * Returns the scale factor (number of points in user unit). 2565 * @return int scale factor. 2566 * @author Nicola Asuni 2567 * @public 2568 * @since 1.5.2 2569 */ 2570 public function getScaleFactor() { 2571 return $this->k; 2572 } 2573 2574 /** 2575 * Defines the left, top and right margins. 2576 * @param $left (float) Left margin. 2577 * @param $top (float) Top margin. 2578 * @param $right (float) Right margin. Default value is the left one. 2579 * @param $keepmargins (boolean) if true overwrites the default page margins 2580 * @public 2581 * @since 1.0 2582 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() 2583 */ 2584 public function SetMargins($left, $top, $right=-1, $keepmargins=false) { 2585 //Set left, top and right margins 2586 $this->lMargin = $left; 2587 $this->tMargin = $top; 2588 if ($right == -1) { 2589 $right = $left; 2590 } 2591 $this->rMargin = $right; 2592 if ($keepmargins) { 2593 // overwrite original values 2594 $this->original_lMargin = $this->lMargin; 2595 $this->original_rMargin = $this->rMargin; 2596 } 2597 } 2598 2599 /** 2600 * 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. 2601 * @param $margin (float) The margin. 2602 * @public 2603 * @since 1.4 2604 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 2605 */ 2606 public function SetLeftMargin($margin) { 2607 //Set left margin 2608 $this->lMargin = $margin; 2609 if (($this->page > 0) AND ($this->x < $margin)) { 2610 $this->x = $margin; 2611 } 2612 } 2613 2614 /** 2615 * Defines the top margin. The method can be called before creating the first page. 2616 * @param $margin (float) The margin. 2617 * @public 2618 * @since 1.5 2619 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 2620 */ 2621 public function SetTopMargin($margin) { 2622 //Set top margin 2623 $this->tMargin = $margin; 2624 if (($this->page > 0) AND ($this->y < $margin)) { 2625 $this->y = $margin; 2626 } 2627 } 2628 2629 /** 2630 * Defines the right margin. The method can be called before creating the first page. 2631 * @param $margin (float) The margin. 2632 * @public 2633 * @since 1.5 2634 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() 2635 */ 2636 public function SetRightMargin($margin) { 2637 $this->rMargin = $margin; 2638 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) { 2639 $this->x = $this->w - $margin; 2640 } 2641 } 2642 2643 /** 2644 * Set the same internal Cell padding for top, right, bottom, left- 2645 * @param $pad (float) internal padding. 2646 * @public 2647 * @since 2.1.000 (2008-01-09) 2648 * @see getCellPaddings(), setCellPaddings() 2649 */ 2650 public function SetCellPadding($pad) { 2651 if ($pad >= 0) { 2652 $this->cell_padding['L'] = $pad; 2653 $this->cell_padding['T'] = $pad; 2654 $this->cell_padding['R'] = $pad; 2655 $this->cell_padding['B'] = $pad; 2656 } 2657 } 2658 2659 /** 2660 * Set the internal Cell paddings. 2661 * @param $left (float) left padding 2662 * @param $top (float) top padding 2663 * @param $right (float) right padding 2664 * @param $bottom (float) bottom padding 2665 * @public 2666 * @since 5.9.000 (2010-10-03) 2667 * @see getCellPaddings(), SetCellPadding() 2668 */ 2669 public function setCellPaddings($left='', $top='', $right='', $bottom='') { 2670 if (($left !== '') AND ($left >= 0)) { 2671 $this->cell_padding['L'] = $left; 2672 } 2673 if (($top !== '') AND ($top >= 0)) { 2674 $this->cell_padding['T'] = $top; 2675 } 2676 if (($right !== '') AND ($right >= 0)) { 2677 $this->cell_padding['R'] = $right; 2678 } 2679 if (($bottom !== '') AND ($bottom >= 0)) { 2680 $this->cell_padding['B'] = $bottom; 2681 } 2682 } 2683 2684 /** 2685 * Get the internal Cell padding array. 2686 * @return array of padding values 2687 * @public 2688 * @since 5.9.000 (2010-10-03) 2689 * @see setCellPaddings(), SetCellPadding() 2690 */ 2691 public function getCellPaddings() { 2692 return $this->cell_padding; 2693 } 2694 2695 /** 2696 * Set the internal Cell margins. 2697 * @param $left (float) left margin 2698 * @param $top (float) top margin 2699 * @param $right (float) right margin 2700 * @param $bottom (float) bottom margin 2701 * @public 2702 * @since 5.9.000 (2010-10-03) 2703 * @see getCellMargins() 2704 */ 2705 public function setCellMargins($left='', $top='', $right='', $bottom='') { 2706 if (($left !== '') AND ($left >= 0)) { 2707 $this->cell_margin['L'] = $left; 2708 } 2709 if (($top !== '') AND ($top >= 0)) { 2710 $this->cell_margin['T'] = $top; 2711 } 2712 if (($right !== '') AND ($right >= 0)) { 2713 $this->cell_margin['R'] = $right; 2714 } 2715 if (($bottom !== '') AND ($bottom >= 0)) { 2716 $this->cell_margin['B'] = $bottom; 2717 } 2718 } 2719 2720 /** 2721 * Get the internal Cell margin array. 2722 * @return array of margin values 2723 * @public 2724 * @since 5.9.000 (2010-10-03) 2725 * @see setCellMargins() 2726 */ 2727 public function getCellMargins() { 2728 return $this->cell_margin; 2729 } 2730 2731 /** 2732 * Adjust the internal Cell padding array to take account of the line width. 2733 * @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))) 2734 * @return void|array of adjustments 2735 * @public 2736 * @since 5.9.000 (2010-10-03) 2737 */ 2738 protected function adjustCellPadding($brd=0) { 2739 if (empty($brd)) { 2740 return; 2741 } 2742 if (is_string($brd)) { 2743 // convert string to array 2744 $slen = strlen($brd); 2745 $newbrd = array(); 2746 for ($i = 0; $i < $slen; ++$i) { 2747 $newbrd[$brd[$i]] = true; 2748 } 2749 $brd = $newbrd; 2750 } elseif ( 2751 ($brd === 1) 2752 || ($brd === true) 2753 || (is_numeric($brd) && ((int)$brd > 0)) 2754 ) { 2755 $brd = array('LRTB' => true); 2756 } 2757 if (!is_array($brd)) { 2758 return; 2759 } 2760 // store current cell padding 2761 $cp = $this->cell_padding; 2762 // select border mode 2763 if (isset($brd['mode'])) { 2764 $mode = $brd['mode']; 2765 unset($brd['mode']); 2766 } else { 2767 $mode = 'normal'; 2768 } 2769 // process borders 2770 foreach ($brd as $border => $style) { 2771 $line_width = $this->LineWidth; 2772 if (is_array($style) && isset($style['width'])) { 2773 // get border width 2774 $line_width = $style['width']; 2775 } 2776 $adj = 0; // line width inside the cell 2777 switch ($mode) { 2778 case 'ext': { 2779 $adj = 0; 2780 break; 2781 } 2782 case 'int': { 2783 $adj = $line_width; 2784 break; 2785 } 2786 case 'normal': 2787 default: { 2788 $adj = ($line_width / 2); 2789 break; 2790 } 2791 } 2792 // correct internal cell padding if required to avoid overlap between text and lines 2793 if ( 2794 is_numeric($this->cell_padding['T']) 2795 && ($this->cell_padding['T'] < $adj) 2796 && (strpos($border, 'T') !== false) 2797 ) { 2798 $this->cell_padding['T'] = $adj; 2799 } 2800 if ( 2801 is_numeric($this->cell_padding['R']) 2802 && ($this->cell_padding['R'] < $adj) 2803 && (strpos($border, 'R') !== false) 2804 ) { 2805 $this->cell_padding['R'] = $adj; 2806 } 2807 if ( 2808 is_numeric($this->cell_padding['B']) 2809 && ($this->cell_padding['B'] < $adj) 2810 && (strpos($border, 'B') !== false) 2811 ) { 2812 $this->cell_padding['B'] = $adj; 2813 } 2814 if ( 2815 is_numeric($this->cell_padding['L']) 2816 && ($this->cell_padding['L'] < $adj) 2817 && (strpos($border, 'L') !== false) 2818 ) { 2819 $this->cell_padding['L'] = $adj; 2820 } 2821 2822 } 2823 2824 return array( 2825 'T' => ($this->cell_padding['T'] - $cp['T']), 2826 'R' => ($this->cell_padding['R'] - $cp['R']), 2827 'B' => ($this->cell_padding['B'] - $cp['B']), 2828 'L' => ($this->cell_padding['L'] - $cp['L']), 2829 ); 2830 } 2831 2832 /** 2833 * 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. 2834 * @param $auto (boolean) Boolean indicating if mode should be on or off. 2835 * @param $margin (float) Distance from the bottom of the page. 2836 * @public 2837 * @since 1.0 2838 * @see Cell(), MultiCell(), AcceptPageBreak() 2839 */ 2840 public function SetAutoPageBreak($auto, $margin=0) { 2841 $this->AutoPageBreak = $auto ? true : false; 2842 $this->bMargin = $margin; 2843 $this->PageBreakTrigger = $this->h - $margin; 2844 } 2845 2846 /** 2847 * Return the auto-page-break mode (true or false). 2848 * @return boolean auto-page-break mode 2849 * @public 2850 * @since 5.9.088 2851 */ 2852 public function getAutoPageBreak() { 2853 return $this->AutoPageBreak; 2854 } 2855 2856 /** 2857 * Defines the way the document is to be displayed by the viewer. 2858 * @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> 2859 * @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> 2860 * @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> 2861 * @public 2862 * @since 1.2 2863 */ 2864 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') { 2865 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) { 2866 $this->ZoomMode = $zoom; 2867 } else { 2868 $this->Error('Incorrect zoom display mode: '.$zoom); 2869 } 2870 $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout); 2871 $this->PageMode = TCPDF_STATIC::getPageMode($mode); 2872 } 2873 2874 /** 2875 * 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. 2876 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. 2877 * @param $compress (boolean) Boolean indicating if compression must be enabled. 2878 * @public 2879 * @since 1.4 2880 */ 2881 public function SetCompression($compress=true) { 2882 $this->compress = false; 2883 if (function_exists('gzcompress')) { 2884 if ($compress) { 2885 if ( !$this->pdfa_mode) { 2886 $this->compress = true; 2887 } 2888 } 2889 } 2890 } 2891 2892 /** 2893 * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document. 2894 * @param $mode (boolean) If true force sRGB output intent. 2895 * @public 2896 * @since 5.9.121 (2011-09-28) 2897 */ 2898 public function setSRGBmode($mode=false) { 2899 $this->force_srgb = $mode ? true : false; 2900 } 2901 2902 /** 2903 * Turn on/off Unicode mode for document information dictionary (meta tags). 2904 * This has effect only when unicode mode is set to false. 2905 * @param $unicode (boolean) if true set the meta information in Unicode 2906 * @since 5.9.027 (2010-12-01) 2907 * @public 2908 */ 2909 public function SetDocInfoUnicode($unicode=true) { 2910 $this->docinfounicode = $unicode ? true : false; 2911 } 2912 2913 /** 2914 * Defines the title of the document. 2915 * @param $title (string) The title. 2916 * @public 2917 * @since 1.2 2918 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() 2919 */ 2920 public function SetTitle($title) { 2921 $this->title = $title; 2922 } 2923 2924 /** 2925 * Defines the subject of the document. 2926 * @param $subject (string) The subject. 2927 * @public 2928 * @since 1.2 2929 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() 2930 */ 2931 public function SetSubject($subject) { 2932 $this->subject = $subject; 2933 } 2934 2935 /** 2936 * Defines the author of the document. 2937 * @param $author (string) The name of the author. 2938 * @public 2939 * @since 1.2 2940 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() 2941 */ 2942 public function SetAuthor($author) { 2943 $this->author = $author; 2944 } 2945 2946 /** 2947 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. 2948 * @param $keywords (string) The list of keywords. 2949 * @public 2950 * @since 1.2 2951 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() 2952 */ 2953 public function SetKeywords($keywords) { 2954 $this->keywords = $keywords; 2955 } 2956 2957 /** 2958 * Defines the creator of the document. This is typically the name of the application that generates the PDF. 2959 * @param $creator (string) The name of the creator. 2960 * @public 2961 * @since 1.2 2962 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() 2963 */ 2964 public function SetCreator($creator) { 2965 $this->creator = $creator; 2966 } 2967 2968 /** 2969 * Whether to allow local file path in image html tags, when prefixed with file:// 2970 * 2971 * @param $allowLocalFiles bool true, when local files should be allowed. Otherwise false. 2972 * @public 2973 * @since 6.4 2974 */ 2975 public function SetAllowLocalFiles($allowLocalFiles) { 2976 $this->allowLocalFiles = (bool) $allowLocalFiles; 2977 } 2978 2979 2980 /** 2981 * Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR constant is set to true. 2982 * @param $msg (string) The error message 2983 * @public 2984 * @since 1.0 2985 */ 2986 public function Error($msg) { 2987 // unset all class variables 2988 $this->_destroy(true); 2989 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) { 2990 die('<strong>TCPDF ERROR: </strong>'.$msg); 2991 } else { 2992 throw new Exception('TCPDF ERROR: '.$msg); 2993 } 2994 } 2995 2996 /** 2997 * This method begins the generation of the PDF document. 2998 * It is not necessary to call it explicitly because AddPage() does it automatically. 2999 * Note: no page is created by this method 3000 * @public 3001 * @since 1.0 3002 * @see AddPage(), Close() 3003 */ 3004 public function Open() { 3005 $this->state = 1; 3006 } 3007 3008 /** 3009 * Terminates the PDF document. 3010 * It is not necessary to call this method explicitly because Output() does it automatically. 3011 * If the document contains no page, AddPage() is called to prevent from getting an invalid document. 3012 * @public 3013 * @since 1.0 3014 * @see Open(), Output() 3015 */ 3016 public function Close() { 3017 if ($this->state == 3) { 3018 return; 3019 } 3020 if ($this->page == 0) { 3021 $this->AddPage(); 3022 } 3023 $this->endLayer(); 3024 if ($this->tcpdflink) { 3025 // save current graphic settings 3026 $gvars = $this->getGraphicVars(); 3027 $this->setEqualColumns(); 3028 $this->lastpage(true); 3029 $this->SetAutoPageBreak(false); 3030 $this->x = 0; 3031 $this->y = $this->h - (1 / $this->k); 3032 $this->lMargin = 0; 3033 $this->_outSaveGraphicsState(); 3034 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica'; 3035 $this->SetFont($font, '', 1); 3036 $this->setTextRenderingMode(0, false, false); 3037 $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"; 3038 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67"; 3039 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B'); 3040 $this->_outRestoreGraphicsState(); 3041 // restore graphic settings 3042 $this->setGraphicVars($gvars); 3043 } 3044 // close page 3045 $this->endPage(); 3046 // close document 3047 $this->_enddoc(); 3048 // unset all class variables (except critical ones) 3049 $this->_destroy(false); 3050 } 3051 3052 /** 3053 * Move pointer at the specified document page and update page dimensions. 3054 * @param $pnum (int) page number (1 ... numpages) 3055 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position. 3056 * @public 3057 * @since 2.1.000 (2008-01-07) 3058 * @see getPage(), lastpage(), getNumPages() 3059 */ 3060 public function setPage($pnum, $resetmargins=false) { 3061 if (($pnum == $this->page) AND ($this->state == 2)) { 3062 return; 3063 } 3064 if (($pnum > 0) AND ($pnum <= $this->numpages)) { 3065 $this->state = 2; 3066 // save current graphic settings 3067 //$gvars = $this->getGraphicVars(); 3068 $oldpage = $this->page; 3069 $this->page = $pnum; 3070 $this->wPt = $this->pagedim[$this->page]['w']; 3071 $this->hPt = $this->pagedim[$this->page]['h']; 3072 $this->w = $this->pagedim[$this->page]['wk']; 3073 $this->h = $this->pagedim[$this->page]['hk']; 3074 $this->tMargin = $this->pagedim[$this->page]['tm']; 3075 $this->bMargin = $this->pagedim[$this->page]['bm']; 3076 $this->original_lMargin = $this->pagedim[$this->page]['olm']; 3077 $this->original_rMargin = $this->pagedim[$this->page]['orm']; 3078 $this->AutoPageBreak = $this->pagedim[$this->page]['pb']; 3079 $this->CurOrientation = $this->pagedim[$this->page]['or']; 3080 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin); 3081 // restore graphic settings 3082 //$this->setGraphicVars($gvars); 3083 if ($resetmargins) { 3084 $this->lMargin = $this->pagedim[$this->page]['olm']; 3085 $this->rMargin = $this->pagedim[$this->page]['orm']; 3086 $this->SetY($this->tMargin); 3087 } else { 3088 // account for booklet mode 3089 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 3090 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm']; 3091 $this->lMargin += $deltam; 3092 $this->rMargin -= $deltam; 3093 } 3094 } 3095 } else { 3096 $this->Error('Wrong page number on setPage() function: '.$pnum); 3097 } 3098 } 3099 3100 /** 3101 * Reset pointer to the last document page. 3102 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position. 3103 * @public 3104 * @since 2.0.000 (2008-01-04) 3105 * @see setPage(), getPage(), getNumPages() 3106 */ 3107 public function lastPage($resetmargins=false) { 3108 $this->setPage($this->getNumPages(), $resetmargins); 3109 } 3110 3111 /** 3112 * Get current document page number. 3113 * @return int page number 3114 * @public 3115 * @since 2.1.000 (2008-01-07) 3116 * @see setPage(), lastpage(), getNumPages() 3117 */ 3118 public function getPage() { 3119 return $this->page; 3120 } 3121 3122 /** 3123 * Get the total number of insered pages. 3124 * @return int number of pages 3125 * @public 3126 * @since 2.1.000 (2008-01-07) 3127 * @see setPage(), getPage(), lastpage() 3128 */ 3129 public function getNumPages() { 3130 return $this->numpages; 3131 } 3132 3133 /** 3134 * Adds a new TOC (Table Of Content) page to the document. 3135 * @param $orientation (string) page orientation. 3136 * @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(). 3137 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins 3138 * @public 3139 * @since 5.0.001 (2010-05-06) 3140 * @see AddPage(), startPage(), endPage(), endTOCPage() 3141 */ 3142 public function addTOCPage($orientation='', $format='', $keepmargins=false) { 3143 $this->AddPage($orientation, $format, $keepmargins, true); 3144 } 3145 3146 /** 3147 * Terminate the current TOC (Table Of Content) page 3148 * @public 3149 * @since 5.0.001 (2010-05-06) 3150 * @see AddPage(), startPage(), endPage(), addTOCPage() 3151 */ 3152 public function endTOCPage() { 3153 $this->endPage(true); 3154 } 3155 3156 /** 3157 * 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). 3158 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards. 3159 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 3160 * @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(). 3161 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins 3162 * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content). 3163 * @public 3164 * @since 1.0 3165 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat() 3166 */ 3167 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) { 3168 if ($this->inxobj) { 3169 // we are inside an XObject template 3170 return; 3171 } 3172 if (!isset($this->original_lMargin) OR $keepmargins) { 3173 $this->original_lMargin = $this->lMargin; 3174 } 3175 if (!isset($this->original_rMargin) OR $keepmargins) { 3176 $this->original_rMargin = $this->rMargin; 3177 } 3178 // terminate previous page 3179 $this->endPage(); 3180 // start new page 3181 $this->startPage($orientation, $format, $tocpage); 3182 } 3183 3184 /** 3185 * Terminate the current page 3186 * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content). 3187 * @public 3188 * @since 4.2.010 (2008-11-14) 3189 * @see AddPage(), startPage(), addTOCPage(), endTOCPage() 3190 */ 3191 public function endPage($tocpage=false) { 3192 // check if page is already closed 3193 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) { 3194 return; 3195 } 3196 // print page footer 3197 $this->setFooter(); 3198 // close page 3199 $this->_endpage(); 3200 // mark page as closed 3201 $this->pageopen[$this->page] = false; 3202 if ($tocpage) { 3203 $this->tocpage = false; 3204 } 3205 } 3206 3207 /** 3208 * Starts a new page to the document. The page must be closed using the endPage() function. 3209 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. 3210 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 3211 * @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(). 3212 * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content. 3213 * @since 4.2.010 (2008-11-14) 3214 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat() 3215 * @public 3216 */ 3217 public function startPage($orientation='', $format='', $tocpage=false) { 3218 if ($tocpage) { 3219 $this->tocpage = true; 3220 } 3221 // move page numbers of documents to be attached 3222 if ($this->tocpage) { 3223 // move reference to unexistent pages (used for page attachments) 3224 // adjust outlines 3225 $tmpoutlines = $this->outlines; 3226 foreach ($tmpoutlines as $key => $outline) { 3227 if (!$outline['f'] AND ($outline['p'] > $this->numpages)) { 3228 $this->outlines[$key]['p'] = ($outline['p'] + 1); 3229 } 3230 } 3231 // adjust dests 3232 $tmpdests = $this->dests; 3233 foreach ($tmpdests as $key => $dest) { 3234 if (!$dest['f'] AND ($dest['p'] > $this->numpages)) { 3235 $this->dests[$key]['p'] = ($dest['p'] + 1); 3236 } 3237 } 3238 // adjust links 3239 $tmplinks = $this->links; 3240 foreach ($tmplinks as $key => $link) { 3241 if (!$link['f'] AND ($link['p'] > $this->numpages)) { 3242 $this->links[$key]['p'] = ($link['p'] + 1); 3243 } 3244 } 3245 } 3246 if ($this->numpages > $this->page) { 3247 // this page has been already added 3248 $this->setPage($this->page + 1); 3249 $this->SetY($this->tMargin); 3250 return; 3251 } 3252 // start a new page 3253 if ($this->state == 0) { 3254 $this->Open(); 3255 } 3256 ++$this->numpages; 3257 $this->swapMargins($this->booklet); 3258 // save current graphic settings 3259 $gvars = $this->getGraphicVars(); 3260 // start new page 3261 $this->_beginpage($orientation, $format); 3262 // mark page as open 3263 $this->pageopen[$this->page] = true; 3264 // restore graphic settings 3265 $this->setGraphicVars($gvars); 3266 // mark this point 3267 $this->setPageMark(); 3268 // print page header 3269 $this->setHeader(); 3270 // restore graphic settings 3271 $this->setGraphicVars($gvars); 3272 // mark this point 3273 $this->setPageMark(); 3274 // print table header (if any) 3275 $this->setTableHeader(); 3276 // set mark for empty page check 3277 $this->emptypagemrk[$this->page]= $this->pagelen[$this->page]; 3278 } 3279 3280 /** 3281 * Set start-writing mark on current page stream used to put borders and fills. 3282 * Borders and fills are always created after content and inserted on the position marked by this method. 3283 * This function must be called after calling Image() function for a background image. 3284 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions. 3285 * @public 3286 * @since 4.0.016 (2008-07-30) 3287 */ 3288 public function setPageMark() { 3289 $this->intmrk[$this->page] = $this->pagelen[$this->page]; 3290 $this->bordermrk[$this->page] = $this->intmrk[$this->page]; 3291 $this->setContentMark(); 3292 } 3293 3294 /** 3295 * Set start-writing mark on selected page. 3296 * Borders and fills are always created after content and inserted on the position marked by this method. 3297 * @param $page (int) page number (default is the current page) 3298 * @protected 3299 * @since 4.6.021 (2009-07-20) 3300 */ 3301 protected function setContentMark($page=0) { 3302 if ($page <= 0) { 3303 $page = $this->page; 3304 } 3305 if (isset($this->footerlen[$page])) { 3306 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page]; 3307 } else { 3308 $this->cntmrk[$page] = $this->pagelen[$page]; 3309 } 3310 } 3311 3312 /** 3313 * Set header data. 3314 * @param $ln (string) header image logo 3315 * @param $lw (string) header image logo width in mm 3316 * @param $ht (string) string to print as title on document header 3317 * @param $hs (string) string to print on document header 3318 * @param $tc (array) RGB array color for text. 3319 * @param $lc (array) RGB array color for line. 3320 * @public 3321 */ 3322 public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) { 3323 $this->header_logo = $ln; 3324 $this->header_logo_width = $lw; 3325 $this->header_title = $ht; 3326 $this->header_string = $hs; 3327 $this->header_text_color = $tc; 3328 $this->header_line_color = $lc; 3329 } 3330 3331 /** 3332 * Set footer data. 3333 * @param $tc (array) RGB array color for text. 3334 * @param $lc (array) RGB array color for line. 3335 * @public 3336 */ 3337 public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) { 3338 $this->footer_text_color = $tc; 3339 $this->footer_line_color = $lc; 3340 } 3341 3342 /** 3343 * Returns header data: 3344 * <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> 3345 * @return array() 3346 * @public 3347 * @since 4.0.012 (2008-07-24) 3348 */ 3349 public function getHeaderData() { 3350 $ret = array(); 3351 $ret['logo'] = $this->header_logo; 3352 $ret['logo_width'] = $this->header_logo_width; 3353 $ret['title'] = $this->header_title; 3354 $ret['string'] = $this->header_string; 3355 $ret['text_color'] = $this->header_text_color; 3356 $ret['line_color'] = $this->header_line_color; 3357 return $ret; 3358 } 3359 3360 /** 3361 * Set header margin. 3362 * (minimum distance between header and top page margin) 3363 * @param $hm (int) distance in user units 3364 * @public 3365 */ 3366 public function setHeaderMargin($hm=10) { 3367 $this->header_margin = $hm; 3368 } 3369 3370 /** 3371 * Returns header margin in user units. 3372 * @return float 3373 * @since 4.0.012 (2008-07-24) 3374 * @public 3375 */ 3376 public function getHeaderMargin() { 3377 return $this->header_margin; 3378 } 3379 3380 /** 3381 * Set footer margin. 3382 * (minimum distance between footer and bottom page margin) 3383 * @param $fm (int) distance in user units 3384 * @public 3385 */ 3386 public function setFooterMargin($fm=10) { 3387 $this->footer_margin = $fm; 3388 } 3389 3390 /** 3391 * Returns footer margin in user units. 3392 * @return float 3393 * @since 4.0.012 (2008-07-24) 3394 * @public 3395 */ 3396 public function getFooterMargin() { 3397 return $this->footer_margin; 3398 } 3399 /** 3400 * Set a flag to print page header. 3401 * @param $val (boolean) set to true to print the page header (default), false otherwise. 3402 * @public 3403 */ 3404 public function setPrintHeader($val=true) { 3405 $this->print_header = $val ? true : false; 3406 } 3407 3408 /** 3409 * Set a flag to print page footer. 3410 * @param $val (boolean) set to true to print the page footer (default), false otherwise. 3411 * @public 3412 */ 3413 public function setPrintFooter($val=true) { 3414 $this->print_footer = $val ? true : false; 3415 } 3416 3417 /** 3418 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image 3419 * @return float 3420 * @public 3421 */ 3422 public function getImageRBX() { 3423 return $this->img_rb_x; 3424 } 3425 3426 /** 3427 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image 3428 * @return float 3429 * @public 3430 */ 3431 public function getImageRBY() { 3432 return $this->img_rb_y; 3433 } 3434 3435 /** 3436 * Reset the xobject template used by Header() method. 3437 * @public 3438 */ 3439 public function resetHeaderTemplate() { 3440 $this->header_xobjid = false; 3441 } 3442 3443 /** 3444 * Set a flag to automatically reset the xobject template used by Header() method at each page. 3445 * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise. 3446 * @public 3447 */ 3448 public function setHeaderTemplateAutoreset($val=true) { 3449 $this->header_xobj_autoreset = $val ? true : false; 3450 } 3451 3452 /** 3453 * This method is used to render the page header. 3454 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 3455 * @public 3456 */ 3457 public function Header() { 3458 if ($this->header_xobjid === false) { 3459 // start a new XObject Template 3460 $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin); 3461 $headerfont = $this->getHeaderFont(); 3462 $headerdata = $this->getHeaderData(); 3463 $this->y = $this->header_margin; 3464 if ($this->rtl) { 3465 $this->x = $this->w - $this->original_rMargin; 3466 } else { 3467 $this->x = $this->original_lMargin; 3468 } 3469 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) { 3470 $imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']); 3471 if (($imgtype == 'eps') OR ($imgtype == 'ai')) { 3472 $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3473 } elseif ($imgtype == 'svg') { 3474 $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3475 } else { 3476 $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 3477 } 3478 $imgy = $this->getImageRBY(); 3479 } else { 3480 $imgy = $this->y; 3481 } 3482 $cell_height = $this->getCellHeight($headerfont[2] / $this->k); 3483 // set starting margin for text data cell 3484 if ($this->getRTL()) { 3485 $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1); 3486 } else { 3487 $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1); 3488 } 3489 $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1); 3490 $this->SetTextColorArray($this->header_text_color); 3491 // header title 3492 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); 3493 $this->SetX($header_x); 3494 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0); 3495 // header string 3496 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]); 3497 $this->SetX($header_x); 3498 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false); 3499 // print an ending header line 3500 $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color'])); 3501 $this->SetY((2.835 / $this->k) + max($imgy, $this->y)); 3502 if ($this->rtl) { 3503 $this->SetX($this->original_rMargin); 3504 } else { 3505 $this->SetX($this->original_lMargin); 3506 } 3507 $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C'); 3508 $this->endTemplate(); 3509 } 3510 // print header template 3511 $x = 0; 3512 $dx = 0; 3513 if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) { 3514 // adjust margins for booklet mode 3515 $dx = ($this->original_lMargin - $this->original_rMargin); 3516 } 3517 if ($this->rtl) { 3518 $x = $this->w + $dx; 3519 } else { 3520 $x = 0 + $dx; 3521 } 3522 $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false); 3523 if ($this->header_xobj_autoreset) { 3524 // reset header xobject template at each page 3525 $this->header_xobjid = false; 3526 } 3527 } 3528 3529 /** 3530 * This method is used to render the page footer. 3531 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 3532 * @public 3533 */ 3534 public function Footer() { 3535 $cur_y = $this->y; 3536 $this->SetTextColorArray($this->footer_text_color); 3537 //set style for cell border 3538 $line_width = (0.85 / $this->k); 3539 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color)); 3540 //print document barcode 3541 $barcode = $this->getBarcode(); 3542 if (!empty($barcode)) { 3543 $this->Ln($line_width); 3544 $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3); 3545 $style = array( 3546 'position' => $this->rtl?'R':'L', 3547 'align' => $this->rtl?'R':'L', 3548 'stretch' => false, 3549 'fitwidth' => true, 3550 'cellfitalign' => '', 3551 'border' => false, 3552 'padding' => 0, 3553 'fgcolor' => array(0,0,0), 3554 'bgcolor' => false, 3555 'text' => false 3556 ); 3557 $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, ''); 3558 } 3559 $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : ''; 3560 if (empty($this->pagegroups)) { 3561 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages(); 3562 } else { 3563 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias(); 3564 } 3565 $this->SetY($cur_y); 3566 //Print page number 3567 if ($this->getRTL()) { 3568 $this->SetX($this->original_rMargin); 3569 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L'); 3570 } else { 3571 $this->SetX($this->original_lMargin); 3572 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R'); 3573 } 3574 } 3575 3576 /** 3577 * This method is used to render the page header. 3578 * @protected 3579 * @since 4.0.012 (2008-07-24) 3580 */ 3581 protected function setHeader() { 3582 if (!$this->print_header OR ($this->state != 2)) { 3583 return; 3584 } 3585 $this->InHeader = true; 3586 $this->setGraphicVars($this->default_graphic_vars); 3587 $temp_thead = $this->thead; 3588 $temp_theadMargins = $this->theadMargins; 3589 $lasth = $this->lasth; 3590 $newline = $this->newline; 3591 $this->_outSaveGraphicsState(); 3592 $this->rMargin = $this->original_rMargin; 3593 $this->lMargin = $this->original_lMargin; 3594 $this->SetCellPadding(0); 3595 //set current position 3596 if ($this->rtl) { 3597 $this->SetXY($this->original_rMargin, $this->header_margin); 3598 } else { 3599 $this->SetXY($this->original_lMargin, $this->header_margin); 3600 } 3601 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); 3602 $this->Header(); 3603 //restore position 3604 if ($this->rtl) { 3605 $this->SetXY($this->original_rMargin, $this->tMargin); 3606 } else { 3607 $this->SetXY($this->original_lMargin, $this->tMargin); 3608 } 3609 $this->_outRestoreGraphicsState(); 3610 $this->lasth = $lasth; 3611 $this->thead = $temp_thead; 3612 $this->theadMargins = $temp_theadMargins; 3613 $this->newline = $newline; 3614 $this->InHeader = false; 3615 } 3616 3617 /** 3618 * This method is used to render the page footer. 3619 * @protected 3620 * @since 4.0.012 (2008-07-24) 3621 */ 3622 protected function setFooter() { 3623 if ($this->state != 2) { 3624 return; 3625 } 3626 $this->InFooter = true; 3627 // save current graphic settings 3628 $gvars = $this->getGraphicVars(); 3629 // mark this point 3630 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 3631 $this->_out("\n"); 3632 if ($this->print_footer) { 3633 $this->setGraphicVars($this->default_graphic_vars); 3634 $this->current_column = 0; 3635 $this->num_columns = 1; 3636 $temp_thead = $this->thead; 3637 $temp_theadMargins = $this->theadMargins; 3638 $lasth = $this->lasth; 3639 $this->_outSaveGraphicsState(); 3640 $this->rMargin = $this->original_rMargin; 3641 $this->lMargin = $this->original_lMargin; 3642 $this->SetCellPadding(0); 3643 //set current position 3644 $footer_y = $this->h - $this->footer_margin; 3645 if ($this->rtl) { 3646 $this->SetXY($this->original_rMargin, $footer_y); 3647 } else { 3648 $this->SetXY($this->original_lMargin, $footer_y); 3649 } 3650 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]); 3651 $this->Footer(); 3652 //restore position 3653 if ($this->rtl) { 3654 $this->SetXY($this->original_rMargin, $this->tMargin); 3655 } else { 3656 $this->SetXY($this->original_lMargin, $this->tMargin); 3657 } 3658 $this->_outRestoreGraphicsState(); 3659 $this->lasth = $lasth; 3660 $this->thead = $temp_thead; 3661 $this->theadMargins = $temp_theadMargins; 3662 } 3663 // restore graphic settings 3664 $this->setGraphicVars($gvars); 3665 $this->current_column = $gvars['current_column']; 3666 $this->num_columns = $gvars['num_columns']; 3667 // calculate footer length 3668 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1; 3669 $this->InFooter = false; 3670 } 3671 3672 /** 3673 * Check if we are on the page body (excluding page header and footer). 3674 * @return true if we are not in page header nor in page footer, false otherwise. 3675 * @protected 3676 * @since 5.9.091 (2011-06-15) 3677 */ 3678 protected function inPageBody() { 3679 return (($this->InHeader === false) AND ($this->InFooter === false)); 3680 } 3681 3682 /** 3683 * This method is used to render the table header on new page (if any). 3684 * @protected 3685 * @since 4.5.030 (2009-03-25) 3686 */ 3687 protected function setTableHeader() { 3688 if ($this->num_columns > 1) { 3689 // multi column mode 3690 return; 3691 } 3692 if (isset($this->theadMargins['top'])) { 3693 // restore the original top-margin 3694 $this->tMargin = $this->theadMargins['top']; 3695 $this->pagedim[$this->page]['tm'] = $this->tMargin; 3696 $this->y = $this->tMargin; 3697 } 3698 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) { 3699 // set margins 3700 $prev_lMargin = $this->lMargin; 3701 $prev_rMargin = $this->rMargin; 3702 $prev_cell_padding = $this->cell_padding; 3703 $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']); 3704 $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']); 3705 $this->cell_padding = $this->theadMargins['cell_padding']; 3706 if ($this->rtl) { 3707 $this->x = $this->w - $this->rMargin; 3708 } else { 3709 $this->x = $this->lMargin; 3710 } 3711 // account for special "cell" mode 3712 if ($this->theadMargins['cell']) { 3713 if ($this->rtl) { 3714 $this->x -= $this->cell_padding['R']; 3715 } else { 3716 $this->x += $this->cell_padding['L']; 3717 } 3718 } 3719 $gvars = $this->getGraphicVars(); 3720 if (!empty($this->theadMargins['gvars'])) { 3721 // set the correct graphic style 3722 $this->setGraphicVars($this->theadMargins['gvars']); 3723 $this->rMargin = $gvars['rMargin']; 3724 $this->lMargin = $gvars['lMargin']; 3725 } 3726 // print table header 3727 $this->writeHTML($this->thead, false, false, false, false, ''); 3728 $this->setGraphicVars($gvars); 3729 // set new top margin to skip the table headers 3730 if (!isset($this->theadMargins['top'])) { 3731 $this->theadMargins['top'] = $this->tMargin; 3732 } 3733 // store end of header position 3734 if (!isset($this->columns[0]['th'])) { 3735 $this->columns[0]['th'] = array(); 3736 } 3737 $this->columns[0]['th']['\''.$this->page.'\''] = $this->y; 3738 $this->tMargin = $this->y; 3739 $this->pagedim[$this->page]['tm'] = $this->tMargin; 3740 $this->lasth = 0; 3741 $this->lMargin = $prev_lMargin; 3742 $this->rMargin = $prev_rMargin; 3743 $this->cell_padding = $prev_cell_padding; 3744 } 3745 } 3746 3747 /** 3748 * Returns the current page number. 3749 * @return int page number 3750 * @public 3751 * @since 1.0 3752 * @see getAliasNbPages() 3753 */ 3754 public function PageNo() { 3755 return $this->page; 3756 } 3757 3758 /** 3759 * Returns the array of spot colors. 3760 * @return (array) Spot colors array. 3761 * @public 3762 * @since 6.0.038 (2013-09-30) 3763 */ 3764 public function getAllSpotColors() { 3765 return $this->spot_colors; 3766 } 3767 3768 /** 3769 * Defines a new spot color. 3770 * It can be expressed in RGB components or gray scale. 3771 * The method can be called before the first page is created and the value is retained from page to page. 3772 * @param $name (string) Full name of the spot color. 3773 * @param $c (float) Cyan color for CMYK. Value between 0 and 100. 3774 * @param $m (float) Magenta color for CMYK. Value between 0 and 100. 3775 * @param $y (float) Yellow color for CMYK. Value between 0 and 100. 3776 * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100. 3777 * @public 3778 * @since 4.0.024 (2008-09-12) 3779 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor() 3780 */ 3781 public function AddSpotColor($name, $c, $m, $y, $k) { 3782 if (!isset($this->spot_colors[$name])) { 3783 $i = (1 + count($this->spot_colors)); 3784 $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i); 3785 } 3786 } 3787 3788 /** 3789 * Set the spot color for the specified type ('draw', 'fill', 'text'). 3790 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3791 * @param $name (string) Name of the spot color. 3792 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3793 * @return (string) PDF color command. 3794 * @public 3795 * @since 5.9.125 (2011-10-03) 3796 */ 3797 public function setSpotColor($type, $name, $tint=100) { 3798 $spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors); 3799 if ($spotcolor === false) { 3800 $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.'); 3801 } 3802 $tint = (max(0, min(100, $tint)) / 100); 3803 $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']); 3804 switch ($type) { 3805 case 'draw': { 3806 $pdfcolor .= sprintf('CS %F SCN', $tint); 3807 $this->DrawColor = $pdfcolor; 3808 $this->strokecolor = $spotcolor; 3809 break; 3810 } 3811 case 'fill': { 3812 $pdfcolor .= sprintf('cs %F scn', $tint); 3813 $this->FillColor = $pdfcolor; 3814 $this->bgcolor = $spotcolor; 3815 break; 3816 } 3817 case 'text': { 3818 $pdfcolor .= sprintf('cs %F scn', $tint); 3819 $this->TextColor = $pdfcolor; 3820 $this->fgcolor = $spotcolor; 3821 break; 3822 } 3823 } 3824 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3825 if ($this->state == 2) { 3826 $this->_out($pdfcolor); 3827 } 3828 if ($this->inxobj) { 3829 // we are inside an XObject template 3830 $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name]; 3831 } 3832 return $pdfcolor; 3833 } 3834 3835 /** 3836 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders). 3837 * @param $name (string) Name of the spot color. 3838 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3839 * @public 3840 * @since 4.0.024 (2008-09-12) 3841 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor() 3842 */ 3843 public function SetDrawSpotColor($name, $tint=100) { 3844 $this->setSpotColor('draw', $name, $tint); 3845 } 3846 3847 /** 3848 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds). 3849 * @param $name (string) Name of the spot color. 3850 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3851 * @public 3852 * @since 4.0.024 (2008-09-12) 3853 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor() 3854 */ 3855 public function SetFillSpotColor($name, $tint=100) { 3856 $this->setSpotColor('fill', $name, $tint); 3857 } 3858 3859 /** 3860 * Defines the spot color used for text. 3861 * @param $name (string) Name of the spot color. 3862 * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3863 * @public 3864 * @since 4.0.024 (2008-09-12) 3865 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor() 3866 */ 3867 public function SetTextSpotColor($name, $tint=100) { 3868 $this->setSpotColor('text', $name, $tint); 3869 } 3870 3871 /** 3872 * Set the color array for the specified type ('draw', 'fill', 'text'). 3873 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3874 * The method can be called before the first page is created and the value is retained from page to page. 3875 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3876 * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values). 3877 * @param $ret (boolean) If true do not send the PDF command. 3878 * @return (string) The PDF command or empty string. 3879 * @public 3880 * @since 3.1.000 (2008-06-11) 3881 */ 3882 public function setColorArray($type, $color, $ret=false) { 3883 if (is_array($color)) { 3884 $color = array_values($color); 3885 // component: grey, RGB red or CMYK cyan 3886 $c = isset($color[0]) ? $color[0] : -1; 3887 // component: RGB green or CMYK magenta 3888 $m = isset($color[1]) ? $color[1] : -1; 3889 // component: RGB blue or CMYK yellow 3890 $y = isset($color[2]) ? $color[2] : -1; 3891 // component: CMYK black 3892 $k = isset($color[3]) ? $color[3] : -1; 3893 // color name 3894 $name = isset($color[4]) ? $color[4] : ''; 3895 if ($c >= 0) { 3896 return $this->setColor($type, $c, $m, $y, $k, $ret, $name); 3897 } 3898 } 3899 return ''; 3900 } 3901 3902 /** 3903 * Defines the color used for all drawing operations (lines, rectangles and cell borders). 3904 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3905 * The method can be called before the first page is created and the value is retained from page to page. 3906 * @param $color (array) Array of colors (1, 3 or 4 values). 3907 * @param $ret (boolean) If true do not send the PDF command. 3908 * @return string the PDF command 3909 * @public 3910 * @since 3.1.000 (2008-06-11) 3911 * @see SetDrawColor() 3912 */ 3913 public function SetDrawColorArray($color, $ret=false) { 3914 return $this->setColorArray('draw', $color, $ret); 3915 } 3916 3917 /** 3918 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 3919 * It can be expressed in RGB, CMYK or GRAY SCALE components. 3920 * The method can be called before the first page is created and the value is retained from page to page. 3921 * @param $color (array) Array of colors (1, 3 or 4 values). 3922 * @param $ret (boolean) If true do not send the PDF command. 3923 * @public 3924 * @since 3.1.000 (2008-6-11) 3925 * @see SetFillColor() 3926 */ 3927 public function SetFillColorArray($color, $ret=false) { 3928 return $this->setColorArray('fill', $color, $ret); 3929 } 3930 3931 /** 3932 * Defines the color used for text. It can be expressed in RGB components or gray scale. 3933 * The method can be called before the first page is created and the value is retained from page to page. 3934 * @param $color (array) Array of colors (1, 3 or 4 values). 3935 * @param $ret (boolean) If true do not send the PDF command. 3936 * @public 3937 * @since 3.1.000 (2008-6-11) 3938 * @see SetFillColor() 3939 */ 3940 public function SetTextColorArray($color, $ret=false) { 3941 return $this->setColorArray('text', $color, $ret); 3942 } 3943 3944 /** 3945 * Defines the color used by the specified type ('draw', 'fill', 'text'). 3946 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). 3947 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 3948 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 3949 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 3950 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 3951 * @param $ret (boolean) If true do not send the command. 3952 * @param $name (string) spot color name (if any) 3953 * @return (string) The PDF command or empty string. 3954 * @public 3955 * @since 5.9.125 (2011-10-03) 3956 */ 3957 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 3958 // set default values 3959 if (!is_numeric($col1)) { 3960 $col1 = 0; 3961 } 3962 if (!is_numeric($col2)) { 3963 $col2 = -1; 3964 } 3965 if (!is_numeric($col3)) { 3966 $col3 = -1; 3967 } 3968 if (!is_numeric($col4)) { 3969 $col4 = -1; 3970 } 3971 // set color by case 3972 $suffix = ''; 3973 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 3974 // Grey scale 3975 $col1 = max(0, min(255, $col1)); 3976 $intcolor = array('G' => $col1); 3977 $pdfcolor = sprintf('%F ', ($col1 / 255)); 3978 $suffix = 'g'; 3979 } elseif ($col4 == -1) { 3980 // RGB 3981 $col1 = max(0, min(255, $col1)); 3982 $col2 = max(0, min(255, $col2)); 3983 $col3 = max(0, min(255, $col3)); 3984 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 3985 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255)); 3986 $suffix = 'rg'; 3987 } else { 3988 $col1 = max(0, min(100, $col1)); 3989 $col2 = max(0, min(100, $col2)); 3990 $col3 = max(0, min(100, $col3)); 3991 $col4 = max(0, min(100, $col4)); 3992 if (empty($name)) { 3993 // CMYK 3994 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 3995 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100)); 3996 $suffix = 'k'; 3997 } else { 3998 // SPOT COLOR 3999 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name); 4000 $this->AddSpotColor($name, $col1, $col2, $col3, $col4); 4001 $pdfcolor = $this->setSpotColor($type, $name, 100); 4002 } 4003 } 4004 switch ($type) { 4005 case 'draw': { 4006 $pdfcolor .= strtoupper($suffix); 4007 $this->DrawColor = $pdfcolor; 4008 $this->strokecolor = $intcolor; 4009 break; 4010 } 4011 case 'fill': { 4012 $pdfcolor .= $suffix; 4013 $this->FillColor = $pdfcolor; 4014 $this->bgcolor = $intcolor; 4015 break; 4016 } 4017 case 'text': { 4018 $pdfcolor .= $suffix; 4019 $this->TextColor = $pdfcolor; 4020 $this->fgcolor = $intcolor; 4021 break; 4022 } 4023 } 4024 $this->ColorFlag = ($this->FillColor != $this->TextColor); 4025 if (($type != 'text') AND ($this->state == 2)) { 4026 if (!$ret) { 4027 $this->_out($pdfcolor); 4028 } 4029 return $pdfcolor; 4030 } 4031 return ''; 4032 } 4033 4034 /** 4035 * 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. 4036 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 4037 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 4038 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 4039 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 4040 * @param $ret (boolean) If true do not send the command. 4041 * @param $name (string) spot color name (if any) 4042 * @return string the PDF command 4043 * @public 4044 * @since 1.3 4045 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() 4046 */ 4047 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 4048 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name); 4049 } 4050 4051 /** 4052 * 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. 4053 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 4054 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 4055 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 4056 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 4057 * @param $ret (boolean) If true do not send the command. 4058 * @param $name (string) Spot color name (if any). 4059 * @return (string) The PDF command. 4060 * @public 4061 * @since 1.3 4062 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() 4063 */ 4064 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 4065 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name); 4066 } 4067 4068 /** 4069 * 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. 4070 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). 4071 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). 4072 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). 4073 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). 4074 * @param $ret (boolean) If true do not send the command. 4075 * @param $name (string) Spot color name (if any). 4076 * @return (string) Empty string. 4077 * @public 4078 * @since 1.3 4079 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() 4080 */ 4081 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 4082 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name); 4083 } 4084 4085 /** 4086 * Returns the length of a string in user unit. A font must be selected.<br> 4087 * @param $s (string) The string whose length is to be computed 4088 * @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. 4089 * @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. 4090 * @param $fontsize (float) Font size in points. The default value is the current size. 4091 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length. 4092 * @return mixed int total string length or array of characted widths 4093 * @author Nicola Asuni 4094 * @public 4095 * @since 1.2 4096 */ 4097 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 4098 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); 4099 } 4100 4101 /** 4102 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br> 4103 * @param $sa (string) The array of chars whose total length is to be computed 4104 * @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. 4105 * @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. 4106 * @param $fontsize (float) Font size in points. The default value is the current size. 4107 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length. 4108 * @return mixed int total string length or array of characted widths 4109 * @author Nicola Asuni 4110 * @public 4111 * @since 2.4.000 (2008-03-06) 4112 */ 4113 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 4114 // store current values 4115 if (!TCPDF_STATIC::empty_string($fontname)) { 4116 $prev_FontFamily = $this->FontFamily; 4117 $prev_FontStyle = $this->FontStyle; 4118 $prev_FontSizePt = $this->FontSizePt; 4119 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false); 4120 } 4121 // convert UTF-8 array to Latin1 if required 4122 if ($this->isunicode AND (!$this->isUnicodeFont())) { 4123 $sa = TCPDF_FONTS::UTF8ArrToLatin1Arr($sa); 4124 } 4125 $w = 0; // total width 4126 $wa = array(); // array of characters widths 4127 foreach ($sa as $ck => $char) { 4128 // character width 4129 $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)])); 4130 $wa[] = $cw; 4131 $w += $cw; 4132 } 4133 // restore previous values 4134 if (!TCPDF_STATIC::empty_string($fontname)) { 4135 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false); 4136 } 4137 if ($getarray) { 4138 return $wa; 4139 } 4140 return $w; 4141 } 4142 4143 /** 4144 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking). 4145 * @param $char (int) The char code whose length is to be returned 4146 * @param $notlast (boolean) If false ignore the font-spacing. 4147 * @return float char width 4148 * @author Nicola Asuni 4149 * @public 4150 * @since 2.4.000 (2008-03-06) 4151 */ 4152 public function GetCharWidth($char, $notlast=true) { 4153 // get raw width 4154 $chw = $this->getRawCharWidth($char); 4155 if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) { 4156 // increase/decrease font spacing 4157 $chw += $this->font_spacing; 4158 } 4159 if ($this->font_stretching != 100) { 4160 // fixed stretching mode 4161 $chw *= ($this->font_stretching / 100); 4162 } 4163 return $chw; 4164 } 4165 4166 /** 4167 * Returns the length of the char in user unit for the current font. 4168 * @param $char (int) The char code whose length is to be returned 4169 * @return float char width 4170 * @author Nicola Asuni 4171 * @public 4172 * @since 5.9.000 (2010-09-28) 4173 */ 4174 public function getRawCharWidth($char) { 4175 if ($char == 173) { 4176 // SHY character will not be printed 4177 return (0); 4178 } 4179 if (isset($this->CurrentFont['cw'][$char])) { 4180 $w = $this->CurrentFont['cw'][$char]; 4181 } elseif (isset($this->CurrentFont['dw'])) { 4182 // default width 4183 $w = $this->CurrentFont['dw']; 4184 } elseif (isset($this->CurrentFont['cw'][32])) { 4185 // default width 4186 $w = $this->CurrentFont['cw'][32]; 4187 } else { 4188 $w = 600; 4189 } 4190 return $this->getAbsFontMeasure($w); 4191 } 4192 4193 /** 4194 * Returns the numbero of characters in a string. 4195 * @param $s (string) The input string. 4196 * @return int number of characters 4197 * @public 4198 * @since 2.0.0001 (2008-01-07) 4199 */ 4200 public function GetNumChars($s) { 4201 if ($this->isUnicodeFont()) { 4202 return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont)); 4203 } 4204 return strlen($s); 4205 } 4206 4207 /** 4208 * Fill the list of available fonts ($this->fontlist). 4209 * @protected 4210 * @since 4.0.013 (2008-07-28) 4211 */ 4212 protected function getFontsList() { 4213 if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) { 4214 while (($file = readdir($fontsdir)) !== false) { 4215 if (substr($file, -4) == '.php') { 4216 array_push($this->fontlist, strtolower(basename($file, '.php'))); 4217 } 4218 } 4219 closedir($fontsdir); 4220 } 4221 } 4222 4223 /** 4224 * Imports a TrueType, Type1, core, or CID0 font and makes it available. 4225 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 4226 * 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. 4227 * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. 4228 * @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> 4229 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 4230 * @return array containing the font data, or false in case of error. 4231 * @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. 4232 * @public 4233 * @since 1.5 4234 * @see SetFont(), setFontSubsetting() 4235 */ 4236 public function AddFont($family, $style='', $fontfile='', $subset='default') { 4237 if ($subset === 'default') { 4238 $subset = $this->font_subsetting; 4239 } 4240 if ($this->pdfa_mode) { 4241 $subset = false; 4242 } 4243 if (TCPDF_STATIC::empty_string($family)) { 4244 if (!TCPDF_STATIC::empty_string($this->FontFamily)) { 4245 $family = $this->FontFamily; 4246 } else { 4247 $this->Error('Empty font family'); 4248 } 4249 } 4250 // move embedded styles on $style 4251 if (substr($family, -1) == 'I') { 4252 $style .= 'I'; 4253 $family = substr($family, 0, -1); 4254 } 4255 if (substr($family, -1) == 'B') { 4256 $style .= 'B'; 4257 $family = substr($family, 0, -1); 4258 } 4259 // normalize family name 4260 $family = strtolower($family); 4261 if ((!$this->isunicode) AND ($family == 'arial')) { 4262 $family = 'helvetica'; 4263 } 4264 if (($family == 'symbol') OR ($family == 'zapfdingbats')) { 4265 $style = ''; 4266 } 4267 if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) { 4268 // all fonts must be embedded 4269 $family = 'pdfa'.$family; 4270 } 4271 $tempstyle = strtoupper($style); 4272 $style = ''; 4273 // underline 4274 if (strpos($tempstyle, 'U') !== false) { 4275 $this->underline = true; 4276 } else { 4277 $this->underline = false; 4278 } 4279 // line-through (deleted) 4280 if (strpos($tempstyle, 'D') !== false) { 4281 $this->linethrough = true; 4282 } else { 4283 $this->linethrough = false; 4284 } 4285 // overline 4286 if (strpos($tempstyle, 'O') !== false) { 4287 $this->overline = true; 4288 } else { 4289 $this->overline = false; 4290 } 4291 // bold 4292 if (strpos($tempstyle, 'B') !== false) { 4293 $style .= 'B'; 4294 } 4295 // oblique 4296 if (strpos($tempstyle, 'I') !== false) { 4297 $style .= 'I'; 4298 } 4299 $bistyle = $style; 4300 $fontkey = $family.$style; 4301 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : ''); 4302 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style); 4303 // check if the font has been already added 4304 $fb = $this->getFontBuffer($fontkey); 4305 if ($fb !== false) { 4306 if ($this->inxobj) { 4307 // we are inside an XObject template 4308 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i']; 4309 } 4310 return $fontdata; 4311 } 4312 // get specified font directory (if any) 4313 $fontdir = false; 4314 if (!TCPDF_STATIC::empty_string($fontfile)) { 4315 $fontdir = dirname($fontfile); 4316 if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) { 4317 $fontdir = ''; 4318 } else { 4319 $fontdir .= '/'; 4320 } 4321 } 4322 // true when the font style variation is missing 4323 $missing_style = false; 4324 // search and include font file 4325 if (TCPDF_STATIC::empty_string($fontfile) OR (!@TCPDF_STATIC::file_exists($fontfile))) { 4326 // build a standard filenames for specified font 4327 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php'; 4328 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir); 4329 if (TCPDF_STATIC::empty_string($fontfile)) { 4330 $missing_style = true; 4331 // try to remove the style part 4332 $tmp_fontfile = str_replace(' ', '', $family).'.php'; 4333 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir); 4334 } 4335 } 4336 // include font file 4337 if (!TCPDF_STATIC::empty_string($fontfile) AND (@TCPDF_STATIC::file_exists($fontfile))) { 4338 include($fontfile); 4339 } else { 4340 $this->Error('Could not include font definition file: '.$family.''); 4341 } 4342 // check font parameters 4343 if ((!isset($type)) OR (!isset($cw))) { 4344 $this->Error('The font definition file has a bad format: '.$fontfile.''); 4345 } 4346 // SET default parameters 4347 if (!isset($file) OR TCPDF_STATIC::empty_string($file)) { 4348 $file = ''; 4349 } 4350 if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) { 4351 $enc = ''; 4352 } 4353 if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) { 4354 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0); 4355 $cidinfo['uni2cid'] = array(); 4356 } 4357 if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) { 4358 $ctg = ''; 4359 } 4360 if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) { 4361 $desc = array(); 4362 } 4363 if (!isset($up) OR TCPDF_STATIC::empty_string($up)) { 4364 $up = -100; 4365 } 4366 if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) { 4367 $ut = 50; 4368 } 4369 if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) { 4370 $cw = array(); 4371 } 4372 if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) { 4373 // set default width 4374 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) { 4375 $dw = $desc['MissingWidth']; 4376 } elseif (isset($cw[32])) { 4377 $dw = $cw[32]; 4378 } else { 4379 $dw = 600; 4380 } 4381 } 4382 ++$this->numfonts; 4383 if ($type == 'core') { 4384 $name = $this->CoreFonts[$fontkey]; 4385 $subset = false; 4386 } elseif (($type == 'TrueType') OR ($type == 'Type1')) { 4387 $subset = false; 4388 } elseif ($type == 'TrueTypeUnicode') { 4389 $enc = 'Identity-H'; 4390 } elseif ($type == 'cidfont0') { 4391 if ($this->pdfa_mode) { 4392 $this->Error('All fonts must be embedded in PDF/A mode!'); 4393 } 4394 } else { 4395 $this->Error('Unknow font type: '.$type.''); 4396 } 4397 // set name if unset 4398 if (!isset($name) OR empty($name)) { 4399 $name = $fontkey; 4400 } 4401 // create artificial font style variations if missing (only works with non-embedded fonts) 4402 if (($type != 'core') AND $missing_style) { 4403 // style variations 4404 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic'); 4405 $name .= $styles[$bistyle]; 4406 // artificial bold 4407 if (strpos($bistyle, 'B') !== false) { 4408 if (isset($desc['StemV'])) { 4409 // from normal to bold 4410 $desc['StemV'] = round($desc['StemV'] * 1.75); 4411 } else { 4412 // bold 4413 $desc['StemV'] = 123; 4414 } 4415 } 4416 // artificial italic 4417 if (strpos($bistyle, 'I') !== false) { 4418 if (isset($desc['ItalicAngle'])) { 4419 $desc['ItalicAngle'] -= 11; 4420 } else { 4421 $desc['ItalicAngle'] = -11; 4422 } 4423 if (isset($desc['Flags'])) { 4424 $desc['Flags'] |= 64; //bit 7 4425 } else { 4426 $desc['Flags'] = 64; 4427 } 4428 } 4429 } 4430 // check if the array of characters bounding boxes is defined 4431 if (!isset($cbbox)) { 4432 $cbbox = array(); 4433 } 4434 // initialize subsetchars 4435 $subsetchars = array_fill(0, 255, true); 4436 $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)); 4437 if ($this->inxobj) { 4438 // we are inside an XObject template 4439 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts; 4440 } 4441 if (isset($diff) AND (!empty($diff))) { 4442 //Search existing encodings 4443 $d = 0; 4444 $nb = count($this->diffs); 4445 for ($i=1; $i <= $nb; ++$i) { 4446 if ($this->diffs[$i] == $diff) { 4447 $d = $i; 4448 break; 4449 } 4450 } 4451 if ($d == 0) { 4452 $d = $nb + 1; 4453 $this->diffs[$d] = $diff; 4454 } 4455 $this->setFontSubBuffer($fontkey, 'diff', $d); 4456 } 4457 if (!TCPDF_STATIC::empty_string($file)) { 4458 if (!isset($this->FontFiles[$file])) { 4459 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) { 4460 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); 4461 } elseif ($type != 'core') { 4462 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); 4463 } 4464 } else { 4465 // update fontkeys that are sharing this font file 4466 $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset); 4467 if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) { 4468 $this->FontFiles[$file]['fontkeys'][] = $fontkey; 4469 } 4470 } 4471 } 4472 return $fontdata; 4473 } 4474 4475 /** 4476 * Sets the font used to print character strings. 4477 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). 4478 * The method can be called before the first page is created and the font is retained from page to page. 4479 * If you just wish to change the current font size, it is simpler to call SetFontSize(). 4480 * 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 /> 4481 * @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. 4482 * @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. 4483 * @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 4484 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 4485 * @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. 4486 * @param $out (boolean) if true output the font size command, otherwise only set the font properties. 4487 * @author Nicola Asuni 4488 * @public 4489 * @since 1.0 4490 * @see AddFont(), SetFontSize() 4491 */ 4492 public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) { 4493 //Select a font; size given in points 4494 if ($size === null) { 4495 $size = $this->FontSizePt; 4496 } 4497 if ($size < 0) { 4498 $size = 0; 4499 } 4500 // try to add font (if not already added) 4501 $fontdata = $this->AddFont($family, $style, $fontfile, $subset); 4502 $this->FontFamily = $fontdata['family']; 4503 $this->FontStyle = $fontdata['style']; 4504 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) { 4505 // save subset chars of the previous font 4506 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 4507 } 4508 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']); 4509 $this->SetFontSize($size, $out); 4510 } 4511 4512 /** 4513 * Defines the size of the current font. 4514 * @param $size (float) The font size in points. 4515 * @param $out (boolean) if true output the font size command, otherwise only set the font properties. 4516 * @public 4517 * @since 1.0 4518 * @see SetFont() 4519 */ 4520 public function SetFontSize($size, $out=true) { 4521 $size = (float)$size; 4522 // font size in points 4523 $this->FontSizePt = $size; 4524 // font size in user units 4525 $this->FontSize = $size / $this->k; 4526 // calculate some font metrics 4527 if (isset($this->CurrentFont['desc']['FontBBox'])) { 4528 $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); 4529 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000); 4530 } else { 4531 $font_height = $size * 1.219; 4532 } 4533 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) { 4534 $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000); 4535 } 4536 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) { 4537 $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000); 4538 } 4539 if (!isset($font_ascent) AND !isset($font_descent)) { 4540 // core font 4541 $font_ascent = 0.76 * $font_height; 4542 $font_descent = $font_height - $font_ascent; 4543 } elseif (!isset($font_descent)) { 4544 $font_descent = $font_height - $font_ascent; 4545 } elseif (!isset($font_ascent)) { 4546 $font_ascent = $font_height - $font_descent; 4547 } 4548 $this->FontAscent = ($font_ascent / $this->k); 4549 $this->FontDescent = ($font_descent / $this->k); 4550 if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) { 4551 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 4552 } 4553 } 4554 4555 /** 4556 * Returns the bounding box of the current font in user units. 4557 * @return array 4558 * @public 4559 * @since 5.9.152 (2012-03-23) 4560 */ 4561 public function getFontBBox() { 4562 $fbbox = array(); 4563 if (isset($this->CurrentFont['desc']['FontBBox'])) { 4564 $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); 4565 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox); 4566 } else { 4567 // Find max width 4568 if (isset($this->CurrentFont['desc']['MaxWidth'])) { 4569 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth'])); 4570 } else { 4571 $maxw = 0; 4572 if (isset($this->CurrentFont['desc']['MissingWidth'])) { 4573 $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']); 4574 } 4575 if (isset($this->CurrentFont['desc']['AvgWidth'])) { 4576 $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']); 4577 } 4578 if (isset($this->CurrentFont['dw'])) { 4579 $maxw = max($maxw, $this->CurrentFont['dw']); 4580 } 4581 foreach ($this->CurrentFont['cw'] as $char => $w) { 4582 $maxw = max($maxw, $w); 4583 } 4584 if ($maxw == 0) { 4585 $maxw = 600; 4586 } 4587 $maxw = $this->getAbsFontMeasure($maxw); 4588 } 4589 $fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent); 4590 } 4591 return $fbbox; 4592 } 4593 4594 /** 4595 * Convert a relative font measure into absolute value. 4596 * @param $s (int) Font measure. 4597 * @return float Absolute measure. 4598 * @since 5.9.186 (2012-09-13) 4599 */ 4600 public function getAbsFontMeasure($s) { 4601 return ($s * $this->FontSize / 1000); 4602 } 4603 4604 /** 4605 * Returns the glyph bounding box of the specified character in the current font in user units. 4606 * @param $char (int) Input character code. 4607 * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined. 4608 * @since 5.9.186 (2012-09-13) 4609 */ 4610 public function getCharBBox($char) { 4611 $c = intval($char); 4612 if (isset($this->CurrentFont['cw'][$c])) { 4613 // glyph is defined ... use zero width & height for glyphs without outlines 4614 $result = array(0,0,0,0); 4615 if (isset($this->CurrentFont['cbbox'][$c])) { 4616 $result = $this->CurrentFont['cbbox'][$c]; 4617 } 4618 return array_map(array($this,'getAbsFontMeasure'), $result); 4619 } 4620 return false; 4621 } 4622 4623 /** 4624 * Return the font descent value 4625 * @param $font (string) font name 4626 * @param $style (string) font style 4627 * @param $size (float) The size (in points) 4628 * @return int font descent 4629 * @public 4630 * @author Nicola Asuni 4631 * @since 4.9.003 (2010-03-30) 4632 */ 4633 public function getFontDescent($font, $style='', $size=0) { 4634 $fontdata = $this->AddFont($font, $style); 4635 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4636 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) { 4637 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000); 4638 } else { 4639 $descent = (1.219 * 0.24 * $size); 4640 } 4641 return ($descent / $this->k); 4642 } 4643 4644 /** 4645 * Return the font ascent value. 4646 * @param $font (string) font name 4647 * @param $style (string) font style 4648 * @param $size (float) The size (in points) 4649 * @return int font ascent 4650 * @public 4651 * @author Nicola Asuni 4652 * @since 4.9.003 (2010-03-30) 4653 */ 4654 public function getFontAscent($font, $style='', $size=0) { 4655 $fontdata = $this->AddFont($font, $style); 4656 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4657 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) { 4658 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000); 4659 } else { 4660 $ascent = 1.219 * 0.76 * $size; 4661 } 4662 return ($ascent / $this->k); 4663 } 4664 4665 /** 4666 * Return true in the character is present in the specified font. 4667 * @param $char (mixed) Character to check (integer value or string) 4668 * @param $font (string) Font name (family name). 4669 * @param $style (string) Font style. 4670 * @return (boolean) true if the char is defined, false otherwise. 4671 * @public 4672 * @since 5.9.153 (2012-03-28) 4673 */ 4674 public function isCharDefined($char, $font='', $style='') { 4675 if (is_string($char)) { 4676 // get character code 4677 $char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont); 4678 $char = $char[0]; 4679 } 4680 if (TCPDF_STATIC::empty_string($font)) { 4681 if (TCPDF_STATIC::empty_string($style)) { 4682 return (isset($this->CurrentFont['cw'][intval($char)])); 4683 } 4684 $font = $this->FontFamily; 4685 } 4686 $fontdata = $this->AddFont($font, $style); 4687 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4688 return (isset($fontinfo['cw'][intval($char)])); 4689 } 4690 4691 /** 4692 * Replace missing font characters on selected font with specified substitutions. 4693 * @param $text (string) Text to process. 4694 * @param $font (string) Font name (family name). 4695 * @param $style (string) Font style. 4696 * @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. 4697 * @return (string) Processed text. 4698 * @public 4699 * @since 5.9.153 (2012-03-28) 4700 */ 4701 public function replaceMissingChars($text, $font='', $style='', $subs=array()) { 4702 if (empty($subs)) { 4703 return $text; 4704 } 4705 if (TCPDF_STATIC::empty_string($font)) { 4706 $font = $this->FontFamily; 4707 } 4708 $fontdata = $this->AddFont($font, $style); 4709 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 4710 $uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont); 4711 foreach ($uniarr as $k => $chr) { 4712 if (!isset($fontinfo['cw'][$chr])) { 4713 // this character is missing on the selected font 4714 if (isset($subs[$chr])) { 4715 // we have available substitutions 4716 if (is_array($subs[$chr])) { 4717 foreach($subs[$chr] as $s) { 4718 if (isset($fontinfo['cw'][$s])) { 4719 $uniarr[$k] = $s; 4720 break; 4721 } 4722 } 4723 } elseif (isset($fontinfo['cw'][$subs[$chr]])) { 4724 $uniarr[$k] = $subs[$chr]; 4725 } 4726 } 4727 } 4728 } 4729 return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode)); 4730 } 4731 4732 /** 4733 * Defines the default monospaced font. 4734 * @param $font (string) Font name. 4735 * @public 4736 * @since 4.5.025 4737 */ 4738 public function SetDefaultMonospacedFont($font) { 4739 $this->default_monospaced_font = $font; 4740 } 4741 4742 /** 4743 * 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 /> 4744 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). 4745 * @public 4746 * @since 1.5 4747 * @see Cell(), Write(), Image(), Link(), SetLink() 4748 */ 4749 public function AddLink() { 4750 // create a new internal link 4751 $n = count($this->links) + 1; 4752 $this->links[$n] = array('p' => 0, 'y' => 0, 'f' => false); 4753 return $n; 4754 } 4755 4756 /** 4757 * Defines the page and position a link points to. 4758 * @param $link (int) The link identifier returned by AddLink() 4759 * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) 4760 * @param $page (int|string) 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. 4761 * @public 4762 * @since 1.5 4763 * @see AddLink() 4764 */ 4765 public function SetLink($link, $y=0, $page=-1) { 4766 $fixed = false; 4767 if (!empty($page) AND (substr($page, 0, 1) == '*')) { 4768 $page = intval(substr($page, 1)); 4769 // this page number will not be changed when moving/add/deleting pages 4770 $fixed = true; 4771 } 4772 if ($page < 0) { 4773 $page = $this->page; 4774 } 4775 if ($y == -1) { 4776 $y = $this->y; 4777 } 4778 $this->links[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed); 4779 } 4780 4781 /** 4782 * Puts a link on a rectangular area of the page. 4783 * 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. 4784 * @param $x (float) Abscissa of the upper-left corner of the rectangle 4785 * @param $y (float) Ordinate of the upper-left corner of the rectangle 4786 * @param $w (float) Width of the rectangle 4787 * @param $h (float) Height of the rectangle 4788 * @param $link (mixed) URL or identifier returned by AddLink() 4789 * @param $spaces (int) number of spaces on the text to link 4790 * @public 4791 * @since 1.5 4792 * @see AddLink(), Annotation(), Cell(), Write(), Image() 4793 */ 4794 public function Link($x, $y, $w, $h, $link, $spaces=0) { 4795 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces); 4796 } 4797 4798 /** 4799 * Puts a markup annotation on a rectangular area of the page. 4800 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!! 4801 * @param $x (float) Abscissa of the upper-left corner of the rectangle 4802 * @param $y (float) Ordinate of the upper-left corner of the rectangle 4803 * @param $w (float) Width of the rectangle 4804 * @param $h (float) Height of the rectangle 4805 * @param $text (string) annotation text or alternate content 4806 * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7). 4807 * @param $spaces (int) number of spaces on the text to link 4808 * @public 4809 * @since 4.0.018 (2008-08-06) 4810 */ 4811 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) { 4812 if ($this->inxobj) { 4813 // store parameters for later use on template 4814 $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces); 4815 return; 4816 } 4817 if ($x === '') { 4818 $x = $this->x; 4819 } 4820 if ($y === '') { 4821 $y = $this->y; 4822 } 4823 // check page for no-write regions and adapt page margins if necessary 4824 list($x, $y) = $this->checkPageRegions($h, $x, $y); 4825 // recalculate coordinates to account for graphic transformations 4826 if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) { 4827 for ($i=$this->transfmatrix_key; $i > 0; --$i) { 4828 $maxid = count($this->transfmatrix[$i]) - 1; 4829 for ($j=$maxid; $j >= 0; --$j) { 4830 $ctm = $this->transfmatrix[$i][$j]; 4831 if (isset($ctm['a'])) { 4832 $x = $x * $this->k; 4833 $y = ($this->h - $y) * $this->k; 4834 $w = $w * $this->k; 4835 $h = $h * $this->k; 4836 // top left 4837 $xt = $x; 4838 $yt = $y; 4839 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4840 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4841 // top right 4842 $xt = $x + $w; 4843 $yt = $y; 4844 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4845 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4846 // bottom left 4847 $xt = $x; 4848 $yt = $y - $h; 4849 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4850 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4851 // bottom right 4852 $xt = $x + $w; 4853 $yt = $y - $h; 4854 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 4855 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 4856 // new coordinates (rectangle area) 4857 $x = min($x1, $x2, $x3, $x4); 4858 $y = max($y1, $y2, $y3, $y4); 4859 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k; 4860 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k; 4861 $x = $x / $this->k; 4862 $y = $this->h - ($y / $this->k); 4863 } 4864 } 4865 } 4866 } 4867 if ($this->page <= 0) { 4868 $page = 1; 4869 } else { 4870 $page = $this->page; 4871 } 4872 if (!isset($this->PageAnnots[$page])) { 4873 $this->PageAnnots[$page] = array(); 4874 } 4875 $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); 4876 if (!$this->pdfa_mode || ($this->pdfa_mode && $this->pdfa_version == 3)) { 4877 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS'])) 4878 AND (@TCPDF_STATIC::file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS'])) 4879 AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) { 4880 $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']); 4881 } 4882 } 4883 // Add widgets annotation's icons 4884 if (isset($opt['mk']['i']) AND @TCPDF_STATIC::file_exists($opt['mk']['i'])) { 4885 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true); 4886 } 4887 if (isset($opt['mk']['ri']) AND @TCPDF_STATIC::file_exists($opt['mk']['ri'])) { 4888 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 4889 } 4890 if (isset($opt['mk']['ix']) AND @TCPDF_STATIC::file_exists($opt['mk']['ix'])) { 4891 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 4892 } 4893 } 4894 4895 /** 4896 * Embedd the attached files. 4897 * @since 4.4.000 (2008-12-07) 4898 * @protected 4899 * @see Annotation() 4900 */ 4901 protected function _putEmbeddedFiles() { 4902 if ($this->pdfa_mode && $this->pdfa_version != 3) { 4903 // embedded files are not allowed in PDF/A mode version 1 and 2 4904 return; 4905 } 4906 reset($this->embeddedfiles); 4907 foreach ($this->embeddedfiles as $filename => $filedata) { 4908 $data = $this->getCachedFileContents($filedata['file']); 4909 if ($data !== FALSE) { 4910 $rawsize = strlen($data); 4911 if ($rawsize > 0) { 4912 // update name tree 4913 $this->efnames[$filename] = $filedata['f'].' 0 R'; 4914 // embedded file specification object 4915 $out = $this->_getobj($filedata['f'])."\n"; 4916 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']); 4917 $out .= ' /UF '.$this->_datastring($filename, $filedata['f']); 4918 $out .= ' /AFRelationship /Source'; 4919 $out .= ' /EF <</F '.$filedata['n'].' 0 R>> >>'; 4920 $out .= "\n".'endobj'; 4921 $this->_out($out); 4922 // embedded file object 4923 $filter = ''; 4924 if ($this->compress) { 4925 $data = gzcompress($data); 4926 $filter = ' /Filter /FlateDecode'; 4927 } 4928 4929 if ($this->pdfa_version == 3) { 4930 $filter = ' /Subtype /text#2Fxml'; 4931 } 4932 4933 $stream = $this->_getrawstream($data, $filedata['n']); 4934 $out = $this->_getobj($filedata['n'])."\n"; 4935 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>'; 4936 $out .= ' stream'."\n".$stream."\n".'endstream'; 4937 $out .= "\n".'endobj'; 4938 $this->_out($out); 4939 } 4940 } 4941 } 4942 } 4943 4944 /** 4945 * Prints a text cell at the specified position. 4946 * This method allows to place a string precisely on the page. 4947 * @param $x (float) Abscissa of the cell origin 4948 * @param $y (float) Ordinate of the cell origin 4949 * @param $txt (string) String to print 4950 * @param $fstroke (int) outline size in user units (false = disable) 4951 * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation). 4952 * @param $ffill (boolean) if true fills the text 4953 * @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))) 4954 * @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. 4955 * @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> 4956 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 4957 * @param $link (mixed) URL or identifier returned by AddLink(). 4958 * @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. 4959 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 4960 * @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> 4961 * @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> 4962 * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position. 4963 * @public 4964 * @since 1.0 4965 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell() 4966 */ 4967 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) { 4968 $textrendermode = $this->textrendermode; 4969 $textstrokewidth = $this->textstrokewidth; 4970 $this->setTextRenderingMode($fstroke, $ffill, $fclip); 4971 $this->SetXY($x, $y, $rtloff); 4972 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign); 4973 // restore previous rendering mode 4974 $this->textrendermode = $textrendermode; 4975 $this->textstrokewidth = $textstrokewidth; 4976 } 4977 4978 /** 4979 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 4980 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> 4981 * This method is called automatically and should not be called directly by the application. 4982 * @return boolean 4983 * @public 4984 * @since 1.4 4985 * @see SetAutoPageBreak() 4986 */ 4987 public function AcceptPageBreak() { 4988 if ($this->num_columns > 1) { 4989 // multi column mode 4990 if ($this->current_column < ($this->num_columns - 1)) { 4991 // go to next column 4992 $this->selectColumn($this->current_column + 1); 4993 } elseif ($this->AutoPageBreak) { 4994 // add a new page 4995 $this->AddPage(); 4996 // set first column 4997 $this->selectColumn(0); 4998 } 4999 // avoid page breaking from checkPageBreak() 5000 return false; 5001 } 5002 return $this->AutoPageBreak; 5003 } 5004 5005 /** 5006 * Add page if needed. 5007 * @param $h (float) Cell height. Default value: 0. 5008 * @param $y (mixed) starting y position, leave empty for current position. 5009 * @param $addpage (boolean) if true add a page, otherwise only return the true/false state 5010 * @return boolean true in case of page break, false otherwise. 5011 * @since 3.2.000 (2008-07-01) 5012 * @protected 5013 */ 5014 protected function checkPageBreak($h=0, $y='', $addpage=true) { 5015 if (TCPDF_STATIC::empty_string($y)) { 5016 $y = $this->y; 5017 } 5018 $current_page = $this->page; 5019 if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) { 5020 if ($addpage) { 5021 //Automatic page break 5022 $x = $this->x; 5023 $this->AddPage($this->CurOrientation); 5024 $this->y = $this->tMargin; 5025 $oldpage = $this->page - 1; 5026 if ($this->rtl) { 5027 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) { 5028 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']); 5029 } else { 5030 $this->x = $x; 5031 } 5032 } else { 5033 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 5034 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']); 5035 } else { 5036 $this->x = $x; 5037 } 5038 } 5039 } 5040 return true; 5041 } 5042 if ($current_page != $this->page) { 5043 // account for columns mode 5044 return true; 5045 } 5046 return false; 5047 } 5048 5049 /** 5050 * 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 /> 5051 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 5052 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 5053 * @param $h (float) Cell height. Default value: 0. 5054 * @param $txt (string) String to print. Default value: empty string. 5055 * @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))) 5056 * @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. 5057 * @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> 5058 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 5059 * @param $link (mixed) URL or identifier returned by AddLink(). 5060 * @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. 5061 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 5062 * @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> 5063 * @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> 5064 * @public 5065 * @since 1.0 5066 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() 5067 */ 5068 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') { 5069 $prev_cell_margin = $this->cell_margin; 5070 $prev_cell_padding = $this->cell_padding; 5071 $this->adjustCellPadding($border); 5072 if (!$ignore_min_height) { 5073 $min_cell_height = $this->getCellHeight($this->FontSize); 5074 if ($h < $min_cell_height) { 5075 $h = $min_cell_height; 5076 } 5077 } 5078 $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']); 5079 // apply text shadow if enabled 5080 if ($this->txtshadow['enabled']) { 5081 // save data 5082 $x = $this->x; 5083 $y = $this->y; 5084 $bc = $this->bgcolor; 5085 $fc = $this->fgcolor; 5086 $sc = $this->strokecolor; 5087 $alpha = $this->alpha; 5088 // print shadow 5089 $this->x += $this->txtshadow['depth_w']; 5090 $this->y += $this->txtshadow['depth_h']; 5091 $this->SetFillColorArray($this->txtshadow['color']); 5092 $this->SetTextColorArray($this->txtshadow['color']); 5093 $this->SetDrawColorArray($this->txtshadow['color']); 5094 if ($this->txtshadow['opacity'] != $alpha['CA']) { 5095 $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']); 5096 } 5097 if ($this->state == 2) { 5098 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); 5099 } 5100 //restore data 5101 $this->x = $x; 5102 $this->y = $y; 5103 $this->SetFillColorArray($bc); 5104 $this->SetTextColorArray($fc); 5105 $this->SetDrawColorArray($sc); 5106 if ($this->txtshadow['opacity'] != $alpha['CA']) { 5107 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']); 5108 } 5109 } 5110 if ($this->state == 2) { 5111 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); 5112 } 5113 $this->cell_padding = $prev_cell_padding; 5114 $this->cell_margin = $prev_cell_margin; 5115 } 5116 5117 /** 5118 * 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 /> 5119 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 5120 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 5121 * @param $h (float) Cell height. Default value: 0. 5122 * @param $txt (string) String to print. Default value: empty string. 5123 * @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))) 5124 * @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. 5125 * @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> 5126 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 5127 * @param $link (mixed) URL or identifier returned by AddLink(). 5128 * @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. 5129 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. 5130 * @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> 5131 * @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> 5132 * @return string containing cell code 5133 * @protected 5134 * @since 1.0 5135 * @see Cell() 5136 */ 5137 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') { 5138 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space 5139 $txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt); 5140 $prev_cell_margin = $this->cell_margin; 5141 $prev_cell_padding = $this->cell_padding; 5142 $txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode); 5143 $rs = ''; //string to be returned 5144 $this->adjustCellPadding($border); 5145 if (!$ignore_min_height) { 5146 $min_cell_height = $this->getCellHeight($this->FontSize); 5147 if ($h < $min_cell_height) { 5148 $h = $min_cell_height; 5149 } 5150 } 5151 $k = $this->k; 5152 // check page for no-write regions and adapt page margins if necessary 5153 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); 5154 if ($this->rtl) { 5155 $x = $this->x - $this->cell_margin['R']; 5156 } else { 5157 $x = $this->x + $this->cell_margin['L']; 5158 } 5159 $y = $this->y + $this->cell_margin['T']; 5160 $prev_font_stretching = $this->font_stretching; 5161 $prev_font_spacing = $this->font_spacing; 5162 // cell vertical alignment 5163 switch ($calign) { 5164 case 'A': { 5165 // font top 5166 switch ($valign) { 5167 case 'T': { 5168 // top 5169 $y -= $this->cell_padding['T']; 5170 break; 5171 } 5172 case 'B': { 5173 // bottom 5174 $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent); 5175 break; 5176 } 5177 default: 5178 case 'C': 5179 case 'M': { 5180 // center 5181 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2); 5182 break; 5183 } 5184 } 5185 break; 5186 } 5187 case 'L': { 5188 // font baseline 5189 switch ($valign) { 5190 case 'T': { 5191 // top 5192 $y -= ($this->cell_padding['T'] + $this->FontAscent); 5193 break; 5194 } 5195 case 'B': { 5196 // bottom 5197 $y -= ($h - $this->cell_padding['B'] - $this->FontDescent); 5198 break; 5199 } 5200 default: 5201 case 'C': 5202 case 'M': { 5203 // center 5204 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2); 5205 break; 5206 } 5207 } 5208 break; 5209 } 5210 case 'D': { 5211 // font bottom 5212 switch ($valign) { 5213 case 'T': { 5214 // top 5215 $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent); 5216 break; 5217 } 5218 case 'B': { 5219 // bottom 5220 $y -= ($h - $this->cell_padding['B']); 5221 break; 5222 } 5223 default: 5224 case 'C': 5225 case 'M': { 5226 // center 5227 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2); 5228 break; 5229 } 5230 } 5231 break; 5232 } 5233 case 'B': { 5234 // cell bottom 5235 $y -= $h; 5236 break; 5237 } 5238 case 'C': 5239 case 'M': { 5240 // cell center 5241 $y -= ($h / 2); 5242 break; 5243 } 5244 default: 5245 case 'T': { 5246 // cell top 5247 break; 5248 } 5249 } 5250 // text vertical alignment 5251 switch ($valign) { 5252 case 'T': { 5253 // top 5254 $yt = $y + $this->cell_padding['T']; 5255 break; 5256 } 5257 case 'B': { 5258 // bottom 5259 $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent; 5260 break; 5261 } 5262 default: 5263 case 'C': 5264 case 'M': { 5265 // center 5266 $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2); 5267 break; 5268 } 5269 } 5270 $basefonty = $yt + $this->FontAscent; 5271 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 5272 if ($this->rtl) { 5273 $w = $x - $this->lMargin; 5274 } else { 5275 $w = $this->w - $this->rMargin - $x; 5276 } 5277 } 5278 $s = ''; 5279 // fill and borders 5280 if (is_string($border) AND (strlen($border) == 4)) { 5281 // full border 5282 $border = 1; 5283 } 5284 if ($fill OR ($border == 1)) { 5285 if ($fill) { 5286 $op = ($border == 1) ? 'B' : 'f'; 5287 } else { 5288 $op = 'S'; 5289 } 5290 if ($this->rtl) { 5291 $xk = (($x - $w) * $k); 5292 } else { 5293 $xk = ($x * $k); 5294 } 5295 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op); 5296 } 5297 // draw borders 5298 $s .= $this->getCellBorder($x, $y, $w, $h, $border); 5299 if ($txt != '') { 5300 $txt2 = $txt; 5301 if ($this->isunicode) { 5302 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 5303 $txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont); 5304 } else { 5305 $unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values 5306 $unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont); 5307 // replace thai chars (if any) 5308 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) { 5309 // number of chars 5310 $numchars = count($unicode); 5311 // po pla, for far, for fan 5312 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f); 5313 // do chada, to patak 5314 $lowtail = array(0x0e0e, 0x0e0f); 5315 // mai hun arkad, sara i, sara ii, sara ue, sara uee 5316 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37); 5317 // mai ek, mai tho, mai tri, mai chattawa, karan 5318 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c); 5319 // sara u, sara uu, pinthu 5320 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a); 5321 $output = array(); 5322 for ($i = 0; $i < $numchars; $i++) { 5323 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) { 5324 $ch0 = $unicode[$i]; 5325 $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0; 5326 $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0; 5327 $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0; 5328 if (in_array($ch0, $tonemark)) { 5329 if ($chn == 0x0e33) { 5330 // sara um 5331 if (in_array($ch1, $longtail)) { 5332 // tonemark at upper left 5333 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); 5334 } else { 5335 // tonemark at upper right (normal position) 5336 $output[] = $ch0; 5337 } 5338 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) { 5339 // tonemark at lower left 5340 $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48)); 5341 } elseif (in_array($ch1, $upvowel)) { 5342 if (in_array($ch2, $longtail)) { 5343 // tonemark at upper left 5344 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); 5345 } else { 5346 // tonemark at upper right (normal position) 5347 $output[] = $ch0; 5348 } 5349 } else { 5350 // tonemark at lower right 5351 $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48)); 5352 } 5353 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) { 5354 // add lower left nikhahit and sara aa 5355 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) { 5356 $output[] = 0xf711; 5357 $this->CurrentFont['subsetchars'][0xf711] = true; 5358 $output[] = 0x0e32; 5359 $this->CurrentFont['subsetchars'][0x0e32] = true; 5360 } else { 5361 $output[] = $ch0; 5362 } 5363 } elseif (in_array($ch1, $longtail)) { 5364 if ($ch0 == 0x0e31) { 5365 // lower left mai hun arkad 5366 $output[] = $this->replaceChar($ch0, 0xf710); 5367 } elseif (in_array($ch0, $upvowel)) { 5368 // lower left 5369 $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34)); 5370 } elseif ($ch0 == 0x0e47) { 5371 // lower left mai tai koo 5372 $output[] = $this->replaceChar($ch0, 0xf712); 5373 } else { 5374 // normal character 5375 $output[] = $ch0; 5376 } 5377 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) { 5378 // lower vowel 5379 $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38)); 5380 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) { 5381 // yo ying without lower part 5382 $output[] = $this->replaceChar($ch0, 0xf70f); 5383 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) { 5384 // tho santan without lower part 5385 $output[] = $this->replaceChar($ch0, 0xf700); 5386 } else { 5387 $output[] = $ch0; 5388 } 5389 } else { 5390 // non-thai character 5391 $output[] = $unicode[$i]; 5392 } 5393 } 5394 $unicode = $output; 5395 // update font subsetchars 5396 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 5397 } // end of K_THAI_TOPCHARS 5398 $txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false); 5399 } 5400 } 5401 $txt2 = TCPDF_STATIC::_escape($txt2); 5402 // get current text width (considering general font stretching and spacing) 5403 $txwidth = $this->GetStringWidth($txt); 5404 $width = $txwidth; 5405 // check for stretch mode 5406 if ($stretch > 0) { 5407 // calculate ratio between cell width and text width 5408 if ($width <= 0) { 5409 $ratio = 1; 5410 } else { 5411 $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width); 5412 } 5413 // check if stretching is required 5414 if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) { 5415 // the text will be stretched to fit cell width 5416 if ($stretch > 2) { 5417 // set new character spacing 5418 $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100)); 5419 } else { 5420 // set new horizontal stretching 5421 $this->font_stretching *= $ratio; 5422 } 5423 // recalculate text width (the text fills the entire cell) 5424 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 5425 // reset alignment 5426 $align = ''; 5427 } 5428 } 5429 if ($this->font_stretching != 100) { 5430 // apply font stretching 5431 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching); 5432 } 5433 if ($this->font_spacing != 0) { 5434 // increase/decrease font spacing 5435 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k)); 5436 } 5437 if ($this->ColorFlag AND ($this->textrendermode < 4)) { 5438 $s .= 'q '.$this->TextColor.' '; 5439 } 5440 // rendering mode 5441 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k)); 5442 // count number of spaces 5443 $ns = substr_count($txt, chr(32)); 5444 // Justification 5445 $spacewidth = 0; 5446 if (($align == 'J') AND ($ns > 0)) { 5447 if ($this->isUnicodeFont()) { 5448 // get string width without spaces 5449 $width = $this->GetStringWidth(str_replace(' ', '', $txt)); 5450 // calculate average space width 5451 $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1); 5452 if ($this->font_stretching != 100) { 5453 // word spacing is affected by stretching 5454 $spacewidth /= ($this->font_stretching / 100); 5455 } 5456 // set word position to be used with TJ operator 5457 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2); 5458 $unicode_justification = true; 5459 } else { 5460 // get string width 5461 $width = $txwidth; 5462 // new space width 5463 $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k; 5464 if ($this->font_stretching != 100) { 5465 // word spacing (Tw) is affected by stretching 5466 $spacewidth /= ($this->font_stretching / 100); 5467 } 5468 // set word spacing 5469 $rs .= sprintf('BT %F Tw ET ', $spacewidth); 5470 } 5471 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 5472 } 5473 // replace carriage return characters 5474 $txt2 = str_replace("\r", ' ', $txt2); 5475 switch ($align) { 5476 case 'C': { 5477 $dx = ($w - $width) / 2; 5478 break; 5479 } 5480 case 'R': { 5481 if ($this->rtl) { 5482 $dx = $this->cell_padding['R']; 5483 } else { 5484 $dx = $w - $width - $this->cell_padding['R']; 5485 } 5486 break; 5487 } 5488 case 'L': { 5489 if ($this->rtl) { 5490 $dx = $w - $width - $this->cell_padding['L']; 5491 } else { 5492 $dx = $this->cell_padding['L']; 5493 } 5494 break; 5495 } 5496 case 'J': 5497 default: { 5498 if ($this->rtl) { 5499 $dx = $this->cell_padding['R']; 5500 } else { 5501 $dx = $this->cell_padding['L']; 5502 } 5503 break; 5504 } 5505 } 5506 if ($this->rtl) { 5507 $xdx = $x - $dx - $width; 5508 } else { 5509 $xdx = $x + $dx; 5510 } 5511 $xdk = $xdx * $k; 5512 // print text 5513 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2); 5514 if (isset($uniblock)) { 5515 // print overlapping characters as separate string 5516 $xshift = 0; // horizontal shift 5517 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k); 5518 $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)); 5519 foreach ($uniblock as $uk => $uniarr) { 5520 if (($uk % 2) == 0) { 5521 // x space to skip 5522 if ($spacewidth != 0) { 5523 // justification shift 5524 $xshift += (count(array_keys($uniarr, 32)) * $spw); 5525 } 5526 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification 5527 } else { 5528 // character to print 5529 $topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false); 5530 $topchr = TCPDF_STATIC::_escape($topchr); 5531 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr); 5532 } 5533 } 5534 } 5535 if ($this->underline) { 5536 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width); 5537 } 5538 if ($this->linethrough) { 5539 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width); 5540 } 5541 if ($this->overline) { 5542 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width); 5543 } 5544 if ($this->ColorFlag AND ($this->textrendermode < 4)) { 5545 $s .= ' Q'; 5546 } 5547 if ($link) { 5548 $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns); 5549 } 5550 } 5551 // output cell 5552 if ($s) { 5553 // output cell 5554 $rs .= $s; 5555 if ($this->font_spacing != 0) { 5556 // reset font spacing mode 5557 $rs .= ' BT 0 Tc ET'; 5558 } 5559 if ($this->font_stretching != 100) { 5560 // reset font stretching mode 5561 $rs .= ' BT 100 Tz ET'; 5562 } 5563 } 5564 // reset word spacing 5565 if (!$this->isUnicodeFont() AND ($align == 'J')) { 5566 $rs .= ' BT 0 Tw ET'; 5567 } 5568 // reset stretching and spacing 5569 $this->font_stretching = $prev_font_stretching; 5570 $this->font_spacing = $prev_font_spacing; 5571 $this->lasth = $h; 5572 if ($ln > 0) { 5573 //Go to the beginning of the next line 5574 $this->y = $y + $h + $this->cell_margin['B']; 5575 if ($ln == 1) { 5576 if ($this->rtl) { 5577 $this->x = $this->w - $this->rMargin; 5578 } else { 5579 $this->x = $this->lMargin; 5580 } 5581 } 5582 } else { 5583 // go left or right by case 5584 if ($this->rtl) { 5585 $this->x = $x - $w - $this->cell_margin['L']; 5586 } else { 5587 $this->x = $x + $w + $this->cell_margin['R']; 5588 } 5589 } 5590 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n"; 5591 $rs = $gstyles.$rs; 5592 $this->cell_padding = $prev_cell_padding; 5593 $this->cell_margin = $prev_cell_margin; 5594 return $rs; 5595 } 5596 5597 /** 5598 * Replace a char if is defined on the current font. 5599 * @param $oldchar (int) Integer code (unicode) of the character to replace. 5600 * @param $newchar (int) Integer code (unicode) of the new character. 5601 * @return int the replaced char or the old char in case the new char i not defined 5602 * @protected 5603 * @since 5.9.167 (2012-06-22) 5604 */ 5605 protected function replaceChar($oldchar, $newchar) { 5606 if ($this->isCharDefined($newchar)) { 5607 // add the new char on the subset list 5608 $this->CurrentFont['subsetchars'][$newchar] = true; 5609 // return the new character 5610 return $newchar; 5611 } 5612 // return the old char 5613 return $oldchar; 5614 } 5615 5616 /** 5617 * Returns the code to draw the cell border 5618 * @param $x (float) X coordinate. 5619 * @param $y (float) Y coordinate. 5620 * @param $w (float) Cell width. 5621 * @param $h (float) Cell height. 5622 * @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))) 5623 * @return string containing cell border code 5624 * @protected 5625 * @see SetLineStyle() 5626 * @since 5.7.000 (2010-08-02) 5627 */ 5628 protected function getCellBorder($x, $y, $w, $h, $brd) { 5629 $s = ''; // string to be returned 5630 if (empty($brd)) { 5631 return $s; 5632 } 5633 if ($brd == 1) { 5634 $brd = array('LRTB' => true); 5635 } 5636 // calculate coordinates for border 5637 $k = $this->k; 5638 if ($this->rtl) { 5639 $xeL = ($x - $w) * $k; 5640 $xeR = $x * $k; 5641 } else { 5642 $xeL = $x * $k; 5643 $xeR = ($x + $w) * $k; 5644 } 5645 $yeL = (($this->h - ($y + $h)) * $k); 5646 $yeT = (($this->h - $y) * $k); 5647 $xeT = $xeL; 5648 $xeB = $xeR; 5649 $yeR = $yeT; 5650 $yeB = $yeL; 5651 if (is_string($brd)) { 5652 // convert string to array 5653 $slen = strlen($brd); 5654 $newbrd = array(); 5655 for ($i = 0; $i < $slen; ++$i) { 5656 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter'); 5657 } 5658 $brd = $newbrd; 5659 } 5660 if (isset($brd['mode'])) { 5661 $mode = $brd['mode']; 5662 unset($brd['mode']); 5663 } else { 5664 $mode = 'normal'; 5665 } 5666 foreach ($brd as $border => $style) { 5667 if (is_array($style) AND !empty($style)) { 5668 // apply border style 5669 $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '; 5670 $s .= $this->SetLineStyle($style, true)."\n"; 5671 } 5672 switch ($mode) { 5673 case 'ext': { 5674 $off = (($this->LineWidth / 2) * $k); 5675 $xL = $xeL - $off; 5676 $xR = $xeR + $off; 5677 $yT = $yeT + $off; 5678 $yL = $yeL - $off; 5679 $xT = $xL; 5680 $xB = $xR; 5681 $yR = $yT; 5682 $yB = $yL; 5683 $w += $this->LineWidth; 5684 $h += $this->LineWidth; 5685 break; 5686 } 5687 case 'int': { 5688 $off = ($this->LineWidth / 2) * $k; 5689 $xL = $xeL + $off; 5690 $xR = $xeR - $off; 5691 $yT = $yeT - $off; 5692 $yL = $yeL + $off; 5693 $xT = $xL; 5694 $xB = $xR; 5695 $yR = $yT; 5696 $yB = $yL; 5697 $w -= $this->LineWidth; 5698 $h -= $this->LineWidth; 5699 break; 5700 } 5701 case 'normal': 5702 default: { 5703 $xL = $xeL; 5704 $xT = $xeT; 5705 $xB = $xeB; 5706 $xR = $xeR; 5707 $yL = $yeL; 5708 $yT = $yeT; 5709 $yB = $yeB; 5710 $yR = $yeR; 5711 break; 5712 } 5713 } 5714 // draw borders by case 5715 if (strlen($border) == 4) { 5716 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k)); 5717 } elseif (strlen($border) == 3) { 5718 if (strpos($border,'B') === false) { // LTR 5719 $s .= sprintf('%F %F m ', $xL, $yL); 5720 $s .= sprintf('%F %F l ', $xT, $yT); 5721 $s .= sprintf('%F %F l ', $xR, $yR); 5722 $s .= sprintf('%F %F l ', $xB, $yB); 5723 $s .= 'S '; 5724 } elseif (strpos($border,'L') === false) { // TRB 5725 $s .= sprintf('%F %F m ', $xT, $yT); 5726 $s .= sprintf('%F %F l ', $xR, $yR); 5727 $s .= sprintf('%F %F l ', $xB, $yB); 5728 $s .= sprintf('%F %F l ', $xL, $yL); 5729 $s .= 'S '; 5730 } elseif (strpos($border,'T') === false) { // RBL 5731 $s .= sprintf('%F %F m ', $xR, $yR); 5732 $s .= sprintf('%F %F l ', $xB, $yB); 5733 $s .= sprintf('%F %F l ', $xL, $yL); 5734 $s .= sprintf('%F %F l ', $xT, $yT); 5735 $s .= 'S '; 5736 } elseif (strpos($border,'R') === false) { // BLT 5737 $s .= sprintf('%F %F m ', $xB, $yB); 5738 $s .= sprintf('%F %F l ', $xL, $yL); 5739 $s .= sprintf('%F %F l ', $xT, $yT); 5740 $s .= sprintf('%F %F l ', $xR, $yR); 5741 $s .= 'S '; 5742 } 5743 } elseif (strlen($border) == 2) { 5744 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT 5745 $s .= sprintf('%F %F m ', $xL, $yL); 5746 $s .= sprintf('%F %F l ', $xT, $yT); 5747 $s .= sprintf('%F %F l ', $xR, $yR); 5748 $s .= 'S '; 5749 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR 5750 $s .= sprintf('%F %F m ', $xT, $yT); 5751 $s .= sprintf('%F %F l ', $xR, $yR); 5752 $s .= sprintf('%F %F l ', $xB, $yB); 5753 $s .= 'S '; 5754 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB 5755 $s .= sprintf('%F %F m ', $xR, $yR); 5756 $s .= sprintf('%F %F l ', $xB, $yB); 5757 $s .= sprintf('%F %F l ', $xL, $yL); 5758 $s .= 'S '; 5759 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL 5760 $s .= sprintf('%F %F m ', $xB, $yB); 5761 $s .= sprintf('%F %F l ', $xL, $yL); 5762 $s .= sprintf('%F %F l ', $xT, $yT); 5763 $s .= 'S '; 5764 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR 5765 $s .= sprintf('%F %F m ', $xL, $yL); 5766 $s .= sprintf('%F %F l ', $xT, $yT); 5767 $s .= 'S '; 5768 $s .= sprintf('%F %F m ', $xR, $yR); 5769 $s .= sprintf('%F %F l ', $xB, $yB); 5770 $s .= 'S '; 5771 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB 5772 $s .= sprintf('%F %F m ', $xT, $yT); 5773 $s .= sprintf('%F %F l ', $xR, $yR); 5774 $s .= 'S '; 5775 $s .= sprintf('%F %F m ', $xB, $yB); 5776 $s .= sprintf('%F %F l ', $xL, $yL); 5777 $s .= 'S '; 5778 } 5779 } else { // strlen($border) == 1 5780 if (strpos($border,'L') !== false) { // L 5781 $s .= sprintf('%F %F m ', $xL, $yL); 5782 $s .= sprintf('%F %F l ', $xT, $yT); 5783 $s .= 'S '; 5784 } elseif (strpos($border,'T') !== false) { // T 5785 $s .= sprintf('%F %F m ', $xT, $yT); 5786 $s .= sprintf('%F %F l ', $xR, $yR); 5787 $s .= 'S '; 5788 } elseif (strpos($border,'R') !== false) { // R 5789 $s .= sprintf('%F %F m ', $xR, $yR); 5790 $s .= sprintf('%F %F l ', $xB, $yB); 5791 $s .= 'S '; 5792 } elseif (strpos($border,'B') !== false) { // B 5793 $s .= sprintf('%F %F m ', $xB, $yB); 5794 $s .= sprintf('%F %F l ', $xL, $yL); 5795 $s .= 'S '; 5796 } 5797 } 5798 if (is_array($style) AND !empty($style)) { 5799 // reset border style to previous value 5800 $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n"; 5801 } 5802 } 5803 return $s; 5804 } 5805 5806 /** 5807 * This method allows printing text with line breaks. 5808 * 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 /> 5809 * Text can be aligned, centered or justified. The cell block can be framed and the background painted. 5810 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 5811 * @param $h (float) Cell minimum height. The cell extends automatically if needed. 5812 * @param $txt (string) String to print 5813 * @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))) 5814 * @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> 5815 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 5816 * @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> 5817 * @param $x (float) x position in user units 5818 * @param $y (float) y position in user units 5819 * @param $reseth (boolean) if true reset the last cell height (default true). 5820 * @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. 5821 * @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. 5822 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width. 5823 * @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. 5824 * @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. 5825 * @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. 5826 * @return int Return the number of cells or 1 for html mode. 5827 * @public 5828 * @since 1.3 5829 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() 5830 */ 5831 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) { 5832 $prev_cell_margin = $this->cell_margin; 5833 $prev_cell_padding = $this->cell_padding; 5834 // adjust internal padding 5835 $this->adjustCellPadding($border); 5836 $mc_padding = $this->cell_padding; 5837 $mc_margin = $this->cell_margin; 5838 $this->cell_padding['T'] = 0; 5839 $this->cell_padding['B'] = 0; 5840 $this->setCellMargins(0, 0, 0, 0); 5841 if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) { 5842 // reset row height 5843 $this->resetLastH(); 5844 } 5845 if (!TCPDF_STATIC::empty_string($y)) { 5846 $this->SetY($y); // set y in order to convert negative y values to positive ones 5847 } 5848 $y = $this->GetY(); 5849 $resth = 0; 5850 if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) { 5851 // spit cell in more pages/columns 5852 $newh = ($this->PageBreakTrigger - $y); 5853 $resth = ($h - $newh); // cell to be printed on the next page/column 5854 $h = $newh; 5855 } 5856 // get current page number 5857 $startpage = $this->page; 5858 // get current column 5859 $startcolumn = $this->current_column; 5860 if (!TCPDF_STATIC::empty_string($x)) { 5861 $this->SetX($x); 5862 } else { 5863 $x = $this->GetX(); 5864 } 5865 // check page for no-write regions and adapt page margins if necessary 5866 list($x, $y) = $this->checkPageRegions(0, $x, $y); 5867 // apply margins 5868 $oy = $y + $mc_margin['T']; 5869 if ($this->rtl) { 5870 $ox = ($this->w - $x - $mc_margin['R']); 5871 } else { 5872 $ox = ($x + $mc_margin['L']); 5873 } 5874 $this->x = $ox; 5875 $this->y = $oy; 5876 // set width 5877 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 5878 if ($this->rtl) { 5879 $w = ($this->x - $this->lMargin - $mc_margin['L']); 5880 } else { 5881 $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']); 5882 } 5883 } 5884 // store original margin values 5885 $lMargin = $this->lMargin; 5886 $rMargin = $this->rMargin; 5887 if ($this->rtl) { 5888 $this->rMargin = ($this->w - $this->x); 5889 $this->lMargin = ($this->x - $w); 5890 } else { 5891 $this->lMargin = ($this->x); 5892 $this->rMargin = ($this->w - $this->x - $w); 5893 } 5894 $this->clMargin = $this->lMargin; 5895 $this->crMargin = $this->rMargin; 5896 if ($autopadding) { 5897 // add top padding 5898 $this->y += $mc_padding['T']; 5899 } 5900 if ($ishtml) { // ******* Write HTML text 5901 $this->writeHTML($txt, true, false, $reseth, true, $align); 5902 $nl = 1; 5903 } else { // ******* Write simple text 5904 $prev_FontSizePt = $this->FontSizePt; 5905 if ($fitcell) { 5906 // ajust height values 5907 $tobottom = ($this->h - $this->y - $this->bMargin - $this->cell_padding['T'] - $this->cell_padding['B']); 5908 $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom)); 5909 } 5910 // vertical alignment 5911 if ($maxh > 0) { 5912 // get text height 5913 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5914 if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt > 1)) { 5915 // try to reduce font size to fit text on cell (use a quick search algorithm) 5916 $fmin = 1; 5917 $fmax = $this->FontSizePt; 5918 $diff_epsilon = (1 / $this->k); // one point (min resolution) 5919 $maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations 5920 while ($maxit >= 0) { 5921 $fmid = (($fmax + $fmin) / 2); 5922 $this->SetFontSize($fmid, false); 5923 $this->resetLastH(); 5924 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5925 $diff = ($maxh - $text_height); 5926 if ($diff >= 0) { 5927 if ($diff <= $diff_epsilon) { 5928 break; 5929 } 5930 $fmin = $fmid; 5931 } else { 5932 $fmax = $fmid; 5933 } 5934 --$maxit; 5935 } 5936 if ($maxit < 0) { 5937 // premature exit, we get the minimum font value to fit the cell 5938 $this->SetFontSize($fmin); 5939 $this->resetLastH(); 5940 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 5941 } else { 5942 $this->SetFontSize($fmid); 5943 $this->resetLastH(); 5944 } 5945 } 5946 if ($text_height < $maxh) { 5947 if ($valign == 'M') { 5948 // text vertically centered 5949 $this->y += (($maxh - $text_height) / 2); 5950 } elseif ($valign == 'B') { 5951 // text vertically aligned on bottom 5952 $this->y += ($maxh - $text_height); 5953 } 5954 } 5955 } 5956 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin); 5957 if ($fitcell) { 5958 // restore font size 5959 $this->SetFontSize($prev_FontSizePt); 5960 } 5961 } 5962 if ($autopadding) { 5963 // add bottom padding 5964 $this->y += $mc_padding['B']; 5965 } 5966 // Get end-of-text Y position 5967 $currentY = $this->y; 5968 // get latest page number 5969 $endpage = $this->page; 5970 if ($resth > 0) { 5971 $skip = ($endpage - $startpage); 5972 $tmpresth = $resth; 5973 while ($tmpresth > 0) { 5974 if ($skip <= 0) { 5975 // add a page (or trig AcceptPageBreak() for multicolumn mode) 5976 $this->checkPageBreak($this->PageBreakTrigger + 1); 5977 } 5978 if ($this->num_columns > 1) { 5979 $tmpresth -= ($this->h - $this->y - $this->bMargin); 5980 } else { 5981 $tmpresth -= ($this->h - $this->tMargin - $this->bMargin); 5982 } 5983 --$skip; 5984 } 5985 $currentY = $this->y; 5986 $endpage = $this->page; 5987 } 5988 // get latest column 5989 $endcolumn = $this->current_column; 5990 if ($this->num_columns == 0) { 5991 $this->num_columns = 1; 5992 } 5993 // disable page regions check 5994 $check_page_regions = $this->check_page_regions; 5995 $this->check_page_regions = false; 5996 // get border modes 5997 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 5998 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 5999 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 6000 // design borders around HTML cells. 6001 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 6002 $ccode = ''; 6003 $this->setPage($page); 6004 if ($this->num_columns < 2) { 6005 // single-column mode 6006 $this->SetX($x); 6007 $this->y = $this->tMargin; 6008 } 6009 // account for margin changes 6010 if ($page > $startpage) { 6011 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 6012 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 6013 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 6014 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 6015 } 6016 } 6017 if ($startpage == $endpage) { 6018 // single page 6019 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 6020 if ($column != $this->current_column) { 6021 $this->selectColumn($column); 6022 } 6023 if ($this->rtl) { 6024 $this->x -= $mc_margin['R']; 6025 } else { 6026 $this->x += $mc_margin['L']; 6027 } 6028 if ($startcolumn == $endcolumn) { // single column 6029 $cborder = $border; 6030 $h = max($h, ($currentY - $oy)); 6031 $this->y = $oy; 6032 } elseif ($column == $startcolumn) { // first column 6033 $cborder = $border_start; 6034 $this->y = $oy; 6035 $h = $this->h - $this->y - $this->bMargin; 6036 } elseif ($column == $endcolumn) { // end column 6037 $cborder = $border_end; 6038 $h = $currentY - $this->y; 6039 if ($resth > $h) { 6040 $h = $resth; 6041 } 6042 } else { // middle column 6043 $cborder = $border_middle; 6044 $h = $this->h - $this->y - $this->bMargin; 6045 $resth -= $h; 6046 } 6047 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6048 } // end for each column 6049 } elseif ($page == $startpage) { // first page 6050 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 6051 if ($column != $this->current_column) { 6052 $this->selectColumn($column); 6053 } 6054 if ($this->rtl) { 6055 $this->x -= $mc_margin['R']; 6056 } else { 6057 $this->x += $mc_margin['L']; 6058 } 6059 if ($column == $startcolumn) { // first column 6060 $cborder = $border_start; 6061 $this->y = $oy; 6062 $h = $this->h - $this->y - $this->bMargin; 6063 } else { // middle column 6064 $cborder = $border_middle; 6065 $h = $this->h - $this->y - $this->bMargin; 6066 $resth -= $h; 6067 } 6068 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6069 } // end for each column 6070 } elseif ($page == $endpage) { // last page 6071 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 6072 if ($column != $this->current_column) { 6073 $this->selectColumn($column); 6074 } 6075 if ($this->rtl) { 6076 $this->x -= $mc_margin['R']; 6077 } else { 6078 $this->x += $mc_margin['L']; 6079 } 6080 if ($column == $endcolumn) { 6081 // end column 6082 $cborder = $border_end; 6083 $h = $currentY - $this->y; 6084 if ($resth > $h) { 6085 $h = $resth; 6086 } 6087 } else { 6088 // middle column 6089 $cborder = $border_middle; 6090 $h = $this->h - $this->y - $this->bMargin; 6091 $resth -= $h; 6092 } 6093 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6094 } // end for each column 6095 } else { // middle page 6096 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 6097 $this->selectColumn($column); 6098 if ($this->rtl) { 6099 $this->x -= $mc_margin['R']; 6100 } else { 6101 $this->x += $mc_margin['L']; 6102 } 6103 $cborder = $border_middle; 6104 $h = $this->h - $this->y - $this->bMargin; 6105 $resth -= $h; 6106 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 6107 } // end for each column 6108 } 6109 if ($cborder OR $fill) { 6110 $offsetlen = strlen($ccode); 6111 // draw border and fill 6112 if ($this->inxobj) { 6113 // we are inside an XObject template 6114 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 6115 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 6116 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 6117 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 6118 } else { 6119 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 6120 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 6121 } 6122 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 6123 $pstart = substr($pagebuff, 0, $pagemark); 6124 $pend = substr($pagebuff, $pagemark); 6125 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 6126 } else { 6127 if (end($this->transfmrk[$this->page]) !== false) { 6128 $pagemarkkey = key($this->transfmrk[$this->page]); 6129 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 6130 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; 6131 } elseif ($this->InFooter) { 6132 $pagemark = $this->footerpos[$this->page]; 6133 $this->footerpos[$this->page] += $offsetlen; 6134 } else { 6135 $pagemark = $this->intmrk[$this->page]; 6136 $this->intmrk[$this->page] += $offsetlen; 6137 } 6138 $pagebuff = $this->getPageBuffer($this->page); 6139 $pstart = substr($pagebuff, 0, $pagemark); 6140 $pend = substr($pagebuff, $pagemark); 6141 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 6142 } 6143 } 6144 } // end for each page 6145 // restore page regions check 6146 $this->check_page_regions = $check_page_regions; 6147 // Get end-of-cell Y position 6148 $currentY = $this->GetY(); 6149 // restore previous values 6150 if ($this->num_columns > 1) { 6151 $this->selectColumn(); 6152 } else { 6153 // restore original margins 6154 $this->lMargin = $lMargin; 6155 $this->rMargin = $rMargin; 6156 if ($this->page > $startpage) { 6157 // check for margin variations between pages (i.e. booklet mode) 6158 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']); 6159 $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']); 6160 if (($dl != 0) OR ($dr != 0)) { 6161 $this->lMargin += $dl; 6162 $this->rMargin += $dr; 6163 } 6164 } 6165 } 6166 if ($ln > 0) { 6167 //Go to the beginning of the next line 6168 $this->SetY($currentY + $mc_margin['B']); 6169 if ($ln == 2) { 6170 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); 6171 } 6172 } else { 6173 // go left or right by case 6174 $this->setPage($startpage); 6175 $this->y = $y; 6176 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); 6177 } 6178 $this->setContentMark(); 6179 $this->cell_padding = $prev_cell_padding; 6180 $this->cell_margin = $prev_cell_margin; 6181 $this->clMargin = $this->lMargin; 6182 $this->crMargin = $this->rMargin; 6183 return $nl; 6184 } 6185 6186 /** 6187 * This method return the estimated number of lines for print a simple text string using Multicell() method. 6188 * @param $txt (string) String for calculating his height 6189 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 6190 * @param $reseth (boolean) if true reset the last cell height (default false). 6191 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true). 6192 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding. 6193 * @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))) 6194 * @return float Return the minimal height needed for multicell method for printing the $txt param. 6195 * @author Alexander Escalona Fern\E1ndez, Nicola Asuni 6196 * @public 6197 * @since 4.5.011 6198 */ 6199 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { 6200 if ($txt === NULL) { 6201 return 0; 6202 } 6203 if ($txt === '') { 6204 // empty string 6205 return 1; 6206 } 6207 // adjust internal padding 6208 $prev_cell_padding = $this->cell_padding; 6209 $prev_lasth = $this->lasth; 6210 if (is_array($cellpadding)) { 6211 $this->cell_padding = $cellpadding; 6212 } 6213 $this->adjustCellPadding($border); 6214 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) { 6215 if ($this->rtl) { 6216 $w = $this->x - $this->lMargin; 6217 } else { 6218 $w = $this->w - $this->rMargin - $this->x; 6219 } 6220 } 6221 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 6222 if ($reseth) { 6223 // reset row height 6224 $this->resetLastH(); 6225 } 6226 $lines = 1; 6227 $sum = 0; 6228 $chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6229 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true); 6230 $length = count($chars); 6231 $lastSeparator = -1; 6232 for ($i = 0; $i < $length; ++$i) { 6233 $c = $chars[$i]; 6234 $charWidth = $charsWidth[$i]; 6235 if (($c != 160) 6236 AND (($c == 173) 6237 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode)) 6238 OR (($c == 45) 6239 AND ($i > 0) AND ($i < ($length - 1)) 6240 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode)) 6241 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode)) 6242 ) 6243 ) 6244 ) { 6245 $lastSeparator = $i; 6246 } 6247 if ((($sum + $charWidth) > $wmax) OR ($c == 10)) { 6248 ++$lines; 6249 if ($c == 10) { 6250 $lastSeparator = -1; 6251 $sum = 0; 6252 } elseif ($lastSeparator != -1) { 6253 $i = $lastSeparator; 6254 $lastSeparator = -1; 6255 $sum = 0; 6256 } else { 6257 $sum = $charWidth; 6258 } 6259 } else { 6260 $sum += $charWidth; 6261 } 6262 } 6263 if ($chars[($length - 1)] == 10) { 6264 --$lines; 6265 } 6266 $this->cell_padding = $prev_cell_padding; 6267 $this->lasth = $prev_lasth; 6268 return $lines; 6269 } 6270 6271 /** 6272 * This method return the estimated height needed for printing a simple text string using the Multicell() method. 6273 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique: 6274 * @pre 6275 * // store current object 6276 * $pdf->startTransaction(); 6277 * // store starting values 6278 * $start_y = $pdf->GetY(); 6279 * $start_page = $pdf->getPage(); 6280 * // call your printing functions with your parameters 6281 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6282 * $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); 6283 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6284 * // get the new Y 6285 * $end_y = $pdf->GetY(); 6286 * $end_page = $pdf->getPage(); 6287 * // calculate height 6288 * $height = 0; 6289 * if ($end_page == $start_page) { 6290 * $height = $end_y - $start_y; 6291 * } else { 6292 * for ($page=$start_page; $page <= $end_page; ++$page) { 6293 * $this->setPage($page); 6294 * if ($page == $start_page) { 6295 * // first page 6296 * $height += $this->h - $start_y - $this->bMargin; 6297 * } elseif ($page == $end_page) { 6298 * // last page 6299 * $height += $end_y - $this->tMargin; 6300 * } else { 6301 * $height += $this->h - $this->tMargin - $this->bMargin; 6302 * } 6303 * } 6304 * } 6305 * // restore previous object 6306 * $pdf = $pdf->rollbackTransaction(); 6307 * 6308 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. 6309 * @param $txt (string) String for calculating his height 6310 * @param $reseth (boolean) if true reset the last cell height (default false). 6311 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true). 6312 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding. 6313 * @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))) 6314 * @return float Return the minimal height needed for multicell method for printing the $txt param. 6315 * @author Nicola Asuni, Alexander Escalona Fern\E1ndez 6316 * @public 6317 */ 6318 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { 6319 // adjust internal padding 6320 $prev_cell_padding = $this->cell_padding; 6321 $prev_lasth = $this->lasth; 6322 if (is_array($cellpadding)) { 6323 $this->cell_padding = $cellpadding; 6324 } 6325 $this->adjustCellPadding($border); 6326 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border); 6327 $height = $this->getCellHeight(($lines * $this->FontSize), $autopadding); 6328 $this->cell_padding = $prev_cell_padding; 6329 $this->lasth = $prev_lasth; 6330 return $height; 6331 } 6332 6333 /** 6334 * This method prints text from the current position.<br /> 6335 * @param $h (float) Line height 6336 * @param $txt (string) String to print 6337 * @param $link (mixed) URL or identifier returned by AddLink() 6338 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 6339 * @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> 6340 * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line. 6341 * @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. 6342 * @param $firstline (boolean) if true prints only the first line and return the remaining string. 6343 * @param $firstblock (boolean) if true the string is the starting of a line. 6344 * @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. 6345 * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode). 6346 * @param $margin (array) margin array of the parent container 6347 * @return mixed Return the number of cells or the remaining string if $firstline = true. 6348 * @public 6349 * @since 1.5 6350 */ 6351 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') { 6352 // check page for no-write regions and adapt page margins if necessary 6353 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); 6354 if (strlen($txt) == 0) { 6355 // fix empty text 6356 $txt = ' '; 6357 } 6358 if ($margin === '') { 6359 // set default margins 6360 $margin = $this->cell_margin; 6361 } 6362 // remove carriage returns 6363 $s = str_replace("\r", '', $txt); 6364 // check if string contains arabic text 6365 if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) { 6366 $arabic = true; 6367 } else { 6368 $arabic = false; 6369 } 6370 // check if string contains RTL text 6371 if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) { 6372 $rtlmode = true; 6373 } else { 6374 $rtlmode = false; 6375 } 6376 // get a char width 6377 $chrwidth = $this->GetCharWidth(46); // dot character 6378 // get array of unicode values 6379 $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont); 6380 // calculate maximum width for a single character on string 6381 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true); 6382 array_walk($chrw, array($this, 'getRawCharWidth')); 6383 $maxchwidth = max($chrw); 6384 // get array of chars 6385 $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode); 6386 // get the number of characters 6387 $nb = count($chars); 6388 // replacement for SHY character (minus symbol) 6389 $shy_replacement = 45; 6390 $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode); 6391 // widht for SHY replacement 6392 $shy_replacement_width = $this->GetCharWidth($shy_replacement); 6393 // page width 6394 $pw = $w = $this->w - $this->lMargin - $this->rMargin; 6395 // calculate remaining line width ($w) 6396 if ($this->rtl) { 6397 $w = $this->x - $this->lMargin; 6398 } else { 6399 $w = $this->w - $this->rMargin - $this->x; 6400 } 6401 // max column width 6402 $wmax = ($w - $wadj); 6403 if (!$firstline) { 6404 $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']); 6405 } 6406 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) { 6407 // the maximum width character do not fit on column 6408 return ''; 6409 } 6410 // minimum row height 6411 $row_height = max($h, $this->getCellHeight($this->FontSize)); 6412 // max Y 6413 $maxy = $this->y + $maxh - max($row_height, $h); 6414 $start_page = $this->page; 6415 $i = 0; // character position 6416 $j = 0; // current starting position 6417 $sep = -1; // position of the last blank space 6418 $prevsep = $sep; // previous separator 6419 $shy = false; // true if the last blank is a soft hypen (SHY) 6420 $prevshy = $shy; // previous shy mode 6421 $l = 0; // current string length 6422 $nl = 0; //number of lines 6423 $linebreak = false; 6424 $pc = 0; // previous character 6425 // for each character 6426 while ($i < $nb) { 6427 if (($maxh > 0) AND ($this->y > $maxy) ) { 6428 break; 6429 } 6430 //Get the current character 6431 $c = $chars[$i]; 6432 if ($c == 10) { // 10 = "\n" = new line 6433 //Explicit line break 6434 if ($align == 'J') { 6435 if ($this->rtl) { 6436 $talign = 'R'; 6437 } else { 6438 $talign = 'L'; 6439 } 6440 } else { 6441 $talign = $align; 6442 } 6443 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6444 if ($firstline) { 6445 $startx = $this->x; 6446 $tmparr = array_slice($chars, $j, ($i - $j)); 6447 if ($rtlmode) { 6448 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6449 } 6450 $linew = $this->GetArrStringWidth($tmparr); 6451 unset($tmparr); 6452 if ($this->rtl) { 6453 $this->endlinex = $startx - $linew; 6454 } else { 6455 $this->endlinex = $startx + $linew; 6456 } 6457 $w = $linew; 6458 $tmpcellpadding = $this->cell_padding; 6459 if ($maxh == 0) { 6460 $this->SetCellPadding(0); 6461 } 6462 } 6463 if ($firstblock AND $this->isRTLTextDir()) { 6464 $tmpstr = $this->stringRightTrim($tmpstr); 6465 } 6466 // Skip newlines at the beginning of a page or column 6467 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) { 6468 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch); 6469 } 6470 unset($tmpstr); 6471 if ($firstline) { 6472 $this->cell_padding = $tmpcellpadding; 6473 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6474 } 6475 ++$nl; 6476 $j = $i + 1; 6477 $l = 0; 6478 $sep = -1; 6479 $prevsep = $sep; 6480 $shy = false; 6481 // account for margin changes 6482 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { 6483 $this->AcceptPageBreak(); 6484 if ($this->rtl) { 6485 $this->x -= $margin['R']; 6486 } else { 6487 $this->x += $margin['L']; 6488 } 6489 $this->lMargin += $margin['L']; 6490 $this->rMargin += $margin['R']; 6491 } 6492 $w = $this->getRemainingWidth(); 6493 $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']); 6494 } else { 6495 // 160 is the non-breaking space. 6496 // 173 is SHY (Soft Hypen). 6497 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 6498 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 6499 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 6500 if (($c != 160) 6501 AND (($c == 173) 6502 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode)) 6503 OR (($c == 45) 6504 AND ($i < ($nb - 1)) 6505 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode)) 6506 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode)) 6507 ) 6508 ) 6509 ) { 6510 // update last blank space position 6511 $prevsep = $sep; 6512 $sep = $i; 6513 // check if is a SHY 6514 if (($c == 173) OR ($c == 45)) { 6515 $prevshy = $shy; 6516 $shy = true; 6517 if ($pc == 45) { 6518 $tmp_shy_replacement_width = 0; 6519 $tmp_shy_replacement_char = ''; 6520 } else { 6521 $tmp_shy_replacement_width = $shy_replacement_width; 6522 $tmp_shy_replacement_char = $shy_replacement_char; 6523 } 6524 } else { 6525 $shy = false; 6526 } 6527 } 6528 // update string length 6529 if ($this->isUnicodeFont() AND ($arabic)) { 6530 // with bidirectional algorithm some chars may be changed affecting the line length 6531 // *** very slow *** 6532 $l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont)); 6533 } else { 6534 $l += $this->GetCharWidth($c, ($i+1 < $nb)); 6535 } 6536 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) >= $wmax))) { 6537 if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) { 6538 $sep = $prevsep; 6539 $shy = $prevshy; 6540 } 6541 // we have reached the end of column 6542 if ($sep == -1) { 6543 // check if the line was already started 6544 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth))) 6545 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) { 6546 // print a void cell and go to next line 6547 $this->Cell($w, $h, '', 0, 1); 6548 $linebreak = true; 6549 if ($firstline) { 6550 return (TCPDF_FONTS::UniArrSubString($uchars, $j)); 6551 } 6552 } else { 6553 // truncate the word because do not fit on column 6554 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6555 if ($firstline) { 6556 $startx = $this->x; 6557 $tmparr = array_slice($chars, $j, ($i - $j)); 6558 if ($rtlmode) { 6559 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6560 } 6561 $linew = $this->GetArrStringWidth($tmparr); 6562 unset($tmparr); 6563 if ($this->rtl) { 6564 $this->endlinex = $startx - $linew; 6565 } else { 6566 $this->endlinex = $startx + $linew; 6567 } 6568 $w = $linew; 6569 $tmpcellpadding = $this->cell_padding; 6570 if ($maxh == 0) { 6571 $this->SetCellPadding(0); 6572 } 6573 } 6574 if ($firstblock AND $this->isRTLTextDir()) { 6575 $tmpstr = $this->stringRightTrim($tmpstr); 6576 } 6577 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 6578 unset($tmpstr); 6579 if ($firstline) { 6580 $this->cell_padding = $tmpcellpadding; 6581 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6582 } 6583 $j = $i; 6584 --$i; 6585 } 6586 } else { 6587 // word wrapping 6588 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) { 6589 $endspace = 1; 6590 } else { 6591 $endspace = 0; 6592 } 6593 // check the length of the next string 6594 $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)); 6595 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest)); 6596 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) { 6597 // truncate the word because do not fit on a full page width 6598 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i); 6599 if ($firstline) { 6600 $startx = $this->x; 6601 $tmparr = array_slice($chars, $j, ($i - $j)); 6602 if ($rtlmode) { 6603 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6604 } 6605 $linew = $this->GetArrStringWidth($tmparr); 6606 unset($tmparr); 6607 if ($this->rtl) { 6608 $this->endlinex = ($startx - $linew); 6609 } else { 6610 $this->endlinex = ($startx + $linew); 6611 } 6612 $w = $linew; 6613 $tmpcellpadding = $this->cell_padding; 6614 if ($maxh == 0) { 6615 $this->SetCellPadding(0); 6616 } 6617 } 6618 if ($firstblock AND $this->isRTLTextDir()) { 6619 $tmpstr = $this->stringRightTrim($tmpstr); 6620 } 6621 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 6622 unset($tmpstr); 6623 if ($firstline) { 6624 $this->cell_padding = $tmpcellpadding; 6625 return (TCPDF_FONTS::UniArrSubString($uchars, $i)); 6626 } 6627 $j = $i; 6628 --$i; 6629 } else { 6630 // word wrapping 6631 if ($shy) { 6632 // add hypen (minus symbol) at the end of the line 6633 $shy_width = $tmp_shy_replacement_width; 6634 if ($this->rtl) { 6635 $shy_char_left = $tmp_shy_replacement_char; 6636 $shy_char_right = ''; 6637 } else { 6638 $shy_char_left = ''; 6639 $shy_char_right = $tmp_shy_replacement_char; 6640 } 6641 } else { 6642 $shy_width = 0; 6643 $shy_char_left = ''; 6644 $shy_char_right = ''; 6645 } 6646 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace)); 6647 if ($firstline) { 6648 $startx = $this->x; 6649 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j)); 6650 if ($rtlmode) { 6651 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6652 } 6653 $linew = $this->GetArrStringWidth($tmparr); 6654 unset($tmparr); 6655 if ($this->rtl) { 6656 $this->endlinex = $startx - $linew - $shy_width; 6657 } else { 6658 $this->endlinex = $startx + $linew + $shy_width; 6659 } 6660 $w = $linew; 6661 $tmpcellpadding = $this->cell_padding; 6662 if ($maxh == 0) { 6663 $this->SetCellPadding(0); 6664 } 6665 } 6666 // print the line 6667 if ($firstblock AND $this->isRTLTextDir()) { 6668 $tmpstr = $this->stringRightTrim($tmpstr); 6669 } 6670 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch); 6671 unset($tmpstr); 6672 if ($firstline) { 6673 if ($chars[$sep] == 45) { 6674 $endspace += 1; 6675 } 6676 // return the remaining text 6677 $this->cell_padding = $tmpcellpadding; 6678 return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace))); 6679 } 6680 $i = $sep; 6681 $sep = -1; 6682 $shy = false; 6683 $j = ($i + 1); 6684 } 6685 } 6686 // account for margin changes 6687 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { 6688 $this->AcceptPageBreak(); 6689 if ($this->rtl) { 6690 $this->x -= $margin['R']; 6691 } else { 6692 $this->x += $margin['L']; 6693 } 6694 $this->lMargin += $margin['L']; 6695 $this->rMargin += $margin['R']; 6696 } 6697 $w = $this->getRemainingWidth(); 6698 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 6699 if ($linebreak) { 6700 $linebreak = false; 6701 } else { 6702 ++$nl; 6703 $l = 0; 6704 } 6705 } 6706 } 6707 // save last character 6708 $pc = $c; 6709 ++$i; 6710 } // end while i < nb 6711 // print last substring (if any) 6712 if ($l > 0) { 6713 switch ($align) { 6714 case 'J': 6715 case 'C': { 6716 $w = $w; 6717 break; 6718 } 6719 case 'L': { 6720 if ($this->rtl) { 6721 $w = $w; 6722 } else { 6723 $w = $l; 6724 } 6725 break; 6726 } 6727 case 'R': { 6728 if ($this->rtl) { 6729 $w = $l; 6730 } else { 6731 $w = $w; 6732 } 6733 break; 6734 } 6735 default: { 6736 $w = $l; 6737 break; 6738 } 6739 } 6740 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb); 6741 if ($firstline) { 6742 $startx = $this->x; 6743 $tmparr = array_slice($chars, $j, ($nb - $j)); 6744 if ($rtlmode) { 6745 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont); 6746 } 6747 $linew = $this->GetArrStringWidth($tmparr); 6748 unset($tmparr); 6749 if ($this->rtl) { 6750 $this->endlinex = $startx - $linew; 6751 } else { 6752 $this->endlinex = $startx + $linew; 6753 } 6754 $w = $linew; 6755 $tmpcellpadding = $this->cell_padding; 6756 if ($maxh == 0) { 6757 $this->SetCellPadding(0); 6758 } 6759 } 6760 if ($firstblock AND $this->isRTLTextDir()) { 6761 $tmpstr = $this->stringRightTrim($tmpstr); 6762 } 6763 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch); 6764 unset($tmpstr); 6765 if ($firstline) { 6766 $this->cell_padding = $tmpcellpadding; 6767 return (TCPDF_FONTS::UniArrSubString($uchars, $nb)); 6768 } 6769 ++$nl; 6770 } 6771 if ($firstline) { 6772 return ''; 6773 } 6774 return $nl; 6775 } 6776 6777 /** 6778 * Returns the remaining width between the current position and margins. 6779 * @return int Return the remaining width 6780 * @protected 6781 */ 6782 protected function getRemainingWidth() { 6783 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y); 6784 if ($this->rtl) { 6785 return ($this->x - $this->lMargin); 6786 } else { 6787 return ($this->w - $this->rMargin - $this->x); 6788 } 6789 } 6790 6791 /** 6792 * Set the block dimensions accounting for page breaks and page/column fitting 6793 * @param $w (float) width 6794 * @param $h (float) height 6795 * @param $x (float) X coordinate 6796 * @param $y (float) Y coodiante 6797 * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions. 6798 * @return array($w, $h, $x, $y) 6799 * @protected 6800 * @since 5.5.009 (2010-07-05) 6801 */ 6802 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) { 6803 if ($w <= 0) { 6804 // set maximum width 6805 $w = ($this->w - $this->lMargin - $this->rMargin); 6806 if ($w <= 0) { 6807 $w = 1; 6808 } 6809 } 6810 if ($h <= 0) { 6811 // set maximum height 6812 $h = ($this->PageBreakTrigger - $this->tMargin); 6813 if ($h <= 0) { 6814 $h = 1; 6815 } 6816 } 6817 // resize the block to be vertically contained on a single page or single column 6818 if ($fitonpage OR $this->AutoPageBreak) { 6819 $ratio_wh = ($w / $h); 6820 if ($h > ($this->PageBreakTrigger - $this->tMargin)) { 6821 $h = $this->PageBreakTrigger - $this->tMargin; 6822 $w = ($h * $ratio_wh); 6823 } 6824 // resize the block to be horizontally contained on a single page or single column 6825 if ($fitonpage) { 6826 $maxw = ($this->w - $this->lMargin - $this->rMargin); 6827 if ($w > $maxw) { 6828 $w = $maxw; 6829 $h = ($w / $ratio_wh); 6830 } 6831 } 6832 } 6833 // Check whether we need a new page or new column first as this does not fit 6834 $prev_x = $this->x; 6835 $prev_y = $this->y; 6836 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) { 6837 $y = $this->y; 6838 if ($this->rtl) { 6839 $x += ($prev_x - $this->x); 6840 } else { 6841 $x += ($this->x - $prev_x); 6842 } 6843 $this->newline = true; 6844 } 6845 // resize the block to be contained on the remaining available page or column space 6846 if ($fitonpage) { 6847 $ratio_wh = ($w / $h); 6848 if (($y + $h) > $this->PageBreakTrigger) { 6849 $h = $this->PageBreakTrigger - $y; 6850 $w = ($h * $ratio_wh); 6851 } 6852 if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) { 6853 $w = $this->w - $this->rMargin - $x; 6854 $h = ($w / $ratio_wh); 6855 } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) { 6856 $w = $x - $this->lMargin; 6857 $h = ($w / $ratio_wh); 6858 } 6859 } 6860 return array($w, $h, $x, $y); 6861 } 6862 6863 /** 6864 * Puts an image in the page. 6865 * The upper-left corner must be given. 6866 * The dimensions can be specified in different ways:<ul> 6867 * <li>explicit width and height (expressed in user unit)</li> 6868 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li> 6869 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> 6870 * 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; 6871 * The format can be specified explicitly or inferred from the file extension.<br /> 6872 * It is possible to put a link on the image.<br /> 6873 * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> 6874 * @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'). 6875 * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL). 6876 * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL). 6877 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 6878 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 6879 * @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. 6880 * @param $link (mixed) URL or identifier returned by AddLink(). 6881 * @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> 6882 * @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). 6883 * @param $dpi (int) dot-per-inch resolution used on resize 6884 * @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> 6885 * @param $ismask (boolean) true if this image is a mask, false otherwise 6886 * @param $imgmask (mixed) image object returned by this function or false 6887 * @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))) 6888 * @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). 6889 * @param $hidden (boolean) If true do not display the image. 6890 * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions. 6891 * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned). 6892 * @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. 6893 * @return image information 6894 * @public 6895 * @since 1.1 6896 */ 6897 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()) { 6898 if ($this->state != 2) { 6899 return; 6900 } 6901 if (strcmp($x, '') === 0) { 6902 $x = $this->x; 6903 } 6904 if (strcmp($y, '') === 0) { 6905 $y = $this->y; 6906 } 6907 // check page for no-write regions and adapt page margins if necessary 6908 list($x, $y) = $this->checkPageRegions($h, $x, $y); 6909 $exurl = ''; // external streams 6910 $imsize = FALSE; 6911 6912 // Make sure the file variable is not empty or null because accessing $file[0] later 6913 // results in error when running PHP 7.4 6914 if (empty($file)) { 6915 return false; 6916 } 6917 // check if we are passing an image as file or string 6918 if ($file[0] === '@') { 6919 // image from string 6920 $imgdata = substr($file, 1); 6921 } else { // image file 6922 if ($file[0] === '*') { 6923 // image as external stream 6924 $file = substr($file, 1); 6925 $exurl = $file; 6926 } 6927 // check if file exist and it is valid 6928 if (!@$this->fileExists($file)) { 6929 return false; 6930 } 6931 if (false !== $info = $this->getImageBuffer($file)) { 6932 $imsize = array($info['w'], $info['h']); 6933 } elseif (($imsize = @getimagesize($file)) === FALSE && strpos($file, '__tcpdf_'.$this->file_id.'_img') === FALSE){ 6934 $imgdata = $this->getCachedFileContents($file); 6935 } 6936 } 6937 if (!empty($imgdata)) { 6938 // copy image to cache 6939 $original_file = $file; 6940 $file = TCPDF_STATIC::getObjFilename('img', $this->file_id); 6941 $fp = TCPDF_STATIC::fopenLocal($file, 'w'); 6942 if (!$fp) { 6943 $this->Error('Unable to write file: '.$file); 6944 } 6945 fwrite($fp, $imgdata); 6946 fclose($fp); 6947 unset($imgdata); 6948 $imsize = @getimagesize($file); 6949 if ($imsize === FALSE) { 6950 unlink($file); 6951 $file = $original_file; 6952 } 6953 } 6954 if ($imsize === FALSE) { 6955 if (($w > 0) AND ($h > 0)) { 6956 // get measures from specified data 6957 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 6958 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 6959 $imsize = array($pw, $ph); 6960 } else { 6961 $this->Error('[Image] Unable to get the size of the image: '.$file); 6962 } 6963 } 6964 // file hash 6965 $filehash = md5($file); 6966 // get original image width and height in pixels 6967 list($pixw, $pixh) = $imsize; 6968 // calculate image width and height on document 6969 if (($w <= 0) AND ($h <= 0)) { 6970 // convert image size to document unit 6971 $w = $this->pixelsToUnits($pixw); 6972 $h = $this->pixelsToUnits($pixh); 6973 } elseif ($w <= 0) { 6974 $w = $h * $pixw / $pixh; 6975 } elseif ($h <= 0) { 6976 $h = $w * $pixh / $pixw; 6977 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) { 6978 if (strlen($fitbox) !== 2) { 6979 // set default alignment 6980 $fitbox = '--'; 6981 } 6982 // scale image dimensions proportionally to fit within the ($w, $h) box 6983 if ((($w * $pixh) / ($h * $pixw)) < 1) { 6984 // store current height 6985 $oldh = $h; 6986 // calculate new height 6987 $h = $w * $pixh / $pixw; 6988 // height difference 6989 $hdiff = ($oldh - $h); 6990 // vertical alignment 6991 switch (strtoupper($fitbox[1])) { 6992 case 'T': { 6993 break; 6994 } 6995 case 'M': { 6996 $y += ($hdiff / 2); 6997 break; 6998 } 6999 case 'B': { 7000 $y += $hdiff; 7001 break; 7002 } 7003 } 7004 } else { 7005 // store current width 7006 $oldw = $w; 7007 // calculate new width 7008 $w = $h * $pixw / $pixh; 7009 // width difference 7010 $wdiff = ($oldw - $w); 7011 // horizontal alignment 7012 switch (strtoupper($fitbox[0])) { 7013 case 'L': { 7014 if ($this->rtl) { 7015 $x -= $wdiff; 7016 } 7017 break; 7018 } 7019 case 'C': { 7020 if ($this->rtl) { 7021 $x -= ($wdiff / 2); 7022 } else { 7023 $x += ($wdiff / 2); 7024 } 7025 break; 7026 } 7027 case 'R': { 7028 if (!$this->rtl) { 7029 $x += $wdiff; 7030 } 7031 break; 7032 } 7033 } 7034 } 7035 } 7036 // fit the image on available space 7037 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 7038 // calculate new minimum dimensions in pixels 7039 $neww = round($w * $this->k * $dpi / $this->dpi); 7040 $newh = round($h * $this->k * $dpi / $this->dpi); 7041 // check if resize is necessary (resize is used only to reduce the image) 7042 $newsize = ($neww * $newh); 7043 $pixsize = ($pixw * $pixh); 7044 if (intval($resize) == 2) { 7045 $resize = true; 7046 } elseif ($newsize >= $pixsize) { 7047 $resize = false; 7048 } 7049 // check if image has been already added on document 7050 $newimage = true; 7051 if (in_array($file, $this->imagekeys)) { 7052 $newimage = false; 7053 // get existing image data 7054 $info = $this->getImageBuffer($file); 7055 if (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) { 7056 // check if the newer image is larger 7057 $oldsize = ($info['w'] * $info['h']); 7058 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 7059 $newimage = true; 7060 } 7061 } 7062 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)) { 7063 // create temp image file (without alpha channel) 7064 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash; 7065 // create temp alpha file 7066 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash; 7067 // check for cached images 7068 if (in_array($tempfile_plain, $this->imagekeys)) { 7069 // get existing image data 7070 $info = $this->getImageBuffer($tempfile_plain); 7071 // check if the newer image is larger 7072 $oldsize = ($info['w'] * $info['h']); 7073 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 7074 $newimage = true; 7075 } else { 7076 $newimage = false; 7077 // embed mask image 7078 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 7079 // embed image, masked with previously embedded mask 7080 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 7081 } 7082 } 7083 } 7084 if ($newimage) { 7085 //First use of image, get info 7086 $type = strtolower($type); 7087 if ($type == '') { 7088 $type = TCPDF_IMAGES::getImageFileType($file, $imsize); 7089 } elseif ($type == 'jpg') { 7090 $type = 'jpeg'; 7091 } 7092 $mqr = TCPDF_STATIC::get_mqr(); 7093 TCPDF_STATIC::set_mqr(false); 7094 // Specific image handlers (defined on TCPDF_IMAGES CLASS) 7095 $mtd = '_parse'.$type; 7096 // GD image handler function 7097 $gdfunction = 'imagecreatefrom'.$type; 7098 $info = false; 7099 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) { 7100 // TCPDF image functions 7101 $info = TCPDF_IMAGES::$mtd($file); 7102 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) 7103 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) { 7104 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash); 7105 } 7106 } 7107 if (($info === false) AND function_exists($gdfunction)) { 7108 try { 7109 // GD library 7110 $img = $gdfunction($file); 7111 if ($img !== false) { 7112 if ($resize) { 7113 $imgr = imagecreatetruecolor($neww, $newh); 7114 if (($type == 'gif') OR ($type == 'png')) { 7115 $imgr = TCPDF_IMAGES::setGDImageTransparency($imgr, $img); 7116 } 7117 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 7118 $img = $imgr; 7119 } 7120 if (($type == 'gif') OR ($type == 'png')) { 7121 $info = TCPDF_IMAGES::_toPNG($img, TCPDF_STATIC::getObjFilename('img', $this->file_id)); 7122 } else { 7123 $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality, TCPDF_STATIC::getObjFilename('img', $this->file_id)); 7124 } 7125 } 7126 } catch(Exception $e) { 7127 $info = false; 7128 } 7129 } 7130 if (($info === false) AND extension_loaded('imagick')) { 7131 try { 7132 // ImageMagick library 7133 $img = new Imagick(); 7134 if ($type == 'svg') { 7135 if ($file[0] === '@') { 7136 // image from string 7137 $svgimg = substr($file, 1); 7138 } else { 7139 // get SVG file content 7140 $svgimg = $this->getCachedFileContents($file); 7141 } 7142 if ($svgimg !== FALSE) { 7143 // get width and height 7144 $regs = array(); 7145 if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) { 7146 $svgtag = $regs[1]; 7147 $tmp = array(); 7148 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { 7149 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 7150 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit; 7151 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1); 7152 } else { 7153 $ow = $w; 7154 } 7155 $tmp = array(); 7156 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { 7157 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 7158 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit; 7159 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1); 7160 } else { 7161 $oh = $h; 7162 } 7163 $tmp = array(); 7164 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) { 7165 $vbw = ($ow * $this->imgscale * $this->k); 7166 $vbh = ($oh * $this->imgscale * $this->k); 7167 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh); 7168 $svgtag = $vbox.$svgtag; 7169 } 7170 $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1); 7171 } 7172 $img->readImageBlob($svgimg); 7173 } 7174 } else { 7175 $img->readImage($file); 7176 } 7177 if ($resize) { 7178 $img->resizeImage($neww, $newh, 10, 1, false); 7179 } 7180 $img->setCompressionQuality($this->jpeg_quality); 7181 $img->setImageFormat('jpeg'); 7182 $tempname = TCPDF_STATIC::getObjFilename('img', $this->file_id); 7183 $img->writeImage($tempname); 7184 $info = TCPDF_IMAGES::_parsejpeg($tempname); 7185 unlink($tempname); 7186 $img->destroy(); 7187 } catch(Exception $e) { 7188 $info = false; 7189 } 7190 } 7191 if ($info === false) { 7192 // unable to process image 7193 return; 7194 } 7195 TCPDF_STATIC::set_mqr($mqr); 7196 if ($ismask) { 7197 // force grayscale 7198 $info['cs'] = 'DeviceGray'; 7199 } 7200 if ($imgmask !== false) { 7201 $info['masked'] = $imgmask; 7202 } 7203 if (!empty($exurl)) { 7204 $info['exurl'] = $exurl; 7205 } 7206 // array of alternative images 7207 $info['altimgs'] = $altimgs; 7208 // add image to document 7209 $info['i'] = $this->setImageBuffer($file, $info); 7210 } 7211 // set alignment 7212 $this->img_rb_x = $x + $w; 7213 $this->img_rb_y = $y + $h; 7214 7215 // set alignment 7216 if ($palign == 'L') { 7217 $ximg = $this->lMargin; 7218 } elseif ($palign == 'C') { 7219 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 7220 } elseif ($palign == 'R') { 7221 $ximg = $this->w - $this->rMargin - $w; 7222 } else { 7223 $ximg = $x; 7224 } 7225 7226 if ($ismask OR $hidden) { 7227 // image is not displayed 7228 return $info['i']; 7229 } 7230 $xkimg = $ximg * $this->k; 7231 if (!$alt) { 7232 // only non-alternative immages will be set 7233 $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'])); 7234 } 7235 if (!empty($border)) { 7236 $bx = $this->x; 7237 $by = $this->y; 7238 $this->x = $ximg; 7239 if ($this->rtl) { 7240 $this->x += $w; 7241 } 7242 $this->y = $y; 7243 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 7244 $this->x = $bx; 7245 $this->y = $by; 7246 } 7247 if ($link) { 7248 $this->Link($ximg, $y, $w, $h, $link, 0); 7249 } 7250 // set pointer to align the next text/objects 7251 switch($align) { 7252 case 'T': { 7253 $this->y = $y; 7254 $this->x = $this->img_rb_x; 7255 break; 7256 } 7257 case 'M': { 7258 $this->y = $y + round($h/2); 7259 $this->x = $this->img_rb_x; 7260 break; 7261 } 7262 case 'B': { 7263 $this->y = $this->img_rb_y; 7264 $this->x = $this->img_rb_x; 7265 break; 7266 } 7267 case 'N': { 7268 $this->SetY($this->img_rb_y); 7269 break; 7270 } 7271 default:{ 7272 break; 7273 } 7274 } 7275 $this->endlinex = $this->img_rb_x; 7276 if ($this->inxobj) { 7277 // we are inside an XObject template 7278 $this->xobjects[$this->xobjid]['images'][] = $info['i']; 7279 } 7280 return $info['i']; 7281 } 7282 7283 /** 7284 * Extract info from a PNG image with alpha channel using the Imagick or GD library. 7285 * @param $file (string) Name of the file containing the image. 7286 * @param $x (float) Abscissa of the upper-left corner. 7287 * @param $y (float) Ordinate of the upper-left corner. 7288 * @param $wpx (float) Original width of the image in pixels. 7289 * @param $hpx (float) original height of the image in pixels. 7290 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 7291 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 7292 * @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. 7293 * @param $link (mixed) URL or identifier returned by AddLink(). 7294 * @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> 7295 * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library). 7296 * @param $dpi (int) dot-per-inch resolution used on resize 7297 * @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> 7298 * @param $filehash (string) File hash used to build unique file names. 7299 * @author Nicola Asuni 7300 * @protected 7301 * @since 4.3.007 (2008-12-04) 7302 * @see Image() 7303 */ 7304 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') { 7305 // create temp images 7306 if (empty($filehash)) { 7307 $filehash = md5($file); 7308 } 7309 // create temp image file (without alpha channel) 7310 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash; 7311 // create temp alpha file 7312 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash; 7313 $parsed = false; 7314 $parse_error = ''; 7315 // ImageMagick extension 7316 if (($parsed === false) AND extension_loaded('imagick')) { 7317 try { 7318 // ImageMagick library 7319 $img = new Imagick(); 7320 $img->readImage($file); 7321 // clone image object 7322 $imga = TCPDF_STATIC::objclone($img); 7323 // extract alpha channel 7324 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) { 7325 $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT); 7326 } else { 7327 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE); 7328 $img->negateImage(true); 7329 } 7330 $img->setImageFormat('png'); 7331 $img->writeImage($tempfile_alpha); 7332 // remove alpha channel 7333 if (method_exists($imga, 'setImageMatte')) { 7334 $imga->setImageMatte(false); 7335 } else { 7336 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE)); 7337 } 7338 $imga->setImageFormat('png'); 7339 $imga->writeImage($tempfile_plain); 7340 $parsed = true; 7341 } catch (Exception $e) { 7342 // Imagemagick fails, try with GD 7343 $parse_error = 'Imagick library error: '.$e->getMessage(); 7344 } 7345 } 7346 // GD extension 7347 if (($parsed === false) AND function_exists('imagecreatefrompng')) { 7348 try { 7349 // generate images 7350 $img = imagecreatefrompng($file); 7351 $imgalpha = imagecreate($wpx, $hpx); 7352 // generate gray scale palette (0 -> 255) 7353 for ($c = 0; $c < 256; ++$c) { 7354 ImageColorAllocate($imgalpha, $c, $c, $c); 7355 } 7356 // extract alpha channel 7357 for ($xpx = 0; $xpx < $wpx; ++$xpx) { 7358 for ($ypx = 0; $ypx < $hpx; ++$ypx) { 7359 $color = imagecolorat($img, $xpx, $ypx); 7360 // get and correct gamma color 7361 $alpha = $this->getGDgamma($img, $color); 7362 imagesetpixel($imgalpha, $xpx, $ypx, $alpha); 7363 } 7364 } 7365 imagepng($imgalpha, $tempfile_alpha); 7366 imagedestroy($imgalpha); 7367 // extract image without alpha channel 7368 $imgplain = imagecreatetruecolor($wpx, $hpx); 7369 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx); 7370 imagepng($imgplain, $tempfile_plain); 7371 imagedestroy($imgplain); 7372 $parsed = true; 7373 } catch (Exception $e) { 7374 // GD fails 7375 $parse_error = 'GD library error: '.$e->getMessage(); 7376 } 7377 } 7378 if ($parsed === false) { 7379 if (empty($parse_error)) { 7380 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.'); 7381 } else { 7382 $this->Error($parse_error); 7383 } 7384 } 7385 // embed mask image 7386 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 7387 // embed image, masked with previously embedded mask 7388 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 7389 } 7390 7391 /** 7392 * Get the GD-corrected PNG gamma value from alpha color 7393 * @param $img (int) GD image Resource ID. 7394 * @param $c (int) alpha color 7395 * @protected 7396 * @since 4.3.007 (2008-12-04) 7397 */ 7398 protected function getGDgamma($img, $c) { 7399 if (!isset($this->gdgammacache['#'.$c])) { 7400 $colors = imagecolorsforindex($img, $c); 7401 // GD alpha is only 7 bit (0 -> 127) 7402 $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255); 7403 // correct gamma 7404 $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255); 7405 // store the latest values on cache to improve performances 7406 if (count($this->gdgammacache) > 8) { 7407 // remove one element from the cache array 7408 array_shift($this->gdgammacache); 7409 } 7410 } 7411 return $this->gdgammacache['#'.$c]; 7412 } 7413 7414 /** 7415 * Performs a line break. 7416 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. 7417 * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell. 7418 * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate 7419 * @public 7420 * @since 1.0 7421 * @see Cell() 7422 */ 7423 public function Ln($h='', $cell=false) { 7424 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'])) { 7425 // revove vertical space from the top of the column 7426 return; 7427 } 7428 if ($cell) { 7429 if ($this->rtl) { 7430 $cellpadding = $this->cell_padding['R']; 7431 } else { 7432 $cellpadding = $this->cell_padding['L']; 7433 } 7434 } else { 7435 $cellpadding = 0; 7436 } 7437 if ($this->rtl) { 7438 $this->x = $this->w - $this->rMargin - $cellpadding; 7439 } else { 7440 $this->x = $this->lMargin + $cellpadding; 7441 } 7442 if (is_string($h)) { 7443 $h = $this->lasth; 7444 } 7445 $this->y += $h; 7446 $this->newline = true; 7447 } 7448 7449 /** 7450 * Returns the relative X value of current position. 7451 * The value is relative to the left border for LTR languages and to the right border for RTL languages. 7452 * @return float 7453 * @public 7454 * @since 1.2 7455 * @see SetX(), GetY(), SetY() 7456 */ 7457 public function GetX() { 7458 //Get x position 7459 if ($this->rtl) { 7460 return ($this->w - $this->x); 7461 } else { 7462 return $this->x; 7463 } 7464 } 7465 7466 /** 7467 * Returns the absolute X value of current position. 7468 * @return float 7469 * @public 7470 * @since 1.2 7471 * @see SetX(), GetY(), SetY() 7472 */ 7473 public function GetAbsX() { 7474 return $this->x; 7475 } 7476 7477 /** 7478 * Returns the ordinate of the current position. 7479 * @return float 7480 * @public 7481 * @since 1.0 7482 * @see SetY(), GetX(), SetX() 7483 */ 7484 public function GetY() { 7485 return $this->y; 7486 } 7487 7488 /** 7489 * Defines the abscissa of the current position. 7490 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). 7491 * @param $x (float) The value of the abscissa in user units. 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 GetX(), GetY(), SetY(), SetXY() 7496 */ 7497 public function SetX($x, $rtloff=false) { 7498 $x = floatval($x); 7499 if (!$rtloff AND $this->rtl) { 7500 if ($x >= 0) { 7501 $this->x = $this->w - $x; 7502 } else { 7503 $this->x = abs($x); 7504 } 7505 } else { 7506 if ($x >= 0) { 7507 $this->x = $x; 7508 } else { 7509 $this->x = $this->w + $x; 7510 } 7511 } 7512 if ($this->x < 0) { 7513 $this->x = 0; 7514 } 7515 if ($this->x > $this->w) { 7516 $this->x = $this->w; 7517 } 7518 } 7519 7520 /** 7521 * Moves the current abscissa back to the left margin and sets the ordinate. 7522 * If the passed value is negative, it is relative to the bottom of the page. 7523 * @param $y (float) The value of the ordinate in user units. 7524 * @param $resetx (bool) if true (default) reset the X position. 7525 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. 7526 * @public 7527 * @since 1.0 7528 * @see GetX(), GetY(), SetY(), SetXY() 7529 */ 7530 public function SetY($y, $resetx=true, $rtloff=false) { 7531 $y = floatval($y); 7532 if ($resetx) { 7533 //reset x 7534 if (!$rtloff AND $this->rtl) { 7535 $this->x = $this->w - $this->rMargin; 7536 } else { 7537 $this->x = $this->lMargin; 7538 } 7539 } 7540 if ($y >= 0) { 7541 $this->y = $y; 7542 } else { 7543 $this->y = $this->h + $y; 7544 } 7545 if ($this->y < 0) { 7546 $this->y = 0; 7547 } 7548 if ($this->y > $this->h) { 7549 $this->y = $this->h; 7550 } 7551 } 7552 7553 /** 7554 * Defines the abscissa and ordinate of the current position. 7555 * If the passed values are negative, they are relative respectively to the right and bottom of the page. 7556 * @param $x (float) The value of the abscissa. 7557 * @param $y (float) The value of the ordinate. 7558 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. 7559 * @public 7560 * @since 1.2 7561 * @see SetX(), SetY() 7562 */ 7563 public function SetXY($x, $y, $rtloff=false) { 7564 $this->SetY($y, false, $rtloff); 7565 $this->SetX($x, $rtloff); 7566 } 7567 7568 /** 7569 * Set the absolute X coordinate of the current pointer. 7570 * @param $x (float) The value of the abscissa in user units. 7571 * @public 7572 * @since 5.9.186 (2012-09-13) 7573 * @see setAbsX(), setAbsY(), SetAbsXY() 7574 */ 7575 public function SetAbsX($x) { 7576 $this->x = floatval($x); 7577 } 7578 7579 /** 7580 * Set the absolute Y coordinate of the current pointer. 7581 * @param $y (float) (float) The value of the ordinate in user units. 7582 * @public 7583 * @since 5.9.186 (2012-09-13) 7584 * @see setAbsX(), setAbsY(), SetAbsXY() 7585 */ 7586 public function SetAbsY($y) { 7587 $this->y = floatval($y); 7588 } 7589 7590 /** 7591 * Set the absolute X and Y coordinates of the current pointer. 7592 * @param $x (float) The value of the abscissa in user units. 7593 * @param $y (float) (float) The value of the ordinate in user units. 7594 * @public 7595 * @since 5.9.186 (2012-09-13) 7596 * @see setAbsX(), setAbsY(), SetAbsXY() 7597 */ 7598 public function SetAbsXY($x, $y) { 7599 $this->SetAbsX($x); 7600 $this->SetAbsY($y); 7601 } 7602 7603 /** 7604 * Send the document to a given destination: string, local file or browser. 7605 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> 7606 * The method first calls Close() if necessary to terminate the document. 7607 * @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. 7608 * @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> 7609 * @return string 7610 * @public 7611 * @since 1.0 7612 * @see Close() 7613 */ 7614 public function Output($name='doc.pdf', $dest='I') { 7615 //Output PDF to some destination 7616 //Finish document if necessary 7617 if ($this->state < 3) { 7618 $this->Close(); 7619 } 7620 //Normalize parameters 7621 if (is_bool($dest)) { 7622 $dest = $dest ? 'D' : 'F'; 7623 } 7624 $dest = strtoupper($dest); 7625 if ($dest[0] != 'F') { 7626 $name = preg_replace('/[\s]+/', '_', $name); 7627 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name); 7628 } 7629 if ($this->sign) { 7630 // *** apply digital signature to the document *** 7631 // get the document content 7632 $pdfdoc = $this->getBuffer(); 7633 // remove last newline 7634 $pdfdoc = substr($pdfdoc, 0, -1); 7635 // remove filler space 7636 $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string); 7637 // define the ByteRange 7638 $byte_range = array(); 7639 $byte_range[0] = 0; 7640 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10; 7641 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2; 7642 $byte_range[3] = strlen($pdfdoc) - $byte_range[2]; 7643 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]); 7644 // replace the ByteRange 7645 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]); 7646 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange))); 7647 $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc); 7648 // write the document to a temporary folder 7649 $tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id); 7650 $f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb'); 7651 if (!$f) { 7652 $this->Error('Unable to create temporary file: '.$tempdoc); 7653 } 7654 $pdfdoc_length = strlen($pdfdoc); 7655 fwrite($f, $pdfdoc, $pdfdoc_length); 7656 fclose($f); 7657 // get digital signature via openssl library 7658 $tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id); 7659 if (empty($this->signature_data['extracerts'])) { 7660 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED); 7661 } else { 7662 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']); 7663 } 7664 // read signature 7665 $signature = file_get_contents($tempsign); 7666 // extract signature 7667 $signature = substr($signature, $pdfdoc_length); 7668 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); 7669 $tmparr = explode("\n\n", $signature); 7670 $signature = $tmparr[1]; 7671 // decode signature 7672 $signature = base64_decode(trim($signature)); 7673 // add TSA timestamp to signature 7674 $signature = $this->applyTSA($signature); 7675 // convert signature to hex 7676 $signature = current(unpack('H*', $signature)); 7677 $signature = str_pad($signature, $this->signature_max_length, '0'); 7678 // Add signature to the document 7679 $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]); 7680 $this->bufferlen = strlen($this->buffer); 7681 } 7682 switch($dest) { 7683 case 'I': { 7684 // Send PDF to the standard output 7685 if (ob_get_contents()) { 7686 $this->Error('Some data has already been output, can\'t send PDF file'); 7687 } 7688 if (php_sapi_name() != 'cli') { 7689 // send output to a browser 7690 header('Content-Type: application/pdf'); 7691 if (headers_sent()) { 7692 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7693 } 7694 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7695 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7696 header('Pragma: public'); 7697 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7698 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7699 header('Content-Disposition: inline; filename="'.basename($name).'"'); 7700 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen); 7701 } else { 7702 echo $this->getBuffer(); 7703 } 7704 break; 7705 } 7706 case 'D': { 7707 // download PDF as file 7708 if (ob_get_contents()) { 7709 $this->Error('Some data has already been output, can\'t send PDF file'); 7710 } 7711 header('Content-Description: File Transfer'); 7712 if (headers_sent()) { 7713 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7714 } 7715 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7716 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7717 header('Pragma: public'); 7718 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7719 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7720 // force download dialog 7721 if (strpos(php_sapi_name(), 'cgi') === false) { 7722 header('Content-Type: application/force-download'); 7723 header('Content-Type: application/octet-stream', false); 7724 header('Content-Type: application/download', false); 7725 header('Content-Type: application/pdf', false); 7726 } else { 7727 header('Content-Type: application/pdf'); 7728 } 7729 // use the Content-Disposition header to supply a recommended filename 7730 header('Content-Disposition: attachment; filename="'.basename($name).'"'); 7731 header('Content-Transfer-Encoding: binary'); 7732 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen); 7733 break; 7734 } 7735 case 'F': 7736 case 'FI': 7737 case 'FD': { 7738 // save PDF to a local file 7739 $f = TCPDF_STATIC::fopenLocal($name, 'wb'); 7740 if (!$f) { 7741 $this->Error('Unable to create output file: '.$name); 7742 } 7743 fwrite($f, $this->getBuffer(), $this->bufferlen); 7744 fclose($f); 7745 if ($dest == 'FI') { 7746 // send headers to browser 7747 header('Content-Type: application/pdf'); 7748 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7749 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 7750 header('Pragma: public'); 7751 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7752 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7753 header('Content-Disposition: inline; filename="'.basename($name).'"'); 7754 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name)); 7755 } elseif ($dest == 'FD') { 7756 // send headers to browser 7757 if (ob_get_contents()) { 7758 $this->Error('Some data has already been output, can\'t send PDF file'); 7759 } 7760 header('Content-Description: File Transfer'); 7761 if (headers_sent()) { 7762 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 7763 } 7764 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); 7765 header('Pragma: public'); 7766 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 7767 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 7768 // force download dialog 7769 if (strpos(php_sapi_name(), 'cgi') === false) { 7770 header('Content-Type: application/force-download'); 7771 header('Content-Type: application/octet-stream', false); 7772 header('Content-Type: application/download', false); 7773 header('Content-Type: application/pdf', false); 7774 } else { 7775 header('Content-Type: application/pdf'); 7776 } 7777 // use the Content-Disposition header to supply a recommended filename 7778 header('Content-Disposition: attachment; filename="'.basename($name).'"'); 7779 header('Content-Transfer-Encoding: binary'); 7780 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name)); 7781 } 7782 break; 7783 } 7784 case 'E': { 7785 // return PDF as base64 mime multi-part email attachment (RFC 2045) 7786 $retval = 'Content-Type: application/pdf;'."\r\n"; 7787 $retval .= ' name="'.$name.'"'."\r\n"; 7788 $retval .= 'Content-Transfer-Encoding: base64'."\r\n"; 7789 $retval .= 'Content-Disposition: attachment;'."\r\n"; 7790 $retval .= ' filename="'.$name.'"'."\r\n\r\n"; 7791 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n"); 7792 return $retval; 7793 } 7794 case 'S': { 7795 // returns PDF as a string 7796 return $this->getBuffer(); 7797 } 7798 default: { 7799 $this->Error('Incorrect output destination: '.$dest); 7800 } 7801 } 7802 return ''; 7803 } 7804 7805 protected static $cleaned_ids = array(); 7806 /** 7807 * Unset all class variables except the following critical variables. 7808 * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables. 7809 * @param $preserve_objcopy (boolean) if true preserves the objcopy variable 7810 * @public 7811 * @since 4.5.016 (2009-02-24) 7812 */ 7813 public function _destroy($destroyall=false, $preserve_objcopy=false) { 7814 if (isset(self::$cleaned_ids[$this->file_id])) { 7815 $destroyall = false; 7816 } 7817 if ($destroyall AND !$preserve_objcopy && isset($this->file_id)) { 7818 self::$cleaned_ids[$this->file_id] = true; 7819 // remove all temporary files 7820 if ($handle = @opendir(K_PATH_CACHE)) { 7821 while ( false !== ( $file_name = readdir( $handle ) ) ) { 7822 if (strpos($file_name, '__tcpdf_'.$this->file_id.'_') === 0) { 7823 unlink(K_PATH_CACHE.$file_name); 7824 } 7825 } 7826 closedir($handle); 7827 } 7828 if (isset($this->imagekeys)) { 7829 foreach($this->imagekeys as $file) { 7830 if (strpos($file, K_PATH_CACHE) === 0 && TCPDF_STATIC::file_exists($file)) { 7831 @unlink($file); 7832 } 7833 } 7834 } 7835 } 7836 $preserve = array( 7837 'file_id', 7838 'state', 7839 'bufferlen', 7840 'buffer', 7841 'cached_files', 7842 'imagekeys', 7843 'sign', 7844 'signature_data', 7845 'signature_max_length', 7846 'byterange_string', 7847 'tsa_timestamp', 7848 'tsa_data' 7849 ); 7850 foreach (array_keys(get_object_vars($this)) as $val) { 7851 if ($destroyall OR !in_array($val, $preserve)) { 7852 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND ($val != 'file_id') AND isset($this->$val)) { 7853 unset($this->$val); 7854 } 7855 } 7856 } 7857 } 7858 7859 /** 7860 * Check for locale-related bug 7861 * @protected 7862 */ 7863 protected function _dochecks() { 7864 //Check for locale-related bug 7865 if (1.1 == 1) { 7866 $this->Error('Don\'t alter the locale before including class file'); 7867 } 7868 //Check for decimal separator 7869 if (sprintf('%.1F', 1.0) != '1.0') { 7870 setlocale(LC_NUMERIC, 'C'); 7871 } 7872 } 7873 7874 /** 7875 * Return an array containing variations for the basic page number alias. 7876 * @param $a (string) Base alias. 7877 * @return array of page number aliases 7878 * @protected 7879 */ 7880 protected function getInternalPageNumberAliases($a= '') { 7881 $alias = array(); 7882 // build array of Unicode + ASCII variants (the order is important) 7883 $alias = array('u' => array(), 'a' => array()); 7884 $u = '{'.$a.'}'; 7885 $alias['u'][] = TCPDF_STATIC::_escape($u); 7886 if ($this->isunicode) { 7887 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont)); 7888 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont)); 7889 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont)); 7890 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont)); 7891 } 7892 $alias['a'][] = TCPDF_STATIC::_escape($a); 7893 return $alias; 7894 } 7895 7896 /** 7897 * Return an array containing all internal page aliases. 7898 * @return array of page number aliases 7899 * @protected 7900 */ 7901 protected function getAllInternalPageNumberAliases() { 7902 $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); 7903 $pnalias = array(); 7904 foreach($basic_alias as $k => $a) { 7905 $pnalias[$k] = $this->getInternalPageNumberAliases($a); 7906 } 7907 return $pnalias; 7908 } 7909 7910 /** 7911 * Replace right shift page number aliases with spaces to correct right alignment. 7912 * This works perfectly only when using monospaced fonts. 7913 * @param $page (string) Page content. 7914 * @param $aliases (array) Array of page aliases. 7915 * @param $diff (int) initial difference to add. 7916 * @return replaced page content. 7917 * @protected 7918 */ 7919 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) { 7920 foreach ($aliases as $type => $alias) { 7921 foreach ($alias as $a) { 7922 // find position of compensation factor 7923 $startnum = (strpos($a, ':') + 1); 7924 $a = substr($a, 0, $startnum); 7925 if (($pos = strpos($page, $a)) !== false) { 7926 // end of alias 7927 $endnum = strpos($page, '}', $pos); 7928 // string to be replaced 7929 $aa = substr($page, $pos, ($endnum - $pos + 1)); 7930 // get compensation factor 7931 $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum)); 7932 $ratio = preg_replace('/[^0-9\.]/', '', $ratio); 7933 $ratio = floatval($ratio); 7934 if ($type == 'u') { 7935 $chrdiff = floor(($diff + 12) * $ratio); 7936 $shift = str_repeat(' ', $chrdiff); 7937 $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont); 7938 } else { 7939 $chrdiff = floor(($diff + 11) * $ratio); 7940 $shift = str_repeat(' ', $chrdiff); 7941 } 7942 $page = str_replace($aa, $shift, $page); 7943 } 7944 } 7945 } 7946 return $page; 7947 } 7948 7949 /** 7950 * Set page boxes to be included on page descriptions. 7951 * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'). 7952 * @protected 7953 */ 7954 protected function setPageBoxTypes($boxes) { 7955 $this->page_boxes = array(); 7956 foreach ($boxes as $box) { 7957 if (in_array($box, TCPDF_STATIC::$pageboxes)) { 7958 $this->page_boxes[] = $box; 7959 } 7960 } 7961 } 7962 7963 /** 7964 * Output pages (and replace page number aliases). 7965 * @protected 7966 */ 7967 protected function _putpages() { 7968 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 7969 // get internal aliases for page numbers 7970 $pnalias = $this->getAllInternalPageNumberAliases(); 7971 $num_pages = $this->numpages; 7972 $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1)); 7973 $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont); 7974 $ptp_num_chars = $this->GetNumChars($ptpa); 7975 $pagegroupnum = 0; 7976 $groupnum = 0; 7977 $ptgu = 1; 7978 $ptga = 1; 7979 $ptg_num_chars = 1; 7980 for ($n = 1; $n <= $num_pages; ++$n) { 7981 // get current page 7982 $temppage = $this->getPageBuffer($n); 7983 $pagelen = strlen($temppage); 7984 // set replacements for total pages number 7985 $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1)); 7986 $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont); 7987 $pnp_num_chars = $this->GetNumChars($pnpa); 7988 $pdiff = 0; // difference used for right shift alignment of page numbers 7989 $gdiff = 0; // difference used for right shift alignment of page group numbers 7990 if (!empty($this->pagegroups)) { 7991 if (isset($this->newpagegroup[$n])) { 7992 $pagegroupnum = 0; 7993 ++$groupnum; 7994 $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]); 7995 $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont); 7996 $ptg_num_chars = $this->GetNumChars($ptga); 7997 } 7998 ++$pagegroupnum; 7999 $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum); 8000 $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont); 8001 $png_num_chars = $this->GetNumChars($pnga); 8002 // replace page numbers 8003 $replace = array(); 8004 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']); 8005 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']); 8006 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']); 8007 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']); 8008 list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff); 8009 } 8010 // replace page numbers 8011 $replace = array(); 8012 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']); 8013 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']); 8014 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']); 8015 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']); 8016 list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff); 8017 // replace right shift alias 8018 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff)); 8019 // replace EPS marker 8020 $temppage = str_replace($this->epsmarker, '', $temppage); 8021 //Page 8022 $this->page_obj_id[$n] = $this->_newobj(); 8023 $out = '<<'; 8024 $out .= ' /Type /Page'; 8025 $out .= ' /Parent 1 0 R'; 8026 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 8027 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp); 8028 } 8029 $out .= ' /Resources 2 0 R'; 8030 foreach ($this->page_boxes as $box) { 8031 $out .= ' /'.$box; 8032 $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']); 8033 } 8034 if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) { 8035 $out .= ' /BoxColorInfo <<'; 8036 foreach ($this->page_boxes as $box) { 8037 if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) { 8038 $out .= ' /'.$box.' <<'; 8039 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) { 8040 $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C']; 8041 $out .= ' /C ['; 8042 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); 8043 $out .= ' ]'; 8044 } 8045 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) { 8046 $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k); 8047 } 8048 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) { 8049 $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S']; 8050 } 8051 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) { 8052 $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D']; 8053 $out .= ' /D ['; 8054 foreach ($dashes as $dash) { 8055 $out .= sprintf(' %F', ($dash * $this->k)); 8056 } 8057 $out .= ' ]'; 8058 } 8059 $out .= ' >>'; 8060 } 8061 } 8062 $out .= ' >>'; 8063 } 8064 $out .= ' /Contents '.($this->n + 1).' 0 R'; 8065 $out .= ' /Rotate '.$this->pagedim[$n]['Rotate']; 8066 if (!$this->pdfa_mode) { 8067 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>'; 8068 } 8069 if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) { 8070 // page transitions 8071 if (isset($this->pagedim[$n]['trans']['Dur'])) { 8072 $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur']; 8073 } 8074 $out .= ' /Trans <<'; 8075 $out .= ' /Type /Trans'; 8076 if (isset($this->pagedim[$n]['trans']['S'])) { 8077 $out .= ' /S /'.$this->pagedim[$n]['trans']['S']; 8078 } 8079 if (isset($this->pagedim[$n]['trans']['D'])) { 8080 $out .= ' /D '.$this->pagedim[$n]['trans']['D']; 8081 } 8082 if (isset($this->pagedim[$n]['trans']['Dm'])) { 8083 $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm']; 8084 } 8085 if (isset($this->pagedim[$n]['trans']['M'])) { 8086 $out .= ' /M /'.$this->pagedim[$n]['trans']['M']; 8087 } 8088 if (isset($this->pagedim[$n]['trans']['Di'])) { 8089 $out .= ' /Di '.$this->pagedim[$n]['trans']['Di']; 8090 } 8091 if (isset($this->pagedim[$n]['trans']['SS'])) { 8092 $out .= ' /SS '.$this->pagedim[$n]['trans']['SS']; 8093 } 8094 if (isset($this->pagedim[$n]['trans']['B'])) { 8095 $out .= ' /B '.$this->pagedim[$n]['trans']['B']; 8096 } 8097 $out .= ' >>'; 8098 } 8099 $out .= $this->_getannotsrefs($n); 8100 $out .= ' /PZ '.$this->pagedim[$n]['PZ']; 8101 $out .= ' >>'; 8102 $out .= "\n".'endobj'; 8103 $this->_out($out); 8104 //Page content 8105 $p = ($this->compress) ? gzcompress($temppage) : $temppage; 8106 $this->_newobj(); 8107 $p = $this->_getrawstream($p); 8108 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj'); 8109 } 8110 //Pages root 8111 $out = $this->_getobj(1)."\n"; 8112 $out .= '<< /Type /Pages /Kids ['; 8113 foreach($this->page_obj_id as $page_obj) { 8114 $out .= ' '.$page_obj.' 0 R'; 8115 } 8116 $out .= ' ] /Count '.$num_pages.' >>'; 8117 $out .= "\n".'endobj'; 8118 $this->_out($out); 8119 } 8120 8121 /** 8122 * Get references to page annotations. 8123 * @param $n (int) page number 8124 * @return string 8125 * @protected 8126 * @author Nicola Asuni 8127 * @since 5.0.010 (2010-05-17) 8128 */ 8129 protected function _getannotsrefs($n) { 8130 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) { 8131 return ''; 8132 } 8133 $out = ' /Annots ['; 8134 if (isset($this->PageAnnots[$n])) { 8135 foreach ($this->PageAnnots[$n] as $key => $val) { 8136 if (!in_array($val['n'], $this->radio_groups)) { 8137 $out .= ' '.$val['n'].' 0 R'; 8138 } 8139 } 8140 // add radiobutton groups 8141 if (isset($this->radiobutton_groups[$n])) { 8142 foreach ($this->radiobutton_groups[$n] as $key => $data) { 8143 if (isset($data['n'])) { 8144 $out .= ' '.$data['n'].' 0 R'; 8145 } 8146 } 8147 } 8148 } 8149 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) { 8150 // set reference for signature object 8151 $out .= ' '.$this->sig_obj_id.' 0 R'; 8152 } 8153 if (!empty($this->empty_signature_appearance)) { 8154 foreach ($this->empty_signature_appearance as $esa) { 8155 if ($esa['page'] == $n) { 8156 // set reference for empty signature objects 8157 $out .= ' '.$esa['objid'].' 0 R'; 8158 } 8159 } 8160 } 8161 $out .= ' ]'; 8162 return $out; 8163 } 8164 8165 /** 8166 * Output annotations objects for all pages. 8167 * !!! THIS METHOD IS NOT YET COMPLETED !!! 8168 * See section 12.5 of PDF 32000_2008 reference. 8169 * @protected 8170 * @author Nicola Asuni 8171 * @since 4.0.018 (2008-08-06) 8172 */ 8173 protected function _putannotsobjs() { 8174 // reset object counter 8175 for ($n=1; $n <= $this->numpages; ++$n) { 8176 if (isset($this->PageAnnots[$n])) { 8177 // set page annotations 8178 foreach ($this->PageAnnots[$n] as $key => $pl) { 8179 $annot_obj_id = $this->PageAnnots[$n][$key]['n']; 8180 // create annotation object for grouping radiobuttons 8181 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) { 8182 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n']; 8183 $annots = '<<'; 8184 $annots .= ' /Type /Annot'; 8185 $annots .= ' /Subtype /Widget'; 8186 $annots .= ' /Rect [0 0 0 0]'; 8187 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) { 8188 // read only 8189 $annots .= ' /F 68'; 8190 $annots .= ' /Ff 49153'; 8191 } else { 8192 $annots .= ' /F 4'; // default print for PDF/A 8193 $annots .= ' /Ff 49152'; 8194 } 8195 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id); 8196 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { 8197 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id); 8198 } 8199 $annots .= ' /FT /Btn'; 8200 $annots .= ' /Kids ['; 8201 $defval = ''; 8202 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) { 8203 if (isset($data['kid'])) { 8204 $annots .= ' '.$data['kid'].' 0 R'; 8205 if ($data['def'] !== 'Off') { 8206 $defval = $data['def']; 8207 } 8208 } 8209 } 8210 $annots .= ' ]'; 8211 if (!empty($defval)) { 8212 $annots .= ' /V /'.$defval; 8213 } 8214 $annots .= ' >>'; 8215 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj'); 8216 $this->form_obj_id[] = $radio_button_obj_id; 8217 // store object id to be used on Parent entry of Kids 8218 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id; 8219 } 8220 $formfield = false; 8221 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER); 8222 $a = $pl['x'] * $this->k; 8223 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k); 8224 $c = $pl['w'] * $this->k; 8225 $d = $pl['h'] * $this->k; 8226 $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d); 8227 // create new annotation object 8228 $annots = '<</Type /Annot'; 8229 $annots .= ' /Subtype /'.$pl['opt']['subtype']; 8230 $annots .= ' /Rect ['.$rect.']'; 8231 $ft = array('Btn', 'Tx', 'Ch', 'Sig'); 8232 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) { 8233 $annots .= ' /FT /'.$pl['opt']['ft']; 8234 $formfield = true; 8235 } 8236 if ($pl['opt']['subtype'] !== 'Link') { 8237 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id); 8238 } 8239 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R'; 8240 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id); 8241 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp); 8242 if (isset($pl['opt']['f'])) { 8243 $fval = 0; 8244 if (is_array($pl['opt']['f'])) { 8245 foreach ($pl['opt']['f'] as $f) { 8246 switch (strtolower($f)) { 8247 case 'invisible': { 8248 $fval += 1 << 0; 8249 break; 8250 } 8251 case 'hidden': { 8252 $fval += 1 << 1; 8253 break; 8254 } 8255 case 'print': { 8256 $fval += 1 << 2; 8257 break; 8258 } 8259 case 'nozoom': { 8260 $fval += 1 << 3; 8261 break; 8262 } 8263 case 'norotate': { 8264 $fval += 1 << 4; 8265 break; 8266 } 8267 case 'noview': { 8268 $fval += 1 << 5; 8269 break; 8270 } 8271 case 'readonly': { 8272 $fval += 1 << 6; 8273 break; 8274 } 8275 case 'locked': { 8276 $fval += 1 << 8; 8277 break; 8278 } 8279 case 'togglenoview': { 8280 $fval += 1 << 9; 8281 break; 8282 } 8283 case 'lockedcontents': { 8284 $fval += 1 << 10; 8285 break; 8286 } 8287 default: { 8288 break; 8289 } 8290 } 8291 } 8292 } else { 8293 $fval = intval($pl['opt']['f']); 8294 } 8295 } else { 8296 $fval = 4; 8297 } 8298 if ($this->pdfa_mode) { 8299 // force print flag for PDF/A mode 8300 $fval |= 4; 8301 } 8302 $annots .= ' /F '.intval($fval); 8303 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) { 8304 $annots .= ' /AS /'.$pl['opt']['as']; 8305 } 8306 if (isset($pl['opt']['ap'])) { 8307 // appearance stream 8308 $annots .= ' /AP <<'; 8309 if (is_array($pl['opt']['ap'])) { 8310 foreach ($pl['opt']['ap'] as $apmode => $apdef) { 8311 // $apmode can be: n = normal; r = rollover; d = down; 8312 $annots .= ' /'.strtoupper($apmode); 8313 if (is_array($apdef)) { 8314 $annots .= ' <<'; 8315 foreach ($apdef as $apstate => $stream) { 8316 // reference to XObject that define the appearance for this mode-state 8317 $apsobjid = $this->_putAPXObject($c, $d, $stream); 8318 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R'; 8319 } 8320 $annots .= ' >>'; 8321 } else { 8322 // reference to XObject that define the appearance for this mode 8323 $apsobjid = $this->_putAPXObject($c, $d, $apdef); 8324 $annots .= ' '.$apsobjid.' 0 R'; 8325 } 8326 } 8327 } else { 8328 $annots .= $pl['opt']['ap']; 8329 } 8330 $annots .= ' >>'; 8331 } 8332 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) { 8333 $annots .= ' /BS <<'; 8334 $annots .= ' /Type /Border'; 8335 if (isset($pl['opt']['bs']['w'])) { 8336 $annots .= ' /W '.intval($pl['opt']['bs']['w']); 8337 } 8338 $bstyles = array('S', 'D', 'B', 'I', 'U'); 8339 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) { 8340 $annots .= ' /S /'.$pl['opt']['bs']['s']; 8341 } 8342 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) { 8343 $annots .= ' /D ['; 8344 foreach ($pl['opt']['bs']['d'] as $cord) { 8345 $annots .= ' '.intval($cord); 8346 } 8347 $annots .= ']'; 8348 } 8349 $annots .= ' >>'; 8350 } else { 8351 $annots .= ' /Border ['; 8352 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) { 8353 $annots .= intval($pl['opt']['border'][0]).' '; 8354 $annots .= intval($pl['opt']['border'][1]).' '; 8355 $annots .= intval($pl['opt']['border'][2]); 8356 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) { 8357 $annots .= ' ['; 8358 foreach ($pl['opt']['border'][3] as $dash) { 8359 $annots .= intval($dash).' '; 8360 } 8361 $annots .= ']'; 8362 } 8363 } else { 8364 $annots .= '0 0 0'; 8365 } 8366 $annots .= ']'; 8367 } 8368 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) { 8369 $annots .= ' /BE <<'; 8370 $bstyles = array('S', 'C'); 8371 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) { 8372 $annots .= ' /S /'.$pl['opt']['bs']['s']; 8373 } else { 8374 $annots .= ' /S /S'; 8375 } 8376 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) { 8377 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']); 8378 } 8379 $annots .= '>>'; 8380 } 8381 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) { 8382 $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']); 8383 } 8384 //$annots .= ' /StructParent '; 8385 //$annots .= ' /OC '; 8386 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound'); 8387 if (in_array(strtolower($pl['opt']['subtype']), $markups)) { 8388 // this is a markup type 8389 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 8390 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id); 8391 } 8392 //$annots .= ' /Popup '; 8393 if (isset($pl['opt']['ca'])) { 8394 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca'])); 8395 } 8396 if (isset($pl['opt']['rc'])) { 8397 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); 8398 } 8399 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp); 8400 //$annots .= ' /IRT '; 8401 if (isset($pl['opt']['subj'])) { 8402 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id); 8403 } 8404 //$annots .= ' /RT '; 8405 //$annots .= ' /IT '; 8406 //$annots .= ' /ExData '; 8407 } 8408 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash'); 8409 // Annotation types 8410 switch (strtolower($pl['opt']['subtype'])) { 8411 case 'text': { 8412 if (isset($pl['opt']['open'])) { 8413 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false'); 8414 } 8415 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph'); 8416 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8417 $annots .= ' /Name /'.$pl['opt']['name']; 8418 } else { 8419 $annots .= ' /Name /Note'; 8420 } 8421 $statemodels = array('Marked', 'Review'); 8422 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) { 8423 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 8424 } else { 8425 $pl['opt']['statemodel'] = 'Marked'; 8426 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 8427 } 8428 if ($pl['opt']['statemodel'] == 'Marked') { 8429 $states = array('Accepted', 'Unmarked'); 8430 } else { 8431 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None'); 8432 } 8433 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) { 8434 $annots .= ' /State /'.$pl['opt']['state']; 8435 } else { 8436 if ($pl['opt']['statemodel'] == 'Marked') { 8437 $annots .= ' /State /Unmarked'; 8438 } else { 8439 $annots .= ' /State /None'; 8440 } 8441 } 8442 break; 8443 } 8444 case 'link': { 8445 if (is_string($pl['txt']) && !empty($pl['txt'])) { 8446 if ($pl['txt'][0] == '#') { 8447 // internal destination 8448 $annots .= ' /A <</S /GoTo /D '.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1)).'>>'; 8449 } elseif ($pl['txt'][0] == '%') { 8450 // embedded PDF file 8451 $filename = basename(substr($pl['txt'], 1)); 8452 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; 8453 } elseif ($pl['txt'][0] == '*') { 8454 // embedded generic file 8455 $filename = basename(substr($pl['txt'], 1)); 8456 $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});'; 8457 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>'; 8458 } else { 8459 $parsedUrl = parse_url($pl['txt']); 8460 if (empty($parsedUrl['scheme']) AND (!empty($parsedUrl['path']) && strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) { 8461 // relative link to a PDF file 8462 $dest = '[0 /Fit]'; // default page 0 8463 if (!empty($parsedUrl['fragment'])) { 8464 // check for named destination 8465 $tmp = explode('=', $parsedUrl['fragment']); 8466 $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')'; 8467 } 8468 $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>'; 8469 } else { 8470 // external URI link 8471 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>'; 8472 } 8473 } 8474 } elseif (isset($this->links[$pl['txt']])) { 8475 // internal link ID 8476 $l = $this->links[$pl['txt']]; 8477 if (isset($this->page_obj_id[($l['p'])])) { 8478 $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))); 8479 } 8480 } 8481 $hmodes = array('N', 'I', 'O', 'P'); 8482 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) { 8483 $annots .= ' /H /'.$pl['opt']['h']; 8484 } else { 8485 $annots .= ' /H /I'; 8486 } 8487 //$annots .= ' /PA '; 8488 //$annots .= ' /Quadpoints '; 8489 break; 8490 } 8491 case 'freetext': { 8492 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 8493 $annots .= ' /DA ('.$pl['opt']['da'].')'; 8494 } 8495 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 8496 $annots .= ' /Q '.intval($pl['opt']['q']); 8497 } 8498 if (isset($pl['opt']['rc'])) { 8499 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); 8500 } 8501 if (isset($pl['opt']['ds'])) { 8502 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id); 8503 } 8504 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) { 8505 $annots .= ' /CL ['; 8506 foreach ($pl['opt']['cl'] as $cl) { 8507 $annots .= sprintf('%F ', $cl * $this->k); 8508 } 8509 $annots .= ']'; 8510 } 8511 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter'); 8512 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) { 8513 $annots .= ' /IT /'.$pl['opt']['it']; 8514 } 8515 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) { 8516 $l = $pl['opt']['rd'][0] * $this->k; 8517 $r = $pl['opt']['rd'][1] * $this->k; 8518 $t = $pl['opt']['rd'][2] * $this->k; 8519 $b = $pl['opt']['rd'][3] * $this->k; 8520 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']'; 8521 } 8522 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) { 8523 $annots .= ' /LE /'.$pl['opt']['le']; 8524 } 8525 break; 8526 } 8527 case 'line': { 8528 break; 8529 } 8530 case 'square': { 8531 break; 8532 } 8533 case 'circle': { 8534 break; 8535 } 8536 case 'polygon': { 8537 break; 8538 } 8539 case 'polyline': { 8540 break; 8541 } 8542 case 'highlight': { 8543 break; 8544 } 8545 case 'underline': { 8546 break; 8547 } 8548 case 'squiggly': { 8549 break; 8550 } 8551 case 'strikeout': { 8552 break; 8553 } 8554 case 'stamp': { 8555 break; 8556 } 8557 case 'caret': { 8558 break; 8559 } 8560 case 'ink': { 8561 break; 8562 } 8563 case 'popup': { 8564 break; 8565 } 8566 case 'fileattachment': { 8567 if ($this->pdfa_mode && $this->pdfa_version != 3) { 8568 // embedded files are not allowed in PDF/A mode version 1 and 2 8569 break; 8570 } 8571 if (!isset($pl['opt']['fs'])) { 8572 break; 8573 } 8574 $filename = basename($pl['opt']['fs']); 8575 if (isset($this->embeddedfiles[$filename]['f'])) { 8576 $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R'; 8577 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag'); 8578 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8579 $annots .= ' /Name /'.$pl['opt']['name']; 8580 } else { 8581 $annots .= ' /Name /PushPin'; 8582 } 8583 // index (zero-based) of the annotation in the Annots array of this page 8584 $this->embeddedfiles[$filename]['a'] = $key; 8585 } 8586 break; 8587 } 8588 case 'sound': { 8589 if (!isset($pl['opt']['fs'])) { 8590 break; 8591 } 8592 $filename = basename($pl['opt']['fs']); 8593 if (isset($this->embeddedfiles[$filename]['f'])) { 8594 // ... TO BE COMPLETED ... 8595 // /R /C /B /E /CO /CP 8596 $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R'; 8597 $iconsapp = array('Speaker', 'Mic'); 8598 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 8599 $annots .= ' /Name /'.$pl['opt']['name']; 8600 } else { 8601 $annots .= ' /Name /Speaker'; 8602 } 8603 } 8604 break; 8605 } 8606 case 'movie': { 8607 break; 8608 } 8609 case 'widget': { 8610 $hmode = array('N', 'I', 'O', 'P', 'T'); 8611 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) { 8612 $annots .= ' /H /'.$pl['opt']['h']; 8613 } 8614 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) { 8615 $annots .= ' /MK <<'; 8616 if (isset($pl['opt']['mk']['r'])) { 8617 $annots .= ' /R '.$pl['opt']['mk']['r']; 8618 } 8619 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) { 8620 $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']); 8621 } 8622 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) { 8623 $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']); 8624 } 8625 if (isset($pl['opt']['mk']['ca'])) { 8626 $annots .= ' /CA '.$pl['opt']['mk']['ca']; 8627 } 8628 if (isset($pl['opt']['mk']['rc'])) { 8629 $annots .= ' /RC '.$pl['opt']['mk']['rc']; 8630 } 8631 if (isset($pl['opt']['mk']['ac'])) { 8632 $annots .= ' /AC '.$pl['opt']['mk']['ac']; 8633 } 8634 if (isset($pl['opt']['mk']['i'])) { 8635 $info = $this->getImageBuffer($pl['opt']['mk']['i']); 8636 if ($info !== false) { 8637 $annots .= ' /I '.$info['n'].' 0 R'; 8638 } 8639 } 8640 if (isset($pl['opt']['mk']['ri'])) { 8641 $info = $this->getImageBuffer($pl['opt']['mk']['ri']); 8642 if ($info !== false) { 8643 $annots .= ' /RI '.$info['n'].' 0 R'; 8644 } 8645 } 8646 if (isset($pl['opt']['mk']['ix'])) { 8647 $info = $this->getImageBuffer($pl['opt']['mk']['ix']); 8648 if ($info !== false) { 8649 $annots .= ' /IX '.$info['n'].' 0 R'; 8650 } 8651 } 8652 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) { 8653 $annots .= ' /IF <<'; 8654 $if_sw = array('A', 'B', 'S', 'N'); 8655 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) { 8656 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw']; 8657 } 8658 $if_s = array('A', 'P'); 8659 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) { 8660 $annots .= ' /S /'.$pl['opt']['mk']['if']['s']; 8661 } 8662 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) { 8663 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]); 8664 } 8665 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) { 8666 $annots .= ' /FB true'; 8667 } 8668 $annots .= '>>'; 8669 } 8670 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) { 8671 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']); 8672 } 8673 $annots .= '>>'; 8674 } // end MK 8675 // --- Entries for field dictionaries --- 8676 if (isset($this->radiobutton_groups[$n][$pl['txt']])) { 8677 // set parent 8678 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R'; 8679 } 8680 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 8681 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id); 8682 } 8683 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { 8684 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id); 8685 } 8686 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) { 8687 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id); 8688 } 8689 if (isset($pl['opt']['ff'])) { 8690 if (is_array($pl['opt']['ff'])) { 8691 // array of bit settings 8692 $flag = 0; 8693 foreach($pl['opt']['ff'] as $val) { 8694 $flag += 1 << ($val - 1); 8695 } 8696 } else { 8697 $flag = intval($pl['opt']['ff']); 8698 } 8699 $annots .= ' /Ff '.$flag; 8700 } 8701 if (isset($pl['opt']['maxlen'])) { 8702 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']); 8703 } 8704 if (isset($pl['opt']['v'])) { 8705 $annots .= ' /V'; 8706 if (is_array($pl['opt']['v'])) { 8707 foreach ($pl['opt']['v'] AS $optval) { 8708 if (is_float($optval)) { 8709 $optval = sprintf('%F', $optval); 8710 } 8711 $annots .= ' '.$optval; 8712 } 8713 } else { 8714 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id); 8715 } 8716 } 8717 if (isset($pl['opt']['dv'])) { 8718 $annots .= ' /DV'; 8719 if (is_array($pl['opt']['dv'])) { 8720 foreach ($pl['opt']['dv'] AS $optval) { 8721 if (is_float($optval)) { 8722 $optval = sprintf('%F', $optval); 8723 } 8724 $annots .= ' '.$optval; 8725 } 8726 } else { 8727 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id); 8728 } 8729 } 8730 if (isset($pl['opt']['rv'])) { 8731 $annots .= ' /RV'; 8732 if (is_array($pl['opt']['rv'])) { 8733 foreach ($pl['opt']['rv'] AS $optval) { 8734 if (is_float($optval)) { 8735 $optval = sprintf('%F', $optval); 8736 } 8737 $annots .= ' '.$optval; 8738 } 8739 } else { 8740 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id); 8741 } 8742 } 8743 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) { 8744 $annots .= ' /A << '.$pl['opt']['a'].' >>'; 8745 } 8746 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) { 8747 $annots .= ' /AA << '.$pl['opt']['aa'].' >>'; 8748 } 8749 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 8750 $annots .= ' /DA ('.$pl['opt']['da'].')'; 8751 } 8752 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 8753 $annots .= ' /Q '.intval($pl['opt']['q']); 8754 } 8755 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) { 8756 $annots .= ' /Opt ['; 8757 foreach($pl['opt']['opt'] AS $copt) { 8758 if (is_array($copt)) { 8759 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']'; 8760 } else { 8761 $annots .= ' '.$this->_textstring($copt, $annot_obj_id); 8762 } 8763 } 8764 $annots .= ']'; 8765 } 8766 if (isset($pl['opt']['ti'])) { 8767 $annots .= ' /TI '.intval($pl['opt']['ti']); 8768 } 8769 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) { 8770 $annots .= ' /I ['; 8771 foreach($pl['opt']['i'] AS $copt) { 8772 $annots .= intval($copt).' '; 8773 } 8774 $annots .= ']'; 8775 } 8776 break; 8777 } 8778 case 'screen': { 8779 break; 8780 } 8781 case 'printermark': { 8782 break; 8783 } 8784 case 'trapnet': { 8785 break; 8786 } 8787 case 'watermark': { 8788 break; 8789 } 8790 case '3d': { 8791 break; 8792 } 8793 default: { 8794 break; 8795 } 8796 } 8797 $annots .= '>>'; 8798 // create new annotation object 8799 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj'); 8800 if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) { 8801 // store reference of form object 8802 $this->form_obj_id[] = $annot_obj_id; 8803 } 8804 } 8805 } 8806 } // end for each page 8807 } 8808 8809 /** 8810 * Put appearance streams XObject used to define annotation's appearance states. 8811 * @param $w (int) annotation width 8812 * @param $h (int) annotation height 8813 * @param $stream (string) appearance stream 8814 * @return int object ID 8815 * @protected 8816 * @since 4.8.001 (2009-09-09) 8817 */ 8818 protected function _putAPXObject($w=0, $h=0, $stream='') { 8819 $stream = trim($stream); 8820 $out = $this->_getobj()."\n"; 8821 $this->xobjects['AX'.$this->n] = array('n' => $this->n); 8822 $out .= '<<'; 8823 $out .= ' /Type /XObject'; 8824 $out .= ' /Subtype /Form'; 8825 $out .= ' /FormType 1'; 8826 if ($this->compress) { 8827 $stream = gzcompress($stream); 8828 $out .= ' /Filter /FlateDecode'; 8829 } 8830 $rect = sprintf('%F %F', $w, $h); 8831 $out .= ' /BBox [0 0 '.$rect.']'; 8832 $out .= ' /Matrix [1 0 0 1 0 0]'; 8833 $out .= ' /Resources 2 0 R'; 8834 $stream = $this->_getrawstream($stream); 8835 $out .= ' /Length '.strlen($stream); 8836 $out .= ' >>'; 8837 $out .= ' stream'."\n".$stream."\n".'endstream'; 8838 $out .= "\n".'endobj'; 8839 $this->_out($out); 8840 return $this->n; 8841 } 8842 8843 /** 8844 * Output fonts. 8845 * @author Nicola Asuni 8846 * @protected 8847 */ 8848 protected function _putfonts() { 8849 $nf = $this->n; 8850 foreach ($this->diffs as $diff) { 8851 //Encodings 8852 $this->_newobj(); 8853 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj'); 8854 } 8855 $mqr = TCPDF_STATIC::get_mqr(); 8856 TCPDF_STATIC::set_mqr(false); 8857 foreach ($this->FontFiles as $file => $info) { 8858 // search and get font file to embedd 8859 $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']); 8860 if (!TCPDF_STATIC::empty_string($fontfile)) { 8861 $font = file_get_contents($fontfile); 8862 $compressed = (substr($file, -2) == '.z'); 8863 if ((!$compressed) AND (isset($info['length2']))) { 8864 $header = (ord($font[0]) == 128); 8865 if ($header) { 8866 // strip first binary header 8867 $font = substr($font, 6); 8868 } 8869 if ($header AND (ord($font[$info['length1']]) == 128)) { 8870 // strip second binary header 8871 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6)); 8872 } 8873 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) { 8874 if ($compressed) { 8875 // uncompress font 8876 $font = gzuncompress($font); 8877 } 8878 // merge subset characters 8879 $subsetchars = array(); // used chars 8880 foreach ($info['fontkeys'] as $fontkey) { 8881 $fontinfo = $this->getFontBuffer($fontkey); 8882 $subsetchars += $fontinfo['subsetchars']; 8883 } 8884 // rebuild a font subset 8885 $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars); 8886 // calculate new font length 8887 $info['length1'] = strlen($font); 8888 if ($compressed) { 8889 // recompress font 8890 $font = gzcompress($font); 8891 } 8892 } 8893 $this->_newobj(); 8894 $this->FontFiles[$file]['n'] = $this->n; 8895 $stream = $this->_getrawstream($font); 8896 $out = '<< /Length '.strlen($stream); 8897 if ($compressed) { 8898 $out .= ' /Filter /FlateDecode'; 8899 } 8900 $out .= ' /Length1 '.$info['length1']; 8901 if (isset($info['length2'])) { 8902 $out .= ' /Length2 '.$info['length2'].' /Length3 0'; 8903 } 8904 $out .= ' >>'; 8905 $out .= ' stream'."\n".$stream."\n".'endstream'; 8906 $out .= "\n".'endobj'; 8907 $this->_out($out); 8908 } 8909 } 8910 TCPDF_STATIC::set_mqr($mqr); 8911 foreach ($this->fontkeys as $k) { 8912 //Font objects 8913 $font = $this->getFontBuffer($k); 8914 $type = $font['type']; 8915 $name = $font['name']; 8916 if ($type == 'core') { 8917 // standard core font 8918 $out = $this->_getobj($this->font_obj_ids[$k])."\n"; 8919 $out .= '<</Type /Font'; 8920 $out .= ' /Subtype /Type1'; 8921 $out .= ' /BaseFont /'.$name; 8922 $out .= ' /Name /F'.$font['i']; 8923 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) { 8924 $out .= ' /Encoding /WinAnsiEncoding'; 8925 } 8926 if ($k == 'helvetica') { 8927 // add default font for annotations 8928 $this->annotation_fonts[$k] = $font['i']; 8929 } 8930 $out .= ' >>'; 8931 $out .= "\n".'endobj'; 8932 $this->_out($out); 8933 } elseif (($type == 'Type1') OR ($type == 'TrueType')) { 8934 // additional Type1 or TrueType font 8935 $out = $this->_getobj($this->font_obj_ids[$k])."\n"; 8936 $out .= '<</Type /Font'; 8937 $out .= ' /Subtype /'.$type; 8938 $out .= ' /BaseFont /'.$name; 8939 $out .= ' /Name /F'.$font['i']; 8940 $out .= ' /FirstChar 32 /LastChar 255'; 8941 $out .= ' /Widths '.($this->n + 1).' 0 R'; 8942 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R'; 8943 if ($font['enc']) { 8944 if (isset($font['diff'])) { 8945 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R'; 8946 } else { 8947 $out .= ' /Encoding /WinAnsiEncoding'; 8948 } 8949 } 8950 $out .= ' >>'; 8951 $out .= "\n".'endobj'; 8952 $this->_out($out); 8953 // Widths 8954 $this->_newobj(); 8955 $s = '['; 8956 for ($i = 32; $i < 256; ++$i) { 8957 if (isset($font['cw'][$i])) { 8958 $s .= $font['cw'][$i].' '; 8959 } else { 8960 $s .= $font['dw'].' '; 8961 } 8962 } 8963 $s .= ']'; 8964 $s .= "\n".'endobj'; 8965 $this->_out($s); 8966 //Descriptor 8967 $this->_newobj(); 8968 $s = '<</Type /FontDescriptor /FontName /'.$name; 8969 foreach ($font['desc'] as $fdk => $fdv) { 8970 if (is_float($fdv)) { 8971 $fdv = sprintf('%F', $fdv); 8972 } 8973 $s .= ' /'.$fdk.' '.$fdv.''; 8974 } 8975 if (!TCPDF_STATIC::empty_string($font['file'])) { 8976 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; 8977 } 8978 $s .= '>>'; 8979 $s .= "\n".'endobj'; 8980 $this->_out($s); 8981 } else { 8982 // additional types 8983 $mtd = '_put'.strtolower($type); 8984 if (!method_exists($this, $mtd)) { 8985 $this->Error('Unsupported font type: '.$type); 8986 } 8987 $this->$mtd($font); 8988 } 8989 } 8990 } 8991 8992 /** 8993 * Adds unicode fonts.<br> 8994 * Based on PDF Reference 1.3 (section 5) 8995 * @param $font (array) font data 8996 * @protected 8997 * @author Nicola Asuni 8998 * @since 1.52.0.TC005 (2005-01-05) 8999 */ 9000 protected function _puttruetypeunicode($font) { 9001 $fontname = ''; 9002 if ($font['subset']) { 9003 // change name for font subsetting 9004 $subtag = sprintf('%06u', $font['i']); 9005 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ'); 9006 $fontname .= $subtag.'+'; 9007 } 9008 $fontname .= $font['name']; 9009 // Type0 Font 9010 // A composite font composed of other fonts, organized hierarchically 9011 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; 9012 $out .= '<< /Type /Font'; 9013 $out .= ' /Subtype /Type0'; 9014 $out .= ' /BaseFont /'.$fontname; 9015 $out .= ' /Name /F'.$font['i']; 9016 $out .= ' /Encoding /'.$font['enc']; 9017 $out .= ' /ToUnicode '.($this->n + 1).' 0 R'; 9018 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]'; 9019 $out .= ' >>'; 9020 $out .= "\n".'endobj'; 9021 $this->_out($out); 9022 // ToUnicode map for Identity-H 9023 $stream = TCPDF_FONT_DATA::$uni_identity_h; 9024 // ToUnicode Object 9025 $this->_newobj(); 9026 $stream = ($this->compress) ? gzcompress($stream) : $stream; 9027 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 9028 $stream = $this->_getrawstream($stream); 9029 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj'); 9030 // CIDFontType2 9031 // A CIDFont whose glyph descriptions are based on TrueType font technology 9032 $oid = $this->_newobj(); 9033 $out = '<< /Type /Font'; 9034 $out .= ' /Subtype /CIDFontType2'; 9035 $out .= ' /BaseFont /'.$fontname; 9036 // A dictionary containing entries that define the character collection of the CIDFont. 9037 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); 9038 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); 9039 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 9040 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>'; 9041 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 9042 $out .= ' /DW '.$font['dw']; // default width 9043 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0); 9044 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) { 9045 $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R'; 9046 } 9047 $out .= ' >>'; 9048 $out .= "\n".'endobj'; 9049 $this->_out($out); 9050 // Font descriptor 9051 // A font descriptor describing the CIDFont default metrics other than its glyph widths 9052 $this->_newobj(); 9053 $out = '<< /Type /FontDescriptor'; 9054 $out .= ' /FontName /'.$fontname; 9055 foreach ($font['desc'] as $key => $value) { 9056 if (is_float($value)) { 9057 $value = sprintf('%F', $value); 9058 } 9059 $out .= ' /'.$key.' '.$value; 9060 } 9061 $fontdir = false; 9062 if (!TCPDF_STATIC::empty_string($font['file'])) { 9063 // A stream containing a TrueType font 9064 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'; 9065 $fontdir = $this->FontFiles[$font['file']]['fontdir']; 9066 } 9067 $out .= ' >>'; 9068 $out .= "\n".'endobj'; 9069 $this->_out($out); 9070 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) { 9071 $this->_newobj(); 9072 // Embed CIDToGIDMap 9073 // A specification of the mapping from CIDs to glyph indices 9074 // search and get CTG font file to embedd 9075 $ctgfile = strtolower($font['ctg']); 9076 // search and get ctg font file to embedd 9077 $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir); 9078 if (TCPDF_STATIC::empty_string($fontfile)) { 9079 $this->Error('Font file not found: '.$ctgfile); 9080 } 9081 $stream = $this->_getrawstream(file_get_contents($fontfile)); 9082 $out = '<< /Length '.strlen($stream).''; 9083 if (substr($fontfile, -2) == '.z') { // check file extension 9084 // Decompresses data encoded using the public-domain 9085 // zlib/deflate compression method, reproducing the 9086 // original text or binary data 9087 $out .= ' /Filter /FlateDecode'; 9088 } 9089 $out .= ' >>'; 9090 $out .= ' stream'."\n".$stream."\n".'endstream'; 9091 $out .= "\n".'endobj'; 9092 $this->_out($out); 9093 } 9094 } 9095 9096 /** 9097 * Output CID-0 fonts. 9098 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format 9099 * @param $font (array) font data 9100 * @protected 9101 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira 9102 * @since 3.2.000 (2008-06-23) 9103 */ 9104 protected function _putcidfont0($font) { 9105 $cidoffset = 0; 9106 if (!isset($font['cw'][1])) { 9107 $cidoffset = 31; 9108 } 9109 if (isset($font['cidinfo']['uni2cid'])) { 9110 // convert unicode to cid. 9111 $uni2cid = $font['cidinfo']['uni2cid']; 9112 $cw = array(); 9113 foreach ($font['cw'] as $uni => $width) { 9114 if (isset($uni2cid[$uni])) { 9115 $cw[($uni2cid[$uni] + $cidoffset)] = $width; 9116 } elseif ($uni < 256) { 9117 $cw[$uni] = $width; 9118 } // else unknown character 9119 } 9120 $font = array_merge($font, array('cw' => $cw)); 9121 } 9122 $name = $font['name']; 9123 $enc = $font['enc']; 9124 if ($enc) { 9125 $longname = $name.'-'.$enc; 9126 } else { 9127 $longname = $name; 9128 } 9129 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; 9130 $out .= '<</Type /Font'; 9131 $out .= ' /Subtype /Type0'; 9132 $out .= ' /BaseFont /'.$longname; 9133 $out .= ' /Name /F'.$font['i']; 9134 if ($enc) { 9135 $out .= ' /Encoding /'.$enc; 9136 } 9137 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]'; 9138 $out .= ' >>'; 9139 $out .= "\n".'endobj'; 9140 $this->_out($out); 9141 $oid = $this->_newobj(); 9142 $out = '<</Type /Font'; 9143 $out .= ' /Subtype /CIDFontType0'; 9144 $out .= ' /BaseFont /'.$name; 9145 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); 9146 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); 9147 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 9148 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>'; 9149 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 9150 $out .= ' /DW '.$font['dw']; 9151 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset); 9152 $out .= ' >>'; 9153 $out .= "\n".'endobj'; 9154 $this->_out($out); 9155 $this->_newobj(); 9156 $s = '<</Type /FontDescriptor /FontName /'.$name; 9157 foreach ($font['desc'] as $k => $v) { 9158 if ($k != 'Style') { 9159 if (is_float($v)) { 9160 $v = sprintf('%F', $v); 9161 } 9162 $s .= ' /'.$k.' '.$v.''; 9163 } 9164 } 9165 $s .= '>>'; 9166 $s .= "\n".'endobj'; 9167 $this->_out($s); 9168 } 9169 9170 /** 9171 * Output images. 9172 * @protected 9173 */ 9174 protected function _putimages() { 9175 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 9176 foreach ($this->imagekeys as $file) { 9177 $info = $this->getImageBuffer($file); 9178 // set object for alternate images array 9179 if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) { 9180 $altoid = $this->_newobj(); 9181 $out = '['; 9182 foreach ($info['altimgs'] as $altimage) { 9183 if (isset($this->xobjects['I'.$altimage[0]]['n'])) { 9184 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R'; 9185 $out .= ' /DefaultForPrinting'; 9186 if ($altimage[1] === true) { 9187 $out .= ' true'; 9188 } else { 9189 $out .= ' false'; 9190 } 9191 $out .= ' >>'; 9192 } 9193 } 9194 $out .= ' ]'; 9195 $out .= "\n".'endobj'; 9196 $this->_out($out); 9197 } 9198 // set image object 9199 $oid = $this->_newobj(); 9200 $this->xobjects['I'.$info['i']] = array('n' => $oid); 9201 $this->setImageSubBuffer($file, 'n', $this->n); 9202 $out = '<</Type /XObject'; 9203 $out .= ' /Subtype /Image'; 9204 $out .= ' /Width '.$info['w']; 9205 $out .= ' /Height '.$info['h']; 9206 if (array_key_exists('masked', $info)) { 9207 $out .= ' /SMask '.($this->n - 1).' 0 R'; 9208 } 9209 // set color space 9210 $icc = false; 9211 if (isset($info['icc']) AND ($info['icc'] !== false)) { 9212 // ICC Colour Space 9213 $icc = true; 9214 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]'; 9215 } elseif ($info['cs'] == 'Indexed') { 9216 // Indexed Colour Space 9217 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]'; 9218 } else { 9219 // Device Colour Space 9220 $out .= ' /ColorSpace /'.$info['cs']; 9221 } 9222 if ($info['cs'] == 'DeviceCMYK') { 9223 $out .= ' /Decode [1 0 1 0 1 0 1 0]'; 9224 } 9225 $out .= ' /BitsPerComponent '.$info['bpc']; 9226 if (isset($altoid) AND ($altoid > 0)) { 9227 // reference to alternate images dictionary 9228 $out .= ' /Alternates '.$altoid.' 0 R'; 9229 } 9230 if (isset($info['exurl']) AND !empty($info['exurl'])) { 9231 // external stream 9232 $out .= ' /Length 0'; 9233 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>'; 9234 if (isset($info['f'])) { 9235 $out .= ' /FFilter /'.$info['f']; 9236 } 9237 $out .= ' >>'; 9238 $out .= ' stream'."\n".'endstream'; 9239 } else { 9240 if (isset($info['f'])) { 9241 $out .= ' /Filter /'.$info['f']; 9242 } 9243 if (isset($info['parms'])) { 9244 $out .= ' '.$info['parms']; 9245 } 9246 if (isset($info['trns']) AND is_array($info['trns'])) { 9247 $trns = ''; 9248 $count_info = count($info['trns']); 9249 if ($info['cs'] == 'Indexed') { 9250 $maxval =(pow(2, $info['bpc']) - 1); 9251 for ($i = 0; $i < $count_info; ++$i) { 9252 if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) { 9253 // this is not a binary type mask @TODO: create a SMask 9254 $trns = ''; 9255 break; 9256 } elseif (empty($trns) AND ($info['trns'][$i] == 0)) { 9257 // store the first fully transparent value 9258 $trns .= $i.' '.$i.' '; 9259 } 9260 } 9261 } else { 9262 // grayscale or RGB 9263 for ($i = 0; $i < $count_info; ++$i) { 9264 if ($info['trns'][$i] == 0) { 9265 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; 9266 } 9267 } 9268 } 9269 // Colour Key Masking 9270 if (!empty($trns)) { 9271 $out .= ' /Mask ['.$trns.']'; 9272 } 9273 } 9274 $stream = $this->_getrawstream($info['data']); 9275 $out .= ' /Length '.strlen($stream).' >>'; 9276 $out .= ' stream'."\n".$stream."\n".'endstream'; 9277 } 9278 $out .= "\n".'endobj'; 9279 $this->_out($out); 9280 if ($icc) { 9281 // ICC colour profile 9282 $this->_newobj(); 9283 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc']; 9284 $icc = $this->_getrawstream($icc); 9285 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); 9286 } elseif ($info['cs'] == 'Indexed') { 9287 // colour palette 9288 $this->_newobj(); 9289 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; 9290 $pal = $this->_getrawstream($pal); 9291 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj'); 9292 } 9293 } 9294 } 9295 9296 /** 9297 * Output Form XObjects Templates. 9298 * @author Nicola Asuni 9299 * @since 5.8.017 (2010-08-24) 9300 * @protected 9301 * @see startTemplate(), endTemplate(), printTemplate() 9302 */ 9303 protected function _putxobjects() { 9304 foreach ($this->xobjects as $key => $data) { 9305 if (isset($data['outdata'])) { 9306 $stream = str_replace($this->epsmarker, '', trim($data['outdata'])); 9307 $out = $this->_getobj($data['n'])."\n"; 9308 $out .= '<<'; 9309 $out .= ' /Type /XObject'; 9310 $out .= ' /Subtype /Form'; 9311 $out .= ' /FormType 1'; 9312 if ($this->compress) { 9313 $stream = gzcompress($stream); 9314 $out .= ' /Filter /FlateDecode'; 9315 } 9316 $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)); 9317 $out .= ' /Matrix [1 0 0 1 0 0]'; 9318 $out .= ' /Resources <<'; 9319 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 9320 if (!$this->pdfa_mode) { 9321 // transparency 9322 if (isset($data['extgstates']) AND !empty($data['extgstates'])) { 9323 $out .= ' /ExtGState <<'; 9324 foreach ($data['extgstates'] as $k => $extgstate) { 9325 if (isset($this->extgstates[$k]['name'])) { 9326 $out .= ' /'.$this->extgstates[$k]['name']; 9327 } else { 9328 $out .= ' /GS'.$k; 9329 } 9330 $out .= ' '.$this->extgstates[$k]['n'].' 0 R'; 9331 } 9332 $out .= ' >>'; 9333 } 9334 if (isset($data['gradients']) AND !empty($data['gradients'])) { 9335 $gp = ''; 9336 $gs = ''; 9337 foreach ($data['gradients'] as $id => $grad) { 9338 // gradient patterns 9339 $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R'; 9340 // gradient shadings 9341 $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R'; 9342 } 9343 $out .= ' /Pattern <<'.$gp.' >>'; 9344 $out .= ' /Shading <<'.$gs.' >>'; 9345 } 9346 } 9347 // spot colors 9348 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) { 9349 $out .= ' /ColorSpace <<'; 9350 foreach ($data['spot_colors'] as $name => $color) { 9351 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R'; 9352 } 9353 $out .= ' >>'; 9354 } 9355 // fonts 9356 if (!empty($data['fonts'])) { 9357 $out .= ' /Font <<'; 9358 foreach ($data['fonts'] as $fontkey => $fontid) { 9359 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; 9360 } 9361 $out .= ' >>'; 9362 } 9363 // images or nested xobjects 9364 if (!empty($data['images']) OR !empty($data['xobjects'])) { 9365 $out .= ' /XObject <<'; 9366 foreach ($data['images'] as $imgid) { 9367 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R'; 9368 } 9369 foreach ($data['xobjects'] as $sub_id => $sub_objid) { 9370 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R'; 9371 } 9372 $out .= ' >>'; 9373 } 9374 $out .= ' >>'; //end resources 9375 if (isset($data['group']) AND ($data['group'] !== false)) { 9376 // set transparency group 9377 $out .= ' /Group << /Type /Group /S /Transparency'; 9378 if (is_array($data['group'])) { 9379 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) { 9380 $out .= ' /CS /'.$data['group']['CS']; 9381 } 9382 if (isset($data['group']['I'])) { 9383 $out .= ' /I /'.($data['group']['I']===true?'true':'false'); 9384 } 9385 if (isset($data['group']['K'])) { 9386 $out .= ' /K /'.($data['group']['K']===true?'true':'false'); 9387 } 9388 } 9389 $out .= ' >>'; 9390 } 9391 $stream = $this->_getrawstream($stream, $data['n']); 9392 $out .= ' /Length '.strlen($stream); 9393 $out .= ' >>'; 9394 $out .= ' stream'."\n".$stream."\n".'endstream'; 9395 $out .= "\n".'endobj'; 9396 $this->_out($out); 9397 } 9398 } 9399 } 9400 9401 /** 9402 * Output Spot Colors Resources. 9403 * @protected 9404 * @since 4.0.024 (2008-09-12) 9405 */ 9406 protected function _putspotcolors() { 9407 foreach ($this->spot_colors as $name => $color) { 9408 $this->_newobj(); 9409 $this->spot_colors[$name]['n'] = $this->n; 9410 $out = '[/Separation /'.str_replace(' ', '#20', $name); 9411 $out .= ' /DeviceCMYK <<'; 9412 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]'; 9413 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100)); 9414 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]'; 9415 $out .= "\n".'endobj'; 9416 $this->_out($out); 9417 } 9418 } 9419 9420 /** 9421 * Return XObjects Dictionary. 9422 * @return string XObjects dictionary 9423 * @protected 9424 * @since 5.8.014 (2010-08-23) 9425 */ 9426 protected function _getxobjectdict() { 9427 $out = ''; 9428 foreach ($this->xobjects as $id => $objid) { 9429 $out .= ' /'.$id.' '.$objid['n'].' 0 R'; 9430 } 9431 return $out; 9432 } 9433 9434 /** 9435 * Output Resources Dictionary. 9436 * @protected 9437 */ 9438 protected function _putresourcedict() { 9439 $out = $this->_getobj(2)."\n"; 9440 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 9441 $out .= ' /Font <<'; 9442 foreach ($this->fontkeys as $fontkey) { 9443 $font = $this->getFontBuffer($fontkey); 9444 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R'; 9445 } 9446 $out .= ' >>'; 9447 $out .= ' /XObject <<'; 9448 $out .= $this->_getxobjectdict(); 9449 $out .= ' >>'; 9450 // layers 9451 if (!empty($this->pdflayers)) { 9452 $out .= ' /Properties <<'; 9453 foreach ($this->pdflayers as $layer) { 9454 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R'; 9455 } 9456 $out .= ' >>'; 9457 } 9458 if (!$this->pdfa_mode) { 9459 // transparency 9460 if (isset($this->extgstates) AND !empty($this->extgstates)) { 9461 $out .= ' /ExtGState <<'; 9462 foreach ($this->extgstates as $k => $extgstate) { 9463 if (isset($extgstate['name'])) { 9464 $out .= ' /'.$extgstate['name']; 9465 } else { 9466 $out .= ' /GS'.$k; 9467 } 9468 $out .= ' '.$extgstate['n'].' 0 R'; 9469 } 9470 $out .= ' >>'; 9471 } 9472 if (isset($this->gradients) AND !empty($this->gradients)) { 9473 $gp = ''; 9474 $gs = ''; 9475 foreach ($this->gradients as $id => $grad) { 9476 // gradient patterns 9477 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R'; 9478 // gradient shadings 9479 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R'; 9480 } 9481 $out .= ' /Pattern <<'.$gp.' >>'; 9482 $out .= ' /Shading <<'.$gs.' >>'; 9483 } 9484 } 9485 // spot colors 9486 if (isset($this->spot_colors) AND !empty($this->spot_colors)) { 9487 $out .= ' /ColorSpace <<'; 9488 foreach ($this->spot_colors as $color) { 9489 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R'; 9490 } 9491 $out .= ' >>'; 9492 } 9493 $out .= ' >>'; 9494 $out .= "\n".'endobj'; 9495 $this->_out($out); 9496 } 9497 9498 /** 9499 * Output Resources. 9500 * @protected 9501 */ 9502 protected function _putresources() { 9503 $this->_putextgstates(); 9504 $this->_putocg(); 9505 $this->_putfonts(); 9506 $this->_putimages(); 9507 $this->_putspotcolors(); 9508 $this->_putshaders(); 9509 $this->_putxobjects(); 9510 $this->_putresourcedict(); 9511 $this->_putdests(); 9512 $this->_putEmbeddedFiles(); 9513 $this->_putannotsobjs(); 9514 $this->_putjavascript(); 9515 $this->_putbookmarks(); 9516 $this->_putencryption(); 9517 } 9518 9519 /** 9520 * Adds some Metadata information (Document Information Dictionary) 9521 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference) 9522 * @return int object id 9523 * @protected 9524 */ 9525 protected function _putinfo() { 9526 $oid = $this->_newobj(); 9527 $out = '<<'; 9528 // store current isunicode value 9529 $prev_isunicode = $this->isunicode; 9530 if ($this->docinfounicode) { 9531 $this->isunicode = true; 9532 } 9533 if (!TCPDF_STATIC::empty_string($this->title)) { 9534 // The document's title. 9535 $out .= ' /Title '.$this->_textstring($this->title, $oid); 9536 } 9537 if (!TCPDF_STATIC::empty_string($this->author)) { 9538 // The name of the person who created the document. 9539 $out .= ' /Author '.$this->_textstring($this->author, $oid); 9540 } 9541 if (!TCPDF_STATIC::empty_string($this->subject)) { 9542 // The subject of the document. 9543 $out .= ' /Subject '.$this->_textstring($this->subject, $oid); 9544 } 9545 if (!TCPDF_STATIC::empty_string($this->keywords)) { 9546 // Keywords associated with the document. 9547 $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid); 9548 } 9549 if (!TCPDF_STATIC::empty_string($this->creator)) { 9550 // 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. 9551 $out .= ' /Creator '.$this->_textstring($this->creator, $oid); 9552 } 9553 // restore previous isunicode value 9554 $this->isunicode = $prev_isunicode; 9555 // default producer 9556 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid); 9557 // The date and time the document was created, in human-readable form 9558 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp); 9559 // The date and time the document was most recently modified, in human-readable form 9560 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp); 9561 // A name object indicating whether the document has been modified to include trapping information 9562 $out .= ' /Trapped /False'; 9563 $out .= ' >>'; 9564 $out .= "\n".'endobj'; 9565 $this->_out($out); 9566 return $oid; 9567 } 9568 9569 /** 9570 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag. 9571 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method! 9572 * @param $xmp (string) Custom XMP data. 9573 * @since 5.9.128 (2011-10-06) 9574 * @public 9575 */ 9576 public function setExtraXMP($xmp) { 9577 $this->custom_xmp = $xmp; 9578 } 9579 9580 /** 9581 * Set additional XMP data to be added on the default XMP data just before the end of "rdf:RDF" tag. 9582 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method! 9583 * @param $xmp (string) Custom XMP RDF data. 9584 * @since 6.3.0 (2019-09-19) 9585 * @public 9586 */ 9587 public function setExtraXMPRDF($xmp) { 9588 $this->custom_xmp_rdf = $xmp; 9589 } 9590 9591 /** 9592 * Put XMP data object and return ID. 9593 * @return (int) The object ID. 9594 * @since 5.9.121 (2011-09-28) 9595 * @protected 9596 */ 9597 protected function _putXMP() { 9598 $oid = $this->_newobj(); 9599 // store current isunicode value 9600 $prev_isunicode = $this->isunicode; 9601 $this->isunicode = true; 9602 $prev_encrypted = $this->encrypted; 9603 $this->encrypted = false; 9604 // set XMP data 9605 $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n"; 9606 $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"; 9607 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n"; 9608 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n"; 9609 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n"; 9610 $xmp .= "\t\t\t".'<dc:title>'."\n"; 9611 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; 9612 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n"; 9613 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; 9614 $xmp .= "\t\t\t".'</dc:title>'."\n"; 9615 $xmp .= "\t\t\t".'<dc:creator>'."\n"; 9616 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n"; 9617 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n"; 9618 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n"; 9619 $xmp .= "\t\t\t".'</dc:creator>'."\n"; 9620 $xmp .= "\t\t\t".'<dc:description>'."\n"; 9621 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; 9622 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n"; 9623 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; 9624 $xmp .= "\t\t\t".'</dc:description>'."\n"; 9625 $xmp .= "\t\t\t".'<dc:subject>'."\n"; 9626 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; 9627 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n"; 9628 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; 9629 $xmp .= "\t\t\t".'</dc:subject>'."\n"; 9630 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9631 // convert doc creation date format 9632 $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp); 9633 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2); 9634 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2); 9635 $doccreationdate .= substr($dcdate, 14, 3).':'.substr($dcdate, 18, 2); 9636 $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate); 9637 // convert doc modification date format 9638 $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp); 9639 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2); 9640 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2); 9641 $docmoddate .= substr($dmdate, 14, 3).':'.substr($dmdate, 18, 2); 9642 $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate); 9643 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n"; 9644 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n"; 9645 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n"; 9646 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n"; 9647 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n"; 9648 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9649 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n"; 9650 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n"; 9651 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n"; 9652 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9653 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n"; 9654 $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); 9655 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n"; 9656 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n"; 9657 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9658 if ($this->pdfa_mode) { 9659 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n"; 9660 $xmp .= "\t\t\t".'<pdfaid:part>'.$this->pdfa_version.'</pdfaid:part>'."\n"; 9661 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n"; 9662 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9663 } 9664 // XMP extension schemas 9665 $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"; 9666 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n"; 9667 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; 9668 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9669 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n"; 9670 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n"; 9671 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n"; 9672 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 9673 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 9674 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9675 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9676 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Adobe PDF Schema</pdfaProperty:description>'."\n"; 9677 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n"; 9678 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n"; 9679 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9680 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 9681 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 9682 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9683 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9684 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n"; 9685 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n"; 9686 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n"; 9687 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 9688 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 9689 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9690 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9691 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n"; 9692 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n"; 9693 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n"; 9694 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9695 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 9696 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 9697 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9698 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9699 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n"; 9700 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n"; 9701 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n"; 9702 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 9703 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 9704 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9705 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9706 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n"; 9707 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n"; 9708 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n"; 9709 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9710 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9711 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9712 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n"; 9713 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n"; 9714 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; 9715 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9716 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 9717 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 9718 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n"; 9719 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n"; 9720 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; 9721 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 9722 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 9723 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 9724 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 9725 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; 9726 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n"; 9727 $xmp .= "\t\t".'</rdf:Description>'."\n"; 9728 $xmp .= $this->custom_xmp_rdf; 9729 $xmp .= "\t".'</rdf:RDF>'."\n"; 9730 $xmp .= $this->custom_xmp; 9731 $xmp .= '</x:xmpmeta>'."\n"; 9732 $xmp .= '<?xpacket end="w"?>'; 9733 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj'; 9734 // restore previous isunicode value 9735 $this->isunicode = $prev_isunicode; 9736 $this->encrypted = $prev_encrypted; 9737 $this->_out($out); 9738 return $oid; 9739 } 9740 9741 /** 9742 * Output Catalog. 9743 * @return int object id 9744 * @protected 9745 */ 9746 protected function _putcatalog() { 9747 // put XMP 9748 $xmpobj = $this->_putXMP(); 9749 // if required, add standard sRGB ICC colour profile 9750 if ($this->pdfa_mode OR $this->force_srgb) { 9751 $iccobj = $this->_newobj(); 9752 $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc'); 9753 $filter = ''; 9754 if ($this->compress) { 9755 $filter = ' /Filter /FlateDecode'; 9756 $icc = gzcompress($icc); 9757 } 9758 $icc = $this->_getrawstream($icc); 9759 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); 9760 } 9761 // start catalog 9762 $oid = $this->_newobj(); 9763 $out = '<< /Type /Catalog'; 9764 $out .= ' /Version /'.$this->PDFVersion; 9765 //$out .= ' /Extensions <<>>'; 9766 $out .= ' /Pages 1 0 R'; 9767 //$out .= ' /PageLabels ' //...; 9768 $out .= ' /Names <<'; 9769 if ((!$this->pdfa_mode) AND !empty($this->n_js)) { 9770 $out .= ' /JavaScript '.$this->n_js; 9771 } 9772 if (!empty($this->efnames)) { 9773 $out .= ' /EmbeddedFiles <</Names ['; 9774 foreach ($this->efnames AS $fn => $fref) { 9775 $out .= ' '.$this->_datastring($fn).' '.$fref; 9776 } 9777 $out .= ' ]>>'; 9778 } 9779 $out .= ' >>'; 9780 if (!empty($this->dests)) { 9781 $out .= ' /Dests '.($this->n_dests).' 0 R'; 9782 } 9783 $out .= $this->_putviewerpreferences(); 9784 if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) { 9785 $out .= ' /PageLayout /'.$this->LayoutMode; 9786 } 9787 if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) { 9788 $out .= ' /PageMode /'.$this->PageMode; 9789 } 9790 if (count($this->outlines) > 0) { 9791 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R'; 9792 $out .= ' /PageMode /UseOutlines'; 9793 } 9794 //$out .= ' /Threads []'; 9795 if ($this->ZoomMode == 'fullpage') { 9796 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]'; 9797 } elseif ($this->ZoomMode == 'fullwidth') { 9798 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]'; 9799 } elseif ($this->ZoomMode == 'real') { 9800 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]'; 9801 } elseif (!is_string($this->ZoomMode)) { 9802 $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100)); 9803 } 9804 //$out .= ' /AA <<>>'; 9805 //$out .= ' /URI <<>>'; 9806 $out .= ' /Metadata '.$xmpobj.' 0 R'; 9807 //$out .= ' /StructTreeRoot <<>>'; 9808 //$out .= ' /MarkInfo <<>>'; 9809 if (isset($this->l['a_meta_language'])) { 9810 $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid); 9811 } 9812 //$out .= ' /SpiderInfo <<>>'; 9813 // set OutputIntent to sRGB IEC61966-2.1 if required 9814 if ($this->pdfa_mode OR $this->force_srgb) { 9815 $out .= ' /OutputIntents [<<'; 9816 $out .= ' /Type /OutputIntent'; 9817 $out .= ' /S /GTS_PDFA1'; 9818 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9819 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9820 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid); 9821 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid); 9822 $out .= ' /DestOutputProfile '.$iccobj.' 0 R'; 9823 $out .= ' >>]'; 9824 } 9825 //$out .= ' /PieceInfo <<>>'; 9826 if (!empty($this->pdflayers)) { 9827 $lyrobjs = ''; 9828 $lyrobjs_off = ''; 9829 $lyrobjs_lock = ''; 9830 foreach ($this->pdflayers as $layer) { 9831 $layer_obj_ref = ' '.$layer['objid'].' 0 R'; 9832 $lyrobjs .= $layer_obj_ref; 9833 if ($layer['view'] === false) { 9834 $lyrobjs_off .= $layer_obj_ref; 9835 } 9836 if ($layer['lock']) { 9837 $lyrobjs_lock .= $layer_obj_ref; 9838 } 9839 } 9840 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']'; 9841 $out .= ' /D <<'; 9842 $out .= ' /Name '.$this->_textstring('Layers', $oid); 9843 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid); 9844 $out .= ' /BaseState /ON'; 9845 $out .= ' /OFF ['.$lyrobjs_off.']'; 9846 $out .= ' /Locked ['.$lyrobjs_lock.']'; 9847 $out .= ' /Intent /View'; 9848 $out .= ' /AS ['; 9849 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>'; 9850 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>'; 9851 $out .= ' ]'; 9852 $out .= ' /Order ['.$lyrobjs.']'; 9853 $out .= ' /ListMode /AllPages'; 9854 //$out .= ' /RBGroups ['..']'; 9855 //$out .= ' /Locked ['..']'; 9856 $out .= ' >>'; 9857 $out .= ' >>'; 9858 } 9859 // AcroForm 9860 if (!empty($this->form_obj_id) 9861 OR ($this->sign AND isset($this->signature_data['cert_type'])) 9862 OR !empty($this->empty_signature_appearance)) { 9863 $out .= ' /AcroForm <<'; 9864 $objrefs = ''; 9865 if ($this->sign AND isset($this->signature_data['cert_type'])) { 9866 // set reference for signature object 9867 $objrefs .= $this->sig_obj_id.' 0 R'; 9868 } 9869 if (!empty($this->empty_signature_appearance)) { 9870 foreach ($this->empty_signature_appearance as $esa) { 9871 // set reference for empty signature objects 9872 $objrefs .= ' '.$esa['objid'].' 0 R'; 9873 } 9874 } 9875 if (!empty($this->form_obj_id)) { 9876 foreach($this->form_obj_id as $objid) { 9877 $objrefs .= ' '.$objid.' 0 R'; 9878 } 9879 } 9880 $out .= ' /Fields ['.$objrefs.']'; 9881 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields. 9882 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 9883 $out .= ' /NeedAppearances false'; 9884 } 9885 if ($this->sign AND isset($this->signature_data['cert_type'])) { 9886 if ($this->signature_data['cert_type'] > 0) { 9887 $out .= ' /SigFlags 3'; 9888 } else { 9889 $out .= ' /SigFlags 1'; 9890 } 9891 } 9892 //$out .= ' /CO '; 9893 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) { 9894 $out .= ' /DR <<'; 9895 $out .= ' /Font <<'; 9896 foreach ($this->annotation_fonts as $fontkey => $fontid) { 9897 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; 9898 } 9899 $out .= ' >> >>'; 9900 } 9901 $font = $this->getFontBuffer('helvetica'); 9902 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)'; 9903 $out .= ' /Q '.(($this->rtl)?'2':'0'); 9904 //$out .= ' /XFA '; 9905 $out .= ' >>'; 9906 // signatures 9907 if ($this->sign AND isset($this->signature_data['cert_type']) 9908 AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) { 9909 if ($this->signature_data['cert_type'] > 0) { 9910 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>'; 9911 } else { 9912 $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>'; 9913 } 9914 } 9915 } 9916 //$out .= ' /Legal <<>>'; 9917 //$out .= ' /Requirements []'; 9918 //$out .= ' /Collection <<>>'; 9919 //$out .= ' /NeedsRendering true'; 9920 $out .= ' >>'; 9921 $out .= "\n".'endobj'; 9922 $this->_out($out); 9923 return $oid; 9924 } 9925 9926 /** 9927 * Output viewer preferences. 9928 * @return string for viewer preferences 9929 * @author Nicola asuni 9930 * @since 3.1.000 (2008-06-09) 9931 * @protected 9932 */ 9933 protected function _putviewerpreferences() { 9934 $vp = $this->viewer_preferences; 9935 $out = ' /ViewerPreferences <<'; 9936 if ($this->rtl) { 9937 $out .= ' /Direction /R2L'; 9938 } else { 9939 $out .= ' /Direction /L2R'; 9940 } 9941 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) { 9942 $out .= ' /HideToolbar true'; 9943 } 9944 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) { 9945 $out .= ' /HideMenubar true'; 9946 } 9947 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) { 9948 $out .= ' /HideWindowUI true'; 9949 } 9950 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) { 9951 $out .= ' /FitWindow true'; 9952 } 9953 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) { 9954 $out .= ' /CenterWindow true'; 9955 } 9956 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) { 9957 $out .= ' /DisplayDocTitle true'; 9958 } 9959 if (isset($vp['NonFullScreenPageMode'])) { 9960 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode']; 9961 } 9962 if (isset($vp['ViewArea'])) { 9963 $out .= ' /ViewArea /'.$vp['ViewArea']; 9964 } 9965 if (isset($vp['ViewClip'])) { 9966 $out .= ' /ViewClip /'.$vp['ViewClip']; 9967 } 9968 if (isset($vp['PrintArea'])) { 9969 $out .= ' /PrintArea /'.$vp['PrintArea']; 9970 } 9971 if (isset($vp['PrintClip'])) { 9972 $out .= ' /PrintClip /'.$vp['PrintClip']; 9973 } 9974 if (isset($vp['PrintScaling'])) { 9975 $out .= ' /PrintScaling /'.$vp['PrintScaling']; 9976 } 9977 if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) { 9978 $out .= ' /Duplex /'.$vp['Duplex']; 9979 } 9980 if (isset($vp['PickTrayByPDFSize'])) { 9981 if ($vp['PickTrayByPDFSize']) { 9982 $out .= ' /PickTrayByPDFSize true'; 9983 } else { 9984 $out .= ' /PickTrayByPDFSize false'; 9985 } 9986 } 9987 if (isset($vp['PrintPageRange'])) { 9988 $PrintPageRangeNum = ''; 9989 foreach ($vp['PrintPageRange'] as $k => $v) { 9990 $PrintPageRangeNum .= ' '.($v - 1).''; 9991 } 9992 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']'; 9993 } 9994 if (isset($vp['NumCopies'])) { 9995 $out .= ' /NumCopies '.intval($vp['NumCopies']); 9996 } 9997 $out .= ' >>'; 9998 return $out; 9999 } 10000 10001 /** 10002 * Output PDF File Header (7.5.2). 10003 * @protected 10004 */ 10005 protected function _putheader() { 10006 $this->_out('%PDF-'.$this->PDFVersion); 10007 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3)); 10008 } 10009 10010 /** 10011 * Output end of document (EOF). 10012 * @protected 10013 */ 10014 protected function _enddoc() { 10015 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) { 10016 // save subset chars of the previous font 10017 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 10018 } 10019 $this->state = 1; 10020 $this->_putheader(); 10021 $this->_putpages(); 10022 $this->_putresources(); 10023 // empty signature fields 10024 if (!empty($this->empty_signature_appearance)) { 10025 foreach ($this->empty_signature_appearance as $key => $esa) { 10026 // widget annotation for empty signature 10027 $out = $this->_getobj($esa['objid'])."\n"; 10028 $out .= '<< /Type /Annot'; 10029 $out .= ' /Subtype /Widget'; 10030 $out .= ' /Rect ['.$esa['rect'].']'; 10031 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page 10032 $out .= ' /F 4'; 10033 $out .= ' /FT /Sig'; 10034 $signame = $esa['name'].sprintf(' [%03d]', ($key + 1)); 10035 $out .= ' /T '.$this->_textstring($signame, $esa['objid']); 10036 $out .= ' /Ff 0'; 10037 $out .= ' >>'; 10038 $out .= "\n".'endobj'; 10039 $this->_out($out); 10040 } 10041 } 10042 // Signature 10043 if ($this->sign AND isset($this->signature_data['cert_type'])) { 10044 // widget annotation for signature 10045 $out = $this->_getobj($this->sig_obj_id)."\n"; 10046 $out .= '<< /Type /Annot'; 10047 $out .= ' /Subtype /Widget'; 10048 $out .= ' /Rect ['.$this->signature_appearance['rect'].']'; 10049 $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page 10050 $out .= ' /F 4'; 10051 $out .= ' /FT /Sig'; 10052 $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id); 10053 $out .= ' /Ff 0'; 10054 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R'; 10055 $out .= ' >>'; 10056 $out .= "\n".'endobj'; 10057 $this->_out($out); 10058 // signature 10059 $this->_putsignature(); 10060 } 10061 // Info 10062 $objid_info = $this->_putinfo(); 10063 // Catalog 10064 $objid_catalog = $this->_putcatalog(); 10065 // Cross-ref 10066 $o = $this->bufferlen; 10067 // XREF section 10068 $this->_out('xref'); 10069 $this->_out('0 '.($this->n + 1)); 10070 $this->_out('0000000000 65535 f '); 10071 $freegen = ($this->n + 2); 10072 for ($i=1; $i <= $this->n; ++$i) { 10073 if (!isset($this->offsets[$i]) AND ($i > 1)) { 10074 $this->_out(sprintf('0000000000 %05d f ', $freegen)); 10075 ++$freegen; 10076 } else { 10077 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 10078 } 10079 } 10080 // TRAILER 10081 $out = 'trailer'."\n"; 10082 $out .= '<<'; 10083 $out .= ' /Size '.($this->n + 1); 10084 $out .= ' /Root '.$objid_catalog.' 0 R'; 10085 $out .= ' /Info '.$objid_info.' 0 R'; 10086 if ($this->encrypted) { 10087 $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R'; 10088 } 10089 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]'; 10090 $out .= ' >>'; 10091 $this->_out($out); 10092 $this->_out('startxref'); 10093 $this->_out($o); 10094 $this->_out('%%EOF'); 10095 $this->state = 3; // end-of-doc 10096 } 10097 10098 /** 10099 * Initialize a new page. 10100 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 10101 * @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(). 10102 * @protected 10103 * @see getPageSizeFromFormat(), setPageFormat() 10104 */ 10105 protected function _beginpage($orientation='', $format='') { 10106 ++$this->page; 10107 $this->pageobjects[$this->page] = array(); 10108 $this->setPageBuffer($this->page, ''); 10109 // initialize array for graphics tranformation positions inside a page buffer 10110 $this->transfmrk[$this->page] = array(); 10111 $this->state = 2; 10112 if (TCPDF_STATIC::empty_string($orientation)) { 10113 if (isset($this->CurOrientation)) { 10114 $orientation = $this->CurOrientation; 10115 } elseif ($this->fwPt > $this->fhPt) { 10116 // landscape 10117 $orientation = 'L'; 10118 } else { 10119 // portrait 10120 $orientation = 'P'; 10121 } 10122 } 10123 if (TCPDF_STATIC::empty_string($format)) { 10124 $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)]; 10125 $this->setPageOrientation($orientation); 10126 } else { 10127 $this->setPageFormat($format, $orientation); 10128 } 10129 if ($this->rtl) { 10130 $this->x = $this->w - $this->rMargin; 10131 } else { 10132 $this->x = $this->lMargin; 10133 } 10134 $this->y = $this->tMargin; 10135 if (isset($this->newpagegroup[$this->page])) { 10136 // start a new group 10137 $this->currpagegroup = $this->newpagegroup[$this->page]; 10138 $this->pagegroups[$this->currpagegroup] = 1; 10139 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { 10140 ++$this->pagegroups[$this->currpagegroup]; 10141 } 10142 } 10143 10144 /** 10145 * Mark end of page. 10146 * @protected 10147 */ 10148 protected function _endpage() { 10149 $this->setVisibility('all'); 10150 $this->state = 1; 10151 } 10152 10153 /** 10154 * Begin a new object and return the object number. 10155 * @return int object number 10156 * @protected 10157 */ 10158 protected function _newobj() { 10159 $this->_out($this->_getobj()); 10160 return $this->n; 10161 } 10162 10163 /** 10164 * Return the starting object string for the selected object ID. 10165 * @param $objid (int) Object ID (leave empty to get a new ID). 10166 * @return string the starting object string 10167 * @protected 10168 * @since 5.8.009 (2010-08-20) 10169 */ 10170 protected function _getobj($objid='') { 10171 if ($objid === '') { 10172 ++$this->n; 10173 $objid = $this->n; 10174 } 10175 $this->offsets[$objid] = $this->bufferlen; 10176 $this->pageobjects[$this->page][] = $objid; 10177 return $objid.' 0 obj'; 10178 } 10179 10180 /** 10181 * Underline text. 10182 * @param $x (int) X coordinate 10183 * @param $y (int) Y coordinate 10184 * @param $txt (string) text to underline 10185 * @protected 10186 */ 10187 protected function _dounderline($x, $y, $txt) { 10188 $w = $this->GetStringWidth($txt); 10189 return $this->_dounderlinew($x, $y, $w); 10190 } 10191 10192 /** 10193 * Underline for rectangular text area. 10194 * @param $x (int) X coordinate 10195 * @param $y (int) Y coordinate 10196 * @param $w (int) width to underline 10197 * @protected 10198 * @since 4.8.008 (2009-09-29) 10199 */ 10200 protected function _dounderlinew($x, $y, $w) { 10201 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10202 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew); 10203 } 10204 10205 /** 10206 * Line through text. 10207 * @param $x (int) X coordinate 10208 * @param $y (int) Y coordinate 10209 * @param $txt (string) text to linethrough 10210 * @protected 10211 */ 10212 protected function _dolinethrough($x, $y, $txt) { 10213 $w = $this->GetStringWidth($txt); 10214 return $this->_dolinethroughw($x, $y, $w); 10215 } 10216 10217 /** 10218 * Line through for rectangular text area. 10219 * @param $x (int) X coordinate 10220 * @param $y (int) Y coordinate 10221 * @param $w (int) line length (width) 10222 * @protected 10223 * @since 4.9.008 (2009-09-29) 10224 */ 10225 protected function _dolinethroughw($x, $y, $w) { 10226 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10227 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew); 10228 } 10229 10230 /** 10231 * Overline text. 10232 * @param $x (int) X coordinate 10233 * @param $y (int) Y coordinate 10234 * @param $txt (string) text to overline 10235 * @protected 10236 * @since 4.9.015 (2010-04-19) 10237 */ 10238 protected function _dooverline($x, $y, $txt) { 10239 $w = $this->GetStringWidth($txt); 10240 return $this->_dooverlinew($x, $y, $w); 10241 } 10242 10243 /** 10244 * Overline for rectangular text area. 10245 * @param $x (int) X coordinate 10246 * @param $y (int) Y coordinate 10247 * @param $w (int) width to overline 10248 * @protected 10249 * @since 4.9.015 (2010-04-19) 10250 */ 10251 protected function _dooverlinew($x, $y, $w) { 10252 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 10253 return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew); 10254 10255 } 10256 10257 /** 10258 * Format a data string for meta information 10259 * @param $s (string) data string to escape. 10260 * @param $n (int) object ID 10261 * @return string escaped string. 10262 * @protected 10263 */ 10264 protected function _datastring($s, $n=0) { 10265 if ($n == 0) { 10266 $n = $this->n; 10267 } 10268 $s = $this->_encrypt_data($n, $s); 10269 return '('. TCPDF_STATIC::_escape($s).')'; 10270 } 10271 10272 /** 10273 * Set the document creation timestamp 10274 * @param $time (mixed) Document creation timestamp in seconds or date-time string. 10275 * @public 10276 * @since 5.9.152 (2012-03-23) 10277 */ 10278 public function setDocCreationTimestamp($time) { 10279 if (is_string($time)) { 10280 $time = TCPDF_STATIC::getTimestamp($time); 10281 } 10282 $this->doc_creation_timestamp = intval($time); 10283 } 10284 10285 /** 10286 * Set the document modification timestamp 10287 * @param $time (mixed) Document modification timestamp in seconds or date-time string. 10288 * @public 10289 * @since 5.9.152 (2012-03-23) 10290 */ 10291 public function setDocModificationTimestamp($time) { 10292 if (is_string($time)) { 10293 $time = TCPDF_STATIC::getTimestamp($time); 10294 } 10295 $this->doc_modification_timestamp = intval($time); 10296 } 10297 10298 /** 10299 * Returns document creation timestamp in seconds. 10300 * @return (int) Creation timestamp in seconds. 10301 * @public 10302 * @since 5.9.152 (2012-03-23) 10303 */ 10304 public function getDocCreationTimestamp() { 10305 return $this->doc_creation_timestamp; 10306 } 10307 10308 /** 10309 * Returns document modification timestamp in seconds. 10310 * @return (int) Modfication timestamp in seconds. 10311 * @public 10312 * @since 5.9.152 (2012-03-23) 10313 */ 10314 public function getDocModificationTimestamp() { 10315 return $this->doc_modification_timestamp; 10316 } 10317 10318 /** 10319 * Returns a formatted date for meta information 10320 * @param $n (int) Object ID. 10321 * @param $timestamp (int) Timestamp to convert. 10322 * @return string escaped date string. 10323 * @protected 10324 * @since 4.6.028 (2009-08-25) 10325 */ 10326 protected function _datestring($n=0, $timestamp=0) { 10327 if ((empty($timestamp)) OR ($timestamp < 0)) { 10328 $timestamp = $this->doc_creation_timestamp; 10329 } 10330 return $this->_datastring('D:'.TCPDF_STATIC::getFormattedDate($timestamp), $n); 10331 } 10332 10333 /** 10334 * Format a text string for meta information 10335 * @param $s (string) string to escape. 10336 * @param $n (int) object ID 10337 * @return string escaped string. 10338 * @protected 10339 */ 10340 protected function _textstring($s, $n=0) { 10341 if ($this->isunicode) { 10342 //Convert string to UTF-16BE 10343 $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont); 10344 } 10345 return $this->_datastring($s, $n); 10346 } 10347 10348 /** 10349 * get raw output stream. 10350 * @param $s (string) string to output. 10351 * @param $n (int) object reference for encryption mode 10352 * @protected 10353 * @author Nicola Asuni 10354 * @since 5.5.000 (2010-06-22) 10355 */ 10356 protected function _getrawstream($s, $n=0) { 10357 if ($n <= 0) { 10358 // default to current object 10359 $n = $this->n; 10360 } 10361 return $this->_encrypt_data($n, $s); 10362 } 10363 10364 /** 10365 * Output a string to the document. 10366 * @param $s (string) string to output. 10367 * @protected 10368 */ 10369 protected function _out($s) { 10370 if ($this->state == 2) { 10371 if ($this->inxobj) { 10372 // we are inside an XObject template 10373 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n"; 10374 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { 10375 // puts data before page footer 10376 $pagebuff = $this->getPageBuffer($this->page); 10377 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); 10378 $footer = substr($pagebuff, -$this->footerlen[$this->page]); 10379 $this->setPageBuffer($this->page, $page.$s."\n".$footer); 10380 // update footer position 10381 $this->footerpos[$this->page] += strlen($s."\n"); 10382 } else { 10383 // set page data 10384 $this->setPageBuffer($this->page, $s."\n", true); 10385 } 10386 } elseif ($this->state > 0) { 10387 // set general data 10388 $this->setBuffer($s."\n"); 10389 } 10390 } 10391 10392 /** 10393 * Set header font. 10394 * @param $font (array) Array describing the basic font parameters: (family, style, size). 10395 * @public 10396 * @since 1.1 10397 */ 10398 public function setHeaderFont($font) { 10399 $this->header_font = $font; 10400 } 10401 10402 /** 10403 * Get header font. 10404 * @return array() Array describing the basic font parameters: (family, style, size). 10405 * @public 10406 * @since 4.0.012 (2008-07-24) 10407 */ 10408 public function getHeaderFont() { 10409 return $this->header_font; 10410 } 10411 10412 /** 10413 * Set footer font. 10414 * @param $font (array) Array describing the basic font parameters: (family, style, size). 10415 * @public 10416 * @since 1.1 10417 */ 10418 public function setFooterFont($font) { 10419 $this->footer_font = $font; 10420 } 10421 10422 /** 10423 * Get Footer font. 10424 * @return array() Array describing the basic font parameters: (family, style, size). 10425 * @public 10426 * @since 4.0.012 (2008-07-24) 10427 */ 10428 public function getFooterFont() { 10429 return $this->footer_font; 10430 } 10431 10432 /** 10433 * Set language array. 10434 * @param $language (array) 10435 * @public 10436 * @since 1.1 10437 */ 10438 public function setLanguageArray($language) { 10439 $this->l = $language; 10440 if (isset($this->l['a_meta_dir'])) { 10441 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; 10442 } else { 10443 $this->rtl = false; 10444 } 10445 } 10446 10447 /** 10448 * Returns the PDF data. 10449 * @public 10450 */ 10451 public function getPDFData() { 10452 if ($this->state < 3) { 10453 $this->Close(); 10454 } 10455 return $this->buffer; 10456 } 10457 10458 /** 10459 * Output anchor link. 10460 * @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>) 10461 * @param $name (string) link name 10462 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 10463 * @param $firstline (boolean) if true prints only the first line and return the remaining string. 10464 * @param $color (array) array of RGB text color 10465 * @param $style (string) font style (U, D, B, I) 10466 * @param $firstblock (boolean) if true the string is the starting of a line. 10467 * @return the number of cells used or the remaining text if $firstline = true; 10468 * @public 10469 */ 10470 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) { 10471 if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) { 10472 // convert url to internal link 10473 $lnkdata = explode(',', $url); 10474 if (isset($lnkdata[0]) ) { 10475 $page = substr($lnkdata[0], 1); 10476 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { 10477 $lnky = floatval($lnkdata[1]); 10478 } else { 10479 $lnky = 0; 10480 } 10481 $url = $this->AddLink(); 10482 $this->SetLink($url, $lnky, $page); 10483 } 10484 } 10485 // store current settings 10486 $prevcolor = $this->fgcolor; 10487 $prevstyle = $this->FontStyle; 10488 if (empty($color)) { 10489 $this->SetTextColorArray($this->htmlLinkColorArray); 10490 } else { 10491 $this->SetTextColorArray($color); 10492 } 10493 if ($style == -1) { 10494 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle); 10495 } else { 10496 $this->SetFont('', $this->FontStyle.$style); 10497 } 10498 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0); 10499 // restore settings 10500 $this->SetFont('', $prevstyle); 10501 $this->SetTextColorArray($prevcolor); 10502 return $ret; 10503 } 10504 10505 /** 10506 * Converts pixels to User's Units. 10507 * @param $px (int) pixels 10508 * @return float value in user's unit 10509 * @public 10510 * @see setImageScale(), getImageScale() 10511 */ 10512 public function pixelsToUnits($px) { 10513 return ($px / ($this->imgscale * $this->k)); 10514 } 10515 10516 /** 10517 * Reverse function for htmlentities. 10518 * Convert entities in UTF-8. 10519 * @param $text_to_convert (string) Text to convert. 10520 * @return string converted text string 10521 * @public 10522 */ 10523 public function unhtmlentities($text_to_convert) { 10524 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); 10525 } 10526 10527 // ENCRYPTION METHODS ---------------------------------- 10528 10529 /** 10530 * Compute encryption key depending on object number where the encrypted data is stored. 10531 * This is used for all strings and streams without crypt filter specifier. 10532 * @param $n (int) object number 10533 * @return int object key 10534 * @protected 10535 * @author Nicola Asuni 10536 * @since 2.0.000 (2008-01-02) 10537 */ 10538 protected function _objectkey($n) { 10539 $objkey = $this->encryptdata['key'].pack('VXxx', $n); 10540 if ($this->encryptdata['mode'] == 2) { // AES-128 10541 // AES padding 10542 $objkey .= "\x73\x41\x6C\x54"; // sAlT 10543 } 10544 $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5)); 10545 $objkey = substr($objkey, 0, 16); 10546 return $objkey; 10547 } 10548 10549 /** 10550 * Encrypt the input string. 10551 * @param $n (int) object number 10552 * @param $s (string) data string to encrypt 10553 * @return encrypted string 10554 * @protected 10555 * @author Nicola Asuni 10556 * @since 5.0.005 (2010-05-11) 10557 */ 10558 protected function _encrypt_data($n, $s) { 10559 if (!$this->encrypted) { 10560 return $s; 10561 } 10562 switch ($this->encryptdata['mode']) { 10563 case 0: // RC4-40 10564 case 1: { // RC4-128 10565 $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c); 10566 break; 10567 } 10568 case 2: { // AES-128 10569 $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s); 10570 break; 10571 } 10572 case 3: { // AES-256 10573 $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s); 10574 break; 10575 } 10576 } 10577 return $s; 10578 } 10579 10580 /** 10581 * Put encryption on PDF document. 10582 * @protected 10583 * @author Nicola Asuni 10584 * @since 2.0.000 (2008-01-02) 10585 */ 10586 protected function _putencryption() { 10587 if (!$this->encrypted) { 10588 return; 10589 } 10590 $this->encryptdata['objid'] = $this->_newobj(); 10591 $out = '<<'; 10592 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) { 10593 $this->encryptdata['Filter'] = 'Standard'; 10594 } 10595 $out .= ' /Filter /'.$this->encryptdata['Filter']; 10596 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) { 10597 $out .= ' /SubFilter /'.$this->encryptdata['SubFilter']; 10598 } 10599 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) { 10600 $this->encryptdata['V'] = 1; 10601 } 10602 // V is a code specifying the algorithm to be used in encrypting and decrypting the document 10603 $out .= ' /V '.$this->encryptdata['V']; 10604 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) { 10605 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256 10606 $out .= ' /Length '.$this->encryptdata['Length']; 10607 } else { 10608 $out .= ' /Length 40'; 10609 } 10610 if ($this->encryptdata['V'] >= 4) { 10611 if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) { 10612 $this->encryptdata['StmF'] = 'Identity'; 10613 } 10614 if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) { 10615 // The name of the crypt filter that shall be used when decrypting all strings in the document. 10616 $this->encryptdata['StrF'] = 'Identity'; 10617 } 10618 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries. 10619 if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) { 10620 $out .= ' /CF <<'; 10621 $out .= ' /'.$this->encryptdata['StmF'].' <<'; 10622 $out .= ' /Type /CryptFilter'; 10623 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) { 10624 // The method used 10625 $out .= ' /CFM /'.$this->encryptdata['CF']['CFM']; 10626 if ($this->encryptdata['pubkey']) { 10627 $out .= ' /Recipients ['; 10628 foreach ($this->encryptdata['Recipients'] as $rec) { 10629 $out .= ' <'.$rec.'>'; 10630 } 10631 $out .= ' ]'; 10632 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { 10633 $out .= ' /EncryptMetadata false'; 10634 } else { 10635 $out .= ' /EncryptMetadata true'; 10636 } 10637 } 10638 } else { 10639 $out .= ' /CFM /None'; 10640 } 10641 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) { 10642 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter. 10643 $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent']; 10644 } else { 10645 $out .= ' /AuthEvent /DocOpen'; 10646 } 10647 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) { 10648 // The bit length of the encryption key. 10649 $out .= ' /Length '.$this->encryptdata['CF']['Length']; 10650 } 10651 $out .= ' >> >>'; 10652 } 10653 // The name of the crypt filter that shall be used by default when decrypting streams. 10654 $out .= ' /StmF /'.$this->encryptdata['StmF']; 10655 // The name of the crypt filter that shall be used when decrypting all strings in the document. 10656 $out .= ' /StrF /'.$this->encryptdata['StrF']; 10657 if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) { 10658 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier. 10659 $out .= ' /EFF /'.$this->encryptdata['']; 10660 } 10661 } 10662 // Additional encryption dictionary entries for the standard security handler 10663 if ($this->encryptdata['pubkey']) { 10664 if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) { 10665 $out .= ' /Recipients ['; 10666 foreach ($this->encryptdata['Recipients'] as $rec) { 10667 $out .= ' <'.$rec.'>'; 10668 } 10669 $out .= ' ]'; 10670 } 10671 } else { 10672 $out .= ' /R'; 10673 if ($this->encryptdata['V'] == 5) { // AES-256 10674 $out .= ' 5'; 10675 $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')'; 10676 $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')'; 10677 $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')'; 10678 } elseif ($this->encryptdata['V'] == 4) { // AES-128 10679 $out .= ' 4'; 10680 } elseif ($this->encryptdata['V'] < 2) { // RC-40 10681 $out .= ' 2'; 10682 } else { // RC-128 10683 $out .= ' 3'; 10684 } 10685 $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')'; 10686 $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')'; 10687 $out .= ' /P '.$this->encryptdata['P']; 10688 if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) { 10689 $out .= ' /EncryptMetadata false'; 10690 } else { 10691 $out .= ' /EncryptMetadata true'; 10692 } 10693 } 10694 $out .= ' >>'; 10695 $out .= "\n".'endobj'; 10696 $this->_out($out); 10697 } 10698 10699 /** 10700 * Compute U value (used for encryption) 10701 * @return string U value 10702 * @protected 10703 * @since 2.0.000 (2008-01-02) 10704 * @author Nicola Asuni 10705 */ 10706 protected function _Uvalue() { 10707 if ($this->encryptdata['mode'] == 0) { // RC4-40 10708 return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c); 10709 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128 10710 $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']); 10711 $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c); 10712 $len = strlen($tmp); 10713 for ($i = 1; $i <= 19; ++$i) { 10714 $ek = ''; 10715 for ($j = 0; $j < $len; ++$j) { 10716 $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i); 10717 } 10718 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c); 10719 } 10720 $enc .= str_repeat("\x00", 16); 10721 return substr($enc, 0, 32); 10722 } elseif ($this->encryptdata['mode'] == 3) { // AES-256 10723 $seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed()); 10724 // User Validation Salt 10725 $this->encryptdata['UVS'] = substr($seed, 0, 8); 10726 // User Key Salt 10727 $this->encryptdata['UKS'] = substr($seed, 8, 16); 10728 return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS']; 10729 } 10730 } 10731 10732 /** 10733 * Compute UE value (used for encryption) 10734 * @return string UE value 10735 * @protected 10736 * @since 5.9.006 (2010-10-19) 10737 * @author Nicola Asuni 10738 */ 10739 protected function _UEvalue() { 10740 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true); 10741 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']); 10742 } 10743 10744 /** 10745 * Compute O value (used for encryption) 10746 * @return string O value 10747 * @protected 10748 * @since 2.0.000 (2008-01-02) 10749 * @author Nicola Asuni 10750 */ 10751 protected function _Ovalue() { 10752 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128 10753 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']); 10754 if ($this->encryptdata['mode'] > 0) { 10755 for ($i = 0; $i < 50; ++$i) { 10756 $tmp = TCPDF_STATIC::_md5_16($tmp); 10757 } 10758 } 10759 $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8)); 10760 $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c); 10761 if ($this->encryptdata['mode'] > 0) { 10762 $len = strlen($owner_key); 10763 for ($i = 1; $i <= 19; ++$i) { 10764 $ek = ''; 10765 for ($j = 0; $j < $len; ++$j) { 10766 $ek .= chr(ord($owner_key[$j]) ^ $i); 10767 } 10768 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c); 10769 } 10770 } 10771 return $enc; 10772 } elseif ($this->encryptdata['mode'] == 3) { // AES-256 10773 $seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed()); 10774 // Owner Validation Salt 10775 $this->encryptdata['OVS'] = substr($seed, 0, 8); 10776 // Owner Key Salt 10777 $this->encryptdata['OKS'] = substr($seed, 8, 16); 10778 return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS']; 10779 } 10780 } 10781 10782 /** 10783 * Compute OE value (used for encryption) 10784 * @return string OE value 10785 * @protected 10786 * @since 5.9.006 (2010-10-19) 10787 * @author Nicola Asuni 10788 */ 10789 protected function _OEvalue() { 10790 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true); 10791 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']); 10792 } 10793 10794 /** 10795 * Convert password for AES-256 encryption mode 10796 * @param $password (string) password 10797 * @return string password 10798 * @protected 10799 * @since 5.9.006 (2010-10-19) 10800 * @author Nicola Asuni 10801 */ 10802 protected function _fixAES256Password($password) { 10803 $psw = ''; // password to be returned 10804 $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont); 10805 foreach ($psw_array as $c) { 10806 $psw .= TCPDF_FONTS::unichr($c, $this->isunicode); 10807 } 10808 return substr($psw, 0, 127); 10809 } 10810 10811 /** 10812 * Compute encryption key 10813 * @protected 10814 * @since 2.0.000 (2008-01-02) 10815 * @author Nicola Asuni 10816 */ 10817 protected function _generateencryptionkey() { 10818 $keybytelen = ($this->encryptdata['Length'] / 8); 10819 if (!$this->encryptdata['pubkey']) { // standard mode 10820 if ($this->encryptdata['mode'] == 3) { // AES-256 10821 // generate 256 bit random key 10822 $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen); 10823 // truncate passwords 10824 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']); 10825 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']); 10826 // Compute U value 10827 $this->encryptdata['U'] = $this->_Uvalue(); 10828 // Compute UE value 10829 $this->encryptdata['UE'] = $this->_UEvalue(); 10830 // Compute O value 10831 $this->encryptdata['O'] = $this->_Ovalue(); 10832 // Compute OE value 10833 $this->encryptdata['OE'] = $this->_OEvalue(); 10834 // Compute P value 10835 $this->encryptdata['P'] = $this->encryptdata['protection']; 10836 // Computing the encryption dictionary's Perms (permissions) value 10837 $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3 10838 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7 10839 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8 10840 $perms .= 'F'; 10841 } else { 10842 $perms .= 'T'; 10843 } 10844 $perms .= 'adb'; // bytes 9-11 10845 $perms .= 'nick'; // bytes 12-15 10846 $this->encryptdata['perms'] = TCPDF_STATIC::_AESnopad($this->encryptdata['key'], $perms); 10847 } else { // RC4-40, RC4-128, AES-128 10848 // Pad passwords 10849 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32); 10850 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32); 10851 // Compute O value 10852 $this->encryptdata['O'] = $this->_Ovalue(); 10853 // get default permissions (reverse byte order) 10854 $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); 10855 // Compute encryption key 10856 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']); 10857 if ($this->encryptdata['mode'] > 0) { 10858 for ($i = 0; $i < 50; ++$i) { 10859 $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen)); 10860 } 10861 } 10862 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen); 10863 // Compute U value 10864 $this->encryptdata['U'] = $this->_Uvalue(); 10865 // Compute P value 10866 $this->encryptdata['P'] = $this->encryptdata['protection']; 10867 } 10868 } else { // Public-Key mode 10869 // random 20-byte seed 10870 $seed = sha1(TCPDF_STATIC::getRandomSeed(), true); 10871 $recipient_bytes = ''; 10872 foreach ($this->encryptdata['pubkeys'] as $pubkey) { 10873 // for each public certificate 10874 if (isset($pubkey['p'])) { 10875 $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']); 10876 } else { 10877 $pkprotection = $this->encryptdata['protection']; 10878 } 10879 // get default permissions (reverse byte order) 10880 $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection); 10881 // envelope data 10882 $envelope = $seed.$pkpermissions; 10883 // write the envelope data to a temporary file 10884 $tempkeyfile = TCPDF_STATIC::getObjFilename('key', $this->file_id); 10885 $f = TCPDF_STATIC::fopenLocal($tempkeyfile, 'wb'); 10886 if (!$f) { 10887 $this->Error('Unable to create temporary key file: '.$tempkeyfile); 10888 } 10889 $envelope_length = strlen($envelope); 10890 fwrite($f, $envelope, $envelope_length); 10891 fclose($f); 10892 $tempencfile = TCPDF_STATIC::getObjFilename('enc', $this->file_id); 10893 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) { 10894 $this->Error('Unable to encrypt the file: '.$tempkeyfile); 10895 } 10896 // read encryption signature 10897 $signature = file_get_contents($tempencfile, false, null, $envelope_length); 10898 // extract signature 10899 $signature = substr($signature, strpos($signature, 'Content-Disposition')); 10900 $tmparr = explode("\n\n", $signature); 10901 $signature = trim($tmparr[1]); 10902 unset($tmparr); 10903 // decode signature 10904 $signature = base64_decode($signature); 10905 // convert signature to hex 10906 $hexsignature = current(unpack('H*', $signature)); 10907 // store signature on recipients array 10908 $this->encryptdata['Recipients'][] = $hexsignature; 10909 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array 10910 $recipient_bytes .= $signature; 10911 } 10912 // calculate encryption key 10913 if ($this->encryptdata['mode'] == 3) { // AES-256 10914 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen); 10915 } else { // RC4-40, RC4-128, AES-128 10916 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen); 10917 } 10918 } 10919 } 10920 10921 /** 10922 * Set document protection 10923 * Remark: the protection against modification is for people who have the full Acrobat product. 10924 * 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. 10925 * 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. 10926 * @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> 10927 * @param $user_pass (String) user password. Empty by default. 10928 * @param $owner_pass (String) owner password. If not specified, a random value is used. 10929 * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit. 10930 * @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'))) 10931 * @public 10932 * @since 2.0.000 (2008-01-02) 10933 * @author Nicola Asuni 10934 */ 10935 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) { 10936 if ($this->pdfa_mode) { 10937 // encryption is not allowed in PDF/A mode 10938 return; 10939 } 10940 $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode); 10941 if (($pubkeys !== null) AND (is_array($pubkeys))) { 10942 // public-key mode 10943 $this->encryptdata['pubkeys'] = $pubkeys; 10944 if ($mode == 0) { 10945 // public-Key Security requires at least 128 bit 10946 $mode = 1; 10947 } 10948 if (!function_exists('openssl_pkcs7_encrypt')) { 10949 $this->Error('Public-Key Security requires openssl library.'); 10950 } 10951 // Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec) 10952 $this->encryptdata['pubkey'] = true; 10953 $this->encryptdata['Filter'] = 'Adobe.PubSec'; 10954 $this->encryptdata['StmF'] = 'DefaultCryptFilter'; 10955 $this->encryptdata['StrF'] = 'DefaultCryptFilter'; 10956 } else { 10957 // standard mode (password mode) 10958 $this->encryptdata['pubkey'] = false; 10959 $this->encryptdata['Filter'] = 'Standard'; 10960 $this->encryptdata['StmF'] = 'StdCF'; 10961 $this->encryptdata['StrF'] = 'StdCF'; 10962 } 10963 if ($mode > 1) { // AES 10964 if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) { 10965 $this->Error('AES encryption requires openssl or mcrypt extension (http://www.php.net/manual/en/mcrypt.requirements.php).'); 10966 } 10967 if (extension_loaded('openssl') && !in_array('aes-256-cbc', openssl_get_cipher_methods())) { 10968 $this->Error('AES encryption requires openssl/aes-256-cbc cypher.'); 10969 } 10970 if (extension_loaded('mcrypt') && mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) { 10971 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.'); 10972 } 10973 if (($mode == 3) AND !function_exists('hash')) { 10974 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2. 10975 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).'); 10976 } 10977 } 10978 if ($owner_pass === null) { 10979 $owner_pass = md5(TCPDF_STATIC::getRandomSeed()); 10980 } 10981 $this->encryptdata['user_password'] = $user_pass; 10982 $this->encryptdata['owner_password'] = $owner_pass; 10983 $this->encryptdata['mode'] = $mode; 10984 switch ($mode) { 10985 case 0: { // RC4 40 bit 10986 $this->encryptdata['V'] = 1; 10987 $this->encryptdata['Length'] = 40; 10988 $this->encryptdata['CF']['CFM'] = 'V2'; 10989 break; 10990 } 10991 case 1: { // RC4 128 bit 10992 $this->encryptdata['V'] = 2; 10993 $this->encryptdata['Length'] = 128; 10994 $this->encryptdata['CF']['CFM'] = 'V2'; 10995 if ($this->encryptdata['pubkey']) { 10996 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4'; 10997 $this->encryptdata['Recipients'] = array(); 10998 } 10999 break; 11000 } 11001 case 2: { // AES 128 bit 11002 $this->encryptdata['V'] = 4; 11003 $this->encryptdata['Length'] = 128; 11004 $this->encryptdata['CF']['CFM'] = 'AESV2'; 11005 $this->encryptdata['CF']['Length'] = 128; 11006 if ($this->encryptdata['pubkey']) { 11007 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; 11008 $this->encryptdata['Recipients'] = array(); 11009 } 11010 break; 11011 } 11012 case 3: { // AES 256 bit 11013 $this->encryptdata['V'] = 5; 11014 $this->encryptdata['Length'] = 256; 11015 $this->encryptdata['CF']['CFM'] = 'AESV3'; 11016 $this->encryptdata['CF']['Length'] = 256; 11017 if ($this->encryptdata['pubkey']) { 11018 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; 11019 $this->encryptdata['Recipients'] = array(); 11020 } 11021 break; 11022 } 11023 } 11024 $this->encrypted = true; 11025 $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id); 11026 $this->_generateencryptionkey(); 11027 } 11028 11029 // END OF ENCRYPTION FUNCTIONS ------------------------- 11030 11031 // START TRANSFORMATIONS SECTION ----------------------- 11032 11033 /** 11034 * Starts a 2D tranformation saving current graphic state. 11035 * This function must be called before scaling, mirroring, translation, rotation and skewing. 11036 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 11037 * @public 11038 * @since 2.1.000 (2008-01-07) 11039 * @see StartTransform(), StopTransform() 11040 */ 11041 public function StartTransform() { 11042 if ($this->state != 2) { 11043 return; 11044 } 11045 $this->_outSaveGraphicsState(); 11046 if ($this->inxobj) { 11047 // we are inside an XObject template 11048 $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']); 11049 } else { 11050 $this->transfmrk[$this->page][] = $this->pagelen[$this->page]; 11051 } 11052 ++$this->transfmatrix_key; 11053 $this->transfmatrix[$this->transfmatrix_key] = array(); 11054 } 11055 11056 /** 11057 * Stops a 2D tranformation restoring previous graphic state. 11058 * This function must be called after scaling, mirroring, translation, rotation and skewing. 11059 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 11060 * @public 11061 * @since 2.1.000 (2008-01-07) 11062 * @see StartTransform(), StopTransform() 11063 */ 11064 public function StopTransform() { 11065 if ($this->state != 2) { 11066 return; 11067 } 11068 $this->_outRestoreGraphicsState(); 11069 if (isset($this->transfmatrix[$this->transfmatrix_key])) { 11070 array_pop($this->transfmatrix[$this->transfmatrix_key]); 11071 --$this->transfmatrix_key; 11072 } 11073 if ($this->inxobj) { 11074 // we are inside an XObject template 11075 array_pop($this->xobjects[$this->xobjid]['transfmrk']); 11076 } else { 11077 array_pop($this->transfmrk[$this->page]); 11078 } 11079 } 11080 /** 11081 * Horizontal Scaling. 11082 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed. 11083 * @param $x (int) abscissa of the scaling center. Default is current x position 11084 * @param $y (int) ordinate of the scaling center. Default is current y position 11085 * @public 11086 * @since 2.1.000 (2008-01-07) 11087 * @see StartTransform(), StopTransform() 11088 */ 11089 public function ScaleX($s_x, $x='', $y='') { 11090 $this->Scale($s_x, 100, $x, $y); 11091 } 11092 11093 /** 11094 * Vertical Scaling. 11095 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed. 11096 * @param $x (int) abscissa of the scaling center. Default is current x position 11097 * @param $y (int) ordinate of the scaling center. Default is current y position 11098 * @public 11099 * @since 2.1.000 (2008-01-07) 11100 * @see StartTransform(), StopTransform() 11101 */ 11102 public function ScaleY($s_y, $x='', $y='') { 11103 $this->Scale(100, $s_y, $x, $y); 11104 } 11105 11106 /** 11107 * Vertical and horizontal proportional Scaling. 11108 * @param $s (float) scaling factor for width and height as percent. 0 is not allowed. 11109 * @param $x (int) abscissa of the scaling center. Default is current x position 11110 * @param $y (int) ordinate of the scaling center. Default is current y position 11111 * @public 11112 * @since 2.1.000 (2008-01-07) 11113 * @see StartTransform(), StopTransform() 11114 */ 11115 public function ScaleXY($s, $x='', $y='') { 11116 $this->Scale($s, $s, $x, $y); 11117 } 11118 11119 /** 11120 * Vertical and horizontal non-proportional Scaling. 11121 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed. 11122 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed. 11123 * @param $x (int) abscissa of the scaling center. Default is current x position 11124 * @param $y (int) ordinate of the scaling center. Default is current y position 11125 * @public 11126 * @since 2.1.000 (2008-01-07) 11127 * @see StartTransform(), StopTransform() 11128 */ 11129 public function Scale($s_x, $s_y, $x='', $y='') { 11130 if ($x === '') { 11131 $x = $this->x; 11132 } 11133 if ($y === '') { 11134 $y = $this->y; 11135 } 11136 if (($s_x == 0) OR ($s_y == 0)) { 11137 $this->Error('Please do not use values equal to zero for scaling'); 11138 } 11139 $y = ($this->h - $y) * $this->k; 11140 $x *= $this->k; 11141 //calculate elements of transformation matrix 11142 $s_x /= 100; 11143 $s_y /= 100; 11144 $tm = array(); 11145 $tm[0] = $s_x; 11146 $tm[1] = 0; 11147 $tm[2] = 0; 11148 $tm[3] = $s_y; 11149 $tm[4] = $x * (1 - $s_x); 11150 $tm[5] = $y * (1 - $s_y); 11151 //scale the coordinate system 11152 $this->Transform($tm); 11153 } 11154 11155 /** 11156 * Horizontal Mirroring. 11157 * @param $x (int) abscissa of the point. Default is current x position 11158 * @public 11159 * @since 2.1.000 (2008-01-07) 11160 * @see StartTransform(), StopTransform() 11161 */ 11162 public function MirrorH($x='') { 11163 $this->Scale(-100, 100, $x); 11164 } 11165 11166 /** 11167 * Verical Mirroring. 11168 * @param $y (int) ordinate of the point. Default is current y position 11169 * @public 11170 * @since 2.1.000 (2008-01-07) 11171 * @see StartTransform(), StopTransform() 11172 */ 11173 public function MirrorV($y='') { 11174 $this->Scale(100, -100, '', $y); 11175 } 11176 11177 /** 11178 * Point reflection mirroring. 11179 * @param $x (int) abscissa of the point. Default is current x position 11180 * @param $y (int) ordinate of the point. Default is current y position 11181 * @public 11182 * @since 2.1.000 (2008-01-07) 11183 * @see StartTransform(), StopTransform() 11184 */ 11185 public function MirrorP($x='',$y='') { 11186 $this->Scale(-100, -100, $x, $y); 11187 } 11188 11189 /** 11190 * Reflection against a straight line through point (x, y) with the gradient angle (angle). 11191 * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line). 11192 * @param $x (int) abscissa of the point. Default is current x position 11193 * @param $y (int) ordinate of the point. Default is current y position 11194 * @public 11195 * @since 2.1.000 (2008-01-07) 11196 * @see StartTransform(), StopTransform() 11197 */ 11198 public function MirrorL($angle=0, $x='',$y='') { 11199 $this->Scale(-100, 100, $x, $y); 11200 $this->Rotate(-2*($angle-90), $x, $y); 11201 } 11202 11203 /** 11204 * Translate graphic object horizontally. 11205 * @param $t_x (int) movement to the right (or left for RTL) 11206 * @public 11207 * @since 2.1.000 (2008-01-07) 11208 * @see StartTransform(), StopTransform() 11209 */ 11210 public function TranslateX($t_x) { 11211 $this->Translate($t_x, 0); 11212 } 11213 11214 /** 11215 * Translate graphic object vertically. 11216 * @param $t_y (int) movement to the bottom 11217 * @public 11218 * @since 2.1.000 (2008-01-07) 11219 * @see StartTransform(), StopTransform() 11220 */ 11221 public function TranslateY($t_y) { 11222 $this->Translate(0, $t_y); 11223 } 11224 11225 /** 11226 * Translate graphic object horizontally and vertically. 11227 * @param $t_x (int) movement to the right 11228 * @param $t_y (int) movement to the bottom 11229 * @public 11230 * @since 2.1.000 (2008-01-07) 11231 * @see StartTransform(), StopTransform() 11232 */ 11233 public function Translate($t_x, $t_y) { 11234 //calculate elements of transformation matrix 11235 $tm = array(); 11236 $tm[0] = 1; 11237 $tm[1] = 0; 11238 $tm[2] = 0; 11239 $tm[3] = 1; 11240 $tm[4] = $t_x * $this->k; 11241 $tm[5] = -$t_y * $this->k; 11242 //translate the coordinate system 11243 $this->Transform($tm); 11244 } 11245 11246 /** 11247 * Rotate object. 11248 * @param $angle (float) angle in degrees for counter-clockwise rotation 11249 * @param $x (int) abscissa of the rotation center. Default is current x position 11250 * @param $y (int) ordinate of the rotation center. Default is current y position 11251 * @public 11252 * @since 2.1.000 (2008-01-07) 11253 * @see StartTransform(), StopTransform() 11254 */ 11255 public function Rotate($angle, $x='', $y='') { 11256 if ($x === '') { 11257 $x = $this->x; 11258 } 11259 if ($y === '') { 11260 $y = $this->y; 11261 } 11262 $y = ($this->h - $y) * $this->k; 11263 $x *= $this->k; 11264 //calculate elements of transformation matrix 11265 $tm = array(); 11266 $tm[0] = cos(deg2rad($angle)); 11267 $tm[1] = sin(deg2rad($angle)); 11268 $tm[2] = -$tm[1]; 11269 $tm[3] = $tm[0]; 11270 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x); 11271 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x); 11272 //rotate the coordinate system around ($x,$y) 11273 $this->Transform($tm); 11274 } 11275 11276 /** 11277 * Skew horizontally. 11278 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right) 11279 * @param $x (int) abscissa of the skewing center. default is current x position 11280 * @param $y (int) ordinate of the skewing center. default is current y position 11281 * @public 11282 * @since 2.1.000 (2008-01-07) 11283 * @see StartTransform(), StopTransform() 11284 */ 11285 public function SkewX($angle_x, $x='', $y='') { 11286 $this->Skew($angle_x, 0, $x, $y); 11287 } 11288 11289 /** 11290 * Skew vertically. 11291 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 11292 * @param $x (int) abscissa of the skewing center. default is current x position 11293 * @param $y (int) ordinate of the skewing center. default is current y position 11294 * @public 11295 * @since 2.1.000 (2008-01-07) 11296 * @see StartTransform(), StopTransform() 11297 */ 11298 public function SkewY($angle_y, $x='', $y='') { 11299 $this->Skew(0, $angle_y, $x, $y); 11300 } 11301 11302 /** 11303 * Skew. 11304 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right) 11305 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 11306 * @param $x (int) abscissa of the skewing center. default is current x position 11307 * @param $y (int) ordinate of the skewing center. default is current y position 11308 * @public 11309 * @since 2.1.000 (2008-01-07) 11310 * @see StartTransform(), StopTransform() 11311 */ 11312 public function Skew($angle_x, $angle_y, $x='', $y='') { 11313 if ($x === '') { 11314 $x = $this->x; 11315 } 11316 if ($y === '') { 11317 $y = $this->y; 11318 } 11319 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) { 11320 $this->Error('Please use values between -90 and +90 degrees for Skewing.'); 11321 } 11322 $x *= $this->k; 11323 $y = ($this->h - $y) * $this->k; 11324 //calculate elements of transformation matrix 11325 $tm = array(); 11326 $tm[0] = 1; 11327 $tm[1] = tan(deg2rad($angle_y)); 11328 $tm[2] = tan(deg2rad($angle_x)); 11329 $tm[3] = 1; 11330 $tm[4] = -$tm[2] * $y; 11331 $tm[5] = -$tm[1] * $x; 11332 //skew the coordinate system 11333 $this->Transform($tm); 11334 } 11335 11336 /** 11337 * Apply graphic transformations. 11338 * @param $tm (array) transformation matrix 11339 * @protected 11340 * @since 2.1.000 (2008-01-07) 11341 * @see StartTransform(), StopTransform() 11342 */ 11343 protected function Transform($tm) { 11344 if ($this->state != 2) { 11345 return; 11346 } 11347 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); 11348 // add tranformation matrix 11349 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]); 11350 // update transformation mark 11351 if ($this->inxobj) { 11352 // we are inside an XObject template 11353 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 11354 $key = key($this->xobjects[$this->xobjid]['transfmrk']); 11355 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']); 11356 } 11357 } elseif (end($this->transfmrk[$this->page]) !== false) { 11358 $key = key($this->transfmrk[$this->page]); 11359 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page]; 11360 } 11361 } 11362 11363 // END TRANSFORMATIONS SECTION ------------------------- 11364 11365 // START GRAPHIC FUNCTIONS SECTION --------------------- 11366 // The following section is based on the code provided by David Hernandez Sanz 11367 11368 /** 11369 * 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. 11370 * @param $width (float) The width. 11371 * @public 11372 * @since 1.0 11373 * @see Line(), Rect(), Cell(), MultiCell() 11374 */ 11375 public function SetLineWidth($width) { 11376 //Set line width 11377 $this->LineWidth = $width; 11378 $this->linestyleWidth = sprintf('%F w', ($width * $this->k)); 11379 if ($this->state == 2) { 11380 $this->_out($this->linestyleWidth); 11381 } 11382 } 11383 11384 /** 11385 * Returns the current the line width. 11386 * @return int Line width 11387 * @public 11388 * @since 2.1.000 (2008-01-07) 11389 * @see Line(), SetLineWidth() 11390 */ 11391 public function GetLineWidth() { 11392 return $this->LineWidth; 11393 } 11394 11395 /** 11396 * Set line style. 11397 * @param $style (array) Line style. Array with keys among the following: 11398 * <ul> 11399 * <li>width (float): Width of the line in user units.</li> 11400 * <li>cap (string): Type of cap to put on the line. Possible values are: 11401 * butt, round, square. The difference between "square" and "butt" is that 11402 * "square" projects a flat end past the end of the line.</li> 11403 * <li>join (string): Type of join. Possible values are: miter, round, 11404 * bevel.</li> 11405 * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with 11406 * series of length values, which are the lengths of the on and off dashes. 11407 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, 11408 * 1 off, 2 on, 1 off, ...</li> 11409 * <li>phase (integer): Modifier on the dash pattern which is used to shift 11410 * the point at which the pattern starts.</li> 11411 * <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> 11412 * </ul> 11413 * @param $ret (boolean) if true do not send the command. 11414 * @return string the PDF command 11415 * @public 11416 * @since 2.1.000 (2008-01-08) 11417 */ 11418 public function SetLineStyle($style, $ret=false) { 11419 $s = ''; // string to be returned 11420 if (!is_array($style)) { 11421 return; 11422 } 11423 if (isset($style['width'])) { 11424 $this->LineWidth = $style['width']; 11425 $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k)); 11426 $s .= $this->linestyleWidth.' '; 11427 } 11428 if (isset($style['cap'])) { 11429 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2); 11430 if (isset($ca[$style['cap']])) { 11431 $this->linestyleCap = $ca[$style['cap']].' J'; 11432 $s .= $this->linestyleCap.' '; 11433 } 11434 } 11435 if (isset($style['join'])) { 11436 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); 11437 if (isset($ja[$style['join']])) { 11438 $this->linestyleJoin = $ja[$style['join']].' j'; 11439 $s .= $this->linestyleJoin.' '; 11440 } 11441 } 11442 if (isset($style['dash'])) { 11443 $dash_string = ''; 11444 if ($style['dash']) { 11445 if (preg_match('/^.+,/', $style['dash']) > 0) { 11446 $tab = explode(',', $style['dash']); 11447 } else { 11448 $tab = array($style['dash']); 11449 } 11450 $dash_string = ''; 11451 foreach ($tab as $i => $v) { 11452 if ($i) { 11453 $dash_string .= ' '; 11454 } 11455 $dash_string .= sprintf('%F', $v); 11456 } 11457 } 11458 if (!isset($style['phase']) OR !$style['dash']) { 11459 $style['phase'] = 0; 11460 } 11461 $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']); 11462 $s .= $this->linestyleDash.' '; 11463 } 11464 if (isset($style['color'])) { 11465 $s .= $this->SetDrawColorArray($style['color'], true).' '; 11466 } 11467 if (!$ret AND ($this->state == 2)) { 11468 $this->_out($s); 11469 } 11470 return $s; 11471 } 11472 11473 /** 11474 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment. 11475 * @param $x (float) Abscissa of point. 11476 * @param $y (float) Ordinate of point. 11477 * @protected 11478 * @since 2.1.000 (2008-01-08) 11479 */ 11480 protected function _outPoint($x, $y) { 11481 if ($this->state == 2) { 11482 $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k))); 11483 } 11484 } 11485 11486 /** 11487 * Append a straight line segment from the current point to the point (x, y). 11488 * The new current point shall be (x, y). 11489 * @param $x (float) Abscissa of end point. 11490 * @param $y (float) Ordinate of end point. 11491 * @protected 11492 * @since 2.1.000 (2008-01-08) 11493 */ 11494 protected function _outLine($x, $y) { 11495 if ($this->state == 2) { 11496 $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k))); 11497 } 11498 } 11499 11500 /** 11501 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space. 11502 * @param $x (float) Abscissa of upper-left corner. 11503 * @param $y (float) Ordinate of upper-left corner. 11504 * @param $w (float) Width. 11505 * @param $h (float) Height. 11506 * @param $op (string) options 11507 * @protected 11508 * @since 2.1.000 (2008-01-08) 11509 */ 11510 protected function _outRect($x, $y, $w, $h, $op) { 11511 if ($this->state == 2) { 11512 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op)); 11513 } 11514 } 11515 11516 /** 11517 * 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. 11518 * The new current point shall be (x3, y3). 11519 * @param $x1 (float) Abscissa of control point 1. 11520 * @param $y1 (float) Ordinate of control point 1. 11521 * @param $x2 (float) Abscissa of control point 2. 11522 * @param $y2 (float) Ordinate of control point 2. 11523 * @param $x3 (float) Abscissa of end point. 11524 * @param $y3 (float) Ordinate of end point. 11525 * @protected 11526 * @since 2.1.000 (2008-01-08) 11527 */ 11528 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { 11529 if ($this->state == 2) { 11530 $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))); 11531 } 11532 } 11533 11534 /** 11535 * 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. 11536 * The new current point shall be (x3, y3). 11537 * @param $x2 (float) Abscissa of control point 2. 11538 * @param $y2 (float) Ordinate of control point 2. 11539 * @param $x3 (float) Abscissa of end point. 11540 * @param $y3 (float) Ordinate of end point. 11541 * @protected 11542 * @since 4.9.019 (2010-04-26) 11543 */ 11544 protected function _outCurveV($x2, $y2, $x3, $y3) { 11545 if ($this->state == 2) { 11546 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k))); 11547 } 11548 } 11549 11550 /** 11551 * 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. 11552 * The new current point shall be (x3, y3). 11553 * @param $x1 (float) Abscissa of control point 1. 11554 * @param $y1 (float) Ordinate of control point 1. 11555 * @param $x3 (float) Abscissa of end point. 11556 * @param $y3 (float) Ordinate of end point. 11557 * @protected 11558 * @since 2.1.000 (2008-01-08) 11559 */ 11560 protected function _outCurveY($x1, $y1, $x3, $y3) { 11561 if ($this->state == 2) { 11562 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k))); 11563 } 11564 } 11565 11566 /** 11567 * Draws a line between two points. 11568 * @param $x1 (float) Abscissa of first point. 11569 * @param $y1 (float) Ordinate of first point. 11570 * @param $x2 (float) Abscissa of second point. 11571 * @param $y2 (float) Ordinate of second point. 11572 * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array). 11573 * @public 11574 * @since 1.0 11575 * @see SetLineWidth(), SetDrawColor(), SetLineStyle() 11576 */ 11577 public function Line($x1, $y1, $x2, $y2, $style=array()) { 11578 if ($this->state != 2) { 11579 return; 11580 } 11581 if (is_array($style)) { 11582 $this->SetLineStyle($style); 11583 } 11584 $this->_outPoint($x1, $y1); 11585 $this->_outLine($x2, $y2); 11586 $this->_out('S'); 11587 } 11588 11589 /** 11590 * Draws a rectangle. 11591 * @param $x (float) Abscissa of upper-left corner. 11592 * @param $y (float) Ordinate of upper-left corner. 11593 * @param $w (float) Width. 11594 * @param $h (float) Height. 11595 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11596 * @param $border_style (array) Border style of rectangle. Array with keys among the following: 11597 * <ul> 11598 * <li>all: Line style of all borders. Array like for SetLineStyle().</li> 11599 * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li> 11600 * </ul> 11601 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array). 11602 * @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). 11603 * @public 11604 * @since 1.0 11605 * @see SetLineStyle() 11606 */ 11607 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) { 11608 if ($this->state != 2) { 11609 return; 11610 } 11611 if (empty($style)) { 11612 $style = 'S'; 11613 } 11614 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) { 11615 // set background color 11616 $this->SetFillColorArray($fill_color); 11617 } 11618 if (!empty($border_style)) { 11619 if (isset($border_style['all']) AND !empty($border_style['all'])) { 11620 //set global style for border 11621 $this->SetLineStyle($border_style['all']); 11622 $border_style = array(); 11623 } else { 11624 // remove stroke operator from style 11625 $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*' ); 11626 if (isset($opnostroke[$style])) { 11627 $style = $opnostroke[$style]; 11628 } 11629 } 11630 } 11631 if (!empty($style)) { 11632 $op = TCPDF_STATIC::getPathPaintOperator($style); 11633 $this->_outRect($x, $y, $w, $h, $op); 11634 } 11635 if (!empty($border_style)) { 11636 $border_style2 = array(); 11637 foreach ($border_style as $line => $value) { 11638 $length = strlen($line); 11639 for ($i = 0; $i < $length; ++$i) { 11640 $border_style2[$line[$i]] = $value; 11641 } 11642 } 11643 $border_style = $border_style2; 11644 if (isset($border_style['L']) AND $border_style['L']) { 11645 $this->Line($x, $y, $x, $y + $h, $border_style['L']); 11646 } 11647 if (isset($border_style['T']) AND $border_style['T']) { 11648 $this->Line($x, $y, $x + $w, $y, $border_style['T']); 11649 } 11650 if (isset($border_style['R']) AND $border_style['R']) { 11651 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']); 11652 } 11653 if (isset($border_style['B']) AND $border_style['B']) { 11654 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']); 11655 } 11656 } 11657 } 11658 11659 /** 11660 * Draws a Bezier curve. 11661 * The Bezier curve is a tangent to the line between the control points at 11662 * either end of the curve. 11663 * @param $x0 (float) Abscissa of start point. 11664 * @param $y0 (float) Ordinate of start point. 11665 * @param $x1 (float) Abscissa of control point 1. 11666 * @param $y1 (float) Ordinate of control point 1. 11667 * @param $x2 (float) Abscissa of control point 2. 11668 * @param $y2 (float) Ordinate of control point 2. 11669 * @param $x3 (float) Abscissa of end point. 11670 * @param $y3 (float) Ordinate of end point. 11671 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11672 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array). 11673 * @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). 11674 * @public 11675 * @see SetLineStyle() 11676 * @since 2.1.000 (2008-01-08) 11677 */ 11678 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) { 11679 if ($this->state != 2) { 11680 return; 11681 } 11682 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11683 $this->SetFillColorArray($fill_color); 11684 } 11685 $op = TCPDF_STATIC::getPathPaintOperator($style); 11686 if ($line_style) { 11687 $this->SetLineStyle($line_style); 11688 } 11689 $this->_outPoint($x0, $y0); 11690 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 11691 $this->_out($op); 11692 } 11693 11694 /** 11695 * Draws a poly-Bezier curve. 11696 * Each Bezier curve segment is a tangent to the line between the control points at 11697 * either end of the curve. 11698 * @param $x0 (float) Abscissa of start point. 11699 * @param $y0 (float) Ordinate of start point. 11700 * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3). 11701 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11702 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array). 11703 * @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). 11704 * @public 11705 * @see SetLineStyle() 11706 * @since 3.0008 (2008-05-12) 11707 */ 11708 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) { 11709 if ($this->state != 2) { 11710 return; 11711 } 11712 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11713 $this->SetFillColorArray($fill_color); 11714 } 11715 $op = TCPDF_STATIC::getPathPaintOperator($style); 11716 if ($op == 'f') { 11717 $line_style = array(); 11718 } 11719 if ($line_style) { 11720 $this->SetLineStyle($line_style); 11721 } 11722 $this->_outPoint($x0, $y0); 11723 foreach ($segments as $segment) { 11724 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment; 11725 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 11726 } 11727 $this->_out($op); 11728 } 11729 11730 /** 11731 * Draws an ellipse. 11732 * An ellipse is formed from n Bezier curves. 11733 * @param $x0 (float) Abscissa of center point. 11734 * @param $y0 (float) Ordinate of center point. 11735 * @param $rx (float) Horizontal radius. 11736 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0. 11737 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0. 11738 * @param $astart: (float) Angle start of draw line. Default value: 0. 11739 * @param $afinish: (float) Angle finish of draw line. Default value: 360. 11740 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11741 * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array). 11742 * @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). 11743 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse. 11744 * @author Nicola Asuni 11745 * @public 11746 * @since 2.1.000 (2008-01-08) 11747 */ 11748 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 11749 if ($this->state != 2) { 11750 return; 11751 } 11752 if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) { 11753 $ry = $rx; 11754 } 11755 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11756 $this->SetFillColorArray($fill_color); 11757 } 11758 $op = TCPDF_STATIC::getPathPaintOperator($style); 11759 if ($op == 'f') { 11760 $line_style = array(); 11761 } 11762 if ($line_style) { 11763 $this->SetLineStyle($line_style); 11764 } 11765 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false); 11766 $this->_out($op); 11767 } 11768 11769 /** 11770 * Append an elliptical arc to the current path. 11771 * An ellipse is formed from n Bezier curves. 11772 * @param $xc (float) Abscissa of center point. 11773 * @param $yc (float) Ordinate of center point. 11774 * @param $rx (float) Horizontal radius. 11775 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0. 11776 * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0. 11777 * @param $angs: (float) Angle start of draw line. Default value: 0. 11778 * @param $angf: (float) Angle finish of draw line. Default value: 360. 11779 * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors). 11780 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse. 11781 * @param $startpoint (boolean) if true output a starting point. 11782 * @param $ccw (boolean) if true draws in counter-clockwise. 11783 * @param $svg (boolean) if true the angles are in svg mode (already calculated). 11784 * @return array bounding box coordinates (x min, y min, x max, y max) 11785 * @author Nicola Asuni 11786 * @protected 11787 * @since 4.9.019 (2010-04-26) 11788 */ 11789 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) { 11790 if (($rx <= 0) OR ($ry < 0)) { 11791 return; 11792 } 11793 $k = $this->k; 11794 if ($nc < 2) { 11795 $nc = 2; 11796 } 11797 $xmin = 2147483647; 11798 $ymin = 2147483647; 11799 $xmax = 0; 11800 $ymax = 0; 11801 if ($pie) { 11802 // center of the arc 11803 $this->_outPoint($xc, $yc); 11804 } 11805 $xang = deg2rad((float) $xang); 11806 $angs = deg2rad((float) $angs); 11807 $angf = deg2rad((float) $angf); 11808 if ($svg) { 11809 $as = $angs; 11810 $af = $angf; 11811 } else { 11812 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx)); 11813 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx)); 11814 } 11815 if ($as < 0) { 11816 $as += (2 * M_PI); 11817 } 11818 if ($af < 0) { 11819 $af += (2 * M_PI); 11820 } 11821 if ($ccw AND ($as > $af)) { 11822 // reverse rotation 11823 $as -= (2 * M_PI); 11824 } elseif (!$ccw AND ($as < $af)) { 11825 // reverse rotation 11826 $af -= (2 * M_PI); 11827 } 11828 $total_angle = ($af - $as); 11829 if ($nc < 2) { 11830 $nc = 2; 11831 } 11832 // total arcs to draw 11833 $nc *= (2 * abs($total_angle) / M_PI); 11834 $nc = round($nc) + 1; 11835 // angle of each arc 11836 $arcang = ($total_angle / $nc); 11837 // center point in PDF coordinates 11838 $x0 = $xc; 11839 $y0 = ($this->h - $yc); 11840 // starting angle 11841 $ang = $as; 11842 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3); 11843 $cos_xang = cos($xang); 11844 $sin_xang = sin($xang); 11845 $cos_ang = cos($ang); 11846 $sin_ang = sin($ang); 11847 // first arc point 11848 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 11849 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 11850 // first Bezier control point 11851 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 11852 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 11853 if ($pie) { 11854 // line from center to arc starting point 11855 $this->_outLine($px1, $this->h - $py1); 11856 } elseif ($startpoint) { 11857 // arc starting point 11858 $this->_outPoint($px1, $this->h - $py1); 11859 } 11860 // draw arcs 11861 for ($i = 1; $i <= $nc; ++$i) { 11862 // starting angle 11863 $ang = $as + ($i * $arcang); 11864 if ($i == $nc) { 11865 $ang = $af; 11866 } 11867 $cos_ang = cos($ang); 11868 $sin_ang = sin($ang); 11869 // second arc point 11870 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 11871 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 11872 // second Bezier control point 11873 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 11874 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 11875 // draw arc 11876 $cx1 = ($px1 + $qx1); 11877 $cy1 = ($this->h - ($py1 + $qy1)); 11878 $cx2 = ($px2 - $qx2); 11879 $cy2 = ($this->h - ($py2 - $qy2)); 11880 $cx3 = $px2; 11881 $cy3 = ($this->h - $py2); 11882 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3); 11883 // get bounding box coordinates 11884 $xmin = min($xmin, $cx1, $cx2, $cx3); 11885 $ymin = min($ymin, $cy1, $cy2, $cy3); 11886 $xmax = max($xmax, $cx1, $cx2, $cx3); 11887 $ymax = max($ymax, $cy1, $cy2, $cy3); 11888 // move to next point 11889 $px1 = $px2; 11890 $py1 = $py2; 11891 $qx1 = $qx2; 11892 $qy1 = $qy2; 11893 } 11894 if ($pie) { 11895 $this->_outLine($xc, $yc); 11896 // get bounding box coordinates 11897 $xmin = min($xmin, $xc); 11898 $ymin = min($ymin, $yc); 11899 $xmax = max($xmax, $xc); 11900 $ymax = max($ymax, $yc); 11901 } 11902 return array($xmin, $ymin, $xmax, $ymax); 11903 } 11904 11905 /** 11906 * Draws a circle. 11907 * A circle is formed from n Bezier curves. 11908 * @param $x0 (float) Abscissa of center point. 11909 * @param $y0 (float) Ordinate of center point. 11910 * @param $r (float) Radius. 11911 * @param $angstr: (float) Angle start of draw line. Default value: 0. 11912 * @param $angend: (float) Angle finish of draw line. Default value: 360. 11913 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11914 * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array). 11915 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). 11916 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle. 11917 * @public 11918 * @since 2.1.000 (2008-01-08) 11919 */ 11920 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 11921 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc); 11922 } 11923 11924 /** 11925 * Draws a polygonal line 11926 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 11927 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11928 * @param $line_style (array) Line style of polygon. Array with keys among the following: 11929 * <ul> 11930 * <li>all: Line style of all lines. Array like for SetLineStyle().</li> 11931 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li> 11932 * </ul> 11933 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 11934 * @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). 11935 * @since 4.8.003 (2009-09-15) 11936 * @public 11937 */ 11938 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) { 11939 $this->Polygon($p, $style, $line_style, $fill_color, false); 11940 } 11941 11942 /** 11943 * Draws a polygon. 11944 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 11945 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 11946 * @param $line_style (array) Line style of polygon. Array with keys among the following: 11947 * <ul> 11948 * <li>all: Line style of all lines. Array like for SetLineStyle().</li> 11949 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li> 11950 * </ul> 11951 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 11952 * @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). 11953 * @param $closed (boolean) if true the polygon is closes, otherwise will remain open 11954 * @public 11955 * @since 2.1.000 (2008-01-08) 11956 */ 11957 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) { 11958 if ($this->state != 2) { 11959 return; 11960 } 11961 $nc = count($p); // number of coordinates 11962 $np = $nc / 2; // number of points 11963 if ($closed) { 11964 // close polygon by adding the first 2 points at the end (one line) 11965 for ($i = 0; $i < 4; ++$i) { 11966 $p[$nc + $i] = $p[$i]; 11967 } 11968 // copy style for the last added line 11969 if (isset($line_style[0])) { 11970 $line_style[$np] = $line_style[0]; 11971 } 11972 $nc += 4; 11973 } 11974 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 11975 $this->SetFillColorArray($fill_color); 11976 } 11977 $op = TCPDF_STATIC::getPathPaintOperator($style); 11978 if ($op == 'f') { 11979 $line_style = array(); 11980 } 11981 $draw = true; 11982 if ($line_style) { 11983 if (isset($line_style['all'])) { 11984 $this->SetLineStyle($line_style['all']); 11985 } else { 11986 $draw = false; 11987 if ($op == 'B') { 11988 // draw fill 11989 $op = 'f'; 11990 $this->_outPoint($p[0], $p[1]); 11991 for ($i = 2; $i < $nc; $i = $i + 2) { 11992 $this->_outLine($p[$i], $p[$i + 1]); 11993 } 11994 $this->_out($op); 11995 } 11996 // draw outline 11997 $this->_outPoint($p[0], $p[1]); 11998 for ($i = 2; $i < $nc; $i = $i + 2) { 11999 $line_num = ($i / 2) - 1; 12000 if (isset($line_style[$line_num])) { 12001 if ($line_style[$line_num] != 0) { 12002 if (is_array($line_style[$line_num])) { 12003 $this->_out('S'); 12004 $this->SetLineStyle($line_style[$line_num]); 12005 $this->_outPoint($p[$i - 2], $p[$i - 1]); 12006 $this->_outLine($p[$i], $p[$i + 1]); 12007 $this->_out('S'); 12008 $this->_outPoint($p[$i], $p[$i + 1]); 12009 } else { 12010 $this->_outLine($p[$i], $p[$i + 1]); 12011 } 12012 } 12013 } else { 12014 $this->_outLine($p[$i], $p[$i + 1]); 12015 } 12016 } 12017 $this->_out($op); 12018 } 12019 } 12020 if ($draw) { 12021 $this->_outPoint($p[0], $p[1]); 12022 for ($i = 2; $i < $nc; $i = $i + 2) { 12023 $this->_outLine($p[$i], $p[$i + 1]); 12024 } 12025 $this->_out($op); 12026 } 12027 } 12028 12029 /** 12030 * Draws a regular polygon. 12031 * @param $x0 (float) Abscissa of center point. 12032 * @param $y0 (float) Ordinate of center point. 12033 * @param $r: (float) Radius of inscribed circle. 12034 * @param $ns (integer) Number of sides. 12035 * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0. 12036 * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false. 12037 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12038 * @param $line_style (array) Line style of polygon sides. Array with keys among the following: 12039 * <ul> 12040 * <li>all: Line style of all sides. Array like for SetLineStyle().</li> 12041 * <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li> 12042 * </ul> 12043 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 12044 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). 12045 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are: 12046 * <ul> 12047 * <li>D or empty string: Draw (default).</li> 12048 * <li>F: Fill.</li> 12049 * <li>DF or FD: Draw and fill.</li> 12050 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 12051 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 12052 * </ul> 12053 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array). 12054 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 12055 * @public 12056 * @since 2.1.000 (2008-01-08) 12057 */ 12058 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()) { 12059 if (3 > $ns) { 12060 $ns = 3; 12061 } 12062 if ($draw_circle) { 12063 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 12064 } 12065 $p = array(); 12066 for ($i = 0; $i < $ns; ++$i) { 12067 $a = $angle + ($i * 360 / $ns); 12068 $a_rad = deg2rad((float) $a); 12069 $p[] = $x0 + ($r * sin($a_rad)); 12070 $p[] = $y0 + ($r * cos($a_rad)); 12071 } 12072 $this->Polygon($p, $style, $line_style, $fill_color); 12073 } 12074 12075 /** 12076 * Draws a star polygon 12077 * @param $x0 (float) Abscissa of center point. 12078 * @param $y0 (float) Ordinate of center point. 12079 * @param $r (float) Radius of inscribed circle. 12080 * @param $nv (integer) Number of vertices. 12081 * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon). 12082 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0. 12083 * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false. 12084 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12085 * @param $line_style (array) Line style of polygon sides. Array with keys among the following: 12086 * <ul> 12087 * <li>all: Line style of all sides. Array like for 12088 * SetLineStyle().</li> 12089 * <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li> 12090 * </ul> 12091 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 12092 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). 12093 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are: 12094 * <ul> 12095 * <li>D or empty string: Draw (default).</li> 12096 * <li>F: Fill.</li> 12097 * <li>DF or FD: Draw and fill.</li> 12098 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 12099 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 12100 * </ul> 12101 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array). 12102 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 12103 * @public 12104 * @since 2.1.000 (2008-01-08) 12105 */ 12106 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()) { 12107 if ($nv < 2) { 12108 $nv = 2; 12109 } 12110 if ($draw_circle) { 12111 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 12112 } 12113 $p2 = array(); 12114 $visited = array(); 12115 for ($i = 0; $i < $nv; ++$i) { 12116 $a = $angle + ($i * 360 / $nv); 12117 $a_rad = deg2rad((float) $a); 12118 $p2[] = $x0 + ($r * sin($a_rad)); 12119 $p2[] = $y0 + ($r * cos($a_rad)); 12120 $visited[] = false; 12121 } 12122 $p = array(); 12123 $i = 0; 12124 do { 12125 $p[] = $p2[$i * 2]; 12126 $p[] = $p2[($i * 2) + 1]; 12127 $visited[$i] = true; 12128 $i += $ng; 12129 $i %= $nv; 12130 } while (!$visited[$i]); 12131 $this->Polygon($p, $style, $line_style, $fill_color); 12132 } 12133 12134 /** 12135 * Draws a rounded rectangle. 12136 * @param $x (float) Abscissa of upper-left corner. 12137 * @param $y (float) Ordinate of upper-left corner. 12138 * @param $w (float) Width. 12139 * @param $h (float) Height. 12140 * @param $r (float) the radius of the circle used to round off the corners of the rectangle. 12141 * @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"). 12142 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12143 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array). 12144 * @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). 12145 * @public 12146 * @since 2.1.000 (2008-01-08) 12147 */ 12148 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 12149 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color); 12150 } 12151 12152 /** 12153 * Draws a rounded rectangle. 12154 * @param $x (float) Abscissa of upper-left corner. 12155 * @param $y (float) Ordinate of upper-left corner. 12156 * @param $w (float) Width. 12157 * @param $h (float) Height. 12158 * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle. 12159 * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle. 12160 * @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"). 12161 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 12162 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array). 12163 * @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). 12164 * @public 12165 * @since 4.9.019 (2010-04-22) 12166 */ 12167 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 12168 if ($this->state != 2) { 12169 return; 12170 } 12171 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) { 12172 // Not rounded 12173 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); 12174 return; 12175 } 12176 // Rounded 12177 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 12178 $this->SetFillColorArray($fill_color); 12179 } 12180 $op = TCPDF_STATIC::getPathPaintOperator($style); 12181 if ($op == 'f') { 12182 $border_style = array(); 12183 } 12184 if ($border_style) { 12185 $this->SetLineStyle($border_style); 12186 } 12187 $MyArc = 4 / 3 * (sqrt(2) - 1); 12188 $this->_outPoint($x + $rx, $y); 12189 $xc = $x + $w - $rx; 12190 $yc = $y + $ry; 12191 $this->_outLine($xc, $y); 12192 if ($round_corner[0]) { 12193 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc); 12194 } else { 12195 $this->_outLine($x + $w, $y); 12196 } 12197 $xc = $x + $w - $rx; 12198 $yc = $y + $h - $ry; 12199 $this->_outLine($x + $w, $yc); 12200 if ($round_corner[1]) { 12201 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry); 12202 } else { 12203 $this->_outLine($x + $w, $y + $h); 12204 } 12205 $xc = $x + $rx; 12206 $yc = $y + $h - $ry; 12207 $this->_outLine($xc, $y + $h); 12208 if ($round_corner[2]) { 12209 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc); 12210 } else { 12211 $this->_outLine($x, $y + $h); 12212 } 12213 $xc = $x + $rx; 12214 $yc = $y + $ry; 12215 $this->_outLine($x, $yc); 12216 if ($round_corner[3]) { 12217 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry); 12218 } else { 12219 $this->_outLine($x, $y); 12220 $this->_outLine($x + $rx, $y); 12221 } 12222 $this->_out($op); 12223 } 12224 12225 /** 12226 * Draws a grahic arrow. 12227 * @param $x0 (float) Abscissa of first point. 12228 * @param $y0 (float) Ordinate of first point. 12229 * @param $x1 (float) Abscissa of second point. 12230 * @param $y1 (float) Ordinate of second point. 12231 * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead) 12232 * @param $arm_size (float) length of arrowhead arms 12233 * @param $arm_angle (int) angle between an arm and the shaft 12234 * @author Piotr Galecki, Nicola Asuni, Andy Meier 12235 * @since 4.6.018 (2009-07-10) 12236 */ 12237 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) { 12238 // getting arrow direction angle 12239 // 0 deg angle is when both arms go along X axis. angle grows clockwise. 12240 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1)); 12241 if ($dir_angle < 0) { 12242 $dir_angle += (2 * M_PI); 12243 } 12244 $arm_angle = deg2rad($arm_angle); 12245 $sx1 = $x1; 12246 $sy1 = $y1; 12247 if ($head_style > 0) { 12248 // calculate the stopping point for the arrow shaft 12249 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle)); 12250 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle)); 12251 } 12252 // main arrow line / shaft 12253 $this->Line($x0, $y0, $sx1, $sy1); 12254 // left arrowhead arm tip 12255 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle)); 12256 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle)); 12257 // right arrowhead arm tip 12258 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle)); 12259 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle)); 12260 $mode = 'D'; 12261 $style = array(); 12262 switch ($head_style) { 12263 case 0: { 12264 // draw only arrowhead arms 12265 $mode = 'D'; 12266 $style = array(1, 1, 0); 12267 break; 12268 } 12269 case 1: { 12270 // draw closed arrowhead, but no fill 12271 $mode = 'D'; 12272 break; 12273 } 12274 case 2: { 12275 // closed and filled arrowhead 12276 $mode = 'DF'; 12277 break; 12278 } 12279 case 3: { 12280 // filled arrowhead 12281 $mode = 'F'; 12282 break; 12283 } 12284 } 12285 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array()); 12286 } 12287 12288 // END GRAPHIC FUNCTIONS SECTION ----------------------- 12289 12290 /** 12291 * Add a Named Destination. 12292 * NOTE: destination names are unique, so only last entry will be saved. 12293 * @param $name (string) Destination name. 12294 * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;). 12295 * @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. 12296 * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;). 12297 * @return (string) Stripped named destination identifier or false in case of error. 12298 * @public 12299 * @author Christian Deligant, Nicola Asuni 12300 * @since 5.9.097 (2011-06-23) 12301 */ 12302 public function setDestination($name, $y=-1, $page='', $x=-1) { 12303 // remove unsupported characters 12304 $name = TCPDF_STATIC::encodeNameObject($name); 12305 if (TCPDF_STATIC::empty_string($name)) { 12306 return false; 12307 } 12308 if ($y == -1) { 12309 $y = $this->GetY(); 12310 } elseif ($y < 0) { 12311 $y = 0; 12312 } elseif ($y > $this->h) { 12313 $y = $this->h; 12314 } 12315 if ($x == -1) { 12316 $x = $this->GetX(); 12317 } elseif ($x < 0) { 12318 $x = 0; 12319 } elseif ($x > $this->w) { 12320 $x = $this->w; 12321 } 12322 $fixed = false; 12323 if (!empty($page) AND (substr($page, 0, 1) == '*')) { 12324 $page = intval(substr($page, 1)); 12325 // this page number will not be changed when moving/add/deleting pages 12326 $fixed = true; 12327 } 12328 if (empty($page)) { 12329 $page = $this->PageNo(); 12330 if (empty($page)) { 12331 return; 12332 } 12333 } 12334 $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed); 12335 return $name; 12336 } 12337 12338 /** 12339 * Return the Named Destination array. 12340 * @return (array) Named Destination array. 12341 * @public 12342 * @author Nicola Asuni 12343 * @since 5.9.097 (2011-06-23) 12344 */ 12345 public function getDestination() { 12346 return $this->dests; 12347 } 12348 12349 /** 12350 * Insert Named Destinations. 12351 * @protected 12352 * @author Johannes G\FCntert, Nicola Asuni 12353 * @since 5.9.098 (2011-06-23) 12354 */ 12355 protected function _putdests() { 12356 if (empty($this->dests)) { 12357 return; 12358 } 12359 $this->n_dests = $this->_newobj(); 12360 $out = ' <<'; 12361 foreach($this->dests as $name => $o) { 12362 $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))); 12363 } 12364 $out .= ' >>'; 12365 $out .= "\n".'endobj'; 12366 $this->_out($out); 12367 } 12368 12369 /** 12370 * Adds a bookmark - alias for Bookmark(). 12371 * @param $txt (string) Bookmark description. 12372 * @param $level (int) Bookmark level (minimum value is 0). 12373 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;). 12374 * @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. 12375 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. 12376 * @param $color (array) RGB color array (values from 0 to 255). 12377 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). 12378 * @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). 12379 * @public 12380 */ 12381 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { 12382 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link); 12383 } 12384 12385 /** 12386 * Adds a bookmark. 12387 * @param $txt (string) Bookmark description. 12388 * @param $level (int) Bookmark level (minimum value is 0). 12389 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;). 12390 * @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. 12391 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. 12392 * @param $color (array) RGB color array (values from 0 to 255). 12393 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). 12394 * @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). 12395 * @public 12396 * @since 2.1.002 (2008-02-12) 12397 */ 12398 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { 12399 if ($level < 0) { 12400 $level = 0; 12401 } 12402 if (isset($this->outlines[0])) { 12403 $lastoutline = end($this->outlines); 12404 $maxlevel = $lastoutline['l'] + 1; 12405 } else { 12406 $maxlevel = 0; 12407 } 12408 if ($level > $maxlevel) { 12409 $level = $maxlevel; 12410 } 12411 if ($y == -1) { 12412 $y = $this->GetY(); 12413 } elseif ($y < 0) { 12414 $y = 0; 12415 } elseif ($y > $this->h) { 12416 $y = $this->h; 12417 } 12418 if ($x == -1) { 12419 $x = $this->GetX(); 12420 } elseif ($x < 0) { 12421 $x = 0; 12422 } elseif ($x > $this->w) { 12423 $x = $this->w; 12424 } 12425 $fixed = false; 12426 $pageAsString = (string) $page; 12427 if ($pageAsString && $pageAsString[0] == '*') { 12428 $page = intval(substr($page, 1)); 12429 // this page number will not be changed when moving/add/deleting pages 12430 $fixed = true; 12431 } 12432 if (empty($page)) { 12433 $page = $this->PageNo(); 12434 if (empty($page)) { 12435 return; 12436 } 12437 } 12438 $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link); 12439 } 12440 12441 /** 12442 * Sort bookmarks for page and key. 12443 * @protected 12444 * @since 5.9.119 (2011-09-19) 12445 */ 12446 protected function sortBookmarks() { 12447 // get sorting columns 12448 $outline_p = array(); 12449 $outline_y = array(); 12450 foreach ($this->outlines as $key => $row) { 12451 $outline_p[$key] = $row['p']; 12452 $outline_k[$key] = $key; 12453 } 12454 // sort outlines by page and original position 12455 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines); 12456 } 12457 12458 /** 12459 * Create a bookmark PDF string. 12460 * @protected 12461 * @author Olivier Plathey, Nicola Asuni 12462 * @since 2.1.002 (2008-02-12) 12463 */ 12464 protected function _putbookmarks() { 12465 $nb = count($this->outlines); 12466 if ($nb == 0) { 12467 return; 12468 } 12469 // sort bookmarks 12470 $this->sortBookmarks(); 12471 $lru = array(); 12472 $level = 0; 12473 foreach ($this->outlines as $i => $o) { 12474 if ($o['l'] > 0) { 12475 $parent = $lru[($o['l'] - 1)]; 12476 //Set parent and last pointers 12477 $this->outlines[$i]['parent'] = $parent; 12478 $this->outlines[$parent]['last'] = $i; 12479 if ($o['l'] > $level) { 12480 //Level increasing: set first pointer 12481 $this->outlines[$parent]['first'] = $i; 12482 } 12483 } else { 12484 $this->outlines[$i]['parent'] = $nb; 12485 } 12486 if (($o['l'] <= $level) AND ($i > 0)) { 12487 //Set prev and next pointers 12488 $prev = $lru[$o['l']]; 12489 $this->outlines[$prev]['next'] = $i; 12490 $this->outlines[$i]['prev'] = $prev; 12491 } 12492 $lru[$o['l']] = $i; 12493 $level = $o['l']; 12494 } 12495 //Outline items 12496 $n = $this->n + 1; 12497 $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'; 12498 foreach ($this->outlines as $i => $o) { 12499 $oid = $this->_newobj(); 12500 // covert HTML title to string 12501 $title = preg_replace($nltags, "\n", $o['t']); 12502 $title = preg_replace("/[\r]+/si", '', $title); 12503 $title = preg_replace("/[\n]+/si", "\n", $title); 12504 $title = strip_tags($title); 12505 $title = $this->stringTrim($title); 12506 $out = '<</Title '.$this->_textstring($title, $oid); 12507 $out .= ' /Parent '.($n + $o['parent']).' 0 R'; 12508 if (isset($o['prev'])) { 12509 $out .= ' /Prev '.($n + $o['prev']).' 0 R'; 12510 } 12511 if (isset($o['next'])) { 12512 $out .= ' /Next '.($n + $o['next']).' 0 R'; 12513 } 12514 if (isset($o['first'])) { 12515 $out .= ' /First '.($n + $o['first']).' 0 R'; 12516 } 12517 if (isset($o['last'])) { 12518 $out .= ' /Last '.($n + $o['last']).' 0 R'; 12519 } 12520 if (isset($o['u']) AND !empty($o['u'])) { 12521 // link 12522 if (is_string($o['u'])) { 12523 if ($o['u'][0] == '#') { 12524 // internal destination 12525 $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1)); 12526 } elseif ($o['u'][0] == '%') { 12527 // embedded PDF file 12528 $filename = basename(substr($o['u'], 1)); 12529 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; 12530 } elseif ($o['u'][0] == '*') { 12531 // embedded generic file 12532 $filename = basename(substr($o['u'], 1)); 12533 $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});'; 12534 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>'; 12535 } else { 12536 // external URI link 12537 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>'; 12538 } 12539 } elseif (isset($this->links[$o['u']])) { 12540 // internal link ID 12541 $l = $this->links[$o['u']]; 12542 if (isset($this->page_obj_id[($l['p'])])) { 12543 $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))); 12544 } 12545 } 12546 } elseif (isset($this->page_obj_id[($o['p'])])) { 12547 // link to a page 12548 $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))); 12549 } 12550 // set font style 12551 $style = 0; 12552 if (!empty($o['s'])) { 12553 // bold 12554 if (strpos($o['s'], 'B') !== false) { 12555 $style |= 2; 12556 } 12557 // oblique 12558 if (strpos($o['s'], 'I') !== false) { 12559 $style |= 1; 12560 } 12561 } 12562 $out .= sprintf(' /F %d', $style); 12563 // set bookmark color 12564 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) { 12565 $color = array_values($o['c']); 12566 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); 12567 } else { 12568 // black 12569 $out .= ' /C [0.0 0.0 0.0]'; 12570 } 12571 $out .= ' /Count 0'; // normally closed item 12572 $out .= ' >>'; 12573 $out .= "\n".'endobj'; 12574 $this->_out($out); 12575 } 12576 //Outline root 12577 $this->OutlineRoot = $this->_newobj(); 12578 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj'); 12579 } 12580 12581 // --- JAVASCRIPT ------------------------------------------------------ 12582 12583 /** 12584 * Adds a javascript 12585 * @param $script (string) Javascript code 12586 * @public 12587 * @author Johannes G\FCntert, Nicola Asuni 12588 * @since 2.1.002 (2008-02-12) 12589 */ 12590 public function IncludeJS($script) { 12591 $this->javascript .= $script; 12592 } 12593 12594 /** 12595 * Adds a javascript object and return object ID 12596 * @param $script (string) Javascript code 12597 * @param $onload (boolean) if true executes this object when opening the document 12598 * @return int internal object ID 12599 * @public 12600 * @author Nicola Asuni 12601 * @since 4.8.000 (2009-09-07) 12602 */ 12603 public function addJavascriptObject($script, $onload=false) { 12604 if ($this->pdfa_mode) { 12605 // javascript is not allowed in PDF/A mode 12606 return false; 12607 } 12608 ++$this->n; 12609 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload); 12610 return $this->n; 12611 } 12612 12613 /** 12614 * Create a javascript PDF string. 12615 * @protected 12616 * @author Johannes G\FCntert, Nicola Asuni 12617 * @since 2.1.002 (2008-02-12) 12618 */ 12619 protected function _putjavascript() { 12620 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) { 12621 return; 12622 } 12623 if (strpos($this->javascript, 'this.addField') > 0) { 12624 if (!$this->ur['enabled']) { 12625 //$this->setUserRights(); 12626 } 12627 // the following two lines are used to avoid form fields duplication after saving 12628 // The addField method only works when releasing user rights (UR3) 12629 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1); 12630 $jsb = "getField('tcpdfdocsaved').value='saved';"; 12631 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb; 12632 } 12633 // name tree for javascript 12634 $this->n_js = '<< /Names ['; 12635 if (!empty($this->javascript)) { 12636 $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R'; 12637 } 12638 if (!empty($this->js_objects)) { 12639 foreach ($this->js_objects as $key => $val) { 12640 if ($val['onload']) { 12641 $this->n_js .= ' (JS'.$key.') '.$key.' 0 R'; 12642 } 12643 } 12644 } 12645 $this->n_js .= ' ] >>'; 12646 // default Javascript object 12647 if (!empty($this->javascript)) { 12648 $obj_id = $this->_newobj(); 12649 $out = '<< /S /JavaScript'; 12650 $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id); 12651 $out .= ' >>'; 12652 $out .= "\n".'endobj'; 12653 $this->_out($out); 12654 } 12655 // additional Javascript objects 12656 if (!empty($this->js_objects)) { 12657 foreach ($this->js_objects as $key => $val) { 12658 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj'; 12659 $this->_out($out); 12660 } 12661 } 12662 } 12663 12664 /** 12665 * Adds a javascript form field. 12666 * @param $type (string) field type 12667 * @param $name (string) field name 12668 * @param $x (int) horizontal position 12669 * @param $y (int) vertical position 12670 * @param $w (int) width 12671 * @param $h (int) height 12672 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12673 * @protected 12674 * @author Denis Van Nuffelen, Nicola Asuni 12675 * @since 2.1.002 (2008-02-12) 12676 */ 12677 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) { 12678 if ($this->rtl) { 12679 $x = $x - $w; 12680 } 12681 // the followind avoid fields duplication after saving the document 12682 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {"; 12683 $k = $this->k; 12684 $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"; 12685 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n"; 12686 foreach($prop as $key => $val) { 12687 if (strcmp(substr($key, -5), 'Color') == 0) { 12688 $val = TCPDF_COLORS::_JScolor($val); 12689 } else { 12690 $val = "'".$val."'"; 12691 } 12692 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n"; 12693 } 12694 if ($this->rtl) { 12695 $this->x -= $w; 12696 } else { 12697 $this->x += $w; 12698 } 12699 $this->javascript .= '}'; 12700 } 12701 12702 // --- FORM FIELDS ----------------------------------------------------- 12703 12704 12705 12706 /** 12707 * Set default properties for form fields. 12708 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12709 * @public 12710 * @author Nicola Asuni 12711 * @since 4.8.000 (2009-09-06) 12712 */ 12713 public function setFormDefaultProp($prop=array()) { 12714 $this->default_form_prop = $prop; 12715 } 12716 12717 /** 12718 * Return the default properties for form fields. 12719 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12720 * @public 12721 * @author Nicola Asuni 12722 * @since 4.8.000 (2009-09-06) 12723 */ 12724 public function getFormDefaultProp() { 12725 return $this->default_form_prop; 12726 } 12727 12728 /** 12729 * Creates a text field 12730 * @param $name (string) field name 12731 * @param $w (float) Width of the rectangle 12732 * @param $h (float) Height of the rectangle 12733 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12734 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 12735 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12736 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12737 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 12738 * @public 12739 * @author Nicola Asuni 12740 * @since 4.8.000 (2009-09-07) 12741 */ 12742 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 12743 if ($x === '') { 12744 $x = $this->x; 12745 } 12746 if ($y === '') { 12747 $y = $this->y; 12748 } 12749 // check page for no-write regions and adapt page margins if necessary 12750 list($x, $y) = $this->checkPageRegions($h, $x, $y); 12751 if ($js) { 12752 $this->_addfield('text', $name, $x, $y, $w, $h, $prop); 12753 return; 12754 } 12755 // get default style 12756 $prop = array_merge($this->getFormDefaultProp(), $prop); 12757 // get annotation data 12758 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12759 // set default appearance stream 12760 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 12761 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 12762 $popt['da'] = $fontstyle; 12763 // build appearance stream 12764 $popt['ap'] = array(); 12765 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 12766 $text = ''; 12767 if (isset($prop['value']) AND !empty($prop['value'])) { 12768 $text = $prop['value']; 12769 } elseif (isset($opt['v']) AND !empty($opt['v'])) { 12770 $text = $opt['v']; 12771 } 12772 $tmpid = $this->startTemplate($w, $h, false); 12773 $align = ''; 12774 if (isset($popt['q'])) { 12775 switch ($popt['q']) { 12776 case 0: { 12777 $align = 'L'; 12778 break; 12779 } 12780 case 1: { 12781 $align = 'C'; 12782 break; 12783 } 12784 case 2: { 12785 $align = 'R'; 12786 break; 12787 } 12788 default: { 12789 $align = ''; 12790 break; 12791 } 12792 } 12793 } 12794 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 12795 $this->endTemplate(); 12796 --$this->n; 12797 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 12798 unset($this->xobjects[$tmpid]); 12799 $popt['ap']['n'] .= 'Q EMC'; 12800 // merge options 12801 $opt = array_merge($popt, $opt); 12802 // remove some conflicting options 12803 unset($opt['bs']); 12804 // set remaining annotation data 12805 $opt['Subtype'] = 'Widget'; 12806 $opt['ft'] = 'Tx'; 12807 $opt['t'] = $name; 12808 // Additional annotation's parameters (check _putannotsobj() method): 12809 //$opt['f'] 12810 //$opt['as'] 12811 //$opt['bs'] 12812 //$opt['be'] 12813 //$opt['c'] 12814 //$opt['border'] 12815 //$opt['h'] 12816 //$opt['mk']; 12817 //$opt['mk']['r'] 12818 //$opt['mk']['bc']; 12819 //$opt['mk']['bg']; 12820 unset($opt['mk']['ca']); 12821 unset($opt['mk']['rc']); 12822 unset($opt['mk']['ac']); 12823 unset($opt['mk']['i']); 12824 unset($opt['mk']['ri']); 12825 unset($opt['mk']['ix']); 12826 unset($opt['mk']['if']); 12827 //$opt['mk']['if']['sw']; 12828 //$opt['mk']['if']['s']; 12829 //$opt['mk']['if']['a']; 12830 //$opt['mk']['if']['fb']; 12831 unset($opt['mk']['tp']); 12832 //$opt['tu'] 12833 //$opt['tm'] 12834 //$opt['ff'] 12835 //$opt['v'] 12836 //$opt['dv'] 12837 //$opt['a'] 12838 //$opt['aa'] 12839 //$opt['q'] 12840 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 12841 if ($this->rtl) { 12842 $this->x -= $w; 12843 } else { 12844 $this->x += $w; 12845 } 12846 } 12847 12848 /** 12849 * Creates a RadioButton field. 12850 * @param $name (string) Field name. 12851 * @param $w (int) Width of the radio button. 12852 * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12853 * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference. 12854 * @param $onvalue (string) Value to be returned if selected. 12855 * @param $checked (boolean) Define the initial state. 12856 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12857 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12858 * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered). 12859 * @public 12860 * @author Nicola Asuni 12861 * @since 4.8.000 (2009-09-07) 12862 */ 12863 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) { 12864 if ($x === '') { 12865 $x = $this->x; 12866 } 12867 if ($y === '') { 12868 $y = $this->y; 12869 } 12870 // check page for no-write regions and adapt page margins if necessary 12871 list($x, $y) = $this->checkPageRegions($w, $x, $y); 12872 if ($js) { 12873 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop); 12874 return; 12875 } 12876 if (TCPDF_STATIC::empty_string($onvalue)) { 12877 $onvalue = 'On'; 12878 } 12879 if ($checked) { 12880 $defval = $onvalue; 12881 } else { 12882 $defval = 'Off'; 12883 } 12884 // set font 12885 $font = 'zapfdingbats'; 12886 if ($this->pdfa_mode) { 12887 // all fonts must be embedded 12888 $font = 'pdfa'.$font; 12889 } 12890 $this->AddFont($font); 12891 $tmpfont = $this->getFontBuffer($font); 12892 // set data for parent group 12893 if (!isset($this->radiobutton_groups[$this->page])) { 12894 $this->radiobutton_groups[$this->page] = array(); 12895 } 12896 if (!isset($this->radiobutton_groups[$this->page][$name])) { 12897 $this->radiobutton_groups[$this->page][$name] = array(); 12898 ++$this->n; 12899 $this->radiobutton_groups[$this->page][$name]['n'] = $this->n; 12900 $this->radio_groups[] = $this->n; 12901 } 12902 $kid = ($this->n + 1); 12903 // save object ID to be added on Kids entry on parent object 12904 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval); 12905 // get default style 12906 $prop = array_merge($this->getFormDefaultProp(), $prop); 12907 $prop['NoToggleToOff'] = 'true'; 12908 $prop['Radio'] = 'true'; 12909 $prop['borderStyle'] = 'inset'; 12910 // get annotation data 12911 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12912 // set additional default options 12913 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; 12914 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); 12915 $popt['da'] = $fontstyle; 12916 // build appearance stream 12917 $popt['ap'] = array(); 12918 $popt['ap']['n'] = array(); 12919 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k); 12920 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); 12921 $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); 12922 $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); 12923 if (!isset($popt['mk'])) { 12924 $popt['mk'] = array(); 12925 } 12926 $popt['mk']['ca'] = '(l)'; 12927 // merge options 12928 $opt = array_merge($popt, $opt); 12929 // set remaining annotation data 12930 $opt['Subtype'] = 'Widget'; 12931 $opt['ft'] = 'Btn'; 12932 if ($checked) { 12933 $opt['v'] = array('/'.$onvalue); 12934 $opt['as'] = $onvalue; 12935 } else { 12936 $opt['as'] = 'Off'; 12937 } 12938 // store readonly flag 12939 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) { 12940 $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false; 12941 } 12942 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64); 12943 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 12944 if ($this->rtl) { 12945 $this->x -= $w; 12946 } else { 12947 $this->x += $w; 12948 } 12949 } 12950 12951 /** 12952 * Creates a List-box field 12953 * @param $name (string) field name 12954 * @param $w (int) width 12955 * @param $h (int) height 12956 * @param $values (array) array containing the list of values. 12957 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 12958 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 12959 * @param $x (float) Abscissa of the upper-left corner of the rectangle 12960 * @param $y (float) Ordinate of the upper-left corner of the rectangle 12961 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 12962 * @public 12963 * @author Nicola Asuni 12964 * @since 4.8.000 (2009-09-07) 12965 */ 12966 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 12967 if ($x === '') { 12968 $x = $this->x; 12969 } 12970 if ($y === '') { 12971 $y = $this->y; 12972 } 12973 // check page for no-write regions and adapt page margins if necessary 12974 list($x, $y) = $this->checkPageRegions($h, $x, $y); 12975 if ($js) { 12976 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop); 12977 $s = ''; 12978 foreach ($values as $value) { 12979 if (is_array($value)) { 12980 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']'; 12981 } else { 12982 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']'; 12983 } 12984 } 12985 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n"; 12986 return; 12987 } 12988 // get default style 12989 $prop = array_merge($this->getFormDefaultProp(), $prop); 12990 // get annotation data 12991 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 12992 // set additional default values 12993 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 12994 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 12995 $popt['da'] = $fontstyle; 12996 // build appearance stream 12997 $popt['ap'] = array(); 12998 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 12999 $text = ''; 13000 foreach($values as $item) { 13001 if (is_array($item)) { 13002 $text .= $item[1]."\n"; 13003 } else { 13004 $text .= $item."\n"; 13005 } 13006 } 13007 $tmpid = $this->startTemplate($w, $h, false); 13008 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 13009 $this->endTemplate(); 13010 --$this->n; 13011 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 13012 unset($this->xobjects[$tmpid]); 13013 $popt['ap']['n'] .= 'Q EMC'; 13014 // merge options 13015 $opt = array_merge($popt, $opt); 13016 // set remaining annotation data 13017 $opt['Subtype'] = 'Widget'; 13018 $opt['ft'] = 'Ch'; 13019 $opt['t'] = $name; 13020 $opt['opt'] = $values; 13021 unset($opt['mk']['ca']); 13022 unset($opt['mk']['rc']); 13023 unset($opt['mk']['ac']); 13024 unset($opt['mk']['i']); 13025 unset($opt['mk']['ri']); 13026 unset($opt['mk']['ix']); 13027 unset($opt['mk']['if']); 13028 unset($opt['mk']['tp']); 13029 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 13030 if ($this->rtl) { 13031 $this->x -= $w; 13032 } else { 13033 $this->x += $w; 13034 } 13035 } 13036 13037 /** 13038 * Creates a Combo-box field 13039 * @param $name (string) field name 13040 * @param $w (int) width 13041 * @param $h (int) height 13042 * @param $values (array) array containing the list of values. 13043 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 13044 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 13045 * @param $x (float) Abscissa of the upper-left corner of the rectangle 13046 * @param $y (float) Ordinate of the upper-left corner of the rectangle 13047 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 13048 * @public 13049 * @author Nicola Asuni 13050 * @since 4.8.000 (2009-09-07) 13051 */ 13052 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 13053 if ($x === '') { 13054 $x = $this->x; 13055 } 13056 if ($y === '') { 13057 $y = $this->y; 13058 } 13059 // check page for no-write regions and adapt page margins if necessary 13060 list($x, $y) = $this->checkPageRegions($h, $x, $y); 13061 if ($js) { 13062 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop); 13063 $s = ''; 13064 foreach ($values as $value) { 13065 if (is_array($value)) { 13066 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']'; 13067 } else { 13068 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']'; 13069 } 13070 } 13071 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n"; 13072 return; 13073 } 13074 // get default style 13075 $prop = array_merge($this->getFormDefaultProp(), $prop); 13076 $prop['Combo'] = true; 13077 // get annotation data 13078 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13079 // set additional default options 13080 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 13081 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 13082 $popt['da'] = $fontstyle; 13083 // build appearance stream 13084 $popt['ap'] = array(); 13085 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 13086 $text = ''; 13087 foreach($values as $item) { 13088 if (is_array($item)) { 13089 $text .= $item[1]."\n"; 13090 } else { 13091 $text .= $item."\n"; 13092 } 13093 } 13094 $tmpid = $this->startTemplate($w, $h, false); 13095 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 13096 $this->endTemplate(); 13097 --$this->n; 13098 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 13099 unset($this->xobjects[$tmpid]); 13100 $popt['ap']['n'] .= 'Q EMC'; 13101 // merge options 13102 $opt = array_merge($popt, $opt); 13103 // set remaining annotation data 13104 $opt['Subtype'] = 'Widget'; 13105 $opt['ft'] = 'Ch'; 13106 $opt['t'] = $name; 13107 $opt['opt'] = $values; 13108 unset($opt['mk']['ca']); 13109 unset($opt['mk']['rc']); 13110 unset($opt['mk']['ac']); 13111 unset($opt['mk']['i']); 13112 unset($opt['mk']['ri']); 13113 unset($opt['mk']['ix']); 13114 unset($opt['mk']['if']); 13115 unset($opt['mk']['tp']); 13116 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 13117 if ($this->rtl) { 13118 $this->x -= $w; 13119 } else { 13120 $this->x += $w; 13121 } 13122 } 13123 13124 /** 13125 * Creates a CheckBox field 13126 * @param $name (string) field name 13127 * @param $w (int) width 13128 * @param $checked (boolean) define the initial state. 13129 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 13130 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 13131 * @param $onvalue (string) value to be returned if selected. 13132 * @param $x (float) Abscissa of the upper-left corner of the rectangle 13133 * @param $y (float) Ordinate of the upper-left corner of the rectangle 13134 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 13135 * @public 13136 * @author Nicola Asuni 13137 * @since 4.8.000 (2009-09-07) 13138 */ 13139 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) { 13140 if ($x === '') { 13141 $x = $this->x; 13142 } 13143 if ($y === '') { 13144 $y = $this->y; 13145 } 13146 // check page for no-write regions and adapt page margins if necessary 13147 list($x, $y) = $this->checkPageRegions($w, $x, $y); 13148 if ($js) { 13149 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop); 13150 return; 13151 } 13152 if (!isset($prop['value'])) { 13153 $prop['value'] = array('Yes'); 13154 } 13155 // get default style 13156 $prop = array_merge($this->getFormDefaultProp(), $prop); 13157 $prop['borderStyle'] = 'inset'; 13158 // get annotation data 13159 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13160 // set additional default options 13161 $font = 'zapfdingbats'; 13162 if ($this->pdfa_mode) { 13163 // all fonts must be embedded 13164 $font = 'pdfa'.$font; 13165 } 13166 $this->AddFont($font); 13167 $tmpfont = $this->getFontBuffer($font); 13168 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; 13169 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); 13170 $popt['da'] = $fontstyle; 13171 // build appearance stream 13172 $popt['ap'] = array(); 13173 $popt['ap']['n'] = array(); 13174 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k); 13175 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); 13176 $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); 13177 $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); 13178 // merge options 13179 $opt = array_merge($popt, $opt); 13180 // set remaining annotation data 13181 $opt['Subtype'] = 'Widget'; 13182 $opt['ft'] = 'Btn'; 13183 $opt['t'] = $name; 13184 if (TCPDF_STATIC::empty_string($onvalue)) { 13185 $onvalue = 'Yes'; 13186 } 13187 $opt['opt'] = array($onvalue); 13188 if ($checked) { 13189 $opt['v'] = array('/Yes'); 13190 $opt['as'] = 'Yes'; 13191 } else { 13192 $opt['v'] = array('/Off'); 13193 $opt['as'] = 'Off'; 13194 } 13195 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 13196 if ($this->rtl) { 13197 $this->x -= $w; 13198 } else { 13199 $this->x += $w; 13200 } 13201 } 13202 13203 /** 13204 * Creates a button field 13205 * @param $name (string) field name 13206 * @param $w (int) width 13207 * @param $h (int) height 13208 * @param $caption (string) caption. 13209 * @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. 13210 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 13211 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. 13212 * @param $x (float) Abscissa of the upper-left corner of the rectangle 13213 * @param $y (float) Ordinate of the upper-left corner of the rectangle 13214 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). 13215 * @public 13216 * @author Nicola Asuni 13217 * @since 4.8.000 (2009-09-07) 13218 */ 13219 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 13220 if ($x === '') { 13221 $x = $this->x; 13222 } 13223 if ($y === '') { 13224 $y = $this->y; 13225 } 13226 // check page for no-write regions and adapt page margins if necessary 13227 list($x, $y) = $this->checkPageRegions($h, $x, $y); 13228 if ($js) { 13229 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop); 13230 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n"; 13231 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n"; 13232 $this->javascript .= 'f'.$name.".highlight='push';\n"; 13233 $this->javascript .= 'f'.$name.".print=false;\n"; 13234 return; 13235 } 13236 // get default style 13237 $prop = array_merge($this->getFormDefaultProp(), $prop); 13238 $prop['Pushbutton'] = 'true'; 13239 $prop['highlight'] = 'push'; 13240 $prop['display'] = 'display.noPrint'; 13241 // get annotation data 13242 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl); 13243 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 13244 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 13245 $popt['da'] = $fontstyle; 13246 // build appearance stream 13247 $popt['ap'] = array(); 13248 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 13249 $tmpid = $this->startTemplate($w, $h, false); 13250 $bw = (2 / $this->k); // border width 13251 $border = array( 13252 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 13253 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)), 13254 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 13255 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51))); 13256 $this->SetFillColor(204); 13257 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M'); 13258 $this->endTemplate(); 13259 --$this->n; 13260 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 13261 unset($this->xobjects[$tmpid]); 13262 $popt['ap']['n'] .= 'Q EMC'; 13263 // set additional default options 13264 if (!isset($popt['mk'])) { 13265 $popt['mk'] = array(); 13266 } 13267 $ann_obj_id = ($this->n + 1); 13268 if (!empty($action) AND !is_array($action)) { 13269 $ann_obj_id = ($this->n + 2); 13270 } 13271 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id); 13272 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id); 13273 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id); 13274 // merge options 13275 $opt = array_merge($popt, $opt); 13276 // set remaining annotation data 13277 $opt['Subtype'] = 'Widget'; 13278 $opt['ft'] = 'Btn'; 13279 $opt['t'] = $caption; 13280 $opt['v'] = $name; 13281 if (!empty($action)) { 13282 if (is_array($action)) { 13283 // form action options as on section 12.7.5 of PDF32000_2008. 13284 $opt['aa'] = '/D <<'; 13285 $bmode = array('SubmitForm', 'ResetForm', 'ImportData'); 13286 foreach ($action AS $key => $val) { 13287 if (($key == 'S') AND in_array($val, $bmode)) { 13288 $opt['aa'] .= ' /S /'.$val; 13289 } elseif (($key == 'F') AND (!empty($val))) { 13290 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id); 13291 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) { 13292 $opt['aa'] .= ' /Fields ['; 13293 foreach ($val AS $field) { 13294 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id); 13295 } 13296 $opt['aa'] .= ']'; 13297 } elseif (($key == 'Flags')) { 13298 $ff = 0; 13299 if (is_array($val)) { 13300 foreach ($val AS $flag) { 13301 switch ($flag) { 13302 case 'Include/Exclude': { 13303 $ff += 1 << 0; 13304 break; 13305 } 13306 case 'IncludeNoValueFields': { 13307 $ff += 1 << 1; 13308 break; 13309 } 13310 case 'ExportFormat': { 13311 $ff += 1 << 2; 13312 break; 13313 } 13314 case 'GetMethod': { 13315 $ff += 1 << 3; 13316 break; 13317 } 13318 case 'SubmitCoordinates': { 13319 $ff += 1 << 4; 13320 break; 13321 } 13322 case 'XFDF': { 13323 $ff += 1 << 5; 13324 break; 13325 } 13326 case 'IncludeAppendSaves': { 13327 $ff += 1 << 6; 13328 break; 13329 } 13330 case 'IncludeAnnotations': { 13331 $ff += 1 << 7; 13332 break; 13333 } 13334 case 'SubmitPDF': { 13335 $ff += 1 << 8; 13336 break; 13337 } 13338 case 'CanonicalFormat': { 13339 $ff += 1 << 9; 13340 break; 13341 } 13342 case 'ExclNonUserAnnots': { 13343 $ff += 1 << 10; 13344 break; 13345 } 13346 case 'ExclFKey': { 13347 $ff += 1 << 11; 13348 break; 13349 } 13350 case 'EmbedForm': { 13351 $ff += 1 << 13; 13352 break; 13353 } 13354 } 13355 } 13356 } else { 13357 $ff = intval($val); 13358 } 13359 $opt['aa'] .= ' /Flags '.$ff; 13360 } 13361 } 13362 $opt['aa'] .= ' >>'; 13363 } else { 13364 // Javascript action or raw action command 13365 $js_obj_id = $this->addJavascriptObject($action); 13366 $opt['aa'] = '/D '.$js_obj_id.' 0 R'; 13367 } 13368 } 13369 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 13370 if ($this->rtl) { 13371 $this->x -= $w; 13372 } else { 13373 $this->x += $w; 13374 } 13375 } 13376 13377 // --- END FORMS FIELDS ------------------------------------------------ 13378 13379 /** 13380 * Add certification signature (DocMDP or UR3) 13381 * You can set only one signature type 13382 * @protected 13383 * @author Nicola Asuni 13384 * @since 4.6.008 (2009-05-07) 13385 */ 13386 protected function _putsignature() { 13387 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) { 13388 return; 13389 } 13390 $sigobjid = ($this->sig_obj_id + 1); 13391 $out = $this->_getobj($sigobjid)."\n"; 13392 $out .= '<< /Type /Sig'; 13393 $out .= ' /Filter /Adobe.PPKLite'; 13394 $out .= ' /SubFilter /adbe.pkcs7.detached'; 13395 $out .= ' '.TCPDF_STATIC::$byterange_string; 13396 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>'; 13397 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) { 13398 $out .= ' /Reference ['; // array of signature reference dictionaries 13399 $out .= ' << /Type /SigRef'; 13400 if ($this->signature_data['cert_type'] > 0) { 13401 $out .= ' /TransformMethod /DocMDP'; 13402 $out .= ' /TransformParams <<'; 13403 $out .= ' /Type /TransformParams'; 13404 $out .= ' /P '.$this->signature_data['cert_type']; 13405 $out .= ' /V /1.2'; 13406 } else { 13407 $out .= ' /TransformMethod /UR3'; 13408 $out .= ' /TransformParams <<'; 13409 $out .= ' /Type /TransformParams'; 13410 $out .= ' /V /2.2'; 13411 if (!TCPDF_STATIC::empty_string($this->ur['document'])) { 13412 $out .= ' /Document['.$this->ur['document'].']'; 13413 } 13414 if (!TCPDF_STATIC::empty_string($this->ur['form'])) { 13415 $out .= ' /Form['.$this->ur['form'].']'; 13416 } 13417 if (!TCPDF_STATIC::empty_string($this->ur['signature'])) { 13418 $out .= ' /Signature['.$this->ur['signature'].']'; 13419 } 13420 if (!TCPDF_STATIC::empty_string($this->ur['annots'])) { 13421 $out .= ' /Annots['.$this->ur['annots'].']'; 13422 } 13423 if (!TCPDF_STATIC::empty_string($this->ur['ef'])) { 13424 $out .= ' /EF['.$this->ur['ef'].']'; 13425 } 13426 if (!TCPDF_STATIC::empty_string($this->ur['formex'])) { 13427 $out .= ' /FormEX['.$this->ur['formex'].']'; 13428 } 13429 } 13430 $out .= ' >>'; // close TransformParams 13431 // optional digest data (values must be calculated and replaced later) 13432 //$out .= ' /Data ********** 0 R'; 13433 //$out .= ' /DigestMethod/MD5'; 13434 //$out .= ' /DigestLocation[********** 34]'; 13435 //$out .= ' /DigestValue<********************************>'; 13436 $out .= ' >>'; 13437 $out .= ' ]'; // end of reference 13438 } 13439 if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) { 13440 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid); 13441 } 13442 if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) { 13443 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid); 13444 } 13445 if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) { 13446 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid); 13447 } 13448 if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) { 13449 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid); 13450 } 13451 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp); 13452 $out .= ' >>'; 13453 $out .= "\n".'endobj'; 13454 $this->_out($out); 13455 } 13456 13457 /** 13458 * Set User's Rights for PDF Reader 13459 * WARNING: This is experimental and currently do not work. 13460 * Check the PDF Reference 8.7.1 Transform Methods, 13461 * Table 8.105 Entries in the UR transform parameters dictionary 13462 * @param $enable (boolean) if true enable user's rights on PDF reader 13463 * @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. 13464 * @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. 13465 * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 13466 * @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. 13467 * @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 13468 Names specifying additional embedded-files-related usage rights for the document. 13469 * @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. 13470 * @public 13471 * @author Nicola Asuni 13472 * @since 2.9.000 (2008-03-26) 13473 */ 13474 public function setUserRights( 13475 $enable=true, 13476 $document='/FullSave', 13477 $annots='/Create/Delete/Modify/Copy/Import/Export', 13478 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', 13479 $signature='/Modify', 13480 $ef='/Create/Delete/Modify/Import', 13481 $formex='') { 13482 $this->ur['enabled'] = $enable; 13483 $this->ur['document'] = $document; 13484 $this->ur['annots'] = $annots; 13485 $this->ur['form'] = $form; 13486 $this->ur['signature'] = $signature; 13487 $this->ur['ef'] = $ef; 13488 $this->ur['formex'] = $formex; 13489 if (!$this->sign) { 13490 $this->setSignature('', '', '', '', 0, array()); 13491 } 13492 } 13493 13494 /** 13495 * Enable document signature (requires the OpenSSL Library). 13496 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader. 13497 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt 13498 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 13499 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes 13500 * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://') 13501 * @param $private_key (mixed) private key (string or filename prefixed with 'file://') 13502 * @param $private_key_password (string) password 13503 * @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. 13504 * @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. 13505 * @param $info (array) array of option information: Name, Location, Reason, ContactInfo. 13506 * @param $approval (string) Enable approval signature eg. for PDF incremental update 13507 * @public 13508 * @author Nicola Asuni 13509 * @since 4.6.005 (2009-04-24) 13510 */ 13511 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') { 13512 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt 13513 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 13514 // to convert pfx certificate to pem: openssl 13515 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes 13516 $this->sign = true; 13517 ++$this->n; 13518 $this->sig_obj_id = $this->n; // signature widget 13519 ++$this->n; // signature object ($this->sig_obj_id + 1) 13520 $this->signature_data = array(); 13521 if (strlen($signing_cert) == 0) { 13522 $this->Error('Please provide a certificate file and password!'); 13523 } 13524 if (strlen($private_key) == 0) { 13525 $private_key = $signing_cert; 13526 } 13527 $this->signature_data['signcert'] = $signing_cert; 13528 $this->signature_data['privkey'] = $private_key; 13529 $this->signature_data['password'] = $private_key_password; 13530 $this->signature_data['extracerts'] = $extracerts; 13531 $this->signature_data['cert_type'] = $cert_type; 13532 $this->signature_data['info'] = $info; 13533 $this->signature_data['approval'] = $approval; 13534 } 13535 13536 /** 13537 * Set the digital signature appearance (a cliccable rectangle area to get signature properties) 13538 * @param $x (float) Abscissa of the upper-left corner. 13539 * @param $y (float) Ordinate of the upper-left corner. 13540 * @param $w (float) Width of the signature area. 13541 * @param $h (float) Height of the signature area. 13542 * @param $page (int) option page number (if < 0 the current page is used). 13543 * @param $name (string) Name of the signature. 13544 * @public 13545 * @author Nicola Asuni 13546 * @since 5.3.011 (2010-06-17) 13547 */ 13548 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13549 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name); 13550 } 13551 13552 /** 13553 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties) 13554 * @param $x (float) Abscissa of the upper-left corner. 13555 * @param $y (float) Ordinate of the upper-left corner. 13556 * @param $w (float) Width of the signature area. 13557 * @param $h (float) Height of the signature area. 13558 * @param $page (int) option page number (if < 0 the current page is used). 13559 * @param $name (string) Name of the signature. 13560 * @public 13561 * @author Nicola Asuni 13562 * @since 5.9.101 (2011-07-06) 13563 */ 13564 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13565 ++$this->n; 13566 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name); 13567 } 13568 13569 /** 13570 * Get the array that defines the signature appearance (page and rectangle coordinates). 13571 * @param $x (float) Abscissa of the upper-left corner. 13572 * @param $y (float) Ordinate of the upper-left corner. 13573 * @param $w (float) Width of the signature area. 13574 * @param $h (float) Height of the signature area. 13575 * @param $page (int) option page number (if < 0 the current page is used). 13576 * @param $name (string) Name of the signature. 13577 * @return (array) Array defining page and rectangle coordinates of signature appearance. 13578 * @protected 13579 * @author Nicola Asuni 13580 * @since 5.9.101 (2011-07-06) 13581 */ 13582 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') { 13583 $sigapp = array(); 13584 if (($page < 1) OR ($page > $this->numpages)) { 13585 $sigapp['page'] = $this->page; 13586 } else { 13587 $sigapp['page'] = intval($page); 13588 } 13589 if (empty($name)) { 13590 $sigapp['name'] = 'Signature'; 13591 } else { 13592 $sigapp['name'] = $name; 13593 } 13594 $a = $x * $this->k; 13595 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k); 13596 $c = $w * $this->k; 13597 $d = $h * $this->k; 13598 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d)); 13599 return $sigapp; 13600 } 13601 13602 /** 13603 * Enable document timestamping (requires the OpenSSL Library). 13604 * The trusted timestamping improve document security that means that no one should be able to change the document once it has been recorded. 13605 * Use with digital signature only! 13606 * @param $tsa_host (string) Time Stamping Authority (TSA) server (prefixed with 'https://') 13607 * @param $tsa_username (string) Specifies the username for TSA authorization (optional) OR specifies the TSA authorization PEM file (see: example_66.php, optional) 13608 * @param $tsa_password (string) Specifies the password for TSA authorization (optional) 13609 * @param $tsa_cert (string) Specifies the location of TSA certificate for authorization (optional for cURL) 13610 * @public 13611 * @author Richard Stockinger 13612 * @since 6.0.090 (2014-06-16) 13613 */ 13614 public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') { 13615 $this->tsa_data = array(); 13616 if (!function_exists('curl_init')) { 13617 $this->Error('Please enable cURL PHP extension!'); 13618 } 13619 if (strlen($tsa_host) == 0) { 13620 $this->Error('Please specify the host of Time Stamping Authority (TSA)!'); 13621 } 13622 $this->tsa_data['tsa_host'] = $tsa_host; 13623 if (is_file($tsa_username)) { 13624 $this->tsa_data['tsa_auth'] = $tsa_username; 13625 } else { 13626 $this->tsa_data['tsa_username'] = $tsa_username; 13627 } 13628 $this->tsa_data['tsa_password'] = $tsa_password; 13629 $this->tsa_data['tsa_cert'] = $tsa_cert; 13630 $this->tsa_timestamp = true; 13631 } 13632 13633 /** 13634 * NOT YET IMPLEMENTED 13635 * Request TSA for a timestamp 13636 * @param $signature (string) Digital signature as binary string 13637 * @return (string) Timestamped digital signature 13638 * @protected 13639 * @author Richard Stockinger 13640 * @since 6.0.090 (2014-06-16) 13641 */ 13642 protected function applyTSA($signature) { 13643 if (!$this->tsa_timestamp) { 13644 return $signature; 13645 } 13646 //@TODO: implement this feature 13647 return $signature; 13648 } 13649 13650 /** 13651 * Create a new page group. 13652 * NOTE: call this function before calling AddPage() 13653 * @param $page (int) starting group page (leave empty for next page). 13654 * @public 13655 * @since 3.0.000 (2008-03-27) 13656 */ 13657 public function startPageGroup($page='') { 13658 if (empty($page)) { 13659 $page = $this->page + 1; 13660 } 13661 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1; 13662 } 13663 13664 /** 13665 * Set the starting page number. 13666 * @param $num (int) Starting page number. 13667 * @since 5.9.093 (2011-06-16) 13668 * @public 13669 */ 13670 public function setStartingPageNumber($num=1) { 13671 $this->starting_page_number = max(0, intval($num)); 13672 } 13673 13674 /** 13675 * Returns the string alias used right align page numbers. 13676 * If the current font is unicode type, the returned string wil contain an additional open curly brace. 13677 * @return string 13678 * @since 5.9.099 (2011-06-27) 13679 * @public 13680 */ 13681 public function getAliasRightShift() { 13682 // calculate aproximatively the ratio between widths of aliases and replacements. 13683 $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}'; 13684 $rep = str_repeat(' ', $this->GetNumChars($ref)); 13685 $wrep = $this->GetStringWidth($rep); 13686 if ($wrep > 0) { 13687 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep)); 13688 } else { 13689 $wdiff = 1; 13690 } 13691 $sdiff = sprintf('%F', $wdiff); 13692 $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}'; 13693 if ($this->isUnicodeFont()) { 13694 $alias = '{'.$alias; 13695 } 13696 return $alias; 13697 } 13698 13699 /** 13700 * Returns the string alias used for the total number of pages. 13701 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13702 * This alias will be replaced by the total number of pages in the document. 13703 * @return string 13704 * @since 4.0.018 (2008-08-08) 13705 * @public 13706 */ 13707 public function getAliasNbPages() { 13708 if ($this->isUnicodeFont()) { 13709 return '{'.TCPDF_STATIC::$alias_tot_pages.'}'; 13710 } 13711 return TCPDF_STATIC::$alias_tot_pages; 13712 } 13713 13714 /** 13715 * Returns the string alias used for the page number. 13716 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13717 * This alias will be replaced by the page number. 13718 * @return string 13719 * @since 4.5.000 (2009-01-02) 13720 * @public 13721 */ 13722 public function getAliasNumPage() { 13723 if ($this->isUnicodeFont()) { 13724 return '{'.TCPDF_STATIC::$alias_num_page.'}'; 13725 } 13726 return TCPDF_STATIC::$alias_num_page; 13727 } 13728 13729 /** 13730 * Return the alias for the total number of pages in the current page group. 13731 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13732 * This alias will be replaced by the total number of pages in this group. 13733 * @return alias of the current page group 13734 * @public 13735 * @since 3.0.000 (2008-03-27) 13736 */ 13737 public function getPageGroupAlias() { 13738 if ($this->isUnicodeFont()) { 13739 return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}'; 13740 } 13741 return TCPDF_STATIC::$alias_group_tot_pages; 13742 } 13743 13744 /** 13745 * Return the alias for the page number on the current page group. 13746 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 13747 * This alias will be replaced by the page number (relative to the belonging group). 13748 * @return alias of the current page group 13749 * @public 13750 * @since 4.5.000 (2009-01-02) 13751 */ 13752 public function getPageNumGroupAlias() { 13753 if ($this->isUnicodeFont()) { 13754 return '{'.TCPDF_STATIC::$alias_group_num_page.'}'; 13755 } 13756 return TCPDF_STATIC::$alias_group_num_page; 13757 } 13758 13759 /** 13760 * Return the current page in the group. 13761 * @return current page in the group 13762 * @public 13763 * @since 3.0.000 (2008-03-27) 13764 */ 13765 public function getGroupPageNo() { 13766 return $this->pagegroups[$this->currpagegroup]; 13767 } 13768 13769 /** 13770 * Returns the current group page number formatted as a string. 13771 * @public 13772 * @since 4.3.003 (2008-11-18) 13773 * @see PaneNo(), formatPageNumber() 13774 */ 13775 public function getGroupPageNoFormatted() { 13776 return TCPDF_STATIC::formatPageNumber($this->getGroupPageNo()); 13777 } 13778 13779 /** 13780 * Returns the current page number formatted as a string. 13781 * @public 13782 * @since 4.2.005 (2008-11-06) 13783 * @see PaneNo(), formatPageNumber() 13784 */ 13785 public function PageNoFormatted() { 13786 return TCPDF_STATIC::formatPageNumber($this->PageNo()); 13787 } 13788 13789 /** 13790 * Put pdf layers. 13791 * @protected 13792 * @since 3.0.000 (2008-03-27) 13793 */ 13794 protected function _putocg() { 13795 if (empty($this->pdflayers)) { 13796 return; 13797 } 13798 foreach ($this->pdflayers as $key => $layer) { 13799 $this->pdflayers[$key]['objid'] = $this->_newobj(); 13800 $out = '<< /Type /OCG'; 13801 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']); 13802 $out .= ' /Usage <<'; 13803 if (isset($layer['print']) AND ($layer['print'] !== NULL)) { 13804 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>'; 13805 } 13806 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>'; 13807 $out .= ' >> >>'; 13808 $out .= "\n".'endobj'; 13809 $this->_out($out); 13810 } 13811 } 13812 13813 /** 13814 * Start a new pdf layer. 13815 * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name. 13816 * @param $print (boolean|null) Set to TRUE to print this layer, FALSE to not print and NULL to not set this option 13817 * @param $view (boolean) Set to true to view this layer. 13818 * @param $lock (boolean) If true lock the layer 13819 * @public 13820 * @since 5.9.102 (2011-07-13) 13821 */ 13822 public function startLayer($name='', $print=true, $view=true, $lock=true) { 13823 if ($this->state != 2) { 13824 return; 13825 } 13826 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1)); 13827 if (empty($name)) { 13828 $name = $layer; 13829 } else { 13830 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name); 13831 } 13832 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock); 13833 $this->openMarkedContent = true; 13834 $this->_out('/OC /'.$layer.' BDC'); 13835 } 13836 13837 /** 13838 * End the current PDF layer. 13839 * @public 13840 * @since 5.9.102 (2011-07-13) 13841 */ 13842 public function endLayer() { 13843 if ($this->state != 2) { 13844 return; 13845 } 13846 if ($this->openMarkedContent) { 13847 // close existing open marked-content layer 13848 $this->_out('EMC'); 13849 $this->openMarkedContent = false; 13850 } 13851 } 13852 13853 /** 13854 * Set the visibility of the successive elements. 13855 * This can be useful, for instance, to put a background 13856 * image or color that will show on screen but won't print. 13857 * @param $v (string) visibility mode. Legal values are: all, print, screen or view. 13858 * @public 13859 * @since 3.0.000 (2008-03-27) 13860 */ 13861 public function setVisibility($v) { 13862 if ($this->state != 2) { 13863 return; 13864 } 13865 $this->endLayer(); 13866 switch($v) { 13867 case 'print': { 13868 $this->startLayer('Print', true, false); 13869 break; 13870 } 13871 case 'view': 13872 case 'screen': { 13873 $this->startLayer('View', false, true); 13874 break; 13875 } 13876 case 'all': { 13877 $this->_out(''); 13878 break; 13879 } 13880 default: { 13881 $this->Error('Incorrect visibility: '.$v); 13882 break; 13883 } 13884 } 13885 } 13886 13887 /** 13888 * Add transparency parameters to the current extgstate 13889 * @param $parms (array) parameters 13890 * @return the number of extgstates 13891 * @protected 13892 * @since 3.0.000 (2008-03-27) 13893 */ 13894 protected function addExtGState($parms) { 13895 if ($this->pdfa_mode) { 13896 // transparencies are not allowed in PDF/A mode 13897 return; 13898 } 13899 // check if this ExtGState already exist 13900 foreach ($this->extgstates as $i => $ext) { 13901 if ($ext['parms'] == $parms) { 13902 if ($this->inxobj) { 13903 // we are inside an XObject template 13904 $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext; 13905 } 13906 // return reference to existing ExtGState 13907 return $i; 13908 } 13909 } 13910 $n = (count($this->extgstates) + 1); 13911 $this->extgstates[$n] = array('parms' => $parms); 13912 if ($this->inxobj) { 13913 // we are inside an XObject template 13914 $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n]; 13915 } 13916 return $n; 13917 } 13918 13919 /** 13920 * Add an extgstate 13921 * @param $gs (array) extgstate 13922 * @protected 13923 * @since 3.0.000 (2008-03-27) 13924 */ 13925 protected function setExtGState($gs) { 13926 if ($this->pdfa_mode OR ($this->state != 2)) { 13927 // transparency is not allowed in PDF/A mode 13928 return; 13929 } 13930 $this->_out(sprintf('/GS%d gs', $gs)); 13931 } 13932 13933 /** 13934 * Put extgstates for object transparency 13935 * @protected 13936 * @since 3.0.000 (2008-03-27) 13937 */ 13938 protected function _putextgstates() { 13939 foreach ($this->extgstates as $i => $ext) { 13940 $this->extgstates[$i]['n'] = $this->_newobj(); 13941 $out = '<< /Type /ExtGState'; 13942 foreach ($ext['parms'] as $k => $v) { 13943 if (is_float($v)) { 13944 $v = sprintf('%F', $v); 13945 } elseif ($v === true) { 13946 $v = 'true'; 13947 } elseif ($v === false) { 13948 $v = 'false'; 13949 } 13950 $out .= ' /'.$k.' '.$v; 13951 } 13952 $out .= ' >>'; 13953 $out .= "\n".'endobj'; 13954 $this->_out($out); 13955 } 13956 } 13957 13958 /** 13959 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations. 13960 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 13961 * @param $stroking (boolean) If true apply overprint for stroking operations. 13962 * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking. 13963 * @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). 13964 * @public 13965 * @since 5.9.152 (2012-03-23) 13966 */ 13967 public function setOverprint($stroking=true, $nonstroking='', $mode=0) { 13968 if ($this->state != 2) { 13969 return; 13970 } 13971 $stroking = $stroking ? true : false; 13972 if (TCPDF_STATIC::empty_string($nonstroking)) { 13973 // default value if not set 13974 $nonstroking = $stroking; 13975 } else { 13976 $nonstroking = $nonstroking ? true : false; 13977 } 13978 if (($mode != 0) AND ($mode != 1)) { 13979 $mode = 0; 13980 } 13981 $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode); 13982 $gs = $this->addExtGState($this->overprint); 13983 $this->setExtGState($gs); 13984 } 13985 13986 /** 13987 * Get the overprint mode array (OP, op, OPM). 13988 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 13989 * @return array. 13990 * @public 13991 * @since 5.9.152 (2012-03-23) 13992 */ 13993 public function getOverprint() { 13994 return $this->overprint; 13995 } 13996 13997 /** 13998 * Set alpha for stroking (CA) and non-stroking (ca) operations. 13999 * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque). 14000 * @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 14001 * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque). 14002 * @param $ais (boolean) 14003 * @public 14004 * @since 3.0.000 (2008-03-27) 14005 */ 14006 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) { 14007 if ($this->pdfa_mode) { 14008 // transparency is not allowed in PDF/A mode 14009 return; 14010 } 14011 $stroking = floatval($stroking); 14012 if (TCPDF_STATIC::empty_string($nonstroking)) { 14013 // default value if not set 14014 $nonstroking = $stroking; 14015 } else { 14016 $nonstroking = floatval($nonstroking); 14017 } 14018 if ($bm[0] == '/') { 14019 // remove trailing slash 14020 $bm = substr($bm, 1); 14021 } 14022 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) { 14023 $bm = 'Normal'; 14024 } 14025 $ais = $ais ? true : false; 14026 $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais); 14027 $gs = $this->addExtGState($this->alpha); 14028 $this->setExtGState($gs); 14029 } 14030 14031 /** 14032 * Get the alpha mode array (CA, ca, BM, AIS). 14033 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). 14034 * @return array. 14035 * @public 14036 * @since 5.9.152 (2012-03-23) 14037 */ 14038 public function getAlpha() { 14039 return $this->alpha; 14040 } 14041 14042 /** 14043 * Set the default JPEG compression quality (1-100) 14044 * @param $quality (int) JPEG quality, integer between 1 and 100 14045 * @public 14046 * @since 3.0.000 (2008-03-27) 14047 */ 14048 public function setJPEGQuality($quality) { 14049 if (($quality < 1) OR ($quality > 100)) { 14050 $quality = 75; 14051 } 14052 $this->jpeg_quality = intval($quality); 14053 } 14054 14055 /** 14056 * Set the default number of columns in a row for HTML tables. 14057 * @param $cols (int) number of columns 14058 * @public 14059 * @since 3.0.014 (2008-06-04) 14060 */ 14061 public function setDefaultTableColumns($cols=4) { 14062 $this->default_table_columns = intval($cols); 14063 } 14064 14065 /** 14066 * Set the height of the cell (line height) respect the font height. 14067 * @param $h (int) cell proportion respect font height (typical value = 1.25). 14068 * @public 14069 * @since 3.0.014 (2008-06-04) 14070 */ 14071 public function setCellHeightRatio($h) { 14072 $this->cell_height_ratio = $h; 14073 } 14074 14075 /** 14076 * return the height of cell repect font height. 14077 * @public 14078 * @since 4.0.012 (2008-07-24) 14079 */ 14080 public function getCellHeightRatio() { 14081 return $this->cell_height_ratio; 14082 } 14083 14084 /** 14085 * Set the PDF version (check PDF reference for valid values). 14086 * @param $version (string) PDF document version. 14087 * @public 14088 * @since 3.1.000 (2008-06-09) 14089 */ 14090 public function setPDFVersion($version='1.7') { 14091 if ($this->pdfa_mode && $this->pdfa_version == 1 ) { 14092 // PDF/A mode 14093 $this->PDFVersion = '1.4'; 14094 } else { 14095 $this->PDFVersion = $version; 14096 } 14097 } 14098 14099 /** 14100 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print. 14101 * (see Section 8.1 of PDF reference, "Viewer Preferences"). 14102 * <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> 14103 * @param $preferences (array) array of options. 14104 * @author Nicola Asuni 14105 * @public 14106 * @since 3.1.000 (2008-06-09) 14107 */ 14108 public function setViewerPreferences($preferences) { 14109 $this->viewer_preferences = $preferences; 14110 } 14111 14112 /** 14113 * Paints color transition registration bars 14114 * @param $x (float) abscissa of the top left corner of the rectangle. 14115 * @param $y (float) ordinate of the top left corner of the rectangle. 14116 * @param $w (float) width of the rectangle. 14117 * @param $h (float) height of the rectangle. 14118 * @param $transition (boolean) if true prints tcolor transitions to white. 14119 * @param $vertical (boolean) if true prints bar vertically. 14120 * @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. 14121 * @author Nicola Asuni 14122 * @since 4.9.000 (2010-03-26) 14123 * @public 14124 */ 14125 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') { 14126 if (strpos($colors, 'ALLSPOT') !== false) { 14127 // expand spot colors 14128 $spot_colors = ''; 14129 foreach ($this->spot_colors as $spot_color_name => $v) { 14130 $spot_colors .= ','.$spot_color_name; 14131 } 14132 if (!empty($spot_colors)) { 14133 $spot_colors = substr($spot_colors, 1); 14134 $colors = str_replace('ALLSPOT', $spot_colors, $colors); 14135 } else { 14136 $colors = str_replace('ALLSPOT', 'NONE', $colors); 14137 } 14138 } 14139 $bars = explode(',', $colors); 14140 $numbars = count($bars); // number of bars to print 14141 if ($numbars <= 0) { 14142 return; 14143 } 14144 // set bar measures 14145 if ($vertical) { 14146 $coords = array(0, 0, 0, 1); 14147 $wb = $w / $numbars; // bar width 14148 $hb = $h; // bar height 14149 $xd = $wb; // delta x 14150 $yd = 0; // delta y 14151 } else { 14152 $coords = array(1, 0, 0, 0); 14153 $wb = $w; // bar width 14154 $hb = $h / $numbars; // bar height 14155 $xd = 0; // delta x 14156 $yd = $hb; // delta y 14157 } 14158 $xb = $x; 14159 $yb = $y; 14160 foreach ($bars as $col) { 14161 switch ($col) { 14162 // set transition colors 14163 case 'A': { // BLACK (GRAYSCALE) 14164 $col_a = array(255); 14165 $col_b = array(0); 14166 break; 14167 } 14168 case 'W': { // WHITE (GRAYSCALE) 14169 $col_a = array(0); 14170 $col_b = array(255); 14171 break; 14172 } 14173 case 'R': { // RED (RGB) 14174 $col_a = array(255,255,255); 14175 $col_b = array(255,0,0); 14176 break; 14177 } 14178 case 'G': { // GREEN (RGB) 14179 $col_a = array(255,255,255); 14180 $col_b = array(0,255,0); 14181 break; 14182 } 14183 case 'B': { // BLUE (RGB) 14184 $col_a = array(255,255,255); 14185 $col_b = array(0,0,255); 14186 break; 14187 } 14188 case 'C': { // CYAN (CMYK) 14189 $col_a = array(0,0,0,0); 14190 $col_b = array(100,0,0,0); 14191 break; 14192 } 14193 case 'M': { // MAGENTA (CMYK) 14194 $col_a = array(0,0,0,0); 14195 $col_b = array(0,100,0,0); 14196 break; 14197 } 14198 case 'Y': { // YELLOW (CMYK) 14199 $col_a = array(0,0,0,0); 14200 $col_b = array(0,0,100,0); 14201 break; 14202 } 14203 case 'K': { // KEY - BLACK (CMYK) 14204 $col_a = array(0,0,0,0); 14205 $col_b = array(0,0,0,100); 14206 break; 14207 } 14208 case 'RGB': { // BLACK REGISTRATION (RGB) 14209 $col_a = array(255,255,255); 14210 $col_b = array(0,0,0); 14211 break; 14212 } 14213 case 'CMYK': { // BLACK REGISTRATION (CMYK) 14214 $col_a = array(0,0,0,0); 14215 $col_b = array(100,100,100,100); 14216 break; 14217 } 14218 case 'ALL': { // SPOT COLOR REGISTRATION 14219 $col_a = array(0,0,0,0,'None'); 14220 $col_b = array(100,100,100,100,'All'); 14221 break; 14222 } 14223 case 'NONE': { // SKIP THIS COLOR 14224 $col_a = array(0,0,0,0,'None'); 14225 $col_b = array(0,0,0,0,'None'); 14226 break; 14227 } 14228 default: { // SPECIFIC SPOT COLOR NAME 14229 $col_a = array(0,0,0,0,'None'); 14230 $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors); 14231 if ($col_b === false) { 14232 // in case of error defaults to the registration color 14233 $col_b = array(100,100,100,100,'All'); 14234 } 14235 break; 14236 } 14237 } 14238 if ($col != 'NONE') { 14239 if ($transition) { 14240 // color gradient 14241 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords); 14242 } else { 14243 $this->SetFillColorArray($col_b); 14244 // colored rectangle 14245 $this->Rect($xb, $yb, $wb, $hb, 'F', array()); 14246 } 14247 $xb += $xd; 14248 $yb += $yd; 14249 } 14250 } 14251 } 14252 14253 /** 14254 * Paints crop marks. 14255 * @param $x (float) abscissa of the crop mark center. 14256 * @param $y (float) ordinate of the crop mark center. 14257 * @param $w (float) width of the crop mark. 14258 * @param $h (float) height of the crop mark. 14259 * @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. 14260 * @param $color (array) crop mark color (default spot registration color). 14261 * @author Nicola Asuni 14262 * @since 4.9.000 (2010-03-26) 14263 * @public 14264 */ 14265 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) { 14266 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color)); 14267 $type = strtoupper($type); 14268 $type = preg_replace('/[^A-Z\-\,]*/', '', $type); 14269 // split type in single components 14270 $type = str_replace('-', ',', $type); 14271 $type = str_replace('TL', 'T,L', $type); 14272 $type = str_replace('TR', 'T,R', $type); 14273 $type = str_replace('BL', 'F,L', $type); 14274 $type = str_replace('BR', 'F,R', $type); 14275 $type = str_replace('A', 'T,L', $type); 14276 $type = str_replace('B', 'T,R', $type); 14277 $type = str_replace('T,RO', 'BO', $type); 14278 $type = str_replace('C', 'F,L', $type); 14279 $type = str_replace('D', 'F,R', $type); 14280 $crops = explode(',', strtoupper($type)); 14281 // remove duplicates 14282 $crops = array_unique($crops); 14283 $dw = ($w / 4); // horizontal space to leave before the intersection point 14284 $dh = ($h / 4); // vertical space to leave before the intersection point 14285 foreach ($crops as $crop) { 14286 switch ($crop) { 14287 case 'T': 14288 case 'TOP': { 14289 $x1 = $x; 14290 $y1 = ($y - $h); 14291 $x2 = $x; 14292 $y2 = ($y - $dh); 14293 break; 14294 } 14295 case 'F': 14296 case 'BOTTOM': { 14297 $x1 = $x; 14298 $y1 = ($y + $dh); 14299 $x2 = $x; 14300 $y2 = ($y + $h); 14301 break; 14302 } 14303 case 'L': 14304 case 'LEFT': { 14305 $x1 = ($x - $w); 14306 $y1 = $y; 14307 $x2 = ($x - $dw); 14308 $y2 = $y; 14309 break; 14310 } 14311 case 'R': 14312 case 'RIGHT': { 14313 $x1 = ($x + $dw); 14314 $y1 = $y; 14315 $x2 = ($x + $w); 14316 $y2 = $y; 14317 break; 14318 } 14319 } 14320 $this->Line($x1, $y1, $x2, $y2); 14321 } 14322 } 14323 14324 /** 14325 * Paints a registration mark 14326 * @param $x (float) abscissa of the registration mark center. 14327 * @param $y (float) ordinate of the registration mark center. 14328 * @param $r (float) radius of the crop mark. 14329 * @param $double (boolean) if true print two concentric crop marks. 14330 * @param $cola (array) crop mark color (default spot registration color 'All'). 14331 * @param $colb (array) second crop mark color (default spot registration color 'None'). 14332 * @author Nicola Asuni 14333 * @since 4.9.000 (2010-03-26) 14334 * @public 14335 */ 14336 public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) { 14337 $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola); 14338 $this->SetFillColorArray($cola); 14339 $this->PieSector($x, $y, $r, 90, 180, 'F'); 14340 $this->PieSector($x, $y, $r, 270, 360, 'F'); 14341 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); 14342 if ($double) { 14343 $ri = $r * 0.5; 14344 $this->SetFillColorArray($colb); 14345 $this->PieSector($x, $y, $ri, 90, 180, 'F'); 14346 $this->PieSector($x, $y, $ri, 270, 360, 'F'); 14347 $this->SetFillColorArray($cola); 14348 $this->PieSector($x, $y, $ri, 0, 90, 'F'); 14349 $this->PieSector($x, $y, $ri, 180, 270, 'F'); 14350 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8); 14351 } 14352 } 14353 14354 /** 14355 * Paints a CMYK registration mark 14356 * @param $x (float) abscissa of the registration mark center. 14357 * @param $y (float) ordinate of the registration mark center. 14358 * @param $r (float) radius of the crop mark. 14359 * @author Nicola Asuni 14360 * @since 6.0.038 (2013-09-30) 14361 * @public 14362 */ 14363 public function registrationMarkCMYK($x, $y, $r) { 14364 // line width 14365 $lw = max((0.5 / $this->k),($r / 8)); 14366 // internal radius 14367 $ri = ($r * 0.6); 14368 // external radius 14369 $re = ($r * 1.3); 14370 // Cyan 14371 $this->SetFillColorArray(array(100,0,0,0)); 14372 $this->PieSector($x, $y, $ri, 270, 360, 'F'); 14373 // Magenta 14374 $this->SetFillColorArray(array(0,100,0,0)); 14375 $this->PieSector($x, $y, $ri, 0, 90, 'F'); 14376 // Yellow 14377 $this->SetFillColorArray(array(0,0,100,0)); 14378 $this->PieSector($x, $y, $ri, 90, 180, 'F'); 14379 // Key - black 14380 $this->SetFillColorArray(array(0,0,0,100)); 14381 $this->PieSector($x, $y, $ri, 180, 270, 'F'); 14382 // registration color 14383 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All')); 14384 $this->SetFillColorArray(array(100,100,100,100,'All')); 14385 // external circle 14386 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); 14387 // cross lines 14388 $this->Line($x, ($y - $re), $x, ($y - $ri)); 14389 $this->Line($x, ($y + $ri), $x, ($y + $re)); 14390 $this->Line(($x - $re), $y, ($x - $ri), $y); 14391 $this->Line(($x + $ri), $y, ($x + $re), $y); 14392 } 14393 14394 /** 14395 * Paints a linear colour gradient. 14396 * @param $x (float) abscissa of the top left corner of the rectangle. 14397 * @param $y (float) ordinate of the top left corner of the rectangle. 14398 * @param $w (float) width of the rectangle. 14399 * @param $h (float) height of the rectangle. 14400 * @param $col1 (array) first color (Grayscale, RGB or CMYK components). 14401 * @param $col2 (array) second color (Grayscale, RGB or CMYK components). 14402 * @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). 14403 * @author Andreas W\FCrmser, Nicola Asuni 14404 * @since 3.1.000 (2008-06-09) 14405 * @public 14406 */ 14407 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) { 14408 $this->Clip($x, $y, $w, $h); 14409 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 14410 } 14411 14412 /** 14413 * Paints a radial colour gradient. 14414 * @param $x (float) abscissa of the top left corner of the rectangle. 14415 * @param $y (float) ordinate of the top left corner of the rectangle. 14416 * @param $w (float) width of the rectangle. 14417 * @param $h (float) height of the rectangle. 14418 * @param $col1 (array) first color (Grayscale, RGB or CMYK components). 14419 * @param $col2 (array) second color (Grayscale, RGB or CMYK components). 14420 * @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. 14421 * @author Andreas W\FCrmser, Nicola Asuni 14422 * @since 3.1.000 (2008-06-09) 14423 * @public 14424 */ 14425 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) { 14426 $this->Clip($x, $y, $w, $h); 14427 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 14428 } 14429 14430 /** 14431 * Paints a coons patch mesh. 14432 * @param $x (float) abscissa of the top left corner of the rectangle. 14433 * @param $y (float) ordinate of the top left corner of the rectangle. 14434 * @param $w (float) width of the rectangle. 14435 * @param $h (float) height of the rectangle. 14436 * @param $col1 (array) first color (lower left corner) (RGB components). 14437 * @param $col2 (array) second color (lower right corner) (RGB components). 14438 * @param $col3 (array) third color (upper right corner) (RGB components). 14439 * @param $col4 (array) fourth color (upper left corner) (RGB components). 14440 * @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> 14441 * @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 14442 * @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 14443 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts. 14444 * @author Andreas W\FCrmser, Nicola Asuni 14445 * @since 3.1.000 (2008-06-09) 14446 * @public 14447 */ 14448 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) { 14449 if ($this->pdfa_mode OR ($this->state != 2)) { 14450 return; 14451 } 14452 $this->Clip($x, $y, $w, $h); 14453 $n = count($this->gradients) + 1; 14454 $this->gradients[$n] = array(); 14455 $this->gradients[$n]['type'] = 6; //coons patch mesh 14456 $this->gradients[$n]['coords'] = array(); 14457 $this->gradients[$n]['antialias'] = $antialias; 14458 $this->gradients[$n]['colors'] = array(); 14459 $this->gradients[$n]['transparency'] = false; 14460 //check the coords array if it is the simple array or the multi patch array 14461 if (!isset($coords[0]['f'])) { 14462 //simple array -> convert to multi patch array 14463 if (!isset($col1[1])) { 14464 $col1[1] = $col1[2] = $col1[0]; 14465 } 14466 if (!isset($col2[1])) { 14467 $col2[1] = $col2[2] = $col2[0]; 14468 } 14469 if (!isset($col3[1])) { 14470 $col3[1] = $col3[2] = $col3[0]; 14471 } 14472 if (!isset($col4[1])) { 14473 $col4[1] = $col4[2] = $col4[0]; 14474 } 14475 $patch_array[0]['f'] = 0; 14476 $patch_array[0]['points'] = $coords; 14477 $patch_array[0]['colors'][0]['r'] = $col1[0]; 14478 $patch_array[0]['colors'][0]['g'] = $col1[1]; 14479 $patch_array[0]['colors'][0]['b'] = $col1[2]; 14480 $patch_array[0]['colors'][1]['r'] = $col2[0]; 14481 $patch_array[0]['colors'][1]['g'] = $col2[1]; 14482 $patch_array[0]['colors'][1]['b'] = $col2[2]; 14483 $patch_array[0]['colors'][2]['r'] = $col3[0]; 14484 $patch_array[0]['colors'][2]['g'] = $col3[1]; 14485 $patch_array[0]['colors'][2]['b'] = $col3[2]; 14486 $patch_array[0]['colors'][3]['r'] = $col4[0]; 14487 $patch_array[0]['colors'][3]['g'] = $col4[1]; 14488 $patch_array[0]['colors'][3]['b'] = $col4[2]; 14489 } else { 14490 //multi patch array 14491 $patch_array = $coords; 14492 } 14493 $bpcd = 65535; //16 bits per coordinate 14494 //build the data stream 14495 $this->gradients[$n]['stream'] = ''; 14496 $count_patch = count($patch_array); 14497 for ($i=0; $i < $count_patch; ++$i) { 14498 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit 14499 $count_points = count($patch_array[$i]['points']); 14500 for ($j=0; $j < $count_points; ++$j) { 14501 //each point as 16 bit 14502 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd; 14503 if ($patch_array[$i]['points'][$j] < 0) { 14504 $patch_array[$i]['points'][$j] = 0; 14505 } 14506 if ($patch_array[$i]['points'][$j] > $bpcd) { 14507 $patch_array[$i]['points'][$j] = $bpcd; 14508 } 14509 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256)); 14510 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256)); 14511 } 14512 $count_cols = count($patch_array[$i]['colors']); 14513 for ($j=0; $j < $count_cols; ++$j) { 14514 //each color component as 8 bit 14515 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']); 14516 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']); 14517 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']); 14518 } 14519 } 14520 //paint the gradient 14521 $this->_out('/Sh'.$n.' sh'); 14522 //restore previous Graphic State 14523 $this->_outRestoreGraphicsState(); 14524 if ($this->inxobj) { 14525 // we are inside an XObject template 14526 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; 14527 } 14528 } 14529 14530 /** 14531 * Set a rectangular clipping area. 14532 * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode). 14533 * @param $y (float) ordinate of the top left corner of the rectangle. 14534 * @param $w (float) width of the rectangle. 14535 * @param $h (float) height of the rectangle. 14536 * @author Andreas W\FCrmser, Nicola Asuni 14537 * @since 3.1.000 (2008-06-09) 14538 * @protected 14539 */ 14540 protected function Clip($x, $y, $w, $h) { 14541 if ($this->state != 2) { 14542 return; 14543 } 14544 if ($this->rtl) { 14545 $x = $this->w - $x - $w; 14546 } 14547 //save current Graphic State 14548 $s = 'q'; 14549 //set clipping area 14550 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k); 14551 //set up transformation matrix for gradient 14552 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k); 14553 $this->_out($s); 14554 } 14555 14556 /** 14557 * Output gradient. 14558 * @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) 14559 * @param $coords (array) array of coordinates. 14560 * @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). 14561 * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value. 14562 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts. 14563 * @author Nicola Asuni 14564 * @since 3.1.000 (2008-06-09) 14565 * @public 14566 */ 14567 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) { 14568 if ($this->pdfa_mode OR ($this->state != 2)) { 14569 return; 14570 } 14571 $n = count($this->gradients) + 1; 14572 $this->gradients[$n] = array(); 14573 $this->gradients[$n]['type'] = $type; 14574 $this->gradients[$n]['coords'] = $coords; 14575 $this->gradients[$n]['antialias'] = $antialias; 14576 $this->gradients[$n]['colors'] = array(); 14577 $this->gradients[$n]['transparency'] = false; 14578 // color space 14579 $numcolspace = count($stops[0]['color']); 14580 $bcolor = array_values($background); 14581 switch($numcolspace) { 14582 case 5: // SPOT 14583 case 4: { // CMYK 14584 $this->gradients[$n]['colspace'] = 'DeviceCMYK'; 14585 if (!empty($background)) { 14586 $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100); 14587 } 14588 break; 14589 } 14590 case 3: { // RGB 14591 $this->gradients[$n]['colspace'] = 'DeviceRGB'; 14592 if (!empty($background)) { 14593 $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255); 14594 } 14595 break; 14596 } 14597 case 1: { // GRAY SCALE 14598 $this->gradients[$n]['colspace'] = 'DeviceGray'; 14599 if (!empty($background)) { 14600 $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255); 14601 } 14602 break; 14603 } 14604 } 14605 $num_stops = count($stops); 14606 $last_stop_id = $num_stops - 1; 14607 foreach ($stops as $key => $stop) { 14608 $this->gradients[$n]['colors'][$key] = array(); 14609 // offset represents a location along the gradient vector 14610 if (isset($stop['offset'])) { 14611 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset']; 14612 } else { 14613 if ($key == 0) { 14614 $this->gradients[$n]['colors'][$key]['offset'] = 0; 14615 } elseif ($key == $last_stop_id) { 14616 $this->gradients[$n]['colors'][$key]['offset'] = 1; 14617 } else { 14618 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key); 14619 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep; 14620 } 14621 } 14622 if (isset($stop['opacity'])) { 14623 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity']; 14624 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) { 14625 $this->gradients[$n]['transparency'] = true; 14626 } 14627 } else { 14628 $this->gradients[$n]['colors'][$key]['opacity'] = 1; 14629 } 14630 // exponent for the exponential interpolation function 14631 if (isset($stop['exponent'])) { 14632 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent']; 14633 } else { 14634 $this->gradients[$n]['colors'][$key]['exponent'] = 1; 14635 } 14636 // set colors 14637 $color = array_values($stop['color']); 14638 switch($numcolspace) { 14639 case 5: // SPOT 14640 case 4: { // CMYK 14641 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100); 14642 break; 14643 } 14644 case 3: { // RGB 14645 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255); 14646 break; 14647 } 14648 case 1: { // GRAY SCALE 14649 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255); 14650 break; 14651 } 14652 } 14653 } 14654 if ($this->gradients[$n]['transparency']) { 14655 // paint luminosity gradient 14656 $this->_out('/TGS'.$n.' gs'); 14657 } 14658 //paint the gradient 14659 $this->_out('/Sh'.$n.' sh'); 14660 //restore previous Graphic State 14661 $this->_outRestoreGraphicsState(); 14662 if ($this->inxobj) { 14663 // we are inside an XObject template 14664 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; 14665 } 14666 } 14667 14668 /** 14669 * Output gradient shaders. 14670 * @author Nicola Asuni 14671 * @since 3.1.000 (2008-06-09) 14672 * @protected 14673 */ 14674 function _putshaders() { 14675 if ($this->pdfa_mode) { 14676 return; 14677 } 14678 $idt = count($this->gradients); //index for transparency gradients 14679 foreach ($this->gradients as $id => $grad) { 14680 if (($grad['type'] == 2) OR ($grad['type'] == 3)) { 14681 $fc = $this->_newobj(); 14682 $out = '<<'; 14683 $out .= ' /FunctionType 3'; 14684 $out .= ' /Domain [0 1]'; 14685 $functions = ''; 14686 $bounds = ''; 14687 $encode = ''; 14688 $i = 1; 14689 $num_cols = count($grad['colors']); 14690 $lastcols = $num_cols - 1; 14691 for ($i = 1; $i < $num_cols; ++$i) { 14692 $functions .= ($fc + $i).' 0 R '; 14693 if ($i < $lastcols) { 14694 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']); 14695 } 14696 $encode .= '0 1 '; 14697 } 14698 $out .= ' /Functions ['.trim($functions).']'; 14699 $out .= ' /Bounds ['.trim($bounds).']'; 14700 $out .= ' /Encode ['.trim($encode).']'; 14701 $out .= ' >>'; 14702 $out .= "\n".'endobj'; 14703 $this->_out($out); 14704 for ($i = 1; $i < $num_cols; ++$i) { 14705 $this->_newobj(); 14706 $out = '<<'; 14707 $out .= ' /FunctionType 2'; 14708 $out .= ' /Domain [0 1]'; 14709 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']'; 14710 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']'; 14711 $out .= ' /N '.$grad['colors'][$i]['exponent']; 14712 $out .= ' >>'; 14713 $out .= "\n".'endobj'; 14714 $this->_out($out); 14715 } 14716 // set transparency functions 14717 if ($grad['transparency']) { 14718 $ft = $this->_newobj(); 14719 $out = '<<'; 14720 $out .= ' /FunctionType 3'; 14721 $out .= ' /Domain [0 1]'; 14722 $functions = ''; 14723 $i = 1; 14724 $num_cols = count($grad['colors']); 14725 for ($i = 1; $i < $num_cols; ++$i) { 14726 $functions .= ($ft + $i).' 0 R '; 14727 } 14728 $out .= ' /Functions ['.trim($functions).']'; 14729 $out .= ' /Bounds ['.trim($bounds).']'; 14730 $out .= ' /Encode ['.trim($encode).']'; 14731 $out .= ' >>'; 14732 $out .= "\n".'endobj'; 14733 $this->_out($out); 14734 for ($i = 1; $i < $num_cols; ++$i) { 14735 $this->_newobj(); 14736 $out = '<<'; 14737 $out .= ' /FunctionType 2'; 14738 $out .= ' /Domain [0 1]'; 14739 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']'; 14740 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']'; 14741 $out .= ' /N '.$grad['colors'][$i]['exponent']; 14742 $out .= ' >>'; 14743 $out .= "\n".'endobj'; 14744 $this->_out($out); 14745 } 14746 } 14747 } 14748 // set shading object 14749 $this->_newobj(); 14750 $out = '<< /ShadingType '.$grad['type']; 14751 if (isset($grad['colspace'])) { 14752 $out .= ' /ColorSpace /'.$grad['colspace']; 14753 } else { 14754 $out .= ' /ColorSpace /DeviceRGB'; 14755 } 14756 if (isset($grad['background']) AND !empty($grad['background'])) { 14757 $out .= ' /Background ['.$grad['background'].']'; 14758 } 14759 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) { 14760 $out .= ' /AntiAlias true'; 14761 } 14762 if ($grad['type'] == 2) { 14763 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]); 14764 $out .= ' /Domain [0 1]'; 14765 $out .= ' /Function '.$fc.' 0 R'; 14766 $out .= ' /Extend [true true]'; 14767 $out .= ' >>'; 14768 } elseif ($grad['type'] == 3) { 14769 //x0, y0, r0, x1, y1, r1 14770 //at this this time radius of inner circle is 0 14771 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]); 14772 $out .= ' /Domain [0 1]'; 14773 $out .= ' /Function '.$fc.' 0 R'; 14774 $out .= ' /Extend [true true]'; 14775 $out .= ' >>'; 14776 } elseif ($grad['type'] == 6) { 14777 $out .= ' /BitsPerCoordinate 16'; 14778 $out .= ' /BitsPerComponent 8'; 14779 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]'; 14780 $out .= ' /BitsPerFlag 8'; 14781 $stream = $this->_getrawstream($grad['stream']); 14782 $out .= ' /Length '.strlen($stream); 14783 $out .= ' >>'; 14784 $out .= ' stream'."\n".$stream."\n".'endstream'; 14785 } 14786 $out .= "\n".'endobj'; 14787 $this->_out($out); 14788 if ($grad['transparency']) { 14789 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out); 14790 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency); 14791 } 14792 $this->gradients[$id]['id'] = $this->n; 14793 // set pattern object 14794 $this->_newobj(); 14795 $out = '<< /Type /Pattern /PatternType 2'; 14796 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R'; 14797 $out .= ' >>'; 14798 $out .= "\n".'endobj'; 14799 $this->_out($out); 14800 $this->gradients[$id]['pattern'] = $this->n; 14801 // set shading and pattern for transparency mask 14802 if ($grad['transparency']) { 14803 // luminosity pattern 14804 $idgs = $id + $idt; 14805 $this->_newobj(); 14806 $this->_out($shading_transparency); 14807 $this->gradients[$idgs]['id'] = $this->n; 14808 $this->_newobj(); 14809 $out = '<< /Type /Pattern /PatternType 2'; 14810 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R'; 14811 $out .= ' >>'; 14812 $out .= "\n".'endobj'; 14813 $this->_out($out); 14814 $this->gradients[$idgs]['pattern'] = $this->n; 14815 // luminosity XObject 14816 $oid = $this->_newobj(); 14817 $this->xobjects['LX'.$oid] = array('n' => $oid); 14818 $filter = ''; 14819 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q'; 14820 if ($this->compress) { 14821 $filter = ' /Filter /FlateDecode'; 14822 $stream = gzcompress($stream); 14823 } 14824 $stream = $this->_getrawstream($stream); 14825 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter; 14826 $out .= ' /Length '.strlen($stream); 14827 $rect = sprintf('%F %F', $this->wPt, $this->hPt); 14828 $out .= ' /BBox [0 0 '.$rect.']'; 14829 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>'; 14830 $out .= ' /Resources <<'; 14831 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>'; 14832 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>'; 14833 $out .= ' >>'; 14834 $out .= ' >> '; 14835 $out .= ' stream'."\n".$stream."\n".'endstream'; 14836 $out .= "\n".'endobj'; 14837 $this->_out($out); 14838 // SMask 14839 $this->_newobj(); 14840 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj'; 14841 $this->_out($out); 14842 // ExtGState 14843 $this->_newobj(); 14844 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj'; 14845 $this->_out($out); 14846 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id); 14847 } 14848 } 14849 } 14850 14851 /** 14852 * Draw the sector of a circle. 14853 * It can be used for instance to render pie charts. 14854 * @param $xc (float) abscissa of the center. 14855 * @param $yc (float) ordinate of the center. 14856 * @param $r (float) radius. 14857 * @param $a (float) start angle (in degrees). 14858 * @param $b (float) end angle (in degrees). 14859 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 14860 * @param $cw: (float) indicates whether to go clockwise (default: true). 14861 * @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. 14862 * @author Maxime Delorme, Nicola Asuni 14863 * @since 3.1.000 (2008-06-09) 14864 * @public 14865 */ 14866 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) { 14867 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o); 14868 } 14869 14870 /** 14871 * Draw the sector of an ellipse. 14872 * It can be used for instance to render pie charts. 14873 * @param $xc (float) abscissa of the center. 14874 * @param $yc (float) ordinate of the center. 14875 * @param $rx (float) the x-axis radius. 14876 * @param $ry (float) the y-axis radius. 14877 * @param $a (float) start angle (in degrees). 14878 * @param $b (float) end angle (in degrees). 14879 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. 14880 * @param $cw: (float) indicates whether to go clockwise. 14881 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). 14882 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc. 14883 * @author Maxime Delorme, Nicola Asuni 14884 * @since 3.1.000 (2008-06-09) 14885 * @public 14886 */ 14887 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) { 14888 if ($this->state != 2) { 14889 return; 14890 } 14891 if ($this->rtl) { 14892 $xc = ($this->w - $xc); 14893 } 14894 $op = TCPDF_STATIC::getPathPaintOperator($style); 14895 if ($op == 'f') { 14896 $line_style = array(); 14897 } 14898 if ($cw) { 14899 $d = $b; 14900 $b = (360 - $a + $o); 14901 $a = (360 - $d + $o); 14902 } else { 14903 $b += $o; 14904 $a += $o; 14905 } 14906 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc); 14907 $this->_out($op); 14908 } 14909 14910 /** 14911 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files. 14912 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library. 14913 * Only vector drawing is supported, not text or bitmap. 14914 * 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). 14915 * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string. 14916 * @param $x (float) Abscissa of the upper-left corner. 14917 * @param $y (float) Ordinate of the upper-left corner. 14918 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 14919 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 14920 * @param $link (mixed) URL or identifier returned by AddLink(). 14921 * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true. 14922 * @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> 14923 * @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> 14924 * @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))) 14925 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions. 14926 * @param $fixoutvals (boolean) if true remove values outside the bounding box. 14927 * @author Valentin Schmidt, Nicola Asuni 14928 * @since 3.1.000 (2008-06-09) 14929 * @public 14930 */ 14931 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) { 14932 if ($this->state != 2) { 14933 return; 14934 } 14935 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { 14936 // convert EPS to raster image using GD or ImageMagick libraries 14937 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 14938 } 14939 if ($x === '') { 14940 $x = $this->x; 14941 } 14942 if ($y === '') { 14943 $y = $this->y; 14944 } 14945 // check page for no-write regions and adapt page margins if necessary 14946 list($x, $y) = $this->checkPageRegions($h, $x, $y); 14947 $k = $this->k; 14948 if ($file[0] === '@') { // image from string 14949 $data = substr($file, 1); 14950 } else { // EPS/AI file 14951 $data = $this->getCachedFileContents($file); 14952 } 14953 if ($data === FALSE) { 14954 $this->Error('EPS file not found: '.$file); 14955 } 14956 $regs = array(); 14957 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!) 14958 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator 14959 if (count($regs) > 1) { 14960 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0" 14961 if (strpos($version_str, 'Adobe Illustrator') !== false) { 14962 $versexp = explode(' ', $version_str); 14963 $version = (float)array_pop($versexp); 14964 if ($version >= 9) { 14965 $this->Error('This version of Adobe Illustrator file is not supported: '.$file); 14966 } 14967 } 14968 } 14969 // strip binary bytes in front of PS-header 14970 $start = strpos($data, '%!PS-Adobe'); 14971 if ($start > 0) { 14972 $data = substr($data, $start); 14973 } 14974 // find BoundingBox params 14975 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs); 14976 if (count($regs) > 1) { 14977 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1])); 14978 } else { 14979 $this->Error('No BoundingBox found in EPS/AI file: '.$file); 14980 } 14981 $start = strpos($data, '%%EndSetup'); 14982 if ($start === false) { 14983 $start = strpos($data, '%%EndProlog'); 14984 } 14985 if ($start === false) { 14986 $start = strpos($data, '%%BoundingBox'); 14987 } 14988 $data = substr($data, $start); 14989 $end = strpos($data, '%%PageTrailer'); 14990 if ($end===false) { 14991 $end = strpos($data, 'showpage'); 14992 } 14993 if ($end) { 14994 $data = substr($data, 0, $end); 14995 } 14996 // calculate image width and height on document 14997 if (($w <= 0) AND ($h <= 0)) { 14998 $w = ($x2 - $x1) / $k; 14999 $h = ($y2 - $y1) / $k; 15000 } elseif ($w <= 0) { 15001 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k)); 15002 } elseif ($h <= 0) { 15003 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k)); 15004 } 15005 // fit the image on available space 15006 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 15007 if ($this->rasterize_vector_images) { 15008 // convert EPS to raster image using GD or ImageMagick libraries 15009 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 15010 } 15011 // set scaling factors 15012 $scale_x = $w / (($x2 - $x1) / $k); 15013 $scale_y = $h / (($y2 - $y1) / $k); 15014 // set alignment 15015 $this->img_rb_y = $y + $h; 15016 // set alignment 15017 if ($this->rtl) { 15018 if ($palign == 'L') { 15019 $ximg = $this->lMargin; 15020 } elseif ($palign == 'C') { 15021 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15022 } elseif ($palign == 'R') { 15023 $ximg = $this->w - $this->rMargin - $w; 15024 } else { 15025 $ximg = $x - $w; 15026 } 15027 $this->img_rb_x = $ximg; 15028 } else { 15029 if ($palign == 'L') { 15030 $ximg = $this->lMargin; 15031 } elseif ($palign == 'C') { 15032 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15033 } elseif ($palign == 'R') { 15034 $ximg = $this->w - $this->rMargin - $w; 15035 } else { 15036 $ximg = $x; 15037 } 15038 $this->img_rb_x = $ximg + $w; 15039 } 15040 if ($useBoundingBox) { 15041 $dx = $ximg * $k - $x1; 15042 $dy = $y * $k - $y1; 15043 } else { 15044 $dx = $ximg * $k; 15045 $dy = $y * $k; 15046 } 15047 // save the current graphic state 15048 $this->_out('q'.$this->epsmarker); 15049 // translate 15050 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1)))); 15051 // scale 15052 if (isset($scale_x)) { 15053 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y))); 15054 } 15055 // handle pc/unix/mac line endings 15056 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY); 15057 $u=0; 15058 $cnt = count($lines); 15059 for ($i=0; $i < $cnt; ++$i) { 15060 $line = $lines[$i]; 15061 if (($line == '') OR ($line[0] == '%')) { 15062 continue; 15063 } 15064 $len = strlen($line); 15065 // check for spot color names 15066 $color_name = ''; 15067 if (strcasecmp('x', substr(trim($line), -1)) == 0) { 15068 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) { 15069 // extract spot color name 15070 $color_name = $matches[0]; 15071 // remove color name from string 15072 $line = str_replace(' '.$color_name, '', $line); 15073 // remove pharentesis from color name 15074 $color_name = substr($color_name, 1, -1); 15075 } 15076 } 15077 $chunks = explode(' ', $line); 15078 $cmd = trim(array_pop($chunks)); 15079 // RGB 15080 if (($cmd == 'Xa') OR ($cmd == 'XA')) { 15081 $b = array_pop($chunks); 15082 $g = array_pop($chunks); 15083 $r = array_pop($chunks); 15084 $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! 15085 continue; 15086 } 15087 $skip = false; 15088 if ($fixoutvals) { 15089 // check for values outside the bounding box 15090 switch ($cmd) { 15091 case 'm': 15092 case 'l': 15093 case 'L': { 15094 // skip values outside bounding box 15095 foreach ($chunks as $key => $val) { 15096 if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) { 15097 $skip = true; 15098 } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) { 15099 $skip = true; 15100 } 15101 } 15102 } 15103 } 15104 } 15105 switch ($cmd) { 15106 case 'm': 15107 case 'l': 15108 case 'v': 15109 case 'y': 15110 case 'c': 15111 case 'k': 15112 case 'K': 15113 case 'g': 15114 case 'G': 15115 case 's': 15116 case 'S': 15117 case 'J': 15118 case 'j': 15119 case 'w': 15120 case 'M': 15121 case 'd': 15122 case 'n': { 15123 if ($skip) { 15124 break; 15125 } 15126 $this->_out($line); 15127 break; 15128 } 15129 case 'x': {// custom fill color 15130 if (empty($color_name)) { 15131 // CMYK color 15132 list($col_c, $col_m, $col_y, $col_k) = $chunks; 15133 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k'); 15134 } else { 15135 // Spot Color (CMYK + tint) 15136 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; 15137 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); 15138 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t)); 15139 $this->_out($color_cmd); 15140 } 15141 break; 15142 } 15143 case 'X': { // custom stroke color 15144 if (empty($color_name)) { 15145 // CMYK color 15146 list($col_c, $col_m, $col_y, $col_k) = $chunks; 15147 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K'); 15148 } else { 15149 // Spot Color (CMYK + tint) 15150 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; 15151 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); 15152 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t)); 15153 $this->_out($color_cmd); 15154 } 15155 break; 15156 } 15157 case 'Y': 15158 case 'N': 15159 case 'V': 15160 case 'L': 15161 case 'C': { 15162 if ($skip) { 15163 break; 15164 } 15165 $line[($len - 1)] = strtolower($cmd); 15166 $this->_out($line); 15167 break; 15168 } 15169 case 'b': 15170 case 'B': { 15171 $this->_out($cmd . '*'); 15172 break; 15173 } 15174 case 'f': 15175 case 'F': { 15176 if ($u > 0) { 15177 $isU = false; 15178 $max = min(($i + 5), $cnt); 15179 for ($j = ($i + 1); $j < $max; ++$j) { 15180 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U'))); 15181 } 15182 if ($isU) { 15183 $this->_out('f*'); 15184 } 15185 } else { 15186 $this->_out('f*'); 15187 } 15188 break; 15189 } 15190 case '*u': { 15191 ++$u; 15192 break; 15193 } 15194 case '*U': { 15195 --$u; 15196 break; 15197 } 15198 } 15199 } 15200 // restore previous graphic state 15201 $this->_out($this->epsmarker.'Q'); 15202 if (!empty($border)) { 15203 $bx = $this->x; 15204 $by = $this->y; 15205 $this->x = $ximg; 15206 if ($this->rtl) { 15207 $this->x += $w; 15208 } 15209 $this->y = $y; 15210 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 15211 $this->x = $bx; 15212 $this->y = $by; 15213 } 15214 if ($link) { 15215 $this->Link($ximg, $y, $w, $h, $link, 0); 15216 } 15217 // set pointer to align the next text/objects 15218 switch($align) { 15219 case 'T':{ 15220 $this->y = $y; 15221 $this->x = $this->img_rb_x; 15222 break; 15223 } 15224 case 'M':{ 15225 $this->y = $y + round($h/2); 15226 $this->x = $this->img_rb_x; 15227 break; 15228 } 15229 case 'B':{ 15230 $this->y = $this->img_rb_y; 15231 $this->x = $this->img_rb_x; 15232 break; 15233 } 15234 case 'N':{ 15235 $this->SetY($this->img_rb_y); 15236 break; 15237 } 15238 default:{ 15239 break; 15240 } 15241 } 15242 $this->endlinex = $this->img_rb_x; 15243 } 15244 15245 /** 15246 * Set document barcode. 15247 * @param $bc (string) barcode 15248 * @public 15249 */ 15250 public function setBarcode($bc='') { 15251 $this->barcode = $bc; 15252 } 15253 15254 /** 15255 * Get current barcode. 15256 * @return string 15257 * @public 15258 * @since 4.0.012 (2008-07-24) 15259 */ 15260 public function getBarcode() { 15261 return $this->barcode; 15262 } 15263 15264 /** 15265 * Print a Linear Barcode. 15266 * @param $code (string) code to print 15267 * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats). 15268 * @param $x (int) x position in user units (empty string = current x position) 15269 * @param $y (int) y position in user units (empty string = current y position) 15270 * @param $w (int) width in user units (empty string = remaining page width) 15271 * @param $h (int) height in user units (empty string = remaining page height) 15272 * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm) 15273 * @param $style (array) array of options:<ul> 15274 * <li>boolean $style['border'] if true prints a border</li> 15275 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li> 15276 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li> 15277 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li> 15278 * <li>array $style['fgcolor'] color array for bars and text</li> 15279 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li> 15280 * <li>boolean $style['text'] if true prints text below the barcode</li> 15281 * <li>string $style['label'] override default label</li> 15282 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li> 15283 * <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> 15284 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li> 15285 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li> 15286 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li> 15287 * <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> 15288 * <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> 15289 * @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> 15290 * @author Nicola Asuni 15291 * @since 3.1.000 (2008-06-09) 15292 * @public 15293 */ 15294 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style=array(), $align='') { 15295 if (TCPDF_STATIC::empty_string(trim($code))) { 15296 return; 15297 } 15298 require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php'); 15299 // save current graphic settings 15300 $gvars = $this->getGraphicVars(); 15301 // create new barcode object 15302 $barcodeobj = new TCPDFBarcode($code, $type); 15303 $arrcode = $barcodeobj->getBarcodeArray(); 15304 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) { 15305 $this->Error('Error in 1D barcode string'); 15306 } 15307 if ($arrcode['maxh'] <= 0) { 15308 $arrcode['maxh'] = 1; 15309 } 15310 // set default values 15311 if (!isset($style['position'])) { 15312 $style['position'] = ''; 15313 } elseif ($style['position'] == 'S') { 15314 // keep this for backward compatibility 15315 $style['position'] = ''; 15316 $style['stretch'] = true; 15317 } 15318 if (!isset($style['fitwidth'])) { 15319 if (!isset($style['stretch'])) { 15320 $style['fitwidth'] = true; 15321 } else { 15322 $style['fitwidth'] = false; 15323 } 15324 } 15325 if ($style['fitwidth']) { 15326 // disable stretch 15327 $style['stretch'] = false; 15328 } 15329 if (!isset($style['stretch'])) { 15330 if (($w === '') OR ($w <= 0)) { 15331 $style['stretch'] = false; 15332 } else { 15333 $style['stretch'] = true; 15334 } 15335 } 15336 if (!isset($style['fgcolor'])) { 15337 $style['fgcolor'] = array(0,0,0); // default black 15338 } 15339 if (!isset($style['bgcolor'])) { 15340 $style['bgcolor'] = false; // default transparent 15341 } 15342 if (!isset($style['border'])) { 15343 $style['border'] = false; 15344 } 15345 $fontsize = 0; 15346 if (!isset($style['text'])) { 15347 $style['text'] = false; 15348 } 15349 if ($style['text'] AND isset($style['font'])) { 15350 if (isset($style['fontsize'])) { 15351 $fontsize = $style['fontsize']; 15352 } 15353 $this->SetFont($style['font'], '', $fontsize); 15354 } 15355 if (!isset($style['stretchtext'])) { 15356 $style['stretchtext'] = 4; 15357 } 15358 if ($x === '') { 15359 $x = $this->x; 15360 } 15361 if ($y === '') { 15362 $y = $this->y; 15363 } 15364 // check page for no-write regions and adapt page margins if necessary 15365 list($x, $y) = $this->checkPageRegions($h, $x, $y); 15366 if (($w === '') OR ($w <= 0)) { 15367 if ($this->rtl) { 15368 $w = $x - $this->lMargin; 15369 } else { 15370 $w = $this->w - $this->rMargin - $x; 15371 } 15372 } 15373 // padding 15374 if (!isset($style['padding'])) { 15375 $padding = 0; 15376 } elseif ($style['padding'] === 'auto') { 15377 $padding = 10 * ($w / ($arrcode['maxw'] + 20)); 15378 } else { 15379 $padding = floatval($style['padding']); 15380 } 15381 // horizontal padding 15382 if (!isset($style['hpadding'])) { 15383 $hpadding = $padding; 15384 } elseif ($style['hpadding'] === 'auto') { 15385 $hpadding = 10 * ($w / ($arrcode['maxw'] + 20)); 15386 } else { 15387 $hpadding = floatval($style['hpadding']); 15388 } 15389 // vertical padding 15390 if (!isset($style['vpadding'])) { 15391 $vpadding = $padding; 15392 } elseif ($style['vpadding'] === 'auto') { 15393 $vpadding = ($hpadding / 2); 15394 } else { 15395 $vpadding = floatval($style['vpadding']); 15396 } 15397 // calculate xres (single bar width) 15398 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw']; 15399 if ($style['stretch']) { 15400 $xres = $max_xres; 15401 } else { 15402 if (TCPDF_STATIC::empty_string($xres)) { 15403 $xres = (0.141 * $this->k); // default bar width = 0.4 mm 15404 } 15405 if ($xres > $max_xres) { 15406 // correct xres to fit on $w 15407 $xres = $max_xres; 15408 } 15409 if ((isset($style['padding']) AND ($style['padding'] === 'auto')) 15410 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) { 15411 $hpadding = 10 * $xres; 15412 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) { 15413 $vpadding = ($hpadding / 2); 15414 } 15415 } 15416 } 15417 if ($style['fitwidth']) { 15418 $wold = $w; 15419 $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding)); 15420 if (isset($style['cellfitalign'])) { 15421 switch ($style['cellfitalign']) { 15422 case 'L': { 15423 if ($this->rtl) { 15424 $x -= ($wold - $w); 15425 } 15426 break; 15427 } 15428 case 'R': { 15429 if (!$this->rtl) { 15430 $x += ($wold - $w); 15431 } 15432 break; 15433 } 15434 case 'C': { 15435 if ($this->rtl) { 15436 $x -= (($wold - $w) / 2); 15437 } else { 15438 $x += (($wold - $w) / 2); 15439 } 15440 break; 15441 } 15442 default : { 15443 break; 15444 } 15445 } 15446 } 15447 } 15448 $text_height = $this->getCellHeight($fontsize / $this->k); 15449 // height 15450 if (($h === '') OR ($h <= 0)) { 15451 // set default height 15452 $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height; 15453 } 15454 $barh = $h - $text_height - (2 * $vpadding); 15455 if ($barh <=0) { 15456 // try to reduce font or padding to fit barcode on available height 15457 if ($text_height > $h) { 15458 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio)); 15459 $text_height = $this->getCellHeight($fontsize / $this->k); 15460 $this->SetFont($style['font'], '', $fontsize); 15461 } 15462 if ($vpadding > 0) { 15463 $vpadding = (($h - $text_height) / 4); 15464 } 15465 $barh = $h - $text_height - (2 * $vpadding); 15466 } 15467 // fit the barcode on available space 15468 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); 15469 // set alignment 15470 $this->img_rb_y = $y + $h; 15471 // set alignment 15472 if ($this->rtl) { 15473 if ($style['position'] == 'L') { 15474 $xpos = $this->lMargin; 15475 } elseif ($style['position'] == 'C') { 15476 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15477 } elseif ($style['position'] == 'R') { 15478 $xpos = $this->w - $this->rMargin - $w; 15479 } else { 15480 $xpos = $x - $w; 15481 } 15482 $this->img_rb_x = $xpos; 15483 } else { 15484 if ($style['position'] == 'L') { 15485 $xpos = $this->lMargin; 15486 } elseif ($style['position'] == 'C') { 15487 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15488 } elseif ($style['position'] == 'R') { 15489 $xpos = $this->w - $this->rMargin - $w; 15490 } else { 15491 $xpos = $x; 15492 } 15493 $this->img_rb_x = $xpos + $w; 15494 } 15495 $xpos_rect = $xpos; 15496 if (!isset($style['align'])) { 15497 $style['align'] = 'C'; 15498 } 15499 switch ($style['align']) { 15500 case 'L': { 15501 $xpos = $xpos_rect + $hpadding; 15502 break; 15503 } 15504 case 'R': { 15505 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding; 15506 break; 15507 } 15508 case 'C': 15509 default : { 15510 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2); 15511 break; 15512 } 15513 } 15514 $xpos_text = $xpos; 15515 // barcode is always printed in LTR direction 15516 $tempRTL = $this->rtl; 15517 $this->rtl = false; 15518 // print background color 15519 if ($style['bgcolor']) { 15520 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 15521 } elseif ($style['border']) { 15522 $this->Rect($xpos_rect, $y, $w, $h, 'D'); 15523 } 15524 // set foreground color 15525 $this->SetDrawColorArray($style['fgcolor']); 15526 $this->SetTextColorArray($style['fgcolor']); 15527 // print bars 15528 foreach ($arrcode['bcode'] as $k => $v) { 15529 $bw = ($v['w'] * $xres); 15530 if ($v['t']) { 15531 // draw a vertical bar 15532 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']); 15533 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']); 15534 } 15535 $xpos += $bw; 15536 } 15537 // print text 15538 if ($style['text']) { 15539 if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) { 15540 $label = $style['label']; 15541 } else { 15542 $label = $code; 15543 } 15544 $txtwidth = ($arrcode['maxw'] * $xres); 15545 if ($this->GetStringWidth($label) > $txtwidth) { 15546 $style['stretchtext'] = 2; 15547 } 15548 // print text 15549 $this->x = $xpos_text; 15550 $this->y = $y + $vpadding + $barh; 15551 $cellpadding = $this->cell_padding; 15552 $this->SetCellPadding(0); 15553 $this->Cell($txtwidth, 0, $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T'); 15554 $this->cell_padding = $cellpadding; 15555 } 15556 // restore original direction 15557 $this->rtl = $tempRTL; 15558 // restore previous settings 15559 $this->setGraphicVars($gvars); 15560 // set pointer to align the next text/objects 15561 switch($align) { 15562 case 'T':{ 15563 $this->y = $y; 15564 $this->x = $this->img_rb_x; 15565 break; 15566 } 15567 case 'M':{ 15568 $this->y = $y + round($h / 2); 15569 $this->x = $this->img_rb_x; 15570 break; 15571 } 15572 case 'B':{ 15573 $this->y = $this->img_rb_y; 15574 $this->x = $this->img_rb_x; 15575 break; 15576 } 15577 case 'N':{ 15578 $this->SetY($this->img_rb_y); 15579 break; 15580 } 15581 default:{ 15582 break; 15583 } 15584 } 15585 $this->endlinex = $this->img_rb_x; 15586 } 15587 15588 /** 15589 * Print 2D Barcode. 15590 * @param $code (string) code to print 15591 * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats). 15592 * @param $x (int) x position in user units 15593 * @param $y (int) y position in user units 15594 * @param $w (int) width in user units 15595 * @param $h (int) height in user units 15596 * @param $style (array) array of options:<ul> 15597 * <li>boolean $style['border'] if true prints a border around the barcode</li> 15598 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li> 15599 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li> 15600 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li> 15601 * <li>int $style['module_width'] width of a single module in points</li> 15602 * <li>int $style['module_height'] height of a single module in points</li> 15603 * <li>array $style['fgcolor'] color array for bars and text</li> 15604 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li> 15605 * <li>string $style['position'] barcode position on the page: L = left margin; C = center; R = right margin; S = stretch</li> 15606 * @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> 15607 * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio 15608 * @author Nicola Asuni 15609 * @since 4.5.037 (2009-04-07) 15610 * @public 15611 */ 15612 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style=array(), $align='', $distort=false) { 15613 if (TCPDF_STATIC::empty_string(trim($code))) { 15614 return; 15615 } 15616 require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php'); 15617 // save current graphic settings 15618 $gvars = $this->getGraphicVars(); 15619 // create new barcode object 15620 $barcodeobj = new TCPDF2DBarcode($code, $type); 15621 $arrcode = $barcodeobj->getBarcodeArray(); 15622 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)) { 15623 $this->Error('Error in 2D barcode string'); 15624 } 15625 // set default values 15626 if (!isset($style['position'])) { 15627 $style['position'] = ''; 15628 } 15629 if (!isset($style['fgcolor'])) { 15630 $style['fgcolor'] = array(0,0,0); // default black 15631 } 15632 if (!isset($style['bgcolor'])) { 15633 $style['bgcolor'] = false; // default transparent 15634 } 15635 if (!isset($style['border'])) { 15636 $style['border'] = false; 15637 } 15638 // padding 15639 if (!isset($style['padding'])) { 15640 $style['padding'] = 0; 15641 } elseif ($style['padding'] === 'auto') { 15642 $style['padding'] = 4; 15643 } 15644 if (!isset($style['hpadding'])) { 15645 $style['hpadding'] = $style['padding']; 15646 } elseif ($style['hpadding'] === 'auto') { 15647 $style['hpadding'] = 4; 15648 } 15649 if (!isset($style['vpadding'])) { 15650 $style['vpadding'] = $style['padding']; 15651 } elseif ($style['vpadding'] === 'auto') { 15652 $style['vpadding'] = 4; 15653 } 15654 $hpad = (2 * $style['hpadding']); 15655 $vpad = (2 * $style['vpadding']); 15656 // cell (module) dimension 15657 if (!isset($style['module_width'])) { 15658 $style['module_width'] = 1; // width of a single module in points 15659 } 15660 if (!isset($style['module_height'])) { 15661 $style['module_height'] = 1; // height of a single module in points 15662 } 15663 if ($x === '') { 15664 $x = $this->x; 15665 } 15666 if ($y === '') { 15667 $y = $this->y; 15668 } 15669 // check page for no-write regions and adapt page margins if necessary 15670 list($x, $y) = $this->checkPageRegions($h, $x, $y); 15671 // number of barcode columns and rows 15672 $rows = $arrcode['num_rows']; 15673 $cols = $arrcode['num_cols']; 15674 if (($rows <= 0) || ($cols <= 0)){ 15675 $this->Error('Error in 2D barcode string'); 15676 } 15677 // module width and height 15678 $mw = $style['module_width']; 15679 $mh = $style['module_height']; 15680 if (($mw <= 0) OR ($mh <= 0)) { 15681 $this->Error('Error in 2D barcode string'); 15682 } 15683 // get max dimensions 15684 if ($this->rtl) { 15685 $maxw = $x - $this->lMargin; 15686 } else { 15687 $maxw = $this->w - $this->rMargin - $x; 15688 } 15689 $maxh = ($this->h - $this->tMargin - $this->bMargin); 15690 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad)); 15691 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad)); 15692 if (!$distort) { 15693 if (($maxw * $ratioHW) > $maxh) { 15694 $maxw = $maxh * $ratioWH; 15695 } 15696 if (($maxh * $ratioWH) > $maxw) { 15697 $maxh = $maxw * $ratioHW; 15698 } 15699 } 15700 // set maximum dimensions 15701 if ($w > $maxw) { 15702 $w = $maxw; 15703 } 15704 if ($h > $maxh) { 15705 $h = $maxh; 15706 } 15707 // set dimensions 15708 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) { 15709 $w = ($cols + $hpad) * ($mw / $this->k); 15710 $h = ($rows + $vpad) * ($mh / $this->k); 15711 } elseif (($w === '') OR ($w <= 0)) { 15712 $w = $h * $ratioWH; 15713 } elseif (($h === '') OR ($h <= 0)) { 15714 $h = $w * $ratioHW; 15715 } 15716 // barcode size (excluding padding) 15717 $bw = ($w * $cols) / ($cols + $hpad); 15718 $bh = ($h * $rows) / ($rows + $vpad); 15719 // dimension of single barcode cell unit 15720 $cw = $bw / $cols; 15721 $ch = $bh / $rows; 15722 if (!$distort) { 15723 if (($cw / $ch) > ($mw / $mh)) { 15724 // correct horizontal distortion 15725 $cw = $ch * $mw / $mh; 15726 $bw = $cw * $cols; 15727 $style['hpadding'] = ($w - $bw) / (2 * $cw); 15728 } else { 15729 // correct vertical distortion 15730 $ch = $cw * $mh / $mw; 15731 $bh = $ch * $rows; 15732 $style['vpadding'] = ($h - $bh) / (2 * $ch); 15733 } 15734 } 15735 // fit the barcode on available space 15736 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); 15737 // set alignment 15738 $this->img_rb_y = $y + $h; 15739 // set alignment 15740 if ($this->rtl) { 15741 if ($style['position'] == 'L') { 15742 $xpos = $this->lMargin; 15743 } elseif ($style['position'] == 'C') { 15744 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15745 } elseif ($style['position'] == 'R') { 15746 $xpos = $this->w - $this->rMargin - $w; 15747 } else { 15748 $xpos = $x - $w; 15749 } 15750 $this->img_rb_x = $xpos; 15751 } else { 15752 if ($style['position'] == 'L') { 15753 $xpos = $this->lMargin; 15754 } elseif ($style['position'] == 'C') { 15755 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 15756 } elseif ($style['position'] == 'R') { 15757 $xpos = $this->w - $this->rMargin - $w; 15758 } else { 15759 $xpos = $x; 15760 } 15761 $this->img_rb_x = $xpos + $w; 15762 } 15763 $xstart = $xpos + ($style['hpadding'] * $cw); 15764 $ystart = $y + ($style['vpadding'] * $ch); 15765 // barcode is always printed in LTR direction 15766 $tempRTL = $this->rtl; 15767 $this->rtl = false; 15768 // print background color 15769 if ($style['bgcolor']) { 15770 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 15771 } elseif ($style['border']) { 15772 $this->Rect($xpos, $y, $w, $h, 'D'); 15773 } 15774 // set foreground color 15775 $this->SetDrawColorArray($style['fgcolor']); 15776 // print barcode cells 15777 // for each row 15778 for ($r = 0; $r < $rows; ++$r) { 15779 $xr = $xstart; 15780 // for each column 15781 for ($c = 0; $c < $cols; ++$c) { 15782 if ($arrcode['bcode'][$r][$c] == 1) { 15783 // draw a single barcode cell 15784 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']); 15785 } 15786 $xr += $cw; 15787 } 15788 $ystart += $ch; 15789 } 15790 // restore original direction 15791 $this->rtl = $tempRTL; 15792 // restore previous settings 15793 $this->setGraphicVars($gvars); 15794 // set pointer to align the next text/objects 15795 switch($align) { 15796 case 'T':{ 15797 $this->y = $y; 15798 $this->x = $this->img_rb_x; 15799 break; 15800 } 15801 case 'M':{ 15802 $this->y = $y + round($h/2); 15803 $this->x = $this->img_rb_x; 15804 break; 15805 } 15806 case 'B':{ 15807 $this->y = $this->img_rb_y; 15808 $this->x = $this->img_rb_x; 15809 break; 15810 } 15811 case 'N':{ 15812 $this->SetY($this->img_rb_y); 15813 break; 15814 } 15815 default:{ 15816 break; 15817 } 15818 } 15819 $this->endlinex = $this->img_rb_x; 15820 } 15821 15822 /** 15823 * Returns an array containing current margins: 15824 * <ul> 15825 <li>$ret['left'] = left margin</li> 15826 <li>$ret['right'] = right margin</li> 15827 <li>$ret['top'] = top margin</li> 15828 <li>$ret['bottom'] = bottom margin</li> 15829 <li>$ret['header'] = header margin</li> 15830 <li>$ret['footer'] = footer margin</li> 15831 <li>$ret['cell'] = cell padding array</li> 15832 <li>$ret['padding_left'] = cell left padding</li> 15833 <li>$ret['padding_top'] = cell top padding</li> 15834 <li>$ret['padding_right'] = cell right padding</li> 15835 <li>$ret['padding_bottom'] = cell bottom padding</li> 15836 * </ul> 15837 * @return array containing all margins measures 15838 * @public 15839 * @since 3.2.000 (2008-06-23) 15840 */ 15841 public function getMargins() { 15842 $ret = array( 15843 'left' => $this->lMargin, 15844 'right' => $this->rMargin, 15845 'top' => $this->tMargin, 15846 'bottom' => $this->bMargin, 15847 'header' => $this->header_margin, 15848 'footer' => $this->footer_margin, 15849 'cell' => $this->cell_padding, 15850 'padding_left' => $this->cell_padding['L'], 15851 'padding_top' => $this->cell_padding['T'], 15852 'padding_right' => $this->cell_padding['R'], 15853 'padding_bottom' => $this->cell_padding['B'] 15854 ); 15855 return $ret; 15856 } 15857 15858 /** 15859 * Returns an array containing original margins: 15860 * <ul> 15861 <li>$ret['left'] = left margin</li> 15862 <li>$ret['right'] = right margin</li> 15863 * </ul> 15864 * @return array containing all margins measures 15865 * @public 15866 * @since 4.0.012 (2008-07-24) 15867 */ 15868 public function getOriginalMargins() { 15869 $ret = array( 15870 'left' => $this->original_lMargin, 15871 'right' => $this->original_rMargin 15872 ); 15873 return $ret; 15874 } 15875 15876 /** 15877 * Returns the current font size. 15878 * @return current font size 15879 * @public 15880 * @since 3.2.000 (2008-06-23) 15881 */ 15882 public function getFontSize() { 15883 return $this->FontSize; 15884 } 15885 15886 /** 15887 * Returns the current font size in points unit. 15888 * @return current font size in points unit 15889 * @public 15890 * @since 3.2.000 (2008-06-23) 15891 */ 15892 public function getFontSizePt() { 15893 return $this->FontSizePt; 15894 } 15895 15896 /** 15897 * Returns the current font family name. 15898 * @return string current font family name 15899 * @public 15900 * @since 4.3.008 (2008-12-05) 15901 */ 15902 public function getFontFamily() { 15903 return $this->FontFamily; 15904 } 15905 15906 /** 15907 * Returns the current font style. 15908 * @return string current font style 15909 * @public 15910 * @since 4.3.008 (2008-12-05) 15911 */ 15912 public function getFontStyle() { 15913 return $this->FontStyle; 15914 } 15915 15916 /** 15917 * Cleanup HTML code (requires HTML Tidy library). 15918 * @param $html (string) htmlcode to fix 15919 * @param $default_css (string) CSS commands to add 15920 * @param $tagvs (array) parameters for setHtmlVSpace method 15921 * @param $tidy_options (array) options for tidy_parse_string function 15922 * @return string XHTML code cleaned up 15923 * @author Nicola Asuni 15924 * @public 15925 * @since 5.9.017 (2010-11-16) 15926 * @see setHtmlVSpace() 15927 */ 15928 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') { 15929 return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces); 15930 } 15931 15932 /** 15933 * Returns the border width from CSS property 15934 * @param $width (string) border width 15935 * @return int with in user units 15936 * @protected 15937 * @since 5.7.000 (2010-08-02) 15938 */ 15939 protected function getCSSBorderWidth($width) { 15940 if ($width == 'thin') { 15941 $width = (2 / $this->k); 15942 } elseif ($width == 'medium') { 15943 $width = (4 / $this->k); 15944 } elseif ($width == 'thick') { 15945 $width = (6 / $this->k); 15946 } else { 15947 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false); 15948 } 15949 return $width; 15950 } 15951 15952 /** 15953 * Returns the border dash style from CSS property 15954 * @param $style (string) border style to convert 15955 * @return int sash style (return -1 in case of none or hidden border) 15956 * @protected 15957 * @since 5.7.000 (2010-08-02) 15958 */ 15959 protected function getCSSBorderDashStyle($style) { 15960 switch (strtolower($style)) { 15961 case 'none': 15962 case 'hidden': { 15963 $dash = -1; 15964 break; 15965 } 15966 case 'dotted': { 15967 $dash = 1; 15968 break; 15969 } 15970 case 'dashed': { 15971 $dash = 3; 15972 break; 15973 } 15974 case 'double': 15975 case 'groove': 15976 case 'ridge': 15977 case 'inset': 15978 case 'outset': 15979 case 'solid': 15980 default: { 15981 $dash = 0; 15982 break; 15983 } 15984 } 15985 return $dash; 15986 } 15987 15988 /** 15989 * Returns the border style array from CSS border properties 15990 * @param $cssborder (string) border properties 15991 * @return array containing border properties 15992 * @protected 15993 * @since 5.7.000 (2010-08-02) 15994 */ 15995 protected function getCSSBorderStyle($cssborder) { 15996 $bprop = preg_split('/[\s]+/', trim($cssborder)); 15997 $border = array(); // value to be returned 15998 switch (count($bprop)) { 15999 case 3: { 16000 $width = $bprop[0]; 16001 $style = $bprop[1]; 16002 $color = $bprop[2]; 16003 break; 16004 } 16005 case 2: { 16006 $width = 'medium'; 16007 $style = $bprop[0]; 16008 $color = $bprop[1]; 16009 break; 16010 } 16011 case 1: { 16012 $width = 'medium'; 16013 $style = $bprop[0]; 16014 $color = 'black'; 16015 break; 16016 } 16017 default: { 16018 $width = 'medium'; 16019 $style = 'solid'; 16020 $color = 'black'; 16021 break; 16022 } 16023 } 16024 if ($style == 'none') { 16025 return array(); 16026 } 16027 $border['cap'] = 'square'; 16028 $border['join'] = 'miter'; 16029 $border['dash'] = $this->getCSSBorderDashStyle($style); 16030 if ($border['dash'] < 0) { 16031 return array(); 16032 } 16033 $border['width'] = $this->getCSSBorderWidth($width); 16034 $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors); 16035 return $border; 16036 } 16037 16038 /** 16039 * Get the internal Cell padding from CSS attribute. 16040 * @param $csspadding (string) padding properties 16041 * @param $width (float) width of the containing element 16042 * @return array of cell paddings 16043 * @public 16044 * @since 5.9.000 (2010-10-04) 16045 */ 16046 public function getCSSPadding($csspadding, $width=0) { 16047 $padding = preg_split('/[\s]+/', trim($csspadding)); 16048 $cell_padding = array(); // value to be returned 16049 switch (count($padding)) { 16050 case 4: { 16051 $cell_padding['T'] = $padding[0]; 16052 $cell_padding['R'] = $padding[1]; 16053 $cell_padding['B'] = $padding[2]; 16054 $cell_padding['L'] = $padding[3]; 16055 break; 16056 } 16057 case 3: { 16058 $cell_padding['T'] = $padding[0]; 16059 $cell_padding['R'] = $padding[1]; 16060 $cell_padding['B'] = $padding[2]; 16061 $cell_padding['L'] = $padding[1]; 16062 break; 16063 } 16064 case 2: { 16065 $cell_padding['T'] = $padding[0]; 16066 $cell_padding['R'] = $padding[1]; 16067 $cell_padding['B'] = $padding[0]; 16068 $cell_padding['L'] = $padding[1]; 16069 break; 16070 } 16071 case 1: { 16072 $cell_padding['T'] = $padding[0]; 16073 $cell_padding['R'] = $padding[0]; 16074 $cell_padding['B'] = $padding[0]; 16075 $cell_padding['L'] = $padding[0]; 16076 break; 16077 } 16078 default: { 16079 return $this->cell_padding; 16080 } 16081 } 16082 if ($width == 0) { 16083 $width = $this->w - $this->lMargin - $this->rMargin; 16084 } 16085 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false); 16086 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false); 16087 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false); 16088 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false); 16089 return $cell_padding; 16090 } 16091 16092 /** 16093 * Get the internal Cell margin from CSS attribute. 16094 * @param $cssmargin (string) margin properties 16095 * @param $width (float) width of the containing element 16096 * @return array of cell margins 16097 * @public 16098 * @since 5.9.000 (2010-10-04) 16099 */ 16100 public function getCSSMargin($cssmargin, $width=0) { 16101 $margin = preg_split('/[\s]+/', trim($cssmargin)); 16102 $cell_margin = array(); // value to be returned 16103 switch (count($margin)) { 16104 case 4: { 16105 $cell_margin['T'] = $margin[0]; 16106 $cell_margin['R'] = $margin[1]; 16107 $cell_margin['B'] = $margin[2]; 16108 $cell_margin['L'] = $margin[3]; 16109 break; 16110 } 16111 case 3: { 16112 $cell_margin['T'] = $margin[0]; 16113 $cell_margin['R'] = $margin[1]; 16114 $cell_margin['B'] = $margin[2]; 16115 $cell_margin['L'] = $margin[1]; 16116 break; 16117 } 16118 case 2: { 16119 $cell_margin['T'] = $margin[0]; 16120 $cell_margin['R'] = $margin[1]; 16121 $cell_margin['B'] = $margin[0]; 16122 $cell_margin['L'] = $margin[1]; 16123 break; 16124 } 16125 case 1: { 16126 $cell_margin['T'] = $margin[0]; 16127 $cell_margin['R'] = $margin[0]; 16128 $cell_margin['B'] = $margin[0]; 16129 $cell_margin['L'] = $margin[0]; 16130 break; 16131 } 16132 default: { 16133 return $this->cell_margin; 16134 } 16135 } 16136 if ($width == 0) { 16137 $width = $this->w - $this->lMargin - $this->rMargin; 16138 } 16139 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false); 16140 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false); 16141 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false); 16142 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false); 16143 return $cell_margin; 16144 } 16145 16146 /** 16147 * Get the border-spacing from CSS attribute. 16148 * @param $cssbspace (string) border-spacing CSS properties 16149 * @param $width (float) width of the containing element 16150 * @return array of border spacings 16151 * @public 16152 * @since 5.9.010 (2010-10-27) 16153 */ 16154 public function getCSSBorderMargin($cssbspace, $width=0) { 16155 $space = preg_split('/[\s]+/', trim($cssbspace)); 16156 $border_spacing = array(); // value to be returned 16157 switch (count($space)) { 16158 case 2: { 16159 $border_spacing['H'] = $space[0]; 16160 $border_spacing['V'] = $space[1]; 16161 break; 16162 } 16163 case 1: { 16164 $border_spacing['H'] = $space[0]; 16165 $border_spacing['V'] = $space[0]; 16166 break; 16167 } 16168 default: { 16169 return array('H' => 0, 'V' => 0); 16170 } 16171 } 16172 if ($width == 0) { 16173 $width = $this->w - $this->lMargin - $this->rMargin; 16174 } 16175 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false); 16176 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false); 16177 return $border_spacing; 16178 } 16179 16180 /** 16181 * Returns the letter-spacing value from CSS value 16182 * @param $spacing (string) letter-spacing value 16183 * @param $parent (float) font spacing (tracking) value of the parent element 16184 * @return float quantity to increases or decreases the space between characters in a text. 16185 * @protected 16186 * @since 5.9.000 (2010-10-02) 16187 */ 16188 protected function getCSSFontSpacing($spacing, $parent=0) { 16189 $val = 0; // value to be returned 16190 $spacing = trim($spacing); 16191 switch ($spacing) { 16192 case 'normal': { 16193 $val = 0; 16194 break; 16195 } 16196 case 'inherit': { 16197 if ($parent == 'normal') { 16198 $val = 0; 16199 } else { 16200 $val = $parent; 16201 } 16202 break; 16203 } 16204 default: { 16205 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false); 16206 } 16207 } 16208 return $val; 16209 } 16210 16211 /** 16212 * Returns the percentage of font stretching from CSS value 16213 * @param $stretch (string) stretch mode 16214 * @param $parent (float) stretch value of the parent element 16215 * @return float font stretching percentage 16216 * @protected 16217 * @since 5.9.000 (2010-10-02) 16218 */ 16219 protected function getCSSFontStretching($stretch, $parent=100) { 16220 $val = 100; // value to be returned 16221 $stretch = trim($stretch); 16222 switch ($stretch) { 16223 case 'ultra-condensed': { 16224 $val = 40; 16225 break; 16226 } 16227 case 'extra-condensed': { 16228 $val = 55; 16229 break; 16230 } 16231 case 'condensed': { 16232 $val = 70; 16233 break; 16234 } 16235 case 'semi-condensed': { 16236 $val = 85; 16237 break; 16238 } 16239 case 'normal': { 16240 $val = 100; 16241 break; 16242 } 16243 case 'semi-expanded': { 16244 $val = 115; 16245 break; 16246 } 16247 case 'expanded': { 16248 $val = 130; 16249 break; 16250 } 16251 case 'extra-expanded': { 16252 $val = 145; 16253 break; 16254 } 16255 case 'ultra-expanded': { 16256 $val = 160; 16257 break; 16258 } 16259 case 'wider': { 16260 $val = ($parent + 10); 16261 break; 16262 } 16263 case 'narrower': { 16264 $val = ($parent - 10); 16265 break; 16266 } 16267 case 'inherit': { 16268 if ($parent == 'normal') { 16269 $val = 100; 16270 } else { 16271 $val = $parent; 16272 } 16273 break; 16274 } 16275 default: { 16276 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false); 16277 } 16278 } 16279 return $val; 16280 } 16281 16282 /** 16283 * Convert HTML string containing font size value to points 16284 * @param $val (string) String containing font size value and unit. 16285 * @param $refsize (float) Reference font size in points. 16286 * @param $parent_size (float) Parent font size in points. 16287 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). 16288 * @return float value in points 16289 * @public 16290 */ 16291 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') { 16292 $refsize = TCPDF_FONTS::getFontRefSize($refsize); 16293 $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize); 16294 switch ($val) { 16295 case 'xx-small': { 16296 $size = ($refsize - 4); 16297 break; 16298 } 16299 case 'x-small': { 16300 $size = ($refsize - 3); 16301 break; 16302 } 16303 case 'small': { 16304 $size = ($refsize - 2); 16305 break; 16306 } 16307 case 'medium': { 16308 $size = $refsize; 16309 break; 16310 } 16311 case 'large': { 16312 $size = ($refsize + 2); 16313 break; 16314 } 16315 case 'x-large': { 16316 $size = ($refsize + 4); 16317 break; 16318 } 16319 case 'xx-large': { 16320 $size = ($refsize + 6); 16321 break; 16322 } 16323 case 'smaller': { 16324 $size = ($parent_size - 3); 16325 break; 16326 } 16327 case 'larger': { 16328 $size = ($parent_size + 3); 16329 break; 16330 } 16331 default: { 16332 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true); 16333 } 16334 } 16335 return $size; 16336 } 16337 16338 /** 16339 * Returns the HTML DOM array. 16340 * @param $html (string) html code 16341 * @return array 16342 * @protected 16343 * @since 3.2.000 (2008-06-20) 16344 */ 16345 protected function getHtmlDomArray($html) { 16346 // array of CSS styles ( selector => properties). 16347 $css = array(); 16348 // get CSS array defined at previous call 16349 $matches = array(); 16350 if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) { 16351 if (isset($matches[1][0])) { 16352 $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true)); 16353 } 16354 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html); 16355 } 16356 // extract external CSS files 16357 $matches = array(); 16358 if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) { 16359 foreach ($matches[1] as $key => $link) { 16360 $type = array(); 16361 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) { 16362 $type = array(); 16363 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type); 16364 // get 'all' and 'print' media, other media types are discarded 16365 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 16366 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { 16367 $type = array(); 16368 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) { 16369 // read CSS data file 16370 $cssdata = $this->getCachedFileContents(trim($type[1])); 16371 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) { 16372 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata)); 16373 } 16374 } 16375 } 16376 } 16377 } 16378 } 16379 // extract style tags 16380 $matches = array(); 16381 if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) { 16382 foreach ($matches[1] as $key => $media) { 16383 $type = array(); 16384 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type); 16385 // get 'all' and 'print' media, other media types are discarded 16386 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 16387 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { 16388 $cssdata = $matches[2][$key]; 16389 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata)); 16390 } 16391 } 16392 } 16393 // create a special tag to contain the CSS array (used for table content) 16394 $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>'; 16395 // remove head and style blocks 16396 $html = preg_replace('/<head([^\>]*)>(.*)?<\/head>/siU', '', $html); 16397 $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html); 16398 // define block tags 16399 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td'); 16400 // define self-closing tags 16401 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta'); 16402 // remove all unsupported tags (the line below lists all supported tags) 16403 $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>'); 16404 //replace some blank characters 16405 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag 16406 $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); 16407 $html = preg_replace('@(\r\n|\r)@', "\n", $html); 16408 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); 16409 $html = strtr($html, $repTable); 16410 $offset = 0; 16411 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) { 16412 $html_a = substr($html, 0, $offset); 16413 $html_b = substr($html, $offset, ($pos - $offset + 6)); 16414 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) { 16415 // preserve newlines on <pre> tag 16416 $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b); 16417 } 16418 while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) { 16419 // preserve spaces on <pre> tag 16420 $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2 \\3</pre>", $html_b); 16421 } 16422 $html = $html_a.$html_b.substr($html, $pos + 6); 16423 $offset = strlen($html_a.$html_b); 16424 } 16425 $offset = 0; 16426 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) { 16427 $html_a = substr($html, 0, $offset); 16428 $html_b = substr($html, $offset, ($pos - $offset + 11)); 16429 while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) { 16430 // preserve newlines on <textarea> tag 16431 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b); 16432 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b); 16433 } 16434 $html = $html_a.$html_b.substr($html, $pos + 11); 16435 $offset = strlen($html_a.$html_b); 16436 } 16437 $html = preg_replace('/([\s]*)<option/si', '<option', $html); 16438 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html); 16439 $offset = 0; 16440 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) { 16441 $html_a = substr($html, 0, $offset); 16442 $html_b = substr($html, $offset, ($pos - $offset + 9)); 16443 while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) { 16444 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b); 16445 $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b); 16446 } 16447 $html = $html_a.$html_b.substr($html, $pos + 9); 16448 $offset = strlen($html_a.$html_b); 16449 } 16450 if (preg_match("'</select'si", $html)) { 16451 $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html); 16452 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html); 16453 } 16454 $html = str_replace("\n", ' ', $html); 16455 // restore textarea newlines 16456 $html = str_replace('<TBR>', "\n", $html); 16457 // remove extra spaces from code 16458 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html); 16459 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html); 16460 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html); 16461 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html); 16462 $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); 16463 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html); 16464 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html); 16465 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html); 16466 $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1> \\2', $html); 16467 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html); 16468 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag 16469 $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html); 16470 $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1> </li>', $html); 16471 $html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1"> </font><img', $html); 16472 $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1> ', $html); // preserve some spaces 16473 $html = preg_replace('/[\s]<\/([^\>]*)>/', ' </\\1>', $html); // preserve some spaces 16474 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment 16475 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment 16476 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space 16477 // trim string 16478 $html = $this->stringTrim($html); 16479 // fix br tag after li 16480 $html = preg_replace('/<li><br([^\>]*)>/', '<li> <br\\1>', $html); 16481 // fix first image tag alignment 16482 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1); 16483 // pattern for generic tag 16484 $tagpattern = '/(<[^>]+>)/'; 16485 // explodes the string 16486 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 16487 // count elements 16488 $maxel = count($a); 16489 $elkey = 0; 16490 $key = 0; 16491 // create an array of elements 16492 $dom = array(); 16493 $dom[$key] = array(); 16494 // set inheritable properties fot the first void element 16495 // 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 16496 $dom[$key]['tag'] = false; 16497 $dom[$key]['block'] = false; 16498 $dom[$key]['value'] = ''; 16499 $dom[$key]['parent'] = 0; 16500 $dom[$key]['hide'] = false; 16501 $dom[$key]['fontname'] = $this->FontFamily; 16502 $dom[$key]['fontstyle'] = $this->FontStyle; 16503 $dom[$key]['fontsize'] = $this->FontSizePt; 16504 $dom[$key]['font-stretch'] = $this->font_stretching; 16505 $dom[$key]['letter-spacing'] = $this->font_spacing; 16506 $dom[$key]['stroke'] = $this->textstrokewidth; 16507 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0); 16508 $dom[$key]['clip'] = ($this->textrendermode > 3); 16509 $dom[$key]['line-height'] = $this->cell_height_ratio; 16510 $dom[$key]['bgcolor'] = false; 16511 $dom[$key]['fgcolor'] = $this->fgcolor; // color 16512 $dom[$key]['strokecolor'] = $this->strokecolor; 16513 $dom[$key]['align'] = ''; 16514 $dom[$key]['listtype'] = ''; 16515 $dom[$key]['text-indent'] = 0; 16516 $dom[$key]['text-transform'] = ''; 16517 $dom[$key]['border'] = array(); 16518 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr'; 16519 $thead = false; // true when we are inside the THEAD tag 16520 ++$key; 16521 $level = array(); 16522 array_push($level, 0); // root 16523 while ($elkey < $maxel) { 16524 $dom[$key] = array(); 16525 $element = $a[$elkey]; 16526 $dom[$key]['elkey'] = $elkey; 16527 if (preg_match($tagpattern, $element)) { 16528 // html tag 16529 $element = substr($element, 1, -1); 16530 // get tag name 16531 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); 16532 $tagname = strtolower($tag[1]); 16533 // check if we are inside a table header 16534 if ($tagname == 'thead') { 16535 if ($element[0] == '/') { 16536 $thead = false; 16537 } else { 16538 $thead = true; 16539 } 16540 ++$elkey; 16541 continue; 16542 } 16543 $dom[$key]['tag'] = true; 16544 $dom[$key]['value'] = $tagname; 16545 if (in_array($dom[$key]['value'], $blocktags)) { 16546 $dom[$key]['block'] = true; 16547 } else { 16548 $dom[$key]['block'] = false; 16549 } 16550 if ($element[0] == '/') { 16551 // *** closing html tag 16552 $dom[$key]['opening'] = false; 16553 $dom[$key]['parent'] = end($level); 16554 array_pop($level); 16555 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide']; 16556 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname']; 16557 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle']; 16558 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize']; 16559 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch']; 16560 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing']; 16561 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke']; 16562 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill']; 16563 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip']; 16564 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height']; 16565 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor']; 16566 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor']; 16567 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor']; 16568 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align']; 16569 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform']; 16570 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir']; 16571 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) { 16572 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype']; 16573 } 16574 // set the number of columns in table tag 16575 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) { 16576 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols']; 16577 } 16578 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 16579 $dom[($dom[$key]['parent'])]['content'] = $csstagarray; 16580 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) { 16581 $dom[($dom[$key]['parent'])]['content'] .= stripslashes($a[$dom[$i]['elkey']]); 16582 } 16583 $key = $i; 16584 // mark nested tables 16585 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']); 16586 // remove thead sections from nested tables 16587 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']); 16588 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']); 16589 } 16590 // store header rows on a new table 16591 if ( 16592 ($dom[$key]['value'] === 'tr') 16593 && !empty($dom[($dom[$key]['parent'])]['thead']) 16594 && ($dom[($dom[$key]['parent'])]['thead'] === true) 16595 ) { 16596 if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) { 16597 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']]; 16598 } 16599 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) { 16600 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']]; 16601 } 16602 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) { 16603 $dom[($dom[$key]['parent'])]['attribute'] = array(); 16604 } 16605 // header elements must be always contained in a single page 16606 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true'; 16607 } 16608 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) { 16609 // remove the nobr attributes from the table header 16610 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']); 16611 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>'; 16612 } 16613 } else { 16614 // *** opening or self-closing html tag 16615 $dom[$key]['opening'] = true; 16616 $dom[$key]['parent'] = end($level); 16617 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) { 16618 // self-closing tag 16619 $dom[$key]['self'] = true; 16620 } else { 16621 // opening tag 16622 array_push($level, $key); 16623 $dom[$key]['self'] = false; 16624 } 16625 // copy some values from parent 16626 $parentkey = 0; 16627 if ($key > 0) { 16628 $parentkey = $dom[$key]['parent']; 16629 $dom[$key]['hide'] = $dom[$parentkey]['hide']; 16630 $dom[$key]['fontname'] = $dom[$parentkey]['fontname']; 16631 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle']; 16632 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize']; 16633 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch']; 16634 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing']; 16635 $dom[$key]['stroke'] = $dom[$parentkey]['stroke']; 16636 $dom[$key]['fill'] = $dom[$parentkey]['fill']; 16637 $dom[$key]['clip'] = $dom[$parentkey]['clip']; 16638 $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; 16639 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor']; 16640 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor']; 16641 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor']; 16642 $dom[$key]['align'] = $dom[$parentkey]['align']; 16643 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 16644 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 16645 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform']; 16646 $dom[$key]['border'] = array(); 16647 $dom[$key]['dir'] = $dom[$parentkey]['dir']; 16648 } 16649 // get attributes 16650 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER); 16651 $dom[$key]['attribute'] = array(); // reset attribute array 16652 foreach($attr_array[1] as $id => $name) { 16653 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; 16654 } 16655 if (!empty($css)) { 16656 // merge CSS style to current style 16657 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css); 16658 $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']); 16659 } 16660 // split style attributes 16661 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) { 16662 // get style attributes 16663 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); 16664 $dom[$key]['style'] = array(); // reset style attribute array 16665 foreach($style_array[1] as $id => $name) { 16666 // in case of duplicate attribute the last replace the previous 16667 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); 16668 } 16669 // --- get some style attributes --- 16670 // text direction 16671 if (isset($dom[$key]['style']['direction'])) { 16672 $dom[$key]['dir'] = $dom[$key]['style']['direction']; 16673 } 16674 // display 16675 if (isset($dom[$key]['style']['display'])) { 16676 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none'); 16677 } 16678 // font family 16679 if (isset($dom[$key]['style']['font-family'])) { 16680 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']); 16681 } 16682 // list-style-type 16683 if (isset($dom[$key]['style']['list-style-type'])) { 16684 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type'])); 16685 if ($dom[$key]['listtype'] == 'inherit') { 16686 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 16687 } 16688 } 16689 // text-indent 16690 if (isset($dom[$key]['style']['text-indent'])) { 16691 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']); 16692 if ($dom[$key]['text-indent'] == 'inherit') { 16693 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 16694 } 16695 } 16696 // text-transform 16697 if (isset($dom[$key]['style']['text-transform'])) { 16698 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform']; 16699 } 16700 // font size 16701 if (isset($dom[$key]['style']['font-size'])) { 16702 $fsize = trim($dom[$key]['style']['font-size']); 16703 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt'); 16704 } 16705 // font-stretch 16706 if (isset($dom[$key]['style']['font-stretch'])) { 16707 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']); 16708 } 16709 // letter-spacing 16710 if (isset($dom[$key]['style']['letter-spacing'])) { 16711 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']); 16712 } 16713 // line-height (internally is the cell height ratio) 16714 if (isset($dom[$key]['style']['line-height'])) { 16715 $lineheight = trim($dom[$key]['style']['line-height']); 16716 switch ($lineheight) { 16717 // A normal line height. This is default 16718 case 'normal': { 16719 $dom[$key]['line-height'] = $dom[0]['line-height']; 16720 break; 16721 } 16722 case 'inherit': { 16723 $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; 16724 } 16725 default: { 16726 if (is_numeric($lineheight)) { 16727 // convert to percentage of font height 16728 $lineheight = ($lineheight * 100).'%'; 16729 } 16730 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true); 16731 if (substr($lineheight, -1) !== '%') { 16732 if ($dom[$key]['fontsize'] <= 0) { 16733 $dom[$key]['line-height'] = 1; 16734 } else { 16735 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']); 16736 } 16737 } 16738 } 16739 } 16740 } 16741 // font style 16742 if (isset($dom[$key]['style']['font-weight'])) { 16743 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') { 16744 if (strpos($dom[$key]['fontstyle'], 'B') !== false) { 16745 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']); 16746 } 16747 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') { 16748 $dom[$key]['fontstyle'] .= 'B'; 16749 } 16750 } 16751 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) { 16752 $dom[$key]['fontstyle'] .= 'I'; 16753 } 16754 // font color 16755 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) { 16756 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors); 16757 } elseif ($dom[$key]['value'] == 'a') { 16758 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; 16759 } 16760 // background color 16761 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) { 16762 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors); 16763 } 16764 // text-decoration 16765 if (isset($dom[$key]['style']['text-decoration'])) { 16766 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration'])); 16767 foreach ($decors as $dec) { 16768 $dec = trim($dec); 16769 if (!TCPDF_STATIC::empty_string($dec)) { 16770 if ($dec[0] == 'u') { 16771 // underline 16772 $dom[$key]['fontstyle'] .= 'U'; 16773 } elseif ($dec[0] == 'l') { 16774 // line-through 16775 $dom[$key]['fontstyle'] .= 'D'; 16776 } elseif ($dec[0] == 'o') { 16777 // overline 16778 $dom[$key]['fontstyle'] .= 'O'; 16779 } 16780 } 16781 } 16782 } elseif ($dom[$key]['value'] == 'a') { 16783 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; 16784 } 16785 // check for width attribute 16786 if (isset($dom[$key]['style']['width'])) { 16787 $dom[$key]['width'] = $dom[$key]['style']['width']; 16788 } 16789 // check for height attribute 16790 if (isset($dom[$key]['style']['height'])) { 16791 $dom[$key]['height'] = $dom[$key]['style']['height']; 16792 } 16793 // check for text alignment 16794 if (isset($dom[$key]['style']['text-align'])) { 16795 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]); 16796 } 16797 // check for CSS border properties 16798 if (isset($dom[$key]['style']['border'])) { 16799 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']); 16800 if (!empty($borderstyle)) { 16801 $dom[$key]['border']['LTRB'] = $borderstyle; 16802 } 16803 } 16804 if (isset($dom[$key]['style']['border-color'])) { 16805 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color'])); 16806 if (isset($brd_colors[3])) { 16807 $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors); 16808 } 16809 if (isset($brd_colors[1])) { 16810 $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors); 16811 } 16812 if (isset($brd_colors[0])) { 16813 $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors); 16814 } 16815 if (isset($brd_colors[2])) { 16816 $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors); 16817 } 16818 } 16819 if (isset($dom[$key]['style']['border-width'])) { 16820 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width'])); 16821 if (isset($brd_widths[3])) { 16822 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]); 16823 } 16824 if (isset($brd_widths[1])) { 16825 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]); 16826 } 16827 if (isset($brd_widths[0])) { 16828 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]); 16829 } 16830 if (isset($brd_widths[2])) { 16831 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]); 16832 } 16833 } 16834 if (isset($dom[$key]['style']['border-style'])) { 16835 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style'])); 16836 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) { 16837 $dom[$key]['border']['L']['cap'] = 'square'; 16838 $dom[$key]['border']['L']['join'] = 'miter'; 16839 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]); 16840 if ($dom[$key]['border']['L']['dash'] < 0) { 16841 $dom[$key]['border']['L'] = array(); 16842 } 16843 } 16844 if (isset($brd_styles[1])) { 16845 $dom[$key]['border']['R']['cap'] = 'square'; 16846 $dom[$key]['border']['R']['join'] = 'miter'; 16847 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]); 16848 if ($dom[$key]['border']['R']['dash'] < 0) { 16849 $dom[$key]['border']['R'] = array(); 16850 } 16851 } 16852 if (isset($brd_styles[0])) { 16853 $dom[$key]['border']['T']['cap'] = 'square'; 16854 $dom[$key]['border']['T']['join'] = 'miter'; 16855 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]); 16856 if ($dom[$key]['border']['T']['dash'] < 0) { 16857 $dom[$key]['border']['T'] = array(); 16858 } 16859 } 16860 if (isset($brd_styles[2])) { 16861 $dom[$key]['border']['B']['cap'] = 'square'; 16862 $dom[$key]['border']['B']['join'] = 'miter'; 16863 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]); 16864 if ($dom[$key]['border']['B']['dash'] < 0) { 16865 $dom[$key]['border']['B'] = array(); 16866 } 16867 } 16868 } 16869 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom'); 16870 foreach ($cellside as $bsk => $bsv) { 16871 if (isset($dom[$key]['style']['border-'.$bsv])) { 16872 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]); 16873 if (!empty($borderstyle)) { 16874 $dom[$key]['border'][$bsk] = $borderstyle; 16875 } 16876 } 16877 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) { 16878 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors); 16879 } 16880 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) { 16881 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']); 16882 } 16883 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) { 16884 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']); 16885 if ($dom[$key]['border'][$bsk]['dash'] < 0) { 16886 $dom[$key]['border'][$bsk] = array(); 16887 } 16888 } 16889 } 16890 // check for CSS padding properties 16891 if (isset($dom[$key]['style']['padding'])) { 16892 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']); 16893 } else { 16894 $dom[$key]['padding'] = $this->cell_padding; 16895 } 16896 foreach ($cellside as $psk => $psv) { 16897 if (isset($dom[$key]['style']['padding-'.$psv])) { 16898 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false); 16899 } 16900 } 16901 // check for CSS margin properties 16902 if (isset($dom[$key]['style']['margin'])) { 16903 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']); 16904 } else { 16905 $dom[$key]['margin'] = $this->cell_margin; 16906 } 16907 foreach ($cellside as $psk => $psv) { 16908 if (isset($dom[$key]['style']['margin-'.$psv])) { 16909 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false); 16910 } 16911 } 16912 // check for CSS border-spacing properties 16913 if (isset($dom[$key]['style']['border-spacing'])) { 16914 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']); 16915 } 16916 // page-break-inside 16917 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) { 16918 $dom[$key]['attribute']['nobr'] = 'true'; 16919 } 16920 // page-break-before 16921 if (isset($dom[$key]['style']['page-break-before'])) { 16922 if ($dom[$key]['style']['page-break-before'] == 'always') { 16923 $dom[$key]['attribute']['pagebreak'] = 'true'; 16924 } elseif ($dom[$key]['style']['page-break-before'] == 'left') { 16925 $dom[$key]['attribute']['pagebreak'] = 'left'; 16926 } elseif ($dom[$key]['style']['page-break-before'] == 'right') { 16927 $dom[$key]['attribute']['pagebreak'] = 'right'; 16928 } 16929 } 16930 // page-break-after 16931 if (isset($dom[$key]['style']['page-break-after'])) { 16932 if ($dom[$key]['style']['page-break-after'] == 'always') { 16933 $dom[$key]['attribute']['pagebreakafter'] = 'true'; 16934 } elseif ($dom[$key]['style']['page-break-after'] == 'left') { 16935 $dom[$key]['attribute']['pagebreakafter'] = 'left'; 16936 } elseif ($dom[$key]['style']['page-break-after'] == 'right') { 16937 $dom[$key]['attribute']['pagebreakafter'] = 'right'; 16938 } 16939 } 16940 } 16941 if (isset($dom[$key]['attribute']['display'])) { 16942 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none'); 16943 } 16944 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) { 16945 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black'); 16946 if (!empty($borderstyle)) { 16947 $dom[$key]['border']['LTRB'] = $borderstyle; 16948 } 16949 } 16950 // check for font tag 16951 if ($dom[$key]['value'] == 'font') { 16952 // font family 16953 if (isset($dom[$key]['attribute']['face'])) { 16954 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']); 16955 } 16956 // font size 16957 if (isset($dom[$key]['attribute']['size'])) { 16958 if ($key > 0) { 16959 if ($dom[$key]['attribute']['size'][0] == '+') { 16960 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1)); 16961 } elseif ($dom[$key]['attribute']['size'][0] == '-') { 16962 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1)); 16963 } else { 16964 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 16965 } 16966 } else { 16967 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 16968 } 16969 } 16970 } 16971 // force natural alignment for lists 16972 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) 16973 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) { 16974 if ($this->rtl) { 16975 $dom[$key]['align'] = 'R'; 16976 } else { 16977 $dom[$key]['align'] = 'L'; 16978 } 16979 } 16980 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) { 16981 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { 16982 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO; 16983 } 16984 } 16985 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) { 16986 $dom[$key]['fontstyle'] .= 'B'; 16987 } 16988 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) { 16989 $dom[$key]['fontstyle'] .= 'I'; 16990 } 16991 if ($dom[$key]['value'] == 'u') { 16992 $dom[$key]['fontstyle'] .= 'U'; 16993 } 16994 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) { 16995 $dom[$key]['fontstyle'] .= 'D'; 16996 } 16997 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) { 16998 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; 16999 } 17000 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) { 17001 $dom[$key]['fontname'] = $this->default_monospaced_font; 17002 } 17003 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)) { 17004 // headings h1, h2, h3, h4, h5, h6 17005 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { 17006 $headsize = (4 - intval($dom[$key]['value'][1])) * 2; 17007 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize; 17008 } 17009 if (!isset($dom[$key]['style']['font-weight'])) { 17010 $dom[$key]['fontstyle'] .= 'B'; 17011 } 17012 } 17013 if (($dom[$key]['value'] == 'table')) { 17014 $dom[$key]['rows'] = 0; // number of rows 17015 $dom[$key]['trids'] = array(); // IDs of TR elements 17016 $dom[$key]['thead'] = ''; // table header rows 17017 } 17018 if (($dom[$key]['value'] == 'tr')) { 17019 $dom[$key]['cols'] = 0; 17020 if ($thead) { 17021 $dom[$key]['thead'] = true; 17022 // rows on thead block are printed as a separate table 17023 } else { 17024 $dom[$key]['thead'] = false; 17025 $parent = $dom[$key]['parent']; 17026 17027 if (!isset($dom[$parent]['rows'])) { 17028 $dom[$parent]['rows'] = 0; 17029 } 17030 // store the number of rows on table element 17031 ++$dom[$parent]['rows']; 17032 17033 if (!isset($dom[$parent]['trids'])) { 17034 $dom[$parent]['trids'] = array(); 17035 } 17036 17037 // store the TR elements IDs on table element 17038 array_push($dom[$parent]['trids'], $key); 17039 } 17040 } 17041 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) { 17042 if (isset($dom[$key]['attribute']['colspan'])) { 17043 $colspan = intval($dom[$key]['attribute']['colspan']); 17044 } else { 17045 $colspan = 1; 17046 } 17047 $dom[$key]['attribute']['colspan'] = $colspan; 17048 $dom[($dom[$key]['parent'])]['cols'] += $colspan; 17049 } 17050 // text direction 17051 if (isset($dom[$key]['attribute']['dir'])) { 17052 $dom[$key]['dir'] = $dom[$key]['attribute']['dir']; 17053 } 17054 // set foreground color attribute 17055 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) { 17056 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors); 17057 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) { 17058 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; 17059 } 17060 // set background color attribute 17061 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) { 17062 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors); 17063 } 17064 // set stroke color attribute 17065 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) { 17066 $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors); 17067 } 17068 // check for width attribute 17069 if (isset($dom[$key]['attribute']['width'])) { 17070 $dom[$key]['width'] = $dom[$key]['attribute']['width']; 17071 } 17072 // check for height attribute 17073 if (isset($dom[$key]['attribute']['height'])) { 17074 $dom[$key]['height'] = $dom[$key]['attribute']['height']; 17075 } 17076 // check for text alignment 17077 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) { 17078 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]); 17079 } 17080 // check for text rendering mode (the following attributes do not exist in HTML) 17081 if (isset($dom[$key]['attribute']['stroke'])) { 17082 // font stroke width 17083 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true); 17084 } 17085 if (isset($dom[$key]['attribute']['fill'])) { 17086 // font fill 17087 if ($dom[$key]['attribute']['fill'] == 'true') { 17088 $dom[$key]['fill'] = true; 17089 } else { 17090 $dom[$key]['fill'] = false; 17091 } 17092 } 17093 if (isset($dom[$key]['attribute']['clip'])) { 17094 // clipping mode 17095 if ($dom[$key]['attribute']['clip'] == 'true') { 17096 $dom[$key]['clip'] = true; 17097 } else { 17098 $dom[$key]['clip'] = false; 17099 } 17100 } 17101 } // end opening tag 17102 } else { 17103 // text 17104 $dom[$key]['tag'] = false; 17105 $dom[$key]['block'] = false; 17106 $dom[$key]['parent'] = end($level); 17107 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir']; 17108 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) { 17109 // text-transform for unicode requires mb_convert_case (Multibyte String Functions) 17110 if (function_exists('mb_convert_case')) { 17111 $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER); 17112 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) { 17113 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding); 17114 } 17115 } elseif (!$this->isunicode) { 17116 switch ($dom[$dom[$key]['parent']]['text-transform']) { 17117 case 'capitalize': { 17118 $element = ucwords(strtolower($element)); 17119 break; 17120 } 17121 case 'uppercase': { 17122 $element = strtoupper($element); 17123 break; 17124 } 17125 case 'lowercase': { 17126 $element = strtolower($element); 17127 break; 17128 } 17129 } 17130 } 17131 } 17132 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element)); 17133 } 17134 ++$elkey; 17135 ++$key; 17136 } 17137 return $dom; 17138 } 17139 17140 /** 17141 * Returns the string used to find spaces 17142 * @return string 17143 * @protected 17144 * @author Nicola Asuni 17145 * @since 4.8.024 (2010-01-15) 17146 */ 17147 protected function getSpaceString() { 17148 $spacestr = chr(32); 17149 if ($this->isUnicodeFont()) { 17150 $spacestr = chr(0).chr(32); 17151 } 17152 return $spacestr; 17153 } 17154 17155 /** 17156 * Return an hash code used to ensure that the serialized data has been generated by this TCPDF instance. 17157 * @param $data (string) serialized data 17158 * @return string 17159 * @public static 17160 */ 17161 protected function getHashForTCPDFtagParams($data) { 17162 return md5(strlen($data).$this->file_id.$data); 17163 } 17164 17165 /** 17166 * Serialize an array of parameters to be used with TCPDF tag in HTML code. 17167 * @param $data (array) parameters array 17168 * @return string containing serialized data 17169 * @public static 17170 */ 17171 public function serializeTCPDFtagParameters($data) { 17172 $encoded = urlencode(json_encode($data)); 17173 return $this->getHashForTCPDFtagParams($encoded).$encoded; 17174 } 17175 17176 /** 17177 * Unserialize parameters to be used with TCPDF tag in HTML code. 17178 * @param $data (string) serialized data 17179 * @return array containing unserialized data 17180 * @protected static 17181 */ 17182 protected function unserializeTCPDFtagParameters($data) { 17183 $hash = substr($data, 0, 32); 17184 $encoded = substr($data, 32); 17185 if ($hash != $this->getHashForTCPDFtagParams($encoded)) { 17186 $this->Error('Invalid parameters'); 17187 } 17188 return json_decode(urldecode($encoded), true); 17189 } 17190 17191 /** 17192 * Prints a cell (rectangular area) with optional borders, background color and html text string. 17193 * 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 /> 17194 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 17195 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. 17196 * 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 17197 * NOTE: all the HTML attributes must be enclosed in double-quote. 17198 * @param $w (float) Cell width. If 0, the cell extends up to the right margin. 17199 * @param $h (float) Cell minimum height. The cell extends automatically if needed. 17200 * @param $x (float) upper-left corner X coordinate 17201 * @param $y (float) upper-left corner Y coordinate 17202 * @param $html (string) html text to print. Default value: empty string. 17203 * @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))) 17204 * @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> 17205Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 17206 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). 17207 * @param $reseth (boolean) if true reset the last cell height (default true). 17208 * @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> 17209 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width. 17210 * @see Multicell(), writeHTML() 17211 * @public 17212 */ 17213 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) { 17214 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false); 17215 } 17216 17217 /** 17218 * Allows to preserve some HTML formatting (limited support).<br /> 17219 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. 17220 * 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 17221 * NOTE: all the HTML attributes must be enclosed in double-quote. 17222 * @param $html (string) text to display 17223 * @param $ln (boolean) if true add a new line after text (default = true) 17224 * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false). 17225 * @param $reseth (boolean) if true reset the last cell height (default false). 17226 * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false). 17227 * @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> 17228 * @public 17229 */ 17230 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') { 17231 $gvars = $this->getGraphicVars(); 17232 // store current values 17233 $prev_cell_margin = $this->cell_margin; 17234 $prev_cell_padding = $this->cell_padding; 17235 $prevPage = $this->page; 17236 $prevlMargin = $this->lMargin; 17237 $prevrMargin = $this->rMargin; 17238 $curfontname = $this->FontFamily; 17239 $curfontstyle = $this->FontStyle; 17240 $curfontsize = $this->FontSizePt; 17241 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize); 17242 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize); 17243 $curfontstretcing = $this->font_stretching; 17244 $curfonttracking = $this->font_spacing; 17245 $this->newline = true; 17246 $newline = true; 17247 $startlinepage = $this->page; 17248 $minstartliney = $this->y; 17249 $maxbottomliney = 0; 17250 $startlinex = $this->x; 17251 $startliney = $this->y; 17252 $yshift = 0; 17253 $loop = 0; 17254 $curpos = 0; 17255 $this_method_vars = array(); 17256 $undo = false; 17257 $fontaligned = false; 17258 $reverse_dir = false; // true when the text direction is reversed 17259 $this->premode = false; 17260 if ($this->inxobj) { 17261 // we are inside an XObject template 17262 $pask = count($this->xobjects[$this->xobjid]['annotations']); 17263 } elseif (isset($this->PageAnnots[$this->page])) { 17264 $pask = count($this->PageAnnots[$this->page]); 17265 } else { 17266 $pask = 0; 17267 } 17268 if ($this->inxobj) { 17269 // we are inside an XObject template 17270 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); 17271 } elseif (!$this->InFooter) { 17272 if (isset($this->footerlen[$this->page])) { 17273 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 17274 } else { 17275 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 17276 } 17277 $startlinepos = $this->footerpos[$this->page]; 17278 } else { 17279 // we are inside the footer 17280 $startlinepos = $this->pagelen[$this->page]; 17281 } 17282 $lalign = $align; 17283 $plalign = $align; 17284 if ($this->rtl) { 17285 $w = $this->x - $this->lMargin; 17286 } else { 17287 $w = $this->w - $this->rMargin - $this->x; 17288 } 17289 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']); 17290 if ($cell) { 17291 if ($this->rtl) { 17292 $this->x -= $this->cell_padding['R']; 17293 $this->lMargin += $this->cell_padding['L']; 17294 } else { 17295 $this->x += $this->cell_padding['L']; 17296 $this->rMargin += $this->cell_padding['R']; 17297 } 17298 } 17299 if ($this->customlistindent >= 0) { 17300 $this->listindent = $this->customlistindent; 17301 } else { 17302 $this->listindent = $this->GetStringWidth('000000'); 17303 } 17304 $this->listindentlevel = 0; 17305 // save previous states 17306 $prev_cell_height_ratio = $this->cell_height_ratio; 17307 $prev_listnum = $this->listnum; 17308 $prev_listordered = $this->listordered; 17309 $prev_listcount = $this->listcount; 17310 $prev_lispacer = $this->lispacer; 17311 $this->listnum = 0; 17312 $this->listordered = array(); 17313 $this->listcount = array(); 17314 $this->lispacer = ''; 17315 if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) { 17316 // reset row height 17317 $this->resetLastH(); 17318 } 17319 $dom = $this->getHtmlDomArray($html); 17320 $maxel = count($dom); 17321 $key = 0; 17322 while ($key < $maxel) { 17323 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) { 17324 // store the node key 17325 $hidden_node_key = $key; 17326 if ($dom[$key]['self']) { 17327 // skip just this self-closing tag 17328 ++$key; 17329 } else { 17330 // skip this and all children tags 17331 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) { 17332 // skip hidden objects 17333 ++$key; 17334 } 17335 ++$key; 17336 } 17337 } 17338 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) { 17339 // check for pagebreak 17340 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) { 17341 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17342 $this->checkPageBreak($this->PageBreakTrigger + 1); 17343 $this->htmlvspace = ($this->PageBreakTrigger + 1); 17344 } 17345 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 17346 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 17347 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17348 $this->checkPageBreak($this->PageBreakTrigger + 1); 17349 $this->htmlvspace = ($this->PageBreakTrigger + 1); 17350 } 17351 } 17352 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) { 17353 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { 17354 $dom[$key]['attribute']['nobr'] = false; 17355 } else { 17356 // store current object 17357 $this->startTransaction(); 17358 // save this method vars 17359 $this_method_vars['html'] = $html; 17360 $this_method_vars['ln'] = $ln; 17361 $this_method_vars['fill'] = $fill; 17362 $this_method_vars['reseth'] = $reseth; 17363 $this_method_vars['cell'] = $cell; 17364 $this_method_vars['align'] = $align; 17365 $this_method_vars['gvars'] = $gvars; 17366 $this_method_vars['prevPage'] = $prevPage; 17367 $this_method_vars['prev_cell_margin'] = $prev_cell_margin; 17368 $this_method_vars['prev_cell_padding'] = $prev_cell_padding; 17369 $this_method_vars['prevlMargin'] = $prevlMargin; 17370 $this_method_vars['prevrMargin'] = $prevrMargin; 17371 $this_method_vars['curfontname'] = $curfontname; 17372 $this_method_vars['curfontstyle'] = $curfontstyle; 17373 $this_method_vars['curfontsize'] = $curfontsize; 17374 $this_method_vars['curfontascent'] = $curfontascent; 17375 $this_method_vars['curfontdescent'] = $curfontdescent; 17376 $this_method_vars['curfontstretcing'] = $curfontstretcing; 17377 $this_method_vars['curfonttracking'] = $curfonttracking; 17378 $this_method_vars['minstartliney'] = $minstartliney; 17379 $this_method_vars['maxbottomliney'] = $maxbottomliney; 17380 $this_method_vars['yshift'] = $yshift; 17381 $this_method_vars['startlinepage'] = $startlinepage; 17382 $this_method_vars['startlinepos'] = $startlinepos; 17383 $this_method_vars['startlinex'] = $startlinex; 17384 $this_method_vars['startliney'] = $startliney; 17385 $this_method_vars['newline'] = $newline; 17386 $this_method_vars['loop'] = $loop; 17387 $this_method_vars['curpos'] = $curpos; 17388 $this_method_vars['pask'] = $pask; 17389 $this_method_vars['lalign'] = $lalign; 17390 $this_method_vars['plalign'] = $plalign; 17391 $this_method_vars['w'] = $w; 17392 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio; 17393 $this_method_vars['prev_listnum'] = $prev_listnum; 17394 $this_method_vars['prev_listordered'] = $prev_listordered; 17395 $this_method_vars['prev_listcount'] = $prev_listcount; 17396 $this_method_vars['prev_lispacer'] = $prev_lispacer; 17397 $this_method_vars['fontaligned'] = $fontaligned; 17398 $this_method_vars['key'] = $key; 17399 $this_method_vars['dom'] = $dom; 17400 } 17401 } 17402 // print THEAD block 17403 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) { 17404 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) { 17405 $this->inthead = true; 17406 // print table header (thead) 17407 $this->writeHTML($this->thead, false, false, false, false, ''); 17408 // check if we are on a new page or on a new column 17409 if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) { 17410 // we are on a new page or on a new column and the total object height is less than the available vertical space. 17411 // restore previous object 17412 $this->rollbackTransaction(true); 17413 // restore previous values 17414 foreach ($this_method_vars as $vkey => $vval) { 17415 $$vkey = $vval; 17416 } 17417 // disable table header 17418 $tmp_thead = $this->thead; 17419 $this->thead = ''; 17420 // add a page (or trig AcceptPageBreak() for multicolumn mode) 17421 $pre_y = $this->y; 17422 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 17423 // fix for multicolumn mode 17424 $startliney = $this->y; 17425 } 17426 $this->start_transaction_page = $this->page; 17427 $this->start_transaction_y = $this->y; 17428 // restore table header 17429 $this->thead = $tmp_thead; 17430 // fix table border properties 17431 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) { 17432 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px'); 17433 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) { 17434 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V']; 17435 } else { 17436 $tmp_cellspacing = 0; 17437 } 17438 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page; 17439 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column; 17440 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing; 17441 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']); 17442 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset; 17443 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset; 17444 // print table header (thead) 17445 $this->writeHTML($this->thead, false, false, false, false, ''); 17446 } 17447 } 17448 // move $key index forward to skip THEAD block 17449 while ( ($key < $maxel) AND (!( 17450 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead'])) 17451 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) { 17452 ++$key; 17453 } 17454 } 17455 if ($dom[$key]['tag'] OR ($key == 0)) { 17456 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) { 17457 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L'; 17458 } 17459 // vertically align image in line 17460 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) { 17461 // get image height 17462 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px'); 17463 $autolinebreak = false; 17464 if (!empty($dom[$key]['width'])) { 17465 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false); 17466 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R'])) 17467 AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L']))) 17468 OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) { 17469 // add automatic line break 17470 $autolinebreak = true; 17471 $this->Ln('', $cell); 17472 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) { 17473 // go back to evaluate this line break 17474 --$key; 17475 } 17476 } 17477 } 17478 if (!$autolinebreak) { 17479 if ($this->inPageBody()) { 17480 $pre_y = $this->y; 17481 // check for page break 17482 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) { 17483 // fix for multicolumn mode 17484 $startliney = $this->y; 17485 } 17486 } 17487 if ($this->page > $startlinepage) { 17488 // fix line splitted over two pages 17489 if (isset($this->footerlen[$startlinepage])) { 17490 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17491 } 17492 // line to be moved one page forward 17493 $pagebuff = $this->getPageBuffer($startlinepage); 17494 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 17495 $tstart = substr($pagebuff, 0, $startlinepos); 17496 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 17497 // remove line from previous page 17498 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 17499 $pagebuff = $this->getPageBuffer($this->page); 17500 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 17501 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 17502 // add line start to current page 17503 $yshift = ($minstartliney - $this->y); 17504 if ($fontaligned) { 17505 $yshift += ($curfontsize / $this->k); 17506 } 17507 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k)); 17508 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 17509 // shift the annotations and links 17510 if (isset($this->PageAnnots[$this->page])) { 17511 $next_pask = count($this->PageAnnots[$this->page]); 17512 } else { 17513 $next_pask = 0; 17514 } 17515 if (isset($this->PageAnnots[$startlinepage])) { 17516 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 17517 if ($pak >= $pask) { 17518 $this->PageAnnots[$this->page][] = $pac; 17519 unset($this->PageAnnots[$startlinepage][$pak]); 17520 $npak = count($this->PageAnnots[$this->page]) - 1; 17521 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 17522 } 17523 } 17524 } 17525 $pask = $next_pask; 17526 $startlinepos = $this->cntmrk[$this->page]; 17527 $startlinepage = $this->page; 17528 $startliney = $this->y; 17529 $this->newline = false; 17530 } 17531 $this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh); 17532 $minstartliney = min($this->y, $minstartliney); 17533 $maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k)); 17534 } 17535 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) { 17536 // account for different font size 17537 $pfontname = $curfontname; 17538 $pfontstyle = $curfontstyle; 17539 $pfontsize = $curfontsize; 17540 $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname); 17541 $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle); 17542 $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize); 17543 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize); 17544 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize); 17545 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize) 17546 OR ($this->cell_height_ratio != $dom[$key]['line-height']) 17547 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) { 17548 if (($key < ($maxel - 1)) AND ( 17549 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) 17550 OR ($this->cell_height_ratio != $dom[$key]['line-height']) 17551 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) 17552 AND ($fontsize >= 0) AND ($curfontsize >= 0) 17553 AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname))) 17554 )) { 17555 if ($this->page > $startlinepage) { 17556 // fix lines splitted over two pages 17557 if (isset($this->footerlen[$startlinepage])) { 17558 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17559 } 17560 // line to be moved one page forward 17561 $pagebuff = $this->getPageBuffer($startlinepage); 17562 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 17563 $tstart = substr($pagebuff, 0, $startlinepos); 17564 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 17565 // remove line start from previous page 17566 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 17567 $pagebuff = $this->getPageBuffer($this->page); 17568 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 17569 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 17570 // add line start to current page 17571 $yshift = ($minstartliney - $this->y); 17572 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k)); 17573 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 17574 // shift the annotations and links 17575 if (isset($this->PageAnnots[$this->page])) { 17576 $next_pask = count($this->PageAnnots[$this->page]); 17577 } else { 17578 $next_pask = 0; 17579 } 17580 if (isset($this->PageAnnots[$startlinepage])) { 17581 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 17582 if ($pak >= $pask) { 17583 $this->PageAnnots[$this->page][] = $pac; 17584 unset($this->PageAnnots[$startlinepage][$pak]); 17585 $npak = count($this->PageAnnots[$this->page]) - 1; 17586 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 17587 } 17588 } 17589 } 17590 $pask = $next_pask; 17591 $startlinepos = $this->cntmrk[$this->page]; 17592 $startlinepage = $this->page; 17593 $startliney = $this->y; 17594 } 17595 if (!isset($dom[$key]['line-height'])) { 17596 $dom[$key]['line-height'] = $this->cell_height_ratio; 17597 } 17598 if (!$dom[$key]['block']) { 17599 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']))) { 17600 $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2; 17601 } 17602 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) { 17603 $current_line_align_data = array($key, $minstartliney, $maxbottomliney); 17604 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)))) { 17605 $minstartliney = min($this->y, $line_align_data[1]); 17606 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]); 17607 } else { 17608 $minstartliney = min($this->y, $minstartliney); 17609 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney); 17610 } 17611 $line_align_data = $current_line_align_data; 17612 } 17613 } 17614 $this->cell_height_ratio = $dom[$key]['line-height']; 17615 $fontaligned = true; 17616 } 17617 $this->SetFont($fontname, $fontstyle, $fontsize); 17618 // reset row height 17619 $this->resetLastH(); 17620 $curfontname = $fontname; 17621 $curfontstyle = $fontstyle; 17622 $curfontsize = $fontsize; 17623 $curfontascent = $fontascent; 17624 $curfontdescent = $fontdescent; 17625 } 17626 } 17627 // set text rendering mode 17628 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth; 17629 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0); 17630 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3); 17631 $this->setTextRenderingMode($textstroke, $textfill, $textclip); 17632 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) { 17633 $this->setFontStretching($dom[$key]['font-stretch']); 17634 } 17635 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) { 17636 $this->setFontSpacing($dom[$key]['letter-spacing']); 17637 } 17638 if (($plalign == 'J') AND $dom[$key]['block']) { 17639 $plalign = ''; 17640 } 17641 // get current position on page buffer 17642 $curpos = $this->pagelen[$startlinepage]; 17643 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) { 17644 $this->SetFillColorArray($dom[$key]['bgcolor']); 17645 $wfill = true; 17646 } else { 17647 $wfill = $fill | false; 17648 } 17649 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) { 17650 $this->SetTextColorArray($dom[$key]['fgcolor']); 17651 } 17652 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) { 17653 $this->SetDrawColorArray($dom[$key]['strokecolor']); 17654 } 17655 if (isset($dom[$key]['align'])) { 17656 $lalign = $dom[$key]['align']; 17657 } 17658 if (TCPDF_STATIC::empty_string($lalign)) { 17659 $lalign = $align; 17660 } 17661 } 17662 // align lines 17663 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) { 17664 $newline = true; 17665 $fontaligned = false; 17666 // we are at the beginning of a new line 17667 if (isset($startlinex)) { 17668 $yshift = ($minstartliney - $startliney); 17669 if (($yshift > 0) OR ($this->page > $startlinepage)) { 17670 $yshift = 0; 17671 } 17672 $t_x = 0; 17673 // the last line must be shifted to be aligned as requested 17674 $linew = abs($this->endlinex - $startlinex); 17675 if ($this->inxobj) { 17676 // we are inside an XObject template 17677 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); 17678 if (isset($opentagpos)) { 17679 $midpos = $opentagpos; 17680 } else { 17681 $midpos = 0; 17682 } 17683 if ($midpos > 0) { 17684 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); 17685 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); 17686 } else { 17687 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); 17688 $pend = ''; 17689 } 17690 } else { 17691 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 17692 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 17693 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17694 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 17695 } elseif (isset($opentagpos)) { 17696 $midpos = $opentagpos; 17697 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 17698 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 17699 $midpos = $this->footerpos[$startlinepage]; 17700 } else { 17701 $midpos = 0; 17702 } 17703 if ($midpos > 0) { 17704 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 17705 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 17706 } else { 17707 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 17708 $pend = ''; 17709 } 17710 } 17711 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { 17712 // calculate shifting amount 17713 $tw = $w; 17714 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) { 17715 $tw += $this->cell_padding['R']; 17716 } 17717 if ($this->lMargin != $prevlMargin) { 17718 $tw += ($prevlMargin - $this->lMargin); 17719 } 17720 if ($this->rMargin != $prevrMargin) { 17721 $tw += ($prevrMargin - $this->rMargin); 17722 } 17723 $one_space_width = $this->GetStringWidth(chr(32)); 17724 $no = 0; // number of spaces on a line contained on a single block 17725 if ($this->isRTLTextDir()) { // RTL 17726 // remove left space if exist 17727 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[('); 17728 if ($pos1 > 0) { 17729 $pos1 = intval($pos1); 17730 if ($this->isUnicodeFont()) { 17731 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32))); 17732 $spacelen = 2; 17733 } else { 17734 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32))); 17735 $spacelen = 1; 17736 } 17737 if ($pos1 == $pos2) { 17738 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); 17739 if (substr($pmid, $pos1, 4) == '[()]') { 17740 $linew -= $one_space_width; 17741 } elseif ($pos1 == strpos($pmid, '[(')) { 17742 $no = 1; 17743 } 17744 } 17745 } 17746 } else { // LTR 17747 // remove right space if exist 17748 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]'); 17749 if ($pos1 > 0) { 17750 $pos1 = intval($pos1); 17751 if ($this->isUnicodeFont()) { 17752 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2; 17753 $spacelen = 2; 17754 } else { 17755 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1; 17756 $spacelen = 1; 17757 } 17758 if ($pos1 == $pos2) { 17759 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); 17760 $linew -= $one_space_width; 17761 } 17762 } 17763 } 17764 $mdiff = ($tw - $linew); 17765 if ($plalign == 'C') { 17766 if ($this->rtl) { 17767 $t_x = -($mdiff / 2); 17768 } else { 17769 $t_x = ($mdiff / 2); 17770 } 17771 } elseif ($plalign == 'R') { 17772 // right alignment on LTR document 17773 $t_x = $mdiff; 17774 } elseif ($plalign == 'L') { 17775 // left alignment on RTL document 17776 $t_x = -$mdiff; 17777 } elseif (($plalign == 'J') AND ($plalign == $lalign)) { 17778 // Justification 17779 if ($this->isRTLTextDir()) { 17780 // align text on the left 17781 $t_x = -$mdiff; 17782 } 17783 $ns = 0; // number of spaces 17784 $pmidtemp = $pmid; 17785 // escape special characters 17786 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 17787 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 17788 // search spaces 17789 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) { 17790 $spacestr = $this->getSpaceString(); 17791 $maxkk = count($lnstring[1]) - 1; 17792 for ($kk=0; $kk <= $maxkk; ++$kk) { 17793 // restore special characters 17794 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]); 17795 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]); 17796 // store number of spaces on the strings 17797 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr); 17798 // count total spaces on line 17799 $ns += $lnstring[2][$kk]; 17800 $lnstring[3][$kk] = $ns; 17801 } 17802 if ($ns == 0) { 17803 $ns = 1; 17804 } 17805 // calculate additional space to add to each existing space 17806 $spacewidth = ($mdiff / ($ns - $no)) * $this->k; 17807 if ($this->FontSize <= 0) { 17808 $this->FontSize = 1; 17809 } 17810 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize; 17811 if ($this->font_spacing != 0) { 17812 // fixed spacing mode 17813 $osw = -1000 * $this->font_spacing / $this->FontSize; 17814 $spacewidthu += $osw; 17815 } 17816 $nsmax = $ns; 17817 $ns = 0; 17818 reset($lnstring); 17819 $offset = 0; 17820 $strcount = 0; 17821 $prev_epsposbeg = 0; 17822 $textpos = 0; 17823 if ($this->isRTLTextDir()) { 17824 $textpos = $this->wPt; 17825 } 17826 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) { 17827 // check if we are inside a string section '[( ... )]' 17828 $stroffset = strpos($pmid, '[(', $offset); 17829 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) { 17830 // set offset to the end of string section 17831 $offset = strpos($pmid, ')]', $stroffset); 17832 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) { 17833 $offset = strpos($pmid, ')]', ($offset + 1)); 17834 } 17835 if ($offset === false) { 17836 $this->Error('HTML Justification: malformed PDF code.'); 17837 } 17838 continue; 17839 } 17840 if ($this->isRTLTextDir()) { 17841 $spacew = ($spacewidth * ($nsmax - $ns)); 17842 } else { 17843 $spacew = ($spacewidth * $ns); 17844 } 17845 $offset = $strpiece[2][1] + strlen($strpiece[2][0]); 17846 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset); 17847 if ($epsposend !== null) { 17848 $epsposend += strlen($this->epsmarker.'Q'); 17849 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); 17850 if ($epsposbeg === null) { 17851 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6)); 17852 $prev_epsposbeg = $epsposbeg; 17853 } 17854 if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) { 17855 // shift EPS images 17856 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew); 17857 $pmid_b = substr($pmid, 0, $epsposbeg); 17858 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg)); 17859 $pmid_e = substr($pmid, $epsposend); 17860 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e; 17861 $offset = $epsposend; 17862 continue; 17863 } 17864 } 17865 $currentxpos = 0; 17866 // shift blocks of code 17867 switch ($strpiece[2][0]) { 17868 case 'Td': 17869 case 'cm': 17870 case 'm': 17871 case 'l': { 17872 // get current X position 17873 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); 17874 if (!isset($xmatches[1])) { 17875 break; 17876 } 17877 $currentxpos = $xmatches[1]; 17878 $textpos = $currentxpos; 17879 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) { 17880 $ns = $lnstring[3][$strcount]; 17881 if ($this->isRTLTextDir()) { 17882 $spacew = ($spacewidth * ($nsmax - $ns)); 17883 } 17884 ++$strcount; 17885 } 17886 // justify block 17887 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) { 17888 $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4]; 17889 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17890 unset($pmatch, $newpmid); 17891 } 17892 break; 17893 } 17894 case 're': { 17895 // justify block 17896 if (!TCPDF_STATIC::empty_string($this->lispacer)) { 17897 $this->lispacer = ''; 17898 break; 17899 } 17900 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches); 17901 if (!isset($xmatches[1])) { 17902 break; 17903 } 17904 $currentxpos = $xmatches[1]; 17905 $x_diff = 0; 17906 $w_diff = 0; 17907 if ($this->isRTLTextDir()) { // RTL 17908 if ($currentxpos < $textpos) { 17909 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount])); 17910 $w_diff = ($spacewidth * $lnstring[2][$strcount]); 17911 } else { 17912 if ($strcount > 0) { 17913 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)])); 17914 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); 17915 } 17916 } 17917 } else { // LTR 17918 if ($currentxpos > $textpos) { 17919 if ($strcount > 0) { 17920 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]); 17921 } 17922 $w_diff = ($spacewidth * $lnstring[2][$strcount]); 17923 } else { 17924 if ($strcount > 1) { 17925 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]); 17926 } 17927 if ($strcount > 0) { 17928 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); 17929 } 17930 } 17931 } 17932 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) { 17933 $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff)); 17934 $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff)); 17935 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6]; 17936 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17937 unset($pmatch, $newpmid, $newx, $neww); 17938 } 17939 break; 17940 } 17941 case 'c': { 17942 // get current X position 17943 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); 17944 if (!isset($xmatches[1])) { 17945 break; 17946 } 17947 $currentxpos = $xmatches[1]; 17948 // justify block 17949 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) { 17950 $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew)); 17951 $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew)); 17952 $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew)); 17953 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8]; 17954 $pmid = str_replace($pmatch[0], $newpmid, $pmid); 17955 unset($pmatch, $newpmid, $newx1, $newx2, $newx3); 17956 } 17957 break; 17958 } 17959 } 17960 // shift the annotations and links 17961 $cxpos = ($currentxpos / $this->k); 17962 $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps); 17963 if ($this->inxobj) { 17964 // we are inside an XObject template 17965 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 17966 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 17967 if ($cxpos > $lmpos) { 17968 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k); 17969 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17970 } else { 17971 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17972 } 17973 break; 17974 } 17975 } 17976 } elseif (isset($this->PageAnnots[$this->page])) { 17977 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 17978 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 17979 if ($cxpos > $lmpos) { 17980 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k); 17981 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17982 } else { 17983 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 17984 } 17985 break; 17986 } 17987 } 17988 } 17989 } // end of while 17990 // remove markers 17991 $pmid = str_replace('x*#!#*x', '', $pmid); 17992 if ($this->isUnicodeFont()) { 17993 // multibyte characters 17994 $spacew = $spacewidthu; 17995 if ($this->font_stretching != 100) { 17996 // word spacing is affected by stretching 17997 $spacew /= ($this->font_stretching / 100); 17998 } 17999 // escape special characters 18000 $pos = 0; 18001 $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid); 18002 $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid); 18003 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) { 18004 foreach($pamatch[0] as $pk => $pmatch) { 18005 $replace = $pamatch[1][$pk]; 18006 $replace = str_replace('#!#OP#!#', '(', $replace); 18007 $replace = str_replace('#!#CP#!#', ')', $replace); 18008 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]'; 18009 $pos = strpos($pmid, $pmatch, $pos); 18010 if ($pos !== FALSE) { 18011 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch)); 18012 } 18013 ++$pos; 18014 } 18015 unset($pamatch); 18016 } 18017 if ($this->inxobj) { 18018 // we are inside an XObject template 18019 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend; 18020 } else { 18021 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend); 18022 } 18023 $endlinepos = strlen($pstart."\n".$pmid."\n"); 18024 } else { 18025 // non-unicode (single-byte characters) 18026 if ($this->font_stretching != 100) { 18027 // word spacing (Tw) is affected by stretching 18028 $spacewidth /= ($this->font_stretching / 100); 18029 } 18030 $rs = sprintf('%F Tw', $spacewidth); 18031 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid); 18032 if ($this->inxobj) { 18033 // we are inside an XObject template 18034 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend; 18035 } else { 18036 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend); 18037 } 18038 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n"); 18039 } 18040 } 18041 } // end of J 18042 } // end if $startlinex 18043 if (($t_x != 0) OR ($yshift < 0)) { 18044 // shift the line 18045 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k)); 18046 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; 18047 $endlinepos = strlen($pstart); 18048 if ($this->inxobj) { 18049 // we are inside an XObject template 18050 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; 18051 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 18052 if ($pak >= $pask) { 18053 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; 18054 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; 18055 } 18056 } 18057 } else { 18058 $this->setPageBuffer($startlinepage, $pstart.$pend); 18059 // shift the annotations and links 18060 if (isset($this->PageAnnots[$this->page])) { 18061 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 18062 if ($pak >= $pask) { 18063 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 18064 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 18065 } 18066 } 18067 } 18068 } 18069 $this->y -= $yshift; 18070 } 18071 } 18072 $pbrk = $this->checkPageBreak($this->lasth); 18073 $this->newline = false; 18074 $startlinex = $this->x; 18075 $startliney = $this->y; 18076 if ($dom[$dom[$key]['parent']]['value'] == 'sup') { 18077 $startliney -= ((0.3 * $this->FontSizePt) / $this->k); 18078 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') { 18079 $startliney -= (($this->FontSizePt / 0.7) / $this->k); 18080 } else { 18081 $minstartliney = $startliney; 18082 $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k)); 18083 } 18084 $startlinepage = $this->page; 18085 if (isset($endlinepos) AND (!$pbrk)) { 18086 $startlinepos = $endlinepos; 18087 } else { 18088 if ($this->inxobj) { 18089 // we are inside an XObject template 18090 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); 18091 } elseif (!$this->InFooter) { 18092 if (isset($this->footerlen[$this->page])) { 18093 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 18094 } else { 18095 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 18096 } 18097 $startlinepos = $this->footerpos[$this->page]; 18098 } else { 18099 $startlinepos = $this->pagelen[$this->page]; 18100 } 18101 } 18102 unset($endlinepos); 18103 $plalign = $lalign; 18104 if (isset($this->PageAnnots[$this->page])) { 18105 $pask = count($this->PageAnnots[$this->page]); 18106 } else { 18107 $pask = 0; 18108 } 18109 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table') 18110 AND (isset($this->emptypagemrk[$this->page])) 18111 AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) { 18112 $this->SetFont($fontname, $fontstyle, $fontsize); 18113 if ($wfill) { 18114 $this->SetFillColorArray($this->bgcolor); 18115 } 18116 } 18117 } // end newline 18118 if (isset($opentagpos)) { 18119 unset($opentagpos); 18120 } 18121 if ($dom[$key]['tag']) { 18122 if ($dom[$key]['opening']) { 18123 // get text indentation (if any) 18124 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) { 18125 $this->textindent = $dom[$key]['text-indent']; 18126 $this->newline = true; 18127 } 18128 // table 18129 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) { 18130 // available page width 18131 if ($this->rtl) { 18132 $wtmp = $this->x - $this->lMargin; 18133 } else { 18134 $wtmp = $this->w - $this->rMargin - $this->x; 18135 } 18136 // get cell spacing 18137 if (isset($dom[$key]['attribute']['cellspacing'])) { 18138 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px'); 18139 $cellspacing = array('H' => $clsp, 'V' => $clsp); 18140 } elseif (isset($dom[$key]['border-spacing'])) { 18141 $cellspacing = $dom[$key]['border-spacing']; 18142 } else { 18143 $cellspacing = array('H' => 0, 'V' => 0); 18144 } 18145 // table width 18146 if (isset($dom[$key]['width'])) { 18147 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); 18148 } else { 18149 $table_width = $wtmp; 18150 } 18151 $table_width -= (2 * $cellspacing['H']); 18152 if (!$this->inthead) { 18153 $this->y += $cellspacing['V']; 18154 } 18155 if ($this->rtl) { 18156 $cellspacingx = -$cellspacing['H']; 18157 } else { 18158 $cellspacingx = $cellspacing['H']; 18159 } 18160 // total table width without cellspaces 18161 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1))); 18162 // minimum column width 18163 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']); 18164 // array of custom column widths 18165 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width); 18166 } 18167 // table row 18168 if ($dom[$key]['value'] == 'tr') { 18169 // reset column counter 18170 $colid = 0; 18171 } 18172 // table cell 18173 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 18174 $trid = $dom[$key]['parent']; 18175 $table_el = $dom[$trid]['parent']; 18176 if (!isset($dom[$table_el]['cols'])) { 18177 $dom[$table_el]['cols'] = $dom[$trid]['cols']; 18178 } 18179 // store border info 18180 $tdborder = 0; 18181 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) { 18182 $tdborder = $dom[$key]['border']; 18183 } 18184 $colspan = intval($dom[$key]['attribute']['colspan']); 18185 if ($colspan <= 0) { 18186 $colspan = 1; 18187 } 18188 $old_cell_padding = $this->cell_padding; 18189 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { 18190 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px'); 18191 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd); 18192 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) { 18193 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding']; 18194 } else { 18195 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0); 18196 } 18197 $this->cell_padding = $current_cell_padding; 18198 if (isset($dom[$key]['height'])) { 18199 // minimum cell height 18200 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px'); 18201 } else { 18202 $cellh = 0; 18203 } 18204 if (isset($dom[$key]['content'])) { 18205 $cell_content = $dom[$key]['content']; 18206 } else { 18207 $cell_content = ' '; 18208 } 18209 $tagtype = $dom[$key]['value']; 18210 $parentid = $key; 18211 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) { 18212 // move $key index forward 18213 ++$key; 18214 } 18215 if (!isset($dom[$trid]['startpage'])) { 18216 $dom[$trid]['startpage'] = $this->page; 18217 } else { 18218 $this->setPage($dom[$trid]['startpage']); 18219 } 18220 if (!isset($dom[$trid]['startcolumn'])) { 18221 $dom[$trid]['startcolumn'] = $this->current_column; 18222 } elseif ($this->current_column != $dom[$trid]['startcolumn']) { 18223 $tmpx = $this->x; 18224 $this->selectColumn($dom[$trid]['startcolumn']); 18225 $this->x = $tmpx; 18226 } 18227 if (!isset($dom[$trid]['starty'])) { 18228 $dom[$trid]['starty'] = $this->y; 18229 } else { 18230 $this->y = $dom[$trid]['starty']; 18231 } 18232 if (!isset($dom[$trid]['startx'])) { 18233 $dom[$trid]['startx'] = $this->x; 18234 $this->x += $cellspacingx; 18235 } else { 18236 $this->x += ($cellspacingx / 2); 18237 } 18238 if (isset($dom[$parentid]['attribute']['rowspan'])) { 18239 $rowspan = intval($dom[$parentid]['attribute']['rowspan']); 18240 } else { 18241 $rowspan = 1; 18242 } 18243 // skip row-spanned cells started on the previous rows 18244 if (isset($dom[$table_el]['rowspans'])) { 18245 $rsk = 0; 18246 $rskmax = count($dom[$table_el]['rowspans']); 18247 while ($rsk < $rskmax) { 18248 $trwsp = $dom[$table_el]['rowspans'][$rsk]; 18249 $rsstartx = $trwsp['startx']; 18250 $rsendx = $trwsp['endx']; 18251 // account for margin changes 18252 if ($trwsp['startpage'] < $this->page) { 18253 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) { 18254 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']); 18255 $rsstartx -= $dl; 18256 $rsendx -= $dl; 18257 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) { 18258 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']); 18259 $rsstartx += $dl; 18260 $rsendx += $dl; 18261 } 18262 } 18263 if (($trwsp['rowspan'] > 0) 18264 AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps)) 18265 AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps)) 18266 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) { 18267 // set the starting X position of the current cell 18268 $this->x = $rsendx + $cellspacingx; 18269 // increment column indicator 18270 $colid += $trwsp['colspan']; 18271 if (($trwsp['rowspan'] == 1) 18272 AND (isset($dom[$trid]['endy'])) 18273 AND (isset($dom[$trid]['endpage'])) 18274 AND (isset($dom[$trid]['endcolumn'])) 18275 AND ($trwsp['endpage'] == $dom[$trid]['endpage']) 18276 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { 18277 // set ending Y position for row 18278 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 18279 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy']; 18280 } 18281 $rsk = 0; 18282 } else { 18283 ++$rsk; 18284 } 18285 } 18286 } 18287 if (isset($dom[$parentid]['width'])) { 18288 // user specified width 18289 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px'); 18290 $tmpcw = ($cellw / $colspan); 18291 for ($i = 0; $i < $colspan; ++$i) { 18292 $table_colwidths[($colid + $i)] = $tmpcw; 18293 } 18294 } else { 18295 // inherit column width 18296 $cellw = 0; 18297 for ($i = 0; $i < $colspan; ++$i) { 18298 $cellw += (isset($table_colwidths[($colid + $i)]) ? $table_colwidths[($colid + $i)] : 0); 18299 } 18300 } 18301 $cellw += (($colspan - 1) * $cellspacing['H']); 18302 // increment column indicator 18303 $colid += $colspan; 18304 // add rowspan information to table element 18305 if ($rowspan > 1) { 18306 $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)); 18307 } 18308 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x)); 18309 if ($rowspan > 1) { 18310 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1); 18311 } 18312 // push background colors 18313 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) { 18314 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor']; 18315 } 18316 // store border info 18317 if (isset($tdborder) AND !empty($tdborder)) { 18318 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder; 18319 } 18320 $prevLastH = $this->lasth; 18321 // store some info for multicolumn mode 18322 if ($this->rtl) { 18323 $this->colxshift['x'] = $this->w - $this->x - $this->rMargin; 18324 } else { 18325 $this->colxshift['x'] = $this->x - $this->lMargin; 18326 } 18327 $this->colxshift['s'] = $cellspacing; 18328 $this->colxshift['p'] = $current_cell_padding; 18329 // ****** write the cell content ****** 18330 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false); 18331 // restore some values 18332 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 18333 $this->lasth = $prevLastH; 18334 $this->cell_padding = $old_cell_padding; 18335 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x; 18336 // update the end of row position 18337 if ($rowspan <= 1) { 18338 if (isset($dom[$trid]['endy'])) { 18339 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) { 18340 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']); 18341 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) { 18342 $dom[$trid]['endy'] = $this->y; 18343 } 18344 } else { 18345 $dom[$trid]['endy'] = $this->y; 18346 } 18347 if (isset($dom[$trid]['endpage'])) { 18348 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']); 18349 } else { 18350 $dom[$trid]['endpage'] = $this->page; 18351 } 18352 if (isset($dom[$trid]['endcolumn'])) { 18353 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']); 18354 } else { 18355 $dom[$trid]['endcolumn'] = $this->current_column; 18356 } 18357 } else { 18358 // account for row-spanned cells 18359 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x; 18360 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y; 18361 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page; 18362 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column; 18363 } 18364 if (isset($dom[$table_el]['rowspans'])) { 18365 // update endy and endpage on rowspanned cells 18366 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 18367 if ($trwsp['rowspan'] > 0) { 18368 if (isset($dom[$trid]['endpage'])) { 18369 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { 18370 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 18371 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) { 18372 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy']; 18373 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage']; 18374 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn']; 18375 } else { 18376 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm']; 18377 } 18378 } 18379 } 18380 } 18381 } 18382 $this->x += ($cellspacingx / 2); 18383 } else { 18384 // opening tag (or self-closing tag) 18385 if (!isset($opentagpos)) { 18386 if ($this->inxobj) { 18387 // we are inside an XObject template 18388 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']); 18389 } elseif (!$this->InFooter) { 18390 if (isset($this->footerlen[$this->page])) { 18391 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 18392 } else { 18393 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 18394 } 18395 $opentagpos = $this->footerpos[$this->page]; 18396 } 18397 } 18398 $dom = $this->openHTMLTagHandler($dom, $key, $cell); 18399 } 18400 } else { // closing tag 18401 $prev_numpages = $this->numpages; 18402 $old_bordermrk = $this->bordermrk[$this->page]; 18403 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney); 18404 if ($this->bordermrk[$this->page] > $old_bordermrk) { 18405 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk); 18406 } 18407 if ($prev_numpages > $this->numpages) { 18408 $startlinepage = $this->page; 18409 } 18410 } 18411 } elseif (strlen($dom[$key]['value']) > 0) { 18412 // print list-item 18413 if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) { 18414 $this->SetFont($pfontname, $pfontstyle, $pfontsize); 18415 $this->resetLastH(); 18416 $minstartliney = $this->y; 18417 $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize)); 18418 if (is_numeric($pfontsize) AND ($pfontsize > 0)) { 18419 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize); 18420 } 18421 $this->SetFont($curfontname, $curfontstyle, $curfontsize); 18422 $this->resetLastH(); 18423 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) { 18424 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize); 18425 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize); 18426 $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2; 18427 $minstartliney = min($this->y, $minstartliney); 18428 $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney); 18429 } 18430 } 18431 // text 18432 $this->htmlvspace = 0; 18433 $isRTLString = preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $dom[$key]['value']) || preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $dom[$key]['value']); 18434 if ((!$this->premode) AND $this->isRTLTextDir() AND !$isRTLString) { 18435 // reverse spaces order 18436 $lsp = ''; // left spaces 18437 $rsp = ''; // right spaces 18438 if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { 18439 $lsp = $matches[1]; 18440 } 18441 if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { 18442 $rsp = $matches[1]; 18443 } 18444 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp; 18445 } 18446 if ($newline) { 18447 if (!$this->premode) { 18448 $prelen = strlen($dom[$key]['value']); 18449 if ($this->isRTLTextDir() AND !$isRTLString) { 18450 // right trim except non-breaking space 18451 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']); 18452 } else { 18453 // left trim except non-breaking space 18454 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']); 18455 } 18456 $postlen = strlen($dom[$key]['value']); 18457 if (($postlen == 0) AND ($prelen > 0)) { 18458 $dom[$key]['trimmed_space'] = true; 18459 } 18460 } 18461 $newline = false; 18462 $firstblock = true; 18463 } else { 18464 $firstblock = false; 18465 // replace empty multiple spaces string with a single space 18466 $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']); 18467 } 18468 $strrest = ''; 18469 if ($this->rtl) { 18470 $this->x -= $this->textindent; 18471 } else { 18472 $this->x += $this->textindent; 18473 } 18474 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) { 18475 $strlinelen = $this->GetStringWidth($dom[$key]['value']); 18476 if (!empty($this->HREF) AND (isset($this->HREF['url']))) { 18477 // HTML <a> Link 18478 $hrefcolor = ''; 18479 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) { 18480 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor']; 18481 } 18482 $hrefstyle = -1; 18483 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) { 18484 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle']; 18485 } 18486 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true); 18487 } else { 18488 $wadj = 0; // space to leave for block continuity 18489 if ($this->rtl) { 18490 $cwa = ($this->x - $this->lMargin); 18491 } else { 18492 $cwa = ($this->w - $this->rMargin - $this->x); 18493 } 18494 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) { 18495 // check the next text blocks for continuity 18496 $nkey = ($key + 1); 18497 $write_block = true; 18498 $same_textdir = true; 18499 $tmp_fontname = $this->FontFamily; 18500 $tmp_fontstyle = $this->FontStyle; 18501 $tmp_fontsize = $this->FontSizePt; 18502 while ($write_block AND isset($dom[$nkey])) { 18503 if ($dom[$nkey]['tag']) { 18504 if ($dom[$nkey]['block']) { 18505 // end of block 18506 $write_block = false; 18507 } 18508 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily; 18509 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle; 18510 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt; 18511 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']); 18512 } else { 18513 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']); 18514 if (isset($nextstr[0]) AND $same_textdir) { 18515 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize); 18516 if (isset($nextstr[1])) { 18517 $write_block = false; 18518 } 18519 } 18520 } 18521 ++$nkey; 18522 } 18523 } 18524 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) { 18525 $wadj = 0; 18526 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']); 18527 $numblks = count($nextstr); 18528 if ($numblks > 1) { 18529 // try to split on blank spaces 18530 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)])); 18531 } else { 18532 // set the entire block on new line 18533 $wadj = $this->GetStringWidth($nextstr[0]); 18534 } 18535 } 18536 // check for reversed text direction 18537 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) { 18538 // LTR text on RTL direction or RTL text on LTR direction 18539 $reverse_dir = true; 18540 $this->rtl = !$this->rtl; 18541 $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems 18542 if ($this->rtl) { 18543 $this->x += $revshift; 18544 } else { 18545 $this->x -= $revshift; 18546 } 18547 $xws = $this->x; 18548 } 18549 // ****** write only until the end of the line and get the rest ****** 18550 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj); 18551 // restore default direction 18552 if ($reverse_dir AND ($wadj == 0)) { 18553 $this->x = $xws; 18554 $this->rtl = !$this->rtl; 18555 $reverse_dir = false; 18556 } 18557 } 18558 } 18559 $this->textindent = 0; 18560 if (strlen($strrest) > 0) { 18561 // store the remaining string on the previous $key position 18562 $this->newline = true; 18563 if ($strrest == $dom[$key]['value']) { 18564 // used to avoid infinite loop 18565 ++$loop; 18566 } else { 18567 $loop = 0; 18568 } 18569 $dom[$key]['value'] = $strrest; 18570 if ($cell) { 18571 if ($this->rtl) { 18572 $this->x -= $this->cell_padding['R']; 18573 } else { 18574 $this->x += $this->cell_padding['L']; 18575 } 18576 } 18577 if ($loop < 3) { 18578 --$key; 18579 } 18580 } else { 18581 $loop = 0; 18582 // add the positive font spacing of the last character (if any) 18583 if ($this->font_spacing > 0) { 18584 if ($this->rtl) { 18585 $this->x -= $this->font_spacing; 18586 } else { 18587 $this->x += $this->font_spacing; 18588 } 18589 } 18590 } 18591 } 18592 ++$key; 18593 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')) { 18594 // check if we are on a new page or on a new column 18595 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) { 18596 // we are on a new page or on a new column and the total object height is less than the available vertical space. 18597 // restore previous object 18598 $this->rollbackTransaction(true); 18599 // restore previous values 18600 foreach ($this_method_vars as $vkey => $vval) { 18601 $$vkey = $vval; 18602 } 18603 if (!empty($dom[$key]['thead'])) { 18604 $this->inthead = true; 18605 } 18606 // add a page (or trig AcceptPageBreak() for multicolumn mode) 18607 $pre_y = $this->y; 18608 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 18609 $startliney = $this->y; 18610 } 18611 $undo = true; // avoid infinite loop 18612 } else { 18613 $undo = false; 18614 } 18615 } 18616 } // end for each $key 18617 // align the last line 18618 if (isset($startlinex)) { 18619 $yshift = ($minstartliney - $startliney); 18620 if (($yshift > 0) OR ($this->page > $startlinepage)) { 18621 $yshift = 0; 18622 } 18623 $t_x = 0; 18624 // the last line must be shifted to be aligned as requested 18625 $linew = abs($this->endlinex - $startlinex); 18626 if ($this->inxobj) { 18627 // we are inside an XObject template 18628 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); 18629 if (isset($opentagpos)) { 18630 $midpos = $opentagpos; 18631 } else { 18632 $midpos = 0; 18633 } 18634 if ($midpos > 0) { 18635 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); 18636 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); 18637 } else { 18638 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); 18639 $pend = ''; 18640 } 18641 } else { 18642 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 18643 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 18644 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 18645 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 18646 } elseif (isset($opentagpos)) { 18647 $midpos = $opentagpos; 18648 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 18649 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 18650 $midpos = $this->footerpos[$startlinepage]; 18651 } else { 18652 $midpos = 0; 18653 } 18654 if ($midpos > 0) { 18655 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 18656 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 18657 } else { 18658 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 18659 $pend = ''; 18660 } 18661 } 18662 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { 18663 // calculate shifting amount 18664 $tw = $w; 18665 if ($this->lMargin != $prevlMargin) { 18666 $tw += ($prevlMargin - $this->lMargin); 18667 } 18668 if ($this->rMargin != $prevrMargin) { 18669 $tw += ($prevrMargin - $this->rMargin); 18670 } 18671 $one_space_width = $this->GetStringWidth(chr(32)); 18672 $no = 0; // number of spaces on a line contained on a single block 18673 if ($this->isRTLTextDir()) { // RTL 18674 // remove left space if exist 18675 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[('); 18676 if ($pos1 > 0) { 18677 $pos1 = intval($pos1); 18678 if ($this->isUnicodeFont()) { 18679 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32))); 18680 $spacelen = 2; 18681 } else { 18682 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32))); 18683 $spacelen = 1; 18684 } 18685 if ($pos1 == $pos2) { 18686 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); 18687 if (substr($pmid, $pos1, 4) == '[()]') { 18688 $linew -= $one_space_width; 18689 } elseif ($pos1 == strpos($pmid, '[(')) { 18690 $no = 1; 18691 } 18692 } 18693 } 18694 } else { // LTR 18695 // remove right space if exist 18696 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]'); 18697 if ($pos1 > 0) { 18698 $pos1 = intval($pos1); 18699 if ($this->isUnicodeFont()) { 18700 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2; 18701 $spacelen = 2; 18702 } else { 18703 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1; 18704 $spacelen = 1; 18705 } 18706 if ($pos1 == $pos2) { 18707 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); 18708 $linew -= $one_space_width; 18709 } 18710 } 18711 } 18712 $mdiff = ($tw - $linew); 18713 if ($plalign == 'C') { 18714 if ($this->rtl) { 18715 $t_x = -($mdiff / 2); 18716 } else { 18717 $t_x = ($mdiff / 2); 18718 } 18719 } elseif ($plalign == 'R') { 18720 // right alignment on LTR document 18721 $t_x = $mdiff; 18722 } elseif ($plalign == 'L') { 18723 // left alignment on RTL document 18724 $t_x = -$mdiff; 18725 } 18726 } // end if startlinex 18727 if (($t_x != 0) OR ($yshift < 0)) { 18728 // shift the line 18729 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k)); 18730 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; 18731 $endlinepos = strlen($pstart); 18732 if ($this->inxobj) { 18733 // we are inside an XObject template 18734 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; 18735 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 18736 if ($pak >= $pask) { 18737 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; 18738 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; 18739 } 18740 } 18741 } else { 18742 $this->setPageBuffer($startlinepage, $pstart.$pend); 18743 // shift the annotations and links 18744 if (isset($this->PageAnnots[$this->page])) { 18745 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 18746 if ($pak >= $pask) { 18747 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 18748 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 18749 } 18750 } 18751 } 18752 } 18753 $this->y -= $yshift; 18754 $yshift = 0; 18755 } 18756 } 18757 // restore previous values 18758 $this->setGraphicVars($gvars); 18759 if ($this->num_columns > 1) { 18760 $this->selectColumn(); 18761 } elseif ($this->page > $prevPage) { 18762 $this->lMargin = $this->pagedim[$this->page]['olm']; 18763 $this->rMargin = $this->pagedim[$this->page]['orm']; 18764 } 18765 // restore previous list state 18766 $this->cell_height_ratio = $prev_cell_height_ratio; 18767 $this->listnum = $prev_listnum; 18768 $this->listordered = $prev_listordered; 18769 $this->listcount = $prev_listcount; 18770 $this->lispacer = $prev_lispacer; 18771 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) { 18772 $this->Ln($this->lasth); 18773 if (($this->y < $maxbottomliney) AND ($startlinepage == $this->page)) { 18774 $this->y = $maxbottomliney; 18775 } 18776 } 18777 unset($dom); 18778 } 18779 18780 /** 18781 * Process opening tags. 18782 * @param $dom (array) html dom array 18783 * @param $key (int) current element id 18784 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 18785 * @return $dom array 18786 * @protected 18787 */ 18788 protected function openHTMLTagHandler($dom, $key, $cell) { 18789 $tag = $dom[$key]; 18790 $parent = $dom[($dom[$key]['parent'])]; 18791 $firsttag = ($key == 1); 18792 // check for text direction attribute 18793 if (isset($tag['dir'])) { 18794 $this->setTempRTL($tag['dir']); 18795 } else { 18796 $this->tmprtl = false; 18797 } 18798 if ($tag['block']) { 18799 $hbz = 0; // distance from y to line bottom 18800 $hb = 0; // vertical space between block tags 18801 // calculate vertical space for block tags 18802 if (isset($this->tagvspaces[$tag['value']][0]['h']) && !empty($this->tagvspaces[$tag['value']][0]['h']) && ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) { 18803 $cur_h = $this->tagvspaces[$tag['value']][0]['h']; 18804 } elseif (isset($tag['fontsize'])) { 18805 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k); 18806 } else { 18807 $cur_h = $this->getCellHeight($this->FontSize); 18808 } 18809 if (isset($this->tagvspaces[$tag['value']][0]['n'])) { 18810 $on = $this->tagvspaces[$tag['value']][0]['n']; 18811 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 18812 $on = 0.6; 18813 } else { 18814 $on = 1; 18815 } 18816 if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) { 18817 $hb = 0; 18818 } else { 18819 $hb = ($on * $cur_h); 18820 } 18821 if (($this->htmlvspace <= 0) AND ($on > 0)) { 18822 if (isset($parent['fontsize'])) { 18823 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); 18824 } else { 18825 $hbz = $this->getCellHeight($this->FontSize); 18826 } 18827 } 18828 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) { 18829 // fix vertical space after table 18830 $hbz = 0; 18831 } 18832 // closing vertical space 18833 $hbc = 0; 18834 if (isset($this->tagvspaces[$tag['value']][1]['h']) && !empty($this->tagvspaces[$tag['value']][1]['h']) && ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { 18835 $pre_h = $this->tagvspaces[$tag['value']][1]['h']; 18836 } elseif (isset($parent['fontsize'])) { 18837 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k); 18838 } else { 18839 $pre_h = $this->getCellHeight($this->FontSize); 18840 } 18841 if (isset($this->tagvspaces[$tag['value']][1]['n'])) { 18842 $cn = $this->tagvspaces[$tag['value']][1]['n']; 18843 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 18844 $cn = 0.6; 18845 } else { 18846 $cn = 1; 18847 } 18848 if (isset($this->tagvspaces[$tag['value']][1])) { 18849 $hbc = ($cn * $pre_h); 18850 } 18851 } 18852 // Opening tag 18853 switch($tag['value']) { 18854 case 'table': { 18855 $cp = 0; 18856 $cs = 0; 18857 $dom[$key]['rowspans'] = array(); 18858 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) { 18859 $this->htmlvspace = 0; 18860 // set table header 18861 if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) { 18862 // set table header 18863 $this->thead = $dom[$key]['thead']; 18864 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) { 18865 $this->theadMargins = array(); 18866 $this->theadMargins['cell_padding'] = $this->cell_padding; 18867 $this->theadMargins['lmargin'] = $this->lMargin; 18868 $this->theadMargins['rmargin'] = $this->rMargin; 18869 $this->theadMargins['page'] = $this->page; 18870 $this->theadMargins['cell'] = $cell; 18871 $this->theadMargins['gvars'] = $this->getGraphicVars(); 18872 } 18873 } 18874 } 18875 // store current margins and page 18876 $dom[$key]['old_cell_padding'] = $this->cell_padding; 18877 if (isset($tag['attribute']['cellpadding'])) { 18878 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px'); 18879 $this->SetCellPadding($pad); 18880 } elseif (isset($tag['padding'])) { 18881 $this->cell_padding = $tag['padding']; 18882 } 18883 if (isset($tag['attribute']['cellspacing'])) { 18884 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 18885 } elseif (isset($tag['border-spacing'])) { 18886 $cs = $tag['border-spacing']['V']; 18887 } 18888 $prev_y = $this->y; 18889 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) { 18890 $this->inthead = true; 18891 // add a page (or trig AcceptPageBreak() for multicolumn mode) 18892 $this->checkPageBreak($this->PageBreakTrigger + 1); 18893 } 18894 break; 18895 } 18896 case 'tr': { 18897 // array of columns positions 18898 $dom[$key]['cellpos'] = array(); 18899 break; 18900 } 18901 case 'hr': { 18902 if ((isset($tag['height'])) AND ($tag['height'] != '')) { 18903 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px'); 18904 } else { 18905 $hrHeight = $this->GetLineWidth(); 18906 } 18907 $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag); 18908 $x = $this->GetX(); 18909 $y = $this->GetY(); 18910 $wtmp = $this->w - $this->lMargin - $this->rMargin; 18911 if ($cell) { 18912 $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']); 18913 } 18914 if ((isset($tag['width'])) AND ($tag['width'] != '')) { 18915 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px'); 18916 } else { 18917 $hrWidth = $wtmp; 18918 } 18919 $prevlinewidth = $this->GetLineWidth(); 18920 $this->SetLineWidth($hrHeight); 18921 18922 $lineStyle = array( 18923 'color' => $tag['fgcolor'], 18924 'cap' => $tag['style']['cap'], 18925 'join' => $tag['style']['join'], 18926 'dash' => $tag['style']['dash'], 18927 'phase' => $tag['style']['phase'], 18928 ); 18929 18930 $lineStyle = array_filter($lineStyle); 18931 18932 $this->Line($x, $y, $x + $hrWidth, $y, $lineStyle); 18933 $this->SetLineWidth($prevlinewidth); 18934 $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)])); 18935 break; 18936 } 18937 case 'a': { 18938 if (array_key_exists('href', $tag['attribute'])) { 18939 $this->HREF['url'] = $tag['attribute']['href']; 18940 } 18941 break; 18942 } 18943 case 'img': { 18944 if (empty($tag['attribute']['src'])) { 18945 break; 18946 } 18947 $imgsrc = $tag['attribute']['src']; 18948 if ($imgsrc[0] === '@') { 18949 // data stream 18950 $imgsrc = '@'.base64_decode(substr($imgsrc, 1)); 18951 $type = ''; 18952 } elseif ( $this->allowLocalFiles && substr($imgsrc, 0, 7) === 'file://') { 18953 // get image type from a local file path 18954 $imgsrc = substr($imgsrc, 7); 18955 $type = TCPDF_IMAGES::getImageFileType($imgsrc); 18956 } else { 18957 if (($imgsrc[0] === '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { 18958 // fix image path 18959 $findroot = strpos($imgsrc, $_SERVER['DOCUMENT_ROOT']); 18960 if (($findroot === false) OR ($findroot > 1)) { 18961 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { 18962 $imgsrc = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$imgsrc; 18963 } else { 18964 $imgsrc = $_SERVER['DOCUMENT_ROOT'].$imgsrc; 18965 } 18966 } 18967 $imgsrc = urldecode($imgsrc); 18968 $testscrtype = @parse_url($imgsrc); 18969 if (empty($testscrtype['query'])) { 18970 // convert URL to server path 18971 $imgsrc = str_replace(K_PATH_URL, K_PATH_MAIN, $imgsrc); 18972 } elseif (preg_match('|^https?://|', $imgsrc) !== 1) { 18973 // convert URL to server path 18974 $imgsrc = str_replace(K_PATH_MAIN, K_PATH_URL, $imgsrc); 18975 } 18976 } 18977 // get image type 18978 $type = TCPDF_IMAGES::getImageFileType($imgsrc); 18979 } 18980 if (!isset($tag['width'])) { 18981 $tag['width'] = 0; 18982 } 18983 if (!isset($tag['height'])) { 18984 $tag['height'] = 0; 18985 } 18986 //if (!isset($tag['attribute']['align'])) { 18987 // the only alignment supported is "bottom" 18988 // further development is required for other modes. 18989 $tag['attribute']['align'] = 'bottom'; 18990 //} 18991 switch($tag['attribute']['align']) { 18992 case 'top': { 18993 $align = 'T'; 18994 break; 18995 } 18996 case 'middle': { 18997 $align = 'M'; 18998 break; 18999 } 19000 case 'bottom': { 19001 $align = 'B'; 19002 break; 19003 } 19004 default: { 19005 $align = 'B'; 19006 break; 19007 } 19008 } 19009 $prevy = $this->y; 19010 $xpos = $this->x; 19011 $imglink = ''; 19012 if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) { 19013 $imglink = $this->HREF['url']; 19014 if ($imglink[0] == '#') { 19015 // convert url to internal link 19016 $lnkdata = explode(',', $imglink); 19017 if (isset($lnkdata[0])) { 19018 $page = intval(substr($lnkdata[0], 1)); 19019 if (empty($page) OR ($page <= 0)) { 19020 $page = $this->page; 19021 } 19022 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { 19023 $lnky = floatval($lnkdata[1]); 19024 } else { 19025 $lnky = 0; 19026 } 19027 $imglink = $this->AddLink(); 19028 $this->SetLink($imglink, $lnky, $page); 19029 } 19030 } 19031 } 19032 $border = 0; 19033 if (isset($tag['border']) AND !empty($tag['border'])) { 19034 // currently only support 1 (frame) or a combination of 'LTRB' 19035 $border = $tag['border']; 19036 } 19037 $iw = ''; 19038 if (isset($tag['width'])) { 19039 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false); 19040 } 19041 $ih = ''; 19042 if (isset($tag['height'])) { 19043 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false); 19044 } 19045 if (($type == 'eps') OR ($type == 'ai')) { 19046 $this->ImageEps($imgsrc, $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true); 19047 } elseif ($type == 'svg') { 19048 $this->ImageSVG($imgsrc, $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true); 19049 } else { 19050 $this->Image($imgsrc, $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true); 19051 } 19052 switch($align) { 19053 case 'T': { 19054 $this->y = $prevy; 19055 break; 19056 } 19057 case 'M': { 19058 $this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2); 19059 break; 19060 } 19061 case 'B': { 19062 $this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio)); 19063 break; 19064 } 19065 } 19066 break; 19067 } 19068 case 'dl': { 19069 ++$this->listnum; 19070 if ($this->listnum == 1) { 19071 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19072 } else { 19073 $this->addHTMLVertSpace(0, 0, $cell, $firsttag); 19074 } 19075 break; 19076 } 19077 case 'dt': { 19078 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19079 break; 19080 } 19081 case 'dd': { 19082 if ($this->rtl) { 19083 $this->rMargin += $this->listindent; 19084 } else { 19085 $this->lMargin += $this->listindent; 19086 } 19087 ++$this->listindentlevel; 19088 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19089 break; 19090 } 19091 case 'ul': 19092 case 'ol': { 19093 ++$this->listnum; 19094 if ($tag['value'] == 'ol') { 19095 $this->listordered[$this->listnum] = true; 19096 } else { 19097 $this->listordered[$this->listnum] = false; 19098 } 19099 if (isset($tag['attribute']['start'])) { 19100 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1; 19101 } else { 19102 $this->listcount[$this->listnum] = 0; 19103 } 19104 if ($this->rtl) { 19105 $this->rMargin += $this->listindent; 19106 $this->x -= $this->listindent; 19107 } else { 19108 $this->lMargin += $this->listindent; 19109 $this->x += $this->listindent; 19110 } 19111 ++$this->listindentlevel; 19112 if ($this->listnum == 1) { 19113 if ($key > 1) { 19114 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19115 } 19116 } else { 19117 $this->addHTMLVertSpace(0, 0, $cell, $firsttag); 19118 } 19119 break; 19120 } 19121 case 'li': { 19122 if ($key > 2) { 19123 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19124 } 19125 if ($this->listordered[$this->listnum]) { 19126 // ordered item 19127 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) { 19128 $this->lispacer = $parent['attribute']['type']; 19129 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) { 19130 $this->lispacer = $parent['listtype']; 19131 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) { 19132 $this->lispacer = $this->lisymbol; 19133 } else { 19134 $this->lispacer = '#'; 19135 } 19136 ++$this->listcount[$this->listnum]; 19137 if (isset($tag['attribute']['value'])) { 19138 $this->listcount[$this->listnum] = intval($tag['attribute']['value']); 19139 } 19140 } else { 19141 // unordered item 19142 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) { 19143 $this->lispacer = $parent['attribute']['type']; 19144 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) { 19145 $this->lispacer = $parent['listtype']; 19146 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) { 19147 $this->lispacer = $this->lisymbol; 19148 } else { 19149 $this->lispacer = '!'; 19150 } 19151 } 19152 break; 19153 } 19154 case 'blockquote': { 19155 if ($this->rtl) { 19156 $this->rMargin += $this->listindent; 19157 } else { 19158 $this->lMargin += $this->listindent; 19159 } 19160 ++$this->listindentlevel; 19161 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19162 break; 19163 } 19164 case 'br': { 19165 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19166 break; 19167 } 19168 case 'div': { 19169 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19170 break; 19171 } 19172 case 'p': { 19173 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19174 break; 19175 } 19176 case 'pre': { 19177 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19178 $this->premode = true; 19179 break; 19180 } 19181 case 'sup': { 19182 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k)); 19183 break; 19184 } 19185 case 'sub': { 19186 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k)); 19187 break; 19188 } 19189 case 'h1': 19190 case 'h2': 19191 case 'h3': 19192 case 'h4': 19193 case 'h5': 19194 case 'h6': { 19195 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 19196 break; 19197 } 19198 // Form fields (since 4.8.000 - 2009-09-07) 19199 case 'form': { 19200 if (isset($tag['attribute']['action'])) { 19201 $this->form_action = $tag['attribute']['action']; 19202 } else { 19203 $this->Error('Please explicitly set action attribute path!'); 19204 } 19205 if (isset($tag['attribute']['enctype'])) { 19206 $this->form_enctype = $tag['attribute']['enctype']; 19207 } else { 19208 $this->form_enctype = 'application/x-www-form-urlencoded'; 19209 } 19210 if (isset($tag['attribute']['method'])) { 19211 $this->form_mode = $tag['attribute']['method']; 19212 } else { 19213 $this->form_mode = 'post'; 19214 } 19215 break; 19216 } 19217 case 'input': { 19218 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19219 $name = $tag['attribute']['name']; 19220 } else { 19221 break; 19222 } 19223 $prop = array(); 19224 $opt = array(); 19225 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) { 19226 $prop['readonly'] = true; 19227 } 19228 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) { 19229 $value = $tag['attribute']['value']; 19230 } 19231 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) { 19232 $opt['maxlen'] = intval($tag['attribute']['maxlength']); 19233 } 19234 $h = $this->getCellHeight($this->FontSize); 19235 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) { 19236 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2; 19237 } else { 19238 $w = $h; 19239 } 19240 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) { 19241 $checked = true; 19242 } else { 19243 $checked = false; 19244 } 19245 if (isset($tag['align'])) { 19246 switch ($tag['align']) { 19247 case 'C': { 19248 $opt['q'] = 1; 19249 break; 19250 } 19251 case 'R': { 19252 $opt['q'] = 2; 19253 break; 19254 } 19255 case 'L': 19256 default: { 19257 break; 19258 } 19259 } 19260 } 19261 switch ($tag['attribute']['type']) { 19262 case 'text': { 19263 if (isset($value)) { 19264 $opt['v'] = $value; 19265 } 19266 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19267 break; 19268 } 19269 case 'password': { 19270 if (isset($value)) { 19271 $opt['v'] = $value; 19272 } 19273 $prop['password'] = 'true'; 19274 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19275 break; 19276 } 19277 case 'checkbox': { 19278 if (!isset($value)) { 19279 break; 19280 } 19281 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false); 19282 break; 19283 } 19284 case 'radio': { 19285 if (!isset($value)) { 19286 break; 19287 } 19288 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false); 19289 break; 19290 } 19291 case 'submit': { 19292 if (!isset($value)) { 19293 $value = 'submit'; 19294 } 19295 $w = $this->GetStringWidth($value) * 1.5; 19296 $h *= 1.6; 19297 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19298 $action = array(); 19299 $action['S'] = 'SubmitForm'; 19300 $action['F'] = $this->form_action; 19301 if ($this->form_enctype != 'FDF') { 19302 $action['Flags'] = array('ExportFormat'); 19303 } 19304 if ($this->form_mode == 'get') { 19305 $action['Flags'] = array('GetMethod'); 19306 } 19307 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false); 19308 break; 19309 } 19310 case 'reset': { 19311 if (!isset($value)) { 19312 $value = 'reset'; 19313 } 19314 $w = $this->GetStringWidth($value) * 1.5; 19315 $h *= 1.6; 19316 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19317 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false); 19318 break; 19319 } 19320 case 'file': { 19321 $prop['fileSelect'] = 'true'; 19322 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19323 if (!isset($value)) { 19324 $value = '*'; 19325 } 19326 $w = $this->GetStringWidth($value) * 2; 19327 $h *= 1.2; 19328 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19329 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();'; 19330 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19331 break; 19332 } 19333 case 'hidden': { 19334 if (isset($value)) { 19335 $opt['v'] = $value; 19336 } 19337 $opt['f'] = array('invisible', 'hidden'); 19338 $this->TextField($name, 0, 0, $prop, $opt, '', '', false); 19339 break; 19340 } 19341 case 'image': { 19342 // THIS TYPE MUST BE FIXED 19343 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) { 19344 $img = $tag['attribute']['src']; 19345 } else { 19346 break; 19347 } 19348 $value = 'img'; 19349 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false)); 19350 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 19351 $jsaction = $tag['attribute']['onclick']; 19352 } else { 19353 $jsaction = ''; 19354 } 19355 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19356 break; 19357 } 19358 case 'button': { 19359 if (!isset($value)) { 19360 $value = ' '; 19361 } 19362 $w = $this->GetStringWidth($value) * 1.5; 19363 $h *= 1.6; 19364 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 19365 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 19366 $jsaction = $tag['attribute']['onclick']; 19367 } else { 19368 $jsaction = ''; 19369 } 19370 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 19371 break; 19372 } 19373 } 19374 break; 19375 } 19376 case 'textarea': { 19377 $prop = array(); 19378 $opt = array(); 19379 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) { 19380 $prop['readonly'] = true; 19381 } 19382 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19383 $name = $tag['attribute']['name']; 19384 } else { 19385 break; 19386 } 19387 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) { 19388 $opt['v'] = $tag['attribute']['value']; 19389 } 19390 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) { 19391 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2; 19392 } else { 19393 $w = 40; 19394 } 19395 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) { 19396 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize); 19397 } else { 19398 $h = 10; 19399 } 19400 $prop['multiline'] = 'true'; 19401 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 19402 break; 19403 } 19404 case 'select': { 19405 $h = $this->getCellHeight($this->FontSize); 19406 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) { 19407 $h *= ($tag['attribute']['size'] + 1); 19408 } 19409 $prop = array(); 19410 $opt = array(); 19411 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) { 19412 $name = $tag['attribute']['name']; 19413 } else { 19414 break; 19415 } 19416 $w = 0; 19417 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) { 19418 $options = explode('#!NwL!#', $tag['attribute']['opt']); 19419 $values = array(); 19420 foreach ($options as $val) { 19421 if (strpos($val, '#!TaB!#') !== false) { 19422 $opts = explode('#!TaB!#', $val); 19423 $values[] = $opts; 19424 $w = max($w, $this->GetStringWidth($opts[1])); 19425 } else { 19426 $values[] = $val; 19427 $w = max($w, $this->GetStringWidth($val)); 19428 } 19429 } 19430 } else { 19431 break; 19432 } 19433 $w *= 2; 19434 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) { 19435 $prop['multipleSelection'] = 'true'; 19436 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false); 19437 } else { 19438 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false); 19439 } 19440 break; 19441 } 19442 case 'tcpdf': { 19443 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) { 19444 // Special tag used to call TCPDF methods 19445 if (isset($tag['attribute']['method'])) { 19446 $tcpdf_method = $tag['attribute']['method']; 19447 if (method_exists($this, $tcpdf_method)) { 19448 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) { 19449 $params = $this->unserializeTCPDFtagParameters($tag['attribute']['params']); 19450 call_user_func_array(array($this, $tcpdf_method), $params); 19451 } else { 19452 $this->$tcpdf_method(); 19453 } 19454 $this->newline = true; 19455 } 19456 } 19457 } 19458 break; 19459 } 19460 default: { 19461 break; 19462 } 19463 } 19464 // define tags that support borders and background colors 19465 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table'); 19466 if (in_array($tag['value'], $bordertags)) { 19467 // set border 19468 $dom[$key]['borderposition'] = $this->getBorderStartPosition(); 19469 } 19470 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) { 19471 $pba = $dom[$key]['attribute']['pagebreakafter']; 19472 // check for pagebreak 19473 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 19474 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19475 $this->checkPageBreak($this->PageBreakTrigger + 1); 19476 } 19477 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 19478 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 19479 // add a page (or trig AcceptPageBreak() for multicolumn mode) 19480 $this->checkPageBreak($this->PageBreakTrigger + 1); 19481 } 19482 } 19483 return $dom; 19484 } 19485 19486 /** 19487 * Process closing tags. 19488 * @param $dom (array) html dom array 19489 * @param $key (int) current element id 19490 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 19491 * @param $maxbottomliney (int) maximum y value of current line 19492 * @return $dom array 19493 * @protected 19494 */ 19495 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) { 19496 $tag = $dom[$key]; 19497 $parent = $dom[($dom[$key]['parent'])]; 19498 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker'))); 19499 $in_table_head = false; 19500 // maximum x position (used to draw borders) 19501 if ($this->rtl) { 19502 $xmax = $this->w; 19503 } else { 19504 $xmax = 0; 19505 } 19506 if ($tag['block']) { 19507 $hbz = 0; // distance from y to line bottom 19508 $hb = 0; // vertical space between block tags 19509 // calculate vertical space for block tags 19510 if (isset($this->tagvspaces[$tag['value']][1]['h']) && !empty($this->tagvspaces[$tag['value']][1]['h']) && ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { 19511 $pre_h = $this->tagvspaces[$tag['value']][1]['h']; 19512 } elseif (isset($parent['fontsize'])) { 19513 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k); 19514 } else { 19515 $pre_h = $this->getCellHeight($this->FontSize); 19516 } 19517 if (isset($this->tagvspaces[$tag['value']][1]['n'])) { 19518 $cn = $this->tagvspaces[$tag['value']][1]['n']; 19519 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 19520 $cn = 0.6; 19521 } else { 19522 $cn = 1; 19523 } 19524 if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) { 19525 $hb = 0; 19526 } else { 19527 $hb = ($cn * $pre_h); 19528 } 19529 if ($maxbottomliney > $this->PageBreakTrigger) { 19530 $hbz = $this->getCellHeight($this->FontSize); 19531 } elseif ($this->y < $maxbottomliney) { 19532 $hbz = ($maxbottomliney - $this->y); 19533 } 19534 } 19535 // Closing tag 19536 switch($tag['value']) { 19537 case 'tr': { 19538 $table_el = $dom[($dom[$key]['parent'])]['parent']; 19539 if (!isset($parent['endy'])) { 19540 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 19541 $parent['endy'] = $this->y; 19542 } 19543 if (!isset($parent['endpage'])) { 19544 $dom[($dom[$key]['parent'])]['endpage'] = $this->page; 19545 $parent['endpage'] = $this->page; 19546 } 19547 if (!isset($parent['endcolumn'])) { 19548 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column; 19549 $parent['endcolumn'] = $this->current_column; 19550 } 19551 // update row-spanned cells 19552 if (isset($dom[$table_el]['rowspans'])) { 19553 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19554 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1; 19555 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19556 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) { 19557 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']); 19558 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) { 19559 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 19560 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 19561 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; 19562 } 19563 } 19564 } 19565 // report new endy and endpage to the rowspanned cells 19566 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19567 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19568 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']); 19569 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 19570 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']); 19571 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; 19572 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']); 19573 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 19574 } 19575 } 19576 // update remaining rowspanned cells 19577 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 19578 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 19579 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage']; 19580 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn']; 19581 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy']; 19582 } 19583 } 19584 } 19585 $prev_page = $this->page; 19586 $this->setPage($dom[($dom[$key]['parent'])]['endpage']); 19587 if ($this->num_columns > 1) { 19588 if (($prev_page < $this->page) 19589 AND ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1))) 19590 OR ($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']))) { 19591 // page jump 19592 $this->selectColumn(0); 19593 $dom[($dom[$key]['parent'])]['endcolumn'] = 0; 19594 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 19595 } else { 19596 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']); 19597 $this->y = $dom[($dom[$key]['parent'])]['endy']; 19598 } 19599 } else { 19600 $this->y = $dom[($dom[$key]['parent'])]['endy']; 19601 } 19602 if (isset($dom[$table_el]['attribute']['cellspacing'])) { 19603 $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px'); 19604 } elseif (isset($dom[$table_el]['border-spacing'])) { 19605 $this->y += $dom[$table_el]['border-spacing']['V']; 19606 } 19607 $this->Ln(0, $cell); 19608 if ($this->current_column == $parent['startcolumn']) { 19609 $this->x = $parent['startx']; 19610 } 19611 // account for booklet mode 19612 if ($this->page > $parent['startpage']) { 19613 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) { 19614 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']); 19615 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) { 19616 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']); 19617 } 19618 } 19619 break; 19620 } 19621 case 'tablehead': 19622 // closing tag used for the thead part 19623 $in_table_head = true; 19624 $this->inthead = false; 19625 case 'table': { 19626 $table_el = $parent; 19627 // set default border 19628 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) { 19629 // set default border 19630 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0))); 19631 } else { 19632 $border = 0; 19633 } 19634 $default_border = $border; 19635 // fix bottom line alignment of last line before page break 19636 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) { 19637 // update row-spanned cells 19638 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 19639 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 19640 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) { 19641 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey; 19642 } 19643 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) { 19644 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1; 19645 } 19646 } 19647 } 19648 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) { 19649 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm']; 19650 $dom[$prevtrkey]['endy'] = $pgendy; 19651 // update row-spanned cells 19652 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 19653 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 19654 if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) { 19655 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy; 19656 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1; 19657 } 19658 } 19659 } 19660 } 19661 $prevtrkey = $trkey; 19662 $table_el = $dom[($dom[$key]['parent'])]; 19663 } 19664 // for each row 19665 if (count($table_el['trids']) > 0) { 19666 unset($xmax); 19667 } 19668 foreach ($table_el['trids'] as $j => $trkey) { 19669 $parent = $dom[$trkey]; 19670 if (!isset($xmax)) { 19671 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx']; 19672 } 19673 // for each cell on the row 19674 foreach ($parent['cellpos'] as $k => $cellpos) { 19675 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) { 19676 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx']; 19677 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx']; 19678 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy']; 19679 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage']; 19680 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage']; 19681 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn']; 19682 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn']; 19683 } else { 19684 $endy = $parent['endy']; 19685 $startpage = $parent['startpage']; 19686 $endpage = $parent['endpage']; 19687 $startcolumn = $parent['startcolumn']; 19688 $endcolumn = $parent['endcolumn']; 19689 } 19690 if ($this->num_columns == 0) { 19691 $this->num_columns = 1; 19692 } 19693 if (isset($cellpos['border'])) { 19694 $border = $cellpos['border']; 19695 } 19696 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { 19697 $this->SetFillColorArray($cellpos['bgcolor']); 19698 $fill = true; 19699 } else { 19700 $fill = false; 19701 } 19702 $x = $cellpos['startx']; 19703 $y = $parent['starty']; 19704 $starty = $y; 19705 $w = abs($cellpos['endx'] - $cellpos['startx']); 19706 // get border modes 19707 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 19708 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 19709 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 19710 // design borders around HTML cells. 19711 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 19712 $ccode = ''; 19713 $this->setPage($page); 19714 if ($this->num_columns < 2) { 19715 // single-column mode 19716 $this->x = $x; 19717 $this->y = $this->tMargin; 19718 } 19719 // account for margin changes 19720 if ($page > $startpage) { 19721 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 19722 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 19723 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 19724 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 19725 } 19726 } 19727 if ($startpage == $endpage) { // single page 19728 $deltacol = 0; 19729 $deltath = 0; 19730 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 19731 $this->selectColumn($column); 19732 if ($startcolumn == $endcolumn) { // single column 19733 $cborder = $border; 19734 $h = $endy - $parent['starty']; 19735 $this->y = $y; 19736 $this->x = $x; 19737 } elseif ($column == $startcolumn) { // first column 19738 $cborder = $border_start; 19739 $this->y = $starty; 19740 $this->x = $x; 19741 $h = $this->h - $this->y - $this->bMargin; 19742 if ($this->rtl) { 19743 $deltacol = $this->x + $this->rMargin - $this->w; 19744 } else { 19745 $deltacol = $this->x - $this->lMargin; 19746 } 19747 } elseif ($column == $endcolumn) { // end column 19748 $cborder = $border_end; 19749 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19750 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19751 } 19752 $this->x += $deltacol; 19753 $h = $endy - $this->y; 19754 } else { // middle column 19755 $cborder = $border_middle; 19756 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19757 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19758 } 19759 $this->x += $deltacol; 19760 $h = $this->h - $this->y - $this->bMargin; 19761 } 19762 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19763 } // end for each column 19764 } elseif ($page == $startpage) { // first page 19765 $deltacol = 0; 19766 $deltath = 0; 19767 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 19768 $this->selectColumn($column); 19769 if ($column == $startcolumn) { // first column 19770 $cborder = $border_start; 19771 $this->y = $starty; 19772 $this->x = $x; 19773 $h = $this->h - $this->y - $this->bMargin; 19774 if ($this->rtl) { 19775 $deltacol = $this->x + $this->rMargin - $this->w; 19776 } else { 19777 $deltacol = $this->x - $this->lMargin; 19778 } 19779 } else { // middle column 19780 $cborder = $border_middle; 19781 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19782 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19783 } 19784 $this->x += $deltacol; 19785 $h = $this->h - $this->y - $this->bMargin; 19786 } 19787 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19788 } // end for each column 19789 } elseif ($page == $endpage) { // last page 19790 $deltacol = 0; 19791 $deltath = 0; 19792 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 19793 $this->selectColumn($column); 19794 if ($column == $endcolumn) { // end column 19795 $cborder = $border_end; 19796 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19797 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19798 } 19799 $this->x += $deltacol; 19800 $h = $endy - $this->y; 19801 } else { // middle column 19802 $cborder = $border_middle; 19803 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19804 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19805 } 19806 $this->x += $deltacol; 19807 $h = $this->h - $this->y - $this->bMargin; 19808 } 19809 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19810 } // end for each column 19811 } else { // middle page 19812 $deltacol = 0; 19813 $deltath = 0; 19814 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 19815 $this->selectColumn($column); 19816 $cborder = $border_middle; 19817 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 19818 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 19819 } 19820 $this->x += $deltacol; 19821 $h = $this->h - $this->y - $this->bMargin; 19822 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 19823 } // end for each column 19824 } 19825 if (!empty($cborder) OR !empty($fill)) { 19826 $offsetlen = strlen($ccode); 19827 // draw border and fill 19828 if ($this->inxobj) { 19829 // we are inside an XObject template 19830 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 19831 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 19832 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 19833 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 19834 } else { 19835 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 19836 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 19837 } 19838 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 19839 $pstart = substr($pagebuff, 0, $pagemark); 19840 $pend = substr($pagebuff, $pagemark); 19841 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 19842 } else { 19843 // draw border and fill 19844 if (end($this->transfmrk[$this->page]) !== false) { 19845 $pagemarkkey = key($this->transfmrk[$this->page]); 19846 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 19847 } elseif ($this->InFooter) { 19848 $pagemark = $this->footerpos[$this->page]; 19849 } else { 19850 $pagemark = $this->intmrk[$this->page]; 19851 } 19852 $pagebuff = $this->getPageBuffer($this->page); 19853 $pstart = substr($pagebuff, 0, $pagemark); 19854 $pend = substr($pagebuff, $pagemark); 19855 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 19856 } 19857 } 19858 } // end for each page 19859 // restore default border 19860 $border = $default_border; 19861 } // end for each cell on the row 19862 if (isset($table_el['attribute']['cellspacing'])) { 19863 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px'); 19864 } elseif (isset($table_el['border-spacing'])) { 19865 $this->y += $table_el['border-spacing']['V']; 19866 } 19867 $this->Ln(0, $cell); 19868 $this->x = $parent['startx']; 19869 if ($endpage > $startpage) { 19870 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) { 19871 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']); 19872 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) { 19873 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']); 19874 } 19875 } 19876 } 19877 if (!$in_table_head) { // we are not inside a thead section 19878 $this->cell_padding = isset($table_el['old_cell_padding']) ? $table_el['old_cell_padding'] : null; 19879 // reset row height 19880 $this->resetLastH(); 19881 if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) { 19882 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]); 19883 if (($plendiff > 0) AND ($plendiff < 60)) { 19884 $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff); 19885 if (substr($pagediff, 0, 5) == 'BT /F') { 19886 // the difference is only a font setting 19887 $plendiff = 0; 19888 } 19889 } 19890 if ($plendiff == 0) { 19891 // remove last blank page 19892 $this->deletePage($this->numpages); 19893 } 19894 } 19895 if (isset($this->theadMargins['top'])) { 19896 // restore top margin 19897 $this->tMargin = $this->theadMargins['top']; 19898 } 19899 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) { 19900 // reset main table header 19901 $this->thead = ''; 19902 $this->theadMargins = array(); 19903 $this->pagedim[$this->page]['tm'] = $this->tMargin; 19904 } 19905 } 19906 $parent = $table_el; 19907 break; 19908 } 19909 case 'a': { 19910 $this->HREF = array(); 19911 break; 19912 } 19913 case 'sup': { 19914 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k)); 19915 break; 19916 } 19917 case 'sub': { 19918 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k)); 19919 break; 19920 } 19921 case 'div': { 19922 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19923 break; 19924 } 19925 case 'blockquote': { 19926 if ($this->rtl) { 19927 $this->rMargin -= $this->listindent; 19928 } else { 19929 $this->lMargin -= $this->listindent; 19930 } 19931 --$this->listindentlevel; 19932 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19933 break; 19934 } 19935 case 'p': { 19936 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19937 break; 19938 } 19939 case 'pre': { 19940 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19941 $this->premode = false; 19942 break; 19943 } 19944 case 'dl': { 19945 --$this->listnum; 19946 if ($this->listnum <= 0) { 19947 $this->listnum = 0; 19948 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19949 } else { 19950 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19951 } 19952 $this->resetLastH(); 19953 break; 19954 } 19955 case 'dt': { 19956 $this->lispacer = ''; 19957 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19958 break; 19959 } 19960 case 'dd': { 19961 $this->lispacer = ''; 19962 if ($this->rtl) { 19963 $this->rMargin -= $this->listindent; 19964 } else { 19965 $this->lMargin -= $this->listindent; 19966 } 19967 --$this->listindentlevel; 19968 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19969 break; 19970 } 19971 case 'ul': 19972 case 'ol': { 19973 --$this->listnum; 19974 $this->lispacer = ''; 19975 if ($this->rtl) { 19976 $this->rMargin -= $this->listindent; 19977 } else { 19978 $this->lMargin -= $this->listindent; 19979 } 19980 --$this->listindentlevel; 19981 if ($this->listnum <= 0) { 19982 $this->listnum = 0; 19983 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 19984 } else { 19985 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19986 } 19987 $this->resetLastH(); 19988 break; 19989 } 19990 case 'li': { 19991 $this->lispacer = ''; 19992 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 19993 break; 19994 } 19995 case 'h1': 19996 case 'h2': 19997 case 'h3': 19998 case 'h4': 19999 case 'h5': 20000 case 'h6': { 20001 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 20002 break; 20003 } 20004 // Form fields (since 4.8.000 - 2009-09-07) 20005 case 'form': { 20006 $this->form_action = ''; 20007 $this->form_enctype = 'application/x-www-form-urlencoded'; 20008 break; 20009 } 20010 default : { 20011 break; 20012 } 20013 } 20014 // draw border and background (if any) 20015 $this->drawHTMLTagBorder($parent, $xmax); 20016 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) { 20017 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter']; 20018 // check for pagebreak 20019 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 20020 // add a page (or trig AcceptPageBreak() for multicolumn mode) 20021 $this->checkPageBreak($this->PageBreakTrigger + 1); 20022 } 20023 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 20024 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 20025 // add a page (or trig AcceptPageBreak() for multicolumn mode) 20026 $this->checkPageBreak($this->PageBreakTrigger + 1); 20027 } 20028 } 20029 $this->tmprtl = false; 20030 return $dom; 20031 } 20032 20033 /** 20034 * Add vertical spaces if needed. 20035 * @param $hbz (string) Distance between current y and line bottom. 20036 * @param $hb (string) The height of the break. 20037 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). 20038 * @param $firsttag (boolean) set to true when the tag is the first. 20039 * @param $lasttag (boolean) set to true when the tag is the last. 20040 * @protected 20041 */ 20042 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) { 20043 if ($firsttag) { 20044 $this->Ln(0, $cell); 20045 $this->htmlvspace = 0; 20046 return; 20047 } 20048 if ($lasttag) { 20049 $this->Ln($hbz, $cell); 20050 $this->htmlvspace = 0; 20051 return; 20052 } 20053 if ($hb < $this->htmlvspace) { 20054 $hd = 0; 20055 } else { 20056 $hd = $hb - $this->htmlvspace; 20057 $this->htmlvspace = $hb; 20058 } 20059 $this->Ln(($hbz + $hd), $cell); 20060 } 20061 20062 /** 20063 * Return the starting coordinates to draw an html border 20064 * @return array containing top-left border coordinates 20065 * @protected 20066 * @since 5.7.000 (2010-08-03) 20067 */ 20068 protected function getBorderStartPosition() { 20069 if ($this->rtl) { 20070 $xmax = $this->lMargin; 20071 } else { 20072 $xmax = $this->w - $this->rMargin; 20073 } 20074 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax); 20075 } 20076 20077 /** 20078 * Draw an HTML block border and fill 20079 * @param $tag (array) array of tag properties. 20080 * @param $xmax (int) end X coordinate for border. 20081 * @protected 20082 * @since 5.7.000 (2010-08-03) 20083 */ 20084 protected function drawHTMLTagBorder($tag, $xmax) { 20085 if (!isset($tag['borderposition'])) { 20086 // nothing to draw 20087 return; 20088 } 20089 $prev_x = $this->x; 20090 $prev_y = $this->y; 20091 $prev_lasth = $this->lasth; 20092 $border = 0; 20093 $fill = false; 20094 $this->lasth = 0; 20095 if (isset($tag['border']) AND !empty($tag['border'])) { 20096 // get border style 20097 $border = $tag['border']; 20098 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) { 20099 // border for table header 20100 $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 20101 } 20102 } 20103 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) { 20104 // get background color 20105 $old_bgcolor = $this->bgcolor; 20106 $this->SetFillColorArray($tag['bgcolor']); 20107 $fill = true; 20108 } 20109 if (!$border AND !$fill) { 20110 // nothing to draw 20111 return; 20112 } 20113 if (isset($tag['attribute']['cellspacing'])) { 20114 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 20115 $cellspacing = array('H' => $clsp, 'V' => $clsp); 20116 } elseif (isset($tag['border-spacing'])) { 20117 $cellspacing = $tag['border-spacing']; 20118 } else { 20119 $cellspacing = array('H' => 0, 'V' => 0); 20120 } 20121 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) { 20122 // draw the border externally respect the sqare edge. 20123 $border['mode'] = 'ext'; 20124 } 20125 if ($this->rtl) { 20126 if ($xmax >= $tag['borderposition']['x']) { 20127 $xmax = $tag['borderposition']['xmax']; 20128 } 20129 $w = ($tag['borderposition']['x'] - $xmax); 20130 } else { 20131 if ($xmax <= $tag['borderposition']['x']) { 20132 $xmax = $tag['borderposition']['xmax']; 20133 } 20134 $w = ($xmax - $tag['borderposition']['x']); 20135 } 20136 if ($w <= 0) { 20137 return; 20138 } 20139 $w += $cellspacing['H']; 20140 $startpage = $tag['borderposition']['page']; 20141 $startcolumn = $tag['borderposition']['column']; 20142 $x = $tag['borderposition']['x']; 20143 $y = $tag['borderposition']['y']; 20144 $endpage = $this->page; 20145 $starty = $tag['borderposition']['y'] - $cellspacing['V']; 20146 $currentY = $this->y; 20147 $this->x = $x; 20148 // get latest column 20149 $endcolumn = $this->current_column; 20150 if ($this->num_columns == 0) { 20151 $this->num_columns = 1; 20152 } 20153 // get border modes 20154 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell); 20155 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell); 20156 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell); 20157 // temporary disable page regions 20158 $temp_page_regions = $this->page_regions; 20159 $this->page_regions = array(); 20160 // design borders around HTML cells. 20161 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 20162 $ccode = ''; 20163 $this->setPage($page); 20164 if ($this->num_columns < 2) { 20165 // single-column mode 20166 $this->x = $x; 20167 $this->y = $this->tMargin; 20168 } 20169 // account for margin changes 20170 if ($page > $startpage) { 20171 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 20172 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 20173 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 20174 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 20175 } 20176 } 20177 if ($startpage == $endpage) { 20178 // single page 20179 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 20180 $this->selectColumn($column); 20181 if ($startcolumn == $endcolumn) { // single column 20182 $cborder = $border; 20183 $h = ($currentY - $y) + $cellspacing['V']; 20184 $this->y = $starty; 20185 } elseif ($column == $startcolumn) { // first column 20186 $cborder = $border_start; 20187 $this->y = $starty; 20188 $h = $this->h - $this->y - $this->bMargin; 20189 } elseif ($column == $endcolumn) { // end column 20190 $cborder = $border_end; 20191 $h = $currentY - $this->y; 20192 } else { // middle column 20193 $cborder = $border_middle; 20194 $h = $this->h - $this->y - $this->bMargin; 20195 } 20196 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20197 } // end for each column 20198 } elseif ($page == $startpage) { // first page 20199 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 20200 $this->selectColumn($column); 20201 if ($column == $startcolumn) { // first column 20202 $cborder = $border_start; 20203 $this->y = $starty; 20204 $h = $this->h - $this->y - $this->bMargin; 20205 } else { // middle column 20206 $cborder = $border_middle; 20207 $h = $this->h - $this->y - $this->bMargin; 20208 } 20209 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20210 } // end for each column 20211 } elseif ($page == $endpage) { // last page 20212 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 20213 $this->selectColumn($column); 20214 if ($column == $endcolumn) { 20215 // end column 20216 $cborder = $border_end; 20217 $h = $currentY - $this->y; 20218 } else { 20219 // middle column 20220 $cborder = $border_middle; 20221 $h = $this->h - $this->y - $this->bMargin; 20222 } 20223 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20224 } // end for each column 20225 } else { // middle page 20226 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 20227 $this->selectColumn($column); 20228 $cborder = $border_middle; 20229 $h = $this->h - $this->y - $this->bMargin; 20230 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 20231 } // end for each column 20232 } 20233 if ($cborder OR $fill) { 20234 $offsetlen = strlen($ccode); 20235 // draw border and fill 20236 if ($this->inxobj) { 20237 // we are inside an XObject template 20238 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 20239 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 20240 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 20241 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 20242 } else { 20243 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 20244 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 20245 } 20246 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 20247 $pstart = substr($pagebuff, 0, $pagemark); 20248 $pend = substr($pagebuff, $pagemark); 20249 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 20250 } else { 20251 if (end($this->transfmrk[$this->page]) !== false) { 20252 $pagemarkkey = key($this->transfmrk[$this->page]); 20253 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 20254 } elseif ($this->InFooter) { 20255 $pagemark = $this->footerpos[$this->page]; 20256 } else { 20257 $pagemark = $this->intmrk[$this->page]; 20258 } 20259 $pagebuff = $this->getPageBuffer($this->page); 20260 $pstart = substr($pagebuff, 0, $pagemark); 20261 $pend = substr($pagebuff, $pagemark); 20262 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 20263 $this->bordermrk[$this->page] += $offsetlen; 20264 $this->cntmrk[$this->page] += $offsetlen; 20265 } 20266 } 20267 } // end for each page 20268 // restore page regions 20269 $this->page_regions = $temp_page_regions; 20270 if (isset($old_bgcolor)) { 20271 // restore background color 20272 $this->SetFillColorArray($old_bgcolor); 20273 } 20274 // restore pointer position 20275 $this->x = $prev_x; 20276 $this->y = $prev_y; 20277 $this->lasth = $prev_lasth; 20278 } 20279 20280 /** 20281 * Set the default bullet to be used as LI bullet symbol 20282 * @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') 20283 * @public 20284 * @since 4.0.028 (2008-09-26) 20285 */ 20286 public function setLIsymbol($symbol='!') { 20287 // check for custom image symbol 20288 if (substr($symbol, 0, 4) == 'img|') { 20289 $this->lisymbol = $symbol; 20290 return; 20291 } 20292 $symbol = strtolower($symbol); 20293 $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'); 20294 if (in_array($symbol, $valid_symbols)) { 20295 $this->lisymbol = $symbol; 20296 } else { 20297 $this->lisymbol = ''; 20298 } 20299 } 20300 20301 /** 20302 * Set the booklet mode for double-sided pages. 20303 * @param $booklet (boolean) true set the booklet mode on, false otherwise. 20304 * @param $inner (float) Inner page margin. 20305 * @param $outer (float) Outer page margin. 20306 * @public 20307 * @since 4.2.000 (2008-10-29) 20308 */ 20309 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) { 20310 $this->booklet = $booklet; 20311 if ($inner >= 0) { 20312 $this->lMargin = $inner; 20313 } 20314 if ($outer >= 0) { 20315 $this->rMargin = $outer; 20316 } 20317 } 20318 20319 /** 20320 * Swap the left and right margins. 20321 * @param $reverse (boolean) if true swap left and right margins. 20322 * @protected 20323 * @since 4.2.000 (2008-10-29) 20324 */ 20325 protected function swapMargins($reverse=true) { 20326 if ($reverse) { 20327 // swap left and right margins 20328 $mtemp = $this->original_lMargin; 20329 $this->original_lMargin = $this->original_rMargin; 20330 $this->original_rMargin = $mtemp; 20331 $deltam = $this->original_lMargin - $this->original_rMargin; 20332 $this->lMargin += $deltam; 20333 $this->rMargin -= $deltam; 20334 } 20335 } 20336 20337 /** 20338 * Set the vertical spaces for HTML tags. 20339 * The array must have the following structure (example): 20340 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1))); 20341 * The first array level contains the tag names, 20342 * the second level contains 0 for opening tags or 1 for closing tags, 20343 * the third level contains the vertical space unit (h) and the number spaces to add (n). 20344 * If the h parameter is not specified, default values are used. 20345 * @param $tagvs (array) array of tags and relative vertical spaces. 20346 * @public 20347 * @since 4.2.001 (2008-10-30) 20348 */ 20349 public function setHtmlVSpace($tagvs) { 20350 $this->tagvspaces = $tagvs; 20351 } 20352 20353 /** 20354 * Set custom width for list indentation. 20355 * @param $width (float) width of the indentation. Use negative value to disable it. 20356 * @public 20357 * @since 4.2.007 (2008-11-12) 20358 */ 20359 public function setListIndentWidth($width) { 20360 return $this->customlistindent = floatval($width); 20361 } 20362 20363 /** 20364 * Set the top/bottom cell sides to be open or closed when the cell cross the page. 20365 * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page. 20366 * @public 20367 * @since 4.2.010 (2008-11-14) 20368 */ 20369 public function setOpenCell($isopen) { 20370 $this->opencell = $isopen; 20371 } 20372 20373 /** 20374 * Set the color and font style for HTML links. 20375 * @param $color (array) RGB array of colors 20376 * @param $fontstyle (string) additional font styles to add 20377 * @public 20378 * @since 4.4.003 (2008-12-09) 20379 */ 20380 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') { 20381 $this->htmlLinkColorArray = $color; 20382 $this->htmlLinkFontStyle = $fontstyle; 20383 } 20384 20385 /** 20386 * Convert HTML string containing value and unit of measure to user's units or points. 20387 * @param $htmlval (string) String containing values and unit. 20388 * @param $refsize (string) Reference value in points. 20389 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). 20390 * @param $points (boolean) If true returns points, otherwise returns value in user's units. 20391 * @return float value in user's unit or point if $points=true 20392 * @public 20393 * @since 4.4.004 (2008-12-10) 20394 */ 20395 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) { 20396 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt'); 20397 $retval = 0; 20398 $value = 0; 20399 $unit = 'px'; 20400 if ($points) { 20401 $k = 1; 20402 } else { 20403 $k = $this->k; 20404 } 20405 if (in_array($defaultunit, $supportedunits)) { 20406 $unit = $defaultunit; 20407 } 20408 if (is_numeric($htmlval)) { 20409 $value = floatval($htmlval); 20410 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) { 20411 $value = floatval($mnum[1]); 20412 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) { 20413 if (in_array($munit[1], $supportedunits)) { 20414 $unit = $munit[1]; 20415 } 20416 } 20417 } 20418 switch ($unit) { 20419 // percentage 20420 case '%': { 20421 $retval = (($value * $refsize) / 100); 20422 break; 20423 } 20424 // relative-size 20425 case 'em': { 20426 $retval = ($value * $refsize); 20427 break; 20428 } 20429 // height of lower case 'x' (about half the font-size) 20430 case 'ex': { 20431 $retval = ($value * ($refsize / 2)); 20432 break; 20433 } 20434 // absolute-size 20435 case 'in': { 20436 $retval = (($value * $this->dpi) / $k); 20437 break; 20438 } 20439 // centimeters 20440 case 'cm': { 20441 $retval = (($value / 2.54 * $this->dpi) / $k); 20442 break; 20443 } 20444 // millimeters 20445 case 'mm': { 20446 $retval = (($value / 25.4 * $this->dpi) / $k); 20447 break; 20448 } 20449 // one pica is 12 points 20450 case 'pc': { 20451 $retval = (($value * 12) / $k); 20452 break; 20453 } 20454 // points 20455 case 'pt': { 20456 $retval = ($value / $k); 20457 break; 20458 } 20459 // pixels 20460 case 'px': { 20461 $retval = $this->pixelsToUnits($value); 20462 if ($points) { 20463 $retval *= $this->k; 20464 } 20465 break; 20466 } 20467 } 20468 return $retval; 20469 } 20470 20471 /** 20472 * Output an HTML list bullet or ordered item symbol 20473 * @param $listdepth (int) list nesting level 20474 * @param $listtype (string) type of list 20475 * @param $size (float) current font size 20476 * @protected 20477 * @since 4.4.004 (2008-12-10) 20478 */ 20479 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) { 20480 if ($this->state != 2) { 20481 return; 20482 } 20483 $size /= $this->k; 20484 $fill = ''; 20485 $bgcolor = $this->bgcolor; 20486 $color = $this->fgcolor; 20487 $strokecolor = $this->strokecolor; 20488 $width = 0; 20489 $textitem = ''; 20490 $tmpx = $this->x; 20491 $lspace = $this->GetStringWidth(' '); 20492 if ($listtype == '^') { 20493 // special symbol used for avoid justification of rect bullet 20494 $this->lispacer = ''; 20495 return; 20496 } elseif ($listtype == '!') { 20497 // set default list type for unordered list 20498 $deftypes = array('disc', 'circle', 'square'); 20499 $listtype = $deftypes[($listdepth - 1) % 3]; 20500 } elseif ($listtype == '#') { 20501 // set default list type for ordered list 20502 $listtype = 'decimal'; 20503 } elseif (substr($listtype, 0, 4) == 'img|') { 20504 // custom image type ('img|type|width|height|image.ext') 20505 $img = explode('|', $listtype); 20506 $listtype = 'img'; 20507 } 20508 switch ($listtype) { 20509 // unordered types 20510 case 'none': { 20511 break; 20512 } 20513 case 'disc': { 20514 $r = $size / 6; 20515 $lspace += (2 * $r); 20516 if ($this->rtl) { 20517 $this->x += $lspace; 20518 } else { 20519 $this->x -= $lspace; 20520 } 20521 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8); 20522 break; 20523 } 20524 case 'circle': { 20525 $r = $size / 6; 20526 $lspace += (2 * $r); 20527 if ($this->rtl) { 20528 $this->x += $lspace; 20529 } else { 20530 $this->x -= $lspace; 20531 } 20532 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor; 20533 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color); 20534 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8); 20535 $this->_out($prev_line_style); // restore line settings 20536 break; 20537 } 20538 case 'square': { 20539 $l = $size / 3; 20540 $lspace += $l; 20541 if ($this->rtl) {; 20542 $this->x += $lspace; 20543 } else { 20544 $this->x -= $lspace; 20545 } 20546 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color); 20547 break; 20548 } 20549 case 'img': { 20550 // 1=>type, 2=>width, 3=>height, 4=>image.ext 20551 $lspace += $img[2]; 20552 if ($this->rtl) {; 20553 $this->x += $lspace; 20554 } else { 20555 $this->x -= $lspace; 20556 } 20557 $imgtype = strtolower($img[1]); 20558 $prev_y = $this->y; 20559 switch ($imgtype) { 20560 case 'svg': { 20561 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false); 20562 break; 20563 } 20564 case 'ai': 20565 case 'eps': { 20566 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false); 20567 break; 20568 } 20569 default: { 20570 $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); 20571 break; 20572 } 20573 } 20574 $this->y = $prev_y; 20575 break; 20576 } 20577 // ordered types 20578 // $this->listcount[$this->listnum]; 20579 // $textitem 20580 case '1': 20581 case 'decimal': { 20582 $textitem = $this->listcount[$this->listnum]; 20583 break; 20584 } 20585 case 'decimal-leading-zero': { 20586 $textitem = sprintf('%02d', $this->listcount[$this->listnum]); 20587 break; 20588 } 20589 case 'i': 20590 case 'lower-roman': { 20591 $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum])); 20592 break; 20593 } 20594 case 'I': 20595 case 'upper-roman': { 20596 $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]); 20597 break; 20598 } 20599 case 'a': 20600 case 'lower-alpha': 20601 case 'lower-latin': { 20602 $textitem = chr(97 + $this->listcount[$this->listnum] - 1); 20603 break; 20604 } 20605 case 'A': 20606 case 'upper-alpha': 20607 case 'upper-latin': { 20608 $textitem = chr(65 + $this->listcount[$this->listnum] - 1); 20609 break; 20610 } 20611 case 'lower-greek': { 20612 $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode); 20613 break; 20614 } 20615 /* 20616 // Types to be implemented (special handling) 20617 case 'hebrew': { 20618 break; 20619 } 20620 case 'armenian': { 20621 break; 20622 } 20623 case 'georgian': { 20624 break; 20625 } 20626 case 'cjk-ideographic': { 20627 break; 20628 } 20629 case 'hiragana': { 20630 break; 20631 } 20632 case 'katakana': { 20633 break; 20634 } 20635 case 'hiragana-iroha': { 20636 break; 20637 } 20638 case 'katakana-iroha': { 20639 break; 20640 } 20641 */ 20642 default: { 20643 $textitem = $this->listcount[$this->listnum]; 20644 } 20645 } 20646 if (!TCPDF_STATIC::empty_string($textitem)) { 20647 // Check whether we need a new page or new column 20648 $prev_y = $this->y; 20649 $h = $this->getCellHeight($this->FontSize); 20650 if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) { 20651 $tmpx = $this->x; 20652 } 20653 // print ordered item 20654 if ($this->rtl) { 20655 $textitem = '.'.$textitem; 20656 } else { 20657 $textitem = $textitem.'.'; 20658 } 20659 $lspace += $this->GetStringWidth($textitem); 20660 if ($this->rtl) { 20661 $this->x += $lspace; 20662 } else { 20663 $this->x -= $lspace; 20664 } 20665 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false); 20666 } 20667 $this->x = $tmpx; 20668 $this->lispacer = '^'; 20669 // restore colors 20670 $this->SetFillColorArray($bgcolor); 20671 $this->SetDrawColorArray($strokecolor); 20672 $this->SettextColorArray($color); 20673 } 20674 20675 /** 20676 * Returns current graphic variables as array. 20677 * @return array of graphic variables 20678 * @protected 20679 * @since 4.2.010 (2008-11-14) 20680 */ 20681 protected function getGraphicVars() { 20682 $grapvars = array( 20683 'FontFamily' => $this->FontFamily, 20684 'FontStyle' => $this->FontStyle, 20685 'FontSizePt' => $this->FontSizePt, 20686 'rMargin' => $this->rMargin, 20687 'lMargin' => $this->lMargin, 20688 'cell_padding' => $this->cell_padding, 20689 'cell_margin' => $this->cell_margin, 20690 'LineWidth' => $this->LineWidth, 20691 'linestyleWidth' => $this->linestyleWidth, 20692 'linestyleCap' => $this->linestyleCap, 20693 'linestyleJoin' => $this->linestyleJoin, 20694 'linestyleDash' => $this->linestyleDash, 20695 'textrendermode' => $this->textrendermode, 20696 'textstrokewidth' => $this->textstrokewidth, 20697 'DrawColor' => $this->DrawColor, 20698 'FillColor' => $this->FillColor, 20699 'TextColor' => $this->TextColor, 20700 'ColorFlag' => $this->ColorFlag, 20701 'bgcolor' => $this->bgcolor, 20702 'fgcolor' => $this->fgcolor, 20703 'htmlvspace' => $this->htmlvspace, 20704 'listindent' => $this->listindent, 20705 'listindentlevel' => $this->listindentlevel, 20706 'listnum' => $this->listnum, 20707 'listordered' => $this->listordered, 20708 'listcount' => $this->listcount, 20709 'lispacer' => $this->lispacer, 20710 'cell_height_ratio' => $this->cell_height_ratio, 20711 'font_stretching' => $this->font_stretching, 20712 'font_spacing' => $this->font_spacing, 20713 'alpha' => $this->alpha, 20714 // extended 20715 'lasth' => $this->lasth, 20716 'tMargin' => $this->tMargin, 20717 'bMargin' => $this->bMargin, 20718 'AutoPageBreak' => $this->AutoPageBreak, 20719 'PageBreakTrigger' => $this->PageBreakTrigger, 20720 'x' => $this->x, 20721 'y' => $this->y, 20722 'w' => $this->w, 20723 'h' => $this->h, 20724 'wPt' => $this->wPt, 20725 'hPt' => $this->hPt, 20726 'fwPt' => $this->fwPt, 20727 'fhPt' => $this->fhPt, 20728 'page' => $this->page, 20729 'current_column' => $this->current_column, 20730 'num_columns' => $this->num_columns 20731 ); 20732 return $grapvars; 20733 } 20734 20735 /** 20736 * Set graphic variables. 20737 * @param $gvars (array) array of graphic variablesto restore 20738 * @param $extended (boolean) if true restore extended graphic variables 20739 * @protected 20740 * @since 4.2.010 (2008-11-14) 20741 */ 20742 protected function setGraphicVars($gvars, $extended=false) { 20743 if ($this->state != 2) { 20744 return; 20745 } 20746 $this->FontFamily = $gvars['FontFamily']; 20747 $this->FontStyle = $gvars['FontStyle']; 20748 $this->FontSizePt = $gvars['FontSizePt']; 20749 $this->rMargin = $gvars['rMargin']; 20750 $this->lMargin = $gvars['lMargin']; 20751 $this->cell_padding = $gvars['cell_padding']; 20752 $this->cell_margin = $gvars['cell_margin']; 20753 $this->LineWidth = $gvars['LineWidth']; 20754 $this->linestyleWidth = $gvars['linestyleWidth']; 20755 $this->linestyleCap = $gvars['linestyleCap']; 20756 $this->linestyleJoin = $gvars['linestyleJoin']; 20757 $this->linestyleDash = $gvars['linestyleDash']; 20758 $this->textrendermode = $gvars['textrendermode']; 20759 $this->textstrokewidth = $gvars['textstrokewidth']; 20760 $this->DrawColor = $gvars['DrawColor']; 20761 $this->FillColor = $gvars['FillColor']; 20762 $this->TextColor = $gvars['TextColor']; 20763 $this->ColorFlag = $gvars['ColorFlag']; 20764 $this->bgcolor = $gvars['bgcolor']; 20765 $this->fgcolor = $gvars['fgcolor']; 20766 $this->htmlvspace = $gvars['htmlvspace']; 20767 $this->listindent = $gvars['listindent']; 20768 $this->listindentlevel = $gvars['listindentlevel']; 20769 $this->listnum = $gvars['listnum']; 20770 $this->listordered = $gvars['listordered']; 20771 $this->listcount = $gvars['listcount']; 20772 $this->lispacer = $gvars['lispacer']; 20773 $this->cell_height_ratio = $gvars['cell_height_ratio']; 20774 $this->font_stretching = $gvars['font_stretching']; 20775 $this->font_spacing = $gvars['font_spacing']; 20776 $this->alpha = $gvars['alpha']; 20777 if ($extended) { 20778 // restore extended values 20779 $this->lasth = $gvars['lasth']; 20780 $this->tMargin = $gvars['tMargin']; 20781 $this->bMargin = $gvars['bMargin']; 20782 $this->AutoPageBreak = $gvars['AutoPageBreak']; 20783 $this->PageBreakTrigger = $gvars['PageBreakTrigger']; 20784 $this->x = $gvars['x']; 20785 $this->y = $gvars['y']; 20786 $this->w = $gvars['w']; 20787 $this->h = $gvars['h']; 20788 $this->wPt = $gvars['wPt']; 20789 $this->hPt = $gvars['hPt']; 20790 $this->fwPt = $gvars['fwPt']; 20791 $this->fhPt = $gvars['fhPt']; 20792 $this->page = $gvars['page']; 20793 $this->current_column = $gvars['current_column']; 20794 $this->num_columns = $gvars['num_columns']; 20795 } 20796 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.''); 20797 if (!TCPDF_STATIC::empty_string($this->FontFamily)) { 20798 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 20799 } 20800 } 20801 20802 /** 20803 * Outputs the "save graphics state" operator 'q' 20804 * @protected 20805 */ 20806 protected function _outSaveGraphicsState() { 20807 $this->_out('q'); 20808 } 20809 20810 /** 20811 * Outputs the "restore graphics state" operator 'Q' 20812 * @protected 20813 */ 20814 protected function _outRestoreGraphicsState() { 20815 $this->_out('Q'); 20816 } 20817 20818 /** 20819 * Set buffer content (always append data). 20820 * @param $data (string) data 20821 * @protected 20822 * @since 4.5.000 (2009-01-02) 20823 */ 20824 protected function setBuffer($data) { 20825 $this->bufferlen += strlen($data); 20826 $this->buffer .= $data; 20827 } 20828 20829 /** 20830 * Replace the buffer content 20831 * @param $data (string) data 20832 * @protected 20833 * @since 5.5.000 (2010-06-22) 20834 */ 20835 protected function replaceBuffer($data) { 20836 $this->bufferlen = strlen($data); 20837 $this->buffer = $data; 20838 } 20839 20840 /** 20841 * Get buffer content. 20842 * @return string buffer content 20843 * @protected 20844 * @since 4.5.000 (2009-01-02) 20845 */ 20846 protected function getBuffer() { 20847 return $this->buffer; 20848 } 20849 20850 /** 20851 * Set page buffer content. 20852 * @param $page (int) page number 20853 * @param $data (string) page data 20854 * @param $append (boolean) if true append data, false replace. 20855 * @protected 20856 * @since 4.5.000 (2008-12-31) 20857 */ 20858 protected function setPageBuffer($page, $data, $append=false) { 20859 if ($append) { 20860 $this->pages[$page] .= $data; 20861 } else { 20862 $this->pages[$page] = $data; 20863 } 20864 if ($append AND isset($this->pagelen[$page])) { 20865 $this->pagelen[$page] += strlen($data); 20866 } else { 20867 $this->pagelen[$page] = strlen($data); 20868 } 20869 } 20870 20871 /** 20872 * Get page buffer content. 20873 * @param $page (int) page number 20874 * @return string page buffer content or false in case of error 20875 * @protected 20876 * @since 4.5.000 (2008-12-31) 20877 */ 20878 protected function getPageBuffer($page) { 20879 if (isset($this->pages[$page])) { 20880 return $this->pages[$page]; 20881 } 20882 return false; 20883 } 20884 20885 /** 20886 * Set image buffer content. 20887 * @param $image (string) image key 20888 * @param $data (array) image data 20889 * @return int image index number 20890 * @protected 20891 * @since 4.5.000 (2008-12-31) 20892 */ 20893 protected function setImageBuffer($image, $data) { 20894 if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) { 20895 $this->imagekeys[$this->numimages] = $image; 20896 $data['i'] = $this->numimages; 20897 ++$this->numimages; 20898 } 20899 $this->images[$image] = $data; 20900 return $data['i']; 20901 } 20902 20903 /** 20904 * Set image buffer content for a specified sub-key. 20905 * @param $image (string) image key 20906 * @param $key (string) image sub-key 20907 * @param $data (array) image data 20908 * @protected 20909 * @since 4.5.000 (2008-12-31) 20910 */ 20911 protected function setImageSubBuffer($image, $key, $data) { 20912 if (!isset($this->images[$image])) { 20913 $this->setImageBuffer($image, array()); 20914 } 20915 $this->images[$image][$key] = $data; 20916 } 20917 20918 /** 20919 * Get image buffer content. 20920 * @param $image (string) image key 20921 * @return string image buffer content or false in case of error 20922 * @protected 20923 * @since 4.5.000 (2008-12-31) 20924 */ 20925 protected function getImageBuffer($image) { 20926 if (isset($this->images[$image])) { 20927 return $this->images[$image]; 20928 } 20929 return false; 20930 } 20931 20932 /** 20933 * Set font buffer content. 20934 * @param $font (string) font key 20935 * @param $data (array) font data 20936 * @protected 20937 * @since 4.5.000 (2009-01-02) 20938 */ 20939 protected function setFontBuffer($font, $data) { 20940 $this->fonts[$font] = $data; 20941 if (!in_array($font, $this->fontkeys)) { 20942 $this->fontkeys[] = $font; 20943 // store object ID for current font 20944 ++$this->n; 20945 $this->font_obj_ids[$font] = $this->n; 20946 $this->setFontSubBuffer($font, 'n', $this->n); 20947 } 20948 } 20949 20950 /** 20951 * Set font buffer content. 20952 * @param $font (string) font key 20953 * @param $key (string) font sub-key 20954 * @param $data (array) font data 20955 * @protected 20956 * @since 4.5.000 (2009-01-02) 20957 */ 20958 protected function setFontSubBuffer($font, $key, $data) { 20959 if (!isset($this->fonts[$font])) { 20960 $this->setFontBuffer($font, array()); 20961 } 20962 $this->fonts[$font][$key] = $data; 20963 } 20964 20965 /** 20966 * Get font buffer content. 20967 * @param $font (string) font key 20968 * @return string font buffer content or false in case of error 20969 * @protected 20970 * @since 4.5.000 (2009-01-02) 20971 */ 20972 protected function getFontBuffer($font) { 20973 if (isset($this->fonts[$font])) { 20974 return $this->fonts[$font]; 20975 } 20976 return false; 20977 } 20978 20979 /** 20980 * Move a page to a previous position. 20981 * @param $frompage (int) number of the source page 20982 * @param $topage (int) number of the destination page (must be less than $frompage) 20983 * @return true in case of success, false in case of error. 20984 * @public 20985 * @since 4.5.000 (2009-01-02) 20986 */ 20987 public function movePage($frompage, $topage) { 20988 if (($frompage > $this->numpages) OR ($frompage <= $topage)) { 20989 return false; 20990 } 20991 if ($frompage == $this->page) { 20992 // close the page before moving it 20993 $this->endPage(); 20994 } 20995 // move all page-related states 20996 $tmppage = $this->getPageBuffer($frompage); 20997 $tmppagedim = $this->pagedim[$frompage]; 20998 $tmppagelen = $this->pagelen[$frompage]; 20999 $tmpintmrk = $this->intmrk[$frompage]; 21000 $tmpbordermrk = $this->bordermrk[$frompage]; 21001 $tmpcntmrk = $this->cntmrk[$frompage]; 21002 $tmppageobjects = $this->pageobjects[$frompage]; 21003 if (isset($this->footerpos[$frompage])) { 21004 $tmpfooterpos = $this->footerpos[$frompage]; 21005 } 21006 if (isset($this->footerlen[$frompage])) { 21007 $tmpfooterlen = $this->footerlen[$frompage]; 21008 } 21009 if (isset($this->transfmrk[$frompage])) { 21010 $tmptransfmrk = $this->transfmrk[$frompage]; 21011 } 21012 if (isset($this->PageAnnots[$frompage])) { 21013 $tmpannots = $this->PageAnnots[$frompage]; 21014 } 21015 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { 21016 for ($i = $frompage; $i > $topage; --$i) { 21017 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) { 21018 --$this->pagegroups[$this->newpagegroup[$i]]; 21019 break; 21020 } 21021 } 21022 for ($i = $topage; $i > 0; --$i) { 21023 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) { 21024 ++$this->pagegroups[$this->newpagegroup[$i]]; 21025 break; 21026 } 21027 } 21028 } 21029 for ($i = $frompage; $i > $topage; --$i) { 21030 $j = $i - 1; 21031 // shift pages down 21032 $this->setPageBuffer($i, $this->getPageBuffer($j)); 21033 $this->pagedim[$i] = $this->pagedim[$j]; 21034 $this->pagelen[$i] = $this->pagelen[$j]; 21035 $this->intmrk[$i] = $this->intmrk[$j]; 21036 $this->bordermrk[$i] = $this->bordermrk[$j]; 21037 $this->cntmrk[$i] = $this->cntmrk[$j]; 21038 $this->pageobjects[$i] = $this->pageobjects[$j]; 21039 if (isset($this->footerpos[$j])) { 21040 $this->footerpos[$i] = $this->footerpos[$j]; 21041 } elseif (isset($this->footerpos[$i])) { 21042 unset($this->footerpos[$i]); 21043 } 21044 if (isset($this->footerlen[$j])) { 21045 $this->footerlen[$i] = $this->footerlen[$j]; 21046 } elseif (isset($this->footerlen[$i])) { 21047 unset($this->footerlen[$i]); 21048 } 21049 if (isset($this->transfmrk[$j])) { 21050 $this->transfmrk[$i] = $this->transfmrk[$j]; 21051 } elseif (isset($this->transfmrk[$i])) { 21052 unset($this->transfmrk[$i]); 21053 } 21054 if (isset($this->PageAnnots[$j])) { 21055 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 21056 } elseif (isset($this->PageAnnots[$i])) { 21057 unset($this->PageAnnots[$i]); 21058 } 21059 if (isset($this->newpagegroup[$j])) { 21060 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 21061 unset($this->newpagegroup[$j]); 21062 } 21063 if ($this->currpagegroup == $j) { 21064 $this->currpagegroup = $i; 21065 } 21066 } 21067 $this->setPageBuffer($topage, $tmppage); 21068 $this->pagedim[$topage] = $tmppagedim; 21069 $this->pagelen[$topage] = $tmppagelen; 21070 $this->intmrk[$topage] = $tmpintmrk; 21071 $this->bordermrk[$topage] = $tmpbordermrk; 21072 $this->cntmrk[$topage] = $tmpcntmrk; 21073 $this->pageobjects[$topage] = $tmppageobjects; 21074 if (isset($tmpfooterpos)) { 21075 $this->footerpos[$topage] = $tmpfooterpos; 21076 } elseif (isset($this->footerpos[$topage])) { 21077 unset($this->footerpos[$topage]); 21078 } 21079 if (isset($tmpfooterlen)) { 21080 $this->footerlen[$topage] = $tmpfooterlen; 21081 } elseif (isset($this->footerlen[$topage])) { 21082 unset($this->footerlen[$topage]); 21083 } 21084 if (isset($tmptransfmrk)) { 21085 $this->transfmrk[$topage] = $tmptransfmrk; 21086 } elseif (isset($this->transfmrk[$topage])) { 21087 unset($this->transfmrk[$topage]); 21088 } 21089 if (isset($tmpannots)) { 21090 $this->PageAnnots[$topage] = $tmpannots; 21091 } elseif (isset($this->PageAnnots[$topage])) { 21092 unset($this->PageAnnots[$topage]); 21093 } 21094 // adjust outlines 21095 $tmpoutlines = $this->outlines; 21096 foreach ($tmpoutlines as $key => $outline) { 21097 if (!$outline['f']) { 21098 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) { 21099 $this->outlines[$key]['p'] = ($outline['p'] + 1); 21100 } elseif ($outline['p'] == $frompage) { 21101 $this->outlines[$key]['p'] = $topage; 21102 } 21103 } 21104 } 21105 // adjust dests 21106 $tmpdests = $this->dests; 21107 foreach ($tmpdests as $key => $dest) { 21108 if (!$dest['f']) { 21109 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) { 21110 $this->dests[$key]['p'] = ($dest['p'] + 1); 21111 } elseif ($dest['p'] == $frompage) { 21112 $this->dests[$key]['p'] = $topage; 21113 } 21114 } 21115 } 21116 // adjust links 21117 $tmplinks = $this->links; 21118 foreach ($tmplinks as $key => $link) { 21119 if (!$link['f']) { 21120 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) { 21121 $this->links[$key]['p'] = ($link['p'] + 1); 21122 } elseif ($link['p'] == $frompage) { 21123 $this->links[$key]['p'] = $topage; 21124 } 21125 } 21126 } 21127 // adjust javascript 21128 $jfrompage = $frompage; 21129 $jtopage = $topage; 21130 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) { 21131 foreach($pamatch[0] as $pk => $pmatch) { 21132 $pagenum = intval($pamatch[3][$pk]) + 1; 21133 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) { 21134 $newpage = ($pagenum + 1); 21135 } elseif ($pagenum == $jfrompage) { 21136 $newpage = $jtopage; 21137 } else { 21138 $newpage = $pagenum; 21139 } 21140 --$newpage; 21141 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage; 21142 $this->javascript = str_replace($pmatch, $newjs, $this->javascript); 21143 } 21144 unset($pamatch); 21145 } 21146 // return to last page 21147 $this->lastPage(true); 21148 return true; 21149 } 21150 21151 /** 21152 * Remove the specified page. 21153 * @param $page (int) page to remove 21154 * @return true in case of success, false in case of error. 21155 * @public 21156 * @since 4.6.004 (2009-04-23) 21157 */ 21158 public function deletePage($page) { 21159 if (($page < 1) OR ($page > $this->numpages)) { 21160 return false; 21161 } 21162 // delete current page 21163 unset($this->pages[$page]); 21164 unset($this->pagedim[$page]); 21165 unset($this->pagelen[$page]); 21166 unset($this->intmrk[$page]); 21167 unset($this->bordermrk[$page]); 21168 unset($this->cntmrk[$page]); 21169 foreach ($this->pageobjects[$page] as $oid) { 21170 if (isset($this->offsets[$oid])){ 21171 unset($this->offsets[$oid]); 21172 } 21173 } 21174 unset($this->pageobjects[$page]); 21175 if (isset($this->footerpos[$page])) { 21176 unset($this->footerpos[$page]); 21177 } 21178 if (isset($this->footerlen[$page])) { 21179 unset($this->footerlen[$page]); 21180 } 21181 if (isset($this->transfmrk[$page])) { 21182 unset($this->transfmrk[$page]); 21183 } 21184 if (isset($this->PageAnnots[$page])) { 21185 unset($this->PageAnnots[$page]); 21186 } 21187 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { 21188 for ($i = $page; $i > 0; --$i) { 21189 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) { 21190 --$this->pagegroups[$this->newpagegroup[$i]]; 21191 break; 21192 } 21193 } 21194 } 21195 if (isset($this->pageopen[$page])) { 21196 unset($this->pageopen[$page]); 21197 } 21198 if ($page < $this->numpages) { 21199 // update remaining pages 21200 for ($i = $page; $i < $this->numpages; ++$i) { 21201 $j = $i + 1; 21202 // shift pages 21203 $this->setPageBuffer($i, $this->getPageBuffer($j)); 21204 $this->pagedim[$i] = $this->pagedim[$j]; 21205 $this->pagelen[$i] = $this->pagelen[$j]; 21206 $this->intmrk[$i] = $this->intmrk[$j]; 21207 $this->bordermrk[$i] = $this->bordermrk[$j]; 21208 $this->cntmrk[$i] = $this->cntmrk[$j]; 21209 $this->pageobjects[$i] = $this->pageobjects[$j]; 21210 if (isset($this->footerpos[$j])) { 21211 $this->footerpos[$i] = $this->footerpos[$j]; 21212 } elseif (isset($this->footerpos[$i])) { 21213 unset($this->footerpos[$i]); 21214 } 21215 if (isset($this->footerlen[$j])) { 21216 $this->footerlen[$i] = $this->footerlen[$j]; 21217 } elseif (isset($this->footerlen[$i])) { 21218 unset($this->footerlen[$i]); 21219 } 21220 if (isset($this->transfmrk[$j])) { 21221 $this->transfmrk[$i] = $this->transfmrk[$j]; 21222 } elseif (isset($this->transfmrk[$i])) { 21223 unset($this->transfmrk[$i]); 21224 } 21225 if (isset($this->PageAnnots[$j])) { 21226 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 21227 } elseif (isset($this->PageAnnots[$i])) { 21228 unset($this->PageAnnots[$i]); 21229 } 21230 if (isset($this->newpagegroup[$j])) { 21231 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 21232 unset($this->newpagegroup[$j]); 21233 } 21234 if ($this->currpagegroup == $j) { 21235 $this->currpagegroup = $i; 21236 } 21237 if (isset($this->pageopen[$j])) { 21238 $this->pageopen[$i] = $this->pageopen[$j]; 21239 } elseif (isset($this->pageopen[$i])) { 21240 unset($this->pageopen[$i]); 21241 } 21242 } 21243 // remove last page 21244 unset($this->pages[$this->numpages]); 21245 unset($this->pagedim[$this->numpages]); 21246 unset($this->pagelen[$this->numpages]); 21247 unset($this->intmrk[$this->numpages]); 21248 unset($this->bordermrk[$this->numpages]); 21249 unset($this->cntmrk[$this->numpages]); 21250 foreach ($this->pageobjects[$this->numpages] as $oid) { 21251 if (isset($this->offsets[$oid])){ 21252 unset($this->offsets[$oid]); 21253 } 21254 } 21255 unset($this->pageobjects[$this->numpages]); 21256 if (isset($this->footerpos[$this->numpages])) { 21257 unset($this->footerpos[$this->numpages]); 21258 } 21259 if (isset($this->footerlen[$this->numpages])) { 21260 unset($this->footerlen[$this->numpages]); 21261 } 21262 if (isset($this->transfmrk[$this->numpages])) { 21263 unset($this->transfmrk[$this->numpages]); 21264 } 21265 if (isset($this->PageAnnots[$this->numpages])) { 21266 unset($this->PageAnnots[$this->numpages]); 21267 } 21268 if (isset($this->newpagegroup[$this->numpages])) { 21269 unset($this->newpagegroup[$this->numpages]); 21270 } 21271 if ($this->currpagegroup == $this->numpages) { 21272 $this->currpagegroup = ($this->numpages - 1); 21273 } 21274 if (isset($this->pagegroups[$this->numpages])) { 21275 unset($this->pagegroups[$this->numpages]); 21276 } 21277 if (isset($this->pageopen[$this->numpages])) { 21278 unset($this->pageopen[$this->numpages]); 21279 } 21280 } 21281 --$this->numpages; 21282 $this->page = $this->numpages; 21283 // adjust outlines 21284 $tmpoutlines = $this->outlines; 21285 foreach ($tmpoutlines as $key => $outline) { 21286 if (!$outline['f']) { 21287 if ($outline['p'] > $page) { 21288 $this->outlines[$key]['p'] = $outline['p'] - 1; 21289 } elseif ($outline['p'] == $page) { 21290 unset($this->outlines[$key]); 21291 } 21292 } 21293 } 21294 // adjust dests 21295 $tmpdests = $this->dests; 21296 foreach ($tmpdests as $key => $dest) { 21297 if (!$dest['f']) { 21298 if ($dest['p'] > $page) { 21299 $this->dests[$key]['p'] = $dest['p'] - 1; 21300 } elseif ($dest['p'] == $page) { 21301 unset($this->dests[$key]); 21302 } 21303 } 21304 } 21305 // adjust links 21306 $tmplinks = $this->links; 21307 foreach ($tmplinks as $key => $link) { 21308 if (!$link['f']) { 21309 if ($link['p'] > $page) { 21310 $this->links[$key]['p'] = $link['p'] - 1; 21311 } elseif ($link['p'] == $page) { 21312 unset($this->links[$key]); 21313 } 21314 } 21315 } 21316 // adjust javascript 21317 $jpage = $page; 21318 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) { 21319 foreach($pamatch[0] as $pk => $pmatch) { 21320 $pagenum = intval($pamatch[3][$pk]) + 1; 21321 if ($pagenum >= $jpage) { 21322 $newpage = ($pagenum - 1); 21323 } elseif ($pagenum == $jpage) { 21324 $newpage = 1; 21325 } else { 21326 $newpage = $pagenum; 21327 } 21328 --$newpage; 21329 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage; 21330 $this->javascript = str_replace($pmatch, $newjs, $this->javascript); 21331 } 21332 unset($pamatch); 21333 } 21334 // return to last page 21335 if ($this->numpages > 0) { 21336 $this->lastPage(true); 21337 } 21338 return true; 21339 } 21340 21341 /** 21342 * Clone the specified page to a new page. 21343 * @param $page (int) number of page to copy (0 = current page) 21344 * @return true in case of success, false in case of error. 21345 * @public 21346 * @since 4.9.015 (2010-04-20) 21347 */ 21348 public function copyPage($page=0) { 21349 if ($page == 0) { 21350 // default value 21351 $page = $this->page; 21352 } 21353 if (($page < 1) OR ($page > $this->numpages)) { 21354 return false; 21355 } 21356 // close the last page 21357 $this->endPage(); 21358 // copy all page-related states 21359 ++$this->numpages; 21360 $this->page = $this->numpages; 21361 $this->setPageBuffer($this->page, $this->getPageBuffer($page)); 21362 $this->pagedim[$this->page] = $this->pagedim[$page]; 21363 $this->pagelen[$this->page] = $this->pagelen[$page]; 21364 $this->intmrk[$this->page] = $this->intmrk[$page]; 21365 $this->bordermrk[$this->page] = $this->bordermrk[$page]; 21366 $this->cntmrk[$this->page] = $this->cntmrk[$page]; 21367 $this->pageobjects[$this->page] = $this->pageobjects[$page]; 21368 $this->pageopen[$this->page] = false; 21369 if (isset($this->footerpos[$page])) { 21370 $this->footerpos[$this->page] = $this->footerpos[$page]; 21371 } 21372 if (isset($this->footerlen[$page])) { 21373 $this->footerlen[$this->page] = $this->footerlen[$page]; 21374 } 21375 if (isset($this->transfmrk[$page])) { 21376 $this->transfmrk[$this->page] = $this->transfmrk[$page]; 21377 } 21378 if (isset($this->PageAnnots[$page])) { 21379 $this->PageAnnots[$this->page] = $this->PageAnnots[$page]; 21380 } 21381 if (isset($this->newpagegroup[$page])) { 21382 // start a new group 21383 $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1; 21384 $this->currpagegroup = $this->newpagegroup[$this->page]; 21385 $this->pagegroups[$this->currpagegroup] = 1; 21386 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { 21387 ++$this->pagegroups[$this->currpagegroup]; 21388 } 21389 // copy outlines 21390 $tmpoutlines = $this->outlines; 21391 foreach ($tmpoutlines as $key => $outline) { 21392 if ($outline['p'] == $page) { 21393 $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']); 21394 } 21395 } 21396 // copy links 21397 $tmplinks = $this->links; 21398 foreach ($tmplinks as $key => $link) { 21399 if ($link['p'] == $page) { 21400 $this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']); 21401 } 21402 } 21403 // return to last page 21404 $this->lastPage(true); 21405 return true; 21406 } 21407 21408 /** 21409 * Output a Table of Content Index (TOC). 21410 * This method must be called after all Bookmarks were set. 21411 * Before calling this method you have to open the page using the addTOCPage() method. 21412 * After calling this method you have to call endTOCPage() to close the TOC page. 21413 * You can override this method to achieve different styles. 21414 * @param $page (int) page number where this TOC should be inserted (leave empty for current page). 21415 * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment). 21416 * @param $filler (string) string used to fill the space between text and page number. 21417 * @param $toc_name (string) name to use for TOC bookmark. 21418 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic. 21419 * @param $color (array) RGB color array for bookmark title (values from 0 to 255). 21420 * @public 21421 * @author Nicola Asuni 21422 * @since 4.5.000 (2009-01-02) 21423 * @see addTOCPage(), endTOCPage(), addHTMLTOC() 21424 */ 21425 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) { 21426 $fontsize = $this->FontSizePt; 21427 $fontfamily = $this->FontFamily; 21428 $fontstyle = $this->FontStyle; 21429 $w = $this->w - $this->lMargin - $this->rMargin; 21430 $spacer = $this->GetStringWidth(chr(32)) * 4; 21431 $lmargin = $this->lMargin; 21432 $rmargin = $this->rMargin; 21433 $x_start = $this->GetX(); 21434 $page_first = $this->page; 21435 $current_page = $this->page; 21436 $page_fill_start = false; 21437 $page_fill_end = false; 21438 $current_column = $this->current_column; 21439 if (TCPDF_STATIC::empty_string($numbersfont)) { 21440 $numbersfont = $this->default_monospaced_font; 21441 } 21442 if (TCPDF_STATIC::empty_string($filler)) { 21443 $filler = ' '; 21444 } 21445 if (TCPDF_STATIC::empty_string($page)) { 21446 $gap = ' '; 21447 } else { 21448 $gap = ''; 21449 if ($page < 1) { 21450 $page = 1; 21451 } 21452 } 21453 $this->SetFont($numbersfont, $fontstyle, $fontsize); 21454 $numwidth = $this->GetStringWidth('00000'); 21455 $maxpage = 0; //used for pages on attached documents 21456 foreach ($this->outlines as $key => $outline) { 21457 // check for extra pages (used for attachments) 21458 if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) { 21459 $outline['p'] += ($this->page - $page_first); 21460 } 21461 if ($this->rtl) { 21462 $aligntext = 'R'; 21463 $alignnum = 'L'; 21464 } else { 21465 $aligntext = 'L'; 21466 $alignnum = 'R'; 21467 } 21468 if ($outline['l'] == 0) { 21469 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize); 21470 } else { 21471 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']); 21472 } 21473 $this->SetTextColorArray($outline['c']); 21474 // check for page break 21475 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize)); 21476 // set margins and X position 21477 if (($this->page == $current_page) AND ($this->current_column == $current_column)) { 21478 $this->lMargin = $lmargin; 21479 $this->rMargin = $rmargin; 21480 } else { 21481 if ($this->current_column != $current_column) { 21482 if ($this->rtl) { 21483 $x_start = $this->w - $this->columns[$this->current_column]['x']; 21484 } else { 21485 $x_start = $this->columns[$this->current_column]['x']; 21486 } 21487 } 21488 $lmargin = $this->lMargin; 21489 $rmargin = $this->rMargin; 21490 $current_page = $this->page; 21491 $current_column = $this->current_column; 21492 } 21493 $this->SetX($x_start); 21494 $indent = ($spacer * $outline['l']); 21495 if ($this->rtl) { 21496 $this->x -= $indent; 21497 $this->rMargin = $this->w - $this->x; 21498 } else { 21499 $this->x += $indent; 21500 $this->lMargin = $this->x; 21501 } 21502 $link = $this->AddLink(); 21503 $this->SetLink($link, $outline['y'], $outline['p']); 21504 // write the text 21505 if ($this->rtl) { 21506 $txt = ' '.$outline['t']; 21507 } else { 21508 $txt = $outline['t'].' '; 21509 } 21510 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, ''); 21511 if ($this->rtl) { 21512 $tw = $this->x - $this->lMargin; 21513 } else { 21514 $tw = $this->w - $this->rMargin - $this->x; 21515 } 21516 $this->SetFont($numbersfont, $fontstyle, $fontsize); 21517 if (TCPDF_STATIC::empty_string($page)) { 21518 $pagenum = $outline['p']; 21519 } else { 21520 // placemark to be replaced with the correct number 21521 $pagenum = '{#'.($outline['p']).'}'; 21522 if ($this->isUnicodeFont()) { 21523 $pagenum = '{'.$pagenum.'}'; 21524 } 21525 $maxpage = max($maxpage, $outline['p']); 21526 } 21527 $fw = ($tw - $this->GetStringWidth($pagenum.$filler)); 21528 $wfiller = $this->GetStringWidth($filler); 21529 if ($wfiller > 0) { 21530 $numfills = floor($fw / $wfiller); 21531 } else { 21532 $numfills = 0; 21533 } 21534 if ($numfills > 0) { 21535 $rowfill = str_repeat($filler, $numfills); 21536 } else { 21537 $rowfill = ''; 21538 } 21539 if ($this->rtl) { 21540 $pagenum = $pagenum.$gap.$rowfill; 21541 } else { 21542 $pagenum = $rowfill.$gap.$pagenum; 21543 } 21544 // write the number 21545 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0); 21546 } 21547 $page_last = $this->getPage(); 21548 $numpages = ($page_last - $page_first + 1); 21549 // account for booklet mode 21550 if ($this->booklet) { 21551 // check if a blank page is required before TOC 21552 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0)); 21553 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start))); 21554 if ($page_fill_start) { 21555 // add a page at the end (to be moved before TOC) 21556 $this->addPage(); 21557 ++$page_last; 21558 ++$numpages; 21559 } 21560 if ($page_fill_end) { 21561 // add a page at the end 21562 $this->addPage(); 21563 ++$page_last; 21564 ++$numpages; 21565 } 21566 } 21567 $maxpage = max($maxpage, $page_last); 21568 if (!TCPDF_STATIC::empty_string($page)) { 21569 for ($p = $page_first; $p <= $page_last; ++$p) { 21570 // get page data 21571 $temppage = $this->getPageBuffer($p); 21572 for ($n = 1; $n <= $maxpage; ++$n) { 21573 // update page numbers 21574 $a = '{#'.$n.'}'; 21575 // get page number aliases 21576 $pnalias = $this->getInternalPageNumberAliases($a); 21577 // calculate replacement number 21578 if (($n >= $page) AND ($n <= $this->numpages)) { 21579 $np = $n + $numpages; 21580 } else { 21581 $np = $n; 21582 } 21583 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1)); 21584 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont); 21585 // replace aliases with numbers 21586 foreach ($pnalias['u'] as $u) { 21587 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' ')))); 21588 if ($this->rtl) { 21589 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont); 21590 } else { 21591 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu; 21592 } 21593 $temppage = str_replace($u, $nr, $temppage); 21594 } 21595 foreach ($pnalias['a'] as $a) { 21596 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' ')))); 21597 if ($this->rtl) { 21598 $nr = $na.' '.$sfill; 21599 } else { 21600 $nr = $sfill.' '.$na; 21601 } 21602 $temppage = str_replace($a, $nr, $temppage); 21603 } 21604 } 21605 // save changes 21606 $this->setPageBuffer($p, $temppage); 21607 } 21608 // move pages 21609 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); 21610 if ($page_fill_start) { 21611 $this->movePage($page_last, $page_first); 21612 } 21613 for ($i = 0; $i < $numpages; ++$i) { 21614 $this->movePage($page_last, $page); 21615 } 21616 } 21617 } 21618 21619 /** 21620 * Output a Table Of Content Index (TOC) using HTML templates. 21621 * This method must be called after all Bookmarks were set. 21622 * Before calling this method you have to open the page using the addTOCPage() method. 21623 * After calling this method you have to call endTOCPage() to close the TOC page. 21624 * @param $page (int) page number where this TOC should be inserted (leave empty for current page). 21625 * @param $toc_name (string) name to use for TOC bookmark. 21626 * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number. 21627 * @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) 21628 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic. 21629 * @param $color (array) RGB color array for title (values from 0 to 255). 21630 * @public 21631 * @author Nicola Asuni 21632 * @since 5.0.001 (2010-05-06) 21633 * @see addTOCPage(), endTOCPage(), addTOC() 21634 */ 21635 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) { 21636 $filler = ' '; 21637 $prev_htmlLinkColorArray = $this->htmlLinkColorArray; 21638 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle; 21639 // set new style for link 21640 $this->htmlLinkColorArray = array(); 21641 $this->htmlLinkFontStyle = ''; 21642 $page_first = $this->getPage(); 21643 $page_fill_start = false; 21644 $page_fill_end = false; 21645 // get the font type used for numbers in each template 21646 $current_font = $this->FontFamily; 21647 foreach ($templates as $level => $html) { 21648 $dom = $this->getHtmlDomArray($html); 21649 foreach ($dom as $key => $value) { 21650 if ($value['value'] == '#TOC_PAGE_NUMBER#') { 21651 $this->SetFont($dom[($key - 1)]['fontname']); 21652 $templates['F'.$level] = $this->isUnicodeFont(); 21653 } 21654 } 21655 } 21656 $this->SetFont($current_font); 21657 $maxpage = 0; //used for pages on attached documents 21658 foreach ($this->outlines as $key => $outline) { 21659 // get HTML template 21660 $row = $templates[$outline['l']]; 21661 if (TCPDF_STATIC::empty_string($page)) { 21662 $pagenum = $outline['p']; 21663 } else { 21664 // placemark to be replaced with the correct number 21665 $pagenum = '{#'.($outline['p']).'}'; 21666 if (isset($templates['F'.$outline['l']]) && $templates['F'.$outline['l']]) { 21667 $pagenum = '{'.$pagenum.'}'; 21668 } 21669 $maxpage = max($maxpage, $outline['p']); 21670 } 21671 // replace templates with current values 21672 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row); 21673 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row); 21674 // add link to page 21675 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>'; 21676 // write bookmark entry 21677 $this->writeHTML($row, false, false, true, false, ''); 21678 } 21679 // restore link styles 21680 $this->htmlLinkColorArray = $prev_htmlLinkColorArray; 21681 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle; 21682 // move TOC page and replace numbers 21683 $page_last = $this->getPage(); 21684 $numpages = ($page_last - $page_first + 1); 21685 // account for booklet mode 21686 if ($this->booklet) { 21687 // check if a blank page is required before TOC 21688 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0)); 21689 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start))); 21690 if ($page_fill_start) { 21691 // add a page at the end (to be moved before TOC) 21692 $this->addPage(); 21693 ++$page_last; 21694 ++$numpages; 21695 } 21696 if ($page_fill_end) { 21697 // add a page at the end 21698 $this->addPage(); 21699 ++$page_last; 21700 ++$numpages; 21701 } 21702 } 21703 $maxpage = max($maxpage, $page_last); 21704 if (!TCPDF_STATIC::empty_string($page)) { 21705 for ($p = $page_first; $p <= $page_last; ++$p) { 21706 // get page data 21707 $temppage = $this->getPageBuffer($p); 21708 for ($n = 1; $n <= $maxpage; ++$n) { 21709 // update page numbers 21710 $a = '{#'.$n.'}'; 21711 // get page number aliases 21712 $pnalias = $this->getInternalPageNumberAliases($a); 21713 // calculate replacement number 21714 if ($n >= $page) { 21715 $np = $n + $numpages; 21716 } else { 21717 $np = $n; 21718 } 21719 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1)); 21720 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont); 21721 // replace aliases with numbers 21722 foreach ($pnalias['u'] as $u) { 21723 if ($correct_align) { 21724 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' '))); 21725 if ($this->rtl) { 21726 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont); 21727 } else { 21728 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu; 21729 } 21730 } else { 21731 $nr = $nu; 21732 } 21733 $temppage = str_replace($u, $nr, $temppage); 21734 } 21735 foreach ($pnalias['a'] as $a) { 21736 if ($correct_align) { 21737 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' '))); 21738 if ($this->rtl) { 21739 $nr = $na.' '.$sfill; 21740 } else { 21741 $nr = $sfill.' '.$na; 21742 } 21743 } else { 21744 $nr = $na; 21745 } 21746 $temppage = str_replace($a, $nr, $temppage); 21747 } 21748 } 21749 // save changes 21750 $this->setPageBuffer($p, $temppage); 21751 } 21752 // move pages 21753 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); 21754 if ($page_fill_start) { 21755 $this->movePage($page_last, $page_first); 21756 } 21757 for ($i = 0; $i < $numpages; ++$i) { 21758 $this->movePage($page_last, $page); 21759 } 21760 } 21761 } 21762 21763 /** 21764 * Stores a copy of the current TCPDF object used for undo operation. 21765 * @public 21766 * @since 4.5.029 (2009-03-19) 21767 */ 21768 public function startTransaction() { 21769 if (isset($this->objcopy)) { 21770 // remove previous copy 21771 $this->commitTransaction(); 21772 } 21773 // record current page number and Y position 21774 $this->start_transaction_page = $this->page; 21775 $this->start_transaction_y = $this->y; 21776 // clone current object 21777 $this->objcopy = TCPDF_STATIC::objclone($this); 21778 } 21779 21780 /** 21781 * Delete the copy of the current TCPDF object used for undo operation. 21782 * @public 21783 * @since 4.5.029 (2009-03-19) 21784 */ 21785 public function commitTransaction() { 21786 if (isset($this->objcopy)) { 21787 $this->objcopy->_destroy(true, true); 21788 /* The unique file_id should not be used during cleanup again */ 21789 $this->objcopy->file_id = NULL; 21790 unset($this->objcopy); 21791 } 21792 } 21793 21794 /** 21795 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction(). 21796 * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value. 21797 * @return TCPDF object. 21798 * @public 21799 * @since 4.5.029 (2009-03-19) 21800 */ 21801 public function rollbackTransaction($self=false) { 21802 if (isset($this->objcopy)) { 21803 $objcopy = $this->objcopy; 21804 $this->_destroy(true, true); 21805 if ($self) { 21806 $objvars = get_object_vars($objcopy); 21807 foreach ($objvars as $key => $value) { 21808 $this->$key = $value; 21809 } 21810 $objcopy->_destroy(true, true); 21811 /* The unique file_id should not be used during cleanup again */ 21812 $objcopy->file_id = NULL; 21813 unset($objcopy); 21814 return $this; 21815 } 21816 /* The unique file_id should not be used during cleanup again */ 21817 $this->file_id = NULL; 21818 return $objcopy; 21819 } 21820 return $this; 21821 } 21822 21823 // --- MULTI COLUMNS METHODS ----------------------- 21824 21825 /** 21826 * Set multiple columns of the same size 21827 * @param $numcols (int) number of columns (set to zero to disable columns mode) 21828 * @param $width (int) column width 21829 * @param $y (int) column starting Y position (leave empty for current Y position) 21830 * @public 21831 * @since 4.9.001 (2010-03-28) 21832 */ 21833 public function setEqualColumns($numcols=0, $width=0, $y='') { 21834 $this->columns = array(); 21835 if ($numcols < 2) { 21836 $numcols = 0; 21837 $this->columns = array(); 21838 } else { 21839 // maximum column width 21840 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols; 21841 if (($width == 0) OR ($width > $maxwidth)) { 21842 $width = $maxwidth; 21843 } 21844 if (TCPDF_STATIC::empty_string($y)) { 21845 $y = $this->y; 21846 } 21847 // space between columns 21848 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1)); 21849 // fill the columns array (with, space, starting Y position) 21850 for ($i = 0; $i < $numcols; ++$i) { 21851 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y); 21852 } 21853 } 21854 $this->num_columns = $numcols; 21855 $this->current_column = 0; 21856 $this->column_start_page = $this->page; 21857 $this->selectColumn(0); 21858 } 21859 21860 /** 21861 * Remove columns and reset page margins. 21862 * @public 21863 * @since 5.9.072 (2011-04-26) 21864 */ 21865 public function resetColumns() { 21866 $this->lMargin = $this->original_lMargin; 21867 $this->rMargin = $this->original_rMargin; 21868 $this->setEqualColumns(); 21869 } 21870 21871 /** 21872 * Set columns array. 21873 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position). 21874 * @param $columns (array) 21875 * @public 21876 * @since 4.9.001 (2010-03-28) 21877 */ 21878 public function setColumnsArray($columns) { 21879 $this->columns = $columns; 21880 $this->num_columns = count($columns); 21881 $this->current_column = 0; 21882 $this->column_start_page = $this->page; 21883 $this->selectColumn(0); 21884 } 21885 21886 /** 21887 * Set position at a given column 21888 * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column. 21889 * @public 21890 * @since 4.9.001 (2010-03-28) 21891 */ 21892 public function selectColumn($col='') { 21893 if (is_string($col)) { 21894 $col = $this->current_column; 21895 } elseif ($col >= $this->num_columns) { 21896 $col = 0; 21897 } 21898 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 21899 $enable_thead = false; 21900 if ($this->num_columns > 1) { 21901 if ($col != $this->current_column) { 21902 // move Y pointer at the top of the column 21903 if ($this->column_start_page == $this->page) { 21904 $this->y = $this->columns[$col]['y']; 21905 } else { 21906 $this->y = $this->tMargin; 21907 } 21908 // Avoid to write table headers more than once 21909 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) { 21910 $enable_thead = true; 21911 $this->maxselcol['page'] = $this->page; 21912 $this->maxselcol['column'] = $col; 21913 } 21914 } 21915 $xshift = $this->colxshift; 21916 // set X position of the current column by case 21917 $listindent = ($this->listindentlevel * $this->listindent); 21918 // calculate column X position 21919 $colpos = 0; 21920 for ($i = 0; $i < $col; ++$i) { 21921 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']); 21922 } 21923 if ($this->rtl) { 21924 $x = $this->w - $this->original_rMargin - $colpos; 21925 $this->rMargin = ($this->w - $x + $listindent); 21926 $this->lMargin = ($x - $this->columns[$col]['w']); 21927 $this->x = $x - $listindent; 21928 } else { 21929 $x = $this->original_lMargin + $colpos; 21930 $this->lMargin = ($x + $listindent); 21931 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']); 21932 $this->x = $x + $listindent; 21933 } 21934 $this->columns[$col]['x'] = $x; 21935 } 21936 $this->current_column = $col; 21937 // fix for HTML mode 21938 $this->newline = true; 21939 // print HTML table header (if any) 21940 if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) { 21941 if ($enable_thead) { 21942 // print table header 21943 $this->writeHTML($this->thead, false, false, false, false, ''); 21944 $this->y += $xshift['s']['V']; 21945 // store end of header position 21946 if (!isset($this->columns[$col]['th'])) { 21947 $this->columns[$col]['th'] = array(); 21948 } 21949 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y; 21950 $this->lasth = 0; 21951 } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) { 21952 $this->y = $this->columns[$col]['th']['\''.$this->page.'\'']; 21953 } 21954 } 21955 // account for an html table cell over multiple columns 21956 if ($this->rtl) { 21957 $this->rMargin += $xshift['x']; 21958 $this->x -= ($xshift['x'] + $xshift['p']['R']); 21959 } else { 21960 $this->lMargin += $xshift['x']; 21961 $this->x += $xshift['x'] + $xshift['p']['L']; 21962 } 21963 } 21964 21965 /** 21966 * Return the current column number 21967 * @return int current column number 21968 * @public 21969 * @since 5.5.011 (2010-07-08) 21970 */ 21971 public function getColumn() { 21972 return $this->current_column; 21973 } 21974 21975 /** 21976 * Return the current number of columns. 21977 * @return int number of columns 21978 * @public 21979 * @since 5.8.018 (2010-08-25) 21980 */ 21981 public function getNumberOfColumns() { 21982 return $this->num_columns; 21983 } 21984 21985 /** 21986 * Set Text rendering mode. 21987 * @param $stroke (int) outline size in user units (0 = disable). 21988 * @param $fill (boolean) if true fills the text (default). 21989 * @param $clip (boolean) if true activate clipping mode 21990 * @public 21991 * @since 4.9.008 (2009-04-02) 21992 */ 21993 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) { 21994 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode 21995 // convert text rendering parameters 21996 if ($stroke < 0) { 21997 $stroke = 0; 21998 } 21999 if ($fill === true) { 22000 if ($stroke > 0) { 22001 if ($clip === true) { 22002 // Fill, then stroke text and add to path for clipping 22003 $textrendermode = 6; 22004 } else { 22005 // Fill, then stroke text 22006 $textrendermode = 2; 22007 } 22008 $textstrokewidth = $stroke; 22009 } else { 22010 if ($clip === true) { 22011 // Fill text and add to path for clipping 22012 $textrendermode = 4; 22013 } else { 22014 // Fill text 22015 $textrendermode = 0; 22016 } 22017 } 22018 } else { 22019 if ($stroke > 0) { 22020 if ($clip === true) { 22021 // Stroke text and add to path for clipping 22022 $textrendermode = 5; 22023 } else { 22024 // Stroke text 22025 $textrendermode = 1; 22026 } 22027 $textstrokewidth = $stroke; 22028 } else { 22029 if ($clip === true) { 22030 // Add text to path for clipping 22031 $textrendermode = 7; 22032 } else { 22033 // Neither fill nor stroke text (invisible) 22034 $textrendermode = 3; 22035 } 22036 } 22037 } 22038 $this->textrendermode = $textrendermode; 22039 $this->textstrokewidth = $stroke; 22040 } 22041 22042 /** 22043 * Set parameters for drop shadow effect for text. 22044 * @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. 22045 * @since 5.9.174 (2012-07-25) 22046 * @public 22047 */ 22048 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) { 22049 if (isset($params['enabled'])) { 22050 $this->txtshadow['enabled'] = $params['enabled']?true:false; 22051 } else { 22052 $this->txtshadow['enabled'] = false; 22053 } 22054 if (isset($params['depth_w'])) { 22055 $this->txtshadow['depth_w'] = floatval($params['depth_w']); 22056 } else { 22057 $this->txtshadow['depth_w'] = 0; 22058 } 22059 if (isset($params['depth_h'])) { 22060 $this->txtshadow['depth_h'] = floatval($params['depth_h']); 22061 } else { 22062 $this->txtshadow['depth_h'] = 0; 22063 } 22064 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) { 22065 $this->txtshadow['color'] = $params['color']; 22066 } else { 22067 $this->txtshadow['color'] = $this->strokecolor; 22068 } 22069 if (isset($params['opacity'])) { 22070 $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity']))); 22071 } else { 22072 $this->txtshadow['opacity'] = 1; 22073 } 22074 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'))) { 22075 $this->txtshadow['blend_mode'] = $params['blend_mode']; 22076 } else { 22077 $this->txtshadow['blend_mode'] = 'Normal'; 22078 } 22079 if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) { 22080 $this->txtshadow['enabled'] = false; 22081 } 22082 } 22083 22084 /** 22085 * Return the text shadow parameters array. 22086 * @return Array of parameters. 22087 * @since 5.9.174 (2012-07-25) 22088 * @public 22089 */ 22090 public function getTextShadow() { 22091 return $this->txtshadow; 22092 } 22093 22094 /** 22095 * Returns an array of chars containing soft hyphens. 22096 * @param $word (array) array of chars 22097 * @param $patterns (array) Array of hypenation patterns. 22098 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algorithm. 22099 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens. 22100 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens. 22101 * @param $charmin (int) Minimum word length to apply the hyphenation algorithm. 22102 * @param $charmax (int) Maximum length of broken piece of word. 22103 * @return array text with soft hyphens 22104 * @author Nicola Asuni 22105 * @since 4.9.012 (2010-04-12) 22106 * @protected 22107 */ 22108 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 22109 $hyphenword = array(); // hyphens positions 22110 $numchars = count($word); 22111 if ($numchars <= $charmin) { 22112 return $word; 22113 } 22114 $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode); 22115 // some words will be returned as-is 22116 $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})(\]?)$/'; 22117 if (preg_match($pattern, $word_string) > 0) { 22118 // email 22119 return $word; 22120 } 22121 $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})(\]?)$/'; 22122 if (preg_match($pattern, $word_string) > 0) { 22123 // URL 22124 return $word; 22125 } 22126 if (isset($dictionary[$word_string])) { 22127 return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont); 22128 } 22129 // surround word with '_' characters 22130 $tmpword = array_merge(array(46), $word, array(46)); 22131 $tmpnumchars = $numchars + 2; 22132 $maxpos = $tmpnumchars - 1; 22133 for ($pos = 0; $pos < $maxpos; ++$pos) { 22134 $imax = min(($tmpnumchars - $pos), $charmax); 22135 for ($i = 1; $i <= $imax; ++$i) { 22136 $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode)); 22137 if (isset($patterns[$subword])) { 22138 $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont); 22139 $pattern_length = count($pattern); 22140 $digits = 1; 22141 for ($j = 0; $j < $pattern_length; ++$j) { 22142 // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid) 22143 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) { 22144 if ($j == 0) { 22145 $zero = $pos - 1; 22146 } else { 22147 $zero = $pos + $j - $digits; 22148 } 22149 // get hyphenation level 22150 $level = ($pattern[$j] - 48); 22151 // if two levels from two different patterns match at the same point, the higher one is selected. 22152 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) { 22153 $hyphenword[$zero] = $level; 22154 } 22155 ++$digits; 22156 } 22157 } 22158 } 22159 } 22160 } 22161 $inserted = 0; 22162 $maxpos = $numchars - $rightmin; 22163 for ($i = $leftmin; $i <= $maxpos; ++$i) { 22164 // only odd levels indicate allowed hyphenation points 22165 if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) { 22166 // 173 = soft hyphen character 22167 array_splice($word, $i + $inserted, 0, 173); 22168 ++$inserted; 22169 } 22170 } 22171 return $word; 22172 } 22173 22174 /** 22175 * Returns text with soft hyphens. 22176 * @param $text (string) text to process 22177 * @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/ 22178 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algorithm. 22179 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens. 22180 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens. 22181 * @param $charmin (int) Minimum word length to apply the hyphenation algorithm. 22182 * @param $charmax (int) Maximum length of broken piece of word. 22183 * @return array text with soft hyphens 22184 * @author Nicola Asuni 22185 * @since 4.9.012 (2010-04-12) 22186 * @public 22187 */ 22188 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 22189 $text = $this->unhtmlentities($text); 22190 $word = array(); // last word 22191 $txtarr = array(); // text to be returned 22192 $intag = false; // true if we are inside an HTML tag 22193 $skip = false; // true to skip hyphenation 22194 if (!is_array($patterns)) { 22195 $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns); 22196 } 22197 // get array of characters 22198 $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont); 22199 // for each char 22200 foreach ($unichars as $char) { 22201 if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') { 22202 // letter character 22203 $word[] = $char; 22204 } else { 22205 // other type of character 22206 if (!TCPDF_STATIC::empty_string($word)) { 22207 // hypenate the word 22208 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 22209 $word = array(); 22210 } 22211 $txtarr[] = $char; 22212 if (chr($char) == '<') { 22213 // we are inside an HTML tag 22214 $intag = true; 22215 } elseif ($intag AND (chr($char) == '>')) { 22216 // end of HTML tag 22217 $intag = false; 22218 // check for style tag 22219 $expected = array(115, 116, 121, 108, 101); // = 'style' 22220 $current = array_slice($txtarr, -6, 5); // last 5 chars 22221 $compare = array_diff($expected, $current); 22222 if (empty($compare)) { 22223 // check if it is a closing tag 22224 $expected = array(47); // = '/' 22225 $current = array_slice($txtarr, -7, 1); 22226 $compare = array_diff($expected, $current); 22227 if (empty($compare)) { 22228 // closing style tag 22229 $skip = false; 22230 } else { 22231 // opening style tag 22232 $skip = true; 22233 } 22234 } 22235 } 22236 } 22237 } 22238 if (!TCPDF_STATIC::empty_string($word)) { 22239 // hypenate the word 22240 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 22241 } 22242 // convert char array to string and return 22243 return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode); 22244 } 22245 22246 /** 22247 * Enable/disable rasterization of vector images using ImageMagick library. 22248 * @param $mode (boolean) if true enable rasterization, false otherwise. 22249 * @public 22250 * @since 5.0.000 (2010-04-27) 22251 */ 22252 public function setRasterizeVectorImages($mode) { 22253 $this->rasterize_vector_images = $mode; 22254 } 22255 22256 /** 22257 * Enable or disable default option for font subsetting. 22258 * @param $enable (boolean) if true enable font subsetting by default. 22259 * @author Nicola Asuni 22260 * @public 22261 * @since 5.3.002 (2010-06-07) 22262 */ 22263 public function setFontSubsetting($enable=true) { 22264 if ($this->pdfa_mode) { 22265 $this->font_subsetting = false; 22266 } else { 22267 $this->font_subsetting = $enable ? true : false; 22268 } 22269 } 22270 22271 /** 22272 * Return the default option for font subsetting. 22273 * @return boolean default font subsetting state. 22274 * @author Nicola Asuni 22275 * @public 22276 * @since 5.3.002 (2010-06-07) 22277 */ 22278 public function getFontSubsetting() { 22279 return $this->font_subsetting; 22280 } 22281 22282 /** 22283 * Left trim the input string 22284 * @param $str (string) string to trim 22285 * @param $replace (string) string that replace spaces. 22286 * @return left trimmed string 22287 * @author Nicola Asuni 22288 * @public 22289 * @since 5.8.000 (2010-08-11) 22290 */ 22291 public function stringLeftTrim($str, $replace='') { 22292 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str); 22293 } 22294 22295 /** 22296 * Right trim the input string 22297 * @param $str (string) string to trim 22298 * @param $replace (string) string that replace spaces. 22299 * @return right trimmed string 22300 * @author Nicola Asuni 22301 * @public 22302 * @since 5.8.000 (2010-08-11) 22303 */ 22304 public function stringRightTrim($str, $replace='') { 22305 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str); 22306 } 22307 22308 /** 22309 * Trim the input string 22310 * @param $str (string) string to trim 22311 * @param $replace (string) string that replace spaces. 22312 * @return trimmed string 22313 * @author Nicola Asuni 22314 * @public 22315 * @since 5.8.000 (2010-08-11) 22316 */ 22317 public function stringTrim($str, $replace='') { 22318 $str = $this->stringLeftTrim($str, $replace); 22319 $str = $this->stringRightTrim($str, $replace); 22320 return $str; 22321 } 22322 22323 /** 22324 * Return true if the current font is unicode type. 22325 * @return true for unicode font, false otherwise. 22326 * @author Nicola Asuni 22327 * @public 22328 * @since 5.8.002 (2010-08-14) 22329 */ 22330 public function isUnicodeFont() { 22331 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')); 22332 } 22333 22334 /** 22335 * Return normalized font name 22336 * @param $fontfamily (string) property string containing font family names 22337 * @return string normalized font name 22338 * @author Nicola Asuni 22339 * @public 22340 * @since 5.8.004 (2010-08-17) 22341 */ 22342 public function getFontFamilyName($fontfamily) { 22343 // remove spaces and symbols 22344 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily)); 22345 // extract all font names 22346 $fontslist = preg_split('/[,]/', $fontfamily); 22347 // find first valid font name 22348 foreach ($fontslist as $font) { 22349 // replace font variations 22350 $font = preg_replace('/regular$/', '', $font); 22351 $font = preg_replace('/italic$/', 'I', $font); 22352 $font = preg_replace('/oblique$/', 'I', $font); 22353 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font); 22354 // replace common family names and core fonts 22355 $pattern = array(); 22356 $replacement = array(); 22357 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/'; 22358 $replacement[] = 'times'; 22359 $pattern[] = '/^sansserif/'; 22360 $replacement[] = 'helvetica'; 22361 $pattern[] = '/^monospace/'; 22362 $replacement[] = 'courier'; 22363 $font = preg_replace($pattern, $replacement, $font); 22364 if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) { 22365 return $font; 22366 } 22367 } 22368 // return current font as default 22369 return $this->CurrentFont['fontkey']; 22370 } 22371 22372 /** 22373 * Start a new XObject Template. 22374 * 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). 22375 * 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. 22376 * Note: X,Y coordinates will be reset to 0,0. 22377 * @param $w (int) Template width in user units (empty string or zero = page width less margins). 22378 * @param $h (int) Template height in user units (empty string or zero = page height less margins). 22379 * @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). 22380 * @return int the XObject Template ID in case of success or false in case of error. 22381 * @author Nicola Asuni 22382 * @public 22383 * @since 5.8.017 (2010-08-24) 22384 * @see endTemplate(), printTemplate() 22385 */ 22386 public function startTemplate($w=0, $h=0, $group=false) { 22387 if ($this->inxobj) { 22388 // we are already inside an XObject template 22389 return false; 22390 } 22391 $this->inxobj = true; 22392 ++$this->n; 22393 // XObject ID 22394 $this->xobjid = 'XT'.$this->n; 22395 // object ID 22396 $this->xobjects[$this->xobjid] = array('n' => $this->n); 22397 // store current graphic state 22398 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars(); 22399 // initialize data 22400 $this->xobjects[$this->xobjid]['intmrk'] = 0; 22401 $this->xobjects[$this->xobjid]['transfmrk'] = array(); 22402 $this->xobjects[$this->xobjid]['outdata'] = ''; 22403 $this->xobjects[$this->xobjid]['xobjects'] = array(); 22404 $this->xobjects[$this->xobjid]['images'] = array(); 22405 $this->xobjects[$this->xobjid]['fonts'] = array(); 22406 $this->xobjects[$this->xobjid]['annotations'] = array(); 22407 $this->xobjects[$this->xobjid]['extgstates'] = array(); 22408 $this->xobjects[$this->xobjid]['gradients'] = array(); 22409 $this->xobjects[$this->xobjid]['spot_colors'] = array(); 22410 // set new environment 22411 $this->num_columns = 1; 22412 $this->current_column = 0; 22413 $this->SetAutoPageBreak(false); 22414 if (($w === '') OR ($w <= 0)) { 22415 $w = $this->w - $this->lMargin - $this->rMargin; 22416 } 22417 if (($h === '') OR ($h <= 0)) { 22418 $h = $this->h - $this->tMargin - $this->bMargin; 22419 } 22420 $this->xobjects[$this->xobjid]['x'] = 0; 22421 $this->xobjects[$this->xobjid]['y'] = 0; 22422 $this->xobjects[$this->xobjid]['w'] = $w; 22423 $this->xobjects[$this->xobjid]['h'] = $h; 22424 $this->w = $w; 22425 $this->h = $h; 22426 $this->wPt = $this->w * $this->k; 22427 $this->hPt = $this->h * $this->k; 22428 $this->fwPt = $this->wPt; 22429 $this->fhPt = $this->hPt; 22430 $this->x = 0; 22431 $this->y = 0; 22432 $this->lMargin = 0; 22433 $this->rMargin = 0; 22434 $this->tMargin = 0; 22435 $this->bMargin = 0; 22436 // set group mode 22437 $this->xobjects[$this->xobjid]['group'] = $group; 22438 return $this->xobjid; 22439 } 22440 22441 /** 22442 * End the current XObject Template started with startTemplate() and restore the previous graphic state. 22443 * 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). 22444 * 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. 22445 * @return int the XObject Template ID in case of success or false in case of error. 22446 * @author Nicola Asuni 22447 * @public 22448 * @since 5.8.017 (2010-08-24) 22449 * @see startTemplate(), printTemplate() 22450 */ 22451 public function endTemplate() { 22452 if (!$this->inxobj) { 22453 // we are not inside a template 22454 return false; 22455 } 22456 $this->inxobj = false; 22457 // restore previous graphic state 22458 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true); 22459 return $this->xobjid; 22460 } 22461 22462 /** 22463 * Print an XObject Template. 22464 * You can print an XObject Template inside the currently opened Template. 22465 * 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). 22466 * 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. 22467 * @param $id (string) The ID of XObject Template to print. 22468 * @param $x (int) X position in user units (empty string = current x position) 22469 * @param $y (int) Y position in user units (empty string = current y position) 22470 * @param $w (int) Width in user units (zero = remaining page width) 22471 * @param $h (int) Height in user units (zero = remaining page height) 22472 * @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> 22473 * @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> 22474 * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions. 22475 * @author Nicola Asuni 22476 * @public 22477 * @since 5.8.017 (2010-08-24) 22478 * @see startTemplate(), endTemplate() 22479 */ 22480 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) { 22481 if ($this->state != 2) { 22482 return; 22483 } 22484 if (!isset($this->xobjects[$id])) { 22485 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!'); 22486 } 22487 if ($this->inxobj) { 22488 if ($id == $this->xobjid) { 22489 // close current template 22490 $this->endTemplate(); 22491 } else { 22492 // use the template as resource for the template currently opened 22493 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id]; 22494 } 22495 } 22496 // set default values 22497 if ($x === '') { 22498 $x = $this->x; 22499 } 22500 if ($y === '') { 22501 $y = $this->y; 22502 } 22503 // check page for no-write regions and adapt page margins if necessary 22504 list($x, $y) = $this->checkPageRegions($h, $x, $y); 22505 $ow = $this->xobjects[$id]['w']; 22506 if ($ow <= 0) { 22507 $ow = 1; 22508 } 22509 $oh = $this->xobjects[$id]['h']; 22510 if ($oh <= 0) { 22511 $oh = 1; 22512 } 22513 // calculate template width and height on document 22514 if (($w <= 0) AND ($h <= 0)) { 22515 $w = $ow; 22516 $h = $oh; 22517 } elseif ($w <= 0) { 22518 $w = $h * $ow / $oh; 22519 } elseif ($h <= 0) { 22520 $h = $w * $oh / $ow; 22521 } 22522 // fit the template on available space 22523 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 22524 // set page alignment 22525 $rb_y = $y + $h; 22526 // set alignment 22527 if ($this->rtl) { 22528 if ($palign == 'L') { 22529 $xt = $this->lMargin; 22530 } elseif ($palign == 'C') { 22531 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22532 } elseif ($palign == 'R') { 22533 $xt = $this->w - $this->rMargin - $w; 22534 } else { 22535 $xt = $x - $w; 22536 } 22537 $rb_x = $xt; 22538 } else { 22539 if ($palign == 'L') { 22540 $xt = $this->lMargin; 22541 } elseif ($palign == 'C') { 22542 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22543 } elseif ($palign == 'R') { 22544 $xt = $this->w - $this->rMargin - $w; 22545 } else { 22546 $xt = $x; 22547 } 22548 $rb_x = $xt + $w; 22549 } 22550 // print XObject Template + Transformation matrix 22551 $this->StartTransform(); 22552 // translate and scale 22553 $sx = ($w / $ow); 22554 $sy = ($h / $oh); 22555 $tm = array(); 22556 $tm[0] = $sx; 22557 $tm[1] = 0; 22558 $tm[2] = 0; 22559 $tm[3] = $sy; 22560 $tm[4] = $xt * $this->k; 22561 $tm[5] = ($this->h - $h - $y) * $this->k; 22562 $this->Transform($tm); 22563 // set object 22564 $this->_out('/'.$id.' Do'); 22565 $this->StopTransform(); 22566 // add annotations 22567 if (!empty($this->xobjects[$id]['annotations'])) { 22568 foreach ($this->xobjects[$id]['annotations'] as $annot) { 22569 // transform original coordinates 22570 $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k))); 22571 $ax = ($coordlt[4] / $this->k); 22572 $ay = ($this->h - $h - ($coordlt[5] / $this->k)); 22573 $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k))); 22574 $aw = ($coordrb[4] / $this->k) - $ax; 22575 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay; 22576 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']); 22577 } 22578 } 22579 // set pointer to align the next text/objects 22580 switch($align) { 22581 case 'T': { 22582 $this->y = $y; 22583 $this->x = $rb_x; 22584 break; 22585 } 22586 case 'M': { 22587 $this->y = $y + round($h/2); 22588 $this->x = $rb_x; 22589 break; 22590 } 22591 case 'B': { 22592 $this->y = $rb_y; 22593 $this->x = $rb_x; 22594 break; 22595 } 22596 case 'N': { 22597 $this->SetY($rb_y); 22598 break; 22599 } 22600 default:{ 22601 break; 22602 } 22603 } 22604 } 22605 22606 /** 22607 * Set the percentage of character stretching. 22608 * @param $perc (int) percentage of stretching (100 = no stretching) 22609 * @author Nicola Asuni 22610 * @public 22611 * @since 5.9.000 (2010-09-29) 22612 */ 22613 public function setFontStretching($perc=100) { 22614 $this->font_stretching = $perc; 22615 } 22616 22617 /** 22618 * Get the percentage of character stretching. 22619 * @return float stretching value 22620 * @author Nicola Asuni 22621 * @public 22622 * @since 5.9.000 (2010-09-29) 22623 */ 22624 public function getFontStretching() { 22625 return $this->font_stretching; 22626 } 22627 22628 /** 22629 * Set the amount to increase or decrease the space between characters in a text. 22630 * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing) 22631 * @author Nicola Asuni 22632 * @public 22633 * @since 5.9.000 (2010-09-29) 22634 */ 22635 public function setFontSpacing($spacing=0) { 22636 $this->font_spacing = $spacing; 22637 } 22638 22639 /** 22640 * Get the amount to increase or decrease the space between characters in a text. 22641 * @return int font spacing (tracking) value 22642 * @author Nicola Asuni 22643 * @public 22644 * @since 5.9.000 (2010-09-29) 22645 */ 22646 public function getFontSpacing() { 22647 return $this->font_spacing; 22648 } 22649 22650 /** 22651 * Return an array of no-write page regions 22652 * @return array of no-write page regions 22653 * @author Nicola Asuni 22654 * @public 22655 * @since 5.9.003 (2010-10-13) 22656 * @see setPageRegions(), addPageRegion() 22657 */ 22658 public function getPageRegions() { 22659 return $this->page_regions; 22660 } 22661 22662 /** 22663 * Set no-write regions on page. 22664 * 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. 22665 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22666 * You can set multiple regions for the same page. 22667 * @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. 22668 * @author Nicola Asuni 22669 * @public 22670 * @since 5.9.003 (2010-10-13) 22671 * @see addPageRegion(), getPageRegions() 22672 */ 22673 public function setPageRegions($regions=array()) { 22674 // empty current regions array 22675 $this->page_regions = array(); 22676 // add regions 22677 foreach ($regions as $data) { 22678 $this->addPageRegion($data); 22679 } 22680 } 22681 22682 /** 22683 * Add a single no-write region on selected page. 22684 * 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. 22685 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22686 * You can set multiple regions for the same page. 22687 * @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). 22688 * @author Nicola Asuni 22689 * @public 22690 * @since 5.9.003 (2010-10-13) 22691 * @see setPageRegions(), getPageRegions() 22692 */ 22693 public function addPageRegion($region) { 22694 if (!isset($region['page']) OR empty($region['page'])) { 22695 $region['page'] = $this->page; 22696 } 22697 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0) 22698 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb']) 22699 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) { 22700 $this->page_regions[] = $region; 22701 } 22702 } 22703 22704 /** 22705 * Remove a single no-write region. 22706 * @param $key (int) region key 22707 * @author Nicola Asuni 22708 * @public 22709 * @since 5.9.003 (2010-10-13) 22710 * @see setPageRegions(), getPageRegions() 22711 */ 22712 public function removePageRegion($key) { 22713 if (isset($this->page_regions[$key])) { 22714 unset($this->page_regions[$key]); 22715 } 22716 } 22717 22718 /** 22719 * Check page for no-write regions and adapt current coordinates and page margins if necessary. 22720 * 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. 22721 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. 22722 * @param $h (float) height of the text/image/object to print in user units 22723 * @param $x (float) current X coordinate in user units 22724 * @param $y (float) current Y coordinate in user units 22725 * @return array($x, $y) 22726 * @author Nicola Asuni 22727 * @protected 22728 * @since 5.9.003 (2010-10-13) 22729 */ 22730 protected function checkPageRegions($h, $x, $y) { 22731 // set default values 22732 if ($x === '') { 22733 $x = $this->x; 22734 } 22735 if ($y === '') { 22736 $y = $this->y; 22737 } 22738 if (!$this->check_page_regions OR empty($this->page_regions)) { 22739 // no page regions defined 22740 return array($x, $y); 22741 } 22742 if (empty($h)) { 22743 $h = $this->getCellHeight($this->FontSize); 22744 } 22745 // check for page break 22746 if ($this->checkPageBreak($h, $y)) { 22747 // the content will be printed on a new page 22748 $x = $this->x; 22749 $y = $this->y; 22750 } 22751 if ($this->num_columns > 1) { 22752 if ($this->rtl) { 22753 $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']); 22754 } else { 22755 $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']); 22756 } 22757 } else { 22758 if ($this->rtl) { 22759 $this->lMargin = max($this->clMargin, $this->original_lMargin); 22760 } else { 22761 $this->rMargin = max($this->crMargin, $this->original_rMargin); 22762 } 22763 } 22764 // adjust coordinates and page margins 22765 foreach ($this->page_regions as $regid => $regdata) { 22766 if ($regdata['page'] == $this->page) { 22767 // check region boundaries 22768 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) { 22769 // Y is inside the region 22770 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient 22771 $yt = max($y, $regdata['yt']); 22772 $yb = min(($yt + $h), $regdata['yb']); 22773 $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt']; 22774 $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt']; 22775 if ($regdata['side'] == 'L') { // left side 22776 $new_margin = max($xt, $xb); 22777 if ($this->lMargin < $new_margin) { 22778 if ($this->rtl) { 22779 // adjust left page margin 22780 $this->lMargin = max(0, $new_margin); 22781 } 22782 if ($x < $new_margin) { 22783 // adjust x position 22784 $x = $new_margin; 22785 if ($new_margin > ($this->w - $this->rMargin)) { 22786 // adjust y position 22787 $y = $regdata['yb'] - $h; 22788 } 22789 } 22790 } 22791 } elseif ($regdata['side'] == 'R') { // right side 22792 $new_margin = min($xt, $xb); 22793 if (($this->w - $this->rMargin) > $new_margin) { 22794 if (!$this->rtl) { 22795 // adjust right page margin 22796 $this->rMargin = max(0, ($this->w - $new_margin)); 22797 } 22798 if ($x > $new_margin) { 22799 // adjust x position 22800 $x = $new_margin; 22801 if ($new_margin > $this->lMargin) { 22802 // adjust y position 22803 $y = $regdata['yb'] - $h; 22804 } 22805 } 22806 } 22807 } 22808 } 22809 } 22810 } 22811 return array($x, $y); 22812 } 22813 22814 // --- SVG METHODS --------------------------------------------------------- 22815 22816 /** 22817 * Embedd a Scalable Vector Graphics (SVG) image. 22818 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library. 22819 * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string. 22820 * @param $x (float) Abscissa of the upper-left corner. 22821 * @param $y (float) Ordinate of the upper-left corner. 22822 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 22823 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 22824 * @param $link (mixed) URL or identifier returned by AddLink(). 22825 * @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. 22826 * @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> 22827 * @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))) 22828 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions. 22829 * @author Nicola Asuni 22830 * @since 5.0.000 (2010-05-02) 22831 * @public 22832 */ 22833 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) { 22834 if ($this->state != 2) { 22835 return; 22836 } 22837 // reset SVG vars 22838 $this->svggradients = array(); 22839 $this->svggradientid = 0; 22840 $this->svgdefsmode = false; 22841 $this->svgdefs = array(); 22842 $this->svgclipmode = false; 22843 $this->svgclippaths = array(); 22844 $this->svgcliptm = array(); 22845 $this->svgclipid = 0; 22846 $this->svgtext = ''; 22847 $this->svgtextmode = array(); 22848 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { 22849 // convert SVG to raster image using GD or ImageMagick libraries 22850 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 22851 } 22852 if ($file[0] === '@') { // image from string 22853 $this->svgdir = ''; 22854 $svgdata = substr($file, 1); 22855 } else { // SVG file 22856 $this->svgdir = dirname($file); 22857 $svgdata = $this->getCachedFileContents($file); 22858 } 22859 if ($svgdata === FALSE) { 22860 $this->Error('SVG file not found: '.$file); 22861 } 22862 if ($x === '') { 22863 $x = $this->x; 22864 } 22865 if ($y === '') { 22866 $y = $this->y; 22867 } 22868 // check page for no-write regions and adapt page margins if necessary 22869 list($x, $y) = $this->checkPageRegions($h, $x, $y); 22870 $k = $this->k; 22871 $ox = 0; 22872 $oy = 0; 22873 $ow = $w; 22874 $oh = $h; 22875 $aspect_ratio_align = 'xMidYMid'; 22876 $aspect_ratio_ms = 'meet'; 22877 $regs = array(); 22878 // get original image width and height 22879 preg_match('/<svg([^\>]*)>/si', $svgdata, $regs); 22880 if (isset($regs[1]) AND !empty($regs[1])) { 22881 $tmp = array(); 22882 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22883 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 22884 } 22885 $tmp = array(); 22886 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22887 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 22888 } 22889 $tmp = array(); 22890 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22891 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 22892 } 22893 $tmp = array(); 22894 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22895 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 22896 } 22897 $tmp = array(); 22898 $view_box = array(); 22899 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) { 22900 if (count($tmp) == 5) { 22901 array_shift($tmp); 22902 foreach ($tmp as $key => $val) { 22903 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 22904 } 22905 $ox = $view_box[0]; 22906 $oy = $view_box[1]; 22907 } 22908 // get aspect ratio 22909 $tmp = array(); 22910 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 22911 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]); 22912 switch (count($aspect_ratio)) { 22913 case 3: { 22914 $aspect_ratio_align = $aspect_ratio[1]; 22915 $aspect_ratio_ms = $aspect_ratio[2]; 22916 break; 22917 } 22918 case 2: { 22919 $aspect_ratio_align = $aspect_ratio[0]; 22920 $aspect_ratio_ms = $aspect_ratio[1]; 22921 break; 22922 } 22923 case 1: { 22924 $aspect_ratio_align = $aspect_ratio[0]; 22925 $aspect_ratio_ms = 'meet'; 22926 break; 22927 } 22928 } 22929 } 22930 } 22931 } 22932 if ($ow <= 0) { 22933 $ow = 1; 22934 } 22935 if ($oh <= 0) { 22936 $oh = 1; 22937 } 22938 // calculate image width and height on document 22939 if (($w <= 0) AND ($h <= 0)) { 22940 // convert image size to document unit 22941 $w = $ow; 22942 $h = $oh; 22943 } elseif ($w <= 0) { 22944 $w = $h * $ow / $oh; 22945 } elseif ($h <= 0) { 22946 $h = $w * $oh / $ow; 22947 } 22948 // fit the image on available space 22949 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 22950 if ($this->rasterize_vector_images) { 22951 // convert SVG to raster image using GD or ImageMagick libraries 22952 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 22953 } 22954 // set alignment 22955 $this->img_rb_y = $y + $h; 22956 // set alignment 22957 if ($this->rtl) { 22958 if ($palign == 'L') { 22959 $ximg = $this->lMargin; 22960 } elseif ($palign == 'C') { 22961 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22962 } elseif ($palign == 'R') { 22963 $ximg = $this->w - $this->rMargin - $w; 22964 } else { 22965 $ximg = $x - $w; 22966 } 22967 $this->img_rb_x = $ximg; 22968 } else { 22969 if ($palign == 'L') { 22970 $ximg = $this->lMargin; 22971 } elseif ($palign == 'C') { 22972 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 22973 } elseif ($palign == 'R') { 22974 $ximg = $this->w - $this->rMargin - $w; 22975 } else { 22976 $ximg = $x; 22977 } 22978 $this->img_rb_x = $ximg + $w; 22979 } 22980 // store current graphic vars 22981 $gvars = $this->getGraphicVars(); 22982 // store SVG position and scale factors 22983 $svgoffset_x = ($ximg - $ox) * $this->k; 22984 $svgoffset_y = -($y - $oy) * $this->k; 22985 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) { 22986 $ow = $view_box[2]; 22987 $oh = $view_box[3]; 22988 } else { 22989 if ($ow <= 0) { 22990 $ow = $w; 22991 } 22992 if ($oh <= 0) { 22993 $oh = $h; 22994 } 22995 } 22996 $svgscale_x = $w / $ow; 22997 $svgscale_y = $h / $oh; 22998 // scaling and alignment 22999 if ($aspect_ratio_align != 'none') { 23000 // store current scaling values 23001 $svgscale_old_x = $svgscale_x; 23002 $svgscale_old_y = $svgscale_y; 23003 // force uniform scaling 23004 if ($aspect_ratio_ms == 'slice') { 23005 // the entire viewport is covered by the viewBox 23006 if ($svgscale_x > $svgscale_y) { 23007 $svgscale_y = $svgscale_x; 23008 } elseif ($svgscale_x < $svgscale_y) { 23009 $svgscale_x = $svgscale_y; 23010 } 23011 } else { // meet 23012 // the entire viewBox is visible within the viewport 23013 if ($svgscale_x < $svgscale_y) { 23014 $svgscale_y = $svgscale_x; 23015 } elseif ($svgscale_x > $svgscale_y) { 23016 $svgscale_x = $svgscale_y; 23017 } 23018 } 23019 // correct X alignment 23020 switch (substr($aspect_ratio_align, 1, 3)) { 23021 case 'Min': { 23022 // do nothing 23023 break; 23024 } 23025 case 'Max': { 23026 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x)); 23027 break; 23028 } 23029 default: 23030 case 'Mid': { 23031 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2); 23032 break; 23033 } 23034 } 23035 // correct Y alignment 23036 switch (substr($aspect_ratio_align, 5)) { 23037 case 'Min': { 23038 // do nothing 23039 break; 23040 } 23041 case 'Max': { 23042 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y)); 23043 break; 23044 } 23045 default: 23046 case 'Mid': { 23047 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2); 23048 break; 23049 } 23050 } 23051 } 23052 // store current page break mode 23053 $page_break_mode = $this->AutoPageBreak; 23054 $page_break_margin = $this->getBreakMargin(); 23055 $cell_padding = $this->cell_padding; 23056 $this->SetCellPadding(0); 23057 $this->SetAutoPageBreak(false); 23058 // save the current graphic state 23059 $this->_out('q'.$this->epsmarker); 23060 // set initial clipping mask 23061 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array()); 23062 // scale and translate 23063 $e = $ox * $this->k * (1 - $svgscale_x); 23064 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y); 23065 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y))); 23066 // creates a new XML parser to be used by the other XML functions 23067 $parser = xml_parser_create('UTF-8'); 23068 // the following function allows to use parser inside object 23069 xml_set_object($parser, $this); 23070 // disable case-folding for this XML parser 23071 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); 23072 // sets the element handler functions for the XML parser 23073 xml_set_element_handler($parser, 'startSVGElementHandler', 'endSVGElementHandler'); 23074 // sets the character data handler function for the XML parser 23075 xml_set_character_data_handler($parser, 'segSVGContentHandler'); 23076 // start parsing an XML document 23077 if (!xml_parse($parser, $svgdata)) { 23078 $error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser)); 23079 $this->Error($error_message); 23080 } 23081 // free this XML parser 23082 xml_parser_free($parser); 23083 23084 // >= PHP 7.0.0 "explicitly unset the reference to parser to avoid memory leaks" 23085 unset($parser); 23086 23087 // restore previous graphic state 23088 $this->_out($this->epsmarker.'Q'); 23089 // restore graphic vars 23090 $this->setGraphicVars($gvars); 23091 $this->lasth = $gvars['lasth']; 23092 if (!empty($border)) { 23093 $bx = $this->x; 23094 $by = $this->y; 23095 $this->x = $ximg; 23096 if ($this->rtl) { 23097 $this->x += $w; 23098 } 23099 $this->y = $y; 23100 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 23101 $this->x = $bx; 23102 $this->y = $by; 23103 } 23104 if ($link) { 23105 $this->Link($ximg, $y, $w, $h, $link, 0); 23106 } 23107 // set pointer to align the next text/objects 23108 switch($align) { 23109 case 'T':{ 23110 $this->y = $y; 23111 $this->x = $this->img_rb_x; 23112 break; 23113 } 23114 case 'M':{ 23115 $this->y = $y + round($h/2); 23116 $this->x = $this->img_rb_x; 23117 break; 23118 } 23119 case 'B':{ 23120 $this->y = $this->img_rb_y; 23121 $this->x = $this->img_rb_x; 23122 break; 23123 } 23124 case 'N':{ 23125 $this->SetY($this->img_rb_y); 23126 break; 23127 } 23128 default:{ 23129 // restore pointer to starting position 23130 $this->x = $gvars['x']; 23131 $this->y = $gvars['y']; 23132 $this->page = $gvars['page']; 23133 $this->current_column = $gvars['current_column']; 23134 $this->tMargin = $gvars['tMargin']; 23135 $this->bMargin = $gvars['bMargin']; 23136 $this->w = $gvars['w']; 23137 $this->h = $gvars['h']; 23138 $this->wPt = $gvars['wPt']; 23139 $this->hPt = $gvars['hPt']; 23140 $this->fwPt = $gvars['fwPt']; 23141 $this->fhPt = $gvars['fhPt']; 23142 break; 23143 } 23144 } 23145 $this->endlinex = $this->img_rb_x; 23146 // restore page break 23147 $this->SetAutoPageBreak($page_break_mode, $page_break_margin); 23148 $this->cell_padding = $cell_padding; 23149 } 23150 23151 /** 23152 * Convert SVG transformation matrix to PDF. 23153 * @param $tm (array) original SVG transformation matrix 23154 * @return array transformation matrix 23155 * @protected 23156 * @since 5.0.000 (2010-05-02) 23157 */ 23158 protected function convertSVGtMatrix($tm) { 23159 $a = $tm[0]; 23160 $b = -$tm[1]; 23161 $c = -$tm[2]; 23162 $d = $tm[3]; 23163 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k; 23164 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k; 23165 $x = 0; 23166 $y = $this->h * $this->k; 23167 $e = ($x * (1 - $a)) - ($y * $c) + $e; 23168 $f = ($y * (1 - $d)) - ($x * $b) + $f; 23169 return array($a, $b, $c, $d, $e, $f); 23170 } 23171 23172 /** 23173 * Apply SVG graphic transformation matrix. 23174 * @param $tm (array) original SVG transformation matrix 23175 * @protected 23176 * @since 5.0.000 (2010-05-02) 23177 */ 23178 protected function SVGTransform($tm) { 23179 $this->Transform($this->convertSVGtMatrix($tm)); 23180 } 23181 23182 /** 23183 * Apply the requested SVG styles (*** TO BE COMPLETED ***) 23184 * @param $svgstyle (array) array of SVG styles to apply 23185 * @param $prevsvgstyle (array) array of previous SVG style 23186 * @param $x (int) X origin of the bounding box 23187 * @param $y (int) Y origin of the bounding box 23188 * @param $w (int) width of the bounding box 23189 * @param $h (int) height of the bounding box 23190 * @param $clip_function (string) clip function 23191 * @param $clip_params (array) array of parameters for clipping function 23192 * @return object style 23193 * @author Nicola Asuni 23194 * @since 5.0.000 (2010-05-02) 23195 * @protected 23196 */ 23197 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) { 23198 if ($this->state != 2) { 23199 return; 23200 } 23201 $objstyle = ''; 23202 $minlen = (0.01 / $this->k); // minimum acceptable length 23203 if (!isset($svgstyle['opacity'])) { 23204 return $objstyle; 23205 } 23206 // clip-path 23207 $regs = array(); 23208 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) { 23209 $clip_path = $this->svgclippaths[$regs[1]]; 23210 foreach ($clip_path as $cp) { 23211 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']); 23212 } 23213 } 23214 // opacity 23215 if ($svgstyle['opacity'] != 1) { 23216 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false); 23217 } 23218 // color 23219 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors); 23220 $this->SetFillColorArray($fill_color); 23221 // text color 23222 $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors); 23223 $this->SetTextColorArray($text_color); 23224 // clip 23225 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) { 23226 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0); 23227 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0); 23228 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0); 23229 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0); 23230 $cx = $x + $left; 23231 $cy = $y + $top; 23232 $cw = $w - $left - $right; 23233 $ch = $h - $top - $bottom; 23234 if ($svgstyle['clip-rule'] == 'evenodd') { 23235 $clip_rule = 'CNZ'; 23236 } else { 23237 $clip_rule = 'CEO'; 23238 } 23239 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array()); 23240 } 23241 // fill 23242 $regs = array(); 23243 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) { 23244 // gradient 23245 $gradient = $this->svggradients[$regs[1]]; 23246 if (isset($gradient['xref'])) { 23247 // reference to another gradient definition 23248 $newgradient = $this->svggradients[$gradient['xref']]; 23249 $newgradient['coords'] = $gradient['coords']; 23250 $newgradient['mode'] = $gradient['mode']; 23251 $newgradient['type'] = $gradient['type']; 23252 $newgradient['gradientUnits'] = $gradient['gradientUnits']; 23253 if (isset($gradient['gradientTransform'])) { 23254 $newgradient['gradientTransform'] = $gradient['gradientTransform']; 23255 } 23256 $gradient = $newgradient; 23257 } 23258 //save current Graphic State 23259 $this->_outSaveGraphicsState(); 23260 //set clipping area 23261 if (!empty($clip_function) AND method_exists($this, $clip_function)) { 23262 $bbox = call_user_func_array(array($this, $clip_function), $clip_params); 23263 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) { 23264 list($x, $y, $w, $h) = $bbox; 23265 } 23266 } 23267 if ($gradient['mode'] == 'measure') { 23268 if (!isset($gradient['coords'][4])) { 23269 $gradient['coords'][4] = 0.5; 23270 } 23271 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) { 23272 $gtm = $gradient['gradientTransform']; 23273 // apply transformation matrix 23274 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4]; 23275 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5]; 23276 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4]; 23277 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5]; 23278 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2)); 23279 $gradient['coords'][0] = $xa; 23280 $gradient['coords'][1] = $ya; 23281 $gradient['coords'][2] = $xb; 23282 $gradient['coords'][3] = $yb; 23283 $gradient['coords'][4] = $r; 23284 } 23285 // convert SVG coordinates to user units 23286 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false); 23287 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false); 23288 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false); 23289 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false); 23290 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false); 23291 if ($w <= $minlen) { 23292 $w = $minlen; 23293 } 23294 if ($h <= $minlen) { 23295 $h = $minlen; 23296 } 23297 // shift units 23298 if ($gradient['gradientUnits'] == 'objectBoundingBox') { 23299 // convert to SVG coordinate system 23300 $gradient['coords'][0] += $x; 23301 $gradient['coords'][1] += $y; 23302 $gradient['coords'][2] += $x; 23303 $gradient['coords'][3] += $y; 23304 } 23305 // calculate percentages 23306 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w); 23307 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h); 23308 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w); 23309 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h); 23310 $gradient['coords'][4] /= $w; 23311 } elseif ($gradient['mode'] == 'percentage') { 23312 foreach($gradient['coords'] as $key => $val) { 23313 $gradient['coords'][$key] = (intval($val) / 100); 23314 if ($val < 0) { 23315 $gradient['coords'][$key] = 0; 23316 } elseif ($val > 1) { 23317 $gradient['coords'][$key] = 1; 23318 } 23319 } 23320 } 23321 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) { 23322 // single color (no shading) 23323 $gradient['coords'][0] = 1; 23324 $gradient['coords'][1] = 0; 23325 $gradient['coords'][2] = 0.999; 23326 $gradient['coords'][3] = 0; 23327 } 23328 // swap Y coordinates 23329 $tmp = $gradient['coords'][1]; 23330 $gradient['coords'][1] = $gradient['coords'][3]; 23331 $gradient['coords'][3] = $tmp; 23332 // set transformation map for gradient 23333 $cy = ($this->h - $y); 23334 if ($gradient['type'] == 3) { 23335 // circular gradient 23336 $cy -= ($gradient['coords'][1] * ($w + $h)); 23337 $h = $w = max($w, $h); 23338 } else { 23339 $cy -= $h; 23340 } 23341 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k))); 23342 if (count($gradient['stops']) > 1) { 23343 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false); 23344 } 23345 } elseif ($svgstyle['fill'] != 'none') { 23346 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors); 23347 if ($svgstyle['fill-opacity'] != 1) { 23348 $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false); 23349 } 23350 $this->SetFillColorArray($fill_color); 23351 if ($svgstyle['fill-rule'] == 'evenodd') { 23352 $objstyle .= 'F*'; 23353 } else { 23354 $objstyle .= 'F'; 23355 } 23356 } 23357 // stroke 23358 if ($svgstyle['stroke'] != 'none') { 23359 if ($svgstyle['stroke-opacity'] != 1) { 23360 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false); 23361 } elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) { 23362 $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false); 23363 } 23364 $stroke_style = array( 23365 'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors), 23366 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false), 23367 'cap' => $svgstyle['stroke-linecap'], 23368 'join' => $svgstyle['stroke-linejoin'] 23369 ); 23370 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) { 23371 $stroke_style['dash'] = $svgstyle['stroke-dasharray']; 23372 } 23373 $this->SetLineStyle($stroke_style); 23374 $objstyle .= 'D'; 23375 } 23376 // font 23377 $regs = array(); 23378 if (!empty($svgstyle['font'])) { 23379 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) { 23380 $font_family = $this->getFontFamilyName($regs[1]); 23381 } else { 23382 $font_family = $svgstyle['font-family']; 23383 } 23384 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23385 $font_size = trim($regs[1]); 23386 } else { 23387 $font_size = $svgstyle['font-size']; 23388 } 23389 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23390 $font_style = trim($regs[1]); 23391 } else { 23392 $font_style = $svgstyle['font-style']; 23393 } 23394 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23395 $font_weight = trim($regs[1]); 23396 } else { 23397 $font_weight = $svgstyle['font-weight']; 23398 } 23399 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23400 $font_stretch = trim($regs[1]); 23401 } else { 23402 $font_stretch = $svgstyle['font-stretch']; 23403 } 23404 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 23405 $font_spacing = trim($regs[1]); 23406 } else { 23407 $font_spacing = $svgstyle['letter-spacing']; 23408 } 23409 } else { 23410 $font_family = $this->getFontFamilyName($svgstyle['font-family']); 23411 $font_size = $svgstyle['font-size']; 23412 $font_style = $svgstyle['font-style']; 23413 $font_weight = $svgstyle['font-weight']; 23414 $font_stretch = $svgstyle['font-stretch']; 23415 $font_spacing = $svgstyle['letter-spacing']; 23416 } 23417 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit); 23418 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']); 23419 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']); 23420 switch ($font_style) { 23421 case 'italic': { 23422 $font_style = 'I'; 23423 break; 23424 } 23425 case 'oblique': { 23426 $font_style = 'I'; 23427 break; 23428 } 23429 default: 23430 case 'normal': { 23431 $font_style = ''; 23432 break; 23433 } 23434 } 23435 switch ($font_weight) { 23436 case 'bold': 23437 case 'bolder': { 23438 $font_style .= 'B'; 23439 break; 23440 } 23441 case 'normal': { 23442 if ((substr($font_family, -1) == 'I') AND (substr($font_family, -2, 1) == 'B')) { 23443 $font_family = substr($font_family, 0, -2).'I'; 23444 } elseif (substr($font_family, -1) == 'B') { 23445 $font_family = substr($font_family, 0, -1); 23446 } 23447 break; 23448 } 23449 } 23450 switch ($svgstyle['text-decoration']) { 23451 case 'underline': { 23452 $font_style .= 'U'; 23453 break; 23454 } 23455 case 'overline': { 23456 $font_style .= 'O'; 23457 break; 23458 } 23459 case 'line-through': { 23460 $font_style .= 'D'; 23461 break; 23462 } 23463 default: 23464 case 'none': { 23465 break; 23466 } 23467 } 23468 $this->SetFont($font_family, $font_style, $font_size); 23469 $this->setFontStretching($font_stretch); 23470 $this->setFontSpacing($font_spacing); 23471 return $objstyle; 23472 } 23473 23474 /** 23475 * Draws an SVG path 23476 * @param $d (string) attribute d of the path SVG element 23477 * @param $style (string) Style of rendering. Possible values are: 23478 * <ul> 23479 * <li>D or empty string: Draw (default).</li> 23480 * <li>F: Fill.</li> 23481 * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li> 23482 * <li>DF or FD: Draw and fill.</li> 23483 * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li> 23484 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 23485 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 23486 * </ul> 23487 * @return array of container box measures (x, y, w, h) 23488 * @author Nicola Asuni 23489 * @since 5.0.000 (2010-05-02) 23490 * @protected 23491 */ 23492 protected function SVGPath($d, $style='') { 23493 if ($this->state != 2) { 23494 return; 23495 } 23496 // set fill/stroke style 23497 $op = TCPDF_STATIC::getPathPaintOperator($style, ''); 23498 if (empty($op)) { 23499 return; 23500 } 23501 $paths = array(); 23502 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d); 23503 $d = preg_replace('/(\.[0-9]+)(\.)/s', '\\1 \\2', $d); 23504 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER); 23505 $x = 0; 23506 $y = 0; 23507 $x1 = 0; 23508 $y1 = 0; 23509 $x2 = 0; 23510 $y2 = 0; 23511 $xmin = 2147483647; 23512 $xmax = 0; 23513 $ymin = 2147483647; 23514 $ymax = 0; 23515 $xinitial = 0; 23516 $yinitial = 0; 23517 $relcoord = false; 23518 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point) 23519 $firstcmd = true; // used to print first point 23520 // draw curve pieces 23521 foreach ($paths as $key => $val) { 23522 // get curve type 23523 $cmd = trim($val[1]); 23524 if (strtolower($cmd) == $cmd) { 23525 // use relative coordinated instead of absolute 23526 $relcoord = true; 23527 $xoffset = $x; 23528 $yoffset = $y; 23529 } else { 23530 $relcoord = false; 23531 $xoffset = 0; 23532 $yoffset = 0; 23533 } 23534 $params = array(); 23535 if (isset($val[2])) { 23536 // get curve parameters 23537 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2])); 23538 $params = array(); 23539 foreach ($rawparams as $ck => $cp) { 23540 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false); 23541 if (abs($params[$ck]) < $minlen) { 23542 // approximate little values to zero 23543 $params[$ck] = 0; 23544 } 23545 } 23546 } 23547 // store current origin point 23548 $x0 = $x; 23549 $y0 = $y; 23550 switch (strtoupper($cmd)) { 23551 case 'M': { // moveto 23552 foreach ($params as $ck => $cp) { 23553 if (($ck % 2) == 0) { 23554 $x = $cp + $xoffset; 23555 } else { 23556 $y = $cp + $yoffset; 23557 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23558 if ($ck == 1) { 23559 $this->_outPoint($x, $y); 23560 $firstcmd = false; 23561 $xinitial = $x; 23562 $yinitial = $y; 23563 } else { 23564 $this->_outLine($x, $y); 23565 } 23566 $x0 = $x; 23567 $y0 = $y; 23568 } 23569 $xmin = min($xmin, $x); 23570 $ymin = min($ymin, $y); 23571 $xmax = max($xmax, $x); 23572 $ymax = max($ymax, $y); 23573 if ($relcoord) { 23574 $xoffset = $x; 23575 $yoffset = $y; 23576 } 23577 } 23578 } 23579 break; 23580 } 23581 case 'L': { // lineto 23582 foreach ($params as $ck => $cp) { 23583 if (($ck % 2) == 0) { 23584 $x = $cp + $xoffset; 23585 } else { 23586 $y = $cp + $yoffset; 23587 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23588 $this->_outLine($x, $y); 23589 $x0 = $x; 23590 $y0 = $y; 23591 } 23592 $xmin = min($xmin, $x); 23593 $ymin = min($ymin, $y); 23594 $xmax = max($xmax, $x); 23595 $ymax = max($ymax, $y); 23596 if ($relcoord) { 23597 $xoffset = $x; 23598 $yoffset = $y; 23599 } 23600 } 23601 } 23602 break; 23603 } 23604 case 'H': { // horizontal lineto 23605 foreach ($params as $ck => $cp) { 23606 $x = $cp + $xoffset; 23607 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23608 $this->_outLine($x, $y); 23609 $x0 = $x; 23610 $y0 = $y; 23611 } 23612 $xmin = min($xmin, $x); 23613 $xmax = max($xmax, $x); 23614 if ($relcoord) { 23615 $xoffset = $x; 23616 } 23617 } 23618 break; 23619 } 23620 case 'V': { // vertical lineto 23621 foreach ($params as $ck => $cp) { 23622 $y = $cp + $yoffset; 23623 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 23624 $this->_outLine($x, $y); 23625 $x0 = $x; 23626 $y0 = $y; 23627 } 23628 $ymin = min($ymin, $y); 23629 $ymax = max($ymax, $y); 23630 if ($relcoord) { 23631 $yoffset = $y; 23632 } 23633 } 23634 break; 23635 } 23636 case 'C': { // curveto 23637 foreach ($params as $ck => $cp) { 23638 $params[$ck] = $cp; 23639 if ((($ck + 1) % 6) == 0) { 23640 $x1 = $params[($ck - 5)] + $xoffset; 23641 $y1 = $params[($ck - 4)] + $yoffset; 23642 $x2 = $params[($ck - 3)] + $xoffset; 23643 $y2 = $params[($ck - 2)] + $yoffset; 23644 $x = $params[($ck - 1)] + $xoffset; 23645 $y = $params[($ck)] + $yoffset; 23646 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 23647 $xmin = min($xmin, $x, $x1, $x2); 23648 $ymin = min($ymin, $y, $y1, $y2); 23649 $xmax = max($xmax, $x, $x1, $x2); 23650 $ymax = max($ymax, $y, $y1, $y2); 23651 if ($relcoord) { 23652 $xoffset = $x; 23653 $yoffset = $y; 23654 } 23655 } 23656 } 23657 break; 23658 } 23659 case 'S': { // shorthand/smooth curveto 23660 foreach ($params as $ck => $cp) { 23661 $params[$ck] = $cp; 23662 if ((($ck + 1) % 4) == 0) { 23663 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) { 23664 $x1 = (2 * $x) - $x2; 23665 $y1 = (2 * $y) - $y2; 23666 } else { 23667 $x1 = $x; 23668 $y1 = $y; 23669 } 23670 $x2 = $params[($ck - 3)] + $xoffset; 23671 $y2 = $params[($ck - 2)] + $yoffset; 23672 $x = $params[($ck - 1)] + $xoffset; 23673 $y = $params[($ck)] + $yoffset; 23674 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 23675 $xmin = min($xmin, $x, $x1, $x2); 23676 $ymin = min($ymin, $y, $y1, $y2); 23677 $xmax = max($xmax, $x, $x1, $x2); 23678 $ymax = max($ymax, $y, $y1, $y2); 23679 if ($relcoord) { 23680 $xoffset = $x; 23681 $yoffset = $y; 23682 } 23683 } 23684 } 23685 break; 23686 } 23687 case 'Q': { // quadratic Bezier curveto 23688 foreach ($params as $ck => $cp) { 23689 $params[$ck] = $cp; 23690 if ((($ck + 1) % 4) == 0) { 23691 // convert quadratic points to cubic points 23692 $x1 = $params[($ck - 3)] + $xoffset; 23693 $y1 = $params[($ck - 2)] + $yoffset; 23694 $xa = ($x + (2 * $x1)) / 3; 23695 $ya = ($y + (2 * $y1)) / 3; 23696 $x = $params[($ck - 1)] + $xoffset; 23697 $y = $params[($ck)] + $yoffset; 23698 $xb = ($x + (2 * $x1)) / 3; 23699 $yb = ($y + (2 * $y1)) / 3; 23700 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 23701 $xmin = min($xmin, $x, $xa, $xb); 23702 $ymin = min($ymin, $y, $ya, $yb); 23703 $xmax = max($xmax, $x, $xa, $xb); 23704 $ymax = max($ymax, $y, $ya, $yb); 23705 if ($relcoord) { 23706 $xoffset = $x; 23707 $yoffset = $y; 23708 } 23709 } 23710 } 23711 break; 23712 } 23713 case 'T': { // shorthand/smooth quadratic Bezier curveto 23714 foreach ($params as $ck => $cp) { 23715 $params[$ck] = $cp; 23716 if (($ck % 2) != 0) { 23717 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) { 23718 $x1 = (2 * $x) - $x1; 23719 $y1 = (2 * $y) - $y1; 23720 } else { 23721 $x1 = $x; 23722 $y1 = $y; 23723 } 23724 // convert quadratic points to cubic points 23725 $xa = ($x + (2 * $x1)) / 3; 23726 $ya = ($y + (2 * $y1)) / 3; 23727 $x = $params[($ck - 1)] + $xoffset; 23728 $y = $params[($ck)] + $yoffset; 23729 $xb = ($x + (2 * $x1)) / 3; 23730 $yb = ($y + (2 * $y1)) / 3; 23731 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 23732 $xmin = min($xmin, $x, $xa, $xb); 23733 $ymin = min($ymin, $y, $ya, $yb); 23734 $xmax = max($xmax, $x, $xa, $xb); 23735 $ymax = max($ymax, $y, $ya, $yb); 23736 if ($relcoord) { 23737 $xoffset = $x; 23738 $yoffset = $y; 23739 } 23740 } 23741 } 23742 break; 23743 } 23744 case 'A': { // elliptical arc 23745 foreach ($params as $ck => $cp) { 23746 $params[$ck] = $cp; 23747 if ((($ck + 1) % 7) == 0) { 23748 $x0 = $x; 23749 $y0 = $y; 23750 $rx = max(abs($params[($ck - 6)]), .000000001); 23751 $ry = max(abs($params[($ck - 5)]), .000000001); 23752 $ang = -$rawparams[($ck - 4)]; 23753 $angle = deg2rad($ang); 23754 $fa = $rawparams[($ck - 3)]; // large-arc-flag 23755 $fs = $rawparams[($ck - 2)]; // sweep-flag 23756 $x = $params[($ck - 1)] + $xoffset; 23757 $y = $params[$ck] + $yoffset; 23758 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) { 23759 // endpoints are almost identical 23760 $xmin = min($xmin, $x); 23761 $ymin = min($ymin, $y); 23762 $xmax = max($xmax, $x); 23763 $ymax = max($ymax, $y); 23764 } else { 23765 $cos_ang = cos($angle); 23766 $sin_ang = sin($angle); 23767 $a = (($x0 - $x) / 2); 23768 $b = (($y0 - $y) / 2); 23769 $xa = ($a * $cos_ang) - ($b * $sin_ang); 23770 $ya = ($a * $sin_ang) + ($b * $cos_ang); 23771 $rx2 = $rx * $rx; 23772 $ry2 = $ry * $ry; 23773 $xa2 = $xa * $xa; 23774 $ya2 = $ya * $ya; 23775 $delta = ($xa2 / $rx2) + ($ya2 / $ry2); 23776 if ($delta > 1) { 23777 $rx *= sqrt($delta); 23778 $ry *= sqrt($delta); 23779 $rx2 = $rx * $rx; 23780 $ry2 = $ry * $ry; 23781 } 23782 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2)); 23783 if ($numerator < 0) { 23784 $root = 0; 23785 } else { 23786 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2))); 23787 } 23788 if ($fa == $fs){ 23789 $root *= -1; 23790 } 23791 $cax = $root * (($rx * $ya) / $ry); 23792 $cay = -$root * (($ry * $xa) / $rx); 23793 // coordinates of ellipse center 23794 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2); 23795 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2); 23796 // get angles 23797 $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry)); 23798 $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry)); 23799 if (($fs == 0) AND ($dang > 0)) { 23800 $dang -= (2 * M_PI); 23801 } elseif (($fs == 1) AND ($dang < 0)) { 23802 $dang += (2 * M_PI); 23803 } 23804 $angf = $angs - $dang; 23805 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) { 23806 // reverse angles 23807 $tmp = $angs; 23808 $angs = $angf; 23809 $angf = $tmp; 23810 } 23811 $angs = round(rad2deg($angs), 6); 23812 $angf = round(rad2deg($angf), 6); 23813 // covent angles to positive values 23814 if (($angs < 0) AND ($angf < 0)) { 23815 $angs += 360; 23816 $angf += 360; 23817 } 23818 $pie = false; 23819 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) { 23820 $pie = true; 23821 } 23822 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true); 23823 $xmin = min($xmin, $x, $axmin); 23824 $ymin = min($ymin, $y, $aymin); 23825 $xmax = max($xmax, $x, $axmax); 23826 $ymax = max($ymax, $y, $aymax); 23827 } 23828 if ($relcoord) { 23829 $xoffset = $x; 23830 $yoffset = $y; 23831 } 23832 } 23833 } 23834 break; 23835 } 23836 case 'Z': { 23837 $this->_out('h'); 23838 $x = $x0 = $xinitial; 23839 $y = $y0 = $yinitial; 23840 break; 23841 } 23842 } 23843 $firstcmd = false; 23844 } // end foreach 23845 if (!empty($op)) { 23846 $this->_out($op); 23847 } 23848 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin)); 23849 } 23850 23851 /** 23852 * Return the tag name without the namespace 23853 * @param $name (string) Tag name 23854 * @protected 23855 */ 23856 protected function removeTagNamespace($name) { 23857 if(strpos($name, ':') !== false) { 23858 $parts = explode(':', $name); 23859 return $parts[(sizeof($parts) - 1)]; 23860 } 23861 return $name; 23862 } 23863 23864 /** 23865 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***) 23866 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 23867 * @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. 23868 * @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. 23869 * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix). 23870 * @author Nicola Asuni 23871 * @since 5.0.000 (2010-05-02) 23872 * @protected 23873 */ 23874 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) { 23875 $name = $this->removeTagNamespace($name); 23876 // check if we are in clip mode 23877 if ($this->svgclipmode) { 23878 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]); 23879 return; 23880 } 23881 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) { 23882 if (isset($attribs['id'])) { 23883 $attribs['child_elements'] = array(); 23884 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs); 23885 return; 23886 } 23887 if (end($this->svgdefs) !== FALSE) { 23888 $last_svgdefs_id = key($this->svgdefs); 23889 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) { 23890 $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1); 23891 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs); 23892 return; 23893 } 23894 } 23895 return; 23896 } 23897 $clipping = false; 23898 if ($parser == 'clip-path') { 23899 // set clipping mode 23900 $clipping = true; 23901 } 23902 // get styling properties 23903 $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style 23904 $svgstyle = $this->svgstyles[0]; // set default style 23905 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) { 23906 // default fill attribute for clipping 23907 $attribs['fill'] = 'none'; 23908 } 23909 if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) { 23910 // fix style for regular expression 23911 $attribs['style'] = ';'.$attribs['style']; 23912 } 23913 foreach ($prev_svgstyle as $key => $val) { 23914 if (in_array($key, TCPDF_IMAGES::$svginheritprop)) { 23915 // inherit previous value 23916 $svgstyle[$key] = $val; 23917 } 23918 if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) { 23919 // specific attribute settings 23920 if ($attribs[$key] == 'inherit') { 23921 $svgstyle[$key] = $val; 23922 } else { 23923 $svgstyle[$key] = $attribs[$key]; 23924 } 23925 } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) { 23926 // CSS style syntax 23927 $attrval = array(); 23928 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) { 23929 if ($attrval[1] == 'inherit') { 23930 $svgstyle[$key] = $val; 23931 } else { 23932 $svgstyle[$key] = $attrval[1]; 23933 } 23934 } 23935 } 23936 } 23937 // transformation matrix 23938 if (!empty($ctm)) { 23939 $tm = $ctm; 23940 } else { 23941 $tm = array(1,0,0,1,0,0); 23942 } 23943 if (isset($attribs['transform']) AND !empty($attribs['transform'])) { 23944 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform'])); 23945 } 23946 $svgstyle['transfmatrix'] = $tm; 23947 $invisible = false; 23948 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) { 23949 // the current graphics element is invisible (nothing is painted) 23950 $invisible = true; 23951 } 23952 // process tag 23953 switch($name) { 23954 case 'defs': { 23955 $this->svgdefsmode = true; 23956 break; 23957 } 23958 // clipPath 23959 case 'clipPath': { 23960 if ($invisible) { 23961 break; 23962 } 23963 $this->svgclipmode = true; 23964 if (!isset($attribs['id'])) { 23965 $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1); 23966 } 23967 $this->svgclipid = $attribs['id']; 23968 $this->svgclippaths[$this->svgclipid] = array(); 23969 $this->svgcliptm[$this->svgclipid] = $tm; 23970 break; 23971 } 23972 case 'svg': { 23973 // start of SVG object 23974 if(++$this->svg_tag_depth <= 1) { 23975 break; 23976 } 23977 // inner SVG 23978 array_push($this->svgstyles, $svgstyle); 23979 $this->StartTransform(); 23980 $svgX = (isset($attribs['x'])?$attribs['x']:0); 23981 $svgY = (isset($attribs['y'])?$attribs['y']:0); 23982 $svgW = (isset($attribs['width'])?$attribs['width']:0); 23983 $svgH = (isset($attribs['height'])?$attribs['height']:0); 23984 // set x, y position using transform matrix 23985 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY)); 23986 $this->SVGTransform($tm); 23987 // set clipping for width and height 23988 $x = 0; 23989 $y = 0; 23990 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w); 23991 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h); 23992 // draw clipping rect 23993 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array()); 23994 // parse viewbox, calculate extra transformation matrix 23995 if (isset($attribs['viewBox'])) { 23996 $tmp = array(); 23997 preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp); 23998 $tmp = $tmp[0]; 23999 if (sizeof($tmp) == 4) { 24000 $vx = $tmp[0]; 24001 $vy = $tmp[1]; 24002 $vw = $tmp[2]; 24003 $vh = $tmp[3]; 24004 // get aspect ratio 24005 $tmp = array(); 24006 $aspectX = 'xMid'; 24007 $aspectY = 'YMid'; 24008 $fit = 'meet'; 24009 if (isset($attribs['preserveAspectRatio'])) { 24010 if($attribs['preserveAspectRatio'] == 'none') { 24011 $fit = 'none'; 24012 } else { 24013 preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp); 24014 $tmp = $tmp[0]; 24015 if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) { 24016 $aspectX = substr($tmp[0], 0, 4); 24017 $aspectY = substr($tmp[0], 4, 4); 24018 $fit = $tmp[1]; 24019 } 24020 } 24021 } 24022 $wr = ($svgW / $vw); 24023 $hr = ($svgH / $vh); 24024 $ax = $ay = 0; 24025 if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) { 24026 if ($aspectX == 'xMax') { 24027 $ax = (($vw * ($wr / $hr)) - $vw); 24028 } 24029 if ($aspectX == 'xMid') { 24030 $ax = ((($vw * ($wr / $hr)) - $vw) / 2); 24031 } 24032 $wr = $hr; 24033 } elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) { 24034 if ($aspectY == 'YMax') { 24035 $ay = (($vh * ($hr / $wr)) - $vh); 24036 } 24037 if ($aspectY == 'YMid') { 24038 $ay = ((($vh * ($hr / $wr)) - $vh) / 2); 24039 } 24040 $hr = $wr; 24041 } 24042 $newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY)); 24043 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, $newtm); 24044 $this->SVGTransform($tm); 24045 } 24046 } 24047 $this->setSVGStyles($svgstyle, $prev_svgstyle); 24048 break; 24049 } 24050 case 'g': { 24051 // group together related graphics elements 24052 array_push($this->svgstyles, $svgstyle); 24053 $this->StartTransform(); 24054 $x = (isset($attribs['x'])?$attribs['x']:0); 24055 $y = (isset($attribs['y'])?$attribs['y']:0); 24056 $w = 1;//(isset($attribs['width'])?$attribs['width']:1); 24057 $h = 1;//(isset($attribs['height'])?$attribs['height']:1); 24058 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y)); 24059 $this->SVGTransform($tm); 24060 $this->setSVGStyles($svgstyle, $prev_svgstyle); 24061 break; 24062 } 24063 case 'linearGradient': { 24064 if ($this->pdfa_mode) { 24065 break; 24066 } 24067 if (!isset($attribs['id'])) { 24068 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); 24069 } 24070 $this->svggradientid = $attribs['id']; 24071 $this->svggradients[$this->svggradientid] = array(); 24072 $this->svggradients[$this->svggradientid]['type'] = 2; 24073 $this->svggradients[$this->svggradientid]['stops'] = array(); 24074 if (isset($attribs['gradientUnits'])) { 24075 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 24076 } else { 24077 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 24078 } 24079 //$attribs['spreadMethod'] 24080 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2']))) 24081 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%')) 24082 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%')) 24083 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%')) 24084 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) { 24085 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 24086 } else { 24087 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 24088 } 24089 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0'); 24090 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0'); 24091 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100'); 24092 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0'); 24093 if (isset($attribs['gradientTransform'])) { 24094 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); 24095 } 24096 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2); 24097 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 24098 // gradient is defined on another place 24099 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 24100 } 24101 break; 24102 } 24103 case 'radialGradient': { 24104 if ($this->pdfa_mode) { 24105 break; 24106 } 24107 if (!isset($attribs['id'])) { 24108 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); 24109 } 24110 $this->svggradientid = $attribs['id']; 24111 $this->svggradients[$this->svggradientid] = array(); 24112 $this->svggradients[$this->svggradientid]['type'] = 3; 24113 $this->svggradients[$this->svggradientid]['stops'] = array(); 24114 if (isset($attribs['gradientUnits'])) { 24115 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 24116 } else { 24117 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 24118 } 24119 //$attribs['spreadMethod'] 24120 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy']))) 24121 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%')) 24122 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) { 24123 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 24124 } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) { 24125 $this->svggradients[$this->svggradientid]['mode'] = 'ratio'; 24126 } else { 24127 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 24128 } 24129 $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5); 24130 $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5); 24131 $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx); 24132 $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy); 24133 $r = (isset($attribs['r']) ? $attribs['r'] : 0.5); 24134 if (isset($attribs['gradientTransform'])) { 24135 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); 24136 } 24137 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r); 24138 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 24139 // gradient is defined on another place 24140 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 24141 } 24142 break; 24143 } 24144 case 'stop': { 24145 // gradient stops 24146 if (substr($attribs['offset'], -1) == '%') { 24147 $offset = floatval(substr($attribs['offset'], 0, -1)) / 100; 24148 } else { 24149 $offset = floatval($attribs['offset']); 24150 if ($offset > 1) { 24151 $offset /= 100; 24152 } 24153 } 24154 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black'; 24155 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1; 24156 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity); 24157 break; 24158 } 24159 // paths 24160 case 'path': { 24161 if ($invisible) { 24162 break; 24163 } 24164 if (isset($attribs['d'])) { 24165 $d = trim($attribs['d']); 24166 if (!empty($d)) { 24167 $x = (isset($attribs['x'])?$attribs['x']:0); 24168 $y = (isset($attribs['y'])?$attribs['y']:0); 24169 $w = (isset($attribs['width'])?$attribs['width']:1); 24170 $h = (isset($attribs['height'])?$attribs['height']:1); 24171 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y)); 24172 if ($clipping) { 24173 $this->SVGTransform($tm); 24174 $this->SVGPath($d, 'CNZ'); 24175 } else { 24176 $this->StartTransform(); 24177 $this->SVGTransform($tm); 24178 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ')); 24179 if (!empty($obstyle)) { 24180 $this->SVGPath($d, $obstyle); 24181 } 24182 $this->StopTransform(); 24183 } 24184 } 24185 } 24186 break; 24187 } 24188 // shapes 24189 case 'rect': { 24190 if ($invisible) { 24191 break; 24192 } 24193 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 24194 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 24195 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 24196 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 24197 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); 24198 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx); 24199 if ($clipping) { 24200 $this->SVGTransform($tm); 24201 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array()); 24202 } else { 24203 $this->StartTransform(); 24204 $this->SVGTransform($tm); 24205 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ')); 24206 if (!empty($obstyle)) { 24207 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array()); 24208 } 24209 $this->StopTransform(); 24210 } 24211 break; 24212 } 24213 case 'circle': { 24214 if ($invisible) { 24215 break; 24216 } 24217 $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0); 24218 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); 24219 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); 24220 $x = ($cx - $r); 24221 $y = ($cy - $r); 24222 $w = (2 * $r); 24223 $h = $w; 24224 if ($clipping) { 24225 $this->SVGTransform($tm); 24226 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8); 24227 } else { 24228 $this->StartTransform(); 24229 $this->SVGTransform($tm); 24230 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ')); 24231 if (!empty($obstyle)) { 24232 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8); 24233 } 24234 $this->StopTransform(); 24235 } 24236 break; 24237 } 24238 case 'ellipse': { 24239 if ($invisible) { 24240 break; 24241 } 24242 $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0); 24243 $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0); 24244 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); 24245 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); 24246 $x = ($cx - $rx); 24247 $y = ($cy - $ry); 24248 $w = (2 * $rx); 24249 $h = (2 * $ry); 24250 if ($clipping) { 24251 $this->SVGTransform($tm); 24252 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); 24253 } else { 24254 $this->StartTransform(); 24255 $this->SVGTransform($tm); 24256 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ')); 24257 if (!empty($obstyle)) { 24258 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8); 24259 } 24260 $this->StopTransform(); 24261 } 24262 break; 24263 } 24264 case 'line': { 24265 if ($invisible) { 24266 break; 24267 } 24268 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0); 24269 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0); 24270 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0); 24271 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0); 24272 $x = $x1; 24273 $y = $y1; 24274 $w = abs($x2 - $x1); 24275 $h = abs($y2 - $y1); 24276 if (!$clipping) { 24277 $this->StartTransform(); 24278 $this->SVGTransform($tm); 24279 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2)); 24280 $this->Line($x1, $y1, $x2, $y2); 24281 $this->StopTransform(); 24282 } 24283 break; 24284 } 24285 case 'polyline': 24286 case 'polygon': { 24287 if ($invisible) { 24288 break; 24289 } 24290 $points = (isset($attribs['points'])?$attribs['points']:'0 0'); 24291 $points = trim($points); 24292 // note that point may use a complex syntax not covered here 24293 $points = preg_split('/[\,\s]+/si', $points); 24294 if (count($points) < 4) { 24295 break; 24296 } 24297 $p = array(); 24298 $xmin = 2147483647; 24299 $xmax = 0; 24300 $ymin = 2147483647; 24301 $ymax = 0; 24302 foreach ($points as $key => $val) { 24303 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 24304 if (($key % 2) == 0) { 24305 // X coordinate 24306 $xmin = min($xmin, $p[$key]); 24307 $xmax = max($xmax, $p[$key]); 24308 } else { 24309 // Y coordinate 24310 $ymin = min($ymin, $p[$key]); 24311 $ymax = max($ymax, $p[$key]); 24312 } 24313 } 24314 $x = $xmin; 24315 $y = $ymin; 24316 $w = ($xmax - $xmin); 24317 $h = ($ymax - $ymin); 24318 if ($name == 'polyline') { 24319 $this->StartTransform(); 24320 $this->SVGTransform($tm); 24321 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); 24322 if (!empty($obstyle)) { 24323 $this->PolyLine($p, $obstyle, array(), array()); 24324 } 24325 $this->StopTransform(); 24326 } else { // polygon 24327 if ($clipping) { 24328 $this->SVGTransform($tm); 24329 $this->Polygon($p, 'CNZ', array(), array(), true); 24330 } else { 24331 $this->StartTransform(); 24332 $this->SVGTransform($tm); 24333 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ')); 24334 if (!empty($obstyle)) { 24335 $this->Polygon($p, $obstyle, array(), array(), true); 24336 } 24337 $this->StopTransform(); 24338 } 24339 } 24340 break; 24341 } 24342 // image 24343 case 'image': { 24344 if ($invisible) { 24345 break; 24346 } 24347 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) { 24348 break; 24349 } 24350 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 24351 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 24352 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 24353 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 24354 $img = $attribs['xlink:href']; 24355 if (!$clipping) { 24356 $this->StartTransform(); 24357 $this->SVGTransform($tm); 24358 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h); 24359 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) { 24360 // embedded image encoded as base64 24361 $img = '@'.base64_decode(substr($img, strlen($m[0]))); 24362 } else { 24363 // fix image path 24364 if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) { 24365 // replace relative path with full server path 24366 $img = $this->svgdir.'/'.$img; 24367 } 24368 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { 24369 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']); 24370 if (($findroot === false) OR ($findroot > 1)) { 24371 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { 24372 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img; 24373 } else { 24374 $img = $_SERVER['DOCUMENT_ROOT'].$img; 24375 } 24376 } 24377 } 24378 $img = urldecode($img); 24379 $testscrtype = @parse_url($img); 24380 if (empty($testscrtype['query'])) { 24381 // convert URL to server path 24382 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img); 24383 } elseif (preg_match('|^https?://|', $img) !== 1) { 24384 // convert server path to URL 24385 $img = str_replace(K_PATH_MAIN, K_PATH_URL, $img); 24386 } 24387 } 24388 // get image type 24389 $imgtype = TCPDF_IMAGES::getImageFileType($img); 24390 if (($imgtype == 'eps') OR ($imgtype == 'ai')) { 24391 $this->ImageEps($img, $x, $y, $w, $h); 24392 } elseif ($imgtype == 'svg') { 24393 // store SVG vars 24394 $svggradients = $this->svggradients; 24395 $svggradientid = $this->svggradientid; 24396 $svgdefsmode = $this->svgdefsmode; 24397 $svgdefs = $this->svgdefs; 24398 $svgclipmode = $this->svgclipmode; 24399 $svgclippaths = $this->svgclippaths; 24400 $svgcliptm = $this->svgcliptm; 24401 $svgclipid = $this->svgclipid; 24402 $svgtext = $this->svgtext; 24403 $svgtextmode = $this->svgtextmode; 24404 $this->ImageSVG($img, $x, $y, $w, $h); 24405 // restore SVG vars 24406 $this->svggradients = $svggradients; 24407 $this->svggradientid = $svggradientid; 24408 $this->svgdefsmode = $svgdefsmode; 24409 $this->svgdefs = $svgdefs; 24410 $this->svgclipmode = $svgclipmode; 24411 $this->svgclippaths = $svgclippaths; 24412 $this->svgcliptm = $svgcliptm; 24413 $this->svgclipid = $svgclipid; 24414 $this->svgtext = $svgtext; 24415 $this->svgtextmode = $svgtextmode; 24416 } else { 24417 $this->Image($img, $x, $y, $w, $h); 24418 } 24419 $this->StopTransform(); 24420 } 24421 break; 24422 } 24423 // text 24424 case 'text': 24425 case 'tspan': { 24426 if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) { 24427 // @TODO: unsupported feature 24428 } 24429 // only basic support - advanced features must be implemented 24430 $this->svgtextmode['invisible'] = $invisible; 24431 if ($invisible) { 24432 break; 24433 } 24434 array_push($this->svgstyles, $svgstyle); 24435 if (isset($attribs['x'])) { 24436 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false); 24437 } elseif ($name == 'tspan') { 24438 $x = $this->x; 24439 } else { 24440 $x = 0; 24441 } 24442 if (isset($attribs['dx'])) { 24443 $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false); 24444 } 24445 if (isset($attribs['y'])) { 24446 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false); 24447 } elseif ($name == 'tspan') { 24448 $y = $this->y; 24449 } else { 24450 $y = 0; 24451 } 24452 if (isset($attribs['dy'])) { 24453 $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false); 24454 } 24455 $svgstyle['text-color'] = $svgstyle['fill']; 24456 $this->svgtext = ''; 24457 if (isset($svgstyle['text-anchor'])) { 24458 $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor']; 24459 } else { 24460 $this->svgtextmode['text-anchor'] = 'start'; 24461 } 24462 if (isset($svgstyle['direction'])) { 24463 if ($svgstyle['direction'] == 'rtl') { 24464 $this->svgtextmode['rtl'] = true; 24465 } else { 24466 $this->svgtextmode['rtl'] = false; 24467 } 24468 } else { 24469 $this->svgtextmode['rtl'] = false; 24470 } 24471 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) { 24472 $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false); 24473 } else { 24474 $this->svgtextmode['stroke'] = false; 24475 } 24476 $this->StartTransform(); 24477 $this->SVGTransform($tm); 24478 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1); 24479 $this->x = $x; 24480 $this->y = $y; 24481 break; 24482 } 24483 // use 24484 case 'use': { 24485 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 24486 $svgdefid = substr($attribs['xlink:href'], 1); 24487 if (isset($this->svgdefs[$svgdefid])) { 24488 $use = $this->svgdefs[$svgdefid]; 24489 if (isset($attribs['xlink:href'])) { 24490 unset($attribs['xlink:href']); 24491 } 24492 if (isset($attribs['id'])) { 24493 unset($attribs['id']); 24494 } 24495 if (isset($use['attribs']['x']) AND isset($attribs['x'])) { 24496 $attribs['x'] += $use['attribs']['x']; 24497 } 24498 if (isset($use['attribs']['y']) AND isset($attribs['y'])) { 24499 $attribs['y'] += $use['attribs']['y']; 24500 } 24501 if (empty($attribs['style'])) { 24502 $attribs['style'] = ''; 24503 } 24504 if (!empty($use['attribs']['style'])) { 24505 // merge styles 24506 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']); 24507 } 24508 $attribs = array_merge($use['attribs'], $attribs); 24509 $this->startSVGElementHandler($parser, $use['name'], $attribs); 24510 return; 24511 } 24512 } 24513 break; 24514 } 24515 default: { 24516 break; 24517 } 24518 } // end of switch 24519 // process child elements 24520 if (!empty($attribs['child_elements'])) { 24521 $child_elements = $attribs['child_elements']; 24522 unset($attribs['child_elements']); 24523 foreach($child_elements as $child_element) { 24524 if (empty($child_element['attribs']['closing_tag'])) { 24525 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']); 24526 } else { 24527 if (isset($child_element['attribs']['content'])) { 24528 $this->svgtext = $child_element['attribs']['content']; 24529 } 24530 $this->endSVGElementHandler('child-tag', $child_element['name']); 24531 } 24532 } 24533 } 24534 } 24535 24536 /** 24537 * Sets the closing SVG element handler function for the XML parser. 24538 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 24539 * @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. 24540 * @author Nicola Asuni 24541 * @since 5.0.000 (2010-05-02) 24542 * @protected 24543 */ 24544 protected function endSVGElementHandler($parser, $name) { 24545 $name = $this->removeTagNamespace($name); 24546 if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {; 24547 if (end($this->svgdefs) !== FALSE) { 24548 $last_svgdefs_id = key($this->svgdefs); 24549 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) { 24550 foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) { 24551 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) { 24552 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext)); 24553 return; 24554 } 24555 } 24556 if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) { 24557 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext)); 24558 return; 24559 } 24560 } 24561 } 24562 return; 24563 } 24564 switch($name) { 24565 case 'defs': { 24566 $this->svgdefsmode = false; 24567 break; 24568 } 24569 // clipPath 24570 case 'clipPath': { 24571 $this->svgclipmode = false; 24572 break; 24573 } 24574 case 'svg': { 24575 if (--$this->svg_tag_depth <= 0) { 24576 break; 24577 } 24578 } 24579 case 'g': { 24580 // ungroup: remove last style from array 24581 array_pop($this->svgstyles); 24582 $this->StopTransform(); 24583 break; 24584 } 24585 case 'text': 24586 case 'tspan': { 24587 if ($this->svgtextmode['invisible']) { 24588 // This implementation must be fixed to following the rule: 24589 // 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. 24590 break; 24591 } 24592 // print text 24593 $text = $this->svgtext; 24594 //$text = $this->stringTrim($text); 24595 $textlen = $this->GetStringWidth($text); 24596 if ($this->svgtextmode['text-anchor'] != 'start') { 24597 // check if string is RTL text 24598 if ($this->svgtextmode['text-anchor'] == 'end') { 24599 if ($this->svgtextmode['rtl']) { 24600 $this->x += $textlen; 24601 } else { 24602 $this->x -= $textlen; 24603 } 24604 } elseif ($this->svgtextmode['text-anchor'] == 'middle') { 24605 if ($this->svgtextmode['rtl']) { 24606 $this->x += ($textlen / 2); 24607 } else { 24608 $this->x -= ($textlen / 2); 24609 } 24610 } 24611 } 24612 $textrendermode = $this->textrendermode; 24613 $textstrokewidth = $this->textstrokewidth; 24614 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false); 24615 if ($name == 'text') { 24616 // store current coordinates 24617 $tmpx = $this->x; 24618 $tmpy = $this->y; 24619 } 24620 // print the text 24621 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T'); 24622 if ($name == 'text') { 24623 // restore coordinates 24624 $this->x = $tmpx; 24625 $this->y = $tmpy; 24626 } 24627 // restore previous rendering mode 24628 $this->textrendermode = $textrendermode; 24629 $this->textstrokewidth = $textstrokewidth; 24630 $this->svgtext = ''; 24631 $this->StopTransform(); 24632 if (!$this->svgdefsmode) { 24633 array_pop($this->svgstyles); 24634 } 24635 break; 24636 } 24637 default: { 24638 break; 24639 } 24640 } 24641 } 24642 24643 /** 24644 * Sets the character data handler function for the XML parser. 24645 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. 24646 * @param $data (string) The second parameter, data, contains the character data as a string. 24647 * @author Nicola Asuni 24648 * @since 5.0.000 (2010-05-02) 24649 * @protected 24650 */ 24651 protected function segSVGContentHandler($parser, $data) { 24652 $this->svgtext .= $data; 24653 } 24654 24655 // --- END SVG METHODS ----------------------------------------------------- 24656 24657 /** 24658 * Keeps files in memory, so it doesn't need to downloaded everytime in a loop 24659 * @param string $file 24660 * @return string 24661 */ 24662 protected function getCachedFileContents($file) 24663 { 24664 if (!isset($this->fileContentCache[$file])) { 24665 $this->fileContentCache[$file] = TCPDF_STATIC::fileGetContents($file); 24666 } 24667 return $this->fileContentCache[$file]; 24668 } 24669 24670 /** 24671 * Avoid multiple calls to an external server to see if a file exists 24672 * @param string $file 24673 * @return bool 24674 */ 24675 protected function fileExists($file) 24676 { 24677 if (isset($this->fileContentCache[$file]) || false !== $this->getImageBuffer($file)) { 24678 return true; 24679 } 24680 24681 return TCPDF_STATIC::file_exists($file); 24682 } 24683 24684} // END OF TCPDF CLASS 24685 24686//============================================================+ 24687// END OF FILE 24688//============================================================+ 24689