1<?php 2 3namespace Mpdf; 4 5use Mpdf\Config\ConfigVariables; 6use Mpdf\Config\FontVariables; 7use Mpdf\Conversion; 8use Mpdf\Css\Border; 9use Mpdf\Css\TextVars; 10use Mpdf\Log\Context as LogContext; 11use Mpdf\Fonts\MetricsGenerator; 12use Mpdf\Output\Destination; 13use Mpdf\QrCode; 14use Mpdf\Utils\Arrays; 15use Mpdf\Utils\NumericString; 16use Mpdf\Utils\UtfString; 17use Psr\Log\LoggerInterface; 18use Psr\Log\NullLogger; 19 20/** 21 * mPDF, PHP library generating PDF files from UTF-8 encoded HTML 22 * 23 * based on FPDF by Olivier Plathey 24 * and HTML2FPDF by Renato Coelho 25 * 26 * @license GPL-2.0 27 */ 28class Mpdf implements \Psr\Log\LoggerAwareInterface 29{ 30 31 use Strict; 32 use FpdiTrait; 33 34 const VERSION = '8.0.17'; 35 36 const SCALE = 72 / 25.4; 37 38 var $useFixedNormalLineHeight; // mPDF 6 39 var $useFixedTextBaseline; // mPDF 6 40 var $adjustFontDescLineheight; // mPDF 6 41 var $interpolateImages; // mPDF 6 42 var $defaultPagebreakType; // mPDF 6 pagebreaktype 43 var $indexUseSubentries; // mPDF 6 44 45 var $autoScriptToLang; // mPDF 6 46 var $baseScript; // mPDF 6 47 var $autoVietnamese; // mPDF 6 48 var $autoArabic; // mPDF 6 49 50 var $CJKforceend; 51 var $h2bookmarks; 52 var $h2toc; 53 var $decimal_align; 54 var $margBuffer; 55 var $splitTableBorderWidth; 56 57 var $bookmarkStyles; 58 var $useActiveForms; 59 60 var $repackageTTF; 61 var $allowCJKorphans; 62 var $allowCJKoverflow; 63 64 var $useKerning; 65 var $restrictColorSpace; 66 var $bleedMargin; 67 var $crossMarkMargin; 68 var $cropMarkMargin; 69 var $cropMarkLength; 70 var $nonPrintMargin; 71 72 var $PDFX; 73 var $PDFXauto; 74 75 var $PDFA; 76 var $PDFAversion = '1-B'; 77 var $PDFAauto; 78 var $ICCProfile; 79 80 var $printers_info; 81 var $iterationCounter; 82 var $smCapsScale; 83 var $smCapsStretch; 84 85 var $backupSubsFont; 86 var $backupSIPFont; 87 var $fonttrans; 88 var $debugfonts; 89 var $useAdobeCJK; 90 var $percentSubset; 91 var $maxTTFFilesize; 92 var $BMPonly; 93 94 var $tableMinSizePriority; 95 96 var $dpi; 97 var $watermarkImgAlphaBlend; 98 var $watermarkImgBehind; 99 var $justifyB4br; 100 var $packTableData; 101 var $pgsIns; 102 var $simpleTables; 103 var $enableImports; 104 105 var $debug; 106 107 var $setAutoTopMargin; 108 var $setAutoBottomMargin; 109 var $autoMarginPadding; 110 var $collapseBlockMargins; 111 var $falseBoldWeight; 112 var $normalLineheight; 113 var $incrementFPR1; 114 var $incrementFPR2; 115 var $incrementFPR3; 116 var $incrementFPR4; 117 118 var $SHYlang; 119 var $SHYleftmin; 120 var $SHYrightmin; 121 var $SHYcharmin; 122 var $SHYcharmax; 123 var $SHYlanguages; 124 125 // PageNumber Conditional Text 126 var $pagenumPrefix; 127 var $pagenumSuffix; 128 129 var $nbpgPrefix; 130 var $nbpgSuffix; 131 var $showImageErrors; 132 var $allow_output_buffering; 133 var $autoPadding; 134 var $tabSpaces; 135 var $autoLangToFont; 136 var $watermarkTextAlpha; 137 var $watermarkImageAlpha; 138 var $watermark_size; 139 var $watermark_pos; 140 var $annotSize; 141 var $annotMargin; 142 var $annotOpacity; 143 var $title2annots; 144 var $keepColumns; 145 var $keep_table_proportions; 146 var $ignore_table_widths; 147 var $ignore_table_percents; 148 var $list_number_suffix; 149 150 var $list_auto_mode; // mPDF 6 151 var $list_indent_first_level; // mPDF 6 152 var $list_indent_default; // mPDF 6 153 var $list_indent_default_mpdf; 154 var $list_marker_offset; // mPDF 6 155 var $list_symbol_size; 156 157 var $useSubstitutions; 158 var $CSSselectMedia; 159 160 var $forcePortraitHeaders; 161 var $forcePortraitMargins; 162 var $displayDefaultOrientation; 163 var $ignore_invalid_utf8; 164 var $allowedCSStags; 165 var $onlyCoreFonts; 166 var $allow_charset_conversion; 167 168 var $jSWord; 169 var $jSmaxChar; 170 var $jSmaxCharLast; 171 var $jSmaxWordLast; 172 173 var $max_colH_correction; 174 175 var $table_error_report; 176 var $table_error_report_param; 177 var $biDirectional; 178 var $text_input_as_HTML; 179 var $anchor2Bookmark; 180 var $shrink_tables_to_fit; 181 182 var $allow_html_optional_endtags; 183 184 var $img_dpi; 185 var $whitelistStreamWrappers; 186 187 var $defaultheaderfontsize; 188 var $defaultheaderfontstyle; 189 var $defaultheaderline; 190 var $defaultfooterfontsize; 191 var $defaultfooterfontstyle; 192 var $defaultfooterline; 193 var $header_line_spacing; 194 var $footer_line_spacing; 195 196 var $pregCJKchars; 197 var $pregRTLchars; 198 var $pregCURSchars; // mPDF 6 199 200 var $mirrorMargins; 201 var $watermarkText; 202 var $watermarkAngle; 203 var $watermarkImage; 204 var $showWatermarkText; 205 var $showWatermarkImage; 206 207 var $svgAutoFont; 208 var $svgClasses; 209 210 var $fontsizes; 211 212 var $defaultPageNumStyle; // mPDF 6 213 214 ////////////////////// 215 // INTERNAL VARIABLES 216 ////////////////////// 217 var $extrapagebreak; // mPDF 6 pagebreaktype 218 219 var $uniqstr; // mPDF 5.7.2 220 var $hasOC; 221 222 var $textvar; // mPDF 5.7.1 223 var $fontLanguageOverride; // mPDF 5.7.1 224 var $OTLtags; // mPDF 5.7.1 225 var $OTLdata; // mPDF 5.7.1 226 227 var $useDictionaryLBR; 228 var $useTibetanLBR; 229 230 var $writingToC; 231 var $layers; 232 var $layerDetails; 233 var $current_layer; 234 var $open_layer_pane; 235 var $decimal_offset; 236 var $inMeter; 237 238 var $CJKleading; 239 var $CJKfollowing; 240 var $CJKoverflow; 241 242 var $textshadow; 243 244 var $colsums; 245 var $spanborder; 246 var $spanborddet; 247 248 var $visibility; 249 250 var $kerning; 251 var $fixedlSpacing; 252 var $minwSpacing; 253 var $lSpacingCSS; 254 var $wSpacingCSS; 255 256 var $spotColorIDs; 257 var $SVGcolors; 258 var $spotColors; 259 var $defTextColor; 260 var $defDrawColor; 261 var $defFillColor; 262 263 var $tableBackgrounds; 264 var $inlineDisplayOff; 265 var $kt_y00; 266 var $kt_p00; 267 var $upperCase; 268 var $checkSIP; 269 var $checkSMP; 270 var $checkCJK; 271 272 var $watermarkImgAlpha; 273 var $PDFAXwarnings; 274 275 var $MetadataRoot; 276 var $OutputIntentRoot; 277 var $InfoRoot; 278 var $associatedFilesRoot; 279 280 var $pdf_version; 281 282 private $fontDir; 283 284 var $tempDir; 285 286 var $cacheCleanupInterval; 287 288 var $allowAnnotationFiles; 289 290 var $fontdata; 291 292 var $noImageFile; 293 var $lastblockbottommargin; 294 var $baselineC; 295 296 // mPDF 5.7.3 inline text-decoration parameters 297 var $baselineSup; 298 var $baselineSub; 299 var $baselineS; 300 var $baselineO; 301 302 var $subPos; 303 var $subArrMB; 304 var $ReqFontStyle; 305 var $tableClipPath; 306 307 var $fullImageHeight; 308 309 var $inFixedPosBlock; // Internal flag for position:fixed block 310 var $fixedPosBlock; // Buffer string for position:fixed block 311 var $fixedPosBlockDepth; 312 var $fixedPosBlockBBox; 313 var $fixedPosBlockSave; 314 var $maxPosL; 315 var $maxPosR; 316 var $loaded; 317 318 var $extraFontSubsets; 319 320 var $docTemplateStart; // Internal flag for page (page no. -1) that docTemplate starts on 321 322 var $time0; 323 324 var $hyphenationDictionaryFile; 325 326 var $spanbgcolorarray; 327 var $default_font; 328 var $headerbuffer; 329 var $lastblocklevelchange; 330 var $nestedtablejustfinished; 331 var $linebreakjustfinished; 332 var $cell_border_dominance_L; 333 var $cell_border_dominance_R; 334 var $cell_border_dominance_T; 335 var $cell_border_dominance_B; 336 var $table_keep_together; 337 var $plainCell_properties; 338 var $shrin_k1; 339 var $outerfilled; 340 341 var $blockContext; 342 var $floatDivs; 343 344 var $patterns; 345 var $pageBackgrounds; 346 347 var $bodyBackgroundGradient; 348 var $bodyBackgroundImage; 349 var $bodyBackgroundColor; 350 351 var $writingHTMLheader; // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block 352 var $writingHTMLfooter; 353 354 var $angle; 355 356 var $gradients; 357 358 var $kwt_Reference; 359 var $kwt_BMoutlines; 360 var $kwt_toc; 361 362 var $tbrot_BMoutlines; 363 var $tbrot_toc; 364 365 var $col_BMoutlines; 366 var $col_toc; 367 368 var $floatbuffer; 369 var $floatmargins; 370 371 var $bullet; 372 var $bulletarray; 373 374 var $currentLang; 375 var $default_lang; 376 377 var $default_available_fonts; 378 379 var $pageTemplate; 380 var $docTemplate; 381 var $docTemplateContinue; 382 var $docTemplateContinue2pages; 383 384 var $arabGlyphs; 385 var $arabHex; 386 var $persianGlyphs; 387 var $persianHex; 388 var $arabVowels; 389 var $arabPrevLink; 390 var $arabNextLink; 391 392 var $formobjects; // array of Form Objects for WMF 393 var $InlineProperties; 394 var $InlineAnnots; 395 var $InlineBDF; // mPDF 6 Bidirectional formatting 396 var $InlineBDFctr; // mPDF 6 397 398 var $ktAnnots; 399 var $tbrot_Annots; 400 var $kwt_Annots; 401 var $columnAnnots; 402 var $columnForms; 403 var $tbrotForms; 404 405 var $PageAnnots; 406 407 var $pageDim; // Keep track of page wxh for orientation changes - set in _beginpage, used in _putannots 408 409 var $breakpoints; 410 411 var $tableLevel; 412 var $tbctr; 413 var $innermostTableLevel; 414 var $saveTableCounter; 415 var $cellBorderBuffer; 416 417 var $saveHTMLFooter_height; 418 var $saveHTMLFooterE_height; 419 420 var $firstPageBoxHeader; 421 var $firstPageBoxHeaderEven; 422 var $firstPageBoxFooter; 423 var $firstPageBoxFooterEven; 424 425 var $page_box; 426 427 var $show_marks; // crop or cross marks 428 var $basepathIsLocal; 429 430 var $use_kwt; 431 var $kwt; 432 var $kwt_height; 433 var $kwt_y0; 434 var $kwt_x0; 435 var $kwt_buffer; 436 var $kwt_Links; 437 var $kwt_moved; 438 var $kwt_saved; 439 440 var $PageNumSubstitutions; 441 442 var $table_borders_separate; 443 var $base_table_properties; 444 var $borderstyles; 445 446 var $blockjustfinished; 447 448 var $orig_bMargin; 449 var $orig_tMargin; 450 var $orig_lMargin; 451 var $orig_rMargin; 452 var $orig_hMargin; 453 var $orig_fMargin; 454 455 var $pageHTMLheaders; 456 var $pageHTMLfooters; 457 458 var $saveHTMLHeader; 459 var $saveHTMLFooter; 460 461 var $HTMLheaderPageLinks; 462 var $HTMLheaderPageAnnots; 463 var $HTMLheaderPageForms; 464 465 // See Config\FontVariables for these next 5 values 466 var $available_unifonts; 467 var $sans_fonts; 468 var $serif_fonts; 469 var $mono_fonts; 470 var $defaultSubsFont; 471 472 // List of ALL available CJK fonts (incl. styles) (Adobe add-ons) hw removed 473 var $available_CJK_fonts; 474 475 var $HTMLHeader; 476 var $HTMLFooter; 477 var $HTMLHeaderE; 478 var $HTMLFooterE; 479 var $bufferoutput; 480 481 // CJK fonts 482 var $Big5_widths; 483 var $GB_widths; 484 var $SJIS_widths; 485 var $UHC_widths; 486 487 // SetProtection 488 var $encrypted; 489 490 var $enc_obj_id; // encryption object id 491 492 // Bookmark 493 var $BMoutlines; 494 var $OutlineRoot; 495 496 // INDEX 497 var $ColActive; 498 var $Reference; 499 var $CurrCol; 500 var $NbCol; 501 var $y0; // Top ordinate of columns 502 503 var $ColL; 504 var $ColWidth; 505 var $ColGap; 506 507 // COLUMNS 508 var $ColR; 509 var $ChangeColumn; 510 var $columnbuffer; 511 var $ColDetails; 512 var $columnLinks; 513 var $colvAlign; 514 515 // Substitutions 516 var $substitute; // Array of substitution strings e.g. <ttz>112</ttz> 517 var $entsearch; // Array of HTML entities (>ASCII 127) to substitute 518 var $entsubstitute; // Array of substitution decimal unicode for the Hi entities 519 520 // Default values if no style sheet offered (cf. http://www.w3.org/TR/CSS21/sample.html) 521 var $defaultCSS; 522 var $defaultCssFile; 523 524 var $lastoptionaltag; // Save current block item which HTML specifies optionsl endtag 525 var $pageoutput; 526 var $charset_in; 527 var $blk; 528 var $blklvl; 529 var $ColumnAdjust; 530 531 var $ws; // Word spacing 532 533 var $HREF; 534 var $pgwidth; 535 var $fontlist; 536 var $oldx; 537 var $oldy; 538 var $B; 539 var $I; 540 541 var $tdbegin; 542 var $table; 543 var $cell; 544 var $col; 545 var $row; 546 547 var $divbegin; 548 var $divwidth; 549 var $divheight; 550 var $spanbgcolor; 551 552 // mPDF 6 Used for table cell (block-type) properties 553 var $cellTextAlign; 554 var $cellLineHeight; 555 var $cellLineStackingStrategy; 556 var $cellLineStackingShift; 557 558 // mPDF 6 Lists 559 var $listcounter; 560 var $listlvl; 561 var $listtype; 562 var $listitem; 563 564 var $pjustfinished; 565 var $ignorefollowingspaces; 566 var $SMALL; 567 var $BIG; 568 var $dash_on; 569 var $dotted_on; 570 571 var $textbuffer; 572 var $currentfontstyle; 573 var $currentfontfamily; 574 var $currentfontsize; 575 var $colorarray; 576 var $bgcolorarray; 577 var $internallink; 578 var $enabledtags; 579 580 var $lineheight; 581 var $default_lineheight_correction; 582 var $basepath; 583 var $textparam; 584 585 var $specialcontent; 586 var $selectoption; 587 var $objectbuffer; 588 589 // Table Rotation 590 var $table_rotate; 591 var $tbrot_maxw; 592 var $tbrot_maxh; 593 var $tablebuffer; 594 var $tbrot_align; 595 var $tbrot_Links; 596 597 var $keep_block_together; // Keep a Block from page-break-inside: avoid 598 599 var $tbrot_y0; 600 var $tbrot_x0; 601 var $tbrot_w; 602 var $tbrot_h; 603 604 var $mb_enc; 605 var $originalMbEnc; 606 var $originalMbRegexEnc; 607 608 var $directionality; 609 610 var $extgstates; // Used for alpha channel - Transparency (Watermark) 611 var $mgl; 612 var $mgt; 613 var $mgr; 614 var $mgb; 615 616 var $tts; 617 var $ttz; 618 var $tta; 619 620 // Best to alter the below variables using default stylesheet above 621 var $page_break_after_avoid; 622 var $margin_bottom_collapse; 623 var $default_font_size; // in pts 624 var $original_default_font_size; // used to save default sizes when using table default 625 var $original_default_font; 626 var $watermark_font; 627 var $defaultAlign; 628 629 // TABLE 630 var $defaultTableAlign; 631 var $tablethead; 632 var $thead_font_weight; 633 var $thead_font_style; 634 var $thead_font_smCaps; 635 var $thead_valign_default; 636 var $thead_textalign_default; 637 var $tabletfoot; 638 var $tfoot_font_weight; 639 var $tfoot_font_style; 640 var $tfoot_font_smCaps; 641 var $tfoot_valign_default; 642 var $tfoot_textalign_default; 643 644 var $trow_text_rotate; 645 646 var $cellPaddingL; 647 var $cellPaddingR; 648 var $cellPaddingT; 649 var $cellPaddingB; 650 var $table_border_attr_set; 651 var $table_border_css_set; 652 653 var $shrin_k; // factor with which to shrink tables - used internally - do not change 654 var $shrink_this_table_to_fit; // 0 or false to disable; value (if set) gives maximum factor to reduce fontsize 655 var $MarginCorrection; // corrects for OddEven Margins 656 var $margin_footer; 657 var $margin_header; 658 659 var $tabletheadjustfinished; 660 var $usingCoreFont; 661 var $charspacing; 662 663 var $js; 664 665 /** 666 * Set timeout for cURL 667 * 668 * @var int 669 */ 670 var $curlTimeout; 671 672 /** 673 * Set execution timeout for cURL 674 * 675 * @var int 676 */ 677 var $curlExecutionTimeout; 678 679 /** 680 * Set to true to follow redirects with cURL. 681 * 682 * @var bool 683 */ 684 var $curlFollowLocation; 685 686 /** 687 * Set your own CA certificate store for SSL Certificate verification when using cURL 688 * 689 * Useful setting to use on hosts with outdated CA certificates. 690 * 691 * Download the latest CA certificate from https://curl.haxx.se/docs/caextract.html 692 * 693 * @var string The absolute path to the pem file 694 */ 695 var $curlCaCertificate; 696 697 /** 698 * Set to true to allow unsafe SSL HTTPS requests. 699 * 700 * Can be useful when using CDN with HTTPS and if you don't want to configure settings with SSL certificates. 701 * 702 * @var bool 703 */ 704 var $curlAllowUnsafeSslRequests; 705 706 /** 707 * Set the proxy for cURL. 708 * 709 * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html 710 * 711 * @var string 712 */ 713 var $curlProxy; 714 715 /** 716 * Set the proxy auth for cURL. 717 * 718 * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXYUSERPWD.html 719 * 720 * @var string 721 */ 722 var $curlProxyAuth; 723 724 /** 725 * Set the User-Agent header in the HTTP requests sent by cURL. 726 * 727 * @see https://curl.haxx.se/libcurl/c/CURLOPT_USERAGENT.html 728 * 729 * @var string User Agent header 730 */ 731 var $curlUserAgent; 732 733 // Private properties FROM FPDF 734 var $DisplayPreferences; 735 var $flowingBlockAttr; 736 737 var $page; // current page number 738 739 var $n; // current object number 740 var $n_js; // current object number 741 742 var $n_ocg_hidden; 743 var $n_ocg_print; 744 var $n_ocg_view; 745 746 var $offsets; // array of object offsets 747 var $buffer; // buffer holding in-memory PDF 748 var $pages; // array containing pages 749 var $state; // current document state 750 var $compress; // compression flag 751 752 var $DefOrientation; // default orientation 753 var $CurOrientation; // current orientation 754 var $OrientationChanges; // array indicating orientation changes 755 756 var $fwPt; 757 var $fhPt; // dimensions of page format in points 758 var $fw; 759 var $fh; // dimensions of page format in user unit 760 var $wPt; 761 var $hPt; // current dimensions of page in points 762 763 var $w; 764 var $h; // current dimensions of page in user unit 765 766 var $lMargin; // left margin 767 var $tMargin; // top margin 768 var $rMargin; // right margin 769 var $bMargin; // page break margin 770 var $cMarginL; // cell margin Left 771 var $cMarginR; // cell margin Right 772 var $cMarginT; // cell margin Left 773 var $cMarginB; // cell margin Right 774 775 var $DeflMargin; // Default left margin 776 var $DefrMargin; // Default right margin 777 778 var $x; 779 var $y; // current position in user unit for cell positioning 780 781 var $lasth; // height of last cell printed 782 var $LineWidth; // line width in user unit 783 784 var $CoreFonts; // array of standard font names 785 var $fonts; // array of used fonts 786 var $FontFiles; // array of font files 787 788 var $images; // array of used images 789 var $imageVars = []; // array of image vars 790 791 var $PageLinks; // array of links in pages 792 var $links; // array of internal links 793 var $FontFamily; // current font family 794 var $FontStyle; // current font style 795 var $CurrentFont; // current font info 796 var $FontSizePt; // current font size in points 797 var $FontSize; // current font size in user unit 798 var $DrawColor; // commands for drawing color 799 var $FillColor; // commands for filling color 800 var $TextColor; // commands for text color 801 var $ColorFlag; // indicates whether fill and text colors are different 802 var $autoPageBreak; // automatic page breaking 803 var $PageBreakTrigger; // threshold used to trigger page breaks 804 var $InFooter; // flag set when processing footer 805 806 var $InHTMLFooter; 807 var $processingFooter; // flag set when processing footer - added for columns 808 var $processingHeader; // flag set when processing header - added for columns 809 var $ZoomMode; // zoom display mode 810 var $LayoutMode; // layout display mode 811 var $title; // title 812 var $subject; // subject 813 var $author; // author 814 var $keywords; // keywords 815 var $creator; // creator 816 817 var $customProperties; // array of custom document properties 818 819 var $associatedFiles; // associated files (see SetAssociatedFiles below) 820 var $additionalXmpRdf; // additional rdf added in xmp 821 822 var $aliasNbPg; // alias for total number of pages 823 var $aliasNbPgGp; // alias for total number of pages in page group 824 825 var $ispre; 826 var $outerblocktags; 827 var $innerblocktags; 828 829 public $exposeVersion; 830 831 private $preambleWritten = false; 832 833 /** 834 * @var string 835 */ 836 private $fontDescriptor; 837 838 /** 839 * @var \Mpdf\Otl 840 */ 841 private $otl; 842 843 /** 844 * @var \Mpdf\CssManager 845 */ 846 private $cssManager; 847 848 /** 849 * @var \Mpdf\Gradient 850 */ 851 private $gradient; 852 853 /** 854 * @var \Mpdf\Image\Bmp 855 */ 856 private $bmp; 857 858 /** 859 * @var \Mpdf\Image\Wmf 860 */ 861 private $wmf; 862 863 /** 864 * @var \Mpdf\TableOfContents 865 */ 866 private $tableOfContents; 867 868 /** 869 * @var \Mpdf\Form 870 */ 871 private $form; 872 873 /** 874 * @var \Mpdf\DirectWrite 875 */ 876 private $directWrite; 877 878 /** 879 * @var \Mpdf\Cache 880 */ 881 private $cache; 882 883 /** 884 * @var \Mpdf\Fonts\FontCache 885 */ 886 private $fontCache; 887 888 /** 889 * @var \Mpdf\Fonts\FontFileFinder 890 */ 891 private $fontFileFinder; 892 893 /** 894 * @var \Mpdf\Tag 895 */ 896 private $tag; 897 898 /** 899 * @var \Mpdf\Barcode 900 * @todo solve Tag dependency and make private 901 */ 902 public $barcode; 903 904 /** 905 * @var \Mpdf\QrCode\QrCode 906 */ 907 private $qrcode; 908 909 /** 910 * @var \Mpdf\SizeConverter 911 */ 912 private $sizeConverter; 913 914 /** 915 * @var \Mpdf\Color\ColorConverter 916 */ 917 private $colorConverter; 918 919 /** 920 * @var \Mpdf\Color\ColorModeConverter 921 */ 922 private $colorModeConverter; 923 924 /** 925 * @var \Mpdf\Color\ColorSpaceRestrictor 926 */ 927 private $colorSpaceRestrictor; 928 929 /** 930 * @var \Mpdf\Hyphenator 931 */ 932 private $hyphenator; 933 934 /** 935 * @var \Mpdf\Pdf\Protection 936 */ 937 private $protection; 938 939 /** 940 * @var \Mpdf\RemoteContentFetcher 941 */ 942 private $remoteContentFetcher; 943 944 /** 945 * @var \Mpdf\Image\ImageProcessor 946 */ 947 private $imageProcessor; 948 949 /** 950 * @var \Mpdf\Language\LanguageToFontInterface 951 */ 952 private $languageToFont; 953 954 /** 955 * @var \Mpdf\Language\ScriptToLanguageInterface 956 */ 957 private $scriptToLanguage; 958 959 /** 960 * @var \Psr\Log\LoggerInterface 961 */ 962 private $logger; 963 964 /** 965 * @var \Mpdf\Writer\BaseWriter 966 */ 967 private $writer; 968 969 /** 970 * @var \Mpdf\Writer\FontWriter 971 */ 972 private $fontWriter; 973 974 /** 975 * @var \Mpdf\Writer\MetadataWriter 976 */ 977 private $metadataWriter; 978 979 /** 980 * @var \Mpdf\Writer\ImageWriter 981 */ 982 private $imageWriter; 983 984 /** 985 * @var \Mpdf\Writer\FormWriter 986 */ 987 private $formWriter; 988 989 /** 990 * @var \Mpdf\Writer\PageWriter 991 */ 992 private $pageWriter; 993 994 /** 995 * @var \Mpdf\Writer\BookmarkWriter 996 */ 997 private $bookmarkWriter; 998 999 /** 1000 * @var \Mpdf\Writer\OptionalContentWriter 1001 */ 1002 private $optionalContentWriter; 1003 1004 /** 1005 * @var \Mpdf\Writer\ColorWriter 1006 */ 1007 private $colorWriter; 1008 1009 /** 1010 * @var \Mpdf\Writer\BackgroundWriter 1011 */ 1012 private $backgroundWriter; 1013 1014 /** 1015 * @var \Mpdf\Writer\JavaScriptWriter 1016 */ 1017 private $javaScriptWriter; 1018 1019 /** 1020 * @var \Mpdf\Writer\ResourceWriter 1021 */ 1022 private $resourceWriter; 1023 1024 /** 1025 * @var string[] 1026 */ 1027 private $services; 1028 1029 /** 1030 * @param mixed[] $config 1031 */ 1032 public function __construct(array $config = []) 1033 { 1034 $this->_dochecks(); 1035 1036 list( 1037 $mode, 1038 $format, 1039 $default_font_size, 1040 $default_font, 1041 $mgl, 1042 $mgr, 1043 $mgt, 1044 $mgb, 1045 $mgh, 1046 $mgf, 1047 $orientation 1048 ) = $this->initConstructorParams($config); 1049 1050 $this->logger = new NullLogger(); 1051 1052 $originalConfig = $config; 1053 $config = $this->initConfig($originalConfig); 1054 1055 $serviceFactory = new ServiceFactory(); 1056 $services = $serviceFactory->getServices( 1057 $this, 1058 $this->logger, 1059 $config, 1060 $this->restrictColorSpace, 1061 $this->languageToFont, 1062 $this->scriptToLanguage, 1063 $this->fontDescriptor, 1064 $this->bmp, 1065 $this->directWrite, 1066 $this->wmf 1067 ); 1068 1069 $this->services = []; 1070 1071 foreach ($services as $key => $service) { 1072 $this->{$key} = $service; 1073 $this->services[] = $key; 1074 } 1075 1076 $this->time0 = microtime(true); 1077 1078 $this->writingToC = false; 1079 1080 $this->layers = []; 1081 $this->current_layer = 0; 1082 $this->open_layer_pane = false; 1083 1084 $this->visibility = 'visible'; 1085 1086 $this->tableBackgrounds = []; 1087 $this->uniqstr = '20110230'; // mPDF 5.7.2 1088 $this->kt_y00 = 0; 1089 $this->kt_p00 = 0; 1090 $this->BMPonly = []; 1091 $this->page = 0; 1092 $this->n = 2; 1093 $this->buffer = ''; 1094 $this->objectbuffer = []; 1095 $this->pages = []; 1096 $this->OrientationChanges = []; 1097 $this->state = 0; 1098 $this->fonts = []; 1099 $this->FontFiles = []; 1100 $this->images = []; 1101 $this->links = []; 1102 $this->InFooter = false; 1103 $this->processingFooter = false; 1104 $this->processingHeader = false; 1105 $this->lasth = 0; 1106 $this->FontFamily = ''; 1107 $this->FontStyle = ''; 1108 $this->FontSizePt = 9; 1109 1110 // Small Caps 1111 $this->inMeter = false; 1112 $this->decimal_offset = 0; 1113 1114 $this->PDFAXwarnings = []; 1115 1116 $this->defTextColor = $this->TextColor = $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true); 1117 $this->defDrawColor = $this->DrawColor = $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true); 1118 $this->defFillColor = $this->FillColor = $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings), true); 1119 1120 $this->upperCase = require __DIR__ . '/../data/upperCase.php'; 1121 1122 $this->extrapagebreak = true; // mPDF 6 pagebreaktype 1123 1124 $this->ColorFlag = false; 1125 $this->extgstates = []; 1126 1127 $this->mb_enc = 'windows-1252'; 1128 $this->originalMbEnc = mb_internal_encoding(); 1129 $this->originalMbRegexEnc = mb_regex_encoding(); 1130 1131 $this->directionality = 'ltr'; 1132 $this->defaultAlign = 'L'; 1133 $this->defaultTableAlign = 'L'; 1134 1135 $this->fixedPosBlockSave = []; 1136 $this->extraFontSubsets = 0; 1137 1138 $this->blockContext = 1; 1139 $this->floatDivs = []; 1140 $this->DisplayPreferences = ''; 1141 1142 // Tiling patterns used for backgrounds 1143 $this->patterns = []; 1144 $this->pageBackgrounds = []; 1145 $this->gradients = []; 1146 1147 // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block 1148 $this->writingHTMLheader = false; 1149 // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block 1150 $this->writingHTMLfooter = false; 1151 1152 $this->kwt_Reference = []; 1153 $this->kwt_BMoutlines = []; 1154 $this->kwt_toc = []; 1155 1156 $this->tbrot_BMoutlines = []; 1157 $this->tbrot_toc = []; 1158 1159 $this->col_BMoutlines = []; 1160 $this->col_toc = []; 1161 1162 $this->pgsIns = []; 1163 $this->PDFAXwarnings = []; 1164 $this->inlineDisplayOff = false; 1165 $this->lSpacingCSS = ''; 1166 $this->wSpacingCSS = ''; 1167 $this->fixedlSpacing = false; 1168 $this->minwSpacing = 0; 1169 1170 // Baseline for text 1171 $this->baselineC = 0.35; 1172 1173 // mPDF 5.7.3 inline text-decoration parameters 1174 // Sets default change in baseline for <sup> text as factor of preceeding fontsize 1175 // 0.35 has been recommended; 0.5 matches applications like MS Word 1176 $this->baselineSup = 0.5; 1177 1178 // Sets default change in baseline for <sub> text as factor of preceeding fontsize 1179 $this->baselineSub = -0.2; 1180 // Sets default height for <strike> text as factor of fontsize 1181 $this->baselineS = 0.3; 1182 // Sets default height for overline text as factor of fontsize 1183 $this->baselineO = 1.1; 1184 1185 $this->noImageFile = __DIR__ . '/../data/no_image.jpg'; 1186 $this->subPos = 0; 1187 1188 $this->fullImageHeight = false; 1189 $this->floatbuffer = []; 1190 $this->floatmargins = []; 1191 $this->formobjects = []; // array of Form Objects for WMF 1192 $this->InlineProperties = []; 1193 $this->InlineAnnots = []; 1194 $this->InlineBDF = []; // mPDF 6 1195 $this->InlineBDFctr = 0; // mPDF 6 1196 $this->tbrot_Annots = []; 1197 $this->kwt_Annots = []; 1198 $this->columnAnnots = []; 1199 $this->PageLinks = []; 1200 $this->OrientationChanges = []; 1201 $this->pageDim = []; 1202 $this->saveHTMLHeader = []; 1203 $this->saveHTMLFooter = []; 1204 $this->PageAnnots = []; 1205 $this->PageNumSubstitutions = []; 1206 $this->breakpoints = []; // used in columnbuffer 1207 $this->tableLevel = 0; 1208 $this->tbctr = []; // counter for nested tables at each level 1209 $this->page_box = []; 1210 $this->show_marks = ''; // crop or cross marks 1211 $this->kwt = false; 1212 $this->kwt_height = 0; 1213 $this->kwt_y0 = 0; 1214 $this->kwt_x0 = 0; 1215 $this->kwt_buffer = []; 1216 $this->kwt_Links = []; 1217 $this->kwt_moved = false; 1218 $this->kwt_saved = false; 1219 $this->PageNumSubstitutions = []; 1220 $this->base_table_properties = []; 1221 $this->borderstyles = ['inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double']; 1222 $this->tbrot_align = 'C'; 1223 1224 $this->pageHTMLheaders = []; 1225 $this->pageHTMLfooters = []; 1226 $this->HTMLheaderPageLinks = []; 1227 $this->HTMLheaderPageAnnots = []; 1228 1229 $this->HTMLheaderPageForms = []; 1230 $this->columnForms = []; 1231 $this->tbrotForms = []; 1232 1233 $this->pageoutput = []; 1234 1235 $this->bufferoutput = false; 1236 1237 $this->encrypted = false; 1238 1239 $this->BMoutlines = []; 1240 $this->ColActive = 0; // Flag indicating that columns are on (the index is being processed) 1241 $this->Reference = []; // Array containing the references 1242 $this->CurrCol = 0; // Current column number 1243 $this->ColL = [0]; // Array of Left pos of columns - absolute - needs Margin correction for Odd-Even 1244 $this->ColR = [0]; // Array of Right pos of columns - absolute pos - needs Margin correction for Odd-Even 1245 $this->ChangeColumn = 0; 1246 $this->columnbuffer = []; 1247 $this->ColDetails = []; // Keeps track of some column details 1248 $this->columnLinks = []; // Cross references PageLinks 1249 $this->substitute = []; // Array of substitution strings e.g. <ttz>112</ttz> 1250 $this->entsearch = []; // Array of HTML entities (>ASCII 127) to substitute 1251 $this->entsubstitute = []; // Array of substitution decimal unicode for the Hi entities 1252 $this->lastoptionaltag = ''; 1253 $this->charset_in = ''; 1254 $this->blk = []; 1255 $this->blklvl = 0; 1256 $this->tts = false; 1257 $this->ttz = false; 1258 $this->tta = false; 1259 $this->ispre = false; 1260 1261 $this->checkSIP = false; 1262 $this->checkSMP = false; 1263 $this->checkCJK = false; 1264 1265 $this->page_break_after_avoid = false; 1266 $this->margin_bottom_collapse = false; 1267 $this->tablethead = 0; 1268 $this->tabletfoot = 0; 1269 $this->table_border_attr_set = 0; 1270 $this->table_border_css_set = 0; 1271 $this->shrin_k = 1.0; 1272 $this->shrink_this_table_to_fit = 0; 1273 $this->MarginCorrection = 0; 1274 1275 $this->tabletheadjustfinished = false; 1276 $this->usingCoreFont = false; 1277 $this->charspacing = 0; 1278 1279 $this->autoPageBreak = true; 1280 1281 $this->_setPageSize($format, $orientation); 1282 $this->DefOrientation = $orientation; 1283 1284 $this->margin_header = $mgh; 1285 $this->margin_footer = $mgf; 1286 1287 $bmargin = $mgb; 1288 1289 $this->DeflMargin = $mgl; 1290 $this->DefrMargin = $mgr; 1291 1292 $this->orig_tMargin = $mgt; 1293 $this->orig_bMargin = $bmargin; 1294 $this->orig_lMargin = $this->DeflMargin; 1295 $this->orig_rMargin = $this->DefrMargin; 1296 $this->orig_hMargin = $this->margin_header; 1297 $this->orig_fMargin = $this->margin_footer; 1298 1299 if ($this->setAutoTopMargin == 'pad') { 1300 $mgt += $this->margin_header; 1301 } 1302 if ($this->setAutoBottomMargin == 'pad') { 1303 $mgb += $this->margin_footer; 1304 } 1305 1306 // sets l r t margin 1307 $this->SetMargins($this->DeflMargin, $this->DefrMargin, $mgt); 1308 1309 // Automatic page break 1310 // sets $this->bMargin & PageBreakTrigger 1311 $this->SetAutoPageBreak($this->autoPageBreak, $bmargin); 1312 1313 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 1314 1315 // Interior cell margin (1 mm) ? not used 1316 $this->cMarginL = 1; 1317 $this->cMarginR = 1; 1318 1319 // Line width (0.2 mm) 1320 $this->LineWidth = .567 / Mpdf::SCALE; 1321 1322 // Enable all tags as default 1323 $this->DisableTags(); 1324 // Full width display mode 1325 $this->SetDisplayMode(100); // fullwidth? 'fullpage' 1326 1327 // Compression 1328 $this->SetCompression(true); 1329 // Set default display preferences 1330 $this->SetDisplayPreferences(''); 1331 1332 $this->initFontConfig($originalConfig); 1333 1334 // Available fonts 1335 $this->available_unifonts = []; 1336 foreach ($this->fontdata as $f => $fs) { 1337 if (isset($fs['R']) && $fs['R']) { 1338 $this->available_unifonts[] = $f; 1339 } 1340 if (isset($fs['B']) && $fs['B']) { 1341 $this->available_unifonts[] = $f . 'B'; 1342 } 1343 if (isset($fs['I']) && $fs['I']) { 1344 $this->available_unifonts[] = $f . 'I'; 1345 } 1346 if (isset($fs['BI']) && $fs['BI']) { 1347 $this->available_unifonts[] = $f . 'BI'; 1348 } 1349 } 1350 1351 $this->default_available_fonts = $this->available_unifonts; 1352 1353 $optcore = false; 1354 $onlyCoreFonts = false; 1355 if (preg_match('/([\-+])aCJK/i', $mode, $m)) { 1356 $mode = preg_replace('/([\-+])aCJK/i', '', $mode); // mPDF 6 1357 if ($m[1] == '+') { 1358 $this->useAdobeCJK = true; 1359 } else { 1360 $this->useAdobeCJK = false; 1361 } 1362 } 1363 1364 if (strlen($mode) == 1) { 1365 if ($mode == 's') { 1366 $this->percentSubset = 100; 1367 $mode = ''; 1368 } elseif ($mode == 'c') { 1369 $onlyCoreFonts = true; 1370 $mode = ''; 1371 } 1372 } elseif (substr($mode, -2) == '-s') { 1373 $this->percentSubset = 100; 1374 $mode = substr($mode, 0, strlen($mode) - 2); 1375 } elseif (substr($mode, -2) == '-c') { 1376 $onlyCoreFonts = true; 1377 $mode = substr($mode, 0, strlen($mode) - 2); 1378 } elseif (substr($mode, -2) == '-x') { 1379 $optcore = true; 1380 $mode = substr($mode, 0, strlen($mode) - 2); 1381 } 1382 1383 // Autodetect if mode is a language_country string (en-GB or en_GB or en) 1384 if ($mode && $mode != 'UTF-8') { // mPDF 6 1385 list ($coreSuitable, $mpdf_pdf_unifont) = $this->languageToFont->getLanguageOptions($mode, $this->useAdobeCJK); 1386 if ($coreSuitable && $optcore) { 1387 $onlyCoreFonts = true; 1388 } 1389 if ($mpdf_pdf_unifont) { // mPDF 6 1390 $default_font = $mpdf_pdf_unifont; 1391 } 1392 $this->currentLang = $mode; 1393 $this->default_lang = $mode; 1394 } 1395 1396 $this->onlyCoreFonts = $onlyCoreFonts; 1397 1398 if ($this->onlyCoreFonts) { 1399 $this->setMBencoding('windows-1252'); // sets $this->mb_enc 1400 } else { 1401 $this->setMBencoding('UTF-8'); // sets $this->mb_enc 1402 } 1403 @mb_regex_encoding('UTF-8'); // required only for mb_ereg... and mb_split functions 1404 1405 // Adobe CJK fonts 1406 $this->available_CJK_fonts = [ 1407 'gb', 1408 'big5', 1409 'sjis', 1410 'uhc', 1411 'gbB', 1412 'big5B', 1413 'sjisB', 1414 'uhcB', 1415 'gbI', 1416 'big5I', 1417 'sjisI', 1418 'uhcI', 1419 'gbBI', 1420 'big5BI', 1421 'sjisBI', 1422 'uhcBI', 1423 ]; 1424 1425 // Standard fonts 1426 $this->CoreFonts = [ 1427 'ccourier' => 'Courier', 1428 'ccourierB' => 'Courier-Bold', 1429 'ccourierI' => 'Courier-Oblique', 1430 'ccourierBI' => 'Courier-BoldOblique', 1431 'chelvetica' => 'Helvetica', 1432 'chelveticaB' => 'Helvetica-Bold', 1433 'chelveticaI' => 'Helvetica-Oblique', 1434 'chelveticaBI' => 'Helvetica-BoldOblique', 1435 'ctimes' => 'Times-Roman', 1436 'ctimesB' => 'Times-Bold', 1437 'ctimesI' => 'Times-Italic', 1438 'ctimesBI' => 'Times-BoldItalic', 1439 'csymbol' => 'Symbol', 1440 'czapfdingbats' => 'ZapfDingbats' 1441 ]; 1442 1443 $this->fontlist = [ 1444 "ctimes", 1445 "ccourier", 1446 "chelvetica", 1447 "csymbol", 1448 "czapfdingbats" 1449 ]; 1450 1451 // Substitutions 1452 $this->setHiEntitySubstitutions(); 1453 1454 if ($this->onlyCoreFonts) { 1455 $this->useSubstitutions = true; 1456 $this->SetSubstitutions(); 1457 } else { 1458 $this->useSubstitutions = $config['useSubstitutions']; 1459 } 1460 1461 if (file_exists($this->defaultCssFile)) { 1462 $css = file_get_contents($this->defaultCssFile); 1463 $this->cssManager->ReadCSS('<style> ' . $css . ' </style>'); 1464 } else { 1465 throw new \Mpdf\MpdfException(sprintf('Unable to read default CSS file "%s"', $this->defaultCssFile)); 1466 } 1467 1468 if ($default_font == '') { 1469 if ($this->onlyCoreFonts) { 1470 if (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->mono_fonts)) { 1471 $default_font = 'ccourier'; 1472 } elseif (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->sans_fonts)) { 1473 $default_font = 'chelvetica'; 1474 } else { 1475 $default_font = 'ctimes'; 1476 } 1477 } else { 1478 $default_font = $this->defaultCSS['BODY']['FONT-FAMILY']; 1479 } 1480 } 1481 if (!$default_font_size) { 1482 $mmsize = $this->sizeConverter->convert($this->defaultCSS['BODY']['FONT-SIZE']); 1483 $default_font_size = $mmsize * (Mpdf::SCALE); 1484 } 1485 1486 if ($default_font) { 1487 $this->SetDefaultFont($default_font); 1488 } 1489 if ($default_font_size) { 1490 $this->SetDefaultFontSize($default_font_size); 1491 } 1492 1493 $this->SetLineHeight(); // lineheight is in mm 1494 1495 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 1496 $this->HREF = ''; 1497 $this->oldy = -1; 1498 $this->B = 0; 1499 $this->I = 0; 1500 1501 // mPDF 6 Lists 1502 $this->listlvl = 0; 1503 $this->listtype = []; 1504 $this->listitem = []; 1505 $this->listcounter = []; 1506 1507 $this->tdbegin = false; 1508 $this->table = []; 1509 $this->cell = []; 1510 $this->col = -1; 1511 $this->row = -1; 1512 $this->cellBorderBuffer = []; 1513 1514 $this->divbegin = false; 1515 // mPDF 6 1516 $this->cellTextAlign = ''; 1517 $this->cellLineHeight = ''; 1518 $this->cellLineStackingStrategy = ''; 1519 $this->cellLineStackingShift = ''; 1520 1521 $this->divwidth = 0; 1522 $this->divheight = 0; 1523 $this->spanbgcolor = false; 1524 $this->spanborder = false; 1525 $this->spanborddet = []; 1526 1527 $this->blockjustfinished = false; 1528 $this->ignorefollowingspaces = true; // in order to eliminate exceeding left-side spaces 1529 $this->dash_on = false; 1530 $this->dotted_on = false; 1531 $this->textshadow = ''; 1532 1533 $this->currentfontfamily = ''; 1534 $this->currentfontsize = ''; 1535 $this->currentfontstyle = ''; 1536 $this->colorarray = ''; // mPDF 6 1537 $this->spanbgcolorarray = ''; // mPDF 6 1538 $this->textbuffer = []; 1539 $this->internallink = []; 1540 $this->basepath = ""; 1541 1542 $this->SetBasePath(''); 1543 1544 $this->textparam = []; 1545 1546 $this->specialcontent = ''; 1547 $this->selectoption = []; 1548 } 1549 1550 public function cleanup() 1551 { 1552 mb_internal_encoding($this->originalMbEnc); 1553 @mb_regex_encoding($this->originalMbRegexEnc); 1554 1555 // this will free up the readers, based on code from Setasign's FpdiTrait::cleanUp() 1556 foreach ($this->createdReaders as $id) { 1557 $this->readers[$id]->getParser()->getStreamReader()->cleanUp(); 1558 unset($this->readers[$id]); 1559 } 1560 1561 $this->createdReaders = []; 1562 } 1563 1564 /** 1565 * @param \Psr\Log\LoggerInterface 1566 * 1567 * @return \Mpdf\Mpdf 1568 */ 1569 public function setLogger(LoggerInterface $logger) 1570 { 1571 $this->logger = $logger; 1572 1573 foreach ($this->services as $name) { 1574 if ($this->$name && $this->$name instanceof \Psr\Log\LoggerAwareInterface) { 1575 $this->$name->setLogger($logger); 1576 } 1577 } 1578 1579 return $this; 1580 } 1581 1582 private function initConfig(array $config) 1583 { 1584 $configObject = new ConfigVariables(); 1585 $defaults = $configObject->getDefaults(); 1586 $config = array_intersect_key($config + $defaults, $defaults); 1587 1588 foreach ($config as $var => $val) { 1589 $this->{$var} = $val; 1590 } 1591 1592 return $config; 1593 } 1594 1595 private function initConstructorParams(array $config) 1596 { 1597 $constructor = [ 1598 'mode' => '', 1599 'format' => 'A4', 1600 'default_font_size' => 0, 1601 'default_font' => '', 1602 'margin_left' => 15, 1603 'margin_right' => 15, 1604 'margin_top' => 16, 1605 'margin_bottom' => 16, 1606 'margin_header' => 9, 1607 'margin_footer' => 9, 1608 'orientation' => 'P', 1609 ]; 1610 1611 foreach ($constructor as $key => $val) { 1612 if (isset($config[$key])) { 1613 $constructor[$key] = $config[$key]; 1614 } 1615 } 1616 1617 return array_values($constructor); 1618 } 1619 1620 private function initFontConfig(array $config) 1621 { 1622 $configObject = new FontVariables(); 1623 $defaults = $configObject->getDefaults(); 1624 $config = array_intersect_key($config + $defaults, $defaults); 1625 foreach ($config as $var => $val) { 1626 $this->{$var} = $val; 1627 } 1628 1629 return $config; 1630 } 1631 1632 function _setPageSize($format, &$orientation) 1633 { 1634 if (is_string($format)) { 1635 1636 if (empty($format)) { 1637 $format = 'A4'; 1638 } 1639 1640 // e.g. A4-L = A4 landscape, A4-P = A4 portrait 1641 if (preg_match('/([0-9a-zA-Z]*)-([P,L])/i', $format, $m)) { 1642 $format = $m[1]; 1643 $orientation = $m[2]; 1644 } elseif (empty($orientation)) { 1645 $orientation = 'P'; 1646 } 1647 1648 $format = PageFormat::getSizeFromName($format); 1649 1650 $this->fwPt = $format[0]; 1651 $this->fhPt = $format[1]; 1652 1653 } else { 1654 1655 if (!$format[0] || !$format[1]) { 1656 throw new \Mpdf\MpdfException('Invalid page format: ' . $format[0] . ' ' . $format[1]); 1657 } 1658 1659 $this->fwPt = $format[0] * Mpdf::SCALE; 1660 $this->fhPt = $format[1] * Mpdf::SCALE; 1661 } 1662 1663 $this->fw = $this->fwPt / Mpdf::SCALE; 1664 $this->fh = $this->fhPt / Mpdf::SCALE; 1665 1666 // Page orientation 1667 $orientation = strtolower($orientation); 1668 if ($orientation === 'p' || $orientation == 'portrait') { 1669 $orientation = 'P'; 1670 $this->wPt = $this->fwPt; 1671 $this->hPt = $this->fhPt; 1672 } elseif ($orientation === 'l' || $orientation == 'landscape') { 1673 $orientation = 'L'; 1674 $this->wPt = $this->fhPt; 1675 $this->hPt = $this->fwPt; 1676 } else { 1677 throw new \Mpdf\MpdfException('Incorrect orientation: ' . $orientation); 1678 } 1679 1680 $this->CurOrientation = $orientation; 1681 1682 $this->w = $this->wPt / Mpdf::SCALE; 1683 $this->h = $this->hPt / Mpdf::SCALE; 1684 } 1685 1686 function RestrictUnicodeFonts($res) 1687 { 1688 // $res = array of (Unicode) fonts to restrict to: e.g. norasi|norasiB - language specific 1689 if (count($res)) { // Leave full list of available fonts if passed blank array 1690 $this->available_unifonts = $res; 1691 } else { 1692 $this->available_unifonts = $this->default_available_fonts; 1693 } 1694 if (count($this->available_unifonts) == 0) { 1695 $this->available_unifonts[] = $this->default_available_fonts[0]; 1696 } 1697 $this->available_unifonts = array_values($this->available_unifonts); 1698 } 1699 1700 function setMBencoding($enc) 1701 { 1702 if ($this->mb_enc != $enc) { 1703 $this->mb_enc = $enc; 1704 mb_internal_encoding($this->mb_enc); 1705 } 1706 } 1707 1708 function SetMargins($left, $right, $top) 1709 { 1710 // Set left, top and right margins 1711 $this->lMargin = $left; 1712 $this->rMargin = $right; 1713 $this->tMargin = $top; 1714 } 1715 1716 function ResetMargins() 1717 { 1718 // ReSet left, top margins 1719 if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P' && $this->CurOrientation == 'L') { 1720 if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN 1721 $this->tMargin = $this->orig_rMargin; 1722 $this->bMargin = $this->orig_lMargin; 1723 } else { // ODD // OR NOT MIRRORING MARGINS/FOOTERS 1724 $this->tMargin = $this->orig_lMargin; 1725 $this->bMargin = $this->orig_rMargin; 1726 } 1727 $this->lMargin = $this->DeflMargin; 1728 $this->rMargin = $this->DefrMargin; 1729 $this->MarginCorrection = 0; 1730 $this->PageBreakTrigger = $this->h - $this->bMargin; 1731 } elseif (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN 1732 $this->lMargin = $this->DefrMargin; 1733 $this->rMargin = $this->DeflMargin; 1734 $this->MarginCorrection = $this->DefrMargin - $this->DeflMargin; 1735 } else { // ODD // OR NOT MIRRORING MARGINS/FOOTERS 1736 $this->lMargin = $this->DeflMargin; 1737 $this->rMargin = $this->DefrMargin; 1738 if ($this->mirrorMargins) { 1739 $this->MarginCorrection = $this->DeflMargin - $this->DefrMargin; 1740 } 1741 } 1742 $this->x = $this->lMargin; 1743 } 1744 1745 function SetLeftMargin($margin) 1746 { 1747 // Set left margin 1748 $this->lMargin = $margin; 1749 if ($this->page > 0 and $this->x < $margin) { 1750 $this->x = $margin; 1751 } 1752 } 1753 1754 function SetTopMargin($margin) 1755 { 1756 // Set top margin 1757 $this->tMargin = $margin; 1758 } 1759 1760 function SetRightMargin($margin) 1761 { 1762 // Set right margin 1763 $this->rMargin = $margin; 1764 } 1765 1766 function SetAutoPageBreak($auto, $margin = 0) 1767 { 1768 // Set auto page break mode and triggering margin 1769 $this->autoPageBreak = $auto; 1770 $this->bMargin = $margin; 1771 $this->PageBreakTrigger = $this->h - $margin; 1772 } 1773 1774 function SetDisplayMode($zoom, $layout = 'continuous') 1775 { 1776 $allowedZoomModes = ['fullpage', 'fullwidth', 'real', 'default', 'none']; 1777 1778 if (in_array($zoom, $allowedZoomModes, true) || is_numeric($zoom)) { 1779 $this->ZoomMode = $zoom; 1780 } else { 1781 throw new \Mpdf\MpdfException('Incorrect zoom display mode: ' . $zoom); 1782 } 1783 1784 $allowedLayoutModes = ['single', 'continuous', 'two', 'twoleft', 'tworight', 'default']; 1785 1786 if (in_array($layout, $allowedLayoutModes, true)) { 1787 $this->LayoutMode = $layout; 1788 } else { 1789 throw new \Mpdf\MpdfException('Incorrect layout display mode: ' . $layout); 1790 } 1791 } 1792 1793 function SetCompression($compress) 1794 { 1795 // Set page compression 1796 if (function_exists('gzcompress')) { 1797 $this->compress = $compress; 1798 } else { 1799 $this->compress = false; 1800 } 1801 } 1802 1803 function SetTitle($title) 1804 { 1805 // Title of document // Arrives as UTF-8 1806 $this->title = $title; 1807 } 1808 1809 function SetSubject($subject) 1810 { 1811 // Subject of document 1812 $this->subject = $subject; 1813 } 1814 1815 function SetAuthor($author) 1816 { 1817 // Author of document 1818 $this->author = $author; 1819 } 1820 1821 function SetKeywords($keywords) 1822 { 1823 // Keywords of document 1824 $this->keywords = $keywords; 1825 } 1826 1827 function SetCreator($creator) 1828 { 1829 // Creator of document 1830 $this->creator = $creator; 1831 } 1832 1833 function AddCustomProperty($key, $value) 1834 { 1835 $this->customProperties[$key] = $value; 1836 } 1837 1838 /** 1839 * Set one or multiple associated file ("/AF" as required by PDF/A-3) 1840 * 1841 * param $files is an array of hash containing: 1842 * path: file path on FS 1843 * content: file content 1844 * name: file name (not necessarily the same as the file on FS) 1845 * mime (optional): file mime type (will show up as /Subtype in the PDF) 1846 * description (optional): file description 1847 * AFRelationship (optional): PDF/A-3 AFRelationship (e.g. "Alternative") 1848 * 1849 * e.g. to associate 1 file: 1850 * [[ 1851 * 'path' => 'tmp/1234.xml', 1852 * 'content' => 'file content', 1853 * 'name' => 'public_name.xml', 1854 * 'mime' => 'text/xml', 1855 * 'description' => 'foo', 1856 * 'AFRelationship' => 'Alternative', 1857 * ]] 1858 * 1859 * @param mixed[] $files Array of arrays of associated files. See above 1860 */ 1861 function SetAssociatedFiles(array $files) 1862 { 1863 $this->associatedFiles = $files; 1864 } 1865 1866 function SetAdditionalXmpRdf($s) 1867 { 1868 $this->additionalXmpRdf = $s; 1869 } 1870 1871 function SetAnchor2Bookmark($x) 1872 { 1873 $this->anchor2Bookmark = $x; 1874 } 1875 1876 public function AliasNbPages($alias = '{nb}') 1877 { 1878 // Define an alias for total number of pages 1879 $this->aliasNbPg = $alias; 1880 } 1881 1882 public function AliasNbPageGroups($alias = '{nbpg}') 1883 { 1884 // Define an alias for total number of pages in a group 1885 $this->aliasNbPgGp = $alias; 1886 } 1887 1888 function SetAlpha($alpha, $bm = 'Normal', $return = false, $mode = 'B') 1889 { 1890 // alpha: real value from 0 (transparent) to 1 (opaque) 1891 // bm: blend mode, one of the following: 1892 // Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, 1893 // HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity 1894 // set alpha for stroking (CA) and non-stroking (ca) operations 1895 // mode determines F (fill) S (stroke) B (both) 1896 if (($this->PDFA || $this->PDFX) && $alpha != 1) { 1897 if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) { 1898 $this->PDFAXwarnings[] = "Image opacity must be 100% (Opacity changed to 100%)"; 1899 } 1900 $alpha = 1; 1901 } 1902 $a = ['BM' => '/' . $bm]; 1903 if ($mode == 'F' || $mode == 'B') { 1904 $a['ca'] = $alpha; // mPDF 5.7.2 1905 } 1906 if ($mode == 'S' || $mode == 'B') { 1907 $a['CA'] = $alpha; // mPDF 5.7.2 1908 } 1909 $gs = $this->AddExtGState($a); 1910 if ($return) { 1911 return sprintf('/GS%d gs', $gs); 1912 } else { 1913 $this->writer->write(sprintf('/GS%d gs', $gs)); 1914 } 1915 } 1916 1917 function AddExtGState($parms) 1918 { 1919 $n = count($this->extgstates); 1920 // check if graphics state already exists 1921 for ($i = 1; $i <= $n; $i++) { 1922 if (count($this->extgstates[$i]['parms']) == count($parms)) { 1923 $same = true; 1924 foreach ($this->extgstates[$i]['parms'] as $k => $v) { 1925 if (!isset($parms[$k]) || $parms[$k] != $v) { 1926 $same = false; 1927 break; 1928 } 1929 } 1930 if ($same) { 1931 return $i; 1932 } 1933 } 1934 } 1935 $n++; 1936 $this->extgstates[$n]['parms'] = $parms; 1937 return $n; 1938 } 1939 1940 function SetVisibility($v) 1941 { 1942 if (($this->PDFA || $this->PDFX) && $this->visibility != 'visible') { 1943 $this->PDFAXwarnings[] = "Cannot set visibility to anything other than full when using PDFA or PDFX"; 1944 return ''; 1945 } elseif (!$this->PDFA && !$this->PDFX) { 1946 $this->pdf_version = '1.5'; 1947 } 1948 if ($this->visibility != 'visible') { 1949 $this->writer->write('EMC'); 1950 $this->hasOC = intval($this->hasOC); 1951 } 1952 if ($v == 'printonly') { 1953 $this->writer->write('/OC /OC1 BDC'); 1954 $this->hasOC = ($this->hasOC | 1); 1955 } elseif ($v == 'screenonly') { 1956 $this->writer->write('/OC /OC2 BDC'); 1957 $this->hasOC = ($this->hasOC | 2); 1958 } elseif ($v == 'hidden') { 1959 $this->writer->write('/OC /OC3 BDC'); 1960 $this->hasOC = ($this->hasOC | 4); 1961 } elseif ($v != 'visible') { 1962 throw new \Mpdf\MpdfException('Incorrect visibility: ' . $v); 1963 } 1964 $this->visibility = $v; 1965 } 1966 1967 function Open() 1968 { 1969 // Begin document 1970 if ($this->state == 0) { 1971 $this->state = 1; 1972 if (false === $this->preambleWritten) { 1973 $this->writer->write('%PDF-' . $this->pdf_version); 1974 $this->writer->write('%' . chr(226) . chr(227) . chr(207) . chr(211)); // 4 chars > 128 to show binary file 1975 $this->preambleWritten = true; 1976 } 1977 } 1978 } 1979 1980 function Close() 1981 { 1982 // @log Closing last page 1983 1984 // Terminate document 1985 if ($this->state == 3) { 1986 return; 1987 } 1988 1989 if ($this->page == 0) { 1990 $this->AddPage($this->CurOrientation); 1991 } 1992 1993 if (count($this->cellBorderBuffer)) { 1994 $this->printcellbuffer(); 1995 } 1996 1997 // *TABLES* 1998 if ($this->tablebuffer) { 1999 $this->printtablebuffer(); 2000 } 2001 2002 /* -- COLUMNS -- */ 2003 2004 if ($this->ColActive) { 2005 $this->SetColumns(0); 2006 $this->ColActive = 0; 2007 if (count($this->columnbuffer)) { 2008 $this->printcolumnbuffer(); 2009 } 2010 } 2011 2012 /* -- END COLUMNS -- */ 2013 2014 // BODY Backgrounds 2015 $s = ''; 2016 2017 $s .= $this->PrintBodyBackgrounds(); 2018 $s .= $this->PrintPageBackgrounds(); 2019 2020 $this->pages[$this->page] = preg_replace( 2021 '/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', 2022 "\n" . $s . "\n" . '\\1', 2023 $this->pages[$this->page] 2024 ); 2025 2026 $this->pageBackgrounds = []; 2027 2028 if ($this->visibility != 'visible') { 2029 $this->SetVisibility('visible'); 2030 } 2031 2032 $this->EndLayer(); 2033 2034 if (!$this->tableOfContents->TOCmark) { // Page footer 2035 $this->InFooter = true; 2036 $this->Footer(); 2037 $this->InFooter = false; 2038 } 2039 2040 if ($this->tableOfContents->TOCmark || count($this->tableOfContents->m_TOC)) { 2041 $this->tableOfContents->insertTOC(); 2042 } 2043 2044 // Close page 2045 $this->_endpage(); 2046 2047 // Close document 2048 $this->_enddoc(); 2049 } 2050 2051 /* -- BACKGROUNDS -- */ 2052 2053 function _resizeBackgroundImage($imw, $imh, $cw, $ch, $resize, $repx, $repy, $pba = [], $size = []) 2054 { 2055 // pba is background positioning area (from CSS background-origin) may not always be set [x,y,w,h] 2056 // size is from CSS3 background-size - takes precendence over old resize 2057 // $w - absolute length or % or auto or cover | contain 2058 // $h - absolute length or % or auto or cover | contain 2059 if (isset($pba['w'])) { 2060 $cw = $pba['w']; 2061 } 2062 if (isset($pba['h'])) { 2063 $ch = $pba['h']; 2064 } 2065 2066 $cw = $cw * Mpdf::SCALE; 2067 $ch = $ch * Mpdf::SCALE; 2068 if (empty($size) && !$resize) { 2069 return [$imw, $imh, $repx, $repy]; 2070 } 2071 2072 if (isset($size['w']) && $size['w']) { 2073 if ($size['w'] == 'contain') { 2074 // Scale the image, while preserving its intrinsic aspect ratio (if any), 2075 // to the largest size such that both its width and its height can fit inside the background positioning area. 2076 // Same as resize==3 2077 $h = $imh * $cw / $imw; 2078 $w = $cw; 2079 if ($h > $ch) { 2080 $w = $w * $ch / $h; 2081 $h = $ch; 2082 } 2083 } elseif ($size['w'] == 'cover') { 2084 // Scale the image, while preserving its intrinsic aspect ratio (if any), 2085 // to the smallest size such that both its width and its height can completely cover the background positioning area. 2086 $h = $imh * $cw / $imw; 2087 $w = $cw; 2088 if ($h < $ch) { 2089 $w = $w * $h / $ch; 2090 $h = $ch; 2091 } 2092 } else { 2093 if (stristr($size['w'], '%')) { 2094 $size['w'] = (float) $size['w']; 2095 $size['w'] /= 100; 2096 $size['w'] = ($cw * $size['w']); 2097 } 2098 if (stristr($size['h'], '%')) { 2099 $size['h'] = (float) $size['h']; 2100 $size['h'] /= 100; 2101 $size['h'] = ($ch * $size['h']); 2102 } 2103 if ($size['w'] == 'auto' && $size['h'] == 'auto') { 2104 $w = $imw; 2105 $h = $imh; 2106 } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') { 2107 $w = $imw * $size['h'] / $imh; 2108 $h = $size['h']; 2109 } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') { 2110 $h = $imh * $size['w'] / $imw; 2111 $w = $size['w']; 2112 } else { 2113 $w = $size['w']; 2114 $h = $size['h']; 2115 } 2116 } 2117 return [$w, $h, $repx, $repy]; 2118 } elseif ($resize == 1 && $imw > $cw) { 2119 $h = $imh * $cw / $imw; 2120 return [$cw, $h, $repx, $repy]; 2121 } elseif ($resize == 2 && $imh > $ch) { 2122 $w = $imw * $ch / $imh; 2123 return [$w, $ch, $repx, $repy]; 2124 } elseif ($resize == 3) { 2125 $w = $imw; 2126 $h = $imh; 2127 if ($w > $cw) { 2128 $h = $h * $cw / $w; 2129 $w = $cw; 2130 } 2131 if ($h > $ch) { 2132 $w = $w * $ch / $h; 2133 $h = $ch; 2134 } 2135 return [$w, $h, $repx, $repy]; 2136 } elseif ($resize == 4) { 2137 $h = $imh * $cw / $imw; 2138 return [$cw, $h, $repx, $repy]; 2139 } elseif ($resize == 5) { 2140 $w = $imw * $ch / $imh; 2141 return [$w, $ch, $repx, $repy]; 2142 } elseif ($resize == 6) { 2143 return [$cw, $ch, $repx, $repy]; 2144 } 2145 return [$imw, $imh, $repx, $repy]; 2146 } 2147 2148 function SetBackground(&$properties, &$maxwidth) 2149 { 2150 if (isset($properties['BACKGROUND-ORIGIN']) && ($properties['BACKGROUND-ORIGIN'] == 'border-box' || $properties['BACKGROUND-ORIGIN'] == 'content-box')) { 2151 $origin = $properties['BACKGROUND-ORIGIN']; 2152 } else { 2153 $origin = 'padding-box'; 2154 } 2155 2156 if (isset($properties['BACKGROUND-SIZE'])) { 2157 if (stristr($properties['BACKGROUND-SIZE'], 'contain')) { 2158 $bsw = $bsh = 'contain'; 2159 } elseif (stristr($properties['BACKGROUND-SIZE'], 'cover')) { 2160 $bsw = $bsh = 'cover'; 2161 } else { 2162 $bsw = $bsh = 'auto'; 2163 $sz = preg_split('/\s+/', trim($properties['BACKGROUND-SIZE'])); 2164 if (count($sz) == 2) { 2165 $bsw = $sz[0]; 2166 $bsh = $sz[1]; 2167 } else { 2168 $bsw = $sz[0]; 2169 } 2170 if (!stristr($bsw, '%') && !stristr($bsw, 'auto')) { 2171 $bsw = $this->sizeConverter->convert($bsw, $maxwidth, $this->FontSize); 2172 } 2173 if (!stristr($bsh, '%') && !stristr($bsh, 'auto')) { 2174 $bsh = $this->sizeConverter->convert($bsh, $maxwidth, $this->FontSize); 2175 } 2176 } 2177 $size = ['w' => $bsw, 'h' => $bsh]; 2178 } else { 2179 $size = false; 2180 } // mPDF 6 2181 if (preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $properties['BACKGROUND-IMAGE'])) { 2182 return ['gradient' => $properties['BACKGROUND-IMAGE'], 'origin' => $origin, 'size' => $size]; 2183 } else { 2184 $file = $properties['BACKGROUND-IMAGE']; 2185 $sizesarray = $this->Image($file, 0, 0, 0, 0, '', '', false, false, false, false, true); 2186 if (isset($sizesarray['IMAGE_ID'])) { 2187 $image_id = $sizesarray['IMAGE_ID']; 2188 $orig_w = $sizesarray['WIDTH'] * Mpdf::SCALE; // in user units i.e. mm 2189 $orig_h = $sizesarray['HEIGHT'] * Mpdf::SCALE; // (using $this->img_dpi) 2190 if (isset($properties['BACKGROUND-IMAGE-RESOLUTION'])) { 2191 if (preg_match('/from-image/i', $properties['BACKGROUND-IMAGE-RESOLUTION']) && isset($sizesarray['set-dpi']) && $sizesarray['set-dpi'] > 0) { 2192 $orig_w *= $this->img_dpi / $sizesarray['set-dpi']; 2193 $orig_h *= $this->img_dpi / $sizesarray['set-dpi']; 2194 } elseif (preg_match('/(\d+)dpi/i', $properties['BACKGROUND-IMAGE-RESOLUTION'], $m)) { 2195 $dpi = $m[1]; 2196 if ($dpi > 0) { 2197 $orig_w *= $this->img_dpi / $dpi; 2198 $orig_h *= $this->img_dpi / $dpi; 2199 } 2200 } 2201 } 2202 $x_repeat = true; 2203 $y_repeat = true; 2204 if (isset($properties['BACKGROUND-REPEAT'])) { 2205 if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-x') { 2206 $y_repeat = false; 2207 } 2208 if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-y') { 2209 $x_repeat = false; 2210 } 2211 } 2212 $x_pos = 0; 2213 $y_pos = 0; 2214 if (isset($properties['BACKGROUND-POSITION'])) { 2215 $ppos = preg_split('/\s+/', $properties['BACKGROUND-POSITION']); 2216 $x_pos = $ppos[0]; 2217 $y_pos = $ppos[1]; 2218 if (!stristr($x_pos, '%')) { 2219 $x_pos = $this->sizeConverter->convert($x_pos, $maxwidth, $this->FontSize); 2220 } 2221 if (!stristr($y_pos, '%')) { 2222 $y_pos = $this->sizeConverter->convert($y_pos, $maxwidth, $this->FontSize); 2223 } 2224 } 2225 if (isset($properties['BACKGROUND-IMAGE-RESIZE'])) { 2226 $resize = $properties['BACKGROUND-IMAGE-RESIZE']; 2227 } else { 2228 $resize = 0; 2229 } 2230 if (isset($properties['BACKGROUND-IMAGE-OPACITY'])) { 2231 $opacity = $properties['BACKGROUND-IMAGE-OPACITY']; 2232 } else { 2233 $opacity = 1; 2234 } 2235 return ['image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $sizesarray['itype'], 'origin' => $origin, 'size' => $size]; 2236 } 2237 } 2238 return false; 2239 } 2240 2241 /* -- END BACKGROUNDS -- */ 2242 2243 function PrintBodyBackgrounds() 2244 { 2245 $s = ''; 2246 $clx = 0; 2247 $cly = 0; 2248 $clw = $this->w; 2249 $clh = $this->h; 2250 // If using bleed and trim margins in paged media 2251 if ($this->pageDim[$this->page]['outer_width_LR'] || $this->pageDim[$this->page]['outer_width_TB']) { 2252 $clx = $this->pageDim[$this->page]['outer_width_LR'] - $this->pageDim[$this->page]['bleedMargin']; 2253 $cly = $this->pageDim[$this->page]['outer_width_TB'] - $this->pageDim[$this->page]['bleedMargin']; 2254 $clw = $this->w - 2 * $clx; 2255 $clh = $this->h - 2 * $cly; 2256 } 2257 2258 if ($this->bodyBackgroundColor) { 2259 $s .= 'q ' . $this->SetFColor($this->bodyBackgroundColor, true) . "\n"; 2260 if ($this->bodyBackgroundColor[0] == 5) { // RGBa 2261 $s .= $this->SetAlpha(ord($this->bodyBackgroundColor[4]) / 100, 'Normal', true, 'F') . "\n"; 2262 } elseif ($this->bodyBackgroundColor[0] == 6) { // CMYKa 2263 $s .= $this->SetAlpha(ord($this->bodyBackgroundColor[5]) / 100, 'Normal', true, 'F') . "\n"; 2264 } 2265 $s .= sprintf('%.3F %.3F %.3F %.3F re f Q', ($clx * Mpdf::SCALE), ($cly * Mpdf::SCALE), $clw * Mpdf::SCALE, $clh * Mpdf::SCALE) . "\n"; 2266 } 2267 2268 /* -- BACKGROUNDS -- */ 2269 if ($this->bodyBackgroundGradient) { 2270 $g = $this->gradient->parseBackgroundGradient($this->bodyBackgroundGradient); 2271 if ($g) { 2272 $s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, (isset($g['gradtype']) ? $g['gradtype'] : null), $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true); 2273 } 2274 } 2275 if ($this->bodyBackgroundImage) { 2276 if (isset($this->bodyBackgroundImage['gradient']) && $this->bodyBackgroundImage['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->bodyBackgroundImage['gradient'])) { 2277 $g = $this->gradient->parseMozGradient($this->bodyBackgroundImage['gradient']); 2278 if ($g) { 2279 $s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true); 2280 } 2281 } elseif ($this->bodyBackgroundImage['image_id']) { // Background pattern 2282 $n = count($this->patterns) + 1; 2283 // If using resize, uses TrimBox (not including the bleed) 2284 list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($this->bodyBackgroundImage['orig_w'], $this->bodyBackgroundImage['orig_h'], $clw, $clh, $this->bodyBackgroundImage['resize'], $this->bodyBackgroundImage['x_repeat'], $this->bodyBackgroundImage['y_repeat']); 2285 2286 $this->patterns[$n] = ['x' => $clx, 'y' => $cly, 'w' => $clw, 'h' => $clh, 'pgh' => $this->h, 'image_id' => $this->bodyBackgroundImage['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $this->bodyBackgroundImage['x_pos'], 'y_pos' => $this->bodyBackgroundImage['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $this->bodyBackgroundImage['itype']]; 2287 if (($this->bodyBackgroundImage['opacity'] > 0 || $this->bodyBackgroundImage['opacity'] === '0') && $this->bodyBackgroundImage['opacity'] < 1) { 2288 $opac = $this->SetAlpha($this->bodyBackgroundImage['opacity'], 'Normal', true); 2289 } else { 2290 $opac = ''; 2291 } 2292 $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, ($clx * Mpdf::SCALE), ($cly * Mpdf::SCALE), $clw * Mpdf::SCALE, $clh * Mpdf::SCALE) . "\n"; 2293 } 2294 } 2295 /* -- END BACKGROUNDS -- */ 2296 return $s; 2297 } 2298 2299 function _setClippingPath($clx, $cly, $clw, $clh) 2300 { 2301 $s = ' q 0 w '; // Line width=0 2302 $s .= sprintf('%.3F %.3F m ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // start point TL before the arc 2303 $s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BL 2304 $s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BR 2305 $s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TR 2306 $s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TL 2307 $s .= ' W n '; // Ends path no-op & Sets the clipping path 2308 return $s; 2309 } 2310 2311 function PrintPageBackgrounds($adjustmenty = 0) 2312 { 2313 $s = ''; 2314 2315 ksort($this->pageBackgrounds); 2316 2317 foreach ($this->pageBackgrounds as $bl => $pbs) { 2318 2319 foreach ($pbs as $pb) { 2320 2321 if ((!isset($pb['image_id']) && !isset($pb['gradient'])) || isset($pb['shadowonly'])) { // Background colour or boxshadow 2322 2323 if ($pb['z-index'] > 0) { 2324 $this->current_layer = $pb['z-index']; 2325 $s .= "\n" . '/OCBZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n"; 2326 } 2327 2328 if ($pb['visibility'] != 'visible') { 2329 if ($pb['visibility'] == 'printonly') { 2330 $s .= '/OC /OC1 BDC' . "\n"; 2331 } elseif ($pb['visibility'] == 'screenonly') { 2332 $s .= '/OC /OC2 BDC' . "\n"; 2333 } elseif ($pb['visibility'] == 'hidden') { 2334 $s .= '/OC /OC3 BDC' . "\n"; 2335 } 2336 } 2337 2338 // Box shadow 2339 if (isset($pb['shadow']) && $pb['shadow']) { 2340 $s .= $pb['shadow'] . "\n"; 2341 } 2342 2343 if (isset($pb['clippath']) && $pb['clippath']) { 2344 $s .= $pb['clippath'] . "\n"; 2345 } 2346 2347 $s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n"; 2348 2349 if ($pb['col'] && $pb['col'][0] === '5') { // RGBa 2350 $s .= $this->SetAlpha(ord($pb['col'][4]) / 100, 'Normal', true, 'F') . "\n"; 2351 } elseif ($pb['col'] && $pb['col'][0] === '6') { // CMYKa 2352 $s .= $this->SetAlpha(ord($pb['col'][5]) / 100, 'Normal', true, 'F') . "\n"; 2353 } 2354 2355 $s .= sprintf('%.3F %.3F %.3F %.3F re f Q', $pb['x'] * Mpdf::SCALE, ($this->h - $pb['y']) * Mpdf::SCALE, $pb['w'] * Mpdf::SCALE, -$pb['h'] * Mpdf::SCALE) . "\n"; 2356 2357 if (isset($pb['clippath']) && $pb['clippath']) { 2358 $s .= 'Q' . "\n"; 2359 } 2360 2361 if ($pb['visibility'] != 'visible') { 2362 $s .= 'EMC' . "\n"; 2363 } 2364 2365 if ($pb['z-index'] > 0) { 2366 $s .= "\n" . 'EMCBZ-index' . "\n"; 2367 $this->current_layer = 0; 2368 } 2369 } 2370 } 2371 2372 /* -- BACKGROUNDS -- */ 2373 foreach ($pbs as $pb) { 2374 2375 if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) { 2376 2377 if ($pb['z-index'] > 0) { 2378 $this->current_layer = $pb['z-index']; 2379 $s .= "\n" . '/OCGZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n"; 2380 } 2381 2382 if ($pb['visibility'] != 'visible') { 2383 if ($pb['visibility'] == 'printonly') { 2384 $s .= '/OC /OC1 BDC' . "\n"; 2385 } elseif ($pb['visibility'] == 'screenonly') { 2386 $s .= '/OC /OC2 BDC' . "\n"; 2387 } elseif ($pb['visibility'] == 'hidden') { 2388 $s .= '/OC /OC3 BDC' . "\n"; 2389 } 2390 } 2391 2392 } 2393 2394 if (isset($pb['gradient']) && $pb['gradient']) { 2395 2396 if (isset($pb['clippath']) && $pb['clippath']) { 2397 $s .= $pb['clippath'] . "\n"; 2398 } 2399 2400 $s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true); 2401 2402 if (isset($pb['clippath']) && $pb['clippath']) { 2403 $s .= 'Q' . "\n"; 2404 } 2405 2406 } elseif (isset($pb['image_id']) && $pb['image_id']) { // Background Image 2407 2408 $pb['y'] -= $adjustmenty; 2409 $pb['h'] += $adjustmenty; 2410 $n = count($this->patterns) + 1; 2411 2412 list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($pb['orig_w'], $pb['orig_h'], $pb['w'], $pb['h'], $pb['resize'], $pb['x_repeat'], $pb['y_repeat'], $pb['bpa'], $pb['size']); 2413 2414 $this->patterns[$n] = ['x' => $pb['x'], 'y' => $pb['y'], 'w' => $pb['w'], 'h' => $pb['h'], 'pgh' => $this->h, 'image_id' => $pb['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $pb['x_pos'], 'y_pos' => $pb['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $pb['itype'], 'bpa' => $pb['bpa']]; 2415 2416 $x = $pb['x'] * Mpdf::SCALE; 2417 $y = ($this->h - $pb['y']) * Mpdf::SCALE; 2418 $w = $pb['w'] * Mpdf::SCALE; 2419 $h = -$pb['h'] * Mpdf::SCALE; 2420 2421 if (isset($pb['clippath']) && $pb['clippath']) { 2422 $s .= $pb['clippath'] . "\n"; 2423 } 2424 2425 if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern 2426 2427 $iw = $pb['orig_w'] / Mpdf::SCALE; 2428 $ih = $pb['orig_h'] / Mpdf::SCALE; 2429 2430 $w = $pb['w']; 2431 $h = $pb['h']; 2432 $x0 = $pb['x']; 2433 $y0 = $pb['y']; 2434 2435 if (isset($pb['bpa']) && $pb['bpa']) { 2436 $w = $pb['bpa']['w']; 2437 $h = $pb['bpa']['h']; 2438 $x0 = $pb['bpa']['x']; 2439 $y0 = $pb['bpa']['y']; 2440 } 2441 2442 if (isset($pb['size']['w']) && $pb['size']['w']) { 2443 $size = $pb['size']; 2444 2445 if ($size['w'] == 'contain') { 2446 // Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest 2447 // size such that both its width and its height can fit inside the background positioning area. 2448 // Same as resize==3 2449 $ih = $ih * $pb['bpa']['w'] / $iw; 2450 $iw = $pb['bpa']['w']; 2451 if ($ih > $pb['bpa']['h']) { 2452 $iw = $iw * $pb['bpa']['h'] / $ih; 2453 $ih = $pb['bpa']['h']; 2454 } 2455 } elseif ($size['w'] == 'cover') { 2456 // Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest 2457 // size such that both its width and its height can completely cover the background positioning area. 2458 $ih = $ih * $pb['bpa']['w'] / $iw; 2459 $iw = $pb['bpa']['w']; 2460 if ($ih < $pb['bpa']['h']) { 2461 $iw = $iw * $ih / $pb['bpa']['h']; 2462 $ih = $pb['bpa']['h']; 2463 } 2464 } else { 2465 2466 if (NumericString::containsPercentChar($size['w'])) { 2467 $size['w'] = NumericString::removePercentChar($size['w']); 2468 $size['w'] /= 100; 2469 $size['w'] = ($pb['bpa']['w'] * $size['w']); 2470 } 2471 2472 if (NumericString::containsPercentChar($size['h'])) { 2473 $size['h'] = NumericString::removePercentChar($size['h']); 2474 $size['h'] /= 100; 2475 $size['h'] = ($pb['bpa']['h'] * $size['h']); 2476 } 2477 2478 if ($size['w'] == 'auto' && $size['h'] == 'auto') { 2479 $iw = $iw; 2480 $ih = $ih; 2481 } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') { 2482 $iw = $iw * $size['h'] / $ih; 2483 $ih = $size['h']; 2484 } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') { 2485 $ih = $ih * $size['w'] / $iw; 2486 $iw = $size['w']; 2487 } else { 2488 $iw = $size['w']; 2489 $ih = $size['h']; 2490 } 2491 } 2492 } 2493 2494 // Number to repeat 2495 if ($pb['x_repeat']) { 2496 $nx = ceil($pb['w'] / $iw) + 1; 2497 } else { 2498 $nx = 1; 2499 } 2500 2501 if ($pb['y_repeat']) { 2502 $ny = ceil($pb['h'] / $ih) + 1; 2503 } else { 2504 $ny = 1; 2505 } 2506 2507 $x_pos = $pb['x_pos']; 2508 if (stristr($x_pos, '%')) { 2509 $x_pos = (float) $x_pos; 2510 $x_pos /= 100; 2511 $x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos); 2512 } 2513 2514 $y_pos = $pb['y_pos']; 2515 2516 if (stristr($y_pos, '%')) { 2517 $y_pos = (float) $y_pos; 2518 $y_pos /= 100; 2519 $y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos); 2520 } 2521 2522 if ($nx > 1) { 2523 while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) { 2524 $x_pos -= $iw; 2525 } 2526 } 2527 2528 if ($ny > 1) { 2529 while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) { 2530 $y_pos -= $ih; 2531 } 2532 } 2533 2534 for ($xi = 0; $xi < $nx; $xi++) { 2535 for ($yi = 0; $yi < $ny; $yi++) { 2536 $x = $x0 + $x_pos + ($iw * $xi); 2537 $y = $y0 + $y_pos + ($ih * $yi); 2538 if ($pb['opacity'] > 0 && $pb['opacity'] < 1) { 2539 $opac = $this->SetAlpha($pb['opacity'], 'Normal', true); 2540 } else { 2541 $opac = ''; 2542 } 2543 $s .= sprintf("q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $opac, $iw * Mpdf::SCALE, $ih * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $ih)) * Mpdf::SCALE, $pb['image_id']) . "\n"; 2544 } 2545 } 2546 2547 } else { 2548 if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) { 2549 $opac = $this->SetAlpha($pb['opacity'], 'Normal', true); 2550 } else { 2551 $opac = ''; 2552 } 2553 $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n"; 2554 } 2555 2556 if (isset($pb['clippath']) && $pb['clippath']) { 2557 $s .= 'Q' . "\n"; 2558 } 2559 } 2560 2561 if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) { 2562 if ($pb['visibility'] != 'visible') { 2563 $s .= 'EMC' . "\n"; 2564 } 2565 2566 if ($pb['z-index'] > 0) { 2567 $s .= "\n" . 'EMCGZ-index' . "\n"; 2568 $this->current_layer = 0; 2569 } 2570 } 2571 } 2572 /* -- END BACKGROUNDS -- */ 2573 } 2574 2575 return $s; 2576 } 2577 2578 function PrintTableBackgrounds($adjustmenty = 0) 2579 { 2580 $s = ''; 2581 /* -- BACKGROUNDS -- */ 2582 ksort($this->tableBackgrounds); 2583 foreach ($this->tableBackgrounds as $bl => $pbs) { 2584 foreach ($pbs as $pb) { 2585 if ((!isset($pb['gradient']) || !$pb['gradient']) && (!isset($pb['image_id']) || !$pb['image_id'])) { 2586 $s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n"; 2587 if ($pb['col'][0] == 5) { // RGBa 2588 $s .= $this->SetAlpha(ord($pb['col'][4]) / 100, 'Normal', true, 'F') . "\n"; 2589 } elseif ($pb['col'][0] == 6) { // CMYKa 2590 $s .= $this->SetAlpha(ord($pb['col'][5]) / 100, 'Normal', true, 'F') . "\n"; 2591 } 2592 $s .= sprintf('%.3F %.3F %.3F %.3F re %s Q', $pb['x'] * Mpdf::SCALE, ($this->h - $pb['y']) * Mpdf::SCALE, $pb['w'] * Mpdf::SCALE, -$pb['h'] * Mpdf::SCALE, 'f') . "\n"; 2593 } 2594 if (isset($pb['gradient']) && $pb['gradient']) { 2595 if (isset($pb['clippath']) && $pb['clippath']) { 2596 $s .= $pb['clippath'] . "\n"; 2597 } 2598 $s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true); 2599 if (isset($pb['clippath']) && $pb['clippath']) { 2600 $s .= 'Q' . "\n"; 2601 } 2602 } 2603 if (isset($pb['image_id']) && $pb['image_id']) { // Background pattern 2604 $pb['y'] -= $adjustmenty; 2605 $pb['h'] += $adjustmenty; 2606 $n = count($this->patterns) + 1; 2607 list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($pb['orig_w'], $pb['orig_h'], $pb['w'], $pb['h'], $pb['resize'], $pb['x_repeat'], $pb['y_repeat']); 2608 $this->patterns[$n] = ['x' => $pb['x'], 'y' => $pb['y'], 'w' => $pb['w'], 'h' => $pb['h'], 'pgh' => $this->h, 'image_id' => $pb['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $pb['x_pos'], 'y_pos' => $pb['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $pb['itype']]; 2609 $x = $pb['x'] * Mpdf::SCALE; 2610 $y = ($this->h - $pb['y']) * Mpdf::SCALE; 2611 $w = $pb['w'] * Mpdf::SCALE; 2612 $h = -$pb['h'] * Mpdf::SCALE; 2613 2614 // mPDF 5.7.3 2615 if (($this->writingHTMLfooter || $this->writingHTMLheader) && (!isset($pb['clippath']) || $pb['clippath'] == '')) { 2616 // Set clipping path 2617 $pb['clippath'] = sprintf(' q 0 w %.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l W n ', $x, $y, $x, $y + $h, $x + $w, $y + $h, $x + $w, $y, $x, $y); 2618 } 2619 2620 if (isset($pb['clippath']) && $pb['clippath']) { 2621 $s .= $pb['clippath'] . "\n"; 2622 } 2623 2624 // mPDF 5.7.3 2625 if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern 2626 $iw = $pb['orig_w'] / Mpdf::SCALE; 2627 $ih = $pb['orig_h'] / Mpdf::SCALE; 2628 2629 $w = $pb['w']; 2630 $h = $pb['h']; 2631 $x0 = $pb['x']; 2632 $y0 = $pb['y']; 2633 2634 if (isset($pb['bpa']) && $pb['bpa']) { 2635 $w = $pb['bpa']['w']; 2636 $h = $pb['bpa']['h']; 2637 $x0 = $pb['bpa']['x']; 2638 $y0 = $pb['bpa']['y']; 2639 } // At present 'bpa' (background page area) is not set for tablebackgrounds - only pagebackgrounds 2640 // For now, just set it as: 2641 else { 2642 $pb['bpa'] = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h]; 2643 } 2644 2645 if (isset($pb['size']['w']) && $pb['size']['w']) { 2646 $size = $pb['size']; 2647 2648 if ($size['w'] == 'contain') { 2649 // Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area. 2650 // Same as resize==3 2651 $ih = $ih * $pb['bpa']['w'] / $iw; 2652 $iw = $pb['bpa']['w']; 2653 if ($ih > $pb['bpa']['h']) { 2654 $iw = $iw * $pb['bpa']['h'] / $ih; 2655 $ih = $pb['bpa']['h']; 2656 } 2657 } elseif ($size['w'] == 'cover') { 2658 // Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area. 2659 $ih = $ih * $pb['bpa']['w'] / $iw; 2660 $iw = $pb['bpa']['w']; 2661 if ($ih < $pb['bpa']['h']) { 2662 $iw = $iw * $ih / $pb['bpa']['h']; 2663 $ih = $pb['bpa']['h']; 2664 } 2665 } else { 2666 if (NumericString::containsPercentChar($size['w'])) { 2667 $size['w'] = NumericString::removePercentChar($size['w']); 2668 $size['w'] /= 100; 2669 $size['w'] = ($pb['bpa']['w'] * $size['w']); 2670 } 2671 if (NumericString::containsPercentChar($size['h'])) { 2672 $size['h'] = NumericString::removePercentChar($size['h']); 2673 $size['h'] /= 100; 2674 $size['h'] = ($pb['bpa']['h'] * $size['h']); 2675 } 2676 if ($size['w'] == 'auto' && $size['h'] == 'auto') { 2677 $iw = $iw; 2678 $ih = $ih; 2679 } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') { 2680 $iw = $iw * $size['h'] / $ih; 2681 $ih = $size['h']; 2682 } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') { 2683 $ih = $ih * $size['w'] / $iw; 2684 $iw = $size['w']; 2685 } else { 2686 $iw = $size['w']; 2687 $ih = $size['h']; 2688 } 2689 } 2690 } 2691 2692 // Number to repeat 2693 if (isset($pb['x_repeat']) && $pb['x_repeat']) { 2694 $nx = ceil($pb['w'] / $iw) + 1; 2695 } else { 2696 $nx = 1; 2697 } 2698 if (isset($pb['y_repeat']) && $pb['y_repeat']) { 2699 $ny = ceil($pb['h'] / $ih) + 1; 2700 } else { 2701 $ny = 1; 2702 } 2703 2704 $x_pos = $pb['x_pos']; 2705 if (NumericString::containsPercentChar($x_pos)) { 2706 $x_pos = NumericString::removePercentChar($x_pos); 2707 $x_pos /= 100; 2708 $x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos); 2709 } 2710 $y_pos = $pb['y_pos']; 2711 if (NumericString::containsPercentChar($y_pos)) { 2712 $y_pos = NumericString::removePercentChar($y_pos); 2713 $y_pos /= 100; 2714 $y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos); 2715 } 2716 if ($nx > 1) { 2717 while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) { 2718 $x_pos -= $iw; 2719 } 2720 } 2721 if ($ny > 1) { 2722 while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) { 2723 $y_pos -= $ih; 2724 } 2725 } 2726 for ($xi = 0; $xi < $nx; $xi++) { 2727 for ($yi = 0; $yi < $ny; $yi++) { 2728 $x = $x0 + $x_pos + ($iw * $xi); 2729 $y = $y0 + $y_pos + ($ih * $yi); 2730 if ($pb['opacity'] > 0 && $pb['opacity'] < 1) { 2731 $opac = $this->SetAlpha($pb['opacity'], 'Normal', true); 2732 } else { 2733 $opac = ''; 2734 } 2735 $s .= sprintf("q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $opac, $iw * Mpdf::SCALE, $ih * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $ih)) * Mpdf::SCALE, $pb['image_id']) . "\n"; 2736 } 2737 } 2738 } else { 2739 if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) { 2740 $opac = $this->SetAlpha($pb['opacity'], 'Normal', true); 2741 } else { 2742 $opac = ''; 2743 } 2744 $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n"; 2745 } 2746 2747 if (isset($pb['clippath']) && $pb['clippath']) { 2748 $s .= 'Q' . "\n"; 2749 } 2750 } 2751 } 2752 } 2753 /* -- END BACKGROUNDS -- */ 2754 return $s; 2755 } 2756 2757 function BeginLayer($id) 2758 { 2759 if ($this->current_layer > 0) { 2760 $this->EndLayer(); 2761 } 2762 if ($id < 1) { 2763 return false; 2764 } 2765 if (!isset($this->layers[$id])) { 2766 $this->layers[$id] = ['name' => 'Layer ' . ($id)]; 2767 if (($this->PDFA || $this->PDFX)) { 2768 $this->PDFAXwarnings[] = "Cannot use layers when using PDFA or PDFX"; 2769 return ''; 2770 } elseif (!$this->PDFA && !$this->PDFX) { 2771 $this->pdf_version = '1.5'; 2772 } 2773 } 2774 $this->current_layer = $id; 2775 $this->writer->write('/OCZ-index /ZI' . $id . ' BDC'); 2776 2777 $this->pageoutput[$this->page] = []; 2778 } 2779 2780 function EndLayer() 2781 { 2782 if ($this->current_layer > 0) { 2783 $this->writer->write('EMCZ-index'); 2784 $this->current_layer = 0; 2785 } 2786 } 2787 2788 function AddPageByArray($a) 2789 { 2790 if (!is_array($a)) { 2791 $a = []; 2792 } 2793 2794 $orientation = (isset($a['orientation']) ? $a['orientation'] : ''); 2795 $condition = (isset($a['condition']) ? $a['condition'] : (isset($a['type']) ? $a['type'] : '')); 2796 $resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : ''); 2797 $pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : ''); 2798 $suppress = (isset($a['suppress']) ? $a['suppress'] : ''); 2799 $mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : '')); 2800 $mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : '')); 2801 $mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : '')); 2802 $mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : '')); 2803 $mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : '')); 2804 $mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : '')); 2805 $ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : '')); 2806 $ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : '')); 2807 $ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : '')); 2808 $efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : '')); 2809 $ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0)); 2810 $ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0)); 2811 $ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0)); 2812 $efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0)); 2813 $pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : '')); 2814 $newformat = (isset($a['newformat']) ? $a['newformat'] : (isset($a['sheet-size']) ? $a['sheet-size'] : '')); 2815 2816 $this->AddPage($orientation, $condition, $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat); 2817 } 2818 2819 // mPDF 6 pagebreaktype 2820 function _preForcedPagebreak($pagebreaktype) 2821 { 2822 if ($pagebreaktype == 'cloneall') { 2823 // Close any open block tags 2824 $arr = []; 2825 $ai = 0; 2826 for ($b = $this->blklvl; $b > 0; $b--) { 2827 $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai); 2828 } 2829 if ($this->blklvl == 0 && !empty($this->textbuffer)) { // Output previously buffered content 2830 $this->printbuffer($this->textbuffer, 1); 2831 $this->textbuffer = []; 2832 } 2833 } elseif ($pagebreaktype == 'clonebycss') { 2834 // Close open block tags whilst box-decoration-break==clone 2835 $arr = []; 2836 $ai = 0; 2837 for ($b = $this->blklvl; $b > 0; $b--) { 2838 if (isset($this->blk[$b]['box_decoration_break']) && $this->blk[$b]['box_decoration_break'] == 'clone') { 2839 $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai); 2840 } else { 2841 if ($b == $this->blklvl && !empty($this->textbuffer)) { // Output previously buffered content 2842 $this->printbuffer($this->textbuffer, 1); 2843 $this->textbuffer = []; 2844 } 2845 break; 2846 } 2847 } 2848 } elseif (!empty($this->textbuffer)) { // Output previously buffered content 2849 $this->printbuffer($this->textbuffer, 1); 2850 $this->textbuffer = []; 2851 } 2852 } 2853 2854 // mPDF 6 pagebreaktype 2855 function _postForcedPagebreak($pagebreaktype, $startpage, $save_blk, $save_blklvl) 2856 { 2857 if ($pagebreaktype == 'cloneall') { 2858 $this->blk = []; 2859 $this->blk[0] = $save_blk[0]; 2860 // Re-open block tags 2861 $this->blklvl = 0; 2862 $arr = []; 2863 $i = 0; 2864 for ($b = 1; $b <= $save_blklvl; $b++) { 2865 $this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i); 2866 } 2867 } elseif ($pagebreaktype == 'clonebycss') { 2868 $this->blk = []; 2869 $this->blk[0] = $save_blk[0]; 2870 // Don't re-open tags for lowest level elements - so need to do some adjustments 2871 for ($b = 1; $b <= $this->blklvl; $b++) { 2872 $this->blk[$b] = $save_blk[$b]; 2873 $this->blk[$b]['startpage'] = 0; 2874 $this->blk[$b]['y0'] = $this->y; // ?? $this->tMargin 2875 if (($this->page - $startpage) % 2) { 2876 if (isset($this->blk[$b]['x0'])) { 2877 $this->blk[$b]['x0'] += $this->MarginCorrection; 2878 } else { 2879 $this->blk[$b]['x0'] = $this->MarginCorrection; 2880 } 2881 } 2882 // for Float DIV 2883 $this->blk[$b]['marginCorrected'][$this->page] = true; 2884 } 2885 2886 // Re-open block tags for any that have box_decoration_break==clone 2887 $arr = []; 2888 $i = 0; 2889 for ($b = $this->blklvl + 1; $b <= $save_blklvl; $b++) { 2890 if ($b < $this->blklvl) { 2891 $this->lastblocklevelchange = -1; 2892 } 2893 $this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i); 2894 } 2895 if ($this->blk[$this->blklvl]['box_decoration_break'] != 'clone') { 2896 $this->lastblocklevelchange = -1; 2897 } 2898 } else { 2899 $this->lastblocklevelchange = -1; 2900 } 2901 } 2902 2903 function AddPage( 2904 $orientation = '', 2905 $condition = '', 2906 $resetpagenum = '', 2907 $pagenumstyle = '', 2908 $suppress = '', 2909 $mgl = '', 2910 $mgr = '', 2911 $mgt = '', 2912 $mgb = '', 2913 $mgh = '', 2914 $mgf = '', 2915 $ohname = '', 2916 $ehname = '', 2917 $ofname = '', 2918 $efname = '', 2919 $ohvalue = 0, 2920 $ehvalue = 0, 2921 $ofvalue = 0, 2922 $efvalue = 0, 2923 $pagesel = '', 2924 $newformat = '' 2925 ) { 2926 /* -- CSS-FLOAT -- */ 2927 // Float DIV 2928 // Cannot do with columns on, or if any change in page orientation/margins etc. 2929 // If next page already exists - i.e background /headers and footers already written 2930 if ($this->state > 0 && $this->page < count($this->pages)) { 2931 $bak_cml = $this->cMarginL; 2932 $bak_cmr = $this->cMarginR; 2933 $bak_dw = $this->divwidth; 2934 // Paint Div Border if necessary 2935 if ($this->blklvl > 0) { 2936 $save_tr = $this->table_rotate; // *TABLES* 2937 $this->table_rotate = 0; // *TABLES* 2938 if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0']) { 2939 $this->blk[$this->blklvl]['startpage'] ++; 2940 } 2941 if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table']) { 2942 $toplvl = $this->blklvl; 2943 } else { 2944 $toplvl = $this->blklvl - 1; 2945 } 2946 $sy = $this->y; 2947 for ($bl = 1; $bl <= $toplvl; $bl++) { 2948 $this->PaintDivBB('pagebottom', 0, $bl); 2949 } 2950 $this->y = $sy; 2951 $this->table_rotate = $save_tr; // *TABLES* 2952 } 2953 $s = $this->PrintPageBackgrounds(); 2954 2955 // Writes after the marker so not overwritten later by page background etc. 2956 $this->pages[$this->page] = preg_replace( 2957 '/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', 2958 '\\1' . "\n" . $s . "\n", 2959 $this->pages[$this->page] 2960 ); 2961 2962 $this->pageBackgrounds = []; 2963 $family = $this->FontFamily; 2964 $style = $this->FontStyle; 2965 $size = $this->FontSizePt; 2966 $lw = $this->LineWidth; 2967 $dc = $this->DrawColor; 2968 $fc = $this->FillColor; 2969 $tc = $this->TextColor; 2970 $cf = $this->ColorFlag; 2971 2972 $this->printfloatbuffer(); 2973 2974 // Move to next page 2975 $this->page++; 2976 2977 $this->ResetMargins(); 2978 $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin); 2979 $this->x = $this->lMargin; 2980 $this->y = $this->tMargin; 2981 $this->FontFamily = ''; 2982 $this->writer->write('2 J'); 2983 $this->LineWidth = $lw; 2984 $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE)); 2985 2986 if ($family) { 2987 $this->SetFont($family, $style, $size, true, true); 2988 } 2989 2990 $this->DrawColor = $dc; 2991 2992 if ($dc != $this->defDrawColor) { 2993 $this->writer->write($dc); 2994 } 2995 2996 $this->FillColor = $fc; 2997 2998 if ($fc != $this->defFillColor) { 2999 $this->writer->write($fc); 3000 } 3001 3002 $this->TextColor = $tc; 3003 $this->ColorFlag = $cf; 3004 3005 for ($bl = 1; $bl <= $this->blklvl; $bl++) { 3006 $this->blk[$bl]['y0'] = $this->y; 3007 // Don't correct more than once for background DIV containing a Float 3008 if (!isset($this->blk[$bl]['marginCorrected'][$this->page])) { 3009 if (isset($this->blk[$bl]['x0'])) { 3010 $this->blk[$bl]['x0'] += $this->MarginCorrection; 3011 } else { 3012 $this->blk[$bl]['x0'] = $this->MarginCorrection; 3013 } 3014 } 3015 $this->blk[$bl]['marginCorrected'][$this->page] = true; 3016 } 3017 3018 $this->cMarginL = $bak_cml; 3019 $this->cMarginR = $bak_cmr; 3020 $this->divwidth = $bak_dw; 3021 3022 return ''; 3023 } 3024 /* -- END CSS-FLOAT -- */ 3025 3026 // Start a new page 3027 if ($this->state == 0) { 3028 $this->Open(); 3029 } 3030 3031 $bak_cml = $this->cMarginL; 3032 $bak_cmr = $this->cMarginR; 3033 $bak_dw = $this->divwidth; 3034 3035 $bak_lh = $this->lineheight; 3036 3037 $orientation = substr(strtoupper($orientation), 0, 1); 3038 $condition = strtoupper($condition); 3039 3040 3041 if ($condition == 'E') { // only adds new page if needed to create an Even page 3042 if (!$this->mirrorMargins || ($this->page) % 2 == 0) { 3043 return false; 3044 } 3045 } elseif ($condition == 'O') { // only adds new page if needed to create an Odd page 3046 if (!$this->mirrorMargins || ($this->page) % 2 == 1) { 3047 return false; 3048 } 3049 } elseif ($condition == 'NEXT-EVEN') { // always adds at least one new page to create an Even page 3050 if (!$this->mirrorMargins) { 3051 $condition = ''; 3052 } else { 3053 if ($pagesel) { 3054 $pbch = $pagesel; 3055 $pagesel = ''; 3056 } // *CSS-PAGE* 3057 else { 3058 $pbch = false; 3059 } // *CSS-PAGE* 3060 $this->AddPage($this->CurOrientation, 'O'); 3061 $this->extrapagebreak = true; // mPDF 6 pagebreaktype 3062 if ($pbch) { 3063 $pagesel = $pbch; 3064 } // *CSS-PAGE* 3065 $condition = ''; 3066 } 3067 } elseif ($condition == 'NEXT-ODD') { // always adds at least one new page to create an Odd page 3068 if (!$this->mirrorMargins) { 3069 $condition = ''; 3070 } else { 3071 if ($pagesel) { 3072 $pbch = $pagesel; 3073 $pagesel = ''; 3074 } // *CSS-PAGE* 3075 else { 3076 $pbch = false; 3077 } // *CSS-PAGE* 3078 $this->AddPage($this->CurOrientation, 'E'); 3079 $this->extrapagebreak = true; // mPDF 6 pagebreaktype 3080 if ($pbch) { 3081 $pagesel = $pbch; 3082 } // *CSS-PAGE* 3083 $condition = ''; 3084 } 3085 } 3086 3087 if ($resetpagenum || $pagenumstyle || $suppress) { 3088 $this->PageNumSubstitutions[] = ['from' => ($this->page + 1), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress]; 3089 } 3090 3091 $save_tr = $this->table_rotate; // *TABLES* 3092 $this->table_rotate = 0; // *TABLES* 3093 $save_kwt = $this->kwt; 3094 $this->kwt = 0; 3095 $save_layer = $this->current_layer; 3096 $save_vis = $this->visibility; 3097 3098 if ($this->visibility != 'visible') { 3099 $this->SetVisibility('visible'); 3100 } 3101 3102 $this->EndLayer(); 3103 3104 // Paint Div Border if necessary 3105 // PAINTS BACKGROUND COLOUR OR BORDERS for DIV - DISABLED FOR COLUMNS (cf. AcceptPageBreak) AT PRESENT in ->PaintDivBB 3106 if (!$this->ColActive && $this->blklvl > 0) { 3107 if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0'] && !$this->extrapagebreak) { // mPDF 6 pagebreaktype 3108 if (isset($this->blk[$this->blklvl]['startpage'])) { 3109 $this->blk[$this->blklvl]['startpage'] ++; 3110 } else { 3111 $this->blk[$this->blklvl]['startpage'] = 1; 3112 } 3113 } 3114 if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table'] || $this->extrapagebreak) { 3115 $toplvl = $this->blklvl; 3116 } // mPDF 6 pagebreaktype 3117 else { 3118 $toplvl = $this->blklvl - 1; 3119 } 3120 $sy = $this->y; 3121 for ($bl = 1; $bl <= $toplvl; $bl++) { 3122 if (isset($this->blk[$bl]['z-index']) && $this->blk[$bl]['z-index'] > 0) { 3123 $this->BeginLayer($this->blk[$bl]['z-index']); 3124 } 3125 if (isset($this->blk[$bl]['visibility']) && $this->blk[$bl]['visibility'] && $this->blk[$bl]['visibility'] != 'visible') { 3126 $this->SetVisibility($this->blk[$bl]['visibility']); 3127 } 3128 $this->PaintDivBB('pagebottom', 0, $bl); 3129 } 3130 $this->y = $sy; 3131 // RESET block y0 and x0 - see below 3132 } 3133 $this->extrapagebreak = false; // mPDF 6 pagebreaktype 3134 3135 if ($this->visibility != 'visible') { 3136 $this->SetVisibility('visible'); 3137 } 3138 3139 $this->EndLayer(); 3140 3141 // BODY Backgrounds 3142 if ($this->page > 0) { 3143 $s = ''; 3144 $s .= $this->PrintBodyBackgrounds(); 3145 3146 $s .= $this->PrintPageBackgrounds(); 3147 $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $s . "\n" . '\\1', $this->pages[$this->page]); 3148 $this->pageBackgrounds = []; 3149 } 3150 3151 $save_kt = $this->keep_block_together; 3152 $this->keep_block_together = 0; 3153 3154 $save_cols = false; 3155 3156 /* -- COLUMNS -- */ 3157 if ($this->ColActive) { 3158 $save_cols = true; 3159 $save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off 3160 $this->SetColumns(0); 3161 } 3162 /* -- END COLUMNS -- */ 3163 3164 $family = $this->FontFamily; 3165 $style = $this->FontStyle; 3166 $size = $this->FontSizePt; 3167 $this->ColumnAdjust = true; // enables column height adjustment for the page 3168 $lw = $this->LineWidth; 3169 $dc = $this->DrawColor; 3170 $fc = $this->FillColor; 3171 $tc = $this->TextColor; 3172 $cf = $this->ColorFlag; 3173 if ($this->page > 0) { 3174 // Page footer 3175 $this->InFooter = true; 3176 3177 $this->Reset(); 3178 $this->pageoutput[$this->page] = []; 3179 3180 $this->Footer(); 3181 // Close page 3182 $this->_endpage(); 3183 } 3184 3185 // Start new page 3186 $pageBeforeNewPage = $this->page; 3187 $this->_beginpage($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat); 3188 $isNewPage = $pageBeforeNewPage !== $this->page; 3189 3190 if ($this->docTemplate) { 3191 $currentReaderId = $this->currentReaderId; 3192 3193 $pagecount = $this->setSourceFile($this->docTemplate); 3194 if (($this->page - $this->docTemplateStart) > $pagecount) { 3195 if ($this->docTemplateContinue) { 3196 if ($this->docTemplateContinue2pages && $pagecount >= 2 && (0 === $this->page % 2)) { 3197 $tplIdx = $this->importPage(($pagecount - 1)); 3198 $this->useTemplate($tplIdx); 3199 } else { 3200 $tplIdx = $this->importPage($pagecount); 3201 $this->useTemplate($tplIdx); 3202 } 3203 } 3204 } else { 3205 $tplIdx = $this->importPage(($this->page - $this->docTemplateStart)); 3206 $this->useTemplate($tplIdx); 3207 } 3208 3209 $this->currentReaderId = $currentReaderId; 3210 } 3211 3212 if ($this->pageTemplate) { 3213 $this->useTemplate($this->pageTemplate); 3214 } 3215 3216 // Only add the headers if it's a new page 3217 if ($isNewPage) { 3218 // Tiling Patterns 3219 $this->writer->write('___PAGE___START' . $this->uniqstr); 3220 $this->writer->write('___BACKGROUND___PATTERNS' . $this->uniqstr); 3221 $this->writer->write('___HEADER___MARKER' . $this->uniqstr); 3222 } 3223 3224 $this->pageBackgrounds = []; 3225 3226 // Set line cap style to square 3227 $this->SetLineCap(2); 3228 // Set line width 3229 $this->LineWidth = $lw; 3230 $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE)); 3231 // Set font 3232 if ($family) { 3233 $this->SetFont($family, $style, $size, true, true); // forces write 3234 } 3235 3236 // Set colors 3237 $this->DrawColor = $dc; 3238 if ($dc != $this->defDrawColor) { 3239 $this->writer->write($dc); 3240 } 3241 $this->FillColor = $fc; 3242 if ($fc != $this->defFillColor) { 3243 $this->writer->write($fc); 3244 } 3245 $this->TextColor = $tc; 3246 $this->ColorFlag = $cf; 3247 3248 // Page header 3249 $this->Header(); 3250 3251 // Restore line width 3252 if ($this->LineWidth != $lw) { 3253 $this->LineWidth = $lw; 3254 $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE)); 3255 } 3256 // Restore font 3257 if ($family) { 3258 $this->SetFont($family, $style, $size, true, true); // forces write 3259 } 3260 3261 // Restore colors 3262 if ($this->DrawColor != $dc) { 3263 $this->DrawColor = $dc; 3264 $this->writer->write($dc); 3265 } 3266 if ($this->FillColor != $fc) { 3267 $this->FillColor = $fc; 3268 $this->writer->write($fc); 3269 } 3270 $this->TextColor = $tc; 3271 $this->ColorFlag = $cf; 3272 $this->InFooter = false; 3273 3274 if ($save_layer > 0) { 3275 $this->BeginLayer($save_layer); 3276 } 3277 3278 if ($save_vis != 'visible') { 3279 $this->SetVisibility($save_vis); 3280 } 3281 3282 /* -- COLUMNS -- */ 3283 if ($save_cols) { 3284 // Restore columns 3285 $this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap); 3286 } 3287 if ($this->ColActive) { 3288 $this->SetCol(0); 3289 } 3290 /* -- END COLUMNS -- */ 3291 3292 3293 // RESET BLOCK BORDER TOP 3294 if (!$this->ColActive) { 3295 for ($bl = 1; $bl <= $this->blklvl; $bl++) { 3296 $this->blk[$bl]['y0'] = $this->y; 3297 if (isset($this->blk[$bl]['x0'])) { 3298 $this->blk[$bl]['x0'] += $this->MarginCorrection; 3299 } else { 3300 $this->blk[$bl]['x0'] = $this->MarginCorrection; 3301 } 3302 // Added mPDF 3.0 Float DIV 3303 $this->blk[$bl]['marginCorrected'][$this->page] = true; 3304 } 3305 } 3306 3307 3308 $this->table_rotate = $save_tr; // *TABLES* 3309 $this->kwt = $save_kwt; 3310 3311 $this->keep_block_together = $save_kt; 3312 3313 $this->cMarginL = $bak_cml; 3314 $this->cMarginR = $bak_cmr; 3315 $this->divwidth = $bak_dw; 3316 3317 $this->lineheight = $bak_lh; 3318 } 3319 3320 /** 3321 * Get current page number 3322 * 3323 * @return int 3324 */ 3325 function PageNo() 3326 { 3327 return $this->page; 3328 } 3329 3330 function AddSpotColorsFromFile($file) 3331 { 3332 $colors = @file($file); 3333 if (!$colors) { 3334 throw new \Mpdf\MpdfException("Cannot load spot colors file - " . $file); 3335 } 3336 foreach ($colors as $sc) { 3337 list($name, $c, $m, $y, $k) = preg_split("/\t/", $sc); 3338 $c = intval($c); 3339 $m = intval($m); 3340 $y = intval($y); 3341 $k = intval($k); 3342 $this->AddSpotColor($name, $c, $m, $y, $k); 3343 } 3344 } 3345 3346 function AddSpotColor($name, $c, $m, $y, $k) 3347 { 3348 $name = strtoupper(trim($name)); 3349 if (!isset($this->spotColors[$name])) { 3350 $i = count($this->spotColors) + 1; 3351 $this->spotColors[$name] = ['i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k]; 3352 $this->spotColorIDs[$i] = $name; 3353 } 3354 } 3355 3356 function SetColor($col, $type = '') 3357 { 3358 $out = ''; 3359 if (!$col) { 3360 return ''; 3361 } // mPDF 6 3362 if ($col[0] == 3 || $col[0] == 5) { // RGB / RGBa 3363 $out = sprintf('%.3F %.3F %.3F rg', ord($col[1]) / 255, ord($col[2]) / 255, ord($col[3]) / 255); 3364 } elseif ($col[0] == 1) { // GRAYSCALE 3365 $out = sprintf('%.3F g', ord($col[1]) / 255); 3366 } elseif ($col[0] == 2) { // SPOT COLOR 3367 $out = sprintf('/CS%d cs %.3F scn', ord($col[1]), ord($col[2]) / 100); 3368 } elseif ($col[0] == 4 || $col[0] == 6) { // CMYK / CMYKa 3369 $out = sprintf('%.3F %.3F %.3F %.3F k', ord($col[1]) / 100, ord($col[2]) / 100, ord($col[3]) / 100, ord($col[4]) / 100); 3370 } 3371 if ($type == 'Draw') { 3372 $out = strtoupper($out); 3373 } // e.g. rg => RG 3374 elseif ($type == 'CodeOnly') { 3375 $out = preg_replace('/\s(rg|g|k)/', '', $out); 3376 } 3377 return $out; 3378 } 3379 3380 function SetDColor($col, $return = false) 3381 { 3382 $out = $this->SetColor($col, 'Draw'); 3383 if ($return) { 3384 return $out; 3385 } 3386 if ($out == '') { 3387 return ''; 3388 } 3389 $this->DrawColor = $out; 3390 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['DrawColor']) && $this->pageoutput[$this->page]['DrawColor'] != $this->DrawColor) || !isset($this->pageoutput[$this->page]['DrawColor']))) { 3391 $this->writer->write($this->DrawColor); 3392 } 3393 $this->pageoutput[$this->page]['DrawColor'] = $this->DrawColor; 3394 } 3395 3396 function SetFColor($col, $return = false) 3397 { 3398 $out = $this->SetColor($col, 'Fill'); 3399 if ($return) { 3400 return $out; 3401 } 3402 if ($out == '') { 3403 return ''; 3404 } 3405 $this->FillColor = $out; 3406 $this->ColorFlag = ($out != $this->TextColor); 3407 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor']))) { 3408 $this->writer->write($this->FillColor); 3409 } 3410 $this->pageoutput[$this->page]['FillColor'] = $this->FillColor; 3411 } 3412 3413 function SetTColor($col, $return = false) 3414 { 3415 $out = $this->SetColor($col, 'Text'); 3416 if ($return) { 3417 return $out; 3418 } 3419 if ($out == '') { 3420 return ''; 3421 } 3422 $this->TextColor = $out; 3423 $this->ColorFlag = ($this->FillColor != $out); 3424 } 3425 3426 function SetDrawColor($r, $g = -1, $b = -1, $col4 = -1, $return = false) 3427 { 3428 // Set color for all stroking operations 3429 $col = []; 3430 if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) { 3431 $col = $this->colorConverter->convert($r, $this->PDFAXwarnings); 3432 } elseif ($col4 == -1) { 3433 $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings); 3434 } else { 3435 $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings); 3436 } 3437 $out = $this->SetDColor($col, $return); 3438 return $out; 3439 } 3440 3441 function SetFillColor($r, $g = -1, $b = -1, $col4 = -1, $return = false) 3442 { 3443 // Set color for all filling operations 3444 $col = []; 3445 if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) { 3446 $col = $this->colorConverter->convert($r, $this->PDFAXwarnings); 3447 } elseif ($col4 == -1) { 3448 $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings); 3449 } else { 3450 $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings); 3451 } 3452 $out = $this->SetFColor($col, $return); 3453 return $out; 3454 } 3455 3456 function SetTextColor($r, $g = -1, $b = -1, $col4 = -1, $return = false) 3457 { 3458 // Set color for text 3459 $col = []; 3460 if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) { 3461 $col = $this->colorConverter->convert($r, $this->PDFAXwarnings); 3462 } elseif ($col4 == -1) { 3463 $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings); 3464 } else { 3465 $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings); 3466 } 3467 $out = $this->SetTColor($col, $return); 3468 return $out; 3469 } 3470 3471 function _getCharWidth(&$cw, $u, $isdef = true) 3472 { 3473 $w = 0; 3474 3475 if ($u == 0) { 3476 $w = false; 3477 } elseif (isset($cw[$u * 2 + 1])) { 3478 $w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]); 3479 } 3480 3481 if ($w == 65535) { 3482 return 0; 3483 } elseif ($w) { 3484 return $w; 3485 } elseif ($isdef) { 3486 return false; 3487 } else { 3488 return 0; 3489 } 3490 } 3491 3492 function _charDefined(&$cw, $u) 3493 { 3494 $w = 0; 3495 if ($u == 0) { 3496 return false; 3497 } 3498 if (isset($cw[$u * 2 + 1])) { 3499 $w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]); 3500 } 3501 3502 return (bool) $w; 3503 } 3504 3505 function GetCharWidthCore($c) 3506 { 3507 // Get width of a single character in the current Core font 3508 $c = (string) $c; 3509 $w = 0; 3510 // Soft Hyphens chr(173) 3511 if ($c == chr(173) && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') { 3512 return 0; 3513 } elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($c)])) { // mPDF 5.7.1 3514 $charw = $this->CurrentFont['cw'][chr($this->upperCase[ord($c)])]; 3515 if ($charw !== false) { 3516 $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100; 3517 $w+=$charw; 3518 } 3519 } elseif (isset($this->CurrentFont['cw'][$c])) { 3520 $w += $this->CurrentFont['cw'][$c]; 3521 } elseif (isset($this->CurrentFont['cw'][ord($c)])) { 3522 $w += $this->CurrentFont['cw'][ord($c)]; 3523 } 3524 $w *= ($this->FontSize / 1000); 3525 if ($this->minwSpacing || $this->fixedlSpacing) { 3526 if ($c == ' ') { 3527 $nb_spaces = 1; 3528 } else { 3529 $nb_spaces = 0; 3530 } 3531 $w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing); 3532 } 3533 return ($w); 3534 } 3535 3536 function GetCharWidthNonCore($c, $addSubset = true) 3537 { 3538 // Get width of a single character in the current Non-Core font 3539 $c = (string) $c; 3540 $w = 0; 3541 $unicode = $this->UTF8StringToArray($c, $addSubset); 3542 $char = $unicode[0]; 3543 /* -- CJK-FONTS -- */ 3544 if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts 3545 if ($char == 173) { 3546 return 0; 3547 } // Soft Hyphens 3548 elseif (isset($this->CurrentFont['cw'][$char])) { 3549 $w+=$this->CurrentFont['cw'][$char]; 3550 } elseif (isset($this->CurrentFont['MissingWidth'])) { 3551 $w += $this->CurrentFont['MissingWidth']; 3552 } else { 3553 $w += 500; 3554 } 3555 } else { 3556 /* -- END CJK-FONTS -- */ 3557 if ($char == 173) { 3558 return 0; 3559 } // Soft Hyphens 3560 elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) { // mPDF 5.7.1 3561 $charw = $this->_getCharWidth($this->CurrentFont['cw'], $this->upperCase[$char]); 3562 if ($charw !== false) { 3563 $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100; 3564 $w+=$charw; 3565 } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) { 3566 $w += $this->CurrentFont['desc']['MissingWidth']; 3567 } elseif (isset($this->CurrentFont['MissingWidth'])) { 3568 $w += $this->CurrentFont['MissingWidth']; 3569 } else { 3570 $w += 500; 3571 } 3572 } else { 3573 $charw = $this->_getCharWidth($this->CurrentFont['cw'], $char); 3574 if ($charw !== false) { 3575 $w+=$charw; 3576 } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) { 3577 $w += $this->CurrentFont['desc']['MissingWidth']; 3578 } elseif (isset($this->CurrentFont['MissingWidth'])) { 3579 $w += $this->CurrentFont['MissingWidth']; 3580 } else { 3581 $w += 500; 3582 } 3583 } 3584 } // *CJK-FONTS* 3585 $w *= ($this->FontSize / 1000); 3586 if ($this->minwSpacing || $this->fixedlSpacing) { 3587 if ($c == ' ') { 3588 $nb_spaces = 1; 3589 } else { 3590 $nb_spaces = 0; 3591 } 3592 $w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing); 3593 } 3594 return ($w); 3595 } 3596 3597 function GetCharWidth($c, $addSubset = true) 3598 { 3599 if (!$this->usingCoreFont) { 3600 return $this->GetCharWidthNonCore($c, $addSubset); 3601 } else { 3602 return $this->GetCharWidthCore($c); 3603 } 3604 } 3605 3606 function GetStringWidth($s, $addSubset = true, $OTLdata = false, $textvar = 0, $includeKashida = false) 3607 { 3608 // mPDF 5.7.1 3609 // Get width of a string in the current font 3610 $s = (string) $s; 3611 $cw = &$this->CurrentFont['cw']; 3612 $w = 0; 3613 $kerning = 0; 3614 $lastchar = 0; 3615 $nb_carac = 0; 3616 $nb_spaces = 0; 3617 $kashida = 0; 3618 // mPDF ITERATION 3619 if ($this->iterationCounter) { 3620 $s = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $s); 3621 } 3622 if (!$this->usingCoreFont) { 3623 $discards = substr_count($s, "\xc2\xad"); // mPDF 6 soft hyphens [U+00AD] 3624 $unicode = $this->UTF8StringToArray($s, $addSubset); 3625 if ($this->minwSpacing || $this->fixedlSpacing) { 3626 $nb_spaces = mb_substr_count($s, ' ', $this->mb_enc); 3627 $nb_carac = count($unicode) - $discards; // mPDF 6 3628 // mPDF 5.7.1 3629 // Use GPOS OTL 3630 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 3631 if (isset($OTLdata['group']) && $OTLdata['group']) { 3632 $nb_carac -= substr_count($OTLdata['group'], 'M'); 3633 } 3634 } 3635 } 3636 /* -- CJK-FONTS -- */ 3637 if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts 3638 foreach ($unicode as $char) { 3639 if ($char == 0x00AD) { 3640 continue; 3641 } // mPDF 6 soft hyphens [U+00AD] 3642 if (isset($cw[$char])) { 3643 $w+=$cw[$char]; 3644 } elseif (isset($this->CurrentFont['MissingWidth'])) { 3645 $w += $this->CurrentFont['MissingWidth']; 3646 } else { 3647 $w += 500; 3648 } 3649 } 3650 } else { 3651 /* -- END CJK-FONTS -- */ 3652 foreach ($unicode as $i => $char) { 3653 if ($char == 0x00AD) { 3654 continue; 3655 } // mPDF 6 soft hyphens [U+00AD] 3656 if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) { 3657 $charw = $this->_getCharWidth($cw, $this->upperCase[$char]); 3658 if ($charw !== false) { 3659 $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100; 3660 $w+=$charw; 3661 } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) { 3662 $w += $this->CurrentFont['desc']['MissingWidth']; 3663 } elseif (isset($this->CurrentFont['MissingWidth'])) { 3664 $w += $this->CurrentFont['MissingWidth']; 3665 } else { 3666 $w += 500; 3667 } 3668 } else { 3669 $charw = $this->_getCharWidth($cw, $char); 3670 if ($charw !== false) { 3671 $w+=$charw; 3672 } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) { 3673 $w += $this->CurrentFont['desc']['MissingWidth']; 3674 } elseif (isset($this->CurrentFont['MissingWidth'])) { 3675 $w += $this->CurrentFont['MissingWidth']; 3676 } else { 3677 $w += 500; 3678 } 3679 // mPDF 5.7.1 3680 // Use GPOS OTL 3681 // ...GetStringWidth... 3682 if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata)) { 3683 if (isset($OTLdata['GPOSinfo'][$i]['wDir']) && $OTLdata['GPOSinfo'][$i]['wDir'] == 'RTL') { 3684 if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceR']) && $OTLdata['GPOSinfo'][$i]['XAdvanceR']) { 3685 $w += $OTLdata['GPOSinfo'][$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm']; 3686 } 3687 } else { 3688 if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceL']) && $OTLdata['GPOSinfo'][$i]['XAdvanceL']) { 3689 $w += $OTLdata['GPOSinfo'][$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm']; 3690 } 3691 } 3692 // Kashida from GPOS 3693 // Kashida is set as an absolute length value (already set as a proportion based on useKashida %) 3694 if ($includeKashida && isset($OTLdata['GPOSinfo'][$i]['kashida_space']) && $OTLdata['GPOSinfo'][$i]['kashida_space']) { 3695 $kashida += $OTLdata['GPOSinfo'][$i]['kashida_space']; 3696 } 3697 } 3698 if (($textvar & TextVars::FC_KERNING) && $lastchar) { 3699 if (isset($this->CurrentFont['kerninfo'][$lastchar][$char])) { 3700 $kerning += $this->CurrentFont['kerninfo'][$lastchar][$char]; 3701 } 3702 } 3703 $lastchar = $char; 3704 } 3705 } 3706 } // *CJK-FONTS* 3707 } else { 3708 if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') { 3709 $s = str_replace(chr(173), '', $s); 3710 } 3711 $nb_carac = $l = strlen($s); 3712 if ($this->minwSpacing || $this->fixedlSpacing) { 3713 $nb_spaces = substr_count($s, ' '); 3714 } 3715 for ($i = 0; $i < $l; $i++) { 3716 if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($s[$i])])) { // mPDF 5.7.1 3717 $charw = $cw[chr($this->upperCase[ord($s[$i])])]; 3718 if ($charw !== false) { 3719 $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100; 3720 $w+=$charw; 3721 } 3722 } elseif (isset($cw[$s[$i]])) { 3723 $w += $cw[$s[$i]]; 3724 } elseif (isset($cw[ord($s[$i])])) { 3725 $w += $cw[ord($s[$i])]; 3726 } 3727 if (($textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1 3728 if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]])) { 3729 $kerning += $this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]]; 3730 } 3731 } 3732 } 3733 } 3734 unset($cw); 3735 if ($textvar & TextVars::FC_KERNING) { 3736 $w += $kerning; 3737 } // mPDF 5.7.1 3738 $w *= ($this->FontSize / 1000); 3739 $w += (($nb_carac + $nb_spaces) * $this->fixedlSpacing) + ($nb_spaces * $this->minwSpacing); 3740 $w += $kashida / Mpdf::SCALE; 3741 3742 return ($w); 3743 } 3744 3745 function SetLineWidth($width) 3746 { 3747 // Set line width 3748 $this->LineWidth = $width; 3749 $lwout = (sprintf('%.3F w', $width * Mpdf::SCALE)); 3750 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineWidth']) && $this->pageoutput[$this->page]['LineWidth'] != $lwout) || !isset($this->pageoutput[$this->page]['LineWidth']))) { 3751 $this->writer->write($lwout); 3752 } 3753 $this->pageoutput[$this->page]['LineWidth'] = $lwout; 3754 } 3755 3756 function Line($x1, $y1, $x2, $y2) 3757 { 3758 // Draw a line 3759 $this->writer->write(sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($this->h - $y2) * Mpdf::SCALE)); 3760 } 3761 3762 function Arrow($x1, $y1, $x2, $y2, $headsize = 3, $fill = 'B', $angle = 25) 3763 { 3764 // F == fill // S == stroke // B == stroke and fill 3765 // angle = splay of arrowhead - 1 - 89 degrees 3766 if ($fill == 'F') { 3767 $fill = 'f'; 3768 } elseif ($fill == 'FD' or $fill == 'DF' or $fill == 'B') { 3769 $fill = 'B'; 3770 } else { 3771 $fill = 'S'; 3772 } 3773 $a = atan2(($y2 - $y1), ($x2 - $x1)); 3774 $b = $