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 = $a + deg2rad($angle); 3775 $c = $a - deg2rad($angle); 3776 $x3 = $x2 - ($headsize * cos($b)); 3777 $y3 = $this->h - ($y2 - ($headsize * sin($b))); 3778 $x4 = $x2 - ($headsize * cos($c)); 3779 $y4 = $this->h - ($y2 - ($headsize * sin($c))); 3780 3781 $x5 = $x3 - ($x3 - $x4) / 2; // mid point of base of arrowhead - to join arrow line to 3782 $y5 = $y3 - ($y3 - $y4) / 2; 3783 3784 $s = ''; 3785 $s .= sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE); 3786 $this->writer->write($s); 3787 3788 $s = ''; 3789 $s .= sprintf('%.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l ', $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE, $x3 * Mpdf::SCALE, $y3 * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($this->h - $y2) * Mpdf::SCALE, $x4 * Mpdf::SCALE, $y4 * Mpdf::SCALE, $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE); 3790 $s .= $fill; 3791 $this->writer->write($s); 3792 } 3793 3794 function Rect($x, $y, $w, $h, $style = '') 3795 { 3796 // Draw a rectangle 3797 if ($style == 'F') { 3798 $op = 'f'; 3799 } elseif ($style == 'FD' or $style == 'DF') { 3800 $op = 'B'; 3801 } else { 3802 $op = 'S'; 3803 } 3804 $this->writer->write(sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$h * Mpdf::SCALE, $op)); 3805 } 3806 3807 function AddFontDirectory($directory) 3808 { 3809 $this->fontDir[] = $directory; 3810 $this->fontFileFinder->setDirectories($this->fontDir); 3811 } 3812 3813 function AddFont($family, $style = '') 3814 { 3815 if (empty($family)) { 3816 return; 3817 } 3818 3819 $family = strtolower($family); 3820 $style = strtoupper($style); 3821 $style = str_replace('U', '', $style); 3822 3823 if ($style == 'IB') { 3824 $style = 'BI'; 3825 } 3826 3827 $fontkey = $family . $style; 3828 3829 // check if the font has been already added 3830 if (isset($this->fonts[$fontkey])) { 3831 return; 3832 } 3833 3834 /* -- CJK-FONTS -- */ 3835 if (in_array($family, $this->available_CJK_fonts)) { 3836 if (empty($this->Big5_widths)) { 3837 require __DIR__ . '/../data/CJKdata.php'; 3838 } 3839 $this->AddCJKFont($family); // don't need to add style 3840 return; 3841 } 3842 /* -- END CJK-FONTS -- */ 3843 3844 if ($this->usingCoreFont) { 3845 throw new \Mpdf\MpdfException("mPDF Error - problem with Font management"); 3846 } 3847 3848 $stylekey = $style; 3849 if (!$style) { 3850 $stylekey = 'R'; 3851 } 3852 3853 if (!isset($this->fontdata[$family][$stylekey]) || !$this->fontdata[$family][$stylekey]) { 3854 throw new \Mpdf\MpdfException(sprintf('Font "%s%s%s" is not supported', $family, $style ? ' - ' : '', $style)); 3855 } 3856 3857 /* Setup defaults */ 3858 $font = [ 3859 'name' => '', 3860 'type' => '', 3861 'desc' => '', 3862 'panose' => '', 3863 'unitsPerEm' => '', 3864 'up' => '', 3865 'ut' => '', 3866 'strs' => '', 3867 'strp' => '', 3868 'sip' => false, 3869 'smp' => false, 3870 'useOTL' => 0, 3871 'fontmetrics' => '', 3872 'haskerninfo' => false, 3873 'haskernGPOS' => false, 3874 'hassmallcapsGSUB' => false, 3875 'BMPselected' => false, 3876 'GSUBScriptLang' => [], 3877 'GSUBFeatures' => [], 3878 'GSUBLookups' => [], 3879 'GPOSScriptLang' => [], 3880 'GPOSFeatures' => [], 3881 'GPOSLookups' => [], 3882 'rtlPUAstr' => '', 3883 ]; 3884 3885 $fontCacheFilename = $fontkey . '.mtx.json'; 3886 if ($this->fontCache->jsonHas($fontCacheFilename)) { 3887 $font = $this->fontCache->jsonLoad($fontCacheFilename); 3888 } 3889 3890 $ttffile = $this->fontFileFinder->findFontFile($this->fontdata[$family][$stylekey]); 3891 $ttfstat = stat($ttffile); 3892 3893 $TTCfontID = isset($this->fontdata[$family]['TTCfontID'][$stylekey]) ? isset($this->fontdata[$family]['TTCfontID'][$stylekey]) : 0; 3894 $fontUseOTL = isset($this->fontdata[$family]['useOTL']) ? $this->fontdata[$family]['useOTL'] : false; 3895 $BMPonly = in_array($family, $this->BMPonly) ? true : false; 3896 3897 $regenerate = false; 3898 if ($BMPonly && !$font['BMPselected']) { 3899 $regenerate = true; 3900 } elseif (!$BMPonly && $font['BMPselected']) { 3901 $regenerate = true; 3902 } 3903 3904 if ($fontUseOTL && $font['useOTL'] != $fontUseOTL) { 3905 $regenerate = true; 3906 $font['useOTL'] = $fontUseOTL; 3907 } elseif (!$fontUseOTL && $font['useOTL']) { 3908 $regenerate = true; 3909 $font['useOTL'] = 0; 3910 } 3911 3912 if ($this->fontDescriptor != $font['fontmetrics']) { 3913 $regenerate = true; 3914 } // mPDF 6 3915 3916 if (empty($font['name']) || $font['originalsize'] != $ttfstat['size'] || $regenerate) { 3917 $generator = new MetricsGenerator($this->fontCache, $this->fontDescriptor); 3918 3919 $generator->generateMetrics( 3920 $ttffile, 3921 $ttfstat, 3922 $fontkey, 3923 $TTCfontID, 3924 $this->debugfonts, 3925 $BMPonly, 3926 $font['useOTL'], 3927 $fontUseOTL 3928 ); 3929 3930 $font = $this->fontCache->jsonLoad($fontCacheFilename); 3931 $cw = $this->fontCache->load($fontkey . '.cw.dat'); 3932 $glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat'); 3933 } else { 3934 if ($this->fontCache->has($fontkey . '.cw.dat')) { 3935 $cw = $this->fontCache->load($fontkey . '.cw.dat'); 3936 } 3937 3938 if ($this->fontCache->has($fontkey . '.gid.dat')) { 3939 $glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat'); 3940 } 3941 } 3942 3943 if (isset($this->fontdata[$family]['sip-ext']) && $this->fontdata[$family]['sip-ext']) { 3944 $sipext = $this->fontdata[$family]['sip-ext']; 3945 } else { 3946 $sipext = ''; 3947 } 3948 3949 // Override with values from config_font.php 3950 if (isset($this->fontdata[$family]['Ascent']) && $this->fontdata[$family]['Ascent']) { 3951 $desc['Ascent'] = $this->fontdata[$family]['Ascent']; 3952 } 3953 if (isset($this->fontdata[$family]['Descent']) && $this->fontdata[$family]['Descent']) { 3954 $desc['Descent'] = $this->fontdata[$family]['Descent']; 3955 } 3956 if (isset($this->fontdata[$family]['Leading']) && $this->fontdata[$family]['Leading']) { 3957 $desc['Leading'] = $this->fontdata[$family]['Leading']; 3958 } 3959 3960 $i = count($this->fonts) + $this->extraFontSubsets + 1; 3961 3962 $this->fonts[$fontkey] = [ 3963 'i' => $i, 3964 'name' => $font['name'], 3965 'type' => $font['type'], 3966 'desc' => $font['desc'], 3967 'panose' => $font['panose'], 3968 'unitsPerEm' => $font['unitsPerEm'], 3969 'up' => $font['up'], 3970 'ut' => $font['ut'], 3971 'strs' => $font['strs'], 3972 'strp' => $font['strp'], 3973 'cw' => $cw, 3974 'ttffile' => $ttffile, 3975 'fontkey' => $fontkey, 3976 'used' => false, 3977 'sip' => $font['sip'], 3978 'sipext' => $sipext, 3979 'smp' => $font['smp'], 3980 'TTCfontID' => $TTCfontID, 3981 'useOTL' => $fontUseOTL, 3982 'useKashida' => (isset($this->fontdata[$family]['useKashida']) ? $this->fontdata[$family]['useKashida'] : false), 3983 'GSUBScriptLang' => $font['GSUBScriptLang'], 3984 'GSUBFeatures' => $font['GSUBFeatures'], 3985 'GSUBLookups' => $font['GSUBLookups'], 3986 'GPOSScriptLang' => $font['GPOSScriptLang'], 3987 'GPOSFeatures' => $font['GPOSFeatures'], 3988 'GPOSLookups' => $font['GPOSLookups'], 3989 'rtlPUAstr' => $font['rtlPUAstr'], 3990 'glyphIDtoUni' => $glyphIDtoUni, 3991 'haskerninfo' => $font['haskerninfo'], 3992 'haskernGPOS' => $font['haskernGPOS'], 3993 'hassmallcapsGSUB' => $font['hassmallcapsGSUB'], 3994 ]; 3995 3996 3997 if (!$font['sip'] && !$font['smp']) { 3998 $subsetRange = range(32, 127); 3999 $this->fonts[$fontkey]['subset'] = array_combine($subsetRange, $subsetRange); 4000 } else { 4001 $this->fonts[$fontkey]['subsets'] = [0 => range(0, 127)]; 4002 $this->fonts[$fontkey]['subsetfontids'] = [$i]; 4003 } 4004 4005 if ($font['haskerninfo']) { 4006 $this->fonts[$fontkey]['kerninfo'] = $font['kerninfo']; 4007 } 4008 4009 $this->FontFiles[$fontkey] = [ 4010 'length1' => $font['originalsize'], 4011 'type' => 'TTF', 4012 'ttffile' => $ttffile, 4013 'sip' => $font['sip'], 4014 'smp' => $font['smp'], 4015 ]; 4016 4017 unset($cw); 4018 } 4019 4020 function SetFont($family, $style = '', $size = 0, $write = true, $forcewrite = false) 4021 { 4022 $family = strtolower($family); 4023 4024 if (!$this->onlyCoreFonts) { 4025 if ($family == 'sans' || $family == 'sans-serif') { 4026 $family = $this->sans_fonts[0]; 4027 } 4028 if ($family == 'serif') { 4029 $family = $this->serif_fonts[0]; 4030 } 4031 if ($family == 'mono' || $family == 'monospace') { 4032 $family = $this->mono_fonts[0]; 4033 } 4034 } 4035 4036 if (isset($this->fonttrans[$family]) && $this->fonttrans[$family]) { 4037 $family = $this->fonttrans[$family]; 4038 } 4039 4040 if ($family == '') { 4041 if ($this->FontFamily) { 4042 $family = $this->FontFamily; 4043 } elseif ($this->default_font) { 4044 $family = $this->default_font; 4045 } else { 4046 throw new \Mpdf\MpdfException("No font or default font set!"); 4047 } 4048 } 4049 4050 $this->ReqFontStyle = $style; // required or requested style - used later for artificial bold/italic 4051 4052 if (($family == 'csymbol') || ($family == 'czapfdingbats') || ($family == 'ctimes') || ($family == 'ccourier') || ($family == 'chelvetica')) { 4053 if ($this->PDFA || $this->PDFX) { 4054 if ($family == 'csymbol' || $family == 'czapfdingbats') { 4055 throw new \Mpdf\MpdfException("Symbol and Zapfdingbats cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a)."); 4056 } 4057 if ($family == 'ctimes' || $family == 'ccourier' || $family == 'chelvetica') { 4058 if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) { 4059 $this->PDFAXwarnings[] = "Core Adobe font " . ucfirst($family) . " cannot be embedded in mPDF, which is required for PDFA1-b or PDFX/1-a. (Embedded font will be substituted.)"; 4060 } 4061 if ($family == 'chelvetica') { 4062 $family = 'sans'; 4063 } 4064 if ($family == 'ctimes') { 4065 $family = 'serif'; 4066 } 4067 if ($family == 'ccourier') { 4068 $family = 'mono'; 4069 } 4070 } 4071 $this->usingCoreFont = false; 4072 } else { 4073 $this->usingCoreFont = true; 4074 } 4075 if ($family == 'csymbol' || $family == 'czapfdingbats') { 4076 $style = ''; 4077 } 4078 } else { 4079 $this->usingCoreFont = false; 4080 } 4081 4082 // mPDF 5.7.1 4083 if ($style) { 4084 $style = strtoupper($style); 4085 if ($style == 'IB') { 4086 $style = 'BI'; 4087 } 4088 } 4089 4090 if (!$size) { 4091 $size = $this->FontSizePt; 4092 } 4093 4094 $fontkey = $family . $style; 4095 4096 $stylekey = $style; 4097 4098 if (!$stylekey) { 4099 $stylekey = "R"; 4100 } 4101 4102 if (!$this->onlyCoreFonts && !$this->usingCoreFont) { 4103 if (!isset($this->fonts[$fontkey]) || count($this->default_available_fonts) != count($this->available_unifonts)) { // not already added 4104 4105 /* -- CJK-FONTS -- */ 4106 if (in_array($fontkey, $this->available_CJK_fonts)) { 4107 if (!isset($this->fonts[$fontkey])) { // already added 4108 if (empty($this->Big5_widths)) { 4109 require __DIR__ . '/../data/CJKdata.php'; 4110 } 4111 $this->AddCJKFont($family); // don't need to add style 4112 } 4113 } else { // Test to see if requested font/style is available - or substitute /* -- END CJK-FONTS -- */ 4114 if (!in_array($fontkey, $this->available_unifonts)) { 4115 // If font[nostyle] exists - set it 4116 if (in_array($family, $this->available_unifonts)) { 4117 $style = ''; 4118 } // elseif only one font available - set it (assumes if only one font available it will not have a style) 4119 elseif (count($this->available_unifonts) == 1) { 4120 $family = $this->available_unifonts[0]; 4121 $style = ''; 4122 } else { 4123 $found = 0; 4124 // else substitute font of similar type 4125 if (in_array($family, $this->sans_fonts)) { 4126 $i = array_intersect($this->sans_fonts, $this->available_unifonts); 4127 if (count($i)) { 4128 $i = array_values($i); 4129 // with requested style if possible 4130 if (!in_array(($i[0] . $style), $this->available_unifonts)) { 4131 $style = ''; 4132 } 4133 $family = $i[0]; 4134 $found = 1; 4135 } 4136 } elseif (in_array($family, $this->serif_fonts)) { 4137 $i = array_intersect($this->serif_fonts, $this->available_unifonts); 4138 if (count($i)) { 4139 $i = array_values($i); 4140 // with requested style if possible 4141 if (!in_array(($i[0] . $style), $this->available_unifonts)) { 4142 $style = ''; 4143 } 4144 $family = $i[0]; 4145 $found = 1; 4146 } 4147 } elseif (in_array($family, $this->mono_fonts)) { 4148 $i = array_intersect($this->mono_fonts, $this->available_unifonts); 4149 if (count($i)) { 4150 $i = array_values($i); 4151 // with requested style if possible 4152 if (!in_array(($i[0] . $style), $this->available_unifonts)) { 4153 $style = ''; 4154 } 4155 $family = $i[0]; 4156 $found = 1; 4157 } 4158 } 4159 4160 if (!$found) { 4161 // set first available font 4162 $fs = $this->available_unifonts[0]; 4163 preg_match('/^([a-z_0-9\-]+)([BI]{0,2})$/', $fs, $fas); // Allow "-" 4164 // with requested style if possible 4165 $ws = $fas[1] . $style; 4166 if (in_array($ws, $this->available_unifonts)) { 4167 $family = $fas[1]; // leave $style as is 4168 } elseif (in_array($fas[1], $this->available_unifonts)) { 4169 // or without style 4170 $family = $fas[1]; 4171 $style = ''; 4172 } else { 4173 // or with the style specified 4174 $family = $fas[1]; 4175 $style = $fas[2]; 4176 } 4177 } 4178 } 4179 $fontkey = $family . $style; 4180 } 4181 } 4182 } 4183 4184 // try to add font (if not already added) 4185 $this->AddFont($family, $style); 4186 4187 // Test if font is already selected 4188 if ($this->FontFamily == $family && $this->FontFamily == $this->currentfontfamily && $this->FontStyle == $style && $this->FontStyle == $this->currentfontstyle && $this->FontSizePt == $size && $this->FontSizePt == $this->currentfontsize && !$forcewrite) { 4189 return $family; 4190 } 4191 4192 $fontkey = $family . $style; 4193 4194 // Select it 4195 $this->FontFamily = $family; 4196 $this->FontStyle = $style; 4197 $this->FontSizePt = $size; 4198 $this->FontSize = $size / Mpdf::SCALE; 4199 $this->CurrentFont = &$this->fonts[$fontkey]; 4200 if ($write) { 4201 $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 4202 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) { 4203 $this->writer->write($fontout); 4204 } 4205 $this->pageoutput[$this->page]['Font'] = $fontout; 4206 } 4207 4208 // Added - currentfont (lowercase) used in HTML2PDF 4209 $this->currentfontfamily = $family; 4210 $this->currentfontsize = $size; 4211 $this->currentfontstyle = $style; 4212 $this->setMBencoding('UTF-8'); 4213 } else { // if using core fonts 4214 if ($this->PDFA || $this->PDFX) { 4215 throw new \Mpdf\MpdfException('Core Adobe fonts cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a) - cannot use option to use core fonts.'); 4216 } 4217 $this->setMBencoding('windows-1252'); 4218 4219 // Test if font is already selected 4220 if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) { 4221 return $family; 4222 } 4223 4224 if (!isset($this->CoreFonts[$fontkey])) { 4225 if (in_array($family, $this->serif_fonts)) { 4226 $family = 'ctimes'; 4227 } elseif (in_array($family, $this->mono_fonts)) { 4228 $family = 'ccourier'; 4229 } else { 4230 $family = 'chelvetica'; 4231 } 4232 $this->usingCoreFont = true; 4233 $fontkey = $family . $style; 4234 } 4235 4236 if (!isset($this->fonts[$fontkey])) { 4237 // STANDARD CORE FONTS 4238 if (isset($this->CoreFonts[$fontkey])) { 4239 // Load metric file 4240 $file = $family; 4241 if ($family == 'ctimes' || $family == 'chelvetica' || $family == 'ccourier') { 4242 $file .= strtolower($style); 4243 } 4244 require __DIR__ . '/../data/font/' . $file . '.php'; 4245 if (!isset($cw)) { 4246 throw new \Mpdf\MpdfException(sprintf('Could not include font metric file "%s"', $file)); 4247 } 4248 $i = count($this->fonts) + $this->extraFontSubsets + 1; 4249 $this->fonts[$fontkey] = ['i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw]; 4250 if ($this->useKerning && isset($kerninfo)) { 4251 $this->fonts[$fontkey]['kerninfo'] = $kerninfo; 4252 } 4253 } else { 4254 throw new \Mpdf\MpdfException(sprintf('Font %s not defined', $fontkey)); 4255 } 4256 } 4257 4258 // Test if font is already selected 4259 if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) { 4260 return $family; 4261 } 4262 // Select it 4263 $this->FontFamily = $family; 4264 $this->FontStyle = $style; 4265 $this->FontSizePt = $size; 4266 $this->FontSize = $size / Mpdf::SCALE; 4267 $this->CurrentFont = &$this->fonts[$fontkey]; 4268 if ($write) { 4269 $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 4270 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) { 4271 $this->writer->write($fontout); 4272 } 4273 $this->pageoutput[$this->page]['Font'] = $fontout; 4274 } 4275 // Added - currentfont (lowercase) used in HTML2PDF 4276 $this->currentfontfamily = $family; 4277 $this->currentfontsize = $size; 4278 $this->currentfontstyle = $style; 4279 } 4280 4281 return $family; 4282 } 4283 4284 function SetFontSize($size, $write = true) 4285 { 4286 // Set font size in points 4287 if ($this->FontSizePt == $size) { 4288 return; 4289 } 4290 $this->FontSizePt = $size; 4291 $this->FontSize = $size / Mpdf::SCALE; 4292 $this->currentfontsize = $size; 4293 if ($write) { 4294 $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 4295 // Edited mPDF 3.0 4296 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) { 4297 $this->writer->write($fontout); 4298 } 4299 $this->pageoutput[$this->page]['Font'] = $fontout; 4300 } 4301 } 4302 4303 function AddLink() 4304 { 4305 // Create a new internal link 4306 $n = count($this->links) + 1; 4307 $this->links[$n] = [0, 0]; 4308 return $n; 4309 } 4310 4311 function SetLink($link, $y = 0, $page = -1) 4312 { 4313 // Set destination of internal link 4314 if ($y == -1) { 4315 $y = $this->y; 4316 } 4317 if ($page == -1) { 4318 $page = $this->page; 4319 } 4320 $this->links[$link] = [$page, $y]; 4321 } 4322 4323 function Link($x, $y, $w, $h, $link) 4324 { 4325 $l = [$x * Mpdf::SCALE, $this->hPt - $y * Mpdf::SCALE, $w * Mpdf::SCALE, $h * Mpdf::SCALE, $link]; 4326 if ($this->keep_block_together) { // don't write yet 4327 return; 4328 } elseif ($this->table_rotate) { // *TABLES* 4329 $this->tbrot_Links[$this->page][] = $l; // *TABLES* 4330 return; // *TABLES* 4331 } // *TABLES* 4332 elseif ($this->kwt) { 4333 $this->kwt_Links[$this->page][] = $l; 4334 return; 4335 } 4336 4337 if ($this->writingHTMLheader || $this->writingHTMLfooter) { 4338 $this->HTMLheaderPageLinks[] = $l; 4339 return; 4340 } 4341 // Put a link on the page 4342 $this->PageLinks[$this->page][] = $l; 4343 // Save cross-reference to Column buffer 4344 $ref = count($this->PageLinks[$this->page]) - 1; // *COLUMNS* 4345 $this->columnLinks[$this->CurrCol][(int) $this->x][(int) $this->y] = $ref; // *COLUMNS* 4346 } 4347 4348 function Text($x, $y, $txt, $OTLdata = [], $textvar = 0, $aixextra = '', $coordsys = '', $return = false) 4349 { 4350 // Output (or return) a string 4351 // Called (internally) by Watermark() & _tableWrite() [rotated cells] & TableHeaderFooter() & WriteText() 4352 // Called also from classes/svg.php 4353 // Expects Font to be set 4354 // Expects input to be mb_encoded if necessary and RTL reversed & OTL processed 4355 // ARTIFICIAL BOLD AND ITALIC 4356 $s = 'q '; 4357 if ($this->falseBoldWeight && strpos($this->ReqFontStyle, "B") !== false && strpos($this->FontStyle, "B") === false) { 4358 $s .= '2 Tr 1 J 1 j '; 4359 $s .= sprintf('%.3F w ', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight); 4360 $tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG 4361 if ($this->FillColor != $tc) { 4362 $s .= $tc . ' '; 4363 } // stroke (outline) = same colour as text(fill) 4364 } 4365 if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) { 4366 $aix = '1 0 0.261799 1 %.3F %.3F Tm'; 4367 } else { 4368 $aix = '%.3F %.3F Td'; 4369 } 4370 4371 $aix = $aixextra . $aix; 4372 4373 if ($this->ColorFlag) { 4374 $s .= $this->TextColor . ' '; 4375 } 4376 4377 $this->CurrentFont['used'] = true; 4378 4379 if ($this->usingCoreFont) { 4380 $txt2 = str_replace(chr(160), chr(32), $txt); 4381 } else { 4382 $txt2 = str_replace(chr(194) . chr(160), chr(32), $txt); 4383 } 4384 4385 $px = $x; 4386 $py = $y; 4387 if ($coordsys != 'SVG') { 4388 $px = $x * Mpdf::SCALE; 4389 $py = ($this->h - $y) * Mpdf::SCALE; 4390 } 4391 4392 4393 /** ************** SIMILAR TO Cell() ************************ */ 4394 4395 // IF corefonts AND NOT SmCaps AND NOT Kerning 4396 // Just output text 4397 if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) { 4398 $txt2 = $this->writer->escape($txt2); 4399 $s .= sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2); 4400 } // IF NOT corefonts [AND NO wordspacing] AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL 4401 // Just output text 4402 elseif (!$this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) { 4403 // IF SIP/SMP 4404 if ($this->CurrentFont['sip'] || $this->CurrentFont['smp']) { 4405 $txt2 = $this->UTF8toSubset($txt2); 4406 $s .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2); 4407 } // NOT SIP/SMP 4408 else { 4409 $txt2 = $this->writer->utf8ToUtf16BigEndian($txt2, false); 4410 $txt2 = $this->writer->escape($txt2); 4411 $s .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2); 4412 } 4413 } // IF NOT corefonts [AND IS wordspacing] AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL 4414 // Not required here (cf. Cell() ) 4415 // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP] 4416 else { 4417 $s .= $this->applyGPOSpdf($txt2, $aix, $px, $py, $OTLdata, $textvar); 4418 } 4419 /* * ************** END ************************ */ 4420 4421 $s .= ' '; 4422 4423 if (($textvar & TextVars::FD_UNDERLINE) && $txt != '') { // mPDF 5.7.1 4424 $c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG 4425 if ($this->FillColor != $c) { 4426 $s.= ' ' . $c . ' '; 4427 } 4428 if (isset($this->CurrentFont['up']) && $this->CurrentFont['up']) { 4429 $up = $this->CurrentFont['up']; 4430 } else { 4431 $up = -100; 4432 } 4433 $adjusty = (-$up / 1000 * $this->FontSize); 4434 if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) { 4435 $ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize; 4436 } else { 4437 $ut = 60 / 1000 * $this->FontSize; 4438 } 4439 $olw = $this->LineWidth; 4440 $s .= ' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE)); 4441 $s .= ' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar); 4442 $s .= ' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE)); 4443 if ($this->FillColor != $c) { 4444 $s.= ' ' . $this->FillColor . ' '; 4445 } 4446 } 4447 // STRIKETHROUGH 4448 if (($textvar & TextVars::FD_LINETHROUGH) && $txt != '') { // mPDF 5.7.1 4449 $c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG 4450 if ($this->FillColor != $c) { 4451 $s.= ' ' . $c . ' '; 4452 } 4453 // Superscript and Subscript Y coordinate adjustment (now for striked-through texts) 4454 if (isset($this->CurrentFont['desc']['CapHeight']) && $this->CurrentFont['desc']['CapHeight']) { 4455 $ch = $this->CurrentFont['desc']['CapHeight']; 4456 } else { 4457 $ch = 700; 4458 } 4459 $adjusty = (-$ch / 1000 * $this->FontSize) * 0.35; 4460 if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) { 4461 $ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize; 4462 } else { 4463 $ut = 60 / 1000 * $this->FontSize; 4464 } 4465 $olw = $this->LineWidth; 4466 $s .= ' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE)); 4467 $s .= ' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar); 4468 $s .= ' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE)); 4469 if ($this->FillColor != $c) { 4470 $s.= ' ' . $this->FillColor . ' '; 4471 } 4472 } 4473 $s .= 'Q'; 4474 4475 if ($return) { 4476 return $s . " \n"; 4477 } 4478 $this->writer->write($s); 4479 } 4480 4481 /* -- DIRECTW -- */ 4482 4483 function WriteText($x, $y, $txt) 4484 { 4485 // Output a string using Text() but does encoding and text reversing of RTL 4486 $txt = $this->purify_utf8_text($txt); 4487 if ($this->text_input_as_HTML) { 4488 $txt = $this->all_entities_to_utf8($txt); 4489 } 4490 if ($this->usingCoreFont) { 4491 $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8'); 4492 } 4493 4494 // DIRECTIONALITY 4495 if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) { 4496 $this->biDirectional = true; 4497 } // *OTL* 4498 4499 $textvar = 0; 4500 $save_OTLtags = $this->OTLtags; 4501 $this->OTLtags = []; 4502 if ($this->useKerning) { 4503 if ($this->CurrentFont['haskernGPOS']) { 4504 $this->OTLtags['Plus'] .= ' kern'; 4505 } else { 4506 $textvar = ($textvar | TextVars::FC_KERNING); 4507 } 4508 } 4509 4510 /* -- OTL -- */ 4511 // Use OTL OpenType Table Layout - GSUB & GPOS 4512 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 4513 $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']); 4514 $OTLdata = $this->otl->OTLdata; 4515 } 4516 /* -- END OTL -- */ 4517 $this->OTLtags = $save_OTLtags; 4518 4519 $this->magic_reverse_dir($txt, $this->directionality, $OTLdata); 4520 4521 $this->Text($x, $y, $txt, $OTLdata, $textvar); 4522 } 4523 4524 function WriteCell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0) 4525 { 4526 // Output a cell using Cell() but does encoding and text reversing of RTL 4527 $txt = $this->purify_utf8_text($txt); 4528 if ($this->text_input_as_HTML) { 4529 $txt = $this->all_entities_to_utf8($txt); 4530 } 4531 if ($this->usingCoreFont) { 4532 $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8'); 4533 } 4534 // DIRECTIONALITY 4535 if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) { 4536 $this->biDirectional = true; 4537 } // *OTL* 4538 4539 $textvar = 0; 4540 $save_OTLtags = $this->OTLtags; 4541 $this->OTLtags = []; 4542 if ($this->useKerning) { 4543 if ($this->CurrentFont['haskernGPOS']) { 4544 $this->OTLtags['Plus'] .= ' kern'; 4545 } else { 4546 $textvar = ($textvar | TextVars::FC_KERNING); 4547 } 4548 } 4549 4550 /* -- OTL -- */ 4551 // Use OTL OpenType Table Layout - GSUB & GPOS 4552 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 4553 $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']); 4554 $OTLdata = $this->otl->OTLdata; 4555 } 4556 /* -- END OTL -- */ 4557 $this->OTLtags = $save_OTLtags; 4558 4559 $this->magic_reverse_dir($txt, $this->directionality, $OTLdata); 4560 4561 $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link, $currentx, 0, 0, 'M', 0, false, $OTLdata, $textvar); 4562 } 4563 4564 /* -- END DIRECTW -- */ 4565 4566 function ResetSpacing() 4567 { 4568 if ($this->ws != 0) { 4569 $this->writer->write('BT 0 Tw ET'); 4570 } 4571 $this->ws = 0; 4572 if ($this->charspacing != 0) { 4573 $this->writer->write('BT 0 Tc ET'); 4574 } 4575 $this->charspacing = 0; 4576 } 4577 4578 function SetSpacing($cs, $ws) 4579 { 4580 if (intval($cs * 1000) == 0) { 4581 $cs = 0; 4582 } 4583 if ($cs) { 4584 $this->writer->write(sprintf('BT %.3F Tc ET', $cs)); 4585 } elseif ($this->charspacing != 0) { 4586 $this->writer->write('BT 0 Tc ET'); 4587 } 4588 $this->charspacing = $cs; 4589 if (intval($ws * 1000) == 0) { 4590 $ws = 0; 4591 } 4592 if ($ws) { 4593 $this->writer->write(sprintf('BT %.3F Tw ET', $ws)); 4594 } elseif ($this->ws != 0) { 4595 $this->writer->write('BT 0 Tw ET'); 4596 } 4597 $this->ws = $ws; 4598 } 4599 4600 // WORD SPACING 4601 function GetJspacing($nc, $ns, $w, $inclCursive, &$cOTLdata) 4602 { 4603 $kashida_present = false; 4604 $kashida_space = 0; 4605 if ($w > 0 && $inclCursive && isset($this->CurrentFont['useKashida']) && $this->CurrentFont['useKashida'] && !empty($cOTLdata)) { 4606 for ($c = 0; $c < count($cOTLdata); $c++) { 4607 for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) { 4608 if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) { 4609 $kashida_present = true; 4610 break 2; 4611 } 4612 } 4613 } 4614 } 4615 4616 if ($kashida_present) { 4617 $k_ctr = 0; // Number of kashida points 4618 $k_total = 0; // Total of kashida values (priority) 4619 // Reset word 4620 $max_kashida_in_word = 0; 4621 $last_kashida_in_word = -1; 4622 4623 for ($c = 0; $c < count($cOTLdata); $c++) { 4624 for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) { 4625 if ($cOTLdata[$c]['group'][$i] == 'S') { 4626 // Save from last word 4627 if ($max_kashida_in_word) { 4628 $k_ctr++; 4629 $k_total = $max_kashida_in_word; 4630 } 4631 // Reset word 4632 $max_kashida_in_word = 0; 4633 $last_kashida_in_word = -1; 4634 } 4635 4636 if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) { 4637 if ($max_kashida_in_word) { 4638 if ($cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > $max_kashida_in_word) { 4639 $max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida']; 4640 $cOTLdata[$c]['GPOSinfo'][$last_kashida_in_word]['kashida'] = 0; 4641 $last_kashida_in_word = $i; 4642 } else { 4643 $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] = 0; 4644 } 4645 } else { 4646 $max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida']; 4647 $last_kashida_in_word = $i; 4648 } 4649 } 4650 } 4651 } 4652 // Save from last word 4653 if ($max_kashida_in_word) { 4654 $k_ctr++; 4655 $k_total = $max_kashida_in_word; 4656 } 4657 4658 // Number of kashida points = $k_ctr 4659 // $useKashida is a % value from CurrentFont/config_fonts.php 4660 // % ratio divided between word-spacing and kashida-spacing 4661 $kashida_space_ratio = intval($this->CurrentFont['useKashida']) / 100; 4662 4663 4664 $kashida_space = $w * $kashida_space_ratio; 4665 4666 $tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640); 4667 // Only use kashida if each allocated kashida width is > 0.01 x width of a tatweel 4668 // Otherwise fontstretch is too small and errors 4669 // If not just leave to adjust word-spacing 4670 if ($tatw && (($kashida_space / $k_ctr) / $tatw) > 0.01) { 4671 for ($c = 0; $c < count($cOTLdata); $c++) { 4672 for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) { 4673 if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) { 4674 // At this point kashida is a number representing priority (higher number - higher priority) 4675 // We are now going to set it as an actual length 4676 // This shares it equally amongst words: 4677 $cOTLdata[$c]['GPOSinfo'][$i]['kashida_space'] = (1 / $k_ctr) * $kashida_space; 4678 } 4679 } 4680 } 4681 $w -= $kashida_space; 4682 } 4683 } 4684 4685 $ws = 0; 4686 $charspacing = 0; 4687 $ww = $this->jSWord; 4688 $ncx = $nc - 1; 4689 if ($nc == 0) { 4690 return [0, 0, 0]; 4691 } // Only word spacing allowed / possible 4692 elseif ($this->fixedlSpacing !== false || $inclCursive) { 4693 if ($ns) { 4694 $ws = $w / $ns; 4695 } 4696 } elseif ($nc == 1) { 4697 $charspacing = $w; 4698 } elseif (!$ns) { 4699 $charspacing = $w / ($ncx ); 4700 if (($this->jSmaxChar > 0) && ($charspacing > $this->jSmaxChar)) { 4701 $charspacing = $this->jSmaxChar; 4702 } 4703 } elseif ($ns == ($ncx )) { 4704 $charspacing = $w / $ns; 4705 } else { 4706 if ($this->usingCoreFont) { 4707 $cs = ($w * (1 - $this->jSWord)) / ($ncx ); 4708 if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) { 4709 $cs = $this->jSmaxChar; 4710 $ww = 1 - (($cs * ($ncx )) / $w); 4711 } 4712 $charspacing = $cs; 4713 $ws = ($w * ($ww) ) / $ns; 4714 } else { 4715 $cs = ($w * (1 - $this->jSWord)) / ($ncx - $ns); 4716 if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) { 4717 $cs = $this->jSmaxChar; 4718 $ww = 1 - (($cs * ($ncx - $ns)) / $w); 4719 } 4720 $charspacing = $cs; 4721 $ws = (($w * ($ww) ) / $ns) - $charspacing; 4722 } 4723 } 4724 return [$charspacing, $ws, $kashida_space]; 4725 } 4726 4727 /** 4728 * Output a cell 4729 * 4730 * Expects input to be mb_encoded if necessary and RTL reversed 4731 * 4732 * @since mPDF 5.7.1 4733 */ 4734 function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0, $lcpaddingL = 0, $lcpaddingR = 0, $valign = 'M', $spanfill = 0, $exactWidth = false, $OTLdata = false, $textvar = 0, $lineBox = false) 4735 { 4736 // NON_BREAKING SPACE 4737 if ($this->usingCoreFont) { 4738 $txt = str_replace(chr(160), chr(32), $txt); 4739 } else { 4740 $txt = str_replace(chr(194) . chr(160), chr(32), $txt); 4741 } 4742 4743 $oldcolumn = $this->CurrCol; 4744 4745 // Automatic page break 4746 // Allows PAGE-BREAK-AFTER = avoid to work 4747 if (isset($this->blk[$this->blklvl])) { 4748 $bottom = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['margin_bottom']; 4749 } else { 4750 $bottom = 0; 4751 } 4752 4753 if (!$this->tableLevel 4754 && ( 4755 ($this->y + $this->divheight > $this->PageBreakTrigger) 4756 || ($this->y + $h > $this->PageBreakTrigger) 4757 || ( 4758 $this->y + ($h * 2) + $bottom > $this->PageBreakTrigger 4759 && (isset($this->blk[$this->blklvl]['page_break_after_avoid']) && $this->blk[$this->blklvl]['page_break_after_avoid']) 4760 ) 4761 ) 4762 && !$this->InFooter 4763 && $this->AcceptPageBreak() 4764 ) { // mPDF 5.7.2 4765 4766 $x = $this->x; // Current X position 4767 4768 // WORD SPACING 4769 $ws = $this->ws; // Word Spacing 4770 $charspacing = $this->charspacing; // Character Spacing 4771 $this->ResetSpacing(); 4772 4773 $this->AddPage($this->CurOrientation); 4774 4775 // Added to correct for OddEven Margins 4776 $x += $this->MarginCorrection; 4777 if ($currentx) { 4778 $currentx += $this->MarginCorrection; 4779 } 4780 $this->x = $x; 4781 // WORD SPACING 4782 $this->SetSpacing($charspacing, $ws); 4783 } 4784 4785 // Test: to put line through centre of cell: $this->Line($this->x,$this->y+($h/2),$this->x+50,$this->y+($h/2)); 4786 // Test: to put border around cell as it is specified: $border='LRTB'; 4787 4788 /* -- COLUMNS -- */ 4789 // COLS 4790 // COLUMN CHANGE 4791 if ($this->CurrCol != $oldcolumn) { 4792 if ($currentx) { 4793 $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 4794 } 4795 $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 4796 } 4797 4798 // COLUMNS Update/overwrite the lowest bottom of printing y value for a column 4799 if ($this->ColActive) { 4800 if ($h) { 4801 $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h; 4802 } else { 4803 $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $this->divheight; 4804 } 4805 } 4806 /* -- END COLUMNS -- */ 4807 4808 4809 if ($w == 0) { 4810 $w = $this->w - $this->rMargin - $this->x; 4811 } 4812 4813 $s = ''; 4814 if ($fill == 1 && $this->FillColor) { 4815 if ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor'])) { 4816 $s .= $this->FillColor . ' '; 4817 } 4818 $this->pageoutput[$this->page]['FillColor'] = $this->FillColor; 4819 } 4820 4821 if ($lineBox && isset($lineBox['boxtop']) && $txt) { // i.e. always from WriteFlowingBlock/finishFlowingBlock (but not objects - 4822 // which only have $lineBox['top'] set) 4823 $boxtop = $this->y + $lineBox['boxtop']; 4824 $boxbottom = $this->y + $lineBox['boxbottom']; 4825 $glyphYorigin = $lineBox['glyphYorigin']; 4826 $baseline_shift = $lineBox['baseline-shift']; 4827 $bord_boxtop = $bg_boxtop = $boxtop = $boxtop - $baseline_shift; 4828 $bord_boxbottom = $bg_boxbottom = $boxbottom = $boxbottom - $baseline_shift; 4829 $bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop; 4830 4831 // If inline element BACKGROUND has bounding box set by parent element: 4832 if (isset($lineBox['background-boxtop'])) { 4833 $bg_boxtop = $this->y + $lineBox['background-boxtop'] - $lineBox['background-baseline-shift']; 4834 $bg_boxbottom = $this->y + $lineBox['background-boxbottom'] - $lineBox['background-baseline-shift']; 4835 $bg_boxheight = $bg_boxbottom - $bg_boxtop; 4836 } 4837 // If inline element BORDER has bounding box set by parent element: 4838 if (isset($lineBox['border-boxtop'])) { 4839 $bord_boxtop = $this->y + $lineBox['border-boxtop'] - $lineBox['border-baseline-shift']; 4840 $bord_boxbottom = $this->y + $lineBox['border-boxbottom'] - $lineBox['border-baseline-shift']; 4841 $bord_boxheight = $bord_boxbottom - $bord_boxtop; 4842 } 4843 4844 } else { 4845 4846 $boxtop = $this->y; 4847 $boxheight = $h; 4848 $boxbottom = $this->y + $h; 4849 $baseline_shift = 0; 4850 4851 if ($txt != '') { 4852 4853 // FONT SIZE - this determines the baseline caculation 4854 $bfs = $this->FontSize; 4855 // Calculate baseline Superscript and Subscript Y coordinate adjustment 4856 $bfx = $this->baselineC; 4857 $baseline = $bfx * $bfs; 4858 4859 if ($textvar & TextVars::FA_SUPERSCRIPT) { 4860 $baseline_shift = $this->textparam['text-baseline']; 4861 } elseif ($textvar & TextVars::FA_SUBSCRIPT) { 4862 $baseline_shift = $this->textparam['text-baseline']; 4863 } elseif ($this->bullet) { 4864 $baseline += ($bfx - 0.7) * $this->FontSize; 4865 } 4866 4867 // Vertical align (for Images) 4868 if ($valign == 'T') { 4869 $va = (0.5 * $bfs * $this->normalLineheight); 4870 } elseif ($valign == 'B') { 4871 $va = $h - (0.5 * $bfs * $this->normalLineheight); 4872 } else { 4873 $va = 0.5 * $h; 4874 } // Middle 4875 4876 // ONLY SET THESE IF WANT TO CONFINE BORDER +/- FILL TO FIT FONTSIZE - NOT FULL CELL AS IS ORIGINAL FUNCTION 4877 // spanfill or spanborder are set in FlowingBlock functions 4878 if ($spanfill || !empty($this->spanborddet) || $link != '') { 4879 $exth = 0.2; // Add to fontsize to increase height of background / link / border 4880 $boxtop = $this->y + $baseline + $va - ($this->FontSize * (1 + $exth / 2) * (0.5 + $bfx)); 4881 $boxheight = $this->FontSize * (1 + $exth); 4882 $boxbottom = $boxtop + $boxheight; 4883 } 4884 4885 $glyphYorigin = $baseline + $va; 4886 } 4887 4888 $boxtop -= $baseline_shift; 4889 $boxbottom -= $baseline_shift; 4890 $bord_boxtop = $bg_boxtop = $boxtop; 4891 $bord_boxbottom = $bg_boxbottom = $boxbottom; 4892 $bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop; 4893 } 4894 4895 $bbw = $tbw = $lbw = $rbw = 0; // Border widths 4896 if (!empty($this->spanborddet)) { 4897 4898 if (!isset($this->spanborddet['B'])) { 4899 $this->spanborddet['B'] = ['s' => 0, 'style' => '', 'w' => 0]; 4900 } 4901 4902 if (!isset($this->spanborddet['T'])) { 4903 $this->spanborddet['T'] = ['s' => 0, 'style' => '', 'w' => 0]; 4904 } 4905 4906 if (!isset($this->spanborddet['L'])) { 4907 $this->spanborddet['L'] = ['s' => 0, 'style' => '', 'w' => 0]; 4908 } 4909 4910 if (!isset($this->spanborddet['R'])) { 4911 $this->spanborddet['R'] = ['s' => 0, 'style' => '', 'w' => 0]; 4912 } 4913 4914 $bbw = $this->spanborddet['B']['w']; 4915 $tbw = $this->spanborddet['T']['w']; 4916 $lbw = $this->spanborddet['L']['w']; 4917 $rbw = $this->spanborddet['R']['w']; 4918 } 4919 4920 if ($fill == 1 || $border == 1 || !empty($this->spanborddet)) { 4921 4922 if (!empty($this->spanborddet)) { 4923 4924 if ($fill == 1) { 4925 $s .= sprintf('%.3F %.3F %.3F %.3F re f ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bg_boxtop + $tbw) * Mpdf::SCALE, ($w + $lbw + $rbw) * Mpdf::SCALE, (-$bg_boxheight - $tbw - $bbw) * Mpdf::SCALE); 4926 } 4927 4928 $s.= ' q '; 4929 $dashon = 3; 4930 $dashoff = 3.5; 4931 $dot = 2.5; 4932 4933 if ($tbw) { 4934 $short = 0; 4935 4936 if ($this->spanborddet['T']['style'] == 'dashed') { 4937 $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $tbw * $dashon * Mpdf::SCALE, $tbw * $dashoff * Mpdf::SCALE); 4938 } elseif ($this->spanborddet['T']['style'] == 'dotted') { 4939 $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $tbw * $dot * Mpdf::SCALE, -$tbw / 2 * Mpdf::SCALE); 4940 $short = $tbw / 2; 4941 } else { 4942 $s .= ' 0 j 0 J [] 0 d '; 4943 } 4944 4945 if ($this->spanborddet['T']['style'] != 'dotted') { 4946 $s .= 'q '; 4947 $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE); 4948 $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE); 4949 $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE); 4950 $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE); 4951 $s .= ' h W n '; // Ends path no-op & Sets the clipping path 4952 } 4953 4954 $c = $this->SetDColor($this->spanborddet['T']['c'], true); 4955 4956 if ($this->spanborddet['T']['style'] == 'double') { 4957 $s .= sprintf(' %s %.3F w ', $c, $tbw / 3 * Mpdf::SCALE); 4958 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw * 5 / 6) * Mpdf::SCALE, ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw * 5 / 6) * Mpdf::SCALE); 4959 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 6) * Mpdf::SCALE, ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 6) * Mpdf::SCALE); 4960 } elseif ($this->spanborddet['T']['style'] == 'dotted') { 4961 $s .= sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE); 4962 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE); 4963 } else { 4964 $s .= sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE); 4965 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE); 4966 } 4967 4968 if ($this->spanborddet['T']['style'] != 'dotted') { 4969 $s .= ' Q '; 4970 } 4971 } 4972 if ($bbw) { 4973 4974 $short = 0; 4975 if ($this->spanborddet['B']['style'] == 'dashed') { 4976 $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $bbw * $dashon * Mpdf::SCALE, $bbw * $dashoff * Mpdf::SCALE); 4977 } elseif ($this->spanborddet['B']['style'] == 'dotted') { 4978 $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $bbw * $dot * Mpdf::SCALE, -$bbw / 2 * Mpdf::SCALE); 4979 $short = $bbw / 2; 4980 } else { 4981 $s .= ' 0 j 0 J [] 0 d '; 4982 } 4983 4984 if ($this->spanborddet['B']['style'] != 'dotted') { 4985 $s .= 'q '; 4986 $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE); 4987 $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE); 4988 $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE); 4989 $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE); 4990 $s .= ' h W n '; // Ends path no-op & Sets the clipping path 4991 } 4992 4993 $c = $this->SetDColor($this->spanborddet['B']['c'], true); 4994 4995 if ($this->spanborddet['B']['style'] == 'double') { 4996 $s .= sprintf(' %s %.3F w ', $c, $bbw / 3 * Mpdf::SCALE); 4997 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 6) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 6) * Mpdf::SCALE); 4998 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw * 5 / 6) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw * 5 / 6) * Mpdf::SCALE); 4999 } elseif ($this->spanborddet['B']['style'] == 'dotted') { 5000 $s .= sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE); 5001 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE); 5002 } else { 5003 $s .= sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE); 5004 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE); 5005 } 5006 5007 if ($this->spanborddet['B']['style'] != 'dotted') { 5008 $s .= ' Q '; 5009 } 5010 } 5011 5012 if ($lbw) { 5013 $short = 0; 5014 if ($this->spanborddet['L']['style'] == 'dashed') { 5015 $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $lbw * $dashon * Mpdf::SCALE, $lbw * $dashoff * Mpdf::SCALE); 5016 } elseif ($this->spanborddet['L']['style'] == 'dotted') { 5017 $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $lbw * $dot * Mpdf::SCALE, -$lbw / 2 * Mpdf::SCALE); 5018 $short = $lbw / 2; 5019 } else { 5020 $s .= ' 0 j 0 J [] 0 d '; 5021 } 5022 5023 if ($this->spanborddet['L']['style'] != 'dotted') { 5024 $s .= 'q '; 5025 $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE); 5026 $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE); 5027 $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE); 5028 $s .= sprintf('%.3F %.3F l ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE); 5029 $s .= ' h W n '; // Ends path no-op & Sets the clipping path 5030 } 5031 5032 $c = $this->SetDColor($this->spanborddet['L']['c'], true); 5033 if ($this->spanborddet['L']['style'] == 'double') { 5034 $s .= sprintf(' %s %.3F w ', $c, $lbw / 3 * Mpdf::SCALE); 5035 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE); 5036 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE); 5037 } elseif ($this->spanborddet['L']['style'] == 'dotted') { 5038 $s .= sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE); 5039 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE); 5040 } else { 5041 $s .= sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE); 5042 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE); 5043 } 5044 5045 if ($this->spanborddet['L']['style'] != 'dotted') { 5046 $s .= ' Q '; 5047 } 5048 } 5049 5050 if ($rbw) { 5051 5052 $short = 0; 5053 if ($this->spanborddet['R']['style'] == 'dashed') { 5054 $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $rbw * $dashon * Mpdf::SCALE, $rbw * $dashoff * Mpdf::SCALE); 5055 } elseif ($this->spanborddet['R']['style'] == 'dotted') { 5056 $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $rbw * $dot * Mpdf::SCALE, -$rbw / 2 * Mpdf::SCALE); 5057 $short = $rbw / 2; 5058 } else { 5059 $s .= ' 0 j 0 J [] 0 d '; 5060 } 5061 5062 if ($this->spanborddet['R']['style'] != 'dotted') { 5063 $s .= 'q '; 5064 $s .= sprintf('%.3F %.3F m ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE); 5065 $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE); 5066 $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE); 5067 $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE); 5068 $s .= ' h W n '; // Ends path no-op & Sets the clipping path 5069 } 5070 5071 $c = $this->SetDColor($this->spanborddet['R']['c'], true); 5072 if ($this->spanborddet['R']['style'] == 'double') { 5073 $s .= sprintf(' %s %.3F w ', $c, $rbw / 3 * Mpdf::SCALE); 5074 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE); 5075 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE); 5076 } elseif ($this->spanborddet['R']['style'] == 'dotted') { 5077 $s .= sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE); 5078 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE); 5079 } else { 5080 $s .= sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE); 5081 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE); 5082 } 5083 5084 if ($this->spanborddet['R']['style'] != 'dotted') { 5085 $s .= ' Q '; 5086 } 5087 } 5088 5089 $s.= ' Q '; 5090 5091 } else { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock 5092 5093 if ($fill == 1) { 5094 $op = ($border == 1) ? 'B' : 'f'; 5095 } else { 5096 $op = 'S'; 5097 } 5098 5099 $s .= sprintf('%.3F %.3F %.3F %.3F re %s ', $this->x * Mpdf::SCALE, ($this->h - $bg_boxtop) * Mpdf::SCALE, $w * Mpdf::SCALE, -$bg_boxheight * Mpdf::SCALE, $op); 5100 } 5101 } 5102 5103 if (is_string($border)) { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock 5104 5105 $x = $this->x; 5106 $y = $this->y; 5107 5108 if (is_int(strpos($border, 'L'))) { 5109 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE); 5110 } 5111 5112 if (is_int(strpos($border, 'T'))) { 5113 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE); 5114 } 5115 5116 if (is_int(strpos($border, 'R'))) { 5117 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE); 5118 } 5119 5120 if (is_int(strpos($border, 'B'))) { 5121 $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE); 5122 } 5123 } 5124 5125 if ($txt != '') { 5126 5127 if ($exactWidth) { 5128 $stringWidth = $w; 5129 } else { 5130 $stringWidth = $this->GetStringWidth($txt, true, $OTLdata, $textvar) + ( $this->charspacing * mb_strlen($txt, $this->mb_enc) / Mpdf::SCALE ) + ( $this->ws * mb_substr_count($txt, ' ', $this->mb_enc) / Mpdf::SCALE ); 5131 } 5132 5133 // Set x OFFSET FOR PRINTING 5134 if ($align == 'R') { 5135 $dx = $w - $this->cMarginR - $stringWidth - $lcpaddingR; 5136 } elseif ($align == 'C') { 5137 $dx = (($w - $stringWidth ) / 2); 5138 } elseif ($align == 'L' or $align == 'J') { 5139 $dx = $this->cMarginL + $lcpaddingL; 5140 } else { 5141 $dx = 0; 5142 } 5143 5144 if ($this->ColorFlag) { 5145 $s .='q ' . $this->TextColor . ' '; 5146 } 5147 5148 // OUTLINE 5149 if (isset($this->textparam['outline-s']) && $this->textparam['outline-s'] && !($textvar & TextVars::FC_SMALLCAPS)) { // mPDF 5.7.1 5150 $s .=' ' . sprintf('%.3F w', $this->LineWidth * Mpdf::SCALE) . ' '; 5151 $s .=" $this->DrawColor "; 5152 $s .=" 2 Tr "; 5153 } elseif ($this->falseBoldWeight && strpos($this->ReqFontStyle, "B") !== false && strpos($this->FontStyle, "B") === false && !($textvar & TextVars::FC_SMALLCAPS)) { // can't use together with OUTLINE or Small Caps // mPDF 5.7.1 ??? why not with SmallCaps ??? 5154 $s .= ' 2 Tr 1 J 1 j '; 5155 $s .= ' ' . sprintf('%.3F w', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight) . ' '; 5156 $tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG 5157 if ($this->FillColor != $tc) { 5158 $s .= ' ' . $tc . ' '; 5159 } // stroke (outline) = same colour as text(fill) 5160 } else { 5161 $s .=" 0 Tr "; 5162 } 5163 5164 if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) { // Artificial italic 5165 $aix = '1 0 0.261799 1 %.3F %.3F Tm '; 5166 } else { 5167 $aix = '%.3F %.3F Td '; 5168 } 5169 5170 $px = ($this->x + $dx) * Mpdf::SCALE; 5171 $py = ($this->h - ($this->y + $glyphYorigin - $baseline_shift)) * Mpdf::SCALE; 5172 5173 // THE TEXT 5174 $txt2 = $txt; 5175 $sub = ''; 5176 $this->CurrentFont['used'] = true; 5177 5178 /* * ************** SIMILAR TO Text() ************************ */ 5179 5180 // IF corefonts AND NOT SmCaps AND NOT Kerning 5181 // Just output text; charspacing and wordspacing already set by charspacing (Tc) and ws (Tw) 5182 if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) { 5183 $txt2 = $this->writer->escape($txt2); 5184 $sub .= sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2); 5185 } // IF NOT corefonts AND NO wordspacing AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL 5186 // Just output text 5187 elseif (!$this->usingCoreFont && !$this->ws && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) { 5188 // IF SIP/SMP 5189 if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) { 5190 $txt2 = $this->UTF8toSubset($txt2); 5191 $sub .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2); 5192 } // NOT SIP/SMP 5193 else { 5194 $txt2 = $this->writer->utf8ToUtf16BigEndian($txt2, false); 5195 $txt2 = $this->writer->escape($txt2); 5196 $sub .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2); 5197 } 5198 } // IF NOT corefonts AND IS wordspacing AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL 5199 // Output text word by word with an adjustment to the intercharacter spacing for SPACEs to form word spacing 5200 // IF multibyte - Tw has no effect - need to do word spacing using an adjustment before each space 5201 elseif (!$this->usingCoreFont && $this->ws && !((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && (!empty($OTLdata['GPOSinfo']) || (strpos($OTLdata['group'], 'M') !== false && $this->charspacing)) )) { 5202 $space = " "; 5203 $space = $this->writer->utf8ToUtf16BigEndian($space, false); 5204 $space = $this->writer->escape($space); 5205 $sub .=sprintf('BT ' . $aix . ' %.3F Tc [', $px, $py, $this->charspacing); 5206 $t = explode(' ', $txt2); 5207 $numt = count($t); 5208 for ($i = 0; $i < $numt; $i++) { 5209 $tx = $t[$i]; 5210 $tx = $this->writer->utf8ToUtf16BigEndian($tx, false); 5211 $tx = $this->writer->escape($tx); 5212 $sub .=sprintf('(%s) ', $tx); 5213 if (($i + 1) < $numt) { 5214 $adj = -($this->ws) * 1000 / $this->FontSizePt; 5215 $sub .=sprintf('%d(%s) ', $adj, $space); 5216 } 5217 } 5218 $sub .='] TJ '; 5219 $sub .=' ET'; 5220 } // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP] 5221 else { 5222 $sub = $this->applyGPOSpdf($txt, $aix, $px, $py, $OTLdata, $textvar); 5223 } 5224 5225 /** ************** END SIMILAR TO Text() ************************ */ 5226 5227 if ($this->shrin_k > 1) { 5228 $shrin_k = $this->shrin_k; 5229 } else { 5230 $shrin_k = 1; 5231 } 5232 5233 // UNDERLINE 5234 if ($textvar & TextVars::FD_UNDERLINE) { // mPDF 5.7.1 // mPDF 6 5235 5236 // mPDF 5.7.3 inline text-decoration parameters 5237 5238 $c = isset($this->textparam['u-decoration']['color']) ? $this->textparam['u-decoration']['color'] : ''; 5239 if ($this->FillColor != $c) { 5240 $sub .= ' ' . $c . ' '; 5241 } 5242 5243 // mPDF 5.7.3 inline text-decoration parameters 5244 $decorationfontkey = isset($this->textparam['u-decoration']['fontkey']) ? $this->textparam['u-decoration']['fontkey'] : ''; 5245 $decorationfontsize = isset($this->textparam['u-decoration']['fontsize']) ? $this->textparam['u-decoration']['fontsize'] / $shrin_k : 0; 5246 5247 if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) { 5248 $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize; 5249 } else { 5250 $ut = 60 / 1000 * $decorationfontsize; 5251 } 5252 5253 if (isset($this->fonts[$decorationfontkey]['up']) && $this->fonts[$decorationfontkey]['up']) { 5254 $up = $this->fonts[$decorationfontkey]['up']; 5255 } else { 5256 $up = -100; 5257 } 5258 5259 $adjusty = (-$up / 1000 * $decorationfontsize) + $ut / 2; 5260 $ubaseline = isset($this->textparam['u-decoration']['baseline']) 5261 ? $glyphYorigin - $this->textparam['u-decoration']['baseline'] / $shrin_k 5262 : $glyphYorigin; 5263 5264 $olw = $this->LineWidth; 5265 5266 $sub .= ' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE)); 5267 $sub .= ' ' . $this->_dounderline($this->x + $dx, $this->y + $ubaseline + $adjusty, $txt, $OTLdata, $textvar); 5268 $sub .= ' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE)); 5269 5270 if ($this->FillColor != $c) { 5271 $sub .= ' ' . $this->FillColor . ' '; 5272 } 5273 } 5274 5275 // STRIKETHROUGH 5276 if ($textvar & TextVars::FD_LINETHROUGH) { // mPDF 5.7.1 // mPDF 6 5277 5278 // mPDF 5.7.3 inline text-decoration parameters 5279 $c = $this->textparam['s-decoration']['color']; 5280 5281 if ($this->FillColor != $c) { 5282 $sub .= ' ' . $c . ' '; 5283 } 5284 5285 // mPDF 5.7.3 inline text-decoration parameters 5286 $decorationfontkey = $this->textparam['s-decoration']['fontkey']; 5287 $decorationfontsize = $this->textparam['s-decoration']['fontsize'] / $shrin_k; 5288 5289 // Use yStrikeoutSize from OS/2 if available 5290 if (isset($this->fonts[$decorationfontkey]['strs']) && $this->fonts[$decorationfontkey]['strs']) { 5291 $ut = $this->fonts[$decorationfontkey]['strs'] / 1000 * $decorationfontsize; 5292 } // else use underlineThickness from post if available 5293 elseif (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) { 5294 $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize; 5295 } else { 5296 $ut = 50 / 1000 * $decorationfontsize; 5297 } 5298 5299 // Use yStrikeoutPosition from OS/2 if available 5300 if (isset($this->fonts[$decorationfontkey]['strp']) && $this->fonts[$decorationfontkey]['strp']) { 5301 $up = $this->fonts[$decorationfontkey]['strp']; 5302 $adjusty = (-$up / 1000 * $decorationfontsize); 5303 } // else use a fraction ($this->baselineS) of CapHeight 5304 else { 5305 if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) { 5306 $ch = $this->fonts[$decorationfontkey]['desc']['CapHeight']; 5307 } else { 5308 $ch = 700; 5309 } 5310 $adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineS; 5311 } 5312 5313 $sbaseline = $glyphYorigin - $this->textparam['s-decoration']['baseline'] / $shrin_k; 5314 5315 $olw = $this->LineWidth; 5316 5317 $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE)); 5318 $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $sbaseline + $adjusty, $txt, $OTLdata, $textvar); 5319 $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE)); 5320 5321 if ($this->FillColor != $c) { 5322 $sub .= ' ' . $this->FillColor . ' '; 5323 } 5324 } 5325 5326 // mPDF 5.7.3 inline text-decoration parameters 5327 // OVERLINE 5328 if ($textvar & TextVars::FD_OVERLINE) { // mPDF 5.7.1 // mPDF 6 5329 // mPDF 5.7.3 inline text-decoration parameters 5330 $c = $this->textparam['o-decoration']['color']; 5331 if ($this->FillColor != $c) { 5332 $sub .= ' ' . $c . ' '; 5333 } 5334 5335 // mPDF 5.7.3 inline text-decoration parameters 5336 $decorationfontkey = (int) (((float) $this->textparam['o-decoration']['fontkey']) / $shrin_k); 5337 $decorationfontsize = $this->textparam['o-decoration']['fontsize']; 5338 5339 if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) { 5340 $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize; 5341 } else { 5342 $ut = 60 / 1000 * $decorationfontsize; 5343 } 5344 if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) { 5345 $ch = $this->fonts[$decorationfontkey]['desc']['CapHeight']; 5346 } else { 5347 $ch = 700; 5348 } 5349 $adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineO; 5350 $obaseline = $glyphYorigin - $this->textparam['o-decoration']['baseline'] / $shrin_k; 5351 $olw = $this->LineWidth; 5352 $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE)); 5353 $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $obaseline + $adjusty, $txt, $OTLdata, $textvar); 5354 $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE)); 5355 if ($this->FillColor != $c) { 5356 $sub .= ' ' . $this->FillColor . ' '; 5357 } 5358 } 5359 5360 // TEXT SHADOW 5361 if ($this->textshadow) { // First to process is last in CSS comma separated shadows 5362 foreach ($this->textshadow as $ts) { 5363 $s .= ' q '; 5364 $s .= $this->SetTColor($ts['col'], true) . "\n"; 5365 if ($ts['col'][0] == 5 && ord($ts['col'][4]) < 100) { // RGBa 5366 $s .= $this->SetAlpha(ord($ts['col'][4]) / 100, 'Normal', true, 'F') . "\n"; 5367 } elseif ($ts['col'][0] == 6 && ord($ts['col'][5]) < 100) { // CMYKa 5368 $s .= $this->SetAlpha(ord($ts['col'][5]) / 100, 'Normal', true, 'F') . "\n"; 5369 } elseif ($ts['col'][0] == 1 && $ts['col'][2] == 1 && ord($ts['col'][3]) < 100) { // Gray 5370 $s .= $this->SetAlpha(ord($ts['col'][3]) / 100, 'Normal', true, 'F') . "\n"; 5371 } 5372 $s .= sprintf(' 1 0 0 1 %.4F %.4F cm', $ts['x'] * Mpdf::SCALE, -$ts['y'] * Mpdf::SCALE) . "\n"; 5373 $s .= $sub; 5374 $s .= ' Q '; 5375 } 5376 } 5377 5378 $s .= $sub; 5379 5380 // COLOR 5381 if ($this->ColorFlag) { 5382 $s .=' Q'; 5383 } 5384 5385 // LINK 5386 if ($link != '') { 5387 $this->Link($this->x, $boxtop, $w, $boxheight, $link); 5388 } 5389 } 5390 if ($s) { 5391 $this->writer->write($s); 5392 } 5393 5394 // WORD SPACING 5395 if ($this->ws && !$this->usingCoreFont) { 5396 $this->writer->write(sprintf('BT %.3F Tc ET', $this->charspacing)); 5397 } 5398 $this->lasth = $h; 5399 if (strpos($txt, "\n") !== false) { 5400 $ln = 1; // cell recognizes \n from <BR> tag 5401 } 5402 if ($ln > 0) { 5403 // Go to next line 5404 $this->y += $h; 5405 if ($ln == 1) { 5406 // Move to next line 5407 if ($currentx != 0) { 5408 $this->x = $currentx; 5409 } else { 5410 $this->x = $this->lMargin; 5411 } 5412 } 5413 } else { 5414 $this->x+=$w; 5415 } 5416 } 5417 5418 function applyGPOSpdf($txt, $aix, $x, $y, $OTLdata, $textvar = 0) 5419 { 5420 // Generate PDF string 5421 // ============================== 5422 if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) { 5423 $sipset = true; 5424 } else { 5425 $sipset = false; 5426 } 5427 5428 if ($textvar & TextVars::FC_SMALLCAPS) { 5429 $smcaps = true; 5430 } // IF SmallCaps using transformation, NOT OTL 5431 else { 5432 $smcaps = false; 5433 } 5434 5435 if ($sipset) { 5436 $fontid = $last_fontid = $original_fontid = $this->CurrentFont['subsetfontids'][0]; 5437 } else { 5438 $fontid = $last_fontid = $original_fontid = $this->CurrentFont['i']; 5439 } 5440 $SmallCapsON = false; // state: uppercase/not 5441 $lastSmallCapsON = false; // state: uppercase/not 5442 $last_fontsize = $fontsize = $this->FontSizePt; 5443 $last_fontstretch = $fontstretch = 100; 5444 $groupBreak = false; 5445 5446 $unicode = $this->UTF8StringToArray($txt); 5447 5448 $GPOSinfo = (isset($OTLdata['GPOSinfo']) ? $OTLdata['GPOSinfo'] : []); 5449 $charspacing = ($this->charspacing * 1000 / $this->FontSizePt); 5450 $wordspacing = ($this->ws * 1000 / $this->FontSizePt); 5451 5452 $XshiftBefore = 0; 5453 $XshiftAfter = 0; 5454 $lastYPlacement = 0; 5455 5456 if ($sipset) { 5457 // mPDF 6 DELETED ******** 5458 // $txt= preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $txt); // ? Need to adjust OTL info 5459 // $txt= preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $txt); // ? Need to adjust OTL info 5460 $tj = '<'; 5461 } else { 5462 $tj = '('; 5463 } 5464 5465 for ($i = 0; $i < count($unicode); $i++) { 5466 $c = $unicode[$i]; 5467 $tx = ''; 5468 $XshiftBefore = $XshiftAfter; 5469 $XshiftAfter = 0; 5470 $YPlacement = 0; 5471 $groupBreak = false; 5472 $kashida = 0; 5473 if (!empty($OTLdata)) { 5474 // YPlacement from GPOS 5475 if (isset($GPOSinfo[$i]['YPlacement']) && $GPOSinfo[$i]['YPlacement']) { 5476 $YPlacement = $GPOSinfo[$i]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm']; 5477 $groupBreak = true; 5478 } 5479 // XPlacement from GPOS 5480 if (isset($GPOSinfo[$i]['XPlacement']) && $GPOSinfo[$i]['XPlacement']) { 5481 if (!isset($GPOSinfo[$i]['wDir']) || $GPOSinfo[$i]['wDir'] != 'RTL') { 5482 if (isset($GPOSinfo[$i]['BaseWidth'])) { 5483 $GPOSinfo[$i]['XPlacement'] -= $GPOSinfo[$i]['BaseWidth']; 5484 } 5485 } 5486 5487 // Convert to PDF Text space (thousandths of a unit ); 5488 $XshiftBefore += $GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm']; 5489 $XshiftAfter += -$GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm']; 5490 } 5491 5492 // Kashida from GPOS 5493 // Kashida is set as an absolute length value, but to adjust text needs to be converted to 5494 // font-related size 5495 if (isset($GPOSinfo[$i]['kashida_space']) && $GPOSinfo[$i]['kashida_space']) { 5496 $kashida = $GPOSinfo[$i]['kashida_space']; 5497 } 5498 5499 if ($c == 32) { // word spacing 5500 $XshiftAfter += $wordspacing; 5501 } 5502 5503 if (substr($OTLdata['group'], ($i + 1), 1) != 'M') { // Don't add inter-character spacing before Marks 5504 $XshiftAfter += $charspacing; 5505 } 5506 5507 // ...applyGPOSpdf... 5508 // XAdvance from GPOS - Convert to PDF Text space (thousandths of a unit ); 5509 if (((isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] != 'RTL') || !isset($GPOSinfo[$i]['wDir'])) && isset($GPOSinfo[$i]['XAdvanceL']) && $GPOSinfo[$i]['XAdvanceL']) { 5510 $XshiftAfter += $GPOSinfo[$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm']; 5511 } elseif (isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] == 'RTL' && isset($GPOSinfo[$i]['XAdvanceR']) && $GPOSinfo[$i]['XAdvanceR']) { 5512 $XshiftAfter += $GPOSinfo[$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm']; 5513 } 5514 } // Character & Word spacing - if NOT OTL 5515 else { 5516 $XshiftAfter += $charspacing; 5517 if ($c == 32) { 5518 $XshiftAfter += $wordspacing; 5519 } 5520 } 5521 5522 // IF Kerning done using pairs rather than OTL 5523 if ($textvar & TextVars::FC_KERNING) { 5524 if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) { 5525 $XshiftBefore += $this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]]; 5526 } 5527 } 5528 5529 if ($YPlacement != $lastYPlacement) { 5530 $groupBreak = true; 5531 } 5532 5533 if ($XshiftBefore) { // +ve value in PDF moves to the left 5534 // If Fontstretch is ongoing, need to adjust X adjustments because these will be stretched out. 5535 $XshiftBefore *= 100 / $last_fontstretch; 5536 if ($sipset) { 5537 $tj .= sprintf('>%d<', (-$XshiftBefore)); 5538 } else { 5539 $tj .= sprintf(')%d(', (-$XshiftBefore)); 5540 } 5541 } 5542 5543 // Small-Caps 5544 if ($smcaps) { 5545 if (isset($this->upperCase[$c])) { 5546 $c = $this->upperCase[$c]; 5547 // $this->CurrentFont['subset'][$this->upperCase[$c]] = $this->upperCase[$c]; // add the CAP to subset 5548 $SmallCapsON = true; 5549 // For $sipset 5550 if (!$lastSmallCapsON) { // Turn ON SmallCaps 5551 $groupBreak = true; 5552 $fontstretch = $this->smCapsStretch; 5553 $fontsize = $this->FontSizePt * $this->smCapsScale; 5554 } 5555 } else { 5556 $SmallCapsON = false; 5557 if ($lastSmallCapsON) { // Turn OFF SmallCaps 5558 $groupBreak = true; 5559 $fontstretch = 100; 5560 $fontsize = $this->FontSizePt; 5561 } 5562 } 5563 } 5564 5565 // Prepare Text and Select Font ID 5566 if ($sipset) { 5567 // mPDF 6 DELETED ******** 5568 // if ($c == 7 || $c == 8) { 5569 // if ($original_fontid != $last_fontid) { 5570 // $groupBreak = true; 5571 // $fontid = $original_fontid; 5572 // } 5573 // if ($c == 7) { $tj .= $this->aliasNbPgHex; } 5574 // else { $tj .= $this->aliasNbPgGpHex; } 5575 // continue; 5576 // } 5577 for ($j = 0; $j < 99; $j++) { 5578 $init = array_search($c, $this->CurrentFont['subsets'][$j]); 5579 if ($init !== false) { 5580 if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) { 5581 $groupBreak = true; 5582 $fontid = $this->CurrentFont['subsetfontids'][$j]; 5583 } 5584 $tx = sprintf("%02s", strtoupper(dechex($init))); 5585 break; 5586 } elseif (count($this->CurrentFont['subsets'][$j]) < 255) { 5587 $n = count($this->CurrentFont['subsets'][$j]); 5588 $this->CurrentFont['subsets'][$j][$n] = $c; 5589 if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) { 5590 $groupBreak = true; 5591 $fontid = $this->CurrentFont['subsetfontids'][$j]; 5592 } 5593 $tx = sprintf("%02s", strtoupper(dechex($n))); 5594 break; 5595 } elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) { 5596 $this->CurrentFont['subsets'][($j + 1)] = [0 => 0]; 5597 $this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1; 5598 $this->extraFontSubsets++; 5599 } 5600 } 5601 } else { 5602 $tx = UtfString::code2utf($c); 5603 if ($this->usingCoreFont) { 5604 $tx = utf8_decode($tx); 5605 } else { 5606 $tx = $this->writer->utf8ToUtf16BigEndian($tx, false); 5607 } 5608 $tx = $this->writer->escape($tx); 5609 } 5610 5611 // If any settings require a new Text Group 5612 if ($groupBreak || $fontstretch != $last_fontstretch) { 5613 if ($sipset) { 5614 $tj .= '>] TJ '; 5615 } else { 5616 $tj .= ')] TJ '; 5617 } 5618 if ($fontid != $last_fontid || $fontsize != $last_fontsize) { 5619 $tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize); 5620 } 5621 if ($fontstretch != $last_fontstretch) { 5622 $tj .= sprintf('%d Tz ', $fontstretch); 5623 } 5624 if ($YPlacement != $lastYPlacement) { 5625 $tj .= sprintf('%.3F Ts ', $YPlacement); 5626 } 5627 if ($sipset) { 5628 $tj .= '[<'; 5629 } else { 5630 $tj .= '[('; 5631 } 5632 } 5633 5634 // Output the code for the txt character 5635 $tj .= $tx; 5636 $lastSmallCapsON = $SmallCapsON; 5637 $last_fontid = $fontid; 5638 $last_fontsize = $fontsize; 5639 $last_fontstretch = $fontstretch; 5640 5641 // Kashida 5642 if ($kashida) { 5643 $c = 0x0640; // add the Tatweel U+0640 5644 if (isset($this->CurrentFont['subset'])) { 5645 $this->CurrentFont['subset'][$c] = $c; 5646 } 5647 $kashida *= 1000 / $this->FontSizePt; 5648 $tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640); 5649 5650 // Get YPlacement from next Base character 5651 $nextbase = $i + 1; 5652 while ($OTLdata['group'][$nextbase] != 'C') { 5653 $nextbase++; 5654 } 5655 if (isset($GPOSinfo[$nextbase]) && isset($GPOSinfo[$nextbase]['YPlacement']) && $GPOSinfo[$nextbase]['YPlacement']) { 5656 $YPlacement = $GPOSinfo[$nextbase]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm']; 5657 } 5658 5659 // Prepare Text and Select Font ID 5660 if ($sipset) { 5661 for ($j = 0; $j < 99; $j++) { 5662 $init = array_search($c, $this->CurrentFont['subsets'][$j]); 5663 if ($init !== false) { 5664 if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) { 5665 $fontid = $this->CurrentFont['subsetfontids'][$j]; 5666 } 5667 $tx = sprintf("%02s", strtoupper(dechex($init))); 5668 break; 5669 } elseif (count($this->CurrentFont['subsets'][$j]) < 255) { 5670 $n = count($this->CurrentFont['subsets'][$j]); 5671 $this->CurrentFont['subsets'][$j][$n] = $c; 5672 if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) { 5673 $fontid = $this->CurrentFont['subsetfontids'][$j]; 5674 } 5675 $tx = sprintf("%02s", strtoupper(dechex($n))); 5676 break; 5677 } elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) { 5678 $this->CurrentFont['subsets'][($j + 1)] = [0 => 0]; 5679 $this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1; 5680 $this->extraFontSubsets++; 5681 } 5682 } 5683 } else { 5684 $tx = UtfString::code2utf($c); 5685 $tx = $this->writer->utf8ToUtf16BigEndian($tx, false); 5686 $tx = $this->writer->escape($tx); 5687 } 5688 5689 if ($kashida > $tatw) { 5690 // Insert multiple tatweel characters, repositioning the last one to give correct total length 5691 $fontstretch = 100; 5692 $nt = intval($kashida / $tatw); 5693 $nudgeback = (($nt + 1) * $tatw) - $kashida; 5694 $optx = str_repeat($tx, $nt); 5695 if ($sipset) { 5696 $optx .= sprintf('>%d<', ($nudgeback)); 5697 } else { 5698 $optx .= sprintf(')%d(', ($nudgeback)); 5699 } 5700 $optx .= $tx; // #last 5701 } else { 5702 // Insert single tatweel character and use fontstretch to get correct length 5703 $fontstretch = ($kashida / $tatw) * 100; 5704 $optx = $tx; 5705 } 5706 5707 if ($sipset) { 5708 $tj .= '>] TJ '; 5709 } else { 5710 $tj .= ')] TJ '; 5711 } 5712 if ($fontid != $last_fontid || $fontsize != $last_fontsize) { 5713 $tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize); 5714 } 5715 if ($fontstretch != $last_fontstretch) { 5716 $tj .= sprintf('%d Tz ', $fontstretch); 5717 } 5718 $tj .= sprintf('%.3F Ts ', $YPlacement); 5719 if ($sipset) { 5720 $tj .= '[<'; 5721 } else { 5722 $tj .= '[('; 5723 } 5724 5725 // Output the code for the txt character(s) 5726 $tj .= $optx; 5727 $last_fontid = $fontid; 5728 $last_fontstretch = $fontstretch; 5729 $fontstretch = 100; 5730 } 5731 5732 $lastYPlacement = $YPlacement; 5733 } 5734 5735 5736 // Finish up 5737 if ($sipset) { 5738 $tj .= '>'; 5739 if ($XshiftAfter) { 5740 $tj .= sprintf('%d', (-$XshiftAfter)); 5741 } 5742 if ($last_fontid != $original_fontid) { 5743 $tj .= '] TJ '; 5744 $tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize); 5745 $tj .= '['; 5746 } 5747 $tj = preg_replace('/([^\\\])<>/', '\\1 ', $tj); 5748 } else { 5749 $tj .= ')'; 5750 if ($XshiftAfter) { 5751 $tj .= sprintf('%d', (-$XshiftAfter)); 5752 } 5753 if ($last_fontid != $original_fontid) { 5754 $tj .= '] TJ '; 5755 $tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize); 5756 $tj .= '['; 5757 } 5758 $tj = preg_replace('/([^\\\])\(\)/', '\\1 ', $tj); 5759 } 5760 5761 $s = sprintf(' BT ' . $aix . ' 0 Tc 0 Tw [%s] TJ ET ', $x, $y, $tj); 5762 5763 // echo $s."\n\n"; // exit; 5764 5765 return $s; 5766 } 5767 5768 function _kern($txt, $mode, $aix, $x, $y) 5769 { 5770 if ($mode == 'MBTw') { // Multibyte requiring word spacing 5771 $space = ' '; 5772 // Convert string to UTF-16BE without BOM 5773 $space = $this->writer->utf8ToUtf16BigEndian($space, false); 5774 $space = $this->writer->escape($space); 5775 $s = sprintf(' BT ' . $aix, $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE); 5776 $t = explode(' ', $txt); 5777 for ($i = 0; $i < count($t); $i++) { 5778 $tx = $t[$i]; 5779 5780 $tj = '('; 5781 $unicode = $this->UTF8StringToArray($tx); 5782 for ($ti = 0; $ti < count($unicode); $ti++) { 5783 if ($ti > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]])) { 5784 $kern = -$this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]]; 5785 $tj .= sprintf(')%d(', $kern); 5786 } 5787 $tc = UtfString::code2utf($unicode[$ti]); 5788 $tc = $this->writer->utf8ToUtf16BigEndian($tc, false); 5789 $tj .= $this->writer->escape($tc); 5790 } 5791 $tj .= ')'; 5792 $s .= sprintf(' %.3F Tc [%s] TJ', $this->charspacing, $tj); 5793 5794 5795 if (($i + 1) < count($t)) { 5796 $s .= sprintf(' %.3F Tc (%s) Tj', $this->ws + $this->charspacing, $space); 5797 } 5798 } 5799 $s .= ' ET '; 5800 } elseif (!$this->usingCoreFont) { 5801 $s = ''; 5802 $tj = '('; 5803 $unicode = $this->UTF8StringToArray($txt); 5804 for ($i = 0; $i < count($unicode); $i++) { 5805 if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) { 5806 $kern = -$this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]]; 5807 $tj .= sprintf(')%d(', $kern); 5808 } 5809 $tx = UtfString::code2utf($unicode[$i]); 5810 $tx = $this->writer->utf8ToUtf16BigEndian($tx, false); 5811 $tj .= $this->writer->escape($tx); 5812 } 5813 $tj .= ')'; 5814 $s .= sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj); 5815 } else { // CORE Font 5816 $s = ''; 5817 $tj = '('; 5818 $l = strlen($txt); 5819 for ($i = 0; $i < $l; $i++) { 5820 if ($i > 0 && isset($this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]])) { 5821 $kern = -$this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]]; 5822 $tj .= sprintf(')%d(', $kern); 5823 } 5824 $tj .= $this->writer->escape($txt[$i]); 5825 } 5826 $tj .= ')'; 5827 $s .= sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj); 5828 } 5829 5830 return $s; 5831 } 5832 5833 function MultiCell( 5834 $w, 5835 $h, 5836 $txt, 5837 $border = 0, 5838 $align = '', 5839 $fill = 0, 5840 $link = '', 5841 $directionality = 'ltr', 5842 $encoded = false, 5843 $OTLdata = false, 5844 $maxrows = false 5845 ) { 5846 // maxrows is called from mpdfform->TEXTAREA 5847 // Parameter (pre-)encoded - When called internally from form::textarea - 5848 // mb_encoding already done and OTL - but not reverse RTL 5849 if (!$encoded) { 5850 5851 $txt = $this->purify_utf8_text($txt); 5852 5853 if ($this->text_input_as_HTML) { 5854 $txt = $this->all_entities_to_utf8($txt); 5855 } 5856 5857 if ($this->usingCoreFont) { 5858 $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8'); 5859 } 5860 5861 if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) { 5862 $this->biDirectional = true; 5863 } 5864 5865 /* -- OTL -- */ 5866 if (!is_array($OTLdata)) { 5867 unset($OTLdata); 5868 } 5869 5870 // Use OTL OpenType Table Layout - GSUB & GPOS 5871 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 5872 $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']); 5873 $OTLdata = $this->otl->OTLdata; 5874 } 5875 5876 if ($directionality == 'rtl' || $this->biDirectional) { 5877 if (!isset($OTLdata)) { 5878 $unicode = $this->UTF8StringToArray($txt, false); 5879 $is_strong = false; 5880 $this->getBasicOTLdata($OTLdata, $unicode, $is_strong); 5881 } 5882 } 5883 /* -- END OTL -- */ 5884 } 5885 5886 if (!$align) { 5887 $align = $this->defaultAlign; 5888 } 5889 5890 // Output text with automatic or explicit line breaks 5891 $cw = &$this->CurrentFont['cw']; 5892 5893 if ($w == 0) { 5894 $w = $this->w - $this->rMargin - $this->x; 5895 } 5896 5897 $wmax = ($w - ($this->cMarginL + $this->cMarginR)); 5898 5899 if ($this->usingCoreFont) { 5900 $s = str_replace("\r", '', $txt); 5901 $nb = strlen($s); 5902 while ($nb > 0 and $s[$nb - 1] == "\n") { 5903 $nb--; 5904 } 5905 } else { 5906 $s = str_replace("\r", '', $txt); 5907 $nb = mb_strlen($s, $this->mb_enc); 5908 while ($nb > 0 and mb_substr($s, $nb - 1, 1, $this->mb_enc) == "\n") { 5909 $nb--; 5910 } 5911 } 5912 5913 $b = 0; 5914 5915 if ($border) { 5916 5917 if ($border == 1) { 5918 $border = 'LTRB'; 5919 $b = 'LRT'; 5920 $b2 = 'LR'; 5921 } else { 5922 $b2 = ''; 5923 if (is_int(strpos($border, 'L'))) { 5924 $b2 .= 'L'; 5925 } 5926 if (is_int(strpos($border, 'R'))) { 5927 $b2 .= 'R'; 5928 } 5929 $b = is_int(strpos($border, 'T')) ? $b2 . 'T' : $b2; 5930 } 5931 } 5932 5933 $sep = -1; 5934 $i = 0; 5935 $j = 0; 5936 $l = 0; 5937 $ns = 0; 5938 $nl = 1; 5939 5940 $rows = 0; 5941 $start_y = $this->y; 5942 5943 if (!$this->usingCoreFont) { 5944 5945 $inclCursive = false; 5946 5947 if (preg_match("/([" . $this->pregCURSchars . "])/u", $s)) { 5948 $inclCursive = true; 5949 } 5950 5951 while ($i < $nb) { 5952 5953 // Get next character 5954 $c = mb_substr($s, $i, 1, $this->mb_enc); 5955 5956 if ($c === "\n") { // Explicit line break 5957 5958 // WORD SPACING 5959 $this->ResetSpacing(); 5960 $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc)); 5961 $tmpOTLdata = false; 5962 5963 /* -- OTL -- */ 5964 if (isset($OTLdata)) { 5965 $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j); 5966 $this->otl->trimOTLdata($tmpOTLdata, false, true); 5967 $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata); 5968 } 5969 /* -- END OTL -- */ 5970 5971 $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata); 5972 5973 if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) { 5974 return false; 5975 } 5976 5977 $i++; 5978 $sep = -1; 5979 $j = $i; 5980 $l = 0; 5981 $ns = 0; 5982 $nl++; 5983 5984 if ($border and $nl == 2) { 5985 $b = $b2; 5986 } 5987 5988 continue; 5989 } 5990 5991 if ($c == " ") { 5992 $sep = $i; 5993 $ls = $l; 5994 $ns++; 5995 } 5996 5997 $l += $this->GetCharWidthNonCore($c); 5998 5999 if ($l > $wmax) { 6000 6001 // Automatic line break 6002 if ($sep == -1) { // Only one word 6003 6004 if ($i == $j) { 6005 $i++; 6006 } 6007 6008 // WORD SPACING 6009 $this->ResetSpacing(); 6010 $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc)); 6011 $tmpOTLdata = false; 6012 6013 /* -- OTL -- */ 6014 if (isset($OTLdata)) { 6015 $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j); 6016 $this->otl->trimOTLdata($tmpOTLdata, false, true); 6017 $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata); 6018 } 6019 /* -- END OTL -- */ 6020 6021 $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata); 6022 6023 } else { 6024 6025 $tmp = rtrim(mb_substr($s, $j, $sep - $j, $this->mb_enc)); 6026 $tmpOTLdata = false; 6027 6028 /* -- OTL -- */ 6029 if (isset($OTLdata)) { 6030 $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $sep - $j); 6031 $this->otl->trimOTLdata($tmpOTLdata, false, true); 6032 } 6033 /* -- END OTL -- */ 6034 6035 if ($align === 'J') { 6036 6037 // JUSTIFY J using Unicode fonts (Word spacing doesn't work) 6038 // WORD SPACING UNICODE 6039 // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly 6040 6041 $tmp = str_replace(chr(194) . chr(160), chr(32), $tmp); 6042 $len_ligne = $this->GetStringWidth($tmp, false, $tmpOTLdata); 6043 $nb_carac = mb_strlen($tmp, $this->mb_enc); 6044 $nb_spaces = mb_substr_count($tmp, ' ', $this->mb_enc); 6045 6046 // Take off number of Marks 6047 // Use GPOS OTL 6048 6049 if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'])) { 6050 if (isset($tmpOTLdata['group']) && $tmpOTLdata['group']) { 6051 $nb_carac -= substr_count($tmpOTLdata['group'], 'M'); 6052 } 6053 } 6054 6055 list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), $inclCursive, $tmpOTLdata); 6056 $this->SetSpacing($charspacing, $ws); 6057 } 6058 6059 if (isset($OTLdata)) { 6060 $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata); 6061 } 6062 6063 $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata); 6064 6065 $i = $sep + 1; 6066 } 6067 6068 if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) { 6069 return false; 6070 } 6071 6072 $sep = -1; 6073 $j = $i; 6074 $l = 0; 6075 $ns = 0; 6076 $nl++; 6077 6078 if ($border and $nl == 2) { 6079 $b = $b2; 6080 } 6081 6082 } else { 6083 $i++; 6084 } 6085 } 6086 6087 // Last chunk 6088 // WORD SPACING 6089 6090 $this->ResetSpacing(); 6091 6092 } else { 6093 6094 while ($i < $nb) { 6095 6096 // Get next character 6097 $c = $s[$i]; 6098 if ($c === "\n") { 6099 6100 // Explicit line break 6101 // WORD SPACING 6102 6103 $this->ResetSpacing(); 6104 $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link); 6105 6106 if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) { 6107 return false; 6108 } 6109 6110 $i++; 6111 $sep = -1; 6112 $j = $i; 6113 $l = 0; 6114 $ns = 0; 6115 $nl++; 6116 6117 if ($border and $nl == 2) { 6118 $b = $b2; 6119 } 6120 6121 continue; 6122 } 6123 6124 if ($c === ' ') { 6125 $sep = $i; 6126 $ls = $l; 6127 $ns++; 6128 } 6129 6130 $l += $this->GetCharWidthCore($c); 6131 6132 if ($l > $wmax) { 6133 6134 // Automatic line break 6135 if ($sep == -1) { 6136 6137 if ($i == $j) { 6138 $i++; 6139 } 6140 6141 // WORD SPACING 6142 $this->ResetSpacing(); 6143 $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link); 6144 6145 } else { 6146 6147 if ($align === 'J') { 6148 6149 $tmp = rtrim(substr($s, $j, $sep - $j)); 6150 6151 // JUSTIFY J using Unicode fonts (Word spacing doesn't work) 6152 // WORD SPACING NON_UNICODE/CJK 6153 // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly 6154 6155 $tmp = str_replace(chr(160), chr(32), $tmp); 6156 $len_ligne = $this->GetStringWidth($tmp); 6157 $nb_carac = strlen($tmp); 6158 $nb_spaces = substr_count($tmp, ' '); 6159 $tmpOTLdata = []; 6160 6161 list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), false, $tmpOTLdata); 6162 $this->SetSpacing($charspacing, $ws); 6163 } 6164 6165 $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill, $link); 6166 $i = $sep + 1; 6167 } 6168 6169 if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) { 6170 return false; 6171 } 6172 6173 $sep = -1; 6174 $j = $i; 6175 $l = 0; 6176 $ns = 0; 6177 $nl++; 6178 6179 if ($border and $nl == 2) { 6180 $b = $b2; 6181 } 6182 6183 } else { 6184 $i++; 6185 } 6186 } 6187 6188 // Last chunk 6189 // WORD SPACING 6190 6191 $this->ResetSpacing(); 6192 } 6193 6194 // Last chunk 6195 if ($border and is_int(strpos($border, 'B'))) { 6196 $b .= 'B'; 6197 } 6198 6199 if (!$this->usingCoreFont) { 6200 6201 $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc)); 6202 $tmpOTLdata = false; 6203 6204 /* -- OTL -- */ 6205 if (isset($OTLdata)) { 6206 $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j); 6207 $this->otl->trimOTLdata($tmpOTLdata, false, true); 6208 $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata); 6209 } 6210 /* -- END OTL -- */ 6211 6212 $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata); 6213 } else { 6214 $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link); 6215 } 6216 6217 $this->x = $this->lMargin; 6218 } 6219 6220 /* -- DIRECTW -- */ 6221 6222 function Write($h, $txt, $currentx = 0, $link = '', $directionality = 'ltr', $align = '', $fill = 0) 6223 { 6224 if (empty($this->directWrite)) { 6225 $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter); 6226 } 6227 6228 $this->directWrite->Write($h, $txt, $currentx, $link, $directionality, $align, $fill); 6229 } 6230 6231 /* -- END DIRECTW -- */ 6232 6233 6234 /* -- HTML-CSS -- */ 6235 6236 function saveInlineProperties() 6237 { 6238 $saved = []; 6239 $saved['family'] = $this->FontFamily; 6240 $saved['style'] = $this->FontStyle; 6241 $saved['sizePt'] = $this->FontSizePt; 6242 $saved['size'] = $this->FontSize; 6243 $saved['HREF'] = $this->HREF; 6244 $saved['textvar'] = $this->textvar; // mPDF 5.7.1 6245 $saved['OTLtags'] = $this->OTLtags; // mPDF 5.7.1 6246 $saved['textshadow'] = $this->textshadow; 6247 $saved['linewidth'] = $this->LineWidth; 6248 $saved['drawcolor'] = $this->DrawColor; 6249 $saved['textparam'] = $this->textparam; 6250 $saved['lSpacingCSS'] = $this->lSpacingCSS; 6251 $saved['wSpacingCSS'] = $this->wSpacingCSS; 6252 $saved['I'] = $this->I; 6253 $saved['B'] = $this->B; 6254 $saved['colorarray'] = $this->colorarray; 6255 $saved['bgcolorarray'] = $this->spanbgcolorarray; 6256 $saved['border'] = $this->spanborddet; 6257 $saved['color'] = $this->TextColor; 6258 $saved['bgcolor'] = $this->FillColor; 6259 $saved['lang'] = $this->currentLang; 6260 $saved['fontLanguageOverride'] = $this->fontLanguageOverride; // mPDF 5.7.1 6261 $saved['display_off'] = $this->inlineDisplayOff; 6262 6263 return $saved; 6264 } 6265 6266 function restoreInlineProperties(&$saved) 6267 { 6268 $FontFamily = $saved['family']; 6269 $this->FontStyle = $saved['style']; 6270 $this->FontSizePt = $saved['sizePt']; 6271 $this->FontSize = $saved['size']; 6272 6273 $this->currentLang = $saved['lang']; 6274 $this->fontLanguageOverride = $saved['fontLanguageOverride']; // mPDF 5.7.1 6275 6276 $this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well 6277 6278 $this->HREF = $saved['HREF']; 6279 $this->textvar = $saved['textvar']; // mPDF 5.7.1 6280 $this->OTLtags = $saved['OTLtags']; // mPDF 5.7.1 6281 $this->textshadow = $saved['textshadow']; 6282 $this->LineWidth = $saved['linewidth']; 6283 $this->DrawColor = $saved['drawcolor']; 6284 $this->textparam = $saved['textparam']; 6285 $this->inlineDisplayOff = $saved['display_off']; 6286 6287 $this->lSpacingCSS = $saved['lSpacingCSS']; 6288 if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') { 6289 $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize); 6290 } else { 6291 $this->fixedlSpacing = false; 6292 } 6293 $this->wSpacingCSS = $saved['wSpacingCSS']; 6294 if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') { 6295 $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize); 6296 } else { 6297 $this->minwSpacing = 0; 6298 } 6299 6300 $this->SetFont($FontFamily, $saved['style'], $saved['sizePt'], false); 6301 6302 $this->currentfontstyle = $saved['style']; 6303 $this->currentfontsize = $saved['sizePt']; 6304 $this->SetStylesArray(['B' => $saved['B'], 'I' => $saved['I']]); // mPDF 5.7.1 6305 6306 $this->TextColor = $saved['color']; 6307 $this->FillColor = $saved['bgcolor']; 6308 $this->colorarray = $saved['colorarray']; 6309 $cor = $saved['colorarray']; 6310 if ($cor) { 6311 $this->SetTColor($cor); 6312 } 6313 $this->spanbgcolorarray = $saved['bgcolorarray']; 6314 $cor = $saved['bgcolorarray']; 6315 if ($cor) { 6316 $this->SetFColor($cor); 6317 } 6318 $this->spanborddet = $saved['border']; 6319 } 6320 6321 // Used when ColActive for tables - updated to return first block with background fill OR borders 6322 function GetFirstBlockFill() 6323 { 6324 // Returns the first blocklevel that uses a bgcolor fill 6325 $startfill = 0; 6326 for ($i = 1; $i <= $this->blklvl; $i++) { 6327 if ($this->blk[$i]['bgcolor'] || $this->blk[$i]['border_left']['w'] || $this->blk[$i]['border_right']['w'] || $this->blk[$i]['border_top']['w'] || $this->blk[$i]['border_bottom']['w']) { 6328 $startfill = $i; 6329 break; 6330 } 6331 } 6332 return $startfill; 6333 } 6334 6335 // -------------------------FLOWING BLOCK------------------------------------// 6336 // The following functions were originally written by Damon Kohler // 6337 // --------------------------------------------------------------------------// 6338 6339 function saveFont() 6340 { 6341 $saved = []; 6342 $saved['family'] = $this->FontFamily; 6343 $saved['style'] = $this->FontStyle; 6344 $saved['sizePt'] = $this->FontSizePt; 6345 $saved['size'] = $this->FontSize; 6346 $saved['curr'] = &$this->CurrentFont; 6347 $saved['lang'] = $this->currentLang; // mPDF 6 6348 $saved['color'] = $this->TextColor; 6349 $saved['spanbgcolor'] = $this->spanbgcolor; 6350 $saved['spanbgcolorarray'] = $this->spanbgcolorarray; 6351 $saved['bord'] = $this->spanborder; 6352 $saved['border'] = $this->spanborddet; 6353 $saved['HREF'] = $this->HREF; 6354 $saved['textvar'] = $this->textvar; // mPDF 5.7.1 6355 $saved['textshadow'] = $this->textshadow; 6356 $saved['linewidth'] = $this->LineWidth; 6357 $saved['drawcolor'] = $this->DrawColor; 6358 $saved['textparam'] = $this->textparam; 6359 $saved['ReqFontStyle'] = $this->ReqFontStyle; 6360 $saved['fixedlSpacing'] = $this->fixedlSpacing; 6361 $saved['minwSpacing'] = $this->minwSpacing; 6362 return $saved; 6363 } 6364 6365 function restoreFont(&$saved, $write = true) 6366 { 6367 if (!isset($saved) || empty($saved)) { 6368 return; 6369 } 6370 6371 $this->FontFamily = $saved['family']; 6372 $this->FontStyle = $saved['style']; 6373 $this->FontSizePt = $saved['sizePt']; 6374 $this->FontSize = $saved['size']; 6375 $this->CurrentFont = &$saved['curr']; 6376 $this->currentLang = $saved['lang']; // mPDF 6 6377 $this->TextColor = $saved['color']; 6378 $this->spanbgcolor = $saved['spanbgcolor']; 6379 $this->spanbgcolorarray = $saved['spanbgcolorarray']; 6380 $this->spanborder = $saved['bord']; 6381 $this->spanborddet = $saved['border']; 6382 $this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well 6383 $this->HREF = $saved['HREF']; 6384 $this->fixedlSpacing = $saved['fixedlSpacing']; 6385 $this->minwSpacing = $saved['minwSpacing']; 6386 $this->textvar = $saved['textvar']; // mPDF 5.7.1 6387 $this->textshadow = $saved['textshadow']; 6388 $this->LineWidth = $saved['linewidth']; 6389 $this->DrawColor = $saved['drawcolor']; 6390 $this->textparam = $saved['textparam']; 6391 if ($write) { 6392 $this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], true, true); // force output 6393 $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 6394 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) { 6395 $this->writer->write($fontout); 6396 } 6397 $this->pageoutput[$this->page]['Font'] = $fontout; 6398 } else { 6399 $this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], false); 6400 } 6401 $this->ReqFontStyle = $saved['ReqFontStyle']; 6402 } 6403 6404 function newFlowingBlock($w, $h, $a = '', $is_table = false, $blockstate = 0, $newblock = true, $blockdir = 'ltr', $table_draft = false) 6405 { 6406 if (!$a) { 6407 if ($blockdir == 'rtl') { 6408 $a = 'R'; 6409 } else { 6410 $a = 'L'; 6411 } 6412 } 6413 $this->flowingBlockAttr['width'] = ($w * Mpdf::SCALE); 6414 // line height in user units 6415 $this->flowingBlockAttr['is_table'] = $is_table; 6416 $this->flowingBlockAttr['table_draft'] = $table_draft; 6417 $this->flowingBlockAttr['height'] = $h; 6418 $this->flowingBlockAttr['lineCount'] = 0; 6419 $this->flowingBlockAttr['align'] = $a; 6420 $this->flowingBlockAttr['font'] = []; 6421 $this->flowingBlockAttr['content'] = []; 6422 $this->flowingBlockAttr['contentB'] = []; 6423 $this->flowingBlockAttr['contentWidth'] = 0; 6424 $this->flowingBlockAttr['blockstate'] = $blockstate; 6425 6426 $this->flowingBlockAttr['newblock'] = $newblock; 6427 $this->flowingBlockAttr['valign'] = 'M'; 6428 $this->flowingBlockAttr['blockdir'] = $blockdir; 6429 $this->flowingBlockAttr['cOTLdata'] = []; // mPDF 5.7.1 6430 $this->flowingBlockAttr['lastBidiText'] = ''; // mPDF 5.7.1 6431 if (!empty($this->otl)) { 6432 $this->otl->lastBidiStrongType = ''; 6433 } // *OTL* 6434 } 6435 6436 function finishFlowingBlock($endofblock = false, $next = '') 6437 { 6438 $currentx = $this->x; 6439 // prints out the last chunk 6440 $is_table = $this->flowingBlockAttr['is_table']; 6441 $table_draft = $this->flowingBlockAttr['table_draft']; 6442 $maxWidth = & $this->flowingBlockAttr['width']; 6443 $stackHeight = & $this->flowingBlockAttr['height']; 6444 $align = & $this->flowingBlockAttr['align']; 6445 $content = & $this->flowingBlockAttr['content']; 6446 $contentB = & $this->flowingBlockAttr['contentB']; 6447 $font = & $this->flowingBlockAttr['font']; 6448 $contentWidth = & $this->flowingBlockAttr['contentWidth']; 6449 $lineCount = & $this->flowingBlockAttr['lineCount']; 6450 $valign = & $this->flowingBlockAttr['valign']; 6451 $blockstate = $this->flowingBlockAttr['blockstate']; 6452 6453 $cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1 6454 $newblock = $this->flowingBlockAttr['newblock']; 6455 $blockdir = $this->flowingBlockAttr['blockdir']; 6456 6457 // *********** BLOCK BACKGROUND COLOR *****************// 6458 if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) { 6459 $fill = 0; 6460 } else { 6461 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 6462 $fill = 0; 6463 } 6464 6465 $hanger = ''; 6466 // Always right trim! 6467 // Right trim last content and adjust width if needed to justify (later) 6468 if (isset($content[count($content) - 1]) && preg_match('/[ ]+$/', $content[count($content) - 1], $m)) { 6469 $strip = strlen($m[0]); 6470 $content[count($content) - 1] = substr($content[count($content) - 1], 0, (strlen($content[count($content) - 1]) - $strip)); 6471 /* -- OTL -- */ 6472 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 6473 $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true); 6474 } 6475 /* -- END OTL -- */ 6476 } 6477 6478 // the amount of space taken up so far in user units 6479 $usedWidth = 0; 6480 6481 // COLS 6482 $oldcolumn = $this->CurrCol; 6483 6484 if ($this->ColActive && !$is_table) { 6485 $this->breakpoints[$this->CurrCol][] = $this->y; 6486 } // *COLUMNS* 6487 // Print out each chunk 6488 6489 /* -- TABLES -- */ 6490 if ($is_table) { 6491 $ipaddingL = 0; 6492 $ipaddingR = 0; 6493 $paddingL = 0; 6494 $paddingR = 0; 6495 } else { 6496 /* -- END TABLES -- */ 6497 $ipaddingL = $this->blk[$this->blklvl]['padding_left']; 6498 $ipaddingR = $this->blk[$this->blklvl]['padding_right']; 6499 $paddingL = ($ipaddingL * Mpdf::SCALE); 6500 $paddingR = ($ipaddingR * Mpdf::SCALE); 6501 $this->cMarginL = $this->blk[$this->blklvl]['border_left']['w']; 6502 $this->cMarginR = $this->blk[$this->blklvl]['border_right']['w']; 6503 6504 // Added mPDF 3.0 Float DIV 6505 $fpaddingR = 0; 6506 $fpaddingL = 0; 6507 /* -- CSS-FLOAT -- */ 6508 if (count($this->floatDivs)) { 6509 list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl); 6510 if ($r_exists) { 6511 $fpaddingR = $r_width; 6512 } 6513 if ($l_exists) { 6514 $fpaddingL = $l_width; 6515 } 6516 } 6517 /* -- END CSS-FLOAT -- */ 6518 6519 $usey = $this->y + 0.002; 6520 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) { 6521 $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w']; 6522 } 6523 /* -- CSS-IMAGE-FLOAT -- */ 6524 // If float exists at this level 6525 if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) { 6526 $fpaddingR += $this->floatmargins['R']['w']; 6527 } 6528 if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) { 6529 $fpaddingL += $this->floatmargins['L']['w']; 6530 } 6531 /* -- END CSS-IMAGE-FLOAT -- */ 6532 } // *TABLES* 6533 6534 6535 $lineBox = []; 6536 6537 $this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table); 6538 6539 if ($is_table && count($content) == 0) { 6540 $stackHeight = 0; 6541 } 6542 6543 if ($table_draft) { 6544 $this->y += $stackHeight; 6545 $this->objectbuffer = []; 6546 return 0; 6547 } 6548 6549 // While we're at it, check if contains cursive text 6550 // Change NBSP to SPACE. 6551 // Re-calculate contentWidth 6552 $contentWidth = 0; 6553 6554 foreach ($content as $k => $chunk) { 6555 $this->restoreFont($font[$k], false); 6556 if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) { 6557 // Soft Hyphens chr(173) 6558 if (!$this->usingCoreFont) { 6559 /* -- OTL -- */ 6560 // mPDF 5.7.1 6561 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 6562 $this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad"); 6563 $this->otl->replaceSpace($chunk, $cOTLdata[$k]); 6564 $content[$k] = $chunk; 6565 } /* -- END OTL -- */ else { // *OTL* 6566 $content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk); 6567 $content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk); 6568 } // *OTL* 6569 } elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') { 6570 $content[$k] = $chunk = str_replace(chr(173), '', $chunk); 6571 $content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk); 6572 } 6573 $contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE; 6574 } elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) { 6575 // LIST MARKERS // mPDF 6 Lists 6576 if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') { 6577 // do nothing 6578 } else { 6579 $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE; 6580 } 6581 } 6582 } 6583 6584 if (isset($font[count($font) - 1])) { 6585 $lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : ''); 6586 $lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : ''); 6587 } else { 6588 $lastfontreqstyle = null; 6589 $lastfontstyle = null; 6590 } 6591 if ($blockdir == 'ltr' && $lastfontreqstyle && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic 6592 $lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE; 6593 } else { 6594 $lastitalic = 0; 6595 } 6596 6597 // Get PAGEBREAK TO TEST for height including the bottom border/padding 6598 $check_h = max($this->divheight, $stackHeight); 6599 6600 // This fixes a proven bug... 6601 if ($endofblock && $newblock && $blockstate == 0 && !$content) { 6602 $check_h = 0; 6603 } 6604 // but ? needs to fix potentially more widespread... 6605 // if (!$content) { $check_h = 0; } 6606 6607 if ($this->blklvl > 0 && !$is_table) { 6608 if ($endofblock && $blockstate > 1) { 6609 if ($this->blk[$this->blklvl]['page_break_after_avoid']) { 6610 $check_h += $stackHeight; 6611 } 6612 $check_h += ($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']); 6613 } 6614 if (($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0) || ($endofblock && $blockstate == 3 && $lineCount == 0)) { 6615 $check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']); 6616 } 6617 } 6618 6619 // Force PAGE break if column height cannot take check-height 6620 if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) { 6621 $this->SetCol($this->NbCol - 1); 6622 } 6623 6624 // Avoid just border/background-color moved on to next page 6625 if ($endofblock && $blockstate > 1 && !$content) { 6626 $buff = $this->margBuffer; 6627 } else { 6628 $buff = 0; 6629 } 6630 6631 6632 // PAGEBREAK 6633 if (!$is_table && ($this->y + $check_h) > ($this->PageBreakTrigger + $buff) and ! $this->InFooter and $this->AcceptPageBreak()) { 6634 $bak_x = $this->x; // Current X position 6635 // WORD SPACING 6636 $ws = $this->ws; // Word Spacing 6637 $charspacing = $this->charspacing; // Character Spacing 6638 $this->ResetSpacing(); 6639 6640 $this->AddPage($this->CurOrientation); 6641 6642 $this->x = $bak_x; 6643 // Added to correct for OddEven Margins 6644 $currentx += $this->MarginCorrection; 6645 $this->x += $this->MarginCorrection; 6646 6647 // WORD SPACING 6648 $this->SetSpacing($charspacing, $ws); 6649 } 6650 6651 6652 /* -- COLUMNS -- */ 6653 // COLS 6654 // COLUMN CHANGE 6655 if ($this->CurrCol != $oldcolumn) { 6656 $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 6657 $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 6658 $oldcolumn = $this->CurrCol; 6659 } 6660 6661 6662 if ($this->ColActive && !$is_table) { 6663 $this->breakpoints[$this->CurrCol][] = $this->y; 6664 } 6665 /* -- END COLUMNS -- */ 6666 6667 // TOP MARGIN 6668 if ($newblock && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && $lineCount == 0 && !$is_table) { 6669 $this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']); 6670 if ($this->ColActive) { 6671 $this->breakpoints[$this->CurrCol][] = $this->y; 6672 } // *COLUMNS* 6673 } 6674 6675 if ($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0 && !$is_table) { 6676 $this->blk[$this->blklvl]['y0'] = $this->y; 6677 $this->blk[$this->blklvl]['startpage'] = $this->page; 6678 if ($this->blk[$this->blklvl]['float']) { 6679 $this->blk[$this->blklvl]['float_start_y'] = $this->y; 6680 } 6681 if ($this->ColActive) { 6682 $this->breakpoints[$this->CurrCol][] = $this->y; 6683 } // *COLUMNS* 6684 } 6685 6686 // Paragraph INDENT 6687 $WidthCorrection = 0; 6688 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) { 6689 $ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false); // mPDF 5.7.4 6690 $WidthCorrection = ($ti * Mpdf::SCALE); 6691 } 6692 6693 6694 // PADDING and BORDER spacing/fill 6695 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 0) && (!$is_table)) { 6696 // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom 6697 $this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1); 6698 if ($this->ColActive) { 6699 $this->breakpoints[$this->CurrCol][] = $this->y; 6700 } // *COLUMNS* 6701 $this->x = $currentx; 6702 } 6703 6704 6705 // Added mPDF 3.0 Float DIV 6706 $fpaddingR = 0; 6707 $fpaddingL = 0; 6708 /* -- CSS-FLOAT -- */ 6709 if (count($this->floatDivs)) { 6710 list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl); 6711 if ($r_exists) { 6712 $fpaddingR = $r_width; 6713 } 6714 if ($l_exists) { 6715 $fpaddingL = $l_width; 6716 } 6717 } 6718 /* -- END CSS-FLOAT -- */ 6719 6720 $usey = $this->y + 0.002; 6721 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) { 6722 $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w']; 6723 } 6724 /* -- CSS-IMAGE-FLOAT -- */ 6725 // If float exists at this level 6726 if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) { 6727 $fpaddingR += $this->floatmargins['R']['w']; 6728 } 6729 if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) { 6730 $fpaddingL += $this->floatmargins['L']['w']; 6731 } 6732 /* -- END CSS-IMAGE-FLOAT -- */ 6733 6734 6735 if ($content) { 6736 // In FinishFlowing Block no lines are justified as it is always last line 6737 // but if CJKorphan has allowed content width to go over max width, use J charspacing to compress line 6738 // JUSTIFICATION J - NOT! 6739 $nb_carac = 0; 6740 $nb_spaces = 0; 6741 $jcharspacing = 0; 6742 $jkashida = 0; 6743 $jws = 0; 6744 $inclCursive = false; 6745 $dottab = false; 6746 foreach ($content as $k => $chunk) { 6747 if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) { 6748 $nb_carac += mb_strlen($chunk, $this->mb_enc); 6749 $nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc); 6750 // mPDF 6 6751 // Use GPOS OTL 6752 $this->restoreFont($font[$k], false); 6753 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 6754 if (isset($cOTLdata[$k]['group']) && $cOTLdata[$k]['group']) { 6755 $nb_marks = substr_count($cOTLdata[$k]['group'], 'M'); 6756 $nb_carac -= $nb_marks; 6757 } 6758 if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) { 6759 $inclCursive = true; 6760 } 6761 } 6762 } else { 6763 $nb_carac ++; // mPDF 6 allow spacing for inline object 6764 if ($this->objectbuffer[$k]['type'] == 'dottab') { 6765 $dottab = $this->objectbuffer[$k]['outdent']; 6766 } 6767 } 6768 } 6769 6770 // DIRECTIONALITY RTL 6771 $chunkorder = range(0, count($content) - 1); // mPDF 6 6772 /* -- OTL -- */ 6773 // mPDF 6 6774 if ($blockdir == 'rtl' || $this->biDirectional) { 6775 $this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir); 6776 // From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to 6777 // $this->objectbuffer and $font ($chunkorder contains the mapping) 6778 } 6779 /* -- END OTL -- */ 6780 6781 // Remove any XAdvance from OTL data at end of line 6782 // And correct for XPlacement on last character 6783 // BIDI is applied 6784 foreach ($chunkorder as $aord => $k) { 6785 if (count($cOTLdata)) { 6786 $this->restoreFont($font[$k], false); 6787 // ...FinishFlowingBlock... 6788 if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line 6789 $nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character 6790 if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) { 6791 if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) { 6792 $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm']; 6793 } else { 6794 $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm']; 6795 } 6796 $w *= ($this->FontSize / 1000); 6797 $contentWidth -= $w * Mpdf::SCALE; 6798 $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0; 6799 $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0; 6800 } 6801 6802 // If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it 6803 if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) { 6804 $w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm']; 6805 $w *= ($this->FontSize / 1000); 6806 $contentWidth -= $w * Mpdf::SCALE; 6807 $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement']; 6808 $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement']; 6809 } 6810 } 6811 } 6812 } 6813 6814 // if it's justified, we need to find the char/word spacing (or if orphans have allowed length of line to go over the maxwidth) 6815 // If "orphans" in fact is just a final space - ignore this 6816 $lastchar = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc); 6817 if (preg_match("/[" . $this->CJKoverflow . "]/u", $lastchar)) { 6818 $CJKoverflow = true; 6819 } else { 6820 $CJKoverflow = false; 6821 } 6822 if ((((($contentWidth + $lastitalic) > $maxWidth) && ($content[(count($chunkorder) - 1)] != ' ') ) || 6823 (!$endofblock && $align == 'J' && ($next == 'image' || $next == 'select' || $next == 'input' || $next == 'textarea' || ($next == 'br' && $this->justifyB4br)))) && !($CJKoverflow && $this->allowCJKoverflow)) { 6824 // WORD SPACING 6825 list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata); 6826 } /* -- CJK-FONTS -- */ elseif ($this->checkCJK && $align == 'J' && $CJKoverflow && $this->allowCJKoverflow && $this->CJKforceend) { 6827 // force-end overhang 6828 $hanger = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc); 6829 if (preg_match("/[" . $this->CJKoverflow . "]/u", $hanger)) { 6830 $content[(count($chunkorder) - 1)] = mb_substr($content[(count($chunkorder) - 1)], 0, mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, $this->mb_enc); 6831 $this->restoreFont($font[$chunkorder[count($chunkorder) - 1]], false); 6832 $contentWidth -= $this->GetStringWidth($hanger) * Mpdf::SCALE; 6833 $nb_carac -= 1; 6834 list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata); 6835 } 6836 } /* -- END CJK-FONTS -- */ 6837 6838 // Check if will fit at word/char spacing of previous line - if so continue it 6839 // but only allow a maximum of $this->jSmaxWordLast and $this->jSmaxCharLast 6840 elseif ($contentWidth < ($maxWidth - $lastitalic - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE))) && !$this->fixedlSpacing) { 6841 if ($this->ws > $this->jSmaxWordLast) { 6842 $jws = $this->jSmaxWordLast; 6843 } 6844 if ($this->charspacing > $this->jSmaxCharLast) { 6845 $jcharspacing = $this->jSmaxCharLast; 6846 } 6847 $check = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) - ( $jcharspacing * $nb_carac) - ( $jws * $nb_spaces); 6848 if ($check <= 0) { 6849 $jcharspacing = 0; 6850 $jws = 0; 6851 } 6852 } 6853 6854 $empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ); 6855 6856 6857 $empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1 6858 $empty -= ($jws * $nb_spaces); 6859 $empty -= ($jkashida); 6860 6861 $empty /= Mpdf::SCALE; 6862 6863 if (!$is_table) { 6864 $this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin'] - $empty)); 6865 $this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $empty)); 6866 } 6867 6868 $arraysize = count($chunkorder); 6869 6870 $margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR ); 6871 6872 if (!$is_table) { 6873 $this->DivLn($stackHeight, $this->blklvl, false); 6874 } // false -> don't advance y 6875 6876 $this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL; 6877 if ($dottab !== false && $blockdir == 'rtl') { 6878 $this->x -= $dottab; 6879 } elseif ($align == 'R') { 6880 $this->x += $empty; 6881 } elseif ($align == 'J' && $blockdir == 'rtl') { 6882 $this->x += $empty; 6883 } elseif ($align == 'C') { 6884 $this->x += ($empty / 2); 6885 } 6886 6887 // Paragraph INDENT 6888 $WidthCorrection = 0; 6889 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) { 6890 $ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false); // mPDF 5.7.4 6891 if ($blockdir != 'rtl') { 6892 $this->x += $ti; 6893 } // mPDF 6 6894 } 6895 6896 foreach ($chunkorder as $aord => $k) { // mPDF 5.7 6897 $chunk = $content[$aord]; 6898 if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) { 6899 $xadj = $this->x - $this->objectbuffer[$k]['OUTER-X']; 6900 $this->objectbuffer[$k]['OUTER-X'] += $xadj; 6901 $this->objectbuffer[$k]['BORDER-X'] += $xadj; 6902 $this->objectbuffer[$k]['INNER-X'] += $xadj; 6903 6904 if ($this->objectbuffer[$k]['type'] == 'listmarker') { 6905 $this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin 6906 } 6907 $yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y']; 6908 if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB 6909 $this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin 6910 } 6911 if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB 6912 $yadj += $lineBox[$k]['top']; 6913 } 6914 $this->objectbuffer[$k]['OUTER-Y'] += $yadj; 6915 $this->objectbuffer[$k]['BORDER-Y'] += $yadj; 6916 $this->objectbuffer[$k]['INNER-Y'] += $yadj; 6917 } 6918 6919 $this->restoreFont($font[$k]); // mPDF 5.7 6920 6921 if ($is_table && substr($align, 0, 1) == 'D' && $aord == 0) { 6922 $dp = $this->decimal_align[substr($align, 0, 2)]; 6923 $s = preg_split('/' . preg_quote($dp, '/') . '/', $content[0], 2); // ? needs to be /u if not core 6924 $s0 = $this->GetStringWidth($s[0], false); 6925 $this->x += ($this->decimal_offset - $s0); 6926 } 6927 6928 $this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws); 6929 $this->fixedlSpacing = false; 6930 $this->minwSpacing = 0; 6931 6932 $save_vis = $this->visibility; 6933 if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) { 6934 $this->SetVisibility($this->textparam['visibility']); 6935 } 6936 6937 // *********** SPAN BACKGROUND COLOR ***************** // 6938 if (isset($this->spanbgcolor) && $this->spanbgcolor) { 6939 $cor = $this->spanbgcolorarray; 6940 $this->SetFColor($cor); 6941 $save_fill = $fill; 6942 $spanfill = 1; 6943 $fill = 1; 6944 } 6945 if (!empty($this->spanborddet)) { 6946 if (strpos($contentB[$k], 'L') !== false && isset($this->spanborddet['L'])) { 6947 $this->x += $this->spanborddet['L']['w']; 6948 } 6949 if (strpos($contentB[$k], 'L') === false) { 6950 $this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0; 6951 } 6952 if (strpos($contentB[$k], 'R') === false) { 6953 $this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0; 6954 } 6955 } 6956 // WORD SPACING 6957 // mPDF 5.7.1 6958 $stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar); 6959 $nch = mb_strlen($chunk, $this->mb_enc); 6960 // Use GPOS OTL 6961 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 6962 if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) { 6963 $nch -= substr_count($cOTLdata[$aord]['group'], 'M'); 6964 } 6965 } 6966 $stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE ); 6967 6968 $stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE ); 6969 6970 if (isset($this->objectbuffer[$k])) { 6971 if ($this->objectbuffer[$k]['type'] == 'dottab') { 6972 $this->objectbuffer[$k]['OUTER-WIDTH'] +=$empty; 6973 $this->objectbuffer[$k]['OUTER-WIDTH'] +=$this->objectbuffer[$k]['outdent']; 6974 } 6975 // LIST MARKERS // mPDF 6 Lists 6976 if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') { 6977 // do nothing 6978 } else { 6979 $stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH']; 6980 } 6981 } 6982 6983 if ($stringWidth == 0) { 6984 $stringWidth = 0.000001; 6985 } 6986 if ($aord == $arraysize - 1) { // mPDF 5.7 6987 // mPDF 5.7.1 6988 if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) { 6989 // force-end overhang 6990 $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1 6991 $this->Cell($this->GetStringWidth($hanger), $stackHeight, $hanger, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1 6992 } else { 6993 $this->Cell($stringWidth, $stackHeight, $chunk, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1 6994 } 6995 } else { 6996 $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, 0, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // first or middle part // mPDF 5.7.1 6997 } 6998 6999 7000 if (!empty($this->spanborddet)) { 7001 if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) { 7002 $this->x += $this->spanborddet['R']['w']; 7003 } 7004 } 7005 // *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** // 7006 if (isset($spanfill) && $spanfill) { 7007 $fill = $save_fill; 7008 $spanfill = 0; 7009 if ($fill) { 7010 $this->SetFColor($bcor); 7011 } 7012 } 7013 if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) { 7014 $this->SetVisibility($save_vis); 7015 } 7016 } 7017 7018 $this->printobjectbuffer($is_table, $blockdir); 7019 $this->objectbuffer = []; 7020 $this->ResetSpacing(); 7021 } // END IF CONTENT 7022 7023 /* -- CSS-IMAGE-FLOAT -- */ 7024 // Update values if set to skipline 7025 if ($this->floatmargins) { 7026 $this->_advanceFloatMargins(); 7027 } 7028 7029 7030 if ($endofblock && $blockstate > 1) { 7031 // If float exists at this level 7032 if (isset($this->floatmargins['R']['y1'])) { 7033 $fry1 = $this->floatmargins['R']['y1']; 7034 } else { 7035 $fry1 = 0; 7036 } 7037 if (isset($this->floatmargins['L']['y1'])) { 7038 $fly1 = $this->floatmargins['L']['y1']; 7039 } else { 7040 $fly1 = 0; 7041 } 7042 if ($this->y < $fry1 || $this->y < $fly1) { 7043 $drop = max($fry1, $fly1) - $this->y; 7044 $this->DivLn($drop); 7045 $this->x = $currentx; 7046 } 7047 } 7048 /* -- END CSS-IMAGE-FLOAT -- */ 7049 7050 7051 // PADDING and BORDER spacing/fill 7052 if ($endofblock && ($blockstate > 1) && ($this->blk[$this->blklvl]['padding_bottom'] || $this->blk[$this->blklvl]['border_bottom'] || $this->blk[$this->blklvl]['css_set_height']) && (!$is_table)) { 7053 // If CSS height set, extend bottom - if on same page as block started, and CSS HEIGHT > actual height, 7054 // and does not force pagebreak 7055 $extra = 0; 7056 if (isset($this->blk[$this->blklvl]['css_set_height']) && $this->blk[$this->blklvl]['css_set_height'] && $this->blk[$this->blklvl]['startpage'] == $this->page) { 7057 // predicted height 7058 $h1 = ($this->y - $this->blk[$this->blklvl]['y0']) + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']; 7059 if ($h1 < ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top'])) { 7060 $extra = ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top']) - $h1; 7061 } 7062 if ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra > $this->PageBreakTrigger) { 7063 $extra = $this->PageBreakTrigger - ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']); 7064 } 7065 } 7066 7067 // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom 7068 $this->DivLn($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra, -3, true, false, 2); 7069 $this->x = $currentx; 7070 7071 if ($this->ColActive) { 7072 $this->breakpoints[$this->CurrCol][] = $this->y; 7073 } // *COLUMNS* 7074 } 7075 7076 // SET Bottom y1 of block (used for painting borders) 7077 if (($endofblock) && ($blockstate > 1) && (!$is_table)) { 7078 $this->blk[$this->blklvl]['y1'] = $this->y; 7079 } 7080 7081 // BOTTOM MARGIN 7082 if (($endofblock) && ($blockstate > 1) && ($this->blk[$this->blklvl]['margin_bottom']) && (!$is_table)) { 7083 if ($this->y + $this->blk[$this->blklvl]['margin_bottom'] < $this->PageBreakTrigger and ! $this->InFooter) { 7084 $this->DivLn($this->blk[$this->blklvl]['margin_bottom'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']); 7085 if ($this->ColActive) { 7086 $this->breakpoints[$this->CurrCol][] = $this->y; 7087 } // *COLUMNS* 7088 } 7089 } 7090 7091 // Reset lineheight 7092 $stackHeight = $this->divheight; 7093 } 7094 7095 function printobjectbuffer($is_table = false, $blockdir = false) 7096 { 7097 if (!$blockdir) { 7098 $blockdir = $this->directionality; 7099 } 7100 7101 if ($is_table && $this->shrin_k > 1) { 7102 $k = $this->shrin_k; 7103 } else { 7104 $k = 1; 7105 } 7106 7107 $save_y = $this->y; 7108 $save_x = $this->x; 7109 7110 $save_currentfontfamily = $this->FontFamily; 7111 $save_currentfontsize = $this->FontSizePt; 7112 $save_currentfontstyle = $this->FontStyle; 7113 7114 if ($blockdir == 'rtl') { 7115 $rtlalign = 'R'; 7116 } else { 7117 $rtlalign = 'L'; 7118 } 7119 7120 foreach ($this->objectbuffer as $ib => $objattr) { 7121 7122 if ($objattr['type'] == 'bookmark' || $objattr['type'] == 'indexentry' || $objattr['type'] == 'toc') { 7123 $x = $objattr['OUTER-X']; 7124 $y = $objattr['OUTER-Y']; 7125 $this->y = $y - $this->FontSize / 2; 7126 $this->x = $x; 7127 if ($objattr['type'] == 'bookmark') { 7128 $this->Bookmark($objattr['CONTENT'], $objattr['bklevel'], $y - $this->FontSize); 7129 } // *BOOKMARKS* 7130 if ($objattr['type'] == 'indexentry') { 7131 $this->IndexEntry($objattr['CONTENT']); 7132 } // *INDEX* 7133 if ($objattr['type'] == 'toc') { 7134 $this->TOC_Entry($objattr['CONTENT'], $objattr['toclevel'], (isset($objattr['toc_id']) ? $objattr['toc_id'] : '')); 7135 } // *TOC* 7136 } /* -- ANNOTATIONS -- */ elseif ($objattr['type'] == 'annot') { 7137 if ($objattr['POS-X']) { 7138 $x = $objattr['POS-X']; 7139 } elseif ($this->annotMargin <> 0) { 7140 $x = -$objattr['OUTER-X']; 7141 } else { 7142 $x = $objattr['OUTER-X']; 7143 } 7144 if ($objattr['POS-Y']) { 7145 $y = $objattr['POS-Y']; 7146 } else { 7147 $y = $objattr['OUTER-Y'] - $this->FontSize / 2; 7148 } 7149 // Create a dummy entry in the _out/columnBuffer with position sensitive data, 7150 // linking $y-1 in the Columnbuffer with entry in $this->columnAnnots 7151 // and when columns are split in length will not break annotation from current line 7152 $this->y = $y - 1; 7153 $this->x = $x - 1; 7154 $this->Line($x - 1, $y - 1, $x - 1, $y - 1); 7155 $this->Annotation($objattr['CONTENT'], $x, $y, $objattr['ICON'], $objattr['AUTHOR'], $objattr['SUBJECT'], $objattr['OPACITY'], $objattr['COLOR'], (isset($objattr['POPUP']) ? $objattr['POPUP'] : ''), (isset($objattr['FILE']) ? $objattr['FILE'] : '')); 7156 } /* -- END ANNOTATIONS -- */ else { 7157 $y = $objattr['OUTER-Y']; 7158 $x = $objattr['OUTER-X']; 7159 $w = $objattr['OUTER-WIDTH']; 7160 $h = $objattr['OUTER-HEIGHT']; 7161 if (isset($objattr['text'])) { 7162 $texto = $objattr['text']; 7163 } 7164 $this->y = $y; 7165 $this->x = $x; 7166 if (isset($objattr['fontfamily'])) { 7167 $this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']); 7168 } 7169 } 7170 7171 // HR 7172 if ($objattr['type'] == 'hr') { 7173 $this->SetDColor($objattr['color']); 7174 switch ($objattr['align']) { 7175 case 'C': 7176 $empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH']; 7177 $empty /= 2; 7178 $x += $empty; 7179 break; 7180 case 'R': 7181 $empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH']; 7182 $x += $empty; 7183 break; 7184 } 7185 $oldlinewidth = $this->LineWidth; 7186 $this->SetLineWidth($objattr['linewidth'] / $k); 7187 $this->y += ($objattr['linewidth'] / 2) + $objattr['margin_top'] / $k; 7188 $this->Line($x, $this->y, $x + $objattr['INNER-WIDTH'], $this->y); 7189 $this->SetLineWidth($oldlinewidth); 7190 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 7191 } 7192 // IMAGE 7193 if ($objattr['type'] == 'image') { 7194 // mPDF 5.7.3 TRANSFORMS 7195 if (isset($objattr['transform'])) { 7196 $this->writer->write("\n" . '% BTR'); // Begin Transform 7197 } 7198 if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) { 7199 $this->BeginLayer($objattr['z-index']); 7200 } 7201 if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) { 7202 $this->SetVisibility($objattr['visibility']); 7203 } 7204 if (isset($objattr['opacity'])) { 7205 $this->SetAlpha($objattr['opacity']); 7206 } 7207 7208 $obiw = $objattr['INNER-WIDTH']; 7209 $obih = $objattr['INNER-HEIGHT']; 7210 7211 $sx = $objattr['orig_w'] ? ($objattr['INNER-WIDTH'] * Mpdf::SCALE / $objattr['orig_w']) : INF; 7212 $sy = $objattr['orig_h'] ? ($objattr['INNER-HEIGHT'] * Mpdf::SCALE / $objattr['orig_h']) : INF; 7213 7214 $rotate = 0; 7215 if (isset($objattr['ROTATE'])) { 7216 $rotate = $objattr['ROTATE']; 7217 } 7218 7219 if ($rotate == 90) { 7220 // Clockwise 7221 $obiw = $objattr['INNER-HEIGHT']; 7222 $obih = $objattr['INNER-WIDTH']; 7223 $tr = $this->transformTranslate(0, -$objattr['INNER-WIDTH'], true); 7224 $tr .= ' ' . $this->transformRotate(90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true); 7225 $sx = $obiw * Mpdf::SCALE / $objattr['orig_h']; 7226 $sy = $obih * Mpdf::SCALE / $objattr['orig_w']; 7227 } elseif ($rotate == -90 || $rotate == 270) { 7228 // AntiClockwise 7229 $obiw = $objattr['INNER-HEIGHT']; 7230 $obih = $objattr['INNER-WIDTH']; 7231 $tr = $this->transformTranslate($objattr['INNER-WIDTH'], ($objattr['INNER-HEIGHT'] - $objattr['INNER-WIDTH']), true); 7232 $tr .= ' ' . $this->transformRotate(-90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true); 7233 $sx = $obiw * Mpdf::SCALE / $objattr['orig_h']; 7234 $sy = $obih * Mpdf::SCALE / $objattr['orig_w']; 7235 } elseif ($rotate == 180) { 7236 // Mirror 7237 $tr = $this->transformTranslate($objattr['INNER-WIDTH'], -$objattr['INNER-HEIGHT'], true); 7238 $tr .= ' ' . $this->transformRotate(180, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-HEIGHT']), true); 7239 } else { 7240 $tr = ''; 7241 } 7242 $tr = trim($tr); 7243 if ($tr) { 7244 $tr .= ' '; 7245 } 7246 $gradmask = ''; 7247 7248 // mPDF 5.7.3 TRANSFORMS 7249 $tr2 = ''; 7250 if (isset($objattr['transform'])) { 7251 $maxsize_x = $w; 7252 $maxsize_y = $h; 7253 $cx = $x + $w / 2; 7254 $cy = $y + $h / 2; 7255 preg_match_all('/(translatex|translatey|translate|scalex|scaley|scale|rotate|skewX|skewY|skew)\((.*?)\)/is', $objattr['transform'], $m); 7256 if (count($m[0])) { 7257 for ($i = 0; $i < count($m[0]); $i++) { 7258 $c = strtolower($m[1][$i]); 7259 $v = trim($m[2][$i]); 7260 $vv = preg_split('/[ ,]+/', $v); 7261 if ($c == 'translate' && count($vv)) { 7262 $translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false); 7263 if (count($vv) == 2) { 7264 $translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false); 7265 } else { 7266 $translate_y = 0; 7267 } 7268 $tr2 .= $this->transformTranslate($translate_x, $translate_y, true) . ' '; 7269 } elseif ($c == 'translatex' && count($vv)) { 7270 $translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false); 7271 $tr2 .= $this->transformTranslate($translate_x, 0, true) . ' '; 7272 } elseif ($c == 'translatey' && count($vv)) { 7273 $translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false); 7274 $tr2 .= $this->transformTranslate(0, $translate_y, true) . ' '; 7275 } elseif ($c == 'scale' && count($vv)) { 7276 $scale_x = $vv[0] * 100; 7277 if (count($vv) == 2) { 7278 $scale_y = $vv[1] * 100; 7279 } else { 7280 $scale_y = $scale_x; 7281 } 7282 $tr2 .= $this->transformScale($scale_x, $scale_y, $cx, $cy, true) . ' '; 7283 } elseif ($c == 'scalex' && count($vv)) { 7284 $scale_x = $vv[0] * 100; 7285 $tr2 .= $this->transformScale($scale_x, 0, $cx, $cy, true) . ' '; 7286 } elseif ($c == 'scaley' && count($vv)) { 7287 $scale_y = $vv[1] * 100; 7288 $tr2 .= $this->transformScale(0, $scale_y, $cx, $cy, true) . ' '; 7289 } elseif ($c == 'skew' && count($vv)) { 7290 $angle_x = $this->ConvertAngle($vv[0], false); 7291 if (count($vv) == 2) { 7292 $angle_y = $this->ConvertAngle($vv[1], false); 7293 } else { 7294 $angle_y = 0; 7295 } 7296 $tr2 .= $this->transformSkew($angle_x, $angle_y, $cx, $cy, true) . ' '; 7297 } elseif ($c == 'skewx' && count($vv)) { 7298 $angle = $this->ConvertAngle($vv[0], false); 7299 $tr2 .= $this->transformSkew($angle, 0, $cx, $cy, true) . ' '; 7300 } elseif ($c == 'skewy' && count($vv)) { 7301 $angle = $this->ConvertAngle($vv[0], false); 7302 $tr2 .= $this->transformSkew(0, $angle, $cx, $cy, true) . ' '; 7303 } elseif ($c == 'rotate' && count($vv)) { 7304 $angle = $this->ConvertAngle($vv[0]); 7305 $tr2 .= $this->transformRotate($angle, $cx, $cy, true) . ' '; 7306 } 7307 } 7308 } 7309 } 7310 7311 // LIST MARKERS (Images) // mPDF 6 Lists 7312 if (isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') { 7313 $mw = $objattr['OUTER-WIDTH']; 7314 // NB If change marker-offset, also need to alter in function _getListMarkerWidth 7315 $adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize); 7316 if ($objattr['dir'] == 'rtl') { 7317 $objattr['INNER-X'] += $adjx; 7318 } else { 7319 $objattr['INNER-X'] -= $adjx; 7320 $objattr['INNER-X'] -= $mw; 7321 } 7322 } 7323 // mPDF 5.7.3 TRANSFORMS / BACKGROUND COLOR 7324 // Transform also affects image background 7325 if ($tr2) { 7326 $this->writer->write('q ' . $tr2 . ' '); 7327 } 7328 if (isset($objattr['bgcolor']) && $objattr['bgcolor']) { 7329 $bgcol = $objattr['bgcolor']; 7330 $this->SetFColor($bgcol); 7331 $this->Rect($x, $y, $w, $h, 'F'); 7332 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 7333 } 7334 if ($tr2) { 7335 $this->writer->write('Q'); 7336 } 7337 7338 /* -- BACKGROUNDS -- */ 7339 if (isset($objattr['GRADIENT-MASK'])) { 7340 $g = $this->gradient->parseMozGradient($objattr['GRADIENT-MASK']); 7341 if ($g) { 7342 $dummy = $this->gradient->Gradient($objattr['INNER-X'], $objattr['INNER-Y'], $obiw, $obih, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true, true); 7343 $gradmask = '/TGS' . count($this->gradients) . ' gs '; 7344 } 7345 } 7346 /* -- END BACKGROUNDS -- */ 7347 /* -- IMAGES-WMF -- */ 7348 if (isset($objattr['itype']) && $objattr['itype'] == 'wmf') { 7349 $outstring = sprintf('q ' . $tr . $tr2 . '%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X'] * Mpdf::SCALE - $sx * $objattr['wmf_x'], (($this->h - $objattr['INNER-Y']) * Mpdf::SCALE) + $sy * $objattr['wmf_y'], $objattr['ID']); // mPDF 5.7.3 TRANSFORMS 7350 } else { /* -- END IMAGES-WMF -- */ 7351 if (isset($objattr['itype']) && $objattr['itype'] == 'svg') { 7352 $outstring = sprintf('q ' . $tr . $tr2 . '%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X'] * Mpdf::SCALE - $sx * $objattr['wmf_x'], (($this->h - $objattr['INNER-Y']) * Mpdf::SCALE) + $sy * $objattr['wmf_y'], $objattr['ID']); // mPDF 5.7.3 TRANSFORMS 7353 } else { 7354 $outstring = sprintf("q " . $tr . $tr2 . "%.3F 0 0 %.3F %.3F %.3F cm " . $gradmask . "/I%d Do Q", $obiw * Mpdf::SCALE, $obih * Mpdf::SCALE, $objattr['INNER-X'] * Mpdf::SCALE, ($this->h - ($objattr['INNER-Y'] + $obih )) * Mpdf::SCALE, $objattr['ID']); // mPDF 5.7.3 TRANSFORMS 7355 } 7356 } 7357 $this->writer->write($outstring); 7358 // LINK 7359 if (isset($objattr['link'])) { 7360 $this->Link($objattr['INNER-X'], $objattr['INNER-Y'], $objattr['INNER-WIDTH'], $objattr['INNER-HEIGHT'], $objattr['link']); 7361 } 7362 if (isset($objattr['opacity'])) { 7363 $this->SetAlpha(1); 7364 } 7365 7366 // mPDF 5.7.3 TRANSFORMS 7367 // Transform also affects image borders 7368 if ($tr2) { 7369 $this->writer->write('q ' . $tr2 . ' '); 7370 } 7371 if ((isset($objattr['border_top']) && $objattr['border_top'] > 0) || (isset($objattr['border_left']) && $objattr['border_left'] > 0) || (isset($objattr['border_right']) && $objattr['border_right'] > 0) || (isset($objattr['border_bottom']) && $objattr['border_bottom'] > 0)) { 7372 $this->PaintImgBorder($objattr, $is_table); 7373 } 7374 if ($tr2) { 7375 $this->writer->write('Q'); 7376 } 7377 7378 if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) { 7379 $this->SetVisibility('visible'); 7380 } 7381 if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) { 7382 $this->EndLayer(); 7383 } 7384 // mPDF 5.7.3 TRANSFORMS 7385 if (isset($objattr['transform'])) { 7386 $this->writer->write("\n" . '% ETR'); // End Transform 7387 } 7388 } 7389 7390 if ($objattr['type'] === 'barcode') { 7391 7392 $bgcol = $this->colorConverter->convert(255, $this->PDFAXwarnings); 7393 7394 if (isset($objattr['bgcolor']) && $objattr['bgcolor']) { 7395 $bgcol = $objattr['bgcolor']; 7396 } 7397 7398 $col = $this->colorConverter->convert(0, $this->PDFAXwarnings); 7399 7400 if (isset($objattr['color']) && $objattr['color']) { 7401 $col = $objattr['color']; 7402 } 7403 7404 $this->SetFColor($bgcol); 7405 $this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F'); 7406 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 7407 7408 if (isset($objattr['BORDER-WIDTH'])) { 7409 $this->PaintImgBorder($objattr, $is_table); 7410 } 7411 7412 $barcodeTypes = ['EAN13', 'ISBN', 'ISSN', 'UPCA', 'UPCE', 'EAN8']; 7413 if (in_array($objattr['btype'], $barcodeTypes, true)) { 7414 7415 $this->WriteBarcode( 7416 $objattr['code'], 7417 $objattr['showtext'], 7418 $objattr['INNER-X'], 7419 $objattr['INNER-Y'], 7420 $objattr['bsize'], 7421 0, 7422 0, 7423 0, 7424 0, 7425 0, 7426 $objattr['bheight'], 7427 $bgcol, 7428 $col, 7429 $objattr['btype'], 7430 $objattr['bsupp'], 7431 (isset($objattr['bsupp_code']) ? $objattr['bsupp_code'] : ''), 7432 $k 7433 ); 7434 7435 } elseif ($objattr['btype'] === 'QR') { 7436 7437 if (!class_exists('Mpdf\QrCode\QrCode') || !class_exists('Mpdf\QrCode\Output\Mpdf')) { 7438 throw new \Mpdf\MpdfException('Mpdf\QrCode package was not found. Install the package from Packagist with "composer require mpdf/qrcode"'); 7439 } 7440 7441 $barcodeContent = str_replace('\r\n', "\r\n", $objattr['code']); 7442 $barcodeContent = str_replace('\n', "\n", $barcodeContent); 7443 7444 $qrcode = new QrCode\QrCode($barcodeContent, $objattr['errorlevel']); 7445 if ($objattr['disableborder']) { 7446 $qrcode->disableBorder(); 7447 } 7448 7449 $bgColor = [255, 255, 255]; 7450 if ($objattr['bgcolor']) { 7451 $bgColor = array_map( 7452 function ($col) { 7453 return intval(255 * floatval($col)); 7454 }, 7455 explode(" ", $this->SetColor($objattr['bgcolor'], 'CodeOnly')) 7456 ); 7457 } 7458 $color = [0, 0, 0]; 7459 if ($objattr['color']) { 7460 $color = array_map( 7461 function ($col) { 7462 return intval(255 * floatval($col)); 7463 }, 7464 explode(" ", $this->SetColor($objattr['color'], 'CodeOnly')) 7465 ); 7466 } 7467 7468 $out = new QrCode\Output\Mpdf(); 7469 $out->output( 7470 $qrcode, 7471 $this, 7472 $objattr['INNER-X'], 7473 $objattr['INNER-Y'], 7474 $objattr['bsize'] * 25, 7475 $bgColor, 7476 $color 7477 ); 7478 7479 unset($qrcode); 7480 7481 } else { 7482 $this->WriteBarcode2( 7483 $objattr['code'], 7484 $objattr['INNER-X'], 7485 $objattr['INNER-Y'], 7486 $objattr['bsize'], 7487 $objattr['bheight'], 7488 $bgcol, 7489 $col, 7490 $objattr['btype'], 7491 $objattr['pr_ratio'], 7492 $k, 7493 $objattr['quiet_zone_left'], 7494 $objattr['quiet_zone_right'] 7495 ); 7496 } 7497 } 7498 7499 // TEXT CIRCLE 7500 if ($objattr['type'] == 'textcircle') { 7501 $bgcol = ''; 7502 if (isset($objattr['bgcolor']) && $objattr['bgcolor']) { 7503 $bgcol = $objattr['bgcolor']; 7504 } 7505 $col = $this->colorConverter->convert(0, $this->PDFAXwarnings); 7506 if (isset($objattr['color']) && $objattr['color']) { 7507 $col = $objattr['color']; 7508 } 7509 $this->SetTColor($col); 7510 $this->SetFColor($bgcol); 7511 if ($bgcol) { 7512 $this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F'); 7513 } 7514 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 7515 if (isset($objattr['BORDER-WIDTH'])) { 7516 $this->PaintImgBorder($objattr, $is_table); 7517 } 7518 if (empty($this->directWrite)) { 7519 $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter); 7520 } 7521 if (isset($objattr['top-text'])) { 7522 $this->directWrite->CircularText($objattr['INNER-X'] + $objattr['INNER-WIDTH'] / 2, $objattr['INNER-Y'] + $objattr['INNER-HEIGHT'] / 2, $objattr['r'] / $k, $objattr['top-text'], 'top', $objattr['fontfamily'], $objattr['fontsize'] / $k, $objattr['fontstyle'], $objattr['space-width'], $objattr['char-width'], (isset($objattr['divider']) ? $objattr['divider'] : '')); 7523 } 7524 if (isset($objattr['bottom-text'])) { 7525 $this->directWrite->CircularText($objattr['INNER-X'] + $objattr['INNER-WIDTH'] / 2, $objattr['INNER-Y'] + $objattr['INNER-HEIGHT'] / 2, $objattr['r'] / $k, $objattr['bottom-text'], 'bottom', $objattr['fontfamily'], $objattr['fontsize'] / $k, $objattr['fontstyle'], $objattr['space-width'], $objattr['char-width'], (isset($objattr['divider']) ? $objattr['divider'] : '')); 7526 } 7527 } 7528 7529 $this->ResetSpacing(); 7530 7531 // LIST MARKERS (Text or bullets) // mPDF 6 Lists 7532 if ($objattr['type'] == 'listmarker') { 7533 if (isset($objattr['fontfamily'])) { 7534 $this->SetFont($objattr['fontfamily'], $objattr['fontstyle'], $objattr['fontsizept']); 7535 } 7536 $col = $this->colorConverter->convert(0, $this->PDFAXwarnings); 7537 if (isset($objattr['colorarray']) && ($objattr['colorarray'])) { 7538 $col = $objattr['colorarray']; 7539 } 7540 7541 if (isset($objattr['bullet']) && $objattr['bullet']) { // Used for position "outside" only 7542 $type = $objattr['bullet']; 7543 $size = $objattr['size']; 7544 7545 if ($objattr['listmarkerposition'] == 'inside') { 7546 $adjx = $size / 2; 7547 if ($objattr['dir'] == 'rtl') { 7548 $adjx += $objattr['offset']; 7549 } 7550 $this->x += $adjx; 7551 } else { 7552 $adjx = $objattr['offset']; 7553 $adjx += $size / 2; 7554 if ($objattr['dir'] == 'rtl') { 7555 $this->x += $adjx; 7556 } else { 7557 $this->x -= $adjx; 7558 } 7559 } 7560 7561 $yadj = $objattr['lineBox']['glyphYorigin']; 7562 if (isset($this->CurrentFont['desc']['XHeight']) && $this->CurrentFont['desc']['XHeight']) { 7563 $xh = $this->CurrentFont['desc']['XHeight']; 7564 } else { 7565 $xh = 500; 7566 } 7567 $yadj -= ($this->FontSize * $xh / 1000) * 0.625; // Vertical height of bullet (centre) from baseline= XHeight * 0.625 7568 $this->y += $yadj; 7569 7570 $this->_printListBullet($this->x, $this->y, $size, $type, $col); 7571 } else { 7572 $this->SetTColor($col); 7573 $w = $this->GetStringWidth($texto); 7574 // NB If change marker-offset, also need to alter in function _getListMarkerWidth 7575 $adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize); 7576 if ($objattr['dir'] == 'rtl') { 7577 $align = 'L'; 7578 $this->x += $adjx; 7579 } else { 7580 // Use these lines to set as marker-offset, right-aligned - default 7581 $align = 'R'; 7582 $this->x -= $adjx; 7583 $this->x -= $w; 7584 } 7585 $this->Cell($w, $this->FontSize, $texto, 0, 0, $align, 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']); 7586 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 7587 } 7588 } 7589 7590 // DOT-TAB 7591 if ($objattr['type'] == 'dottab') { 7592 if (isset($objattr['fontfamily'])) { 7593 $this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']); 7594 } 7595 $sp = $this->GetStringWidth(' '); 7596 $nb = floor(($w - 2 * $sp) / $this->GetStringWidth('.')); 7597 if ($nb > 0) { 7598 $dots = ' ' . str_repeat('.', $nb) . ' '; 7599 } else { 7600 $dots = ' '; 7601 } 7602 $col = $this->colorConverter->convert(0, $this->PDFAXwarnings); 7603 if (isset($objattr['colorarray']) && ($objattr['colorarray'])) { 7604 $col = $objattr['colorarray']; 7605 } 7606 $this->SetTColor($col); 7607 $save_dh = $this->divheight; 7608 $save_sbd = $this->spanborddet; 7609 $save_textvar = $this->textvar; // mPDF 5.7.1 7610 $this->spanborddet = ''; 7611 $this->divheight = 0; 7612 $this->textvar = 0x00; // mPDF 5.7.1 7613 7614 $this->Cell($w, $h, $dots, 0, 0, 'C', 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']); // mPDF 6 DOTTAB 7615 $this->spanborddet = $save_sbd; 7616 $this->textvar = $save_textvar; // mPDF 5.7.1 7617 $this->divheight = $save_dh; 7618 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 7619 } 7620 7621 /* -- FORMS -- */ 7622 // TEXT/PASSWORD INPUT 7623 if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD')) { 7624 $this->form->print_ob_text($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir); 7625 } 7626 7627 // TEXTAREA 7628 if ($objattr['type'] == 'textarea') { 7629 $this->form->print_ob_textarea($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir); 7630 } 7631 7632 // SELECT 7633 if ($objattr['type'] == 'select') { 7634 $this->form->print_ob_select($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir); 7635 } 7636 7637 7638 // INPUT/BUTTON as IMAGE 7639 if ($objattr['type'] == 'input' && $objattr['subtype'] == 'IMAGE') { 7640 $this->form->print_ob_imageinput($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $is_table); 7641 } 7642 7643 // BUTTON 7644 if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'SUBMIT' || $objattr['subtype'] == 'RESET' || $objattr['subtype'] == 'BUTTON')) { 7645 $this->form->print_ob_button($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir); 7646 } 7647 7648 // CHECKBOX 7649 if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'CHECKBOX')) { 7650 $this->form->print_ob_checkbox($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y); 7651 } 7652 // RADIO 7653 if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'RADIO')) { 7654 $this->form->print_ob_radio($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y); 7655 } 7656 /* -- END FORMS -- */ 7657 } 7658 7659 $this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize); 7660 7661 $this->y = $save_y; 7662 $this->x = $save_x; 7663 7664 unset($content); 7665 } 7666 7667 function _printListBullet($x, $y, $size, $type, $color) 7668 { 7669 // x and y are the centre of the bullet; size is the width and/or height in mm 7670 $fcol = $this->SetTColor($color, true); 7671 $lcol = strtoupper($fcol); // change 0 0 0 rg to 0 0 0 RG 7672 $this->writer->write(sprintf('q %s %s', $lcol, $fcol)); 7673 $this->writer->write('0 j 0 J [] 0 d'); 7674 if ($type == 'square') { 7675 $size *= 0.85; // Smaller to appear the same size as circle/disc 7676 $this->writer->write(sprintf('%.3F %.3F %.3F %.3F re f', ($x - $size / 2) * Mpdf::SCALE, ($this->h - $y + $size / 2) * Mpdf::SCALE, ($size) * Mpdf::SCALE, (-$size) * Mpdf::SCALE)); 7677 } elseif ($type == 'disc') { 7678 $this->Circle($x, $y, $size / 2, 'F'); // Fill 7679 } elseif ($type == 'circle') { 7680 $lw = $size / 12; // Line width 7681 $this->writer->write(sprintf('%.3F w ', $lw * Mpdf::SCALE)); 7682 $this->Circle($x, $y, $size / 2 - $lw / 2, 'S'); // Stroke 7683 } 7684 $this->writer->write('Q'); 7685 } 7686 7687 // mPDF 6 7688 // Get previous character and move pointers 7689 function _moveToPrevChar(&$contentctr, &$charctr, $content) 7690 { 7691 $lastchar = false; 7692 $charctr--; 7693 while ($charctr < 0) { // go back to previous $content[] 7694 $contentctr--; 7695 if ($contentctr < 0) { 7696 return false; 7697 } 7698 if ($this->usingCoreFont) { 7699 $charctr = strlen($content[$contentctr]) - 1; 7700 } else { 7701 $charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1; 7702 } 7703 } 7704 if ($this->usingCoreFont) { 7705 $lastchar = $content[$contentctr][$charctr]; 7706 } else { 7707 $lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc); 7708 } 7709 return $lastchar; 7710 } 7711 7712 // Get previous character 7713 function _getPrevChar($contentctr, $charctr, $content) 7714 { 7715 $lastchar = false; 7716 $charctr--; 7717 while ($charctr < 0) { // go back to previous $content[] 7718 $contentctr--; 7719 if ($contentctr < 0) { 7720 return false; 7721 } 7722 if ($this->usingCoreFont) { 7723 $charctr = strlen($content[$contentctr]) - 1; 7724 } else { 7725 $charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1; 7726 } 7727 } 7728 if ($this->usingCoreFont) { 7729 $lastchar = $content[$contentctr][$charctr]; 7730 } else { 7731 $lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc); 7732 } 7733 return $lastchar; 7734 } 7735 7736 function WriteFlowingBlock($s, $sOTLdata) 7737 { 7738 // mPDF 5.7.1 7739 $currentx = $this->x; 7740 $is_table = $this->flowingBlockAttr['is_table']; 7741 $table_draft = $this->flowingBlockAttr['table_draft']; 7742 // width of all the content so far in points 7743 $contentWidth = & $this->flowingBlockAttr['contentWidth']; 7744 // cell width in points 7745 $maxWidth = & $this->flowingBlockAttr['width']; 7746 $lineCount = & $this->flowingBlockAttr['lineCount']; 7747 // line height in user units 7748 $stackHeight = & $this->flowingBlockAttr['height']; 7749 $align = & $this->flowingBlockAttr['align']; 7750 $content = & $this->flowingBlockAttr['content']; 7751 $contentB = & $this->flowingBlockAttr['contentB']; 7752 $font = & $this->flowingBlockAttr['font']; 7753 $valign = & $this->flowingBlockAttr['valign']; 7754 $blockstate = $this->flowingBlockAttr['blockstate']; 7755 $cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1 7756 7757 $newblock = $this->flowingBlockAttr['newblock']; 7758 $blockdir = $this->flowingBlockAttr['blockdir']; 7759 7760 // *********** BLOCK BACKGROUND COLOR ***************** // 7761 if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) { 7762 $fill = 0; 7763 } else { 7764 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 7765 $fill = 0; 7766 } 7767 $font[] = $this->saveFont(); 7768 $content[] = ''; 7769 $contentB[] = ''; 7770 $cOTLdata[] = $sOTLdata; // mPDF 5.7.1 7771 $currContent = & $content[count($content) - 1]; 7772 7773 $CJKoverflow = false; 7774 $Oikomi = false; // mPDF 6 7775 $hanger = ''; 7776 7777 // COLS 7778 $oldcolumn = $this->CurrCol; 7779 if ($this->ColActive && !$is_table) { 7780 $this->breakpoints[$this->CurrCol][] = $this->y; 7781 } // *COLUMNS* 7782 7783 /* -- TABLES -- */ 7784 if ($is_table) { 7785 $ipaddingL = 0; 7786 $ipaddingR = 0; 7787 $paddingL = 0; 7788 $paddingR = 0; 7789 $cpaddingadjustL = 0; 7790 $cpaddingadjustR = 0; 7791 // Added mPDF 3.0 7792 $fpaddingR = 0; 7793 $fpaddingL = 0; 7794 } else { 7795 /* -- END TABLES -- */ 7796 $ipaddingL = $this->blk[$this->blklvl]['padding_left']; 7797 $ipaddingR = $this->blk[$this->blklvl]['padding_right']; 7798 $paddingL = ($ipaddingL * Mpdf::SCALE); 7799 $paddingR = ($ipaddingR * Mpdf::SCALE); 7800 $this->cMarginL = $this->blk[$this->blklvl]['border_left']['w']; 7801 $cpaddingadjustL = -$this->cMarginL; 7802 $this->cMarginR = $this->blk[$this->blklvl]['border_right']['w']; 7803 $cpaddingadjustR = -$this->cMarginR; 7804 // Added mPDF 3.0 Float DIV 7805 $fpaddingR = 0; 7806 $fpaddingL = 0; 7807 /* -- CSS-FLOAT -- */ 7808 if (count($this->floatDivs)) { 7809 list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl); 7810 if ($r_exists) { 7811 $fpaddingR = $r_width; 7812 } 7813 if ($l_exists) { 7814 $fpaddingL = $l_width; 7815 } 7816 } 7817 /* -- END CSS-FLOAT -- */ 7818 7819 $usey = $this->y + 0.002; 7820 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) { 7821 $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w']; 7822 } 7823 /* -- CSS-IMAGE-FLOAT -- */ 7824 // If float exists at this level 7825 if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) { 7826 $fpaddingR += $this->floatmargins['R']['w']; 7827 } 7828 if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) { 7829 $fpaddingL += $this->floatmargins['L']['w']; 7830 } 7831 /* -- END CSS-IMAGE-FLOAT -- */ 7832 } // *TABLES* 7833 // OBJECTS - IMAGES & FORM Elements (NB has already skipped line/page if required - in printbuffer) 7834 if (substr($s, 0, 3) == "\xbb\xa4\xac") { // identifier has been identified! 7835 $objattr = $this->_getObjAttr($s); 7836 $h_corr = 0; 7837 if ($is_table) { // *TABLES* 7838 $maximumW = ($maxWidth / Mpdf::SCALE) - ($this->cellPaddingL + $this->cMarginL + $this->cellPaddingR + $this->cMarginR); // *TABLES* 7839 } // *TABLES* 7840 else { // *TABLES* 7841 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0) && (!$is_table)) { 7842 $h_corr = $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w']; 7843 } 7844 $maximumW = ($maxWidth / Mpdf::SCALE) - ($this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_right'] + $this->blk[$this->blklvl]['border_right']['w'] + $fpaddingL + $fpaddingR ); 7845 } // *TABLES* 7846 $objattr = $this->inlineObject($objattr['type'], $this->lMargin + $fpaddingL + ($contentWidth / Mpdf::SCALE), ($this->y + $h_corr), $objattr, $this->lMargin, ($contentWidth / Mpdf::SCALE), $maximumW, $stackHeight, true, $is_table); 7847 7848 // SET LINEHEIGHT for this line ================ RESET AT END 7849 $stackHeight = max($stackHeight, $objattr['OUTER-HEIGHT']); 7850 $this->objectbuffer[count($content) - 1] = $objattr; 7851 // if (isset($objattr['vertical-align'])) { $valign = $objattr['vertical-align']; } 7852 // else { $valign = ''; } 7853 // LIST MARKERS // mPDF 6 Lists 7854 if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') { 7855 // do nothing 7856 } else { 7857 $contentWidth += ($objattr['OUTER-WIDTH'] * Mpdf::SCALE); 7858 } 7859 return; 7860 } 7861 7862 $lbw = $rbw = 0; // Border widths 7863 if (!empty($this->spanborddet)) { 7864 if (isset($this->spanborddet['L'])) { 7865 $lbw = $this->spanborddet['L']['w']; 7866 } 7867 if (isset($this->spanborddet['R'])) { 7868 $rbw = $this->spanborddet['R']['w']; 7869 } 7870 } 7871 7872 if ($this->usingCoreFont) { 7873 $clen = strlen($s); 7874 } else { 7875 $clen = mb_strlen($s, $this->mb_enc); 7876 } 7877 7878 // for every character in the string 7879 for ($i = 0; $i < $clen; $i++) { 7880 // extract the current character 7881 // get the width of the character in points 7882 if ($this->usingCoreFont) { 7883 $c = $s[$i]; 7884 // Soft Hyphens chr(173) 7885 $cw = ($this->GetCharWidthCore($c) * Mpdf::SCALE); 7886 if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1 7887 if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c])) { 7888 $cw += ($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c] * $this->FontSizePt / 1000 ); 7889 } 7890 } 7891 } else { 7892 $c = mb_substr($s, $i, 1, $this->mb_enc); 7893 $cw = ($this->GetCharWidthNonCore($c, false) * Mpdf::SCALE); 7894 // mPDF 5.7.1 7895 // Use OTL GPOS 7896 if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) { 7897 // ...WriteFlowingBlock... 7898 // Only add XAdvanceL (not sure at present whether RTL or LTR writing direction) 7899 // At this point, XAdvanceL and XAdvanceR will balance 7900 if (isset($sOTLdata['GPOSinfo'][$i]['XAdvanceL'])) { 7901 $cw += $sOTLdata['GPOSinfo'][$i]['XAdvanceL'] * (1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000) * Mpdf::SCALE; 7902 } 7903 } 7904 if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1 7905 $lastc = mb_substr($s, ($i - 1), 1, $this->mb_enc); 7906 $ulastc = $this->UTF8StringToArray($lastc, false); 7907 $uc = $this->UTF8StringToArray($c, false); 7908 if (isset($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]])) { 7909 $cw += ($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]] * $this->FontSizePt / 1000 ); 7910 } 7911 } 7912 } 7913 7914 if ($i == 0) { 7915 $cw += $lbw * Mpdf::SCALE; 7916 $contentB[(count($contentB) - 1)] .= 'L'; 7917 } 7918 if ($i == ($clen - 1)) { 7919 $cw += $rbw * Mpdf::SCALE; 7920 $contentB[(count($contentB) - 1)] .= 'R'; 7921 } 7922 if ($c == ' ') { 7923 $currContent .= $c; 7924 $contentWidth += $cw; 7925 continue; 7926 } 7927 7928 // Paragraph INDENT 7929 $WidthCorrection = 0; 7930 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) { 7931 $ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false); // mPDF 5.7.4 7932 $WidthCorrection = ($ti * Mpdf::SCALE); 7933 } 7934 // OUTDENT 7935 foreach ($this->objectbuffer as $k => $objattr) { // mPDF 6 DOTTAB 7936 if ($objattr['type'] == 'dottab') { 7937 $WidthCorrection -= ($objattr['outdent'] * Mpdf::SCALE); 7938 break; 7939 } 7940 } 7941 7942 7943 // Added mPDF 3.0 Float DIV 7944 $fpaddingR = 0; 7945 $fpaddingL = 0; 7946 /* -- CSS-FLOAT -- */ 7947 if (count($this->floatDivs)) { 7948 list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl); 7949 if ($r_exists) { 7950 $fpaddingR = $r_width; 7951 } 7952 if ($l_exists) { 7953 $fpaddingL = $l_width; 7954 } 7955 } 7956 /* -- END CSS-FLOAT -- */ 7957 7958 $usey = $this->y + 0.002; 7959 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) { 7960 $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w']; 7961 } 7962 7963 /* -- CSS-IMAGE-FLOAT -- */ 7964 // If float exists at this level 7965 if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) { 7966 $fpaddingR += $this->floatmargins['R']['w']; 7967 } 7968 if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) { 7969 $fpaddingL += $this->floatmargins['L']['w']; 7970 } 7971 /* -- END CSS-IMAGE-FLOAT -- */ 7972 7973 7974 // try adding another char 7975 if (( $contentWidth + $cw > $maxWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) + 0.001)) {// 0.001 is to correct for deviations converting mm=>pts 7976 // it won't fit, output what we already have 7977 $lineCount++; 7978 7979 // contains any content that didn't make it into this print 7980 $savedContent = ''; 7981 $savedContentB = ''; 7982 $savedOTLdata = []; // mPDF 5.7.1 7983 $savedFont = []; 7984 $savedObj = []; 7985 $savedPreOTLdata = []; // mPDF 5.7.1 7986 $savedPreContent = []; 7987 $savedPreContentB = []; 7988 $savedPreFont = []; 7989 7990 // mPDF 6 7991 // New line-breaking algorithm 7992 ///////////////////// 7993 // LINE BREAKING 7994 ///////////////////// 7995 $breakfound = false; 7996 $contentctr = count($content) - 1; 7997 if ($this->usingCoreFont) { 7998 $charctr = strlen($currContent); 7999 } else { 8000 $charctr = mb_strlen($currContent, $this->mb_enc); 8001 } 8002 $checkchar = $c; 8003 $prevchar = $this->_getPrevChar($contentctr, $charctr, $content); 8004 8005 /* -- CJK-FONTS -- */ 8006 // 1) CJK Overflowing a) punctuation or b) Oikomi 8007 // Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi 8008 if ($CJKoverflow || $Oikomi) { // If flag already set 8009 $CJKoverflow = false; 8010 $Oikomi = false; 8011 $breakfound = true; 8012 } 8013 if (!$this->usingCoreFont && !$breakfound && $this->checkCJK) { 8014 8015 // Get next/following character (in this chunk) 8016 $followingchar = ''; 8017 if ($i < ($clen - 1)) { 8018 if ($this->usingCoreFont) { 8019 $followingchar = $s[$i + 1]; 8020 } else { 8021 $followingchar = mb_substr($s, $i + 1, 1, $this->mb_enc); 8022 } 8023 } 8024 8025 // 1a) Overflow punctuation 8026 if (preg_match("/[" . $this->pregCJKchars . "]/u", $prevchar) && preg_match("/[" . $this->CJKoverflow . "]/u", $checkchar) && $this->allowCJKorphans) { 8027 // add character onto this line 8028 $currContent .= $c; 8029 $contentWidth += $cw; 8030 $CJKoverflow = true; // Set flag 8031 continue; 8032 } elseif (preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) && $this->allowCJKorphans && 8033 (preg_match("/[" . $this->CJKleading . "]/u", $followingchar) || preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar)) && 8034 !preg_match("/[" . $this->CJKleading . "]/u", $checkchar) && !preg_match("/[" . $this->CJKfollowing . "]/u", $followingchar) && 8035 !(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $followingchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) { 8036 // 1b) Try squeezing another character(s) onto this line = Oikomi, if character cannot end line 8037 // or next character cannot start line (and not splitting CJK numerals) 8038 // NB otherwise it move lastchar(s) to next line to keep $c company = Oidashi, which is done below in standard way 8039 // add character onto this line 8040 $currContent .= $c; 8041 $contentWidth += $cw; 8042 $Oikomi = true; // Set flag 8043 continue; 8044 } 8045 } 8046 /* -- END CJK-FONTS -- */ 8047 /* -- HYPHENATION -- */ 8048 8049 // AUTOMATIC HYPHENATION 8050 // 2) Automatic hyphen in current word (does not cross tags) 8051 if (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] == 1) { 8052 $currWord = ''; 8053 // Look back and ahead to get current word 8054 for ($ac = $charctr - 1; $ac >= 0; $ac--) { 8055 if ($this->usingCoreFont) { 8056 $addc = substr($currContent, $ac, 1); 8057 } else { 8058 $addc = mb_substr($currContent, $ac, 1, $this->mb_enc); 8059 } 8060 if ($addc == ' ') { 8061 break; 8062 } 8063 $currWord = $addc . $currWord; 8064 } 8065 $start = $ac + 1; 8066 for ($ac = $i; $ac < ($clen - 1); $ac++) { 8067 if ($this->usingCoreFont) { 8068 $addc = substr($s, $ac, 1); 8069 } else { 8070 $addc = mb_substr($s, $ac, 1, $this->mb_enc); 8071 } 8072 if ($addc == ' ') { 8073 break; 8074 } 8075 $currWord .= $addc; 8076 } 8077 $ptr = $this->hyphenator->hyphenateWord($currWord, $charctr - $start); 8078 if ($ptr > -1) { 8079 $breakfound = [$contentctr, $start + $ptr, $contentctr, $start + $ptr, 'hyphen']; 8080 } 8081 } 8082 /* -- END HYPHENATION -- */ 8083 8084 // Search backwards to find first line-break opportunity 8085 while ($breakfound == false && $prevchar !== false) { 8086 $cutcontentctr = $contentctr; 8087 $cutcharctr = $charctr; 8088 $prevchar = $this->_moveToPrevChar($contentctr, $charctr, $content); 8089 ///////////////////// 8090 // 3) Break at SPACE 8091 ///////////////////// 8092 if ($prevchar == ' ') { 8093 $breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard']; 8094 } ///////////////////// 8095 // 4) Break at U+200B in current word (Khmer, Lao & Thai Invisible word boundary, and Tibetan) 8096 ///////////////////// 8097 elseif ($prevchar == "\xe2\x80\x8b") { // U+200B Zero-width Word Break 8098 $breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard']; 8099 } ///////////////////// 8100 // 5) Break at Hard HYPHEN '-' or U+2010 8101 ///////////////////// 8102 elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && ($prevchar == '-' || $prevchar == "\xe2\x80\x90")) { 8103 // Don't break a URL 8104 // Look back to get first part of current word 8105 $checkw = ''; 8106 for ($ac = $charctr - 1; $ac >= 0; $ac--) { 8107 if ($this->usingCoreFont) { 8108 $addc = substr($currContent, $ac, 1); 8109 } else { 8110 $addc = mb_substr($currContent, $ac, 1, $this->mb_enc); 8111 } 8112 if ($addc == ' ') { 8113 break; 8114 } 8115 $checkw = $addc . $checkw; 8116 } 8117 // Don't break if HyphenMinus AND (a URL or before a numeral or before a >) 8118 if ((!preg_match('/(http:|ftp:|https:|www\.)/', $checkw) && $checkchar != '>' && !preg_match('/[0-9]/', $checkchar)) || $prevchar == "\xe2\x80\x90") { 8119 $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut']; 8120 } 8121 } ///////////////////// 8122 // 6) Break at Soft HYPHEN (replace with hard hyphen) 8123 ///////////////////// 8124 elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && !$this->usingCoreFont && $prevchar == "\xc2\xad") { 8125 $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut']; 8126 $content[$contentctr] = mb_substr($content[$contentctr], 0, $charctr, $this->mb_enc) . '-' . mb_substr($content[$contentctr], $charctr + 1, mb_strlen($content[$contentctr]), $this->mb_enc); 8127 if (!empty($cOTLdata[$contentctr])) { 8128 $cOTLdata[$contentctr]['char_data'][$charctr] = ['bidi_class' => 9, 'uni' => 45]; 8129 $cOTLdata[$contentctr]['group'][$charctr] = 'C'; 8130 } 8131 } elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && $prevchar == chr(173)) { 8132 $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut']; 8133 $content[$contentctr] = substr($content[$contentctr], 0, $charctr) . '-' . substr($content[$contentctr], $charctr + 1); 8134 } /* -- CJK-FONTS -- */ 8135 ///////////////////// 8136 // 7) Break at CJK characters (unless forbidden characters to end or start line) 8137 // CJK Avoiding line break in the middle of numerals 8138 ///////////////////// 8139 elseif (!$this->usingCoreFont && $this->checkCJK && preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) && 8140 !preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar) && !preg_match("/[" . $this->CJKleading . "]/u", $prevchar) && 8141 !(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $prevchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) { 8142 $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut']; 8143 } 8144 /* -- END CJK-FONTS -- */ 8145 ///////////////////// 8146 // 8) Break at OBJECT (Break before all objects here - selected objects are moved forward to next line below e.g. dottab) 8147 ///////////////////// 8148 if (isset($this->objectbuffer[$contentctr])) { 8149 $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut']; 8150 } 8151 8152 8153 $checkchar = $prevchar; 8154 } 8155 8156 // If a line-break opportunity found: 8157 if (is_array($breakfound)) { 8158 $contentctr = $breakfound[0]; 8159 $charctr = $breakfound[1]; 8160 $cutcontentctr = $breakfound[2]; 8161 $cutcharctr = $breakfound[3]; 8162 $type = $breakfound[4]; 8163 // Cache chunks which are already processed, but now need to be passed on to the new line 8164 for ($ix = count($content) - 1; $ix > $cutcontentctr; $ix--) { 8165 // save and crop off any subsequent chunks 8166 /* -- OTL -- */ 8167 if (!empty($sOTLdata)) { 8168 $tmpOTL = array_pop($cOTLdata); 8169 $savedPreOTLdata[] = $tmpOTL; 8170 } 8171 /* -- END OTL -- */ 8172 $savedPreContent[] = array_pop($content); 8173 $savedPreContentB[] = array_pop($contentB); 8174 $savedPreFont[] = array_pop($font); 8175 } 8176 8177 // Next cache the part which will start the next line 8178 if ($this->usingCoreFont) { 8179 $savedPreContent[] = substr($content[$cutcontentctr], $cutcharctr); 8180 } else { 8181 $savedPreContent[] = mb_substr($content[$cutcontentctr], $cutcharctr, mb_strlen($content[$cutcontentctr]), $this->mb_enc); 8182 } 8183 $savedPreContentB[] = preg_replace('/L/', '', $contentB[$cutcontentctr]); 8184 $savedPreFont[] = $font[$cutcontentctr]; 8185 /* -- OTL -- */ 8186 if (!empty($sOTLdata)) { 8187 $savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[$cutcontentctr], $cutcharctr, $cutcharctr); 8188 } 8189 /* -- END OTL -- */ 8190 8191 8192 // Finally adjust the Current content which ends this line 8193 if ($cutcharctr == 0 && $type == 'discard') { 8194 array_pop($content); 8195 array_pop($contentB); 8196 array_pop($font); 8197 array_pop($cOTLdata); 8198 } 8199 8200 $currContent = & $content[count($content) - 1]; 8201 if ($this->usingCoreFont) { 8202 $currContent = substr($currContent, 0, $charctr); 8203 } else { 8204 $currContent = mb_substr($currContent, 0, $charctr, $this->mb_enc); 8205 } 8206 8207 if (!empty($sOTLdata)) { 8208 $savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc)); 8209 } 8210 8211 if (strpos($contentB[(count($contentB) - 1)], 'R') !== false) { // ??? 8212 $contentB[count($content) - 1] = preg_replace('/R/', '', $contentB[count($content) - 1]); // ??? 8213 } 8214 8215 if ($type == 'hyphen') { 8216 $currContent .= '-'; 8217 if (!empty($cOTLdata[(count($cOTLdata) - 1)])) { 8218 $cOTLdata[(count($cOTLdata) - 1)]['char_data'][] = ['bidi_class' => 9, 'uni' => 45]; 8219 $cOTLdata[(count($cOTLdata) - 1)]['group'] .= 'C'; 8220 } 8221 } 8222 8223 $savedContent = ''; 8224 $savedContentB = ''; 8225 $savedFont = []; 8226 $savedOTLdata = []; 8227 } 8228 // If no line-break opportunity found - split at current position 8229 // or - Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi, as set above by: 8230 // 1) CJK Overflowing a) punctuation or b) Oikomi 8231 // in which case $breakfound==1 and NOT array 8232 8233 if (!is_array($breakfound)) { 8234 $savedFont = $this->saveFont(); 8235 if (!empty($sOTLdata)) { 8236 $savedOTLdata = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc)); 8237 } 8238 } 8239 8240 if ($content[count($content) - 1] == '' && !isset($this->objectbuffer[count($content) - 1])) { 8241 array_pop($content); 8242 array_pop($contentB); 8243 array_pop($font); 8244 array_pop($cOTLdata); 8245 $currContent = & $content[count($content) - 1]; 8246 } 8247 8248 // Right Trim current content - including CJK space, and for OTLdata 8249 // incl. CJK - strip CJK space at end of line   = \xe3\x80\x80 = CJK space 8250 $currContent = $currContent ? rtrim($currContent) : ''; 8251 if ($this->checkCJK) { 8252 $currContent = preg_replace("/\xe3\x80\x80$/", '', $currContent); 8253 } // *CJK-FONTS* 8254 /* -- OTL -- */ 8255 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 8256 $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true); // NB also does U+3000 8257 } 8258 /* -- END OTL -- */ 8259 8260 8261 // Selected OBJECTS are moved forward to next line, unless they come before a space or U+200B (type='discard') 8262 if (isset($this->objectbuffer[(count($content) - 1)]) && (!isset($type) || $type != 'discard')) { 8263 $objtype = $this->objectbuffer[(count($content) - 1)]['type']; 8264 if ($objtype == 'dottab' || $objtype == 'bookmark' || $objtype == 'indexentry' || $objtype == 'toc' || $objtype == 'annot') { 8265 $savedObj = array_pop($this->objectbuffer); 8266 } 8267 } 8268 8269 8270 // Decimal alignment (cancel if wraps to > 1 line) 8271 if ($is_table && substr($align, 0, 1) == 'D') { 8272 $align = substr($align, 2, 1); 8273 } 8274 8275 $lineBox = []; 8276 8277 $this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table); 8278 8279 // update $contentWidth since it has changed with cropping 8280 $contentWidth = 0; 8281 8282 $inclCursive = false; 8283 foreach ($content as $k => $chunk) { 8284 if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) { 8285 // LIST MARKERS 8286 if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker']) { 8287 if ($this->objectbuffer[$k]['listmarkerposition'] != 'outside') { 8288 $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE; 8289 } 8290 } else { 8291 $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE; 8292 } 8293 } elseif (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) { 8294 $this->restoreFont($font[$k], false); 8295 if ($this->checkCJK && $k == count($content) - 1 && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $this->CJKforceend) { 8296 // force-end overhang 8297 $hanger = mb_substr($chunk, mb_strlen($chunk, $this->mb_enc) - 1, 1, $this->mb_enc); 8298 // Probably ought to do something with char_data and GPOS in cOTLdata... 8299 $content[$k] = $chunk = mb_substr($chunk, 0, mb_strlen($chunk, $this->mb_enc) - 1, $this->mb_enc); 8300 } 8301 8302 // Soft Hyphens chr(173) + Replace NBSP with SPACE + Set inclcursive if includes CURSIVE TEXT 8303 if (!$this->usingCoreFont) { 8304 /* -- OTL -- */ 8305 if ((isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) || !empty($sOTLdata)) { 8306 $this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad"); 8307 $this->otl->replaceSpace($chunk, $cOTLdata[$k]); // NBSP -> space 8308 if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) { 8309 $inclCursive = true; 8310 } 8311 $content[$k] = $chunk; 8312 } /* -- END OTL -- */ else { // *OTL* 8313 $content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk); 8314 $content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk); 8315 } // *OTL* 8316 } elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') { 8317 $content[$k] = $chunk = str_replace(chr(173), '', $chunk); 8318 $content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk); 8319 } 8320 8321 $contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1 8322 if (!empty($this->spanborddet)) { 8323 if (isset($this->spanborddet['L']['w']) && strpos($contentB[$k], 'L') !== false) { 8324 $contentWidth += $this->spanborddet['L']['w'] * Mpdf::SCALE; 8325 } 8326 if (isset($this->spanborddet['R']['w']) && strpos($contentB[$k], 'R') !== false) { 8327 $contentWidth += $this->spanborddet['R']['w'] * Mpdf::SCALE; 8328 } 8329 } 8330 } 8331 } 8332 8333 $lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : ''); 8334 $lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : ''); 8335 if ($blockdir == 'ltr' && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic 8336 $lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE; 8337 } else { 8338 $lastitalic = 0; 8339 } 8340 8341 8342 8343 8344 // NOW FORMAT THE LINE TO OUTPUT 8345 if (!$table_draft) { 8346 // DIRECTIONALITY RTL 8347 $chunkorder = range(0, count($content) - 1); // mPDF 5.7 8348 /* -- OTL -- */ 8349 // mPDF 6 8350 if ($blockdir == 'rtl' || $this->biDirectional) { 8351 $this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir); 8352 // From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to 8353 // $this->objectbuffer and $font ($chunkorder contains the mapping) 8354 } 8355 8356 /* -- END OTL -- */ 8357 // Remove any XAdvance from OTL data at end of line 8358 foreach ($chunkorder as $aord => $k) { 8359 if (count($cOTLdata)) { 8360 $this->restoreFont($font[$k], false); 8361 // ...WriteFlowingBlock... 8362 if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line 8363 $nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character 8364 if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) { 8365 if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) { 8366 $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm']; 8367 } else { 8368 $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm']; 8369 } 8370 $w *= ($this->FontSize / 1000); 8371 $contentWidth -= $w * Mpdf::SCALE; 8372 $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0; 8373 $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0; 8374 } 8375 8376 // If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it 8377 if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) { 8378 $w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm']; 8379 $w *= ($this->FontSize / 1000); 8380 $contentWidth -= $w * Mpdf::SCALE; 8381 $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement']; 8382 $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement']; 8383 } 8384 } 8385 } 8386 } 8387 8388 // JUSTIFICATION J 8389 $jcharspacing = 0; 8390 $jws = 0; 8391 $nb_carac = 0; 8392 $nb_spaces = 0; 8393 $jkashida = 0; 8394 // if it's justified, we need to find the char/word spacing (or if hanger $this->CJKforceend) 8395 if (($align == 'J' && !$CJKoverflow) || (($contentWidth + $lastitalic > $maxWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) + 0.001) && (!$CJKoverflow || ($CJKoverflow && !$this->allowCJKoverflow))) || $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) { // 0.001 is to correct for deviations converting mm=>pts 8396 // JUSTIFY J (Use character spacing) 8397 // WORD SPACING 8398 // mPDF 5.7 8399 foreach ($chunkorder as $aord => $k) { 8400 $chunk = isset($content[$aord]) ? $content[$aord] : ''; 8401 if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) { 8402 $nb_carac += mb_strlen($chunk, $this->mb_enc); 8403 $nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc); 8404 // Use GPOS OTL 8405 if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) { 8406 if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) { 8407 $nb_carac -= substr_count($cOTLdata[$aord]['group'], 'M'); 8408 } 8409 } 8410 } else { 8411 $nb_carac ++; 8412 } // mPDF 6 allow spacing for inline object 8413 } 8414 // GetJSpacing adds kashida spacing to GPOSinfo if appropriate for Font 8415 list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata); 8416 } 8417 8418 // WORD SPACING 8419 $empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ); 8420 8421 $empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1 8422 $empty -= ($jws * $nb_spaces); 8423 $empty -= ($jkashida); 8424 $empty /= Mpdf::SCALE; 8425 8426 $b = ''; // do not use borders 8427 // Get PAGEBREAK TO TEST for height including the top border/padding 8428 $check_h = max($this->divheight, $stackHeight); 8429 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blklvl > 0) && ($lineCount == 1) && (!$is_table)) { 8430 $check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']); 8431 } 8432 8433 if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) { 8434 $this->SetCol($this->NbCol - 1); 8435 } 8436 8437 // PAGEBREAK 8438 // 'If' below used in order to fix "first-line of other page with justify on" bug 8439 if (!$is_table && ($this->y + $check_h) > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) { 8440 $bak_x = $this->x; // Current X position 8441 // WORD SPACING 8442 $ws = $this->ws; // Word Spacing 8443 $charspacing = $this->charspacing; // Character Spacing 8444 $this->ResetSpacing(); 8445 8446 $this->AddPage($this->CurOrientation); 8447 8448 $this->x = $bak_x; 8449 // Added to correct for OddEven Margins 8450 $currentx += $this->MarginCorrection; 8451 $this->x += $this->MarginCorrection; 8452 8453 // WORD SPACING 8454 $this->SetSpacing($charspacing, $ws); 8455 } 8456 8457 if ($this->kwt && !$is_table) { // mPDF 5.7+ 8458 $this->printkwtbuffer(); 8459 $this->kwt = false; 8460 } 8461 8462 8463 /* -- COLUMNS -- */ 8464 // COLS 8465 // COLUMN CHANGE 8466 if ($this->CurrCol != $oldcolumn) { 8467 $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 8468 $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 8469 $oldcolumn = $this->CurrCol; 8470 } 8471 8472 if ($this->ColActive && !$is_table) { 8473 $this->breakpoints[$this->CurrCol][] = $this->y; 8474 } // *COLUMNS* 8475 /* -- END COLUMNS -- */ 8476 8477 // TOP MARGIN 8478 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && ($lineCount == 1) && (!$is_table)) { 8479 $this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']); 8480 if ($this->ColActive) { 8481 $this->breakpoints[$this->CurrCol][] = $this->y; 8482 } // *COLUMNS* 8483 } 8484 8485 8486 // Update y0 for top of block (used to paint border) 8487 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table)) { 8488 $this->blk[$this->blklvl]['y0'] = $this->y; 8489 $this->blk[$this->blklvl]['startpage'] = $this->page; 8490 if ($this->blk[$this->blklvl]['float']) { 8491 $this->blk[$this->blklvl]['float_start_y'] = $this->y; 8492 } 8493 } 8494 8495 // TOP PADDING and BORDER spacing/fill 8496 if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 1) && (!$is_table)) { 8497 // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom 8498 $this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1); 8499 if ($this->ColActive) { 8500 $this->breakpoints[$this->CurrCol][] = $this->y; 8501 } // *COLUMNS* 8502 } 8503 8504 $arraysize = count($chunkorder); 8505 8506 $margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR ); 8507 8508 // PAINT BACKGROUND FOR THIS LINE 8509 if (!$is_table) { 8510 $this->DivLn($stackHeight, $this->blklvl, false); 8511 } // false -> don't advance y 8512 8513 $this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL; 8514 if ($align == 'R') { 8515 $this->x += $empty; 8516 } elseif ($align == 'C') { 8517 $this->x += ($empty / 2); 8518 } 8519 8520 // Paragraph INDENT 8521 if (isset($this->blk[$this->blklvl]['text_indent']) && ($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table) && ($blockdir != 'rtl') && ($align != 'C')) { 8522 $ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false); // mPDF 5.7.4 8523 $this->x += $ti; 8524 } 8525 8526 // BIDI magic_reverse moved upwards from here 8527 foreach ($chunkorder as $aord => $k) { // mPDF 5.7 8528 8529 $chunk = isset($content[$aord]) ? $content[$aord] : ''; 8530 8531 if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) { 8532 $xadj = $this->x - $this->objectbuffer[$k]['OUTER-X']; 8533 $this->objectbuffer[$k]['OUTER-X'] += $xadj; 8534 $this->objectbuffer[$k]['BORDER-X'] += $xadj; 8535 $this->objectbuffer[$k]['INNER-X'] += $xadj; 8536 8537 if ($this->objectbuffer[$k]['type'] == 'listmarker') { 8538 $this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin 8539 } 8540 $yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y']; 8541 if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB 8542 $this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin 8543 } 8544 if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB 8545 $yadj += $lineBox[$k]['top']; 8546 } 8547 $this->objectbuffer[$k]['OUTER-Y'] += $yadj; 8548 $this->objectbuffer[$k]['BORDER-Y'] += $yadj; 8549 $this->objectbuffer[$k]['INNER-Y'] += $yadj; 8550 } 8551 8552 $this->restoreFont($font[$k]); // mPDF 5.7 8553 8554 $this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws); 8555 // Now unset these values so they don't influence GetStringwidth below or in fn. Cell 8556 $this->fixedlSpacing = false; 8557 $this->minwSpacing = 0; 8558 8559 $save_vis = $this->visibility; 8560 if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) { 8561 $this->SetVisibility($this->textparam['visibility']); 8562 } 8563 // *********** SPAN BACKGROUND COLOR ***************** // 8564 if ($this->spanbgcolor) { 8565 $cor = $this->spanbgcolorarray; 8566 $this->SetFColor($cor); 8567 $save_fill = $fill; 8568 $spanfill = 1; 8569 $fill = 1; 8570 } 8571 if (!empty($this->spanborddet)) { 8572 if (strpos($contentB[$k], 'L') !== false) { 8573 $this->x += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0); 8574 } 8575 if (strpos($contentB[$k], 'L') === false) { 8576 $this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0; 8577 } 8578 if (strpos($contentB[$k], 'R') === false) { 8579 $this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0; 8580 } 8581 } 8582 8583 // WORD SPACING 8584 // StringWidth this time includes any kashida spacing 8585 $stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, true); 8586 8587 $nch = mb_strlen($chunk, $this->mb_enc); 8588 // Use GPOS OTL 8589 if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) { 8590 if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) { 8591 $nch -= substr_count($cOTLdata[$aord]['group'], 'M'); 8592 } 8593 } 8594 $stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE ); 8595 8596 $stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE ); 8597 8598 if (isset($this->objectbuffer[$k])) { 8599 // LIST MARKERS // mPDF 6 Lists 8600 if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') { 8601 $stringWidth = 0; 8602 } else { 8603 $stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH']; 8604 } 8605 } 8606 8607 if ($stringWidth == 0) { 8608 $stringWidth = 0.000001; 8609 } 8610 8611 if ($aord == $arraysize - 1) { 8612 $stringWidth -= ( $this->charspacing / Mpdf::SCALE ); 8613 if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) { 8614 // force-end overhang 8615 $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); 8616 $this->Cell($this->GetStringWidth($hanger), $stackHeight, $hanger, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); 8617 } else { 8618 $this->Cell($stringWidth, $stackHeight, $chunk, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mono-style line or last part (skips line) 8619 } 8620 } else { 8621 $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, 0, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // first or middle part 8622 } 8623 8624 if (!empty($this->spanborddet)) { 8625 if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) { 8626 $this->x += $this->spanborddet['R']['w']; 8627 } 8628 } 8629 // *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** // 8630 if (isset($spanfill) && $spanfill) { 8631 $fill = $save_fill; 8632 $spanfill = 0; 8633 if ($fill) { 8634 $this->SetFColor($bcor); 8635 } 8636 } 8637 if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) { 8638 $this->SetVisibility($save_vis); 8639 } 8640 } 8641 } elseif ($table_draft) { 8642 $this->y += $stackHeight; 8643 } 8644 8645 if (!$is_table) { 8646 $this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin'])); 8647 $this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'])); 8648 } 8649 8650 // move on to the next line, reset variables, tack on saved content and current char 8651 8652 if (!$table_draft) { 8653 $this->printobjectbuffer($is_table, $blockdir); 8654 } 8655 $this->objectbuffer = []; 8656 8657 8658 /* -- CSS-IMAGE-FLOAT -- */ 8659 // Update values if set to skipline 8660 if ($this->floatmargins) { 8661 $this->_advanceFloatMargins(); 8662 } 8663 /* -- END CSS-IMAGE-FLOAT -- */ 8664 8665 // Reset lineheight 8666 $stackHeight = $this->divheight; 8667 $valign = 'M'; 8668 8669 $font = []; 8670 $content = []; 8671 $contentB = []; 8672 $cOTLdata = []; // mPDF 5.7.1 8673 $contentWidth = 0; 8674 if (!empty($savedObj)) { 8675 $this->objectbuffer[] = $savedObj; 8676 $font[] = $savedFont; 8677 $content[] = ''; 8678 $contentB[] = ''; 8679 $cOTLdata[] = []; // mPDF 5.7.1 8680 $contentWidth += $savedObj['OUTER-WIDTH'] * Mpdf::SCALE; 8681 } 8682 if (count($savedPreContent) > 0) { 8683 for ($ix = count($savedPreContent) - 1; $ix >= 0; $ix--) { 8684 $font[] = $savedPreFont[$ix]; 8685 $content[] = $savedPreContent[$ix]; 8686 $contentB[] = $savedPreContentB[$ix]; 8687 if (!empty($sOTLdata)) { 8688 $cOTLdata[] = $savedPreOTLdata[$ix]; 8689 } 8690 $this->restoreFont($savedPreFont[$ix]); 8691 $lbw = $rbw = 0; // Border widths 8692 if (!empty($this->spanborddet)) { 8693 $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0); 8694 $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0); 8695 } 8696 if ($ix > 0) { 8697 $contentWidth += $this->GetStringWidth($savedPreContent[$ix], true, (isset($savedPreOTLdata[$ix]) ? $savedPreOTLdata[$ix] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1 8698 if (strpos($savedPreContentB[$ix], 'L') !== false) { 8699 $contentWidth += $lbw; 8700 } 8701 if (strpos($savedPreContentB[$ix], 'R') !== false) { 8702 $contentWidth += $rbw; 8703 } 8704 } 8705 } 8706 $savedPreContent = []; 8707 $savedPreContentB = []; 8708 $savedPreOTLdata = []; // mPDF 5.7.1 8709 $savedPreFont = []; 8710 $content[(count($content) - 1)] .= $c; 8711 } else { 8712 $font[] = $savedFont; 8713 $content[] = $savedContent . $c; 8714 $contentB[] = $savedContentB; 8715 $cOTLdata[] = $savedOTLdata; // mPDF 5.7.1 8716 } 8717 8718 $currContent = & $content[(count($content) - 1)]; 8719 $this->restoreFont($font[(count($font) - 1)]); // mPDF 6.0 8720 8721 /* -- CJK-FONTS -- */ 8722 // CJK - strip CJK space at start of line 8723 //   = \xe3\x80\x80 = CJK space 8724 if ($this->checkCJK && $currContent == "\xe3\x80\x80") { 8725 $currContent = ''; 8726 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 8727 $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], true, false); // left trim U+3000 8728 } 8729 } 8730 /* -- END CJK-FONTS -- */ 8731 8732 $lbw = $rbw = 0; // Border widths 8733 if (!empty($this->spanborddet)) { 8734 $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0); 8735 $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0); 8736 } 8737 8738 $contentWidth += $this->GetStringWidth($currContent, false, (isset($cOTLdata[(count($cOTLdata) - 1)]) ? $cOTLdata[(count($cOTLdata) - 1)] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1 8739 if (strpos($savedContentB, 'L') !== false) { 8740 $contentWidth += $lbw; 8741 } 8742 $CJKoverflow = false; 8743 $hanger = ''; 8744 } // another character will fit, so add it on 8745 else { 8746 $contentWidth += $cw; 8747 $currContent .= $c; 8748 } 8749 } 8750 8751 unset($content); 8752 unset($contentB); 8753 } 8754 8755 // ----------------------END OF FLOWING BLOCK------------------------------------// 8756 8757 8758 /* -- CSS-IMAGE-FLOAT -- */ 8759 // Update values if set to skipline 8760 function _advanceFloatMargins() 8761 { 8762 // Update floatmargins - L 8763 if (isset($this->floatmargins['L']) && $this->floatmargins['L']['skipline'] && $this->floatmargins['L']['y0'] != $this->y) { 8764 $yadj = $this->y - $this->floatmargins['L']['y0']; 8765 $this->floatmargins['L']['y0'] = $this->y; 8766 $this->floatmargins['L']['y1'] += $yadj; 8767 8768 // Update objattr in floatbuffer 8769 if ($this->floatbuffer[$this->floatmargins['L']['id']]['border_left']['w']) { 8770 $this->floatbuffer[$this->floatmargins['L']['id']]['BORDER-Y'] += $yadj; 8771 } 8772 $this->floatbuffer[$this->floatmargins['L']['id']]['INNER-Y'] += $yadj; 8773 $this->floatbuffer[$this->floatmargins['L']['id']]['OUTER-Y'] += $yadj; 8774 8775 // Unset values 8776 $this->floatbuffer[$this->floatmargins['L']['id']]['skipline'] = false; 8777 $this->floatmargins['L']['skipline'] = false; 8778 $this->floatmargins['L']['id'] = ''; 8779 } 8780 // Update floatmargins - R 8781 if (isset($this->floatmargins['R']) && $this->floatmargins['R']['skipline'] && $this->floatmargins['R']['y0'] != $this->y) { 8782 $yadj = $this->y - $this->floatmargins['R']['y0']; 8783 $this->floatmargins['R']['y0'] = $this->y; 8784 $this->floatmargins['R']['y1'] += $yadj; 8785 8786 // Update objattr in floatbuffer 8787 if ($this->floatbuffer[$this->floatmargins['R']['id']]['border_left']['w']) { 8788 $this->floatbuffer[$this->floatmargins['R']['id']]['BORDER-Y'] += $yadj; 8789 } 8790 $this->floatbuffer[$this->floatmargins['R']['id']]['INNER-Y'] += $yadj; 8791 $this->floatbuffer[$this->floatmargins['R']['id']]['OUTER-Y'] += $yadj; 8792 8793 // Unset values 8794 $this->floatbuffer[$this->floatmargins['R']['id']]['skipline'] = false; 8795 $this->floatmargins['R']['skipline'] = false; 8796 $this->floatmargins['R']['id'] = ''; 8797 } 8798 } 8799 8800 /* -- END CSS-IMAGE-FLOAT -- */ 8801 8802 8803 8804 /* -- END HTML-CSS -- */ 8805 8806 function _SetTextRendering($mode) 8807 { 8808 if (!(($mode == 0) || ($mode == 1) || ($mode == 2))) { 8809 throw new \Mpdf\MpdfException("Text rendering mode should be 0, 1 or 2 (value : $mode)"); 8810 } 8811 $tr = ($mode . ' Tr'); 8812 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) { 8813 $this->writer->write($tr); 8814 } 8815 $this->pageoutput[$this->page]['TextRendering'] = $tr; 8816 } 8817 8818 function SetTextOutline($params = []) 8819 { 8820 if (isset($params['outline-s']) && $params['outline-s']) { 8821 $this->SetLineWidth($params['outline-WIDTH']); 8822 $this->SetDColor($params['outline-COLOR']); 8823 $tr = ('2 Tr'); 8824 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) { 8825 $this->writer->write($tr); 8826 } 8827 $this->pageoutput[$this->page]['TextRendering'] = $tr; 8828 } else { // Now resets all values 8829 $this->SetLineWidth(0.2); 8830 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 8831 $this->_SetTextRendering(0); 8832 $tr = ('0 Tr'); 8833 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) { 8834 $this->writer->write($tr); 8835 } 8836 $this->pageoutput[$this->page]['TextRendering'] = $tr; 8837 } 8838 } 8839 8840 function Image($file, $x, $y, $w = 0, $h = 0, $type = '', $link = '', $paint = true, $constrain = true, $watermark = false, $shownoimg = true, $allowvector = true) 8841 { 8842 $orig_srcpath = $file; 8843 $this->GetFullPath($file); 8844 8845 $info = $this->imageProcessor->getImage($file, true, $allowvector, $orig_srcpath); 8846 if (!$info && $paint) { 8847 $info = $this->imageProcessor->getImage($this->noImageFile); 8848 if ($info) { 8849 $file = $this->noImageFile; 8850 $w = ($info['w'] * (25.4 / $this->img_dpi)); // 14 x 16px 8851 $h = ($info['h'] * (25.4 / $this->img_dpi)); // 14 x 16px 8852 } 8853 } 8854 if (!$info) { 8855 return false; 8856 } 8857 // Automatic width and height calculation if needed 8858 if ($w == 0 and $h == 0) { 8859 /* -- IMAGES-WMF -- */ 8860 if ($info['type'] == 'wmf') { 8861 // WMF units are twips (1/20pt) 8862 // divide by 20 to get points 8863 // divide by k to get user units 8864 $w = abs($info['w']) / (20 * Mpdf::SCALE); 8865 $h = abs($info['h']) / (20 * Mpdf::SCALE); 8866 } else { /* -- END IMAGES-WMF -- */ 8867 if ($info['type'] == 'svg') { 8868 // returned SVG units are pts 8869 // divide by k to get user units (mm) 8870 $w = abs($info['w']) / Mpdf::SCALE; 8871 $h = abs($info['h']) / Mpdf::SCALE; 8872 } else { 8873 // Put image at default image dpi 8874 $w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi); 8875 $h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi); 8876 } 8877 } 8878 } 8879 if ($w == 0) { 8880 $w = abs($h * $info['w'] / $info['h']); 8881 } 8882 if ($h == 0) { 8883 $h = abs($w * $info['h'] / $info['w']); 8884 } 8885 8886 /* -- WATERMARK -- */ 8887 if ($watermark) { 8888 $maxw = $this->w; 8889 $maxh = $this->h; 8890 // Size = D PF or array 8891 if (is_array($this->watermark_size)) { 8892 $w = $this->watermark_size[0]; 8893 $h = $this->watermark_size[1]; 8894 } elseif (!is_string($this->watermark_size)) { 8895 $maxw -= $this->watermark_size * 2; 8896 $maxh -= $this->watermark_size * 2; 8897 $w = $maxw; 8898 $h = abs($w * $info['h'] / $info['w']); 8899 if ($h > $maxh) { 8900 $h = $maxh; 8901 $w = abs($h * $info['w'] / $info['h']); 8902 } 8903 } elseif ($this->watermark_size == 'F') { 8904 if ($this->ColActive) { 8905 $maxw = $this->w - ($this->DeflMargin + $this->DefrMargin); 8906 } else { 8907 $maxw = $this->pgwidth; 8908 } 8909 $maxh = $this->h - ($this->tMargin + $this->bMargin); 8910 $w = $maxw; 8911 $h = abs($w * $info['h'] / $info['w']); 8912 if ($h > $maxh) { 8913 $h = $maxh; 8914 $w = abs($h * $info['w'] / $info['h']); 8915 } 8916 } elseif ($this->watermark_size == 'P') { // Default P 8917 $w = $maxw; 8918 $h = abs($w * $info['h'] / $info['w']); 8919 if ($h > $maxh) { 8920 $h = $maxh; 8921 $w = abs($h * $info['w'] / $info['h']); 8922 } 8923 } 8924 // Automatically resize to maximum dimensions of page if too large 8925 if ($w > $maxw) { 8926 $w = $maxw; 8927 $h = abs($w * $info['h'] / $info['w']); 8928 } 8929 if ($h > $maxh) { 8930 $h = $maxh; 8931 $w = abs($h * $info['w'] / $info['h']); 8932 } 8933 // Position 8934 if (is_array($this->watermark_pos)) { 8935 $x = $this->watermark_pos[0]; 8936 $y = $this->watermark_pos[1]; 8937 } elseif ($this->watermark_pos == 'F') { // centred on printable area 8938 if ($this->ColActive) { // *COLUMNS* 8939 if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { 8940 $xadj = $this->DeflMargin - $this->DefrMargin; 8941 } // *COLUMNS* 8942 else { 8943 $xadj = 0; 8944 } // *COLUMNS* 8945 $x = ($this->DeflMargin - $xadj + ($this->w - ($this->DeflMargin + $this->DefrMargin)) / 2) - ($w / 2); // *COLUMNS* 8946 } // *COLUMNS* 8947 else { // *COLUMNS* 8948 $x = ($this->lMargin + ($this->pgwidth) / 2) - ($w / 2); 8949 } // *COLUMNS* 8950 $y = ($this->tMargin + ($this->h - ($this->tMargin + $this->bMargin)) / 2) - ($h / 2); 8951 } else { // default P - centred on whole page 8952 $x = ($this->w / 2) - ($w / 2); 8953 $y = ($this->h / 2) - ($h / 2); 8954 } 8955 /* -- IMAGES-WMF -- */ 8956 if ($info['type'] == 'wmf') { 8957 $sx = $w * Mpdf::SCALE / $info['w']; 8958 $sy = -$h * Mpdf::SCALE / $info['h']; 8959 $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']); 8960 } else { /* -- END IMAGES-WMF -- */ 8961 if ($info['type'] == 'svg') { 8962 $sx = $w * Mpdf::SCALE / $info['w']; 8963 $sy = -$h * Mpdf::SCALE / $info['h']; 8964 $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']); 8965 } else { 8966 $outstring = sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $w * Mpdf::SCALE, $h * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $h)) * Mpdf::SCALE, $info['i']); 8967 } 8968 } 8969 8970 if ($this->watermarkImgBehind) { 8971 $outstring = $this->watermarkImgAlpha . "\n" . $outstring . "\n" . $this->SetAlpha(1, 'Normal', true) . "\n"; 8972 $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $outstring . "\n" . '\\1', $this->pages[$this->page]); 8973 } else { 8974 $this->writer->write($outstring); 8975 } 8976 8977 return 0; 8978 } // end of IF watermark 8979 /* -- END WATERMARK -- */ 8980 8981 if ($constrain) { 8982 // Automatically resize to maximum dimensions of page if too large 8983 if (isset($this->blk[$this->blklvl]['inner_width']) && $this->blk[$this->blklvl]['inner_width']) { 8984 $maxw = $this->blk[$this->blklvl]['inner_width']; 8985 } else { 8986 $maxw = $this->pgwidth; 8987 } 8988 if ($w > $maxw) { 8989 $w = $maxw; 8990 $h = abs($w * $info['h'] / $info['w']); 8991 } 8992 if ($h > $this->h - ($this->tMargin + $this->bMargin + 1)) { // see below - +10 to avoid drawing too close to border of page 8993 $h = $this->h - ($this->tMargin + $this->bMargin + 1); 8994 if ($this->fullImageHeight) { 8995 $h = $this->fullImageHeight; 8996 } 8997 $w = abs($h * $info['w'] / $info['h']); 8998 } 8999 9000 9001 // Avoid drawing out of the paper(exceeding width limits). 9002 // if ( ($x + $w) > $this->fw ) { 9003 if (($x + $w) > $this->w) { 9004 $x = $this->lMargin; 9005 $y += 5; 9006 } 9007 9008 $changedpage = false; 9009 $oldcolumn = $this->CurrCol; 9010 // Avoid drawing out of the page. 9011 if ($y + $h > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) { 9012 $this->AddPage($this->CurOrientation); 9013 // Added to correct for OddEven Margins 9014 $x = $x + $this->MarginCorrection; 9015 $y = $this->tMargin; // mPDF 5.7.3 9016 $changedpage = true; 9017 } 9018 /* -- COLUMNS -- */ 9019 // COLS 9020 // COLUMN CHANGE 9021 if ($this->CurrCol != $oldcolumn) { 9022 $y = $this->y0; 9023 $x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 9024 $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 9025 } 9026 /* -- END COLUMNS -- */ 9027 } // end of IF constrain 9028 9029 /* -- IMAGES-WMF -- */ 9030 if ($info['type'] == 'wmf') { 9031 $sx = $w * Mpdf::SCALE / $info['w']; 9032 $sy = -$h * Mpdf::SCALE / $info['h']; 9033 $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']); 9034 } else { /* -- END IMAGES-WMF -- */ 9035 if ($info['type'] == 'svg') { 9036 $sx = $w * Mpdf::SCALE / $info['w']; 9037 $sy = -$h * Mpdf::SCALE / $info['h']; 9038 $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']); 9039 } else { 9040 $outstring = sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $w * Mpdf::SCALE, $h * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $h)) * Mpdf::SCALE, $info['i']); 9041 } 9042 } 9043 9044 if ($paint) { 9045 $this->writer->write($outstring); 9046 if ($link) { 9047 $this->Link($x, $y, $w, $h, $link); 9048 } 9049 9050 // Avoid writing text on top of the image. // THIS WAS OUTSIDE THE if ($paint) bit!!!!!!!!!!!!!!!! 9051 $this->y = $y + $h; 9052 } 9053 9054 // Return width-height array 9055 $sizesarray['WIDTH'] = $w; 9056 $sizesarray['HEIGHT'] = $h; 9057 $sizesarray['X'] = $x; // Position before painting image 9058 $sizesarray['Y'] = $y; // Position before painting image 9059 $sizesarray['OUTPUT'] = $outstring; 9060 9061 $sizesarray['IMAGE_ID'] = $info['i']; 9062 $sizesarray['itype'] = $info['type']; 9063 $sizesarray['set-dpi'] = (isset($info['set-dpi']) ? $info['set-dpi'] : 0); 9064 return $sizesarray; 9065 } 9066 9067 // ============================================================= 9068 // ============================================================= 9069 // ============================================================= 9070 // ============================================================= 9071 // ============================================================= 9072 /* -- HTML-CSS -- */ 9073 9074 function _getObjAttr($t) 9075 { 9076 $c = explode("\xbb\xa4\xac", $t, 2); 9077 $c = explode(",", $c[1], 2); 9078 foreach ($c as $v) { 9079 $v = explode("=", $v, 2); 9080 $sp[$v[0]] = $v[1]; 9081 } 9082 return (unserialize($sp['objattr'])); 9083 } 9084 9085 function inlineObject($type, $x, $y, $objattr, $Lmargin, $widthUsed, $maxWidth, $lineHeight, $paint = false, $is_table = false) 9086 { 9087 if ($is_table) { 9088 $k = $this->shrin_k; 9089 } else { 9090 $k = 1; 9091 } 9092 9093 // NB $x is only used when paint=true 9094 // Lmargin not used 9095 $w = 0; 9096 if (isset($objattr['width'])) { 9097 $w = $objattr['width'] / $k; 9098 } 9099 $h = 0; 9100 if (isset($objattr['height'])) { 9101 $h = abs($objattr['height'] / $k); 9102 } 9103 $widthLeft = $maxWidth - $widthUsed; 9104 $maxHeight = $this->h - ($this->tMargin + $this->bMargin + 10); 9105 if ($this->fullImageHeight) { 9106 $maxHeight = $this->fullImageHeight; 9107 } 9108 // For Images 9109 if (isset($objattr['border_left'])) { 9110 $extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']) / $k; 9111 $extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']) / $k; 9112 9113 if ($type == 'image' || $type == 'barcode' || $type == 'textcircle') { 9114 $extraWidth += ($objattr['padding_left'] + $objattr['padding_right']) / $k; 9115 $extraHeight += ($objattr['padding_top'] + $objattr['padding_bottom']) / $k; 9116 } 9117 } 9118 9119 if (!isset($objattr['vertical-align'])) { 9120 if ($objattr['type'] == 'select') { 9121 $objattr['vertical-align'] = 'M'; 9122 } else { 9123 $objattr['vertical-align'] = 'BS'; 9124 } 9125 } // mPDF 6 9126 9127 if ($type == 'image' || (isset($objattr['subtype']) && $objattr['subtype'] == 'IMAGE')) { 9128 if (isset($objattr['itype']) && ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg')) { 9129 $file = $objattr['file']; 9130 $info = $this->formobjects[$file]; 9131 } elseif (isset($objattr['file'])) { 9132 $file = $objattr['file']; 9133 $info = $this->images[$file]; 9134 } 9135 } 9136 if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') { 9137 $w = 0.00001; 9138 $h = 0.00001; 9139 } 9140 9141 // TEST whether need to skipline 9142 if (!$paint) { 9143 if ($type == 'hr') { // always force new line 9144 if (($y + $h + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) { 9145 return [-2, $w, $h]; 9146 } // New page + new line 9147 else { 9148 return [1, $w, $h]; 9149 } // new line 9150 } else { 9151 // LIST MARKERS // mPDF 6 Lists 9152 $displayheight = $h; 9153 $displaywidth = $w; 9154 if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker']) { 9155 $displayheight = 0; 9156 if ($objattr['listmarkerposition'] == 'outside') { 9157 $displaywidth = 0; 9158 } 9159 } 9160 9161 if ($widthUsed > 0 && $displaywidth > $widthLeft && (!$is_table || $type != 'image')) { // New line needed 9162 // mPDF 6 Lists 9163 if (($y + $displayheight + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter) { 9164 return [-2, $w, $h]; 9165 } // New page + new line 9166 return [1, $w, $h]; // new line 9167 } elseif ($widthUsed > 0 && $displaywidth > $widthLeft && $is_table) { // New line needed in TABLE 9168 return [1, $w, $h]; // new line 9169 } // Will fit on line but NEW PAGE REQUIRED 9170 elseif (($y + $displayheight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) { 9171 return [-1, $w, $h]; 9172 } // mPDF 6 Lists 9173 else { 9174 return [0, $w, $h]; 9175 } 9176 } 9177 } 9178 9179 if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') { 9180 $w = 0.00001; 9181 $h = 0.00001; 9182 $objattr['BORDER-WIDTH'] = 0; 9183 $objattr['BORDER-HEIGHT'] = 0; 9184 $objattr['BORDER-X'] = $x; 9185 $objattr['BORDER-Y'] = $y; 9186 $objattr['INNER-WIDTH'] = 0; 9187 $objattr['INNER-HEIGHT'] = 0; 9188 $objattr['INNER-X'] = $x; 9189 $objattr['INNER-Y'] = $y; 9190 } 9191 9192 if ($type == 'image') { 9193 // Automatically resize to width remaining 9194 if ($w > ($widthLeft + 0.0001) && !$is_table) { // mPDF 5.7.4 0.0001 to allow for rounding errors when w==maxWidth 9195 $w = $widthLeft; 9196 $h = abs($w * $info['h'] / $info['w']); 9197 } 9198 $img_w = $w - $extraWidth; 9199 $img_h = $h - $extraHeight; 9200 9201 $objattr['BORDER-WIDTH'] = $img_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2); 9202 $objattr['BORDER-HEIGHT'] = $img_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2); 9203 $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2); 9204 $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2); 9205 $objattr['INNER-WIDTH'] = $img_w; 9206 $objattr['INNER-HEIGHT'] = $img_h; 9207 $objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k); 9208 $objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k); 9209 $objattr['ID'] = $info['i']; 9210 } 9211 9212 if ($type == 'input' && $objattr['subtype'] == 'IMAGE') { 9213 $img_w = $w - $extraWidth; 9214 $img_h = $h - $extraHeight; 9215 $objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2); 9216 $objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2); 9217 $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2); 9218 $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2); 9219 $objattr['INNER-WIDTH'] = $img_w; 9220 $objattr['INNER-HEIGHT'] = $img_h; 9221 $objattr['INNER-X'] = $x + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k); 9222 $objattr['INNER-Y'] = $y + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k); 9223 $objattr['ID'] = $info['i']; 9224 } 9225 9226 if ($type == 'barcode' || $type == 'textcircle') { 9227 $b_w = $w - $extraWidth; 9228 $b_h = $h - $extraHeight; 9229 $objattr['BORDER-WIDTH'] = $b_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2); 9230 $objattr['BORDER-HEIGHT'] = $b_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2); 9231 $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2); 9232 $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2); 9233 $objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k); 9234 $objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k); 9235 $objattr['INNER-WIDTH'] = $b_w; 9236 $objattr['INNER-HEIGHT'] = $b_h; 9237 } 9238 9239 9240 if ($type == 'textarea') { 9241 // Automatically resize to width remaining 9242 if ($w > $widthLeft && !$is_table) { 9243 $w = $widthLeft; 9244 } 9245 // This used to resize height to maximum remaining on page ? why. Causes problems when in table and causing a new column 9246 // if (($y + $h > $this->PageBreakTrigger) && !$this->InFooter) { 9247 // $h=$this->h - $y - $this->bMargin; 9248 // } 9249 } 9250 9251 if ($type == 'hr') { 9252 if ($is_table) { 9253 $objattr['INNER-WIDTH'] = $maxWidth * $objattr['W-PERCENT'] / 100; 9254 $objattr['width'] = $objattr['INNER-WIDTH']; 9255 $w = $maxWidth; 9256 } else { 9257 if ($w > $maxWidth) { 9258 $w = $maxWidth; 9259 } 9260 $objattr['INNER-WIDTH'] = $w; 9261 $w = $maxWidth; 9262 } 9263 } 9264 9265 9266 9267 if (($type == 'select') || ($type == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD'))) { 9268 // Automatically resize to width remaining 9269 if ($w > $widthLeft && !$is_table) { 9270 $w = $widthLeft; 9271 } 9272 } 9273 9274 if ($type == 'textarea' || $type == 'select' || $type == 'input') { 9275 if (isset($objattr['fontsize'])) { 9276 $objattr['fontsize'] /= $k; 9277 } 9278 if (isset($objattr['linewidth'])) { 9279 $objattr['linewidth'] /= $k; 9280 } 9281 } 9282 9283 if (!isset($objattr['BORDER-Y'])) { 9284 $objattr['BORDER-Y'] = 0; 9285 } 9286 if (!isset($objattr['BORDER-X'])) { 9287 $objattr['BORDER-X'] = 0; 9288 } 9289 if (!isset($objattr['INNER-Y'])) { 9290 $objattr['INNER-Y'] = 0; 9291 } 9292 if (!isset($objattr['INNER-X'])) { 9293 $objattr['INNER-X'] = 0; 9294 } 9295 9296 // Return width-height array 9297 $objattr['OUTER-WIDTH'] = $w; 9298 $objattr['OUTER-HEIGHT'] = $h; 9299 $objattr['OUTER-X'] = $x; 9300 $objattr['OUTER-Y'] = $y; 9301 return $objattr; 9302 } 9303 9304 /* -- END HTML-CSS -- */ 9305 9306 // ============================================================= 9307 // ============================================================= 9308 // ============================================================= 9309 // ============================================================= 9310 // ============================================================= 9311 9312 function SetLineJoin($mode = 0) 9313 { 9314 $s = sprintf('%d j', $mode); 9315 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineJoin']) && $this->pageoutput[$this->page]['LineJoin'] != $s) || !isset($this->pageoutput[$this->page]['LineJoin']))) { 9316 $this->writer->write($s); 9317 } 9318 $this->pageoutput[$this->page]['LineJoin'] = $s; 9319 } 9320 9321 function SetLineCap($mode = 2) 9322 { 9323 $s = sprintf('%d J', $mode); 9324 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineCap']) && $this->pageoutput[$this->page]['LineCap'] != $s) || !isset($this->pageoutput[$this->page]['LineCap']))) { 9325 $this->writer->write($s); 9326 } 9327 $this->pageoutput[$this->page]['LineCap'] = $s; 9328 } 9329 9330 function SetDash($black = false, $white = false) 9331 { 9332 if ($black and $white) { 9333 $s = sprintf('[%.3F %.3F] 0 d', $black * Mpdf::SCALE, $white * Mpdf::SCALE); 9334 } else { 9335 $s = '[] 0 d'; 9336 } 9337 9338 if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Dash']) && $this->pageoutput[$this->page]['Dash'] != $s) || !isset($this->pageoutput[$this->page]['Dash']))) { 9339 $this->writer->write($s); 9340 } 9341 9342 $this->pageoutput[$this->page]['Dash'] = $s; 9343 } 9344 9345 function SetDisplayPreferences($preferences) 9346 { 9347 // String containing any or none of /HideMenubar/HideToolbar/HideWindowUI/DisplayDocTitle/CenterWindow/FitWindow 9348 9349 $this->DisplayPreferences .= $preferences; 9350 } 9351 9352 function Ln($h = '', $collapsible = 0) 9353 { 9354 // Added collapsible to allow collapsible top-margin on new page 9355 // Line feed; default value is last cell height 9356 9357 $margin = isset($this->blk[$this->blklvl]['outer_left_margin']) ? $this->blk[$this->blklvl]['outer_left_margin'] : 0; 9358 9359 $this->x = $this->lMargin + $margin; 9360 9361 if ($collapsible && ($this->y == $this->tMargin) && (!$this->ColActive)) { 9362 $h = 0; 9363 } 9364 9365 if (is_string($h)) { 9366 $this->y += $this->lasth; 9367 } else { 9368 $this->y += $h; 9369 } 9370 } 9371 9372 /* -- HTML-CSS -- */ 9373 9374 function DivLn($h, $level = -3, $move_y = true, $collapsible = false, $state = 0) 9375 { 9376 // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom 9377 // Used in Columns and keep-with-table i.e. "kwt" 9378 // writes background block by block so it can be repositioned 9379 // and also used in writingFlowingBlock at top and bottom of blocks to move y (not to draw/paint anything) 9380 // adds lines (y) where DIV bgcolors are filled in 9381 // this->x is returned as it was 9382 // allows .00001 as nominal height used for bookmarks/annotations etc. 9383 if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)) && (!$this->ColActive)) { 9384 return; 9385 } 9386 9387 // mPDF 6 Columns 9388 // if ($collapsible && (sprintf("%0.4f", $this->y)==sprintf("%0.4f", $this->y0)) && ($this->ColActive) && $this->CurrCol == 0) { return; } // *COLUMNS* 9389 if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->y0)) && ($this->ColActive)) { 9390 return; 9391 } // *COLUMNS* 9392 // Still use this method if columns or keep-with-table, as it allows repositioning later 9393 // otherwise, now uses PaintDivBB() 9394 if (!$this->ColActive && !$this->kwt) { 9395 if ($move_y && !$this->ColActive) { 9396 $this->y += $h; 9397 } 9398 return; 9399 } 9400 9401 if ($level == -3) { 9402 $level = $this->blklvl; 9403 } 9404 $firstblockfill = $this->GetFirstBlockFill(); 9405 if ($firstblockfill && $this->blklvl > 0 && $this->blklvl >= $firstblockfill) { 9406 $last_x = 0; 9407 $last_w = 0; 9408 $last_fc = $this->FillColor; 9409 $bak_x = $this->x; 9410 $bak_h = $this->divheight; 9411 $this->divheight = 0; // Temporarily turn off divheight - as Cell() uses it to check for PageBreak 9412 for ($blvl = $firstblockfill; $blvl <= $level; $blvl++) { 9413 $this->x = $this->lMargin + $this->blk[$blvl]['outer_left_margin']; 9414 // mPDF 6 9415 if ($this->blk[$blvl]['bgcolor']) { 9416 $this->SetFColor($this->blk[$blvl]['bgcolorarray']); 9417 } 9418 if ($last_x != ($this->lMargin + $this->blk[$blvl]['outer_left_margin']) || ($last_w != $this->blk[$blvl]['width']) || $last_fc != $this->FillColor || (isset($this->blk[$blvl]['border_top']['s']) && $this->blk[$blvl]['border_top']['s']) || (isset($this->blk[$blvl]['border_bottom']['s']) && $this->blk[$blvl]['border_bottom']['s']) || (isset($this->blk[$blvl]['border_left']['s']) && $this->blk[$blvl]['border_left']['s']) || (isset($this->blk[$blvl]['border_right']['s']) && $this->blk[$blvl]['border_right']['s'])) { 9419 $x = $this->x; 9420 $this->Cell(($this->blk[$blvl]['width']), $h, '', '', 0, '', 1); 9421 $this->x = $x; 9422 if (!$this->keep_block_together && !$this->writingHTMLheader && !$this->writingHTMLfooter) { 9423 // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom 9424 if ($blvl == $this->blklvl) { 9425 $this->PaintDivLnBorder($state, $blvl, $h); 9426 } else { 9427 $this->PaintDivLnBorder(0, $blvl, $h); 9428 } 9429 } 9430 } 9431 $last_x = $this->lMargin + $this->blk[$blvl]['outer_left_margin']; 9432 $last_w = $this->blk[$blvl]['width']; 9433 $last_fc = $this->FillColor; 9434 } 9435 // Reset current block fill 9436 if (isset($this->blk[$this->blklvl]['bgcolorarray'])) { 9437 $bcor = $this->blk[$this->blklvl]['bgcolorarray']; 9438 $this->SetFColor($bcor); 9439 } 9440 $this->x = $bak_x; 9441 $this->divheight = $bak_h; 9442 } 9443 if ($move_y) { 9444 $this->y += $h; 9445 } 9446 } 9447 9448 /* -- END HTML-CSS -- */ 9449 9450 function SetX($x) 9451 { 9452 // Set x position 9453 if ($x >= 0) { 9454 $this->x = $x; 9455 } else { 9456 $this->x = $this->w + $x; 9457 } 9458 } 9459 9460 function SetY($y) 9461 { 9462 // Set y position and reset x 9463 $this->x = $this->lMargin; 9464 if ($y >= 0) { 9465 $this->y = $y; 9466 } else { 9467 $this->y = $this->h + $y; 9468 } 9469 } 9470 9471 function SetXY($x, $y) 9472 { 9473 // Set x and y positions 9474 $this->SetY($y); 9475 $this->SetX($x); 9476 } 9477 9478 function Output($name = '', $dest = '') 9479 { 9480 $this->logger->debug(sprintf('PDF generated in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]); 9481 9482 // Finish document if necessary 9483 if ($this->state < 3) { 9484 $this->Close(); 9485 } 9486 9487 if ($this->debug && error_get_last()) { 9488 $e = error_get_last(); 9489 if (($e['type'] < 2048 && $e['type'] != 8) || (intval($e['type']) & intval(ini_get("error_reporting")))) { 9490 throw new \Mpdf\MpdfException( 9491 sprintf('Error detected. PDF file generation aborted: %s', $e['message']), 9492 $e['type'], 9493 1, 9494 $e['file'], 9495 $e['line'] 9496 ); 9497 } 9498 } 9499 9500 if (($this->PDFA || $this->PDFX) && $this->encrypted) { 9501 throw new \Mpdf\MpdfException('PDF/A1-b or PDF/X1-a does not permit encryption of documents.'); 9502 } 9503 9504 if (count($this->PDFAXwarnings) && (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto))) { 9505 if ($this->PDFA) { 9506 $standard = 'PDFA/1-b'; 9507 $option = '$mpdf->PDFAauto'; 9508 } else { 9509 $standard = 'PDFX/1-a '; 9510 $option = '$mpdf->PDFXauto'; 9511 } 9512 9513 $this->logger->warning(sprintf('PDF could not be generated as it stands as a %s compliant file.', $standard), ['context' => LogContext::PDFA_PDFX]); 9514 $this->logger->warning(sprintf('These issues can be automatically fixed by mPDF using %s = true;', $option), ['context' => LogContext::PDFA_PDFX]); 9515 $this->logger->warning(sprintf('Action that mPDF will take to automatically force %s compliance are shown further in the log.', $standard), ['context' => LogContext::PDFA_PDFX]); 9516 9517 $this->PDFAXwarnings = array_unique($this->PDFAXwarnings); 9518 foreach ($this->PDFAXwarnings as $w) { 9519 $this->logger->warning($w, ['context' => LogContext::PDFA_PDFX]); 9520 } 9521 9522 throw new \Mpdf\MpdfException('PDFA/PDFX warnings generated. See log for further details'); 9523 } 9524 9525 $this->logger->debug(sprintf('Compiled in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]); 9526 $this->logger->debug(sprintf('Peak Memory usage %s MB', number_format(memory_get_peak_usage(true) / (1024 * 1024), 2)), ['context' => LogContext::STATISTICS]); 9527 $this->logger->debug(sprintf('PDF file size %s kB', number_format(strlen($this->buffer) / 1024)), ['context' => LogContext::STATISTICS]); 9528 $this->logger->debug(sprintf('%d fonts used', count($this->fonts)), ['context' => LogContext::STATISTICS]); 9529 9530 if (is_bool($dest)) { 9531 $dest = $dest ? Destination::DOWNLOAD : Destination::FILE; 9532 } 9533 9534 $dest = strtoupper($dest); 9535 if (empty($dest)) { 9536 if (empty($name)) { 9537 $name = 'mpdf.pdf'; 9538 $dest = Destination::INLINE; 9539 } else { 9540 $dest = Destination::FILE; 9541 } 9542 } 9543 9544 switch ($dest) { 9545 9546 case Destination::INLINE: 9547 9548 if (headers_sent($filename, $line)) { 9549 throw new \Mpdf\MpdfException( 9550 sprintf('Data has already been sent to output (%s at line %s), unable to output PDF file', $filename, $line) 9551 ); 9552 } 9553 9554 if ($this->debug && !$this->allow_output_buffering && ob_get_contents()) { 9555 throw new \Mpdf\MpdfException('Output has already been sent from the script - PDF file generation aborted.'); 9556 } 9557 9558 // We send to a browser 9559 if (PHP_SAPI !== 'cli') { 9560 header('Content-Type: application/pdf'); 9561 9562 if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) { 9563 // don't use length if server using compression 9564 header('Content-Length: ' . strlen($this->buffer)); 9565 } 9566 9567 header('Content-disposition: inline; filename="' . $name . '"'); 9568 header('Cache-Control: public, must-revalidate, max-age=0'); 9569 header('Pragma: public'); 9570 header('X-Generator: mPDF' . ($this->exposeVersion ? (' ' . static::VERSION) : '')); 9571 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); 9572 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); 9573 } 9574 9575 echo $this->buffer; 9576 9577 break; 9578 9579 case Destination::DOWNLOAD: 9580 9581 if (headers_sent()) { 9582 throw new \Mpdf\MpdfException('Data has already been sent to output, unable to output PDF file'); 9583 } 9584 9585 header('Content-Description: File Transfer'); 9586 header('Content-Transfer-Encoding: binary'); 9587 header('Cache-Control: public, must-revalidate, max-age=0'); 9588 header('Pragma: public'); 9589 header('X-Generator: mPDF' . ($this->exposeVersion ? (' ' . static::VERSION) : '')); 9590 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); 9591 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); 9592 header('Content-Type: application/pdf'); 9593 9594 if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) { 9595 // don't use length if server using compression 9596 header('Content-Length: ' . strlen($this->buffer)); 9597 } 9598 9599 header('Content-Disposition: attachment; filename="' . $name . '"'); 9600 9601 echo $this->buffer; 9602 9603 break; 9604 9605 case Destination::FILE: 9606 $f = fopen($name, 'wb'); 9607 9608 if (!$f) { 9609 throw new \Mpdf\MpdfException(sprintf('Unable to create output file %s', $name)); 9610 } 9611 9612 fwrite($f, $this->buffer, strlen($this->buffer)); 9613 fclose($f); 9614 9615 break; 9616 9617 case Destination::STRING_RETURN: 9618 $this->cache->clearOld(); 9619 return $this->buffer; 9620 9621 default: 9622 throw new \Mpdf\MpdfException(sprintf('Incorrect output destination %s', $dest)); 9623 } 9624 9625 $this->cache->clearOld(); 9626 } 9627 9628 // ***************************************************************************** 9629 // * 9630 // Protected methods * 9631 // * 9632 // ***************************************************************************** 9633 function _dochecks() 9634 { 9635 // Check for locale-related bug 9636 if (1.1 == 1) { 9637 throw new \Mpdf\MpdfException('Do not alter the locale before including mPDF'); 9638 } 9639 9640 // Check for decimal separator 9641 if (sprintf('%.1f', 1.0) != '1.0') { 9642 setlocale(LC_NUMERIC, 'C'); 9643 } 9644 9645 if (ini_get('mbstring.func_overload')) { 9646 throw new \Mpdf\MpdfException('Mpdf cannot function properly with mbstring.func_overload enabled'); 9647 } 9648 9649 if (!function_exists('mb_substr')) { 9650 throw new \Mpdf\MpdfException('mbstring extension must be loaded in order to run mPDF'); 9651 } 9652 9653 if (!function_exists('mb_regex_encoding')) { 9654 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 9655 $mamp = ' If using MAMP, there is a bug in its PHP build causing this.'; 9656 } 9657 9658 throw new \Mpdf\MpdfException('mbstring extension with mbregex support must be loaded in order to run mPDF.' . $mamp); 9659 } 9660 } 9661 9662 function _puthtmlheaders() 9663 { 9664 $this->state = 2; 9665 $nb = $this->page; 9666 for ($n = 1; $n <= $nb; $n++) { 9667 if ($this->mirrorMargins && $n % 2 == 0) { 9668 $OE = 'E'; 9669 } // EVEN 9670 else { 9671 $OE = 'O'; 9672 } 9673 $this->page = $n; 9674 $pn = $this->docPageNum($n); 9675 if ($pn) { 9676 $pnstr = $this->pagenumPrefix . $pn . $this->pagenumSuffix; 9677 } else { 9678 $pnstr = ''; 9679 } 9680 9681 $pnt = $this->docPageNumTotal($n); 9682 9683 if ($pnt) { 9684 $pntstr = $this->nbpgPrefix . $pnt . $this->nbpgSuffix; 9685 } else { 9686 $pntstr = ''; 9687 } 9688 9689 if (isset($this->saveHTMLHeader[$n][$OE])) { 9690 $html = isset($this->saveHTMLHeader[$n][$OE]['html']) ? $this->saveHTMLHeader[$n][$OE]['html'] : ''; 9691 $this->lMargin = $this->saveHTMLHeader[$n][$OE]['ml']; 9692 $this->rMargin = $this->saveHTMLHeader[$n][$OE]['mr']; 9693 $this->tMargin = $this->saveHTMLHeader[$n][$OE]['mh']; 9694 $this->bMargin = $this->saveHTMLHeader[$n][$OE]['mf']; 9695 $this->margin_header = $this->saveHTMLHeader[$n][$OE]['mh']; 9696 $this->margin_footer = $this->saveHTMLHeader[$n][$OE]['mf']; 9697 $this->w = $this->saveHTMLHeader[$n][$OE]['pw']; 9698 $this->h = $this->saveHTMLHeader[$n][$OE]['ph']; 9699 $rotate = (isset($this->saveHTMLHeader[$n][$OE]['rotate']) ? $this->saveHTMLHeader[$n][$OE]['rotate'] : null); 9700 $this->Reset(); 9701 $this->pageoutput[$n] = []; 9702 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 9703 $this->x = $this->lMargin; 9704 $this->y = $this->margin_header; 9705 9706 // Replace of page number aliases and date format 9707 $html = $this->aliasReplace($html, $pnstr, $pntstr, $nb); 9708 9709 $this->HTMLheaderPageLinks = []; 9710 $this->HTMLheaderPageAnnots = []; 9711 $this->HTMLheaderPageForms = []; 9712 $this->pageBackgrounds = []; 9713 9714 $this->writingHTMLheader = true; 9715 $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER); 9716 $this->writingHTMLheader = false; 9717 $this->Reset(); 9718 $this->pageoutput[$n] = []; 9719 9720 $s = $this->PrintPageBackgrounds(); 9721 $this->headerbuffer = $s . $this->headerbuffer; 9722 $os = ''; 9723 if ($rotate) { 9724 $os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE)); 9725 // To rotate the other way i.e. Header to left of page: 9726 // $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE)); 9727 } 9728 $os .= $this->headerbuffer; 9729 if ($rotate) { 9730 $os .= ' Q' . "\n"; 9731 } 9732 9733 // Writes over the page background but behind any other output on page 9734 $os = preg_replace(['/\\\\/', '/\$/'], ['\\\\\\\\', '\\\\$'], $os); 9735 9736 $this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]); 9737 9738 $lks = $this->HTMLheaderPageLinks; 9739 foreach ($lks as $lk) { 9740 if ($rotate) { 9741 $lw = $lk[2]; 9742 $lh = $lk[3]; 9743 $lk[2] = $lh; 9744 $lk[3] = $lw; // swap width and height 9745 $ax = $lk[0] / Mpdf::SCALE; 9746 $ay = $lk[1] / Mpdf::SCALE; 9747 $bx = $ay - ($lh / Mpdf::SCALE); 9748 $by = $this->w - $ax; 9749 $lk[0] = $bx * Mpdf::SCALE; 9750 $lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw; 9751 } 9752 $this->PageLinks[$n][] = $lk; 9753 } 9754 /* -- FORMS -- */ 9755 foreach ($this->HTMLheaderPageForms as $f) { 9756 $this->form->forms[$f['n']] = $f; 9757 } 9758 /* -- END FORMS -- */ 9759 } 9760 9761 if (isset($this->saveHTMLFooter[$n][$OE])) { 9762 9763 $html = $this->saveHTMLFooter[$this->page][$OE]['html']; 9764 9765 $this->lMargin = $this->saveHTMLFooter[$n][$OE]['ml']; 9766 $this->rMargin = $this->saveHTMLFooter[$n][$OE]['mr']; 9767 $this->tMargin = $this->saveHTMLFooter[$n][$OE]['mh']; 9768 $this->bMargin = $this->saveHTMLFooter[$n][$OE]['mf']; 9769 $this->margin_header = $this->saveHTMLFooter[$n][$OE]['mh']; 9770 $this->margin_footer = $this->saveHTMLFooter[$n][$OE]['mf']; 9771 $this->w = $this->saveHTMLFooter[$n][$OE]['pw']; 9772 $this->h = $this->saveHTMLFooter[$n][$OE]['ph']; 9773 $rotate = (isset($this->saveHTMLFooter[$n][$OE]['rotate']) ? $this->saveHTMLFooter[$n][$OE]['rotate'] : null); 9774 $this->Reset(); 9775 $this->pageoutput[$n] = []; 9776 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 9777 $this->x = $this->lMargin; 9778 $top_y = $this->y = $this->h - $this->margin_footer; 9779 9780 // if bottom-margin==0, corrects to avoid division by zero 9781 if ($this->y == $this->h) { 9782 $top_y = $this->y = ($this->h + 0.01); 9783 } 9784 9785 // Replace of page number aliases and date format 9786 $html = $this->aliasReplace($html, $pnstr, $pntstr, $nb); 9787 9788 $this->HTMLheaderPageLinks = []; 9789 $this->HTMLheaderPageAnnots = []; 9790 $this->HTMLheaderPageForms = []; 9791 $this->pageBackgrounds = []; 9792 9793 $this->writingHTMLfooter = true; 9794 $this->InFooter = true; 9795 $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER); 9796 $this->InFooter = false; 9797 $this->Reset(); 9798 $this->pageoutput[$n] = []; 9799 9800 $fheight = $this->y - $top_y; 9801 $adj = -$fheight; 9802 9803 $s = $this->PrintPageBackgrounds(-$adj); 9804 $this->headerbuffer = $s . $this->headerbuffer; 9805 $this->writingHTMLfooter = false; // mPDF 5.7.3 (moved after PrintPageBackgrounds so can adjust position of images in footer) 9806 9807 $os = ''; 9808 $os .= $this->StartTransform(true) . "\n"; 9809 9810 if ($rotate) { 9811 $os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE)); 9812 // To rotate the other way i.e. Header to left of page: 9813 // $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE)); 9814 } 9815 9816 $os .= $this->transformTranslate(0, $adj, true) . "\n"; 9817 $os .= $this->headerbuffer; 9818 9819 if ($rotate) { 9820 $os .= ' Q' . "\n"; 9821 } 9822 9823 $os .= $this->StopTransform(true) . "\n"; 9824 9825 // Writes over the page background but behind any other output on page 9826 $os = preg_replace(['/\\\\/', '/\$/'], ['\\\\\\\\', '\\\\$'], $os); 9827 9828 $this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]); 9829 9830 $lks = $this->HTMLheaderPageLinks; 9831 9832 foreach ($lks as $lk) { 9833 9834 $lk[1] -= $adj * Mpdf::SCALE; 9835 9836 if ($rotate) { 9837 $lw = $lk[2]; 9838 $lh = $lk[3]; 9839 $lk[2] = $lh; 9840 $lk[3] = $lw; // swap width and height 9841 9842 $ax = $lk[0] / Mpdf::SCALE; 9843 $ay = $lk[1] / Mpdf::SCALE; 9844 $bx = $ay - ($lh / Mpdf::SCALE); 9845 $by = $this->w - $ax; 9846 $lk[0] = $bx * Mpdf::SCALE; 9847 $lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw; 9848 } 9849 9850 $this->PageLinks[$n][] = $lk; 9851 } 9852 9853 /* -- FORMS -- */ 9854 foreach ($this->HTMLheaderPageForms as $f) { 9855 $f['y'] += $adj; 9856 $this->form->forms[$f['n']] = $f; 9857 } 9858 /* -- END FORMS -- */ 9859 } 9860 9861 // Customization for https://github.com/mpdf/mpdf/issues/172 9862 // Replace of page number aliases and date format 9863 $this->pages[$n] = $this->aliasReplace($this->pages[$n], $pnstr, $pntstr, $nb); 9864 } 9865 9866 $this->page = $nb; 9867 $this->state = 1; 9868 } 9869 9870 /* -- ANNOTATIONS -- */ 9871 function Annotation($text, $x = 0, $y = 0, $icon = 'Note', $author = '', $subject = '', $opacity = 0, $colarray = false, $popup = '', $file = '') 9872 { 9873 if (is_array($colarray) && count($colarray) == 3) { 9874 $colarray = $this->colorConverter->convert('rgb(' . $colarray[0] . ',' . $colarray[1] . ',' . $colarray[2] . ')', $this->PDFAXwarnings); 9875 } 9876 if ($colarray === false) { 9877 $colarray = $this->colorConverter->convert('yellow', $this->PDFAXwarnings); 9878 } 9879 if ($x == 0) { 9880 $x = $this->x; 9881 } 9882 if ($y == 0) { 9883 $y = $this->y; 9884 } 9885 $page = $this->page; 9886 if ($page < 1) { // Document has not been started - assume it's for first page 9887 $page = 1; 9888 if ($x == 0) { 9889 $x = $this->lMargin; 9890 } 9891 if ($y == 0) { 9892 $y = $this->tMargin; 9893 } 9894 } 9895 9896 if ($this->PDFA || $this->PDFX) { 9897 if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) { 9898 $this->PDFAXwarnings[] = "Annotation markers cannot be semi-transparent in PDFA1-b or PDFX/1-a, so they may make underlying text unreadable. (Annotation markers moved to right margin)"; 9899 } 9900 $x = ($this->w) - $this->rMargin * 0.66; 9901 } 9902 if (!$this->annotMargin) { 9903 $y -= $this->FontSize / 2; 9904 } 9905 9906 if (!$opacity && $this->annotMargin) { 9907 $opacity = 1; 9908 } elseif (!$opacity) { 9909 $opacity = $this->annotOpacity; 9910 } 9911 9912 $an = ['txt' => $text, 'x' => $x, 'y' => $y, 'opt' => ['Icon' => $icon, 'T' => $author, 'Subj' => $subject, 'C' => $colarray, 'CA' => $opacity, 'popup' => $popup, 'file' => $file]]; 9913 9914 if ($this->keep_block_together) { // don't write yet 9915 return; 9916 } elseif ($this->table_rotate) { 9917 $this->tbrot_Annots[$this->page][] = $an; 9918 return; 9919 } elseif ($this->kwt) { 9920 $this->kwt_Annots[$this->page][] = $an; 9921 return; 9922 } 9923 9924 if ($this->writingHTMLheader || $this->writingHTMLfooter) { 9925 $this->HTMLheaderPageAnnots[] = $an; 9926 return; 9927 } 9928 9929 // Put an Annotation on the page 9930 $this->PageAnnots[$page][] = $an; 9931 9932 /* -- COLUMNS -- */ 9933 // Save cross-reference to Column buffer 9934 $ref = isset($this->PageAnnots[$this->page]) ? (count($this->PageAnnots[$this->page]) - 1) : -1; 9935 $this->columnAnnots[$this->CurrCol][intval($this->x)][intval($this->y)] = $ref; 9936 /* -- END COLUMNS -- */ 9937 } 9938 9939 /* -- END ANNOTATIONS -- */ 9940 9941 function _enddoc() 9942 { 9943 // @log Writing Headers & Footers 9944 9945 $this->_puthtmlheaders(); 9946 9947 // @log Writing Pages 9948 9949 // Remove references to unused fonts (usually default font) 9950 foreach ($this->fonts as $fk => $font) { 9951 if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) { 9952 if ($font['sip'] || $font['smp']) { 9953 foreach ($font['subsetfontids'] as $k => $fid) { 9954 foreach ($this->pages as $pn => $page) { 9955 $this->pages[$pn] = preg_replace('/\s\/F' . $fid . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]); 9956 } 9957 } 9958 } else { 9959 foreach ($this->pages as $pn => $page) { 9960 $this->pages[$pn] = preg_replace('/\s\/F' . $font['i'] . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]); 9961 } 9962 } 9963 } 9964 } 9965 9966 if (count($this->layers)) { 9967 foreach ($this->pages as $pn => $page) { 9968 preg_match_all('/\/OCZ-index \/ZI(\d+) BDC(.*?)(EMCZ)-index/is', $this->pages[$pn], $m1); 9969 preg_match_all('/\/OCBZ-index \/ZI(\d+) BDC(.*?)(EMCBZ)-index/is', $this->pages[$pn], $m2); 9970 preg_match_all('/\/OCGZ-index \/ZI(\d+) BDC(.*?)(EMCGZ)-index/is', $this->pages[$pn], $m3); 9971 $m = []; 9972 for ($i = 0; $i < 4; $i++) { 9973 $m[$i] = array_merge($m1[$i], $m2[$i], $m3[$i]); 9974 } 9975 if (count($m[0])) { 9976 $sortarr = []; 9977 for ($i = 0; $i < count($m[0]); $i++) { 9978 $key = $m[1][$i] * 2; 9979 if ($m[3][$i] == 'EMCZ') { 9980 $key +=2; // background first then gradient then normal 9981 } elseif ($m[3][$i] == 'EMCGZ') { 9982 $key +=1; 9983 } 9984 $sortarr[$i] = $key; 9985 } 9986 asort($sortarr); 9987 foreach ($sortarr as $i => $k) { 9988 $this->pages[$pn] = str_replace($m[0][$i], '', $this->pages[$pn]); 9989 $this->pages[$pn] .= "\n" . $m[0][$i] . "\n"; 9990 } 9991 $this->pages[$pn] = preg_replace('/\/OC[BG]{0,1}Z-index \/ZI(\d+) BDC/is', '/OC /ZI\\1 BDC ', $this->pages[$pn]); 9992 $this->pages[$pn] = preg_replace('/EMC[BG]{0,1}Z-index/is', 'EMC', $this->pages[$pn]); 9993 } 9994 } 9995 } 9996 9997 $this->pageWriter->writePages(); 9998 9999 // @log Writing document resources 10000 10001 $this->resourceWriter->writeResources(); 10002 10003 // Info 10004 $this->writer->object(); 10005 $this->InfoRoot = $this->n; 10006 $this->writer->write('<<'); 10007 10008 // @log Writing document info 10009 $this->metadataWriter->writeInfo(); 10010 10011 $this->writer->write('>>'); 10012 $this->writer->write('endobj'); 10013 10014 // METADATA 10015 if ($this->PDFA || $this->PDFX) { 10016 $this->metadataWriter->writeMetadata(); 10017 } 10018 10019 // OUTPUTINTENT 10020 if ($this->PDFA || $this->PDFX || $this->ICCProfile) { 10021 $this->metadataWriter->writeOutputIntent(); 10022 } 10023 10024 // Associated files 10025 if ($this->associatedFiles) { 10026 $this->metadataWriter->writeAssociatedFiles(); 10027 } 10028 10029 // Catalog 10030 $this->writer->object(); 10031 $this->writer->write('<<'); 10032 10033 // @log Writing document catalog 10034 10035 $this->metadataWriter->writeCatalog(); 10036 10037 $this->writer->write('>>'); 10038 $this->writer->write('endobj'); 10039 10040 // Cross-ref 10041 $o = strlen($this->buffer); 10042 $this->writer->write('xref'); 10043 $this->writer->write('0 ' . ($this->n + 1)); 10044 $this->writer->write('0000000000 65535 f '); 10045 10046 for ($i = 1; $i <= $this->n; $i++) { 10047 $this->writer->write(sprintf('%010d 00000 n ', $this->offsets[$i])); 10048 } 10049 10050 // Trailer 10051 $this->writer->write('trailer'); 10052 $this->writer->write('<<'); 10053 10054 $this->metadataWriter->writeTrailer(); 10055 10056 $this->writer->write('>>'); 10057 $this->writer->write('startxref'); 10058 $this->writer->write($o); 10059 10060 $this->buffer .= '%%EOF'; 10061 $this->state = 3; 10062 } 10063 10064 function _beginpage( 10065 $orientation, 10066 $mgl = '', 10067 $mgr = '', 10068 $mgt = '', 10069 $mgb = '', 10070 $mgh = '', 10071 $mgf = '', 10072 $ohname = '', 10073 $ehname = '', 10074 $ofname = '', 10075 $efname = '', 10076 $ohvalue = 0, 10077 $ehvalue = 0, 10078 $ofvalue = 0, 10079 $efvalue = 0, 10080 $pagesel = '', 10081 $newformat = '' 10082 ) { 10083 if (!($pagesel && $this->page == 1 && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)))) { 10084 $this->page++; 10085 $this->pages[$this->page] = ''; 10086 } 10087 $this->state = 2; 10088 $resetHTMLHeadersrequired = false; 10089 10090 if ($newformat) { 10091 $this->_setPageSize($newformat, $orientation); 10092 } 10093 10094 /* -- CSS-PAGE -- */ 10095 // Paged media (page-box) 10096 if ($pagesel || (isset($this->page_box['using']) && $this->page_box['using'])) { 10097 10098 if ($pagesel || $this->page == 1) { 10099 $first = true; 10100 } else { 10101 $first = false; 10102 } 10103 10104 if ($this->mirrorMargins && ($this->page % 2 == 0)) { 10105 $oddEven = 'E'; 10106 } else { 10107 $oddEven = 'O'; 10108 } 10109 10110 if ($pagesel) { 10111 $psel = $pagesel; 10112 } elseif ($this->page_box['current']) { 10113 $psel = $this->page_box['current']; 10114 } else { 10115 $psel = ''; 10116 } 10117 10118 list($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS($psel, $first, $oddEven); 10119 10120 if ($this->mirrorMargins && ($this->page % 2 == 0)) { 10121 10122 if ($hname) { 10123 $ehvalue = 1; 10124 $ehname = $hname; 10125 } else { 10126 $ehvalue = -1; 10127 } 10128 10129 if ($fname) { 10130 $efvalue = 1; 10131 $efname = $fname; 10132 } else { 10133 $efvalue = -1; 10134 } 10135 10136 } else { 10137 10138 if ($hname) { 10139 $ohvalue = 1; 10140 $ohname = $hname; 10141 } else { 10142 $ohvalue = -1; 10143 } 10144 10145 if ($fname) { 10146 $ofvalue = 1; 10147 $ofname = $fname; 10148 } else { 10149 $ofvalue = -1; 10150 } 10151 } 10152 10153 if ($resetpagenum || $pagenumstyle || $suppress) { 10154 $this->PageNumSubstitutions[] = ['from' => ($this->page), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress]; 10155 } 10156 10157 // PAGED MEDIA - CROP / CROSS MARKS from @PAGE 10158 $this->show_marks = $marks; 10159 10160 // Background color 10161 if (isset($bg['BACKGROUND-COLOR'])) { 10162 $cor = $this->colorConverter->convert($bg['BACKGROUND-COLOR'], $this->PDFAXwarnings); 10163 if ($cor) { 10164 $this->bodyBackgroundColor = $cor; 10165 } 10166 } else { 10167 $this->bodyBackgroundColor = false; 10168 } 10169 10170 /* -- BACKGROUNDS -- */ 10171 if (isset($bg['BACKGROUND-GRADIENT'])) { 10172 $this->bodyBackgroundGradient = $bg['BACKGROUND-GRADIENT']; 10173 } else { 10174 $this->bodyBackgroundGradient = false; 10175 } 10176 10177 // Tiling Patterns 10178 if (isset($bg['BACKGROUND-IMAGE']) && $bg['BACKGROUND-IMAGE']) { 10179 $ret = $this->SetBackground($bg, $this->pgwidth); 10180 if ($ret) { 10181 $this->bodyBackgroundImage = $ret; 10182 } 10183 } else { 10184 $this->bodyBackgroundImage = false; 10185 } 10186 /* -- END BACKGROUNDS -- */ 10187 10188 $this->page_box['current'] = $psel; 10189 $this->page_box['using'] = true; 10190 } 10191 /* -- END CSS-PAGE -- */ 10192 10193 // Page orientation 10194 if (!$orientation) { 10195 $orientation = $this->DefOrientation; 10196 } else { 10197 $orientation = strtoupper(substr($orientation, 0, 1)); 10198 if ($orientation != $this->DefOrientation) { 10199 $this->OrientationChanges[$this->page] = true; 10200 } 10201 } 10202 10203 if ($orientation != $this->CurOrientation || $newformat) { 10204 10205 // Change orientation 10206 if ($orientation == 'P') { 10207 $this->wPt = $this->fwPt; 10208 $this->hPt = $this->fhPt; 10209 $this->w = $this->fw; 10210 $this->h = $this->fh; 10211 if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') { 10212 $this->tMargin = $this->orig_tMargin; 10213 $this->bMargin = $this->orig_bMargin; 10214 $this->DeflMargin = $this->orig_lMargin; 10215 $this->DefrMargin = $this->orig_rMargin; 10216 $this->margin_header = $this->orig_hMargin; 10217 $this->margin_footer = $this->orig_fMargin; 10218 } else { 10219 $resetHTMLHeadersrequired = true; 10220 } 10221 } else { 10222 $this->wPt = $this->fhPt; 10223 $this->hPt = $this->fwPt; 10224 $this->w = $this->fh; 10225 $this->h = $this->fw; 10226 10227 if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') { 10228 $this->tMargin = $this->orig_lMargin; 10229 $this->bMargin = $this->orig_rMargin; 10230 $this->DeflMargin = $this->orig_bMargin; 10231 $this->DefrMargin = $this->orig_tMargin; 10232 $this->margin_header = $this->orig_hMargin; 10233 $this->margin_footer = $this->orig_fMargin; 10234 } else { 10235 $resetHTMLHeadersrequired = true; 10236 } 10237 } 10238 10239 $this->CurOrientation = $orientation; 10240 $this->ResetMargins(); 10241 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 10242 $this->PageBreakTrigger = $this->h - $this->bMargin; 10243 } 10244 10245 $this->pageDim[$this->page]['w'] = $this->w; 10246 $this->pageDim[$this->page]['h'] = $this->h; 10247 10248 $this->pageDim[$this->page]['outer_width_LR'] = isset($this->page_box['outer_width_LR']) ? $this->page_box['outer_width_LR'] : 0; 10249 $this->pageDim[$this->page]['outer_width_TB'] = isset($this->page_box['outer_width_TB']) ? $this->page_box['outer_width_TB'] : 0; 10250 10251 if (!isset($this->page_box['outer_width_LR']) && !isset($this->page_box['outer_width_TB'])) { 10252 $this->pageDim[$this->page]['bleedMargin'] = 0; 10253 } elseif ($this->bleedMargin <= $this->page_box['outer_width_LR'] && $this->bleedMargin <= $this->page_box['outer_width_TB']) { 10254 $this->pageDim[$this->page]['bleedMargin'] = $this->bleedMargin; 10255 } else { 10256 $this->pageDim[$this->page]['bleedMargin'] = min($this->page_box['outer_width_LR'], $this->page_box['outer_width_TB']) - 0.01; 10257 } 10258 10259 // If Page Margins are re-defined 10260 // strlen()>0 is used to pick up (integer) 0, (string) '0', or set value 10261 if ((strlen($mgl) > 0 && $this->DeflMargin != $mgl) || (strlen($mgr) > 0 && $this->DefrMargin != $mgr) || (strlen($mgt) > 0 && $this->tMargin != $mgt) || (strlen($mgb) > 0 && $this->bMargin != $mgb) || (strlen($mgh) > 0 && $this->margin_header != $mgh) || (strlen($mgf) > 0 && $this->margin_footer != $mgf)) { 10262 10263 if (strlen($mgl) > 0) { 10264 $this->DeflMargin = $mgl; 10265 } 10266 10267 if (strlen($mgr) > 0) { 10268 $this->DefrMargin = $mgr; 10269 } 10270 10271 if (strlen($mgt) > 0) { 10272 $this->tMargin = $mgt; 10273 } 10274 10275 if (strlen($mgb) > 0) { 10276 $this->bMargin = $mgb; 10277 } 10278 10279 if (strlen($mgh) > 0) { 10280 $this->margin_header = $mgh; 10281 } 10282 10283 if (strlen($mgf) > 0) { 10284 $this->margin_footer = $mgf; 10285 } 10286 10287 $this->ResetMargins(); 10288 $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin); 10289 10290 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 10291 $resetHTMLHeadersrequired = true; 10292 } 10293 10294 $this->ResetMargins(); 10295 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 10296 $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin); 10297 10298 // Reset column top margin 10299 $this->y0 = $this->tMargin; 10300 10301 $this->x = $this->lMargin; 10302 $this->y = $this->tMargin; 10303 $this->FontFamily = ''; 10304 10305 // HEADERS AND FOOTERS // mPDF 6 10306 if ($ohvalue < 0 || strtoupper($ohvalue) == 'OFF') { 10307 $this->HTMLHeader = ''; 10308 $resetHTMLHeadersrequired = true; 10309 } elseif ($ohname && $ohvalue > 0) { 10310 if (preg_match('/^html_(.*)$/i', $ohname, $n)) { 10311 $name = $n[1]; 10312 } else { 10313 $name = $ohname; 10314 } 10315 if (isset($this->pageHTMLheaders[$name])) { 10316 $this->HTMLHeader = $this->pageHTMLheaders[$name]; 10317 } else { 10318 $this->HTMLHeader = ''; 10319 } 10320 $resetHTMLHeadersrequired = true; 10321 } 10322 10323 if ($ehvalue < 0 || strtoupper($ehvalue) == 'OFF') { 10324 $this->HTMLHeaderE = ''; 10325 $resetHTMLHeadersrequired = true; 10326 } elseif ($ehname && $ehvalue > 0) { 10327 if (preg_match('/^html_(.*)$/i', $ehname, $n)) { 10328 $name = $n[1]; 10329 } else { 10330 $name = $ehname; 10331 } 10332 if (isset($this->pageHTMLheaders[$name])) { 10333 $this->HTMLHeaderE = $this->pageHTMLheaders[$name]; 10334 } else { 10335 $this->HTMLHeaderE = ''; 10336 } 10337 $resetHTMLHeadersrequired = true; 10338 } 10339 10340 if ($ofvalue < 0 || strtoupper($ofvalue) == 'OFF') { 10341 $this->HTMLFooter = ''; 10342 $resetHTMLHeadersrequired = true; 10343 } elseif ($ofname && $ofvalue > 0) { 10344 if (preg_match('/^html_(.*)$/i', $ofname, $n)) { 10345 $name = $n[1]; 10346 } else { 10347 $name = $ofname; 10348 } 10349 if (isset($this->pageHTMLfooters[$name])) { 10350 $this->HTMLFooter = $this->pageHTMLfooters[$name]; 10351 } else { 10352 $this->HTMLFooter = ''; 10353 } 10354 $resetHTMLHeadersrequired = true; 10355 } 10356 10357 if ($efvalue < 0 || strtoupper($efvalue) == 'OFF') { 10358 $this->HTMLFooterE = ''; 10359 $resetHTMLHeadersrequired = true; 10360 } elseif ($efname && $efvalue > 0) { 10361 if (preg_match('/^html_(.*)$/i', $efname, $n)) { 10362 $name = $n[1]; 10363 } else { 10364 $name = $efname; 10365 } 10366 if (isset($this->pageHTMLfooters[$name])) { 10367 $this->HTMLFooterE = $this->pageHTMLfooters[$name]; 10368 } else { 10369 $this->HTMLFooterE = ''; 10370 } 10371 $resetHTMLHeadersrequired = true; 10372 } 10373 10374 if ($resetHTMLHeadersrequired) { 10375 $this->SetHTMLHeader($this->HTMLHeader); 10376 $this->SetHTMLHeader($this->HTMLHeaderE, 'E'); 10377 $this->SetHTMLFooter($this->HTMLFooter); 10378 $this->SetHTMLFooter($this->HTMLFooterE, 'E'); 10379 } 10380 10381 10382 if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN 10383 $this->_setAutoHeaderHeight($this->HTMLHeaderE); 10384 $this->_setAutoFooterHeight($this->HTMLFooterE); 10385 } else { // ODD or DEFAULT 10386 $this->_setAutoHeaderHeight($this->HTMLHeader); 10387 $this->_setAutoFooterHeight($this->HTMLFooter); 10388 } 10389 10390 // Reset column top margin 10391 $this->y0 = $this->tMargin; 10392 10393 $this->x = $this->lMargin; 10394 $this->y = $this->tMargin; 10395 } 10396 10397 // mPDF 6 10398 function _setAutoHeaderHeight(&$htmlh) 10399 { 10400 /* When the setAutoTopMargin option is set to pad/stretch, only apply auto header height when a header exists */ 10401 if ($this->HTMLHeader === '' && $this->HTMLHeaderE === '') { 10402 return; 10403 } 10404 10405 if ($this->setAutoTopMargin == 'pad') { 10406 if (isset($htmlh['h']) && $htmlh['h']) { 10407 $h = $htmlh['h']; 10408 } // 5.7.3 10409 else { 10410 $h = 0; 10411 } 10412 $this->tMargin = $this->margin_header + $h + $this->orig_tMargin; 10413 } elseif ($this->setAutoTopMargin == 'stretch') { 10414 if (isset($htmlh['h']) && $htmlh['h']) { 10415 $h = $htmlh['h']; 10416 } // 5.7.3 10417 else { 10418 $h = 0; 10419 } 10420 $this->tMargin = max($this->orig_tMargin, $this->margin_header + $h + $this->autoMarginPadding); 10421 } 10422 } 10423 10424 // mPDF 6 10425 function _setAutoFooterHeight(&$htmlf) 10426 { 10427 /* When the setAutoTopMargin option is set to pad/stretch, only apply auto footer height when a footer exists */ 10428 if ($this->HTMLFooter === '' && $this->HTMLFooterE === '') { 10429 return; 10430 } 10431 10432 if ($this->setAutoBottomMargin == 'pad') { 10433 if (isset($htmlf['h']) && $htmlf['h']) { 10434 $h = $htmlf['h']; 10435 } // 5.7.3 10436 else { 10437 $h = 0; 10438 } 10439 $this->bMargin = $this->margin_footer + $h + $this->orig_bMargin; 10440 $this->PageBreakTrigger = $this->h - $this->bMargin; 10441 } elseif ($this->setAutoBottomMargin == 'stretch') { 10442 if (isset($htmlf['h']) && $htmlf['h']) { 10443 $h = $htmlf['h']; 10444 } // 5.7.3 10445 else { 10446 $h = 0; 10447 } 10448 $this->bMargin = max($this->orig_bMargin, $this->margin_footer + $h + $this->autoMarginPadding); 10449 $this->PageBreakTrigger = $this->h - $this->bMargin; 10450 } 10451 } 10452 10453 function _endpage() 10454 { 10455 /* -- CSS-IMAGE-FLOAT -- */ 10456 $this->printfloatbuffer(); 10457 /* -- END CSS-IMAGE-FLOAT -- */ 10458 10459 if ($this->visibility != 'visible') { 10460 $this->SetVisibility('visible'); 10461 } 10462 $this->EndLayer(); 10463 // End of page contents 10464 $this->state = 1; 10465 } 10466 10467 function _dounderline($x, $y, $txt, $OTLdata = false, $textvar = 0) 10468 { 10469 // Now print line exactly where $y secifies - called from Text() and Cell() - adjust position there 10470 // WORD SPACING 10471 $w = ($this->GetStringWidth($txt, false, $OTLdata, $textvar) * Mpdf::SCALE) + ($this->charspacing * mb_strlen($txt, $this->mb_enc)) + ( $this->ws * mb_substr_count($txt, ' ', $this->mb_enc)); 10472 // Draw a line 10473 return sprintf('%.3F %.3F m %.3F %.3F l S', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, ($x * Mpdf::SCALE) + $w, ($this->h - $y) * Mpdf::SCALE); 10474 } 10475 10476 10477 10478 /* -- WATERMARK -- */ 10479 10480 // add a watermark 10481 function watermark($texte, $angle = 45, $fontsize = 96, $alpha = 0.2) 10482 { 10483 if ($this->PDFA || $this->PDFX) { 10484 throw new \Mpdf\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!'); 10485 } 10486 10487 if (!$this->watermark_font) { 10488 $this->watermark_font = $this->default_font; 10489 } 10490 10491 $this->SetFont($this->watermark_font, "B", $fontsize, false); // Don't output 10492 $texte = $this->purify_utf8_text($texte); 10493 10494 if ($this->text_input_as_HTML) { 10495 $texte = $this->all_entities_to_utf8($texte); 10496 } 10497 10498 if ($this->usingCoreFont) { 10499 $texte = mb_convert_encoding($texte, $this->mb_enc, 'UTF-8'); 10500 } 10501 10502 // DIRECTIONALITY 10503 if (preg_match("/([" . $this->pregRTLchars . "])/u", $texte)) { 10504 $this->biDirectional = true; 10505 } // *OTL* 10506 10507 $textvar = 0; 10508 $save_OTLtags = $this->OTLtags; 10509 $this->OTLtags = []; 10510 if ($this->useKerning) { 10511 if ($this->CurrentFont['haskernGPOS']) { 10512 $this->OTLtags['Plus'] .= ' kern'; 10513 } else { 10514 $textvar = ($textvar | TextVars::FC_KERNING); 10515 } 10516 } 10517 10518 /* -- OTL -- */ 10519 // Use OTL OpenType Table Layout - GSUB & GPOS 10520 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 10521 $texte = $this->otl->applyOTL($texte, $this->CurrentFont['useOTL']); 10522 $OTLdata = $this->otl->OTLdata; 10523 } 10524 /* -- END OTL -- */ 10525 $this->OTLtags = $save_OTLtags; 10526 10527 $this->magic_reverse_dir($texte, $this->directionality, $OTLdata); 10528 10529 $this->SetAlpha($alpha); 10530 10531 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 10532 10533 $szfont = $fontsize; 10534 $loop = 0; 10535 $maxlen = (min($this->w, $this->h) ); // sets max length of text as 7/8 width/height of page 10536 10537 while ($loop == 0) { 10538 $this->SetFont($this->watermark_font, "B", $szfont, false); // Don't output 10539 $offset = ((sin(deg2rad($angle))) * ($szfont / Mpdf::SCALE)); 10540 10541 $strlen = $this->GetStringWidth($texte, true, $OTLdata, $textvar); 10542 if ($strlen > $maxlen - $offset) { 10543 $szfont --; 10544 } else { 10545 $loop ++; 10546 } 10547 } 10548 10549 $this->SetFont($this->watermark_font, "B", $szfont - 0.1, true, true); // Output The -0.1 is because SetFont above is not written to PDF 10550 10551 // Repeating it will not output anything as mPDF thinks it is set 10552 $adj = ((cos(deg2rad($angle))) * ($strlen / 2)); 10553 $opp = ((sin(deg2rad($angle))) * ($strlen / 2)); 10554 10555 $wx = ($this->w / 2) - $adj + $offset / 3; 10556 $wy = ($this->h / 2) + $opp; 10557 10558 $this->Rotate($angle, $wx, $wy); 10559 $this->Text($wx, $wy, $texte, $OTLdata, $textvar); 10560 $this->Rotate(0); 10561 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 10562 10563 $this->SetAlpha(1); 10564 } 10565 10566 function watermarkImg($src, $alpha = 0.2) 10567 { 10568 if ($this->PDFA || $this->PDFX) { 10569 throw new \Mpdf\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!'); 10570 } 10571 10572 if ($this->watermarkImgBehind) { 10573 $this->watermarkImgAlpha = $this->SetAlpha($alpha, 'Normal', true); 10574 } else { 10575 $this->SetAlpha($alpha, $this->watermarkImgAlphaBlend); 10576 } 10577 10578 $this->Image($src, 0, 0, 0, 0, '', '', true, true, true); 10579 10580 if (!$this->watermarkImgBehind) { 10581 $this->SetAlpha(1); 10582 } 10583 } 10584 10585 /* -- END WATERMARK -- */ 10586 10587 function Rotate($angle, $x = -1, $y = -1) 10588 { 10589 if ($x == -1) { 10590 $x = $this->x; 10591 } 10592 if ($y == -1) { 10593 $y = $this->y; 10594 } 10595 if ($this->angle != 0) { 10596 $this->writer->write('Q'); 10597 } 10598 $this->angle = $angle; 10599 if ($angle != 0) { 10600 $angle*=M_PI / 180; 10601 $c = cos($angle); 10602 $s = sin($angle); 10603 $cx = $x * Mpdf::SCALE; 10604 $cy = ($this->h - $y) * Mpdf::SCALE; 10605 $this->writer->write(sprintf('q %.5F %.5F %.5F %.5F %.3F %.3F cm 1 0 0 1 %.3F %.3F cm', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy)); 10606 } 10607 } 10608 10609 function CircularText($x, $y, $r, $text, $align = 'top', $fontfamily = '', $fontsize = 0, $fontstyle = '', $kerning = 120, $fontwidth = 100, $divider = '') 10610 { 10611 if (empty($this->directWrite)) { 10612 $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter); 10613 } 10614 10615 $this->directWrite->CircularText($x, $y, $r, $text, $align, $fontfamily, $fontsize, $fontstyle, $kerning, $fontwidth, $divider); 10616 } 10617 10618 // From Invoice 10619 function RoundedRect($x, $y, $w, $h, $r, $style = '') 10620 { 10621 $hp = $this->h; 10622 10623 if ($style == 'F') { 10624 $op = 'f'; 10625 } elseif ($style == 'FD' or $style == 'DF') { 10626 $op = 'B'; 10627 } else { 10628 $op = 'S'; 10629 } 10630 10631 $MyArc = 4 / 3 * (sqrt(2) - 1); 10632 $this->writer->write(sprintf('%.3F %.3F m', ($x + $r) * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE)); 10633 $xc = $x + $w - $r; 10634 $yc = $y + $r; 10635 $this->writer->write(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE)); 10636 10637 $this->_Arc($xc + $r * $MyArc, $yc - $r, $xc + $r, $yc - $r * $MyArc, $xc + $r, $yc); 10638 $xc = $x + $w - $r; 10639 $yc = $y + $h - $r; 10640 $this->writer->write(sprintf('%.3F %.3F l', ($x + $w) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE)); 10641 10642 $this->_Arc($xc + $r, $yc + $r * $MyArc, $xc + $r * $MyArc, $yc + $r, $xc, $yc + $r); 10643 $xc = $x + $r; 10644 $yc = $y + $h - $r; 10645 $this->writer->write(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - ($y + $h)) * Mpdf::SCALE)); 10646 10647 $this->_Arc($xc - $r * $MyArc, $yc + $r, $xc - $r, $yc + $r * $MyArc, $xc - $r, $yc); 10648 $xc = $x + $r; 10649 $yc = $y + $r; 10650 $this->writer->write(sprintf('%.3F %.3F l', ($x) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE)); 10651 10652 $this->_Arc($xc - $r, $yc - $r * $MyArc, $xc - $r * $MyArc, $yc - $r, $xc, $yc - $r); 10653 $this->writer->write($op); 10654 } 10655 10656 function _Arc($x1, $y1, $x2, $y2, $x3, $y3) 10657 { 10658 $h = $this->h; 10659 $this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x1 * Mpdf::SCALE, ($h - $y1) * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($h - $y2) * Mpdf::SCALE, $x3 * Mpdf::SCALE, ($h - $y3) * Mpdf::SCALE)); 10660 } 10661 10662 // ==================================================== 10663 10664 10665 10666 /* -- DIRECTW -- */ 10667 function Shaded_box($text, $font = '', $fontstyle = 'B', $szfont = '', $width = '70%', $style = 'DF', $radius = 2.5, $fill = '#FFFFFF', $color = '#000000', $pad = 2) 10668 { 10669 // F (shading - no line),S (line, no shading),DF (both) 10670 if (empty($this->directWrite)) { 10671 $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter); 10672 } 10673 $this->directWrite->Shaded_box($text, $font, $fontstyle, $szfont, $width, $style, $radius, $fill, $color, $pad); 10674 } 10675 10676 /* -- END DIRECTW -- */ 10677 10678 function UTF8StringToArray($str, $addSubset = true) 10679 { 10680 $out = []; 10681 $len = strlen($str); 10682 for ($i = 0; $i < $len; $i++) { 10683 $uni = -1; 10684 $h = ord($str[$i]); 10685 if ($h <= 0x7F) { 10686 $uni = $h; 10687 } elseif ($h >= 0xC2) { 10688 if (($h <= 0xDF) && ($i < $len - 1)) { 10689 $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F); 10690 } elseif (($h <= 0xEF) && ($i < $len - 2)) { 10691 $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F); 10692 } elseif (($h <= 0xF4) && ($i < $len - 3)) { 10693 $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F); 10694 } 10695 } 10696 if ($uni >= 0) { 10697 $out[] = $uni; 10698 if ($addSubset && isset($this->CurrentFont['subset'])) { 10699 $this->CurrentFont['subset'][$uni] = $uni; 10700 } 10701 } 10702 } 10703 return $out; 10704 } 10705 10706 // Convert utf-8 string to <HHHHHH> for Font Subsets 10707 function UTF8toSubset($str) 10708 { 10709 $ret = '<'; 10710 // $str = preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $str ); // mPDF 6 deleted 10711 // $str = preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $str ); // mPDF 6 deleted 10712 $unicode = $this->UTF8StringToArray($str); 10713 $orig_fid = $this->CurrentFont['subsetfontids'][0]; 10714 $last_fid = $this->CurrentFont['subsetfontids'][0]; 10715 foreach ($unicode as $c) { 10716 /* // mPDF 6 deleted 10717 if ($c == 7 || $c == 8) { 10718 if ($orig_fid != $last_fid) { 10719 $ret .= '> Tj /F'.$orig_fid.' '.$this->FontSizePt.' Tf <'; 10720 $last_fid = $orig_fid; 10721 } 10722 if ($c == 7) { $ret .= $this->aliasNbPgHex; } 10723 else { $ret .= $this->aliasNbPgGpHex; } 10724 continue; 10725 } 10726 */ 10727 if (!$this->_charDefined($this->CurrentFont['cw'], $c)) { 10728 $c = 0; 10729 } // mPDF 6 10730 for ($i = 0; $i < 99; $i++) { 10731 // return c as decimal char 10732 $init = array_search($c, $this->CurrentFont['subsets'][$i]); 10733 if ($init !== false) { 10734 if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) { 10735 $ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <'; 10736 $last_fid = $this->CurrentFont['subsetfontids'][$i]; 10737 } 10738 $ret .= sprintf("%02s", strtoupper(dechex($init))); 10739 break; 10740 } // TrueType embedded SUBSETS 10741 elseif (count($this->CurrentFont['subsets'][$i]) < 255) { 10742 $n = count($this->CurrentFont['subsets'][$i]); 10743 $this->CurrentFont['subsets'][$i][$n] = $c; 10744 if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) { 10745 $ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <'; 10746 $last_fid = $this->CurrentFont['subsetfontids'][$i]; 10747 } 10748 $ret .= sprintf("%02s", strtoupper(dechex($n))); 10749 break; 10750 } elseif (!isset($this->CurrentFont['subsets'][($i + 1)])) { 10751 // TrueType embedded SUBSETS 10752 $this->CurrentFont['subsets'][($i + 1)] = [0 => 0]; 10753 $new_fid = count($this->fonts) + $this->extraFontSubsets + 1; 10754 $this->CurrentFont['subsetfontids'][($i + 1)] = $new_fid; 10755 $this->extraFontSubsets++; 10756 } 10757 } 10758 } 10759 $ret .= '>'; 10760 if ($last_fid != $orig_fid) { 10761 $ret .= ' Tj /F' . $orig_fid . ' ' . $this->FontSizePt . ' Tf <> '; 10762 } 10763 return $ret; 10764 } 10765 10766 /* -- CJK-FONTS -- */ 10767 10768 // from class PDF_Chinese CJK EXTENSIONS 10769 function AddCIDFont($family, $style, $name, &$cw, $CMap, $registry, $desc) 10770 { 10771 $fontkey = strtolower($family) . strtoupper($style); 10772 if (isset($this->fonts[$fontkey])) { 10773 throw new \Mpdf\MpdfException("Font already added: $family $style"); 10774 } 10775 $i = count($this->fonts) + $this->extraFontSubsets + 1; 10776 $name = str_replace(' ', '', $name); 10777 if ($family == 'sjis') { 10778 $up = -120; 10779 } else { 10780 $up = -130; 10781 } 10782 // ? 'up' and 'ut' do not seem to be referenced anywhere 10783 $this->fonts[$fontkey] = ['i' => $i, 'type' => 'Type0', 'name' => $name, 'up' => $up, 'ut' => 40, 'cw' => $cw, 'CMap' => $CMap, 'registry' => $registry, 'MissingWidth' => 1000, 'desc' => $desc]; 10784 } 10785 10786 function AddCJKFont($family) 10787 { 10788 10789 if ($this->PDFA || $this->PDFX) { 10790 throw new \Mpdf\MpdfException("Adobe CJK fonts cannot be embedded in mPDF (required for PDFA1-b and PDFX/1-a)."); 10791 } 10792 if ($family == 'big5') { 10793 $this->AddBig5Font(); 10794 } elseif ($family == 'gb') { 10795 $this->AddGBFont(); 10796 } elseif ($family == 'sjis') { 10797 $this->AddSJISFont(); 10798 } elseif ($family == 'uhc') { 10799 $this->AddUHCFont(); 10800 } 10801 } 10802 10803 function AddBig5Font() 10804 { 10805 // Add Big5 font with proportional Latin 10806 $family = 'big5'; 10807 $name = 'MSungStd-Light-Acro'; 10808 $cw = $this->Big5_widths; 10809 $CMap = 'UniCNS-UTF16-H'; 10810 $registry = ['ordering' => 'CNS1', 'supplement' => 4]; 10811 $desc = [ 10812 'Ascent' => 880, 10813 'Descent' => -120, 10814 'CapHeight' => 880, 10815 'Flags' => 6, 10816 'FontBBox' => '[-160 -249 1015 1071]', 10817 'ItalicAngle' => 0, 10818 'StemV' => 93, 10819 ]; 10820 $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc); 10821 $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc); 10822 $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc); 10823 $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc); 10824 } 10825 10826 function AddGBFont() 10827 { 10828 // Add GB font with proportional Latin 10829 $family = 'gb'; 10830 $name = 'STSongStd-Light-Acro'; 10831 $cw = $this->GB_widths; 10832 $CMap = 'UniGB-UTF16-H'; 10833 $registry = ['ordering' => 'GB1', 'supplement' => 4]; 10834 $desc = [ 10835 'Ascent' => 880, 10836 'Descent' => -120, 10837 'CapHeight' => 737, 10838 'Flags' => 6, 10839 'FontBBox' => '[-25 -254 1000 880]', 10840 'ItalicAngle' => 0, 10841 'StemV' => 58, 10842 'Style' => '<< /Panose <000000000400000000000000> >>', 10843 ]; 10844 $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc); 10845 $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc); 10846 $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc); 10847 $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc); 10848 } 10849 10850 function AddSJISFont() 10851 { 10852 // Add SJIS font with proportional Latin 10853 $family = 'sjis'; 10854 $name = 'KozMinPro-Regular-Acro'; 10855 $cw = $this->SJIS_widths; 10856 $CMap = 'UniJIS-UTF16-H'; 10857 $registry = ['ordering' => 'Japan1', 'supplement' => 5]; 10858 $desc = [ 10859 'Ascent' => 880, 10860 'Descent' => -120, 10861 'CapHeight' => 740, 10862 'Flags' => 6, 10863 'FontBBox' => '[-195 -272 1110 1075]', 10864 'ItalicAngle' => 0, 10865 'StemV' => 86, 10866 'XHeight' => 502, 10867 ]; 10868 $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc); 10869 $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc); 10870 $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc); 10871 $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc); 10872 } 10873 10874 function AddUHCFont() 10875 { 10876 // Add UHC font with proportional Latin 10877 $family = 'uhc'; 10878 $name = 'HYSMyeongJoStd-Medium-Acro'; 10879 $cw = $this->UHC_widths; 10880 $CMap = 'UniKS-UTF16-H'; 10881 $registry = ['ordering' => 'Korea1', 'supplement' => 2]; 10882 $desc = [ 10883 'Ascent' => 880, 10884 'Descent' => -120, 10885 'CapHeight' => 720, 10886 'Flags' => 6, 10887 'FontBBox' => '[-28 -148 1001 880]', 10888 'ItalicAngle' => 0, 10889 'StemV' => 60, 10890 'Style' => '<< /Panose <000000000600000000000000> >>', 10891 ]; 10892 $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc); 10893 $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc); 10894 $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc); 10895 $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc); 10896 } 10897 10898 /* -- END CJK-FONTS -- */ 10899 10900 ////////////////////////////////////////////////////////////////////////////// 10901 ////////////////////////////////////////////////////////////////////////////// 10902 ////////////////////////////////////////////////////////////////////////////// 10903 ////////////////////////////////////////////////////////////////////////////// 10904 ////////////////////////////////////////////////////////////////////////////// 10905 ////////////////////////////////////////////////////////////////////////////// 10906 ////////////////////////////////////////////////////////////////////////////// 10907 10908 function SetDefaultFont($font) 10909 { 10910 // Disallow embedded fonts to be used as defaults in PDFA 10911 if ($this->PDFA || $this->PDFX) { 10912 if (strtolower($font) == 'ctimes') { 10913 $font = 'serif'; 10914 } 10915 if (strtolower($font) == 'ccourier') { 10916 $font = 'monospace'; 10917 } 10918 if (strtolower($font) == 'chelvetica') { 10919 $font = 'sans-serif'; 10920 } 10921 } 10922 $font = $this->SetFont($font); // returns substituted font if necessary 10923 $this->default_font = $font; 10924 $this->original_default_font = $font; 10925 if (!$this->watermark_font) { 10926 $this->watermark_font = $font; 10927 } // *WATERMARK* 10928 $this->defaultCSS['BODY']['FONT-FAMILY'] = $font; 10929 $this->cssManager->CSS['BODY']['FONT-FAMILY'] = $font; 10930 } 10931 10932 function SetDefaultFontSize($fontsize) 10933 { 10934 $this->default_font_size = $fontsize; 10935 $this->original_default_font_size = $fontsize; 10936 $this->SetFontSize($fontsize); 10937 $this->defaultCSS['BODY']['FONT-SIZE'] = $fontsize . 'pt'; 10938 $this->cssManager->CSS['BODY']['FONT-SIZE'] = $fontsize . 'pt'; 10939 } 10940 10941 function SetDefaultBodyCSS($prop, $val) 10942 { 10943 if ($prop) { 10944 $this->defaultCSS['BODY'][strtoupper($prop)] = $val; 10945 $this->cssManager->CSS['BODY'][strtoupper($prop)] = $val; 10946 } 10947 } 10948 10949 function SetDirectionality($dir = 'ltr') 10950 { 10951 /* -- OTL -- */ 10952 if (strtolower($dir) == 'rtl') { 10953 if ($this->directionality != 'rtl') { 10954 // Swop L/R Margins so page 1 RTL is an 'even' page 10955 $tmp = $this->DeflMargin; 10956 $this->DeflMargin = $this->DefrMargin; 10957 $this->DefrMargin = $tmp; 10958 $this->orig_lMargin = $this->DeflMargin; 10959 $this->orig_rMargin = $this->DefrMargin; 10960 10961 $this->SetMargins($this->DeflMargin, $this->DefrMargin, $this->tMargin); 10962 } 10963 $this->directionality = 'rtl'; 10964 $this->defaultAlign = 'R'; 10965 $this->defaultTableAlign = 'R'; 10966 } else { 10967 /* -- END OTL -- */ 10968 $this->directionality = 'ltr'; 10969 $this->defaultAlign = 'L'; 10970 $this->defaultTableAlign = 'L'; 10971 } // *OTL* 10972 $this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality; 10973 } 10974 10975 // Return either a number (factor) - based on current set fontsize (if % or em) - or exact lineheight (with 'mm' after it) 10976 function fixLineheight($v) 10977 { 10978 $lh = false; 10979 if (preg_match('/^[0-9\.,]*$/', $v) && $v >= 0) { 10980 return ($v + 0); 10981 } elseif (strtoupper($v) == 'NORMAL' || $v == 'N') { 10982 return 'N'; // mPDF 6 10983 } else { 10984 $tlh = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize, true); 10985 if ($tlh) { 10986 return ($tlh . 'mm'); 10987 } 10988 } 10989 return $this->normalLineheight; 10990 } 10991 10992 function _getNormalLineheight($desc = false) 10993 { 10994 if (!$desc) { 10995 $desc = $this->CurrentFont['desc']; 10996 } 10997 if (!isset($desc['Leading'])) { 10998 $desc['Leading'] = 0; 10999 } 11000 if ($this->useFixedNormalLineHeight) { 11001 $lh = $this->normalLineheight; 11002 } elseif (isset($desc['Ascent']) && $desc['Ascent']) { 11003 $lh = ($this->adjustFontDescLineheight * ($desc['Ascent'] - $desc['Descent'] + $desc['Leading']) / 1000); 11004 } else { 11005 $lh = $this->normalLineheight; 11006 } 11007 return $lh; 11008 } 11009 11010 // Set a (fixed) lineheight to an actual value - either to named fontsize(pts) or default 11011 function SetLineHeight($FontPt = '', $lh = '') 11012 { 11013 if (!$FontPt) { 11014 $FontPt = $this->FontSizePt; 11015 } 11016 $fs = $FontPt / Mpdf::SCALE; 11017 $this->lineheight = $this->_computeLineheight($lh, $fs); 11018 } 11019 11020 function _computeLineheight($lh, $fs = '') 11021 { 11022 if ($this->shrin_k > 1) { 11023 $k = $this->shrin_k; 11024 } else { 11025 $k = 1; 11026 } 11027 if (!$fs) { 11028 $fs = $this->FontSize; 11029 } 11030 if ($lh == 'N') { 11031 $lh = $this->_getNormalLineheight(); 11032 } 11033 if (preg_match('/mm/', $lh)) { 11034 return (((float) $lh) / $k); // convert to number 11035 } elseif ($lh > 0) { 11036 return ($fs * $lh); 11037 } 11038 return ($fs * $this->normalLineheight); 11039 } 11040 11041 function _setLineYpos(&$fontsize, &$fontdesc, &$CSSlineheight, $blockYpos = false) 11042 { 11043 $ypos['glyphYorigin'] = 0; 11044 $ypos['baseline-shift'] = 0; 11045 $linegap = 0; 11046 $leading = 0; 11047 11048 if (isset($fontdesc['Ascent']) && $fontdesc['Ascent'] && !$this->useFixedTextBaseline) { 11049 // Fontsize uses font metrics - this method seems to produce results compatible with browsers (except IE9) 11050 $ypos['boxtop'] = $fontdesc['Ascent'] / 1000 * $fontsize; 11051 $ypos['boxbottom'] = $fontdesc['Descent'] / 1000 * $fontsize; 11052 if (isset($fontdesc['Leading'])) { 11053 $linegap = $fontdesc['Leading'] / 1000 * $fontsize; 11054 } 11055 } // Default if not set - uses baselineC 11056 else { 11057 $ypos['boxtop'] = (0.5 + $this->baselineC) * $fontsize; 11058 $ypos['boxbottom'] = -(0.5 - $this->baselineC) * $fontsize; 11059 } 11060 $fontheight = $ypos['boxtop'] - $ypos['boxbottom']; 11061 11062 if ($this->shrin_k > 1) { 11063 $shrin_k = $this->shrin_k; 11064 } else { 11065 $shrin_k = 1; 11066 } 11067 11068 $leading = 0; 11069 if ($CSSlineheight == 'N') { 11070 $lh = $this->_getNormalLineheight($fontdesc); 11071 $lineheight = ($fontsize * $lh); 11072 $leading += $linegap; // specified in hhea or sTypo in OpenType tables 11073 } elseif (preg_match('/mm/', $CSSlineheight)) { 11074 $lineheight = (((float) $CSSlineheight) / $shrin_k); // convert to number 11075 } // ??? If lineheight is a factor e.g. 1.3 ?? use factor x 1em or ? use 'normal' lineheight * factor 11076 // Could depend on value for $text_height - a draft CSS value as set above for now 11077 elseif ($CSSlineheight > 0) { 11078 $lineheight = ($fontsize * $CSSlineheight); 11079 } else { 11080 $lineheight = ($fontsize * $this->normalLineheight); 11081 } 11082 11083 // In general, calculate the "leading" - the difference between the fontheight and the lineheight 11084 // and add half to the top and half to the bottom. BUT 11085 // If an inline element has a font-size less than the block element, and the line-height is set as an em or % value 11086 // it will add too much leading below the font and expand the height of the line - so just use the block element exttop/extbottom: 11087 if (preg_match('/mm/', $CSSlineheight) 11088 && ($blockYpos && $ypos['boxtop'] < $blockYpos['boxtop']) 11089 && ($blockYpos && $ypos['boxbottom'] > $blockYpos['boxbottom'])) { 11090 11091 $ypos['exttop'] = $blockYpos['exttop']; 11092 $ypos['extbottom'] = $blockYpos['extbottom']; 11093 11094 } else { 11095 11096 $leading += ($lineheight - $fontheight); 11097 11098 $ypos['exttop'] = $ypos['boxtop'] + $leading / 2; 11099 $ypos['extbottom'] = $ypos['boxbottom'] - $leading / 2; 11100 } 11101 11102 11103 // TEMP ONLY FOR DEBUGGING ********************************* 11104 // $ypos['lineheight'] = $lineheight; 11105 // $ypos['fontheight'] = $fontheight; 11106 // $ypos['leading'] = $leading; 11107 11108 return $ypos; 11109 } 11110 11111 /* Called from WriteFlowingBlock() and finishFlowingBlock() 11112 Determines the line hieght and glyph/writing position 11113 for each element in the line to be written */ 11114 11115 function _setInlineBlockHeights(&$lineBox, &$stackHeight, &$content, &$font, $is_table) 11116 { 11117 if ($this->shrin_k > 1) { 11118 $shrin_k = $this->shrin_k; 11119 } else { 11120 $shrin_k = 1; 11121 } 11122 11123 $ypos = []; 11124 $bordypos = []; 11125 $bgypos = []; 11126 11127 if ($is_table) { 11128 // FOR TABLE 11129 $fontsize = $this->FontSize; 11130 $fontkey = $this->FontFamily . $this->FontStyle; 11131 $fontdesc = $this->fonts[$fontkey]['desc']; 11132 $CSSlineheight = $this->cellLineHeight; 11133 $line_stacking_strategy = $this->cellLineStackingStrategy; // inline-line-height [default] | block-line-height | max-height | grid-height 11134 $line_stacking_shift = $this->cellLineStackingShift; // consider-shifts [default] | disregard-shifts 11135 } else { 11136 // FOR BLOCK FONT 11137 $fontsize = $this->blk[$this->blklvl]['InlineProperties']['size']; 11138 $fontkey = $this->blk[$this->blklvl]['InlineProperties']['family'] . $this->blk[$this->blklvl]['InlineProperties']['style']; 11139 $fontdesc = $this->fonts[$fontkey]['desc']; 11140 $CSSlineheight = $this->blk[$this->blklvl]['line_height']; 11141 // inline-line-height | block-line-height | max-height | grid-height 11142 $line_stacking_strategy = (isset($this->blk[$this->blklvl]['line_stacking_strategy']) ? $this->blk[$this->blklvl]['line_stacking_strategy'] : 'inline-line-height'); 11143 // consider-shifts | disregard-shifts 11144 $line_stacking_shift = (isset($this->blk[$this->blklvl]['line_stacking_shift']) ? $this->blk[$this->blklvl]['line_stacking_shift'] : 'consider-shifts'); 11145 } 11146 $boxLineHeight = $this->_computeLineheight($CSSlineheight, $fontsize); 11147 11148 11149 // First, set a "strut" using block font at index $lineBox[-1] 11150 $ypos[-1] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight); 11151 11152 // for the block element - always taking the block EXTENDED progression including leading - which may be negative 11153 if ($line_stacking_strategy == 'block-line-height') { 11154 $topy = $ypos[-1]['exttop']; 11155 $bottomy = $ypos[-1]['extbottom']; 11156 } else { 11157 $topy = 0; 11158 $bottomy = 0; 11159 } 11160 11161 // Get text-middle for aligning images/objects 11162 $midpoint = $ypos[-1]['boxtop'] - (($ypos[-1]['boxtop'] - $ypos[-1]['boxbottom']) / 2); 11163 11164 // for images / inline objects / replaced elements 11165 $mta = 0; // Maximum top-aligned 11166 $mba = 0; // Maximum bottom-aligned 11167 foreach ($content as $k => $chunk) { 11168 if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'listmarker') { 11169 $ypos[$k] = $ypos[-1]; 11170 // UPDATE Maximums 11171 if ($line_stacking_strategy == 'block-line-height' || $line_stacking_strategy == 'grid-height' || $line_stacking_strategy == 'max-height') { // don't include extended block progression of all inline elements 11172 if ($ypos[$k]['boxtop'] > $topy) { 11173 $topy = $ypos[$k]['boxtop']; 11174 } 11175 if ($ypos[$k]['boxbottom'] < $bottomy) { 11176 $bottomy = $ypos[$k]['boxbottom']; 11177 } 11178 } else { 11179 if ($ypos[$k]['exttop'] > $topy) { 11180 $topy = $ypos[$k]['exttop']; 11181 } 11182 if ($ypos[$k]['extbottom'] < $bottomy) { 11183 $bottomy = $ypos[$k]['extbottom']; 11184 } 11185 } 11186 } elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB 11187 $fontsize = $font[$k]['size']; 11188 $fontdesc = $font[$k]['curr']['desc']; 11189 $lh = 1; 11190 $ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $lh, $ypos[-1]); // Lineheight=1 fixed 11191 } elseif (isset($this->objectbuffer[$k])) { 11192 $oh = $this->objectbuffer[$k]['OUTER-HEIGHT']; 11193 $va = $this->objectbuffer[$k]['vertical-align']; 11194 11195 if ($va == 'BS') { // (BASELINE default) 11196 if ($oh > $topy) { 11197 $topy = $oh; 11198 } 11199 } elseif ($va == 'M') { 11200 if (($midpoint + $oh / 2) > $topy) { 11201 $topy = $midpoint + $oh / 2; 11202 } 11203 if (($midpoint - $oh / 2) < $bottomy) { 11204 $bottomy = $midpoint - $oh / 2; 11205 } 11206 } elseif ($va == 'TT') { 11207 if (($ypos[-1]['boxtop'] - $oh) < $bottomy) { 11208 $bottomy = $ypos[-1]['boxtop'] - $oh; 11209 $topy = max($topy, $ypos[-1]['boxtop']); 11210 } 11211 } elseif ($va == 'TB') { 11212 if (($ypos[-1]['boxbottom'] + $oh) > $topy) { 11213 $topy = $ypos[-1]['boxbottom'] + $oh; 11214 $bottomy = min($bottomy, $ypos[-1]['boxbottom']); 11215 } 11216 } elseif ($va == 'T') { 11217 if ($oh > $mta) { 11218 $mta = $oh; 11219 } 11220 } elseif ($va == 'B') { 11221 if ($oh > $mba) { 11222 $mba = $oh; 11223 } 11224 } 11225 } elseif ($content[$k] || $content[$k] === '0') { 11226 // FOR FLOWING BLOCK 11227 $fontsize = $font[$k]['size']; 11228 $fontdesc = $font[$k]['curr']['desc']; 11229 // In future could set CSS line-height from inline elements; for now, use block level: 11230 $ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight, $ypos[-1]); 11231 11232 if (isset($font[$k]['textparam']['text-baseline']) && $font[$k]['textparam']['text-baseline'] != 0) { 11233 $ypos[$k]['baseline-shift'] = $font[$k]['textparam']['text-baseline']; 11234 } 11235 11236 // DO ALIGNMENT FOR BASELINES ******************* 11237 // Until most fonts have OpenType BASE tables, this won't work 11238 // $ypos[$k] compared to $ypos[-1] or $ypos[$k-1] using $dominant_baseline and $baseline_table 11239 // UPDATE Maximums 11240 if ($line_stacking_strategy == 'block-line-height' || $line_stacking_strategy == 'grid-height' || $line_stacking_strategy == 'max-height') { // don't include extended block progression of all inline elements 11241 if ($line_stacking_shift == 'disregard-shifts') { 11242 if ($ypos[$k]['boxtop'] > $topy) { 11243 $topy = $ypos[$k]['boxtop']; 11244 } 11245 if ($ypos[$k]['boxbottom'] < $bottomy) { 11246 $bottomy = $ypos[$k]['boxbottom']; 11247 } 11248 } else { 11249 if (($ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift']) > $topy) { 11250 $topy = $ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift']; 11251 } 11252 if (($ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) { 11253 $bottomy = $ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift']; 11254 } 11255 } 11256 } else { 11257 if ($line_stacking_shift == 'disregard-shifts') { 11258 if ($ypos[$k]['exttop'] > $topy) { 11259 $topy = $ypos[$k]['exttop']; 11260 } 11261 if ($ypos[$k]['extbottom'] < $bottomy) { 11262 $bottomy = $ypos[$k]['extbottom']; 11263 } 11264 } else { 11265 if (($ypos[$k]['exttop'] + $ypos[$k]['baseline-shift']) > $topy) { 11266 $topy = $ypos[$k]['exttop'] + $ypos[$k]['baseline-shift']; 11267 } 11268 if (($ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) { 11269 $bottomy = $ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift']; 11270 } 11271 } 11272 } 11273 11274 // If BORDER set on inline element 11275 if (isset($font[$k]['bord']) && $font[$k]['bord']) { 11276 $bordfontsize = $font[$k]['textparam']['bord-decoration']['fontsize'] / $shrin_k; 11277 $bordfontkey = $font[$k]['textparam']['bord-decoration']['fontkey']; 11278 if ($bordfontkey != $fontkey || $bordfontsize != $fontsize || isset($font[$k]['textparam']['bord-decoration']['baseline'])) { 11279 $bordfontdesc = $this->fonts[$bordfontkey]['desc']; 11280 $bordypos[$k] = $this->_setLineYpos($bordfontsize, $bordfontdesc, $CSSlineheight, $ypos[-1]); 11281 if (isset($font[$k]['textparam']['bord-decoration']['baseline']) && $font[$k]['textparam']['bord-decoration']['baseline'] != 0) { 11282 $bordypos[$k]['baseline-shift'] = $font[$k]['textparam']['bord-decoration']['baseline'] / $shrin_k; 11283 } 11284 } 11285 } 11286 // If BACKGROUND set on inline element 11287 if (isset($font[$k]['spanbgcolor']) && $font[$k]['spanbgcolor']) { 11288 $bgfontsize = $font[$k]['textparam']['bg-decoration']['fontsize'] / $shrin_k; 11289 $bgfontkey = $font[$k]['textparam']['bg-decoration']['fontkey']; 11290 if ($bgfontkey != $fontkey || $bgfontsize != $fontsize || isset($font[$k]['textparam']['bg-decoration']['baseline'])) { 11291 $bgfontdesc = $this->fonts[$bgfontkey]['desc']; 11292 $bgypos[$k] = $this->_setLineYpos($bgfontsize, $bgfontdesc, $CSSlineheight, $ypos[-1]); 11293 if (isset($font[$k]['textparam']['bg-decoration']['baseline']) && $font[$k]['textparam']['bg-decoration']['baseline'] != 0) { 11294 $bgypos[$k]['baseline-shift'] = $font[$k]['textparam']['bg-decoration']['baseline'] / $shrin_k; 11295 } 11296 } 11297 } 11298 } 11299 } 11300 11301 11302 // TOP or BOTTOM aligned images 11303 if ($mta > ($topy - $bottomy)) { 11304 if (($topy - $mta) < $bottomy) { 11305 $bottomy = $topy - $mta; 11306 } 11307 } 11308 if ($mba > ($topy - $bottomy)) { 11309 if (($bottomy + $mba) > $topy) { 11310 $topy = $bottomy + $mba; 11311 } 11312 } 11313 11314 if ($line_stacking_strategy == 'block-line-height') { // fixed height set by block element (whether present or not) 11315 $topy = $ypos[-1]['exttop']; 11316 $bottomy = $ypos[-1]['extbottom']; 11317 } 11318 11319 $inclusiveHeight = $topy - $bottomy; 11320 11321 // SET $stackHeight taking note of line_stacking_strategy 11322 // NB inclusive height already takes account of need to consider block progression height (excludes leading set by lineheight) 11323 // or extended block progression height (includes leading set by lineheight) 11324 if ($line_stacking_strategy == 'block-line-height') { // fixed = extended block progression height of block element 11325 $stackHeight = $boxLineHeight; 11326 } elseif ($line_stacking_strategy == 'max-height') { // smallest height which includes extended block progression height of block element 11327 // and block progression heights of inline elements (NOT extended) 11328 $stackHeight = $inclusiveHeight; 11329 } elseif ($line_stacking_strategy == 'grid-height') { // smallest multiple of block element lineheight to include 11330 // block progression heights of inline elements (NOT extended) 11331 $stackHeight = $boxLineHeight; 11332 while ($stackHeight < $inclusiveHeight) { 11333 $stackHeight += $boxLineHeight; 11334 } 11335 } else { // 'inline-line-height' = default // smallest height which includes extended block progression height of block element 11336 // AND extended block progression heights of inline elements 11337 $stackHeight = $inclusiveHeight; 11338 } 11339 11340 $diff = $stackHeight - $inclusiveHeight; 11341 $topy += $diff / 2; 11342 $bottomy -= $diff / 2; 11343 11344 // ADJUST $ypos => lineBox using $stackHeight; lineBox are all offsets from the top of stackHeight in mm 11345 // and SET IMAGE OFFSETS 11346 $lineBox[-1]['boxtop'] = $topy - $ypos[-1]['boxtop']; 11347 $lineBox[-1]['boxbottom'] = $topy - $ypos[-1]['boxbottom']; 11348 // $lineBox[-1]['exttop'] = $topy - $ypos[-1]['exttop']; 11349 // $lineBox[-1]['extbottom'] = $topy - $ypos[-1]['extbottom']; 11350 $lineBox[-1]['glyphYorigin'] = $topy - $ypos[-1]['glyphYorigin']; 11351 $lineBox[-1]['baseline-shift'] = $ypos[-1]['baseline-shift']; 11352 11353 $midpoint = $lineBox[-1]['boxbottom'] - (($lineBox[-1]['boxbottom'] - $lineBox[-1]['boxtop']) / 2); 11354 11355 foreach ($content as $k => $chunk) { 11356 if (isset($this->objectbuffer[$k])) { 11357 $oh = $this->objectbuffer[$k]['OUTER-HEIGHT']; 11358 // LIST MARKERS 11359 if ($this->objectbuffer[$k]['type'] == 'listmarker') { 11360 $oh = $fontsize; 11361 } elseif ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB 11362 $oh = $font[$k]['size']; // == $this->objectbuffer[$k]['fontsize']/Mpdf::SCALE; 11363 $lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop']; 11364 $lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom']; 11365 $lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin']; 11366 $lineBox[$k]['baseline-shift'] = 0; 11367 // continue; 11368 } 11369 $va = $this->objectbuffer[$k]['vertical-align']; // = $objattr['vertical-align'] = set as M,T,B,S 11370 11371 if ($va == 'BS') { // (BASELINE default) 11372 $lineBox[$k]['top'] = $lineBox[-1]['glyphYorigin'] - $oh; 11373 } elseif ($va == 'M') { 11374 $lineBox[$k]['top'] = $midpoint - $oh / 2; 11375 } elseif ($va == 'TT') { 11376 $lineBox[$k]['top'] = $lineBox[-1]['boxtop']; 11377 } elseif ($va == 'TB') { 11378 $lineBox[$k]['top'] = $lineBox[-1]['boxbottom'] - $oh; 11379 } elseif ($va == 'T') { 11380 $lineBox[$k]['top'] = 0; 11381 } elseif ($va == 'B') { 11382 $lineBox[$k]['top'] = $stackHeight - $oh; 11383 } 11384 } elseif ($content[$k] || $content[$k] === '0') { 11385 $lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop']; 11386 $lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom']; 11387 // $lineBox[$k]['exttop'] = $topy - $ypos[$k]['exttop']; 11388 // $lineBox[$k]['extbottom'] = $topy - $ypos[$k]['extbottom']; 11389 $lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin']; 11390 $lineBox[$k]['baseline-shift'] = $ypos[$k]['baseline-shift']; 11391 if (isset($bordypos[$k]['boxtop'])) { 11392 $lineBox[$k]['border-boxtop'] = $topy - $bordypos[$k]['boxtop']; 11393 $lineBox[$k]['border-boxbottom'] = $topy - $bordypos[$k]['boxbottom']; 11394 $lineBox[$k]['border-baseline-shift'] = $bordypos[$k]['baseline-shift']; 11395 } 11396 if (isset($bgypos[$k]['boxtop'])) { 11397 $lineBox[$k]['background-boxtop'] = $topy - $bgypos[$k]['boxtop']; 11398 $lineBox[$k]['background-boxbottom'] = $topy - $bgypos[$k]['boxbottom']; 11399 $lineBox[$k]['background-baseline-shift'] = $bgypos[$k]['baseline-shift']; 11400 } 11401 } 11402 } 11403 } 11404 11405 function SetBasePath($str = '') 11406 { 11407 if (isset($_SERVER['HTTP_HOST'])) { 11408 $host = $_SERVER['HTTP_HOST']; 11409 } elseif (isset($_SERVER['SERVER_NAME'])) { 11410 $host = $_SERVER['SERVER_NAME']; 11411 } else { 11412 $host = ''; 11413 } 11414 if (!$str) { 11415 if (isset($_SERVER['SCRIPT_NAME'])) { 11416 $currentPath = dirname($_SERVER['SCRIPT_NAME']); 11417 } else { 11418 $currentPath = dirname($_SERVER['PHP_SELF']); 11419 } 11420 $currentPath = str_replace("\\", "/", $currentPath); 11421 if ($currentPath == '/') { 11422 $currentPath = ''; 11423 } 11424 if ($host) { // mPDF 6 11425 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') { 11426 $currpath = 'https://' . $host . $currentPath . '/'; 11427 } else { 11428 $currpath = 'http://' . $host . $currentPath . '/'; 11429 } 11430 } else { 11431 $currpath = ''; 11432 } 11433 $this->basepath = $currpath; 11434 $this->basepathIsLocal = true; 11435 return; 11436 } 11437 $str = preg_replace('/\?.*/', '', $str); 11438 if (!preg_match('/(http|https|ftp):\/\/.*\//i', $str)) { 11439 $str .= '/'; 11440 } 11441 $str .= 'xxx'; // in case $str ends in / e.g. http://www.bbc.co.uk/ 11442 $this->basepath = dirname($str) . "/"; // returns e.g. e.g. http://www.google.com/dir1/dir2/dir3/ 11443 $this->basepath = str_replace("\\", "/", $this->basepath); // If on Windows 11444 $tr = parse_url($this->basepath); 11445 if (isset($tr['host']) && ($tr['host'] == $host)) { 11446 $this->basepathIsLocal = true; 11447 } else { 11448 $this->basepathIsLocal = false; 11449 } 11450 } 11451 11452 public function GetFullPath(&$path, $basepath = '') 11453 { 11454 // When parsing CSS need to pass temporary basepath - so links are relative to current stylesheet 11455 if (!$basepath) { 11456 $basepath = $this->basepath; 11457 } 11458 11459 // Fix path value 11460 $path = str_replace("\\", '/', $path); // If on Windows 11461 11462 // mPDF 5.7.2 11463 if (substr($path, 0, 2) === '//') { 11464 $scheme = parse_url($basepath, PHP_URL_SCHEME); 11465 $scheme = $scheme ?: 'http'; 11466 $path = $scheme . ':' . $path; 11467 } 11468 11469 $path = preg_replace('|^./|', '', $path); // Inadvertently corrects "./path/etc" and "//www.domain.com/etc" 11470 11471 if (substr($path, 0, 1) == '#') { 11472 return; 11473 } 11474 11475 // Skip schemes not supported by installed stream wrappers 11476 $wrappers = stream_get_wrappers(); 11477 $pattern = sprintf('@^(?!%s)[a-z0-9\.\-+]+:.*@i', implode('|', $wrappers)); 11478 if (preg_match($pattern, $path)) { 11479 return; 11480 } 11481 11482 if (substr($path, 0, 3) == "../") { // It is a relative link 11483 11484 $backtrackamount = substr_count($path, "../"); 11485 $maxbacktrack = substr_count($basepath, "/") - 3; 11486 $filepath = str_replace("../", '', $path); 11487 $path = $basepath; 11488 11489 // If it is an invalid relative link, then make it go to directory root 11490 if ($backtrackamount > $maxbacktrack) { 11491 $backtrackamount = $maxbacktrack; 11492 } 11493 11494 // Backtrack some directories 11495 for ($i = 0; $i < $backtrackamount + 1; $i++) { 11496 $path = substr($path, 0, strrpos($path, "/")); 11497 } 11498 11499 $path = $path . "/" . $filepath; // Make it an absolute path 11500 11501 } elseif ((strpos($path, ":/") === false || strpos($path, ":/") > 10) && !@is_file($path)) { // It is a local link. Ignore potential file errors 11502 11503 if (substr($path, 0, 1) == "/") { 11504 11505 $tr = parse_url($basepath); 11506 11507 // mPDF 5.7.2 11508 $root = ''; 11509 if (!empty($tr['scheme'])) { 11510 $root .= $tr['scheme'] . '://'; 11511 } 11512 11513 $root .= isset($tr['host']) ? $tr['host'] : ''; 11514 $root .= ((isset($tr['port']) && $tr['port']) ? (':' . $tr['port']) : ''); // mPDF 5.7.3 11515 11516 $path = $root . $path; 11517 11518 } else { 11519 $path = $basepath . $path; 11520 } 11521 } 11522 // Do nothing if it is an Absolute Link 11523 } 11524 11525 function docPageNum($num = 0, $extras = false) 11526 { 11527 if ($num < 1) { 11528 $num = $this->page; 11529 } 11530 11531 $type = $this->defaultPageNumStyle; // set default Page Number Style 11532 $ppgno = $num; 11533 $suppress = 0; 11534 $offset = 0; 11535 $lastreset = 0; 11536 11537 foreach ($this->PageNumSubstitutions as $psarr) { 11538 11539 if ($num >= $psarr['from']) { 11540 11541 if ($psarr['reset']) { 11542 if ($psarr['reset'] > 1) { 11543 $offset = $psarr['reset'] - 1; 11544 } 11545 $ppgno = $num - $psarr['from'] + 1 + $offset; 11546 $lastreset = $psarr['from']; 11547 } 11548 11549 if ($psarr['type']) { 11550 $type = $psarr['type']; 11551 } 11552 11553 if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) { 11554 $suppress = 1; 11555 } elseif (strtoupper($psarr['suppress']) == 'OFF') { 11556 $suppress = 0; 11557 } 11558 } 11559 } 11560 11561 if ($suppress) { 11562 return ''; 11563 } 11564 11565 $ppgno = $this->_getStyledNumber($ppgno, $type); 11566 11567 if ($extras) { 11568 $ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix; 11569 } 11570 11571 return $ppgno; 11572 } 11573 11574 function docPageNumTotal($num = 0, $extras = false) 11575 { 11576 if ($num < 1) { 11577 $num = $this->page; 11578 } 11579 11580 $type = $this->defaultPageNumStyle; // set default Page Number Style 11581 $ppgstart = 1; 11582 $ppgend = count($this->pages) + 1; 11583 $suppress = 0; 11584 $offset = 0; 11585 11586 foreach ($this->PageNumSubstitutions as $psarr) { 11587 if ($num >= $psarr['from']) { 11588 if ($psarr['reset']) { 11589 if ($psarr['reset'] > 1) { 11590 $offset = $psarr['reset'] - 1; 11591 } 11592 $ppgstart = $psarr['from'] + $offset; 11593 $ppgend = count($this->pages) + 1 + $offset; 11594 } 11595 if ($psarr['type']) { 11596 $type = $psarr['type']; 11597 } 11598 if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) { 11599 $suppress = 1; 11600 } elseif (strtoupper($psarr['suppress']) == 'OFF') { 11601 $suppress = 0; 11602 } 11603 } 11604 if ($num < $psarr['from']) { 11605 if ($psarr['reset']) { 11606 $ppgend = $psarr['from'] + $offset; 11607 break; 11608 } 11609 } 11610 } 11611 11612 if ($suppress) { 11613 return ''; 11614 } 11615 11616 $ppgno = $ppgend - $ppgstart + $offset; 11617 $ppgno = $this->_getStyledNumber($ppgno, $type); 11618 11619 if ($extras) { 11620 $ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix; 11621 } 11622 11623 return $ppgno; 11624 } 11625 11626 // mPDF 6 11627 function _getStyledNumber($ppgno, $type, $listmarker = false) 11628 { 11629 if ($listmarker) { 11630 $reverse = true; // Reverse RTL numerals (Hebrew) when using for list 11631 $checkfont = true; // Using list - font is set, so check if character is available 11632 } else { 11633 $reverse = false; // For pagenumbers, RTL numerals (Hebrew) will get reversed later by bidi 11634 $checkfont = false; // For pagenumbers - font is not set, so no check 11635 } 11636 11637 $decToAlpha = new Conversion\DecToAlpha(); 11638 $decToCjk = new Conversion\DecToCjk(); 11639 $decToHebrew = new Conversion\DecToHebrew(); 11640 $decToRoman = new Conversion\DecToRoman(); 11641 $decToOther = new Conversion\DecToOther($this); 11642 11643 $lowertype = strtolower($type); 11644 11645 if ($lowertype == 'upper-latin' || $lowertype == 'upper-alpha' || $type == 'A') { 11646 11647 $ppgno = $decToAlpha->convert($ppgno, true); 11648 11649 } elseif ($lowertype == 'lower-latin' || $lowertype == 'lower-alpha' || $type == 'a') { 11650 11651 $ppgno = $decToAlpha->convert($ppgno, false); 11652 11653 } elseif ($lowertype == 'upper-roman' || $type == 'I') { 11654 11655 $ppgno = $decToRoman->convert($ppgno, true); 11656 11657 } elseif ($lowertype == 'lower-roman' || $type == 'i') { 11658 11659 $ppgno = $decToRoman->convert($ppgno, false); 11660 11661 } elseif ($lowertype == 'hebrew') { 11662 11663 $ppgno = $decToHebrew->convert($ppgno, $reverse); 11664 11665 } elseif (preg_match('/(arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao|myanmar)/i', $lowertype, $m)) { 11666 11667 $cp = $decToOther->getCodePage($m[1]); 11668 $ppgno = $decToOther->convert($ppgno, $cp, $checkfont); 11669 11670 } elseif ($lowertype == 'cjk-decimal') { 11671 11672 $ppgno = $decToCjk->convert($ppgno); 11673 11674 } 11675 11676 return $ppgno; 11677 } 11678 11679 function docPageSettings($num = 0) 11680 { 11681 // Returns current type (numberstyle), suppression state for this page number; 11682 // reset is only returned if set for this page number 11683 if ($num < 1) { 11684 $num = $this->page; 11685 } 11686 11687 $type = $this->defaultPageNumStyle; // set default Page Number Style 11688 $ppgno = $num; 11689 $suppress = 0; 11690 $offset = 0; 11691 $reset = ''; 11692 11693 foreach ($this->PageNumSubstitutions as $psarr) { 11694 if ($num >= $psarr['from']) { 11695 if ($psarr['reset']) { 11696 if ($psarr['reset'] > 1) { 11697 $offset = $psarr['reset'] - 1; 11698 } 11699 $ppgno = $num - $psarr['from'] + 1 + $offset; 11700 } 11701 if ($psarr['type']) { 11702 $type = $psarr['type']; 11703 } 11704 if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) { 11705 $suppress = 1; 11706 } elseif (strtoupper($psarr['suppress']) == 'OFF') { 11707 $suppress = 0; 11708 } 11709 } 11710 if ($num == $psarr['from']) { 11711 $reset = $psarr['reset']; 11712 } 11713 } 11714 11715 if ($suppress) { 11716 $suppress = 'on'; 11717 } else { 11718 $suppress = 'off'; 11719 } 11720 11721 return [$type, $suppress, $reset]; 11722 } 11723 11724 function RestartDocTemplate() 11725 { 11726 $this->docTemplateStart = $this->page; 11727 } 11728 11729 // Page header 11730 function Header($content = '') 11731 { 11732 11733 $this->cMarginL = 0; 11734 $this->cMarginR = 0; 11735 11736 11737 if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLHeaderE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLHeader) || (!$this->mirrorMargins && $this->HTMLHeader)) { 11738 $this->writeHTMLHeaders(); 11739 return; 11740 } 11741 } 11742 11743 /* -- TABLES -- */ 11744 function TableHeaderFooter($content = '', $tablestartpage = '', $tablestartcolumn = '', $horf = 'H', $level = 0, $firstSpread = true, $finalSpread = true) 11745 { 11746 if (($horf == 'H' || $horf == 'F') && !empty($content)) { // mPDF 5.7.2 11747 $table = &$this->table[1][1]; 11748 11749 // mPDF 5.7.2 11750 if ($horf == 'F') { // Table Footer 11751 $firstrow = count($table['cells']) - $table['footernrows']; 11752 $lastrow = count($table['cells']) - 1; 11753 } else { // Table Header 11754 $firstrow = 0; 11755 $lastrow = $table['headernrows'] - 1; 11756 } 11757 if (empty($content[$firstrow])) { 11758 if ($this->debug) { 11759 throw new \Mpdf\MpdfException("<tfoot> must precede <tbody> in a table"); 11760 } else { 11761 return; 11762 } 11763 } 11764 11765 11766 // Advance down page by half width of top border 11767 if ($horf == 'H') { // Only if header 11768 if ($table['borders_separate']) { 11769 $adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T']; 11770 } else { 11771 $adv = $table['max_cell_border_width']['T'] / 2; 11772 } 11773 if ($adv) { 11774 if ($this->table_rotate) { 11775 $this->y += ($adv); 11776 } else { 11777 $this->DivLn($adv, $this->blklvl, true); 11778 } 11779 } 11780 } 11781 11782 $topy = $content[$firstrow][0]['y'] - $this->y; 11783 11784 for ($i = $firstrow; $i <= $lastrow; $i++) { 11785 $y = $this->y; 11786 11787 /* -- COLUMNS -- */ 11788 // If outside columns, this is done in PaintDivBB 11789 if ($this->ColActive) { 11790 // OUTER FILL BGCOLOR of DIVS 11791 if ($this->blklvl > 0) { 11792 $firstblockfill = $this->GetFirstBlockFill(); 11793 if ($firstblockfill && $this->blklvl >= $firstblockfill) { 11794 $divh = $content[$i][0]['h']; 11795 $bak_x = $this->x; 11796 $this->DivLn($divh, -3, false); 11797 // Reset current block fill 11798 $bcor = $this->blk[$this->blklvl]['bgcolorarray']; 11799 $this->SetFColor($bcor); 11800 $this->x = $bak_x; 11801 } 11802 } 11803 } 11804 /* -- END COLUMNS -- */ 11805 11806 $colctr = 0; 11807 foreach ($content[$i] as $tablehf) { 11808 $colctr++; 11809 $y = Arrays::get($tablehf, 'y', null) - $topy; 11810 $this->y = $y; 11811 // Set some cell values 11812 $x = Arrays::get($tablehf, 'x', null); 11813 if (($this->mirrorMargins) && ($tablestartpage == 'ODD') && (($this->page) % 2 == 0)) { // EVEN 11814 $x = $x + $this->MarginCorrection; 11815 } elseif (($this->mirrorMargins) && ($tablestartpage == 'EVEN') && (($this->page) % 2 == 1)) { // ODD 11816 $x = $x + $this->MarginCorrection; 11817 } 11818 /* -- COLUMNS -- */ 11819 // Added to correct for Columns 11820 if ($this->ColActive) { 11821 if ($this->directionality == 'rtl') { // *OTL* 11822 $x -= ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap); // *OTL* 11823 } // *OTL* 11824 else { // *OTL* 11825 $x += ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap); 11826 } // *OTL* 11827 } 11828 /* -- END COLUMNS -- */ 11829 11830 if ($colctr == 1) { 11831 $x0 = $x; 11832 } 11833 11834 // mPDF ITERATION 11835 if ($this->iterationCounter) { 11836 foreach ($tablehf['textbuffer'] as $k => $t) { 11837 if (!is_array($t[0]) && preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) { 11838 $vname = '__' . $m[1] . '_'; 11839 if (!isset($this->$vname)) { 11840 $this->$vname = 1; 11841 } else { 11842 $this->$vname++; 11843 } 11844 $tablehf['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $tablehf['textbuffer'][$k][0]); 11845 } 11846 } 11847 } 11848 11849 $w = Arrays::get($tablehf, 'w', null); 11850 $h = Arrays::get($tablehf, 'h', null); 11851 $va = Arrays::get($tablehf, 'va', null); 11852 $R = Arrays::get($tablehf, 'R', null); 11853 $direction = Arrays::get($tablehf, 'direction', null); 11854 $mih = Arrays::get($tablehf, 'mih', null); 11855 $border = Arrays::get($tablehf, 'border', null); 11856 $border_details = Arrays::get($tablehf, 'border_details', null); 11857 $padding = Arrays::get($tablehf, 'padding', null); 11858 $this->tabletheadjustfinished = true; 11859 11860 $textbuffer = Arrays::get($tablehf, 'textbuffer', null); 11861 11862 // Align 11863 $align = Arrays::get($tablehf, 'a', null); 11864 $this->cellTextAlign = $align; 11865 11866 $this->cellLineHeight = Arrays::get($tablehf, 'cellLineHeight', null); 11867 $this->cellLineStackingStrategy = Arrays::get($tablehf, 'cellLineStackingStrategy', null); 11868 $this->cellLineStackingShift = Arrays::get($tablehf, 'cellLineStackingShift', null); 11869 11870 $this->x = $x; 11871 11872 if ($this->ColActive) { 11873 if ($table['borders_separate']) { 11874 $tablefill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0; 11875 if ($tablefill) { 11876 $color = $this->colorConverter->convert($tablefill, $this->PDFAXwarnings); 11877 if ($color) { 11878 $xadj = ($table['border_spacing_H'] / 2); 11879 $yadj = ($table['border_spacing_V'] / 2); 11880 $wadj = $table['border_spacing_H']; 11881 $hadj = $table['border_spacing_V']; 11882 if ($i == $firstrow && $horf == 'H') { // Top 11883 $yadj += $table['padding']['T'] + $table['border_details']['T']['w']; 11884 $hadj += $table['padding']['T'] + $table['border_details']['T']['w']; 11885 } 11886 if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1)) || (!isset($tablehf['rowspan']) && ($i + 1) == ($lastrow + 1))) && $horf == 'F') { // Bottom 11887 $hadj += $table['padding']['B'] + $table['border_details']['B']['w']; 11888 } 11889 if ($colctr == 1) { // Left 11890 $xadj += $table['padding']['L'] + $table['border_details']['L']['w']; 11891 $wadj += $table['padding']['L'] + $table['border_details']['L']['w']; 11892 } 11893 if ($colctr == count($content[$i])) { // Right 11894 $wadj += $table['padding']['R'] + $table['border_details']['R']['w']; 11895 } 11896 $this->SetFColor($color); 11897 $this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F'); 11898 } 11899 } 11900 } 11901 } 11902 11903 if ($table['empty_cells'] != 'hide' || !empty($textbuffer) || !$table['borders_separate']) { 11904 $paintcell = true; 11905 } else { 11906 $paintcell = false; 11907 } 11908 11909 // Vertical align 11910 if ($R && intval($R) > 0 && isset($va) && $va != 'B') { 11911 $va = 'B'; 11912 } 11913 11914 if (!isset($va) || empty($va) || $va == 'M') { 11915 $this->y += ($h - $mih) / 2; 11916 } elseif (isset($va) && $va == 'B') { 11917 $this->y += $h - $mih; 11918 } 11919 11920 11921 // TABLE ROW OR CELL FILL BGCOLOR 11922 $fill = 0; 11923 if (isset($tablehf['bgcolor']) && $tablehf['bgcolor'] && $tablehf['bgcolor'] != 'transparent') { 11924 $fill = $tablehf['bgcolor']; 11925 $leveladj = 6; 11926 } elseif (isset($content[$i][0]['trbgcolor']) && $content[$i][0]['trbgcolor'] && $content[$i][0]['trbgcolor'] != 'transparent') { // Row color 11927 $fill = $content[$i][0]['trbgcolor']; 11928 $leveladj = 3; 11929 } 11930 if ($fill && $paintcell) { 11931 $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings); 11932 if ($color) { 11933 if ($table['borders_separate']) { 11934 if ($this->ColActive) { 11935 $this->SetFColor($color); 11936 $this->Rect($x + ($table['border_spacing_H'] / 2), $y + ($table['border_spacing_V'] / 2), $w - $table['border_spacing_H'], $h - $table['border_spacing_V'], 'F'); 11937 } else { 11938 $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => ($x + ($table['border_spacing_H'] / 2)), 'y' => ($y + ($table['border_spacing_V'] / 2)), 'w' => ($w - $table['border_spacing_H']), 'h' => ($h - $table['border_spacing_V']), 'col' => $color]; 11939 } 11940 } else { 11941 if ($this->ColActive) { 11942 $this->SetFColor($color); 11943 $this->Rect($x, $y, $w, $h, 'F'); 11944 } else { 11945 $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color]; 11946 } 11947 } 11948 } 11949 } 11950 11951 11952 /* -- BACKGROUNDS -- */ 11953 if (isset($tablehf['gradient']) && $tablehf['gradient'] && $paintcell) { 11954 $g = $this->gradient->parseBackgroundGradient($tablehf['gradient']); 11955 if ($g) { 11956 if ($table['borders_separate']) { 11957 $px = $x + ($table['border_spacing_H'] / 2); 11958 $py = $y + ($table['border_spacing_V'] / 2); 11959 $pw = $w - $table['border_spacing_H']; 11960 $ph = $h - $table['border_spacing_V']; 11961 } else { 11962 $px = $x; 11963 $py = $y; 11964 $pw = $w; 11965 $ph = $h; 11966 } 11967 if ($this->ColActive) { 11968 $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']); 11969 } else { 11970 $this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 11971 } 11972 } 11973 } 11974 11975 if (isset($tablehf['background-image']) && $paintcell) { 11976 if ($tablehf['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $tablehf['background-image']['gradient'])) { 11977 $g = $this->gradient->parseMozGradient($tablehf['background-image']['gradient']); 11978 if ($g) { 11979 if ($table['borders_separate']) { 11980 $px = $x + ($table['border_spacing_H'] / 2); 11981 $py = $y + ($table['border_spacing_V'] / 2); 11982 $pw = $w - $table['border_spacing_H']; 11983 $ph = $h - $table['border_spacing_V']; 11984 } else { 11985 $px = $x; 11986 $py = $y; 11987 $pw = $w; 11988 $ph = $h; 11989 } 11990 if ($this->ColActive) { 11991 $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']); 11992 } else { 11993 $this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 11994 } 11995 } 11996 } elseif ($tablehf['background-image']['image_id']) { // Background pattern 11997 $n = count($this->patterns) + 1; 11998 if ($table['borders_separate']) { 11999 $px = $x + ($table['border_spacing_H'] / 2); 12000 $py = $y + ($table['border_spacing_V'] / 2); 12001 $pw = $w - $table['border_spacing_H']; 12002 $ph = $h - $table['border_spacing_V']; 12003 } else { 12004 $px = $x; 12005 $py = $y; 12006 $pw = $w; 12007 $ph = $h; 12008 } 12009 if ($this->ColActive) { 12010 list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($tablehf['background-image']['orig_w'], $tablehf['background-image']['orig_h'], $pw, $ph, $tablehf['background-image']['resize'], $tablehf['background-image']['x_repeat'], $tablehf['background-image']['y_repeat']); 12011 $this->patterns[$n] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'pgh' => $this->h, 'image_id' => $tablehf['background-image']['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $tablehf['background-image']['x_pos'], 'y_pos' => $tablehf['background-image']['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $tablehf['background-image']['itype']]; 12012 if ($tablehf['background-image']['opacity'] > 0 && $tablehf['background-image']['opacity'] < 1) { 12013 $opac = $this->SetAlpha($tablehf['background-image']['opacity'], 'Normal', true); 12014 } else { 12015 $opac = ''; 12016 } 12017 $this->writer->write(sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $px * Mpdf::SCALE, ($this->h - $py) * Mpdf::SCALE, $pw * Mpdf::SCALE, -$ph * Mpdf::SCALE)); 12018 } else { 12019 $this->tableBackgrounds[$level * 9 + 8][] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'image_id' => $tablehf['background-image']['image_id'], 'orig_w' => $tablehf['background-image']['orig_w'], 'orig_h' => $tablehf['background-image']['orig_h'], 'x_pos' => $tablehf['background-image']['x_pos'], 'y_pos' => $tablehf['background-image']['y_pos'], 'x_repeat' => $tablehf['background-image']['x_repeat'], 'y_repeat' => $tablehf['background-image']['y_repeat'], 'clippath' => '', 'resize' => $tablehf['background-image']['resize'], 'opacity' => $tablehf['background-image']['opacity'], 'itype' => $tablehf['background-image']['itype']]; 12020 } 12021 } 12022 } 12023 /* -- END BACKGROUNDS -- */ 12024 12025 // Cell Border 12026 if ($table['borders_separate'] && $paintcell && $border) { 12027 $this->_tableRect($x + ($table['border_spacing_H'] / 2) + ($border_details['L']['w'] / 2), $y + ($table['border_spacing_V'] / 2) + ($border_details['T']['w'] / 2), $w - $table['border_spacing_H'] - ($border_details['L']['w'] / 2) - ($border_details['R']['w'] / 2), $h - $table['border_spacing_V'] - ($border_details['T']['w'] / 2) - ($border_details['B']['w'] / 2), $border, $border_details, false, $table['borders_separate']); 12028 } elseif ($paintcell && $border) { 12029 $this->_tableRect($x, $y, $w, $h, $border, $border_details, true, $table['borders_separate']); // true causes buffer 12030 } 12031 12032 // Print cell content 12033 if (!empty($textbuffer)) { 12034 if ($horf == 'F' && preg_match('/{colsum([0-9]*)[_]*}/', $textbuffer[0][0], $m)) { 12035 $rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$colctr - 1]); 12036 $textbuffer[0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $textbuffer[0][0]); 12037 } 12038 12039 if ($R) { 12040 $cellPtSize = $textbuffer[0][11] / $this->shrin_k; 12041 if (!$cellPtSize) { 12042 $cellPtSize = $this->default_font_size; 12043 } 12044 $cellFontHeight = ($cellPtSize / Mpdf::SCALE); 12045 $opx = $this->x; 12046 $opy = $this->y; 12047 $angle = intval($R); 12048 12049 // Only allow 45 - 90 degrees (when bottom-aligned) or -90 12050 if ($angle > 90) { 12051 $angle = 90; 12052 } elseif ($angle > 0 && (isset($va) && $va != 'B')) { 12053 $angle = 90; 12054 } elseif ($angle > 0 && $angle < 45) { 12055 $angle = 45; 12056 } elseif ($angle < 0) { 12057 $angle = -90; 12058 } 12059 12060 $offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight); 12061 if (isset($align) && $align == 'R') { 12062 $this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($padding['R'] + $border_details['R']['w']); 12063 } elseif (!isset($align) || $align == 'C') { 12064 $this->x += ($w / 2) + ($offset); 12065 } else { 12066 $this->x += ($offset) + ($cellFontHeight / 3) + ($padding['L'] + $border_details['L']['w']); 12067 } 12068 $str = ''; 12069 foreach ($tablehf['textbuffer'] as $t) { 12070 $str .= $t[0] . ' '; 12071 } 12072 $str = rtrim($str); 12073 12074 if (!isset($va) || $va == 'M') { 12075 $this->y -= ($h - $mih) / 2; // Undo what was added earlier VERTICAL ALIGN 12076 if ($angle > 0) { 12077 $this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']) + ($mih - ($padding['T'] + $border_details['T']['w'] + $border_details['B']['w'] + $padding['B'])); 12078 } elseif ($angle < 0) { 12079 $this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']); 12080 } 12081 } elseif (isset($va) && $va == 'B') { 12082 $this->y -= $h - $mih; // Undo what was added earlier VERTICAL ALIGN 12083 if ($angle > 0) { 12084 $this->y += $h - ($border_details['B']['w'] + $padding['B']); 12085 } elseif ($angle < 0) { 12086 $this->y += $h - $mih + ($padding['T'] + $border_details['T']['w']); 12087 } 12088 } elseif (isset($va) && $va == 'T') { 12089 if ($angle > 0) { 12090 $this->y += $mih - ($border_details['B']['w'] + $padding['B']); 12091 } elseif ($angle < 0) { 12092 $this->y += ($padding['T'] + $border_details['T']['w']); 12093 } 12094 } 12095 12096 $this->Rotate($angle, $this->x, $this->y); 12097 $s_fs = $this->FontSizePt; 12098 $s_f = $this->FontFamily; 12099 $s_st = $this->FontStyle; 12100 if (!empty($textbuffer[0][3])) { // Font Color 12101 $cor = $textbuffer[0][3]; 12102 $this->SetTColor($cor); 12103 } 12104 $this->SetFont($textbuffer[0][4], $textbuffer[0][2], $cellPtSize, true, true); 12105 12106 $this->magic_reverse_dir($str, $this->directionality, $textbuffer[0][18]); 12107 $this->Text($this->x, $this->y, $str, $textbuffer[0][18], $textbuffer[0][8]); // textvar 12108 $this->Rotate(0); 12109 $this->SetFont($s_f, $s_st, $s_fs, true, true); 12110 $this->SetTColor(0); 12111 $this->x = $opx; 12112 $this->y = $opy; 12113 } else { 12114 if ($table['borders_separate']) { // NB twice border width 12115 $xadj = $border_details['L']['w'] + $padding['L'] + ($table['border_spacing_H'] / 2); 12116 $wadj = $border_details['L']['w'] + $border_details['R']['w'] + $padding['L'] + $padding['R'] + $table['border_spacing_H']; 12117 $yadj = $border_details['T']['w'] + $padding['T'] + ($table['border_spacing_H'] / 2); 12118 } else { 12119 $xadj = $border_details['L']['w'] / 2 + $padding['L']; 12120 $wadj = ($border_details['L']['w'] + $border_details['R']['w']) / 2 + $padding['L'] + $padding['R']; 12121 $yadj = $border_details['T']['w'] / 2 + $padding['T']; 12122 } 12123 12124 $this->divwidth = $w - ($wadj); 12125 $this->x += $xadj; 12126 $this->y += $yadj; 12127 $this->printbuffer($textbuffer, '', true, false, $direction); 12128 } 12129 } 12130 $textbuffer = []; 12131 12132 /* -- BACKGROUNDS -- */ 12133 if (!$this->ColActive) { 12134 if (isset($content[$i][0]['trgradients']) && ($colctr == 1 || $table['borders_separate'])) { 12135 $g = $this->gradient->parseBackgroundGradient($content[$i][0]['trgradients']); 12136 if ($g) { 12137 $gx = $x0; 12138 $gy = $y; 12139 $gh = $h; 12140 $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R']; 12141 if ($table['borders_separate']) { 12142 $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']); 12143 $clx = $x + ($table['border_spacing_H'] / 2); 12144 $cly = $y + ($table['border_spacing_V'] / 2); 12145 $clw = $w - $table['border_spacing_H']; 12146 $clh = $h - $table['border_spacing_V']; 12147 // Set clipping path 12148 $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6 12149 $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s]; 12150 } else { 12151 $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 12152 } 12153 } 12154 } 12155 12156 if (isset($content[$i][0]['trbackground-images']) && ($colctr == 1 || $table['borders_separate'])) { 12157 if ($content[$i][0]['trbackground-images']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $content[$i][0]['trbackground-images']['gradient'])) { 12158 $g = $this->gradient->parseMozGradient($content[$i][0]['trbackground-images']['gradient']); 12159 if ($g) { 12160 $gx = $x0; 12161 $gy = $y; 12162 $gh = $h; 12163 $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R']; 12164 if ($table['borders_separate']) { 12165 $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']); 12166 $clx = $x + ($table['border_spacing_H'] / 2); 12167 $cly = $y + ($table['border_spacing_V'] / 2); 12168 $clw = $w - $table['border_spacing_H']; 12169 $clh = $h - $table['border_spacing_V']; 12170 // Set clipping path 12171 $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6 12172 $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s]; 12173 } else { 12174 $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 12175 } 12176 } 12177 } else { 12178 $image_id = $content[$i][0]['trbackground-images']['image_id']; 12179 $orig_w = $content[$i][0]['trbackground-images']['orig_w']; 12180 $orig_h = $content[$i][0]['trbackground-images']['orig_h']; 12181 $x_pos = $content[$i][0]['trbackground-images']['x_pos']; 12182 $y_pos = $content[$i][0]['trbackground-images']['y_pos']; 12183 $x_repeat = $content[$i][0]['trbackground-images']['x_repeat']; 12184 $y_repeat = $content[$i][0]['trbackground-images']['y_repeat']; 12185 $resize = $content[$i][0]['trbackground-images']['resize']; 12186 $opacity = $content[$i][0]['trbackground-images']['opacity']; 12187 $itype = $content[$i][0]['trbackground-images']['itype']; 12188 12189 $clippath = ''; 12190 $gx = $x0; 12191 $gy = $y; 12192 $gh = $h; 12193 $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R']; 12194 if ($table['borders_separate']) { 12195 $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']); 12196 $clx = $x + ($table['border_spacing_H'] / 2); 12197 $cly = $y + ($table['border_spacing_V'] / 2); 12198 $clw = $w - $table['border_spacing_H']; 12199 $clh = $h - $table['border_spacing_V']; 12200 // Set clipping path 12201 $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6 12202 $this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], '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, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype]; 12203 } else { 12204 $this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, '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, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype]; 12205 } 12206 } 12207 } 12208 } 12209 /* -- END BACKGROUNDS -- */ 12210 12211 // TABLE BORDER - if separate OR collapsed and only table border 12212 if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) { 12213 $halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2); 12214 $halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2); 12215 $halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2); 12216 $halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2); 12217 $tbx = $x; 12218 $tby = $y; 12219 $tbw = $w; 12220 $tbh = $h; 12221 $tab_bord = 0; 12222 $corner = ''; 12223 if ($i == $firstrow && $horf == 'H') { // Top 12224 $tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2); 12225 $tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2); 12226 $this->setBorder($tab_bord, Border::TOP); 12227 $corner .= 'T'; 12228 } 12229 if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1))) && $horf == 'F') { // Bottom 12230 $tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2); 12231 $this->setBorder($tab_bord, Border::BOTTOM); 12232 $corner .= 'B'; 12233 } 12234 if ($colctr == 1 && $firstSpread) { // Left 12235 $tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2); 12236 $tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2); 12237 $this->setBorder($tab_bord, Border::LEFT); 12238 $corner .= 'L'; 12239 } 12240 if ($colctr == count($content[$i]) && $finalSpread) { // Right 12241 $tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2); 12242 $this->setBorder($tab_bord, Border::RIGHT); 12243 $corner .= 'R'; 12244 } 12245 $this->_tableRect($tbx, $tby, $tbw, $tbh, $tab_bord, $table['border_details'], false, $table['borders_separate'], 'table', $corner, $table['border_spacing_V'], $table['border_spacing_H']); 12246 } 12247 }// end column $content 12248 $this->y = $y + $h; // Update y coordinate 12249 }// end row $i 12250 unset($table); 12251 $this->colsums = []; 12252 } 12253 } 12254 12255 /* -- END TABLES -- */ 12256 12257 function SetHTMLHeader($header = '', $OE = '', $write = false) 12258 { 12259 12260 $height = 0; 12261 if (is_array($header) && isset($header['html']) && $header['html']) { 12262 $Hhtml = $header['html']; 12263 if ($this->setAutoTopMargin) { 12264 if (isset($header['h'])) { 12265 $height = $header['h']; 12266 } else { 12267 $height = $this->_getHtmlHeight($Hhtml); 12268 } 12269 } 12270 } elseif (!is_array($header) && $header) { 12271 $Hhtml = $header; 12272 if ($this->setAutoTopMargin) { 12273 $height = $this->_getHtmlHeight($Hhtml); 12274 } 12275 } else { 12276 $Hhtml = ''; 12277 } 12278 12279 if ($OE !== 'E') { 12280 $OE = 'O'; 12281 } 12282 12283 if ($OE === 'E') { 12284 if ($Hhtml) { 12285 $this->HTMLHeaderE = []; 12286 $this->HTMLHeaderE['html'] = $Hhtml; 12287 $this->HTMLHeaderE['h'] = $height; 12288 } else { 12289 $this->HTMLHeaderE = ''; 12290 } 12291 } else { 12292 if ($Hhtml) { 12293 $this->HTMLHeader = []; 12294 $this->HTMLHeader['html'] = $Hhtml; 12295 $this->HTMLHeader['h'] = $height; 12296 } else { 12297 $this->HTMLHeader = ''; 12298 } 12299 } 12300 12301 if (!$this->mirrorMargins && $OE == 'E') { 12302 return; 12303 } 12304 if ($Hhtml == '') { 12305 return; 12306 } 12307 12308 if ($this->setAutoTopMargin == 'pad') { 12309 $this->tMargin = $this->margin_header + $height + $this->orig_tMargin; 12310 if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) { 12311 $this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin; 12312 } 12313 } elseif ($this->setAutoTopMargin == 'stretch') { 12314 $this->tMargin = max($this->orig_tMargin, $this->margin_header + $height + $this->autoMarginPadding); 12315 if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) { 12316 $this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin; 12317 } 12318 } 12319 if ($write && $this->state != 0 && (($this->mirrorMargins && $OE == 'E' && ($this->page) % 2 == 0) || ($this->mirrorMargins && $OE != 'E' && ($this->page) % 2 == 1) || !$this->mirrorMargins)) { 12320 $this->writeHTMLHeaders(); 12321 } 12322 } 12323 12324 function SetHTMLFooter($footer = '', $OE = '') 12325 { 12326 $height = 0; 12327 if (is_array($footer) && isset($footer['html']) && $footer['html']) { 12328 $Fhtml = $footer['html']; 12329 if ($this->setAutoBottomMargin) { 12330 if (isset($footer['h'])) { 12331 $height = $footer['h']; 12332 } else { 12333 $height = $this->_getHtmlHeight($Fhtml); 12334 } 12335 } 12336 } elseif (!is_array($footer) && $footer) { 12337 $Fhtml = $footer; 12338 if ($this->setAutoBottomMargin) { 12339 $height = $this->_getHtmlHeight($Fhtml); 12340 } 12341 } else { 12342 $Fhtml = ''; 12343 } 12344 12345 if ($OE !== 'E') { 12346 $OE = 'O'; 12347 } 12348 12349 if ($OE === 'E') { 12350 if ($Fhtml) { 12351 $this->HTMLFooterE = []; 12352 $this->HTMLFooterE['html'] = $Fhtml; 12353 $this->HTMLFooterE['h'] = $height; 12354 } else { 12355 $this->HTMLFooterE = ''; 12356 } 12357 } else { 12358 if ($Fhtml) { 12359 $this->HTMLFooter = []; 12360 $this->HTMLFooter['html'] = $Fhtml; 12361 $this->HTMLFooter['h'] = $height; 12362 } else { 12363 $this->HTMLFooter = ''; 12364 } 12365 } 12366 12367 if (!$this->mirrorMargins && $OE == 'E') { 12368 return; 12369 } 12370 12371 if ($Fhtml == '') { 12372 return false; 12373 } 12374 12375 if ($this->setAutoBottomMargin == 'pad') { 12376 $this->bMargin = $this->margin_footer + $height + $this->orig_bMargin; 12377 $this->PageBreakTrigger = $this->h - $this->bMargin; 12378 if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) { 12379 $this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin; 12380 } 12381 } elseif ($this->setAutoBottomMargin == 'stretch') { 12382 $this->bMargin = max($this->orig_bMargin, $this->margin_footer + $height + $this->autoMarginPadding); 12383 $this->PageBreakTrigger = $this->h - $this->bMargin; 12384 if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) { 12385 $this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin; 12386 } 12387 } 12388 } 12389 12390 function _getHtmlHeight($html) 12391 { 12392 $save_state = $this->state; 12393 if ($this->state == 0) { 12394 $this->AddPage($this->CurOrientation); 12395 } 12396 $this->state = 2; 12397 $this->Reset(); 12398 $this->pageoutput[$this->page] = []; 12399 $save_x = $this->x; 12400 $save_y = $this->y; 12401 $this->x = $this->lMargin; 12402 $this->y = $this->margin_header; 12403 12404 // Replace of page number aliases and date format 12405 $pnstr = $this->pagenumPrefix . $this->docPageNum($this->page) . $this->pagenumSuffix; 12406 $pntstr = $this->nbpgPrefix . $this->docPageNumTotal($this->page) . $this->nbpgSuffix; 12407 $nb = $this->page; 12408 $html = $this->aliasReplace($html, $pnstr, $pntstr, $nb); 12409 12410 $this->HTMLheaderPageLinks = []; 12411 $this->HTMLheaderPageAnnots = []; 12412 $this->HTMLheaderPageForms = []; 12413 $savepb = $this->pageBackgrounds; 12414 $this->writingHTMLheader = true; 12415 $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER); 12416 $this->writingHTMLheader = false; 12417 $h = ($this->y - $this->margin_header); 12418 $this->Reset(); 12419 12420 // mPDF 5.7.2 - Clear in case Float used in Header/Footer 12421 $this->blk[0]['blockContext'] = 0; 12422 $this->blk[0]['float_endpos'] = 0; 12423 12424 $this->pageoutput[$this->page] = []; 12425 $this->headerbuffer = ''; 12426 $this->pageBackgrounds = $savepb; 12427 $this->x = $save_x; 12428 $this->y = $save_y; 12429 $this->state = $save_state; 12430 12431 if ($save_state == 0) { 12432 unset($this->pages[1]); 12433 $this->page = 0; 12434 } 12435 return $h; 12436 } 12437 12438 // Called internally from Header 12439 function writeHTMLHeaders() 12440 { 12441 12442 if ($this->mirrorMargins && ($this->page) % 2 == 0) { 12443 $OE = 'E'; 12444 } else { 12445 $OE = 'O'; 12446 } 12447 12448 if ($OE === 'E') { 12449 $this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeaderE['html']; 12450 } else { 12451 $this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeader['html']; 12452 } 12453 12454 if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) { 12455 $this->saveHTMLHeader[$this->page][$OE]['rotate'] = true; 12456 $this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->tMargin; 12457 $this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->bMargin; 12458 $this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header; 12459 $this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer; 12460 $this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->h; 12461 $this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->w; 12462 } else { 12463 $this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->lMargin; 12464 $this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->rMargin; 12465 $this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header; 12466 $this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer; 12467 $this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->w; 12468 $this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->h; 12469 } 12470 } 12471 12472 function writeHTMLFooters() 12473 { 12474 12475 if ($this->mirrorMargins && ($this->page) % 2 == 0) { 12476 $OE = 'E'; 12477 } else { 12478 $OE = 'O'; 12479 } 12480 12481 if ($OE === 'E') { 12482 $this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooterE['html']; 12483 } else { 12484 $this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooter['html']; 12485 } 12486 12487 if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) { 12488 $this->saveHTMLFooter[$this->page][$OE]['rotate'] = true; 12489 $this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->tMargin; 12490 $this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->bMargin; 12491 $this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->rMargin; 12492 $this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->lMargin; 12493 $this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header; 12494 $this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer; 12495 $this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->h; 12496 $this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->w; 12497 } else { 12498 $this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->lMargin; 12499 $this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->rMargin; 12500 $this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->tMargin; 12501 $this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->bMargin; 12502 $this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header; 12503 $this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer; 12504 $this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->w; 12505 $this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->h; 12506 } 12507 } 12508 12509 // mPDF 6 12510 function _shareHeaderFooterWidth($cl, $cc, $cr) 12511 { 12512 // mPDF 6 12513 $l = mb_strlen($cl, 'UTF-8'); 12514 $c = mb_strlen($cc, 'UTF-8'); 12515 $r = mb_strlen($cr, 'UTF-8'); 12516 $s = max($l, $r); 12517 $tw = $c + 2 * $s; 12518 if ($tw > 0) { 12519 return [intval($s * 100 / $tw), intval($c * 100 / $tw), intval($s * 100 / $tw)]; 12520 } else { 12521 return [33, 33, 33]; 12522 } 12523 } 12524 12525 // mPDF 6 12526 // Create an HTML header/footer from array (non-HTML header/footer) 12527 function _createHTMLheaderFooter($arr, $hf) 12528 { 12529 $lContent = (isset($arr['L']['content']) ? $arr['L']['content'] : ''); 12530 $cContent = (isset($arr['C']['content']) ? $arr['C']['content'] : ''); 12531 $rContent = (isset($arr['R']['content']) ? $arr['R']['content'] : ''); 12532 12533 list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($lContent, $cContent, $rContent); 12534 12535 if ($hf == 'H') { 12536 $valign = 'bottom'; 12537 $vpadding = '0 0 ' . $this->header_line_spacing . 'em 0'; 12538 } else { 12539 $valign = 'top'; 12540 $vpadding = '' . $this->footer_line_spacing . 'em 0 0 0'; 12541 } 12542 12543 if ($this->directionality == 'rtl') { // table columns get reversed so need different text-alignment 12544 $talignL = 'right'; 12545 $talignR = 'left'; 12546 } else { 12547 $talignL = 'left'; 12548 $talignR = 'right'; 12549 } 12550 12551 $html = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: ' . $valign . '; color: #000000; '; 12552 12553 if (isset($arr['line']) && $arr['line']) { 12554 $html .= ' border-' . $valign . ': 0.1mm solid #000000;'; 12555 } 12556 12557 $html .= '">'; 12558 $html .= '<tr>'; 12559 $html .= '<td width="' . $lw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignL . '; '; 12560 12561 if (isset($arr['L']['font-family'])) { 12562 $html .= ' font-family: ' . $arr['L']['font-family'] . ';'; 12563 } 12564 12565 if (isset($arr['L']['color'])) { 12566 $html .= ' color: ' . $arr['L']['color'] . ';'; 12567 } 12568 12569 if (isset($arr['L']['font-size'])) { 12570 $html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;'; 12571 } 12572 12573 if (isset($arr['L']['font-style'])) { 12574 if ($arr['L']['font-style'] == 'B' || $arr['L']['font-style'] == 'BI') { 12575 $html .= ' font-weight: bold;'; 12576 } 12577 if ($arr['L']['font-style'] == 'I' || $arr['L']['font-style'] == 'BI') { 12578 $html .= ' font-style: italic;'; 12579 } 12580 } 12581 12582 $html .= '">' . $lContent . '</td>'; 12583 $html .= '<td width="' . $cw . '%" style="padding: ' . $vpadding . '; text-align: center; '; 12584 12585 if (isset($arr['C']['font-family'])) { 12586 $html .= ' font-family: ' . $arr['C']['font-family'] . ';'; 12587 } 12588 12589 if (isset($arr['C']['color'])) { 12590 $html .= ' color: ' . $arr['C']['color'] . ';'; 12591 } 12592 12593 if (isset($arr['C']['font-size'])) { 12594 $html .= ' font-size: ' . $arr['C']['font-size'] . 'pt;'; 12595 } 12596 12597 if (isset($arr['C']['font-style'])) { 12598 if ($arr['C']['font-style'] == 'B' || $arr['C']['font-style'] == 'BI') { 12599 $html .= ' font-weight: bold;'; 12600 } 12601 if ($arr['C']['font-style'] == 'I' || $arr['C']['font-style'] == 'BI') { 12602 $html .= ' font-style: italic;'; 12603 } 12604 } 12605 12606 $html .= '">' . $cContent . '</td>'; 12607 $html .= '<td width="' . $rw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignR . '; '; 12608 12609 if (isset($arr['R']['font-family'])) { 12610 $html .= ' font-family: ' . $arr['R']['font-family'] . ';'; 12611 } 12612 12613 if (isset($arr['R']['color'])) { 12614 $html .= ' color: ' . $arr['R']['color'] . ';'; 12615 } 12616 12617 if (isset($arr['R']['font-size'])) { 12618 $html .= ' font-size: ' . $arr['R']['font-size'] . 'pt;'; 12619 } 12620 12621 if (isset($arr['R']['font-style'])) { 12622 if ($arr['R']['font-style'] == 'B' || $arr['R']['font-style'] == 'BI') { 12623 $html .= ' font-weight: bold;'; 12624 } 12625 if ($arr['R']['font-style'] == 'I' || $arr['R']['font-style'] == 'BI') { 12626 $html .= ' font-style: italic;'; 12627 } 12628 } 12629 12630 $html .= '">' . $rContent . '</td>'; 12631 $html .= '</tr></table>'; 12632 12633 return $html; 12634 } 12635 12636 function DefHeaderByName($name, $arr) 12637 { 12638 if (!$name) { 12639 $name = '_nonhtmldefault'; 12640 } 12641 $html = $this->_createHTMLheaderFooter($arr, 'H'); 12642 12643 $this->pageHTMLheaders[$name]['html'] = $html; 12644 $this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html); 12645 } 12646 12647 function DefFooterByName($name, $arr) 12648 { 12649 if (!$name) { 12650 $name = '_nonhtmldefault'; 12651 } 12652 $html = $this->_createHTMLheaderFooter($arr, 'F'); 12653 12654 $this->pageHTMLfooters[$name]['html'] = $html; 12655 $this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html); 12656 } 12657 12658 function SetHeaderByName($name, $side = 'O', $write = false) 12659 { 12660 if (!$name) { 12661 $name = '_nonhtmldefault'; 12662 } 12663 $this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write); 12664 } 12665 12666 function SetFooterByName($name, $side = 'O') 12667 { 12668 if (!$name) { 12669 $name = '_nonhtmldefault'; 12670 } 12671 $this->SetHTMLFooter($this->pageHTMLfooters[$name], $side); 12672 } 12673 12674 function DefHTMLHeaderByName($name, $html) 12675 { 12676 if (!$name) { 12677 $name = '_default'; 12678 } 12679 12680 $this->pageHTMLheaders[$name]['html'] = $html; 12681 $this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html); 12682 } 12683 12684 function DefHTMLFooterByName($name, $html) 12685 { 12686 if (!$name) { 12687 $name = '_default'; 12688 } 12689 12690 $this->pageHTMLfooters[$name]['html'] = $html; 12691 $this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html); 12692 } 12693 12694 function SetHTMLHeaderByName($name, $side = 'O', $write = false) 12695 { 12696 if (!$name) { 12697 $name = '_default'; 12698 } 12699 $this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write); 12700 } 12701 12702 function SetHTMLFooterByName($name, $side = 'O') 12703 { 12704 if (!$name) { 12705 $name = '_default'; 12706 } 12707 $this->SetHTMLFooter($this->pageHTMLfooters[$name], $side); 12708 } 12709 12710 function SetHeader($Harray = [], $side = '', $write = false) 12711 { 12712 $oddhtml = ''; 12713 $evenhtml = ''; 12714 12715 if (is_string($Harray)) { 12716 12717 if (strlen($Harray) === 0) { 12718 12719 $oddhtml = ''; 12720 $evenhtml = ''; 12721 12722 } elseif (strpos($Harray, '|') !== false) { 12723 12724 $hdet = explode('|', $Harray); 12725 12726 list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($hdet[0], $hdet[1], $hdet[2]); 12727 $oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; '; 12728 12729 if ($this->defaultheaderfontsize) { 12730 $oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;'; 12731 } 12732 12733 if ($this->defaultheaderfontstyle) { 12734 12735 if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') { 12736 $oddhtml .= ' font-weight: bold;'; 12737 } 12738 12739 if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') { 12740 $oddhtml .= ' font-style: italic;'; 12741 } 12742 } 12743 12744 if ($this->defaultheaderline) { 12745 $oddhtml .= ' border-bottom: 0.1mm solid #000000;'; 12746 } 12747 12748 $oddhtml .= '">'; 12749 $oddhtml .= '<tr>'; 12750 $oddhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[0] . '</td>'; 12751 $oddhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>'; 12752 $oddhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[2] . '</td>'; 12753 $oddhtml .= '</tr></table>'; 12754 12755 $evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; '; 12756 12757 if ($this->defaultheaderfontsize) { 12758 $evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;'; 12759 } 12760 12761 if ($this->defaultheaderfontstyle) { 12762 if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') { 12763 $evenhtml .= ' font-weight: bold;'; 12764 } 12765 if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') { 12766 $evenhtml .= ' font-style: italic;'; 12767 } 12768 } 12769 12770 if ($this->defaultheaderline) { 12771 $evenhtml .= ' border-bottom: 0.1mm solid #000000;'; 12772 } 12773 12774 $evenhtml .= '">'; 12775 $evenhtml .= '<tr>'; 12776 $evenhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[2] . '</td>'; 12777 $evenhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>'; 12778 $evenhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[0] . '</td>'; 12779 $evenhtml .= '</tr></table>'; 12780 12781 } else { 12782 12783 $oddhtml = '<div style="margin: 0; color: #000000; '; 12784 12785 if ($this->defaultheaderfontsize) { 12786 $oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;'; 12787 } 12788 12789 if ($this->defaultheaderfontstyle) { 12790 12791 if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') { 12792 $oddhtml .= ' font-weight: bold;'; 12793 } 12794 12795 if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') { 12796 $oddhtml .= ' font-style: italic;'; 12797 } 12798 } 12799 12800 if ($this->defaultheaderline) { 12801 $oddhtml .= ' border-bottom: 0.1mm solid #000000;'; 12802 } 12803 12804 $oddhtml .= 'text-align: right; ">' . $Harray . '</div>'; 12805 $evenhtml = '<div style="margin: 0; color: #000000; '; 12806 12807 if ($this->defaultheaderfontsize) { 12808 $evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;'; 12809 } 12810 12811 if ($this->defaultheaderfontstyle) { 12812 12813 if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') { 12814 $evenhtml .= ' font-weight: bold;'; 12815 } 12816 12817 if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') { 12818 $evenhtml .= ' font-style: italic;'; 12819 } 12820 } 12821 12822 if ($this->defaultheaderline) { 12823 $evenhtml .= ' border-bottom: 0.1mm solid #000000;'; 12824 } 12825 12826 $evenhtml .= 'text-align: left; ">' . $Harray . '</div>'; 12827 } 12828 12829 } elseif (is_array($Harray) && !empty($Harray)) { 12830 12831 $odd = null; 12832 $even = null; 12833 12834 if ($side === 'O') { 12835 $odd = $Harray; 12836 } elseif ($side === 'E') { 12837 $even = $Harray; 12838 } else { 12839 $odd = Arrays::get($Harray, 'odd', null); 12840 $even = Arrays::get($Harray, 'even', null); 12841 } 12842 12843 $oddhtml = $this->_createHTMLheaderFooter($odd, 'H'); 12844 $evenhtml = $this->_createHTMLheaderFooter($even, 'H'); 12845 } 12846 12847 if ($side === 'E') { 12848 $this->SetHTMLHeader($evenhtml, 'E', $write); 12849 } elseif ($side === 'O') { 12850 $this->SetHTMLHeader($oddhtml, 'O', $write); 12851 } else { 12852 $this->SetHTMLHeader($oddhtml, 'O', $write); 12853 $this->SetHTMLHeader($evenhtml, 'E', $write); 12854 } 12855 } 12856 12857 function SetFooter($Farray = [], $side = '') 12858 { 12859 $oddhtml = ''; 12860 $evenhtml = ''; 12861 12862 if (is_string($Farray)) { 12863 12864 if (strlen($Farray) == 0) { 12865 12866 $oddhtml = ''; 12867 $evenhtml = ''; 12868 12869 } elseif (strpos($Farray, '|') !== false) { 12870 12871 $hdet = explode('|', $Farray); 12872 $oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; '; 12873 12874 if ($this->defaultfooterfontsize) { 12875 $oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;'; 12876 } 12877 12878 if ($this->defaultfooterfontstyle) { 12879 if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') { 12880 $oddhtml .= ' font-weight: bold;'; 12881 } 12882 if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') { 12883 $oddhtml .= ' font-style: italic;'; 12884 } 12885 } 12886 12887 if ($this->defaultfooterline) { 12888 $oddhtml .= ' border-top: 0.1mm solid #000000;'; 12889 } 12890 12891 $oddhtml .= '">'; 12892 $oddhtml .= '<tr>'; 12893 $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[0] . '</td>'; 12894 $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>'; 12895 $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[2] . '</td>'; 12896 $oddhtml .= '</tr></table>'; 12897 12898 $evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; '; 12899 12900 if ($this->defaultfooterfontsize) { 12901 $evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;'; 12902 } 12903 12904 if ($this->defaultfooterfontstyle) { 12905 12906 if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') { 12907 $evenhtml .= ' font-weight: bold;'; 12908 } 12909 12910 if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') { 12911 $evenhtml .= ' font-style: italic;'; 12912 } 12913 } 12914 12915 if ($this->defaultfooterline) { 12916 $evenhtml .= ' border-top: 0.1mm solid #000000;'; 12917 } 12918 12919 $evenhtml .= '">'; 12920 $evenhtml .= '<tr>'; 12921 $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[2] . '</td>'; 12922 $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>'; 12923 $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[0] . '</td>'; 12924 $evenhtml .= '</tr></table>'; 12925 12926 } else { 12927 12928 $oddhtml = '<div style="margin: 0; color: #000000; '; 12929 12930 if ($this->defaultfooterfontsize) { 12931 $oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;'; 12932 } 12933 12934 if ($this->defaultfooterfontstyle) { 12935 12936 if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') { 12937 $oddhtml .= ' font-weight: bold;'; 12938 } 12939 12940 if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') { 12941 $oddhtml .= ' font-style: italic;'; 12942 } 12943 } 12944 12945 if ($this->defaultfooterline) { 12946 $oddhtml .= ' border-top: 0.1mm solid #000000;'; 12947 } 12948 12949 $oddhtml .= 'text-align: right; ">' . $Farray . '</div>'; 12950 12951 $evenhtml = '<div style="margin: 0; color: #000000; '; 12952 12953 if ($this->defaultfooterfontsize) { 12954 $evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;'; 12955 } 12956 12957 if ($this->defaultfooterfontstyle) { 12958 12959 if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') { 12960 $evenhtml .= ' font-weight: bold;'; 12961 } 12962 12963 if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') { 12964 $evenhtml .= ' font-style: italic;'; 12965 } 12966 } 12967 12968 if ($this->defaultfooterline) { 12969 $evenhtml .= ' border-top: 0.1mm solid #000000;'; 12970 } 12971 12972 $evenhtml .= 'text-align: left; ">' . $Farray . '</div>'; 12973 } 12974 12975 } elseif (is_array($Farray)) { 12976 12977 $odd = null; 12978 $even = null; 12979 12980 if ($side === 'O') { 12981 $odd = $Farray; 12982 } elseif ($side == 'E') { 12983 $even = $Farray; 12984 } else { 12985 $odd = Arrays::get($Farray, 'odd', null); 12986 $even = Arrays::get($Farray, 'even', null); 12987 } 12988 12989 $oddhtml = $this->_createHTMLheaderFooter($odd, 'F'); 12990 $evenhtml = $this->_createHTMLheaderFooter($even, 'F'); 12991 } 12992 12993 if ($side === 'E') { 12994 $this->SetHTMLFooter($evenhtml, 'E'); 12995 } elseif ($side === 'O') { 12996 $this->SetHTMLFooter($oddhtml, 'O'); 12997 } else { 12998 $this->SetHTMLFooter($oddhtml, 'O'); 12999 $this->SetHTMLFooter($evenhtml, 'E'); 13000 } 13001 } 13002 13003 /* -- WATERMARK -- */ 13004 13005 function SetWatermarkText($txt = '', $alpha = -1) 13006 { 13007 if ($alpha >= 0) { 13008 $this->watermarkTextAlpha = $alpha; 13009 } 13010 $this->watermarkText = $txt; 13011 } 13012 13013 function SetWatermarkImage($src, $alpha = -1, $size = 'D', $pos = 'F') 13014 { 13015 if ($alpha >= 0) { 13016 $this->watermarkImageAlpha = $alpha; 13017 } 13018 $this->watermarkImage = $src; 13019 $this->watermark_size = $size; 13020 $this->watermark_pos = $pos; 13021 } 13022 13023 /* -- END WATERMARK -- */ 13024 13025 // Page footer 13026 function Footer() 13027 { 13028 /* -- CSS-PAGE -- */ 13029 // PAGED MEDIA - CROP / CROSS MARKS from @PAGE 13030 if ($this->show_marks == 'CROP' || $this->show_marks == 'CROPCROSS') { 13031 // Show TICK MARKS 13032 $this->SetLineWidth(0.1); // = 0.1 mm 13033 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 13034 $l = $this->cropMarkLength; 13035 $m = $this->cropMarkMargin; // Distance of crop mark from margin 13036 $b = $this->nonPrintMargin; // Non-printable border at edge of paper sheet 13037 $ax1 = $b; 13038 $bx = $this->page_box['outer_width_LR'] - $m; 13039 $ax = max($ax1, $bx - $l); 13040 $cx1 = $this->w - $b; 13041 $dx = $this->w - $this->page_box['outer_width_LR'] + $m; 13042 $cx = min($cx1, $dx + $l); 13043 $ay1 = $b; 13044 $by = $this->page_box['outer_width_TB'] - $m; 13045 $ay = max($ay1, $by - $l); 13046 $cy1 = $this->h - $b; 13047 $dy = $this->h - $this->page_box['outer_width_TB'] + $m; 13048 $cy = min($cy1, $dy + $l); 13049 13050 $this->Line($ax, $this->page_box['outer_width_TB'], $bx, $this->page_box['outer_width_TB']); 13051 $this->Line($cx, $this->page_box['outer_width_TB'], $dx, $this->page_box['outer_width_TB']); 13052 $this->Line($ax, $this->h - $this->page_box['outer_width_TB'], $bx, $this->h - $this->page_box['outer_width_TB']); 13053 $this->Line($cx, $this->h - $this->page_box['outer_width_TB'], $dx, $this->h - $this->page_box['outer_width_TB']); 13054 $this->Line($this->page_box['outer_width_LR'], $ay, $this->page_box['outer_width_LR'], $by); 13055 $this->Line($this->page_box['outer_width_LR'], $cy, $this->page_box['outer_width_LR'], $dy); 13056 $this->Line($this->w - $this->page_box['outer_width_LR'], $ay, $this->w - $this->page_box['outer_width_LR'], $by); 13057 $this->Line($this->w - $this->page_box['outer_width_LR'], $cy, $this->w - $this->page_box['outer_width_LR'], $dy); 13058 13059 if ($this->printers_info) { 13060 $hd = date('Y-m-d H:i') . ' Page ' . $this->page . ' of {nb}'; 13061 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 13062 $this->SetFont('arial', '', 7.5, true, true); 13063 $this->x = $this->page_box['outer_width_LR'] + 1.5; 13064 $this->y = 1; 13065 $this->Cell(0, $this->FontSize, $hd, 0, 0, 'L', 0, '', 0, 0, 0, 'M'); 13066 $this->SetFont($this->default_font, '', $this->original_default_font_size); 13067 } 13068 } 13069 if ($this->show_marks == 'CROSS' || $this->show_marks == 'CROPCROSS') { 13070 $this->SetLineWidth(0.1); // = 0.1 mm 13071 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 13072 $l = 14 / 2; // longer length of the cross line (half) 13073 $w = 6 / 2; // shorter width of the cross line (half) 13074 $r = 1.2; // radius of circle 13075 $m = $this->crossMarkMargin; // Distance of cross mark from margin 13076 $x1 = $this->page_box['outer_width_LR'] - $m; 13077 $x2 = $this->w - $this->page_box['outer_width_LR'] + $m; 13078 $y1 = $this->page_box['outer_width_TB'] - $m; 13079 $y2 = $this->h - $this->page_box['outer_width_TB'] + $m; 13080 // Left 13081 $this->Circle($x1, $this->h / 2, $r, 'S'); 13082 $this->Line($x1 - $w, $this->h / 2, $x1 + $w, $this->h / 2); 13083 $this->Line($x1, $this->h / 2 - $l, $x1, $this->h / 2 + $l); 13084 // Right 13085 $this->Circle($x2, $this->h / 2, $r, 'S'); 13086 $this->Line($x2 - $w, $this->h / 2, $x2 + $w, $this->h / 2); 13087 $this->Line($x2, $this->h / 2 - $l, $x2, $this->h / 2 + $l); 13088 // Top 13089 $this->Circle($this->w / 2, $y1, $r, 'S'); 13090 $this->Line($this->w / 2, $y1 - $w, $this->w / 2, $y1 + $w); 13091 $this->Line($this->w / 2 - $l, $y1, $this->w / 2 + $l, $y1); 13092 // Bottom 13093 $this->Circle($this->w / 2, $y2, $r, 'S'); 13094 $this->Line($this->w / 2, $y2 - $w, $this->w / 2, $y2 + $w); 13095 $this->Line($this->w / 2 - $l, $y2, $this->w / 2 + $l, $y2); 13096 } 13097 13098 /* -- END CSS-PAGE -- */ 13099 13100 // mPDF 6 13101 // If @page set non-HTML headers/footers named, they were not read until later in the HTML code - so now set them 13102 if ($this->page == 1) { 13103 if ($this->firstPageBoxHeader) { 13104 if (isset($this->pageHTMLheaders[$this->firstPageBoxHeader])) { 13105 $this->HTMLHeader = $this->pageHTMLheaders[$this->firstPageBoxHeader]; 13106 } 13107 $this->Header(); 13108 } 13109 if ($this->firstPageBoxFooter) { 13110 if (isset($this->pageHTMLfooters[$this->firstPageBoxFooter])) { 13111 $this->HTMLFooter = $this->pageHTMLfooters[$this->firstPageBoxFooter]; 13112 } 13113 } 13114 $this->firstPageBoxHeader = ''; 13115 $this->firstPageBoxFooter = ''; 13116 } 13117 13118 13119 if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLFooterE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLFooter) || (!$this->mirrorMargins && $this->HTMLFooter)) { 13120 $this->writeHTMLFooters(); 13121 } 13122 13123 /* -- WATERMARK -- */ 13124 if (($this->watermarkText) && ($this->showWatermarkText)) { 13125 $this->watermark($this->watermarkText, $this->watermarkAngle, 120, $this->watermarkTextAlpha); // Watermark text 13126 } 13127 if (($this->watermarkImage) && ($this->showWatermarkImage)) { 13128 $this->watermarkImg($this->watermarkImage, $this->watermarkImageAlpha); // Watermark image 13129 } 13130 /* -- END WATERMARK -- */ 13131 } 13132 13133 /* -- HTML-CSS -- */ 13134 13135 /** 13136 * Write HTML code to the document 13137 * 13138 * Also used internally to parse HTML into buffers 13139 * 13140 * @param string $html 13141 * @param int $mode Use HTMLParserMode constants. Controls what parts of the $html code is parsed. 13142 * @param bool $init Clears and sets buffers to Top level block etc. 13143 * @param bool $close If false leaves buffers etc. in current state, so that it can continue a block etc. 13144 */ 13145 function WriteHTML($html, $mode = HTMLParserMode::DEFAULT_MODE, $init = true, $close = true) 13146 { 13147 /* Check $html is an integer, float, string, boolean or class with __toString(), otherwise throw exception */ 13148 if (is_scalar($html) === false) { 13149 if (!is_object($html) || ! method_exists($html, '__toString')) { 13150 throw new \Mpdf\MpdfException('WriteHTML() requires $html be an integer, float, string, boolean or an object with the __toString() magic method.'); 13151 } 13152 } 13153 13154 // Check the mode is valid 13155 if (in_array($mode, HTMLParserMode::getAllModes(), true) === false) { 13156 throw new \Mpdf\MpdfException('WriteHTML() requires $mode to be one of the modes defined in HTMLParserMode'); 13157 } 13158 13159 /* Cast $html as a string */ 13160 $html = (string) $html; 13161 13162 // @log Parsing CSS & Headers 13163 13164 if ($init) { 13165 $this->headerbuffer = ''; 13166 $this->textbuffer = []; 13167 $this->fixedPosBlockSave = []; 13168 } 13169 if ($mode === HTMLParserMode::HEADER_CSS) { 13170 $html = '<style> ' . $html . ' </style>'; 13171 } // stylesheet only 13172 13173 if ($this->allow_charset_conversion) { 13174 if ($mode === HTMLParserMode::DEFAULT_MODE) { 13175 $this->ReadCharset($html); 13176 } 13177 if ($this->charset_in && $mode !== HTMLParserMode::HTML_HEADER_BUFFER) { 13178 $success = iconv($this->charset_in, 'UTF-8//TRANSLIT', $html); 13179 if ($success) { 13180 $html = $success; 13181 } 13182 } 13183 } 13184 13185 $html = $this->purify_utf8($html, false); 13186 if ($init) { 13187 $this->blklvl = 0; 13188 $this->lastblocklevelchange = 0; 13189 $this->blk = []; 13190 $this->initialiseBlock($this->blk[0]); 13191 $this->blk[0]['width'] = & $this->pgwidth; 13192 $this->blk[0]['inner_width'] = & $this->pgwidth; 13193 $this->blk[0]['blockContext'] = $this->blockContext; 13194 } 13195 13196 $zproperties = []; 13197 if ($mode === HTMLParserMode::DEFAULT_MODE || $mode === HTMLParserMode::HEADER_CSS) { 13198 $this->ReadMetaTags($html); 13199 13200 if (preg_match('/<base[^>]*href=["\']([^"\'>]*)["\']/i', $html, $m)) { 13201 $this->SetBasePath($m[1]); 13202 } 13203 $html = $this->cssManager->ReadCSS($html); 13204 13205 if ($this->autoLangToFont && !$this->usingCoreFont && preg_match('/<html [^>]*lang=[\'\"](.*?)[\'\"]/ism', $html, $m)) { 13206 $html_lang = $m[1]; 13207 } 13208 13209 if (preg_match('/<html [^>]*dir=[\'\"]\s*rtl\s*[\'\"]/ism', $html)) { 13210 $zproperties['DIRECTION'] = 'rtl'; 13211 } 13212 13213 // allow in-line CSS for body tag to be parsed // Get <body> tag inline CSS 13214 if (preg_match('/<body([^>]*)>(.*?)<\/body>/ism', $html, $m) || preg_match('/<body([^>]*)>(.*)$/ism', $html, $m)) { 13215 $html = $m[2]; 13216 // Changed to allow style="background: url('bg.jpg')" 13217 if (preg_match('/style=[\"](.*?)[\"]/ism', $m[1], $mm) || preg_match('/style=[\'](.*?)[\']/ism', $m[1], $mm)) { 13218 $zproperties = $this->cssManager->readInlineCSS($mm[1]); 13219 } 13220 if (preg_match('/dir=[\'\"]\s*rtl\s*[\'\"]/ism', $m[1])) { 13221 $zproperties['DIRECTION'] = 'rtl'; 13222 } 13223 if (isset($html_lang) && $html_lang) { 13224 $zproperties['LANG'] = $html_lang; 13225 } 13226 if ($this->autoLangToFont && !$this->onlyCoreFonts && preg_match('/lang=[\'\"](.*?)[\'\"]/ism', $m[1], $mm)) { 13227 $zproperties['LANG'] = $mm[1]; 13228 } 13229 } 13230 } 13231 $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', ''); 13232 if ($zproperties) { 13233 $properties = $this->cssManager->array_merge_recursive_unique($properties, $zproperties); 13234 } 13235 13236 if (isset($properties['DIRECTION']) && $properties['DIRECTION']) { 13237 $this->cssManager->CSS['BODY']['DIRECTION'] = $properties['DIRECTION']; 13238 } 13239 if (!isset($this->cssManager->CSS['BODY']['DIRECTION'])) { 13240 $this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality; 13241 } else { 13242 $this->SetDirectionality($this->cssManager->CSS['BODY']['DIRECTION']); 13243 } 13244 13245 $this->setCSS($properties, '', 'BODY'); 13246 13247 $this->blk[0]['InlineProperties'] = $this->saveInlineProperties(); 13248 13249 if ($mode === HTMLParserMode::HEADER_CSS) { 13250 return ''; 13251 } 13252 if (!isset($this->cssManager->CSS['BODY'])) { 13253 $this->cssManager->CSS['BODY'] = []; 13254 } 13255 13256 /* -- BACKGROUNDS -- */ 13257 if (isset($properties['BACKGROUND-GRADIENT'])) { 13258 $this->bodyBackgroundGradient = $properties['BACKGROUND-GRADIENT']; 13259 } 13260 13261 if (isset($properties['BACKGROUND-IMAGE']) && $properties['BACKGROUND-IMAGE']) { 13262 $ret = $this->SetBackground($properties, $this->pgwidth); 13263 if ($ret) { 13264 $this->bodyBackgroundImage = $ret; 13265 } 13266 } 13267 /* -- END BACKGROUNDS -- */ 13268 13269 /* -- CSS-PAGE -- */ 13270 // If page-box is set 13271 if ($this->state == 0 && ((isset($this->cssManager->CSS['@PAGE']) && $this->cssManager->CSS['@PAGE']) || (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']) && $this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']))) { // mPDF 5.7.3 13272 $this->page_box['current'] = ''; 13273 $this->page_box['using'] = true; 13274 list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', false, 'O'); 13275 $this->DefOrientation = $this->CurOrientation = $pborientation; 13276 $this->orig_lMargin = $this->DeflMargin = $pbmgl; 13277 $this->orig_rMargin = $this->DefrMargin = $pbmgr; 13278 $this->orig_tMargin = $this->tMargin = $pbmgt; 13279 $this->orig_bMargin = $this->bMargin = $pbmgb; 13280 $this->orig_hMargin = $this->margin_header = $pbmgh; 13281 $this->orig_fMargin = $this->margin_footer = $pbmgf; 13282 list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', true, 'O'); // first page 13283 $this->show_marks = $marks; 13284 if ($hname) { 13285 $this->firstPageBoxHeader = $hname; 13286 } 13287 if ($fname) { 13288 $this->firstPageBoxFooter = $fname; 13289 } 13290 } 13291 /* -- END CSS-PAGE -- */ 13292 13293 $parseonly = false; 13294 $this->bufferoutput = false; 13295 if ($mode == HTMLParserMode::HTML_PARSE_NO_WRITE) { 13296 $parseonly = true; 13297 // Close any open block tags 13298 $arr = []; 13299 $ai = 0; 13300 for ($b = $this->blklvl; $b > 0; $b--) { 13301 $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai); 13302 } 13303 // Output any text left in buffer 13304 if (count($this->textbuffer)) { 13305 $this->printbuffer($this->textbuffer); 13306 } 13307 $this->textbuffer = []; 13308 } elseif ($mode === HTMLParserMode::HTML_HEADER_BUFFER) { 13309 // Close any open block tags 13310 $arr = []; 13311 $ai = 0; 13312 for ($b = $this->blklvl; $b > 0; $b--) { 13313 $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai); 13314 } 13315 // Output any text left in buffer 13316 if (count($this->textbuffer)) { 13317 $this->printbuffer($this->textbuffer); 13318 } 13319 $this->bufferoutput = true; 13320 $this->textbuffer = []; 13321 $this->headerbuffer = ''; 13322 $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', ''); 13323 $this->setCSS($properties, '', 'BODY'); 13324 } 13325 13326 mb_internal_encoding('UTF-8'); 13327 13328 $html = $this->AdjustHTML($html, $this->tabSpaces); // Try to make HTML look more like XHTML 13329 13330 if ($this->autoScriptToLang) { 13331 $html = $this->markScriptToLang($html); 13332 } 13333 13334 preg_match_all('/<htmlpageheader([^>]*)>(.*?)<\/htmlpageheader>/si', $html, $h); 13335 for ($i = 0; $i < count($h[1]); $i++) { 13336 if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $h[1][$i], $n)) { 13337 $this->pageHTMLheaders[$n[1]]['html'] = $h[2][$i]; 13338 $this->pageHTMLheaders[$n[1]]['h'] = $this->_getHtmlHeight($h[2][$i]); 13339 } 13340 } 13341 preg_match_all('/<htmlpagefooter([^>]*)>(.*?)<\/htmlpagefooter>/si', $html, $f); 13342 for ($i = 0; $i < count($f[1]); $i++) { 13343 if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $f[1][$i], $n)) { 13344 $this->pageHTMLfooters[$n[1]]['html'] = $f[2][$i]; 13345 $this->pageHTMLfooters[$n[1]]['h'] = $this->_getHtmlHeight($f[2][$i]); 13346 } 13347 } 13348 13349 $html = preg_replace('/<htmlpageheader.*?<\/htmlpageheader>/si', '', $html); 13350 $html = preg_replace('/<htmlpagefooter.*?<\/htmlpagefooter>/si', '', $html); 13351 13352 if ($this->state == 0 && ($mode === HTMLParserMode::DEFAULT_MODE || $mode === HTMLParserMode::HTML_BODY)) { 13353 $this->AddPage($this->CurOrientation); 13354 } 13355 13356 13357 if (isset($hname) && preg_match('/^html_(.*)$/i', $hname, $n)) { 13358 $this->SetHTMLHeader($this->pageHTMLheaders[$n[1]], 'O', true); 13359 } 13360 if (isset($fname) && preg_match('/^html_(.*)$/i', $fname, $n)) { 13361 $this->SetHTMLFooter($this->pageHTMLfooters[$n[1]], 'O'); 13362 } 13363 13364 13365 13366 $html = str_replace('<?', '< ', $html); // Fix '<?XML' bug from HTML code generated by MS Word 13367 13368 $this->checkSIP = false; 13369 $this->checkSMP = false; 13370 $this->checkCJK = false; 13371 if ($this->onlyCoreFonts) { 13372 $html = $this->SubstituteChars($html); 13373 } else { 13374 if (preg_match("/([" . $this->pregRTLchars . "])/u", $html)) { 13375 $this->biDirectional = true; 13376 } // *OTL* 13377 if (preg_match("/([\x{20000}-\x{2FFFF}])/u", $html)) { 13378 $this->checkSIP = true; 13379 } 13380 if (preg_match("/([\x{10000}-\x{1FFFF}])/u", $html)) { 13381 $this->checkSMP = true; 13382 } 13383 /* -- CJK-FONTS -- */ 13384 if (preg_match("/([" . $this->pregCJKchars . "])/u", $html)) { 13385 $this->checkCJK = true; 13386 } 13387 /* -- END CJK-FONTS -- */ 13388 } 13389 13390 // Don't allow non-breaking spaces that are converted to substituted chars or will break anyway and mess up table width calc. 13391 $html = str_replace('<tta>160</tta>', chr(32), $html); 13392 $html = str_replace('</tta><tta>', '|', $html); 13393 $html = str_replace('</tts><tts>', '|', $html); 13394 $html = str_replace('</ttz><ttz>', '|', $html); 13395 13396 // Add new supported tags in the DisableTags function 13397 $html = strip_tags($html, $this->enabledtags); // remove all unsupported tags, but the ones inside the 'enabledtags' string 13398 // Explode the string in order to parse the HTML code 13399 $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE); 13400 // ? more accurate regexp that allows e.g. <a name="Silly <name>"> 13401 // if changing - also change in fn.SubstituteChars() 13402 // $a = preg_split ('/<((?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE); 13403 13404 if ($this->mb_enc) { 13405 mb_internal_encoding($this->mb_enc); 13406 } 13407 $pbc = 0; 13408 $this->subPos = -1; 13409 $cnt = count($a); 13410 for ($i = 0; $i < $cnt; $i++) { 13411 $e = $a[$i]; 13412 if ($i % 2 == 0) { 13413 // TEXT 13414 if ($this->blk[$this->blklvl]['hide']) { 13415 continue; 13416 } 13417 if ($this->inlineDisplayOff) { 13418 continue; 13419 } 13420 if ($this->inMeter) { 13421 continue; 13422 } 13423 13424 if ($this->inFixedPosBlock) { 13425 $this->fixedPosBlock .= $e; 13426 continue; 13427 } // *CSS-POSITION* 13428 if (strlen($e) == 0) { 13429 continue; 13430 } 13431 13432 if ($this->ignorefollowingspaces && !$this->ispre) { 13433 if (strlen(ltrim($e)) == 0) { 13434 continue; 13435 } 13436 if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && substr($e, 0, 1) == ' ') { 13437 $this->ignorefollowingspaces = false; 13438 $e = ltrim($e); 13439 } 13440 } 13441 13442 $this->OTLdata = null; // mPDF 5.7.1 13443 13444 $e = UtfString::strcode2utf($e); 13445 $e = $this->lesser_entity_decode($e); 13446 13447 if ($this->usingCoreFont) { 13448 // If core font is selected in document which is not onlyCoreFonts - substitute with non-core font 13449 if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->subPos < $i && !$this->specialcontent) { 13450 $cnt += $this->SubstituteCharsNonCore($a, $i, $e); 13451 } 13452 // CONVERT ENCODING 13453 $e = mb_convert_encoding($e, $this->mb_enc, 'UTF-8'); 13454 if ($this->textvar & TextVars::FT_UPPERCASE) { 13455 $e = mb_strtoupper($e, $this->mb_enc); 13456 } // mPDF 5.7.1 13457 elseif ($this->textvar & TextVars::FT_LOWERCASE) { 13458 $e = mb_strtolower($e, $this->mb_enc); 13459 } // mPDF 5.7.1 13460 elseif ($this->textvar & TextVars::FT_CAPITALIZE) { 13461 $e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8"); 13462 } // mPDF 5.7.1 13463 } else { 13464 if ($this->checkSIP && $this->CurrentFont['sipext'] && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) { 13465 $cnt += $this->SubstituteCharsSIP($a, $i, $e); 13466 } 13467 13468 if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->CurrentFont['type'] != 'Type0' && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) { 13469 $cnt += $this->SubstituteCharsMB($a, $i, $e); 13470 } 13471 13472 if ($this->textvar & TextVars::FT_UPPERCASE) { 13473 $e = mb_strtoupper($e, $this->mb_enc); 13474 } elseif ($this->textvar & TextVars::FT_LOWERCASE) { 13475 $e = mb_strtolower($e, $this->mb_enc); 13476 } elseif ($this->textvar & TextVars::FT_CAPITALIZE) { 13477 $e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8"); 13478 } 13479 13480 /* -- OTL -- */ 13481 // Use OTL OpenType Table Layout - GSUB & GPOS 13482 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL'] && (!$this->specialcontent || !$this->useActiveForms)) { 13483 if (!$this->otl) { 13484 $this->otl = new Otl($this, $this->fontCache); 13485 } 13486 $e = $this->otl->applyOTL($e, $this->CurrentFont['useOTL']); 13487 $this->OTLdata = $this->otl->OTLdata; 13488 $this->otl->removeChar($e, $this->OTLdata, "\xef\xbb\xbf"); // Remove ZWNBSP (also Byte order mark FEFF) 13489 } /* -- END OTL -- */ 13490 else { 13491 // removes U+200E/U+200F LTR and RTL mark and U+200C/U+200D Zero-width Joiner and Non-joiner 13492 $e = preg_replace("/[\xe2\x80\x8c\xe2\x80\x8d\xe2\x80\x8e\xe2\x80\x8f]/u", '', $e); 13493 $e = preg_replace("/[\xef\xbb\xbf]/u", '', $e); // Remove ZWNBSP (also Byte order mark FEFF) 13494 } 13495 } 13496 13497 if (($this->tts) || ($this->ttz) || ($this->tta)) { 13498 $es = explode('|', $e); 13499 $e = ''; 13500 foreach ($es as $val) { 13501 $e .= chr($val); 13502 } 13503 } 13504 13505 // FORM ELEMENTS 13506 if ($this->specialcontent) { 13507 /* -- FORMS -- */ 13508 // SELECT tag (form element) 13509 if ($this->specialcontent == "type=select") { 13510 $e = ltrim($e); 13511 if (!empty($this->OTLdata)) { 13512 $this->otl->trimOTLdata($this->OTLdata, true, false); 13513 } // *OTL* 13514 $stringwidth = $this->GetStringWidth($e); 13515 if (!isset($this->selectoption['MAXWIDTH']) || $stringwidth > $this->selectoption['MAXWIDTH']) { 13516 $this->selectoption['MAXWIDTH'] = $stringwidth; 13517 } 13518 if (!isset($this->selectoption['SELECTED']) || $this->selectoption['SELECTED'] == '') { 13519 $this->selectoption['SELECTED'] = $e; 13520 if (!empty($this->OTLdata)) { 13521 $this->selectoption['SELECTED-OTLDATA'] = $this->OTLdata; 13522 } // *OTL* 13523 } 13524 // Active Forms 13525 if (isset($this->selectoption['ACTIVE']) && $this->selectoption['ACTIVE']) { 13526 $this->selectoption['ITEMS'][] = ['exportValue' => $this->selectoption['currentVAL'], 'content' => $e, 'selected' => $this->selectoption['currentSEL']]; 13527 } 13528 $this->OTLdata = []; 13529 } // TEXTAREA 13530 else { 13531 $objattr = unserialize($this->specialcontent); 13532 $objattr['text'] = $e; 13533 $objattr['OTLdata'] = $this->OTLdata; 13534 $this->OTLdata = []; 13535 $te = "\xbb\xa4\xactype=textarea,objattr=" . serialize($objattr) . "\xbb\xa4\xac"; 13536 if ($this->tdbegin) { 13537 $this->_saveCellTextBuffer($te, $this->HREF); 13538 } else { 13539 $this->_saveTextBuffer($te, $this->HREF); 13540 } 13541 } 13542 /* -- END FORMS -- */ 13543 } // TABLE 13544 elseif ($this->tableLevel) { 13545 /* -- TABLES -- */ 13546 if ($this->tdbegin) { 13547 if (($this->ignorefollowingspaces) && !$this->ispre) { 13548 $e = ltrim($e); 13549 if (!empty($this->OTLdata)) { 13550 $this->otl->trimOTLdata($this->OTLdata, true, false); 13551 } // *OTL* 13552 } 13553 if ($e || $e === '0') { 13554 if ($this->blockjustfinished && $this->cell[$this->row][$this->col]['s'] > 0) { 13555 $this->_saveCellTextBuffer("\n"); 13556 if (!isset($this->cell[$this->row][$this->col]['maxs'])) { 13557 $this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s']; 13558 } elseif ($this->cell[$this->row][$this->col]['maxs'] < $this->cell[$this->row][$this->col]['s']) { 13559 $this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s']; 13560 } 13561 $this->cell[$this->row][$this->col]['s'] = 0; // reset 13562 } 13563 $this->blockjustfinished = false; 13564 13565 if (!isset($this->cell[$this->row][$this->col]['R']) || !$this->cell[$this->row][$this->col]['R']) { 13566 if (isset($this->cell[$this->row][$this->col]['s'])) { 13567 $this->cell[$this->row][$this->col]['s'] += $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar); 13568 } else { 13569 $this->cell[$this->row][$this->col]['s'] = $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar); 13570 } 13571 if (!empty($this->spanborddet)) { 13572 $this->cell[$this->row][$this->col]['s'] += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0) + (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0); 13573 } 13574 } 13575 13576 $this->_saveCellTextBuffer($e, $this->HREF); 13577 13578 if (substr($this->cell[$this->row][$this->col]['a'], 0, 1) == 'D') { 13579 13580 $dp = $this->decimal_align[substr($this->cell[$this->row][$this->col]['a'], 0, 2)]; 13581 $s = preg_split('/' . preg_quote($dp, '/') . '/', $e, 2); // ? needs to be /u if not core 13582 $s0 = $this->GetStringWidth($s[0], false); 13583 13584 if (isset($s[1]) && $s[1]) { 13585 $s1 = $this->GetStringWidth(($s[1] . $dp), false); 13586 } else { 13587 $s1 = 0; 13588 } 13589 13590 if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'])) { 13591 if ($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'] === false) { 13592 $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'] = []; 13593 } 13594 $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = $s0; 13595 } else { 13596 $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = max($s0, $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0']); 13597 } 13598 13599 if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'])) { 13600 $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = $s1; 13601 } else { 13602 $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = max($s1, $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1']); 13603 } 13604 } 13605 13606 $this->nestedtablejustfinished = false; 13607 $this->linebreakjustfinished = false; 13608 } 13609 } 13610 /* -- END TABLES -- */ 13611 } // ALL ELSE 13612 else { 13613 if ($this->ignorefollowingspaces && !$this->ispre) { 13614 $e = ltrim($e); 13615 if (!empty($this->OTLdata)) { 13616 $this->otl->trimOTLdata($this->OTLdata, true, false); 13617 } // *OTL* 13618 } 13619 if ($e || $e === '0') { 13620 $this->_saveTextBuffer($e, $this->HREF); 13621 } 13622 } 13623 if ($e || $e === '0') { 13624 $this->ignorefollowingspaces = false; // mPDF 6 13625 } 13626 if (substr($e, -1, 1) == ' ' && !$this->ispre && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') { 13627 $this->ignorefollowingspaces = true; 13628 } 13629 } else { // TAG ** 13630 if (isset($e[0]) && $e[0] == '/') { 13631 $endtag = trim(strtoupper(substr($e, 1))); 13632 13633 /* -- CSS-POSITION -- */ 13634 // mPDF 6 13635 if ($this->inFixedPosBlock) { 13636 if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) { 13637 $this->fixedPosBlockDepth--; 13638 } 13639 if ($this->fixedPosBlockDepth == 0) { 13640 $this->fixedPosBlockSave[] = [$this->fixedPosBlock, $this->fixedPosBlockBBox, $this->page]; 13641 $this->fixedPosBlock = ''; 13642 $this->inFixedPosBlock = false; 13643 continue; 13644 } 13645 $this->fixedPosBlock .= '<' . $e . '>'; 13646 continue; 13647 } 13648 /* -- END CSS-POSITION -- */ 13649 13650 // mPDF 6 13651 // Correct for tags where HTML5 specifies optional end tags (see also OpenTag() ) 13652 if ($this->allow_html_optional_endtags && !$parseonly) { 13653 if (isset($this->blk[$this->blklvl]['tag'])) { 13654 $closed = false; 13655 // li end tag may be omitted if there is no more content in the parent element 13656 if (!$closed && $this->blk[$this->blklvl]['tag'] == 'LI' && $endtag != 'LI' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) { 13657 $this->tag->CloseTag('LI', $a, $i); 13658 $closed = true; 13659 } 13660 // dd end tag may be omitted if there is no more content in the parent element 13661 if (!$closed && $this->blk[$this->blklvl]['tag'] == 'DD' && $endtag != 'DD' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) { 13662 $this->tag->CloseTag('DD', $a, $i); 13663 $closed = true; 13664 } 13665 // p end tag may be omitted if there is no more content in the parent element and the parent element is not an A element [??????] 13666 if (!$closed && $this->blk[$this->blklvl]['tag'] == 'P' && $endtag != 'P' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) { 13667 $this->tag->CloseTag('P', $a, $i); 13668 $closed = true; 13669 } 13670 // option end tag may be omitted if there is no more content in the parent element 13671 if (!$closed && $this->blk[$this->blklvl]['tag'] == 'OPTION' && $endtag != 'OPTION' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) { 13672 $this->tag->CloseTag('OPTION', $a, $i); 13673 $closed = true; 13674 } 13675 } 13676 /* -- TABLES -- */ 13677 // Check for Table tags where HTML specifies optional end tags, 13678 if ($endtag == 'TABLE') { 13679 if ($this->lastoptionaltag == 'THEAD' || $this->lastoptionaltag == 'TBODY' || $this->lastoptionaltag == 'TFOOT') { 13680 $this->tag->CloseTag($this->lastoptionaltag, $a, $i); 13681 } 13682 if ($this->lastoptionaltag == 'TR') { 13683 $this->tag->CloseTag('TR', $a, $i); 13684 } 13685 if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') { 13686 $this->tag->CloseTag($this->lastoptionaltag, $a, $i); 13687 $this->tag->CloseTag('TR', $a, $i); 13688 } 13689 } 13690 if ($endtag == 'THEAD' || $endtag == 'TBODY' || $endtag == 'TFOOT') { 13691 if ($this->lastoptionaltag == 'TR') { 13692 $this->tag->CloseTag('TR', $a, $i); 13693 } 13694 if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') { 13695 $this->tag->CloseTag($this->lastoptionaltag, $a, $i); 13696 $this->tag->CloseTag('TR', $a, $i); 13697 } 13698 } 13699 if ($endtag == 'TR') { 13700 if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') { 13701 $this->tag->CloseTag($this->lastoptionaltag, $a, $i); 13702 } 13703 } 13704 /* -- END TABLES -- */ 13705 } 13706 13707 13708 // mPDF 6 13709 if ($this->blk[$this->blklvl]['hide']) { 13710 if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) { 13711 unset($this->blk[$this->blklvl]); 13712 $this->blklvl--; 13713 } 13714 continue; 13715 } 13716 13717 // mPDF 6 13718 $this->tag->CloseTag($endtag, $a, $i); // mPDF 6 13719 } else { // OPENING TAG 13720 if ($this->blk[$this->blklvl]['hide']) { 13721 if (strpos($e, ' ')) { 13722 $te = strtoupper(substr($e, 0, strpos($e, ' '))); 13723 } else { 13724 $te = strtoupper($e); 13725 } 13726 // mPDF 6 13727 if ($te == 'THEAD' || $te == 'TBODY' || $te == 'TFOOT' || $te == 'TR' || $te == 'TD' || $te == 'TH') { 13728 $this->lastoptionaltag = $te; 13729 } 13730 if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) { 13731 $this->blklvl++; 13732 $this->blk[$this->blklvl]['hide'] = true; 13733 $this->blk[$this->blklvl]['tag'] = $te; // mPDF 6 13734 } 13735 continue; 13736 } 13737 13738 /* -- CSS-POSITION -- */ 13739 if ($this->inFixedPosBlock) { 13740 if (strpos($e, ' ')) { 13741 $te = strtoupper(substr($e, 0, strpos($e, ' '))); 13742 } else { 13743 $te = strtoupper($e); 13744 } 13745 $this->fixedPosBlock .= '<' . $e . '>'; 13746 if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) { 13747 $this->fixedPosBlockDepth++; 13748 } 13749 continue; 13750 } 13751 /* -- END CSS-POSITION -- */ 13752 $regexp = '|=\'(.*?)\'|s'; // eliminate single quotes, if any 13753 $e = preg_replace($regexp, "=\"\$1\"", $e); 13754 // changes anykey=anyvalue to anykey="anyvalue" (only do this inside [some] tags) 13755 if (substr($e, 0, 10) != 'pageheader' && substr($e, 0, 10) != 'pagefooter' && substr($e, 0, 12) != 'tocpagebreak' && substr($e, 0, 10) != 'indexentry' && substr($e, 0, 8) != 'tocentry') { // mPDF 6 (ZZZ99H) 13756 $regexp = '| (\\w+?)=([^\\s>"]+)|si'; 13757 $e = preg_replace($regexp, " \$1=\"\$2\"", $e); 13758 } 13759 13760 $e = preg_replace('/ (\\S+?)\s*=\s*"/i', " \\1=\"", $e); 13761 13762 // Fix path values, if needed 13763 $orig_srcpath = ''; 13764 if ((stristr($e, "href=") !== false) or ( stristr($e, "src=") !== false)) { 13765 $regexp = '/ (href|src)\s*=\s*"(.*?)"/i'; 13766 preg_match($regexp, $e, $auxiliararray); 13767 if (isset($auxiliararray[2])) { 13768 $path = $auxiliararray[2]; 13769 } else { 13770 $path = ''; 13771 } 13772 if (trim($path) != '' && !(stristr($e, "src=") !== false && substr($path, 0, 4) == 'var:') && substr($path, 0, 1) != '@') { 13773 $path = htmlspecialchars_decode($path); // mPDF 5.7.4 URLs 13774 $orig_srcpath = $path; 13775 $this->GetFullPath($path); 13776 $regexp = '/ (href|src)="(.*?)"/i'; 13777 $e = preg_replace($regexp, ' \\1="' . $path . '"', $e); 13778 } 13779 }//END of Fix path values 13780 // Extract attributes 13781 $contents = []; 13782 $contents1 = []; 13783 $contents2 = []; 13784 // Changed to allow style="background: url('bg.jpg')" 13785 // Changed to improve performance; maximum length of \S (attribute) = 16 13786 // Increase allowed attribute name to 32 - cutting off "toc-even-header-name" etc. 13787 preg_match_all('/\\S{1,32}=["][^"]*["]/', $e, $contents1); 13788 preg_match_all('/\\S{1,32}=[\'][^\']*[\']/i', $e, $contents2); 13789 13790 $contents = array_merge($contents1, $contents2); 13791 preg_match('/\\S+/', $e, $a2); 13792 $tag = (isset($a2[0]) ? strtoupper($a2[0]) : ''); 13793 $attr = []; 13794 if ($orig_srcpath) { 13795 $attr['ORIG_SRC'] = $orig_srcpath; 13796 } 13797 if (!empty($contents)) { 13798 foreach ($contents[0] as $v) { 13799 // Changed to allow style="background: url('bg.jpg')" 13800 if (preg_match('/^([^=]*)=["]?([^"]*)["]?$/', $v, $a3) || preg_match('/^([^=]*)=[\']?([^\']*)[\']?$/', $v, $a3)) { 13801 if (strtoupper($a3[1]) == 'ID' || strtoupper($a3[1]) == 'CLASS') { // 4.2.013 Omits STYLE 13802 $attr[strtoupper($a3[1])] = trim(strtoupper($a3[2])); 13803 } // includes header-style-right etc. used for <pageheader> 13804 elseif (preg_match('/^(HEADER|FOOTER)-STYLE/i', $a3[1])) { 13805 $attr[strtoupper($a3[1])] = trim(strtoupper($a3[2])); 13806 } else { 13807 $attr[strtoupper($a3[1])] = trim($a3[2]); 13808 } 13809 } 13810 } 13811 } 13812 $this->tag->OpenTag($tag, $attr, $a, $i); // mPDF 6 13813 /* -- CSS-POSITION -- */ 13814 if ($this->inFixedPosBlock) { 13815 $this->fixedPosBlockBBox = [$tag, $attr, $this->x, $this->y]; 13816 $this->fixedPosBlock = ''; 13817 $this->fixedPosBlockDepth = 1; 13818 } 13819 /* -- END CSS-POSITION -- */ 13820 if (preg_match('/\/$/', $e)) { 13821 $this->tag->CloseTag($tag, $a, $i); 13822 } 13823 } 13824 } // end TAG 13825 } // end of foreach($a as $i=>$e) 13826 13827 if ($close) { 13828 // Close any open block tags 13829 for ($b = $this->blklvl; $b > 0; $b--) { 13830 $this->tag->CloseTag($this->blk[$b]['tag'], $a, $i); 13831 } 13832 13833 // Output any text left in buffer 13834 if (count($this->textbuffer) && !$parseonly) { 13835 $this->printbuffer($this->textbuffer); 13836 } 13837 if (!$parseonly) { 13838 $this->textbuffer = []; 13839 } 13840 13841 /* -- CSS-FLOAT -- */ 13842 // If ended with a float, need to move to end page 13843 $currpos = $this->page * 1000 + $this->y; 13844 if (isset($this->blk[$this->blklvl]['float_endpos']) && $this->blk[$this->blklvl]['float_endpos'] > $currpos) { 13845 $old_page = $this->page; 13846 $new_page = intval($this->blk[$this->blklvl]['float_endpos'] / 1000); 13847 if ($old_page != $new_page) { 13848 $s = $this->PrintPageBackgrounds(); 13849 // Writes after the marker so not overwritten later by page background etc. 13850 $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]); 13851 $this->pageBackgrounds = []; 13852 $this->page = $new_page; 13853 $this->ResetMargins(); 13854 $this->Reset(); 13855 $this->pageoutput[$this->page] = []; 13856 } 13857 $this->y = (round($this->blk[$this->blklvl]['float_endpos'] * 1000) % 1000000) / 1000; // mod changes operands to integers before processing 13858 } 13859 /* -- END CSS-FLOAT -- */ 13860 13861 /* -- CSS-IMAGE-FLOAT -- */ 13862 $this->printfloatbuffer(); 13863 /* -- END CSS-IMAGE-FLOAT -- */ 13864 13865 // Create Internal Links, if needed 13866 if (!empty($this->internallink)) { 13867 13868 foreach ($this->internallink as $k => $v) { 13869 13870 if (strpos($k, "#") !== false) { 13871 continue; 13872 } 13873 13874 if (!is_array($v)) { 13875 continue; 13876 } 13877 13878 $ypos = $v['Y']; 13879 $pagenum = $v['PAGE']; 13880 $sharp = "#"; 13881 13882 while (array_key_exists($sharp . $k, $this->internallink)) { 13883 $internallink = $this->internallink[$sharp . $k]; 13884 $this->SetLink($internallink, $ypos, $pagenum); 13885 $sharp .= "#"; 13886 } 13887 } 13888 } 13889 13890 $this->bufferoutput = false; 13891 13892 /* -- CSS-POSITION -- */ 13893 if (count($this->fixedPosBlockSave)) { 13894 foreach ($this->fixedPosBlockSave as $fpbs) { 13895 $old_page = $this->page; 13896 $this->page = $fpbs[2]; 13897 $this->WriteFixedPosHTML($fpbs[0], 0, 0, 100, 100, 'auto', $fpbs[1]); // 0,0,10,10 are overwritten by bbox 13898 $this->page = $old_page; 13899 } 13900 $this->fixedPosBlockSave = []; 13901 } 13902 /* -- END CSS-POSITION -- */ 13903 } 13904 } 13905 13906 /* -- CSS-POSITION -- */ 13907 13908 function WriteFixedPosHTML($html, $x, $y, $w, $h, $overflow = 'visible', $bounding = []) 13909 { 13910 // $overflow can be 'hidden', 'visible' or 'auto' - 'auto' causes autofit to size 13911 // Annotations disabled - enabled in mPDF 5.0 13912 // Links do work 13913 // Will always go on current page (or start Page 1 if required) 13914 // Probably INCOMPATIBLE WITH keep with table, columns etc. 13915 // Called externally or interally via <div style="position: [fixed|absolute]"> 13916 // When used internally, $x $y $w $h and $overflow are all overridden by $bounding 13917 13918 $overflow = strtolower($overflow); 13919 if ($this->state == 0) { 13920 $this->AddPage($this->CurOrientation); 13921 } 13922 $save_y = $this->y; 13923 $save_x = $this->x; 13924 $this->fullImageHeight = $this->h; 13925 $save_cols = false; 13926 /* -- COLUMNS -- */ 13927 if ($this->ColActive) { 13928 $save_cols = true; 13929 $save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off 13930 $this->SetColumns(0); 13931 } 13932 /* -- END COLUMNS -- */ 13933 $save_annots = $this->title2annots; // *ANNOTATIONS* 13934 $this->writingHTMLheader = true; // a FIX to stop pagebreaks etc. 13935 $this->writingHTMLfooter = true; 13936 $this->InFooter = true; // suppresses autopagebreaks 13937 $save_bgs = $this->pageBackgrounds; 13938 $checkinnerhtml = preg_replace('/\s/', '', $html); 13939 $rotate = 0; 13940 13941 if ($w > $this->w) { 13942 $x = 0; 13943 $w = $this->w; 13944 } 13945 if ($h > $this->h) { 13946 $y = 0; 13947 $h = $this->h; 13948 } 13949 if ($x > $this->w) { 13950 $x = $this->w - $w; 13951 } 13952 if ($y > $this->h) { 13953 $y = $this->h - $h; 13954 } 13955 13956 if (!empty($bounding)) { 13957 // $cont_ containing block = full physical page (position: absolute) or page inside margins (position: fixed) 13958 // $bbox_ Bounding box is the <div> which is positioned absolutely/fixed 13959 // top/left/right/bottom/width/height/background*/border*/padding*/margin* are taken from bounding 13960 // font*[family/size/style/weight]/line-height/text*[align/decoration/transform/indent]/color are transferred to $inner 13961 // as an enclosing <div> (after having checked ID/CLASS) 13962 // $x, $y, $w, $h are inside of $bbox_ = containing box for $inner_ 13963 // $inner_ InnerHTML is the contents of that block to be output 13964 $tag = $bounding[0]; 13965 $attr = $bounding[1]; 13966 $orig_x0 = $bounding[2]; 13967 $orig_y0 = $bounding[3]; 13968 13969 // As in WriteHTML() initialising 13970 $this->blklvl = 0; 13971 $this->lastblocklevelchange = 0; 13972 $this->blk = []; 13973 $this->initialiseBlock($this->blk[0]); 13974 13975 $this->blk[0]['width'] = & $this->pgwidth; 13976 $this->blk[0]['inner_width'] = & $this->pgwidth; 13977 13978 $this->blk[0]['blockContext'] = $this->blockContext; 13979 13980 $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', ''); 13981 $this->setCSS($properties, '', 'BODY'); 13982 $this->blklvl = 1; 13983 $this->initialiseBlock($this->blk[1]); 13984 $this->blk[1]['tag'] = $tag; 13985 $this->blk[1]['attr'] = $attr; 13986 $this->Reset(); 13987 $p = $this->cssManager->MergeCSS('BLOCK', $tag, $attr); 13988 if (isset($p['ROTATE']) && ($p['ROTATE'] == 90 || $p['ROTATE'] == -90 || $p['ROTATE'] == 180)) { 13989 $rotate = $p['ROTATE']; 13990 } // mPDF 6 13991 if (isset($p['OVERFLOW'])) { 13992 $overflow = strtolower($p['OVERFLOW']); 13993 } 13994 if (strtolower($p['POSITION']) == 'fixed') { 13995 $cont_w = $this->pgwidth; // $this->blk[0]['inner_width']; 13996 $cont_h = $this->h - $this->tMargin - $this->bMargin; 13997 $cont_x = $this->lMargin; 13998 $cont_y = $this->tMargin; 13999 } else { 14000 $cont_w = $this->w; // ABSOLUTE; 14001 $cont_h = $this->h; 14002 $cont_x = 0; 14003 $cont_y = 0; 14004 } 14005 14006 // Pass on in-line properties to the innerhtml 14007 $css = ''; 14008 if (isset($p['TEXT-ALIGN'])) { 14009 $css .= 'text-align: ' . strtolower($p['TEXT-ALIGN']) . '; '; 14010 } 14011 if (isset($p['TEXT-TRANSFORM'])) { 14012 $css .= 'text-transform: ' . strtolower($p['TEXT-TRANSFORM']) . '; '; 14013 } 14014 if (isset($p['TEXT-INDENT'])) { 14015 $css .= 'text-indent: ' . strtolower($p['TEXT-INDENT']) . '; '; 14016 } 14017 if (isset($p['TEXT-DECORATION'])) { 14018 $css .= 'text-decoration: ' . strtolower($p['TEXT-DECORATION']) . '; '; 14019 } 14020 if (isset($p['FONT-FAMILY'])) { 14021 $css .= 'font-family: ' . strtolower($p['FONT-FAMILY']) . '; '; 14022 } 14023 if (isset($p['FONT-STYLE'])) { 14024 $css .= 'font-style: ' . strtolower($p['FONT-STYLE']) . '; '; 14025 } 14026 if (isset($p['FONT-WEIGHT'])) { 14027 $css .= 'font-weight: ' . strtolower($p['FONT-WEIGHT']) . '; '; 14028 } 14029 if (isset($p['FONT-SIZE'])) { 14030 $css .= 'font-size: ' . strtolower($p['FONT-SIZE']) . '; '; 14031 } 14032 if (isset($p['LINE-HEIGHT'])) { 14033 $css .= 'line-height: ' . strtolower($p['LINE-HEIGHT']) . '; '; 14034 } 14035 if (isset($p['TEXT-SHADOW'])) { 14036 $css .= 'text-shadow: ' . strtolower($p['TEXT-SHADOW']) . '; '; 14037 } 14038 if (isset($p['LETTER-SPACING'])) { 14039 $css .= 'letter-spacing: ' . strtolower($p['LETTER-SPACING']) . '; '; 14040 } 14041 // mPDF 6 14042 if (isset($p['FONT-VARIANT-POSITION'])) { 14043 $css .= 'font-variant-position: ' . strtolower($p['FONT-VARIANT-POSITION']) . '; '; 14044 } 14045 if (isset($p['FONT-VARIANT-CAPS'])) { 14046 $css .= 'font-variant-caps: ' . strtolower($p['FONT-VARIANT-CAPS']) . '; '; 14047 } 14048 if (isset($p['FONT-VARIANT-LIGATURES'])) { 14049 $css .= 'font-variant-ligatures: ' . strtolower($p['FONT-VARIANT-LIGATURES']) . '; '; 14050 } 14051 if (isset($p['FONT-VARIANT-NUMERIC'])) { 14052 $css .= 'font-variant-numeric: ' . strtolower($p['FONT-VARIANT-NUMERIC']) . '; '; 14053 } 14054 if (isset($p['FONT-VARIANT-ALTERNATES'])) { 14055 $css .= 'font-variant-alternates: ' . strtolower($p['FONT-VARIANT-ALTERNATES']) . '; '; 14056 } 14057 if (isset($p['FONT-FEATURE-SETTINGS'])) { 14058 $css .= 'font-feature-settings: ' . strtolower($p['FONT-FEATURE-SETTINGS']) . '; '; 14059 } 14060 if (isset($p['FONT-LANGUAGE-OVERRIDE'])) { 14061 $css .= 'font-language-override: ' . strtolower($p['FONT-LANGUAGE-OVERRIDE']) . '; '; 14062 } 14063 if (isset($p['FONT-KERNING'])) { 14064 $css .= 'font-kerning: ' . strtolower($p['FONT-KERNING']) . '; '; 14065 } 14066 14067 if (isset($p['COLOR'])) { 14068 $css .= 'color: ' . strtolower($p['COLOR']) . '; '; 14069 } 14070 if (isset($p['Z-INDEX'])) { 14071 $css .= 'z-index: ' . $p['Z-INDEX'] . '; '; 14072 } 14073 if ($css) { 14074 $html = '<div style="' . $css . '">' . $html . '</div>'; 14075 } 14076 // Copy over (only) the properties to set for border and background 14077 $pb = []; 14078 $pb['MARGIN-TOP'] = (isset($p['MARGIN-TOP']) ? $p['MARGIN-TOP'] : ''); 14079 $pb['MARGIN-RIGHT'] = (isset($p['MARGIN-RIGHT']) ? $p['MARGIN-RIGHT'] : ''); 14080 $pb['MARGIN-BOTTOM'] = (isset($p['MARGIN-BOTTOM']) ? $p['MARGIN-BOTTOM'] : ''); 14081 $pb['MARGIN-LEFT'] = (isset($p['MARGIN-LEFT']) ? $p['MARGIN-LEFT'] : ''); 14082 $pb['PADDING-TOP'] = (isset($p['PADDING-TOP']) ? $p['PADDING-TOP'] : ''); 14083 $pb['PADDING-RIGHT'] = (isset($p['PADDING-RIGHT']) ? $p['PADDING-RIGHT'] : ''); 14084 $pb['PADDING-BOTTOM'] = (isset($p['PADDING-BOTTOM']) ? $p['PADDING-BOTTOM'] : ''); 14085 $pb['PADDING-LEFT'] = (isset($p['PADDING-LEFT']) ? $p['PADDING-LEFT'] : ''); 14086 $pb['BORDER-TOP'] = (isset($p['BORDER-TOP']) ? $p['BORDER-TOP'] : ''); 14087 $pb['BORDER-RIGHT'] = (isset($p['BORDER-RIGHT']) ? $p['BORDER-RIGHT'] : ''); 14088 $pb['BORDER-BOTTOM'] = (isset($p['BORDER-BOTTOM']) ? $p['BORDER-BOTTOM'] : ''); 14089 $pb['BORDER-LEFT'] = (isset($p['BORDER-LEFT']) ? $p['BORDER-LEFT'] : ''); 14090 if (isset($p['BORDER-TOP-LEFT-RADIUS-H'])) { 14091 $pb['BORDER-TOP-LEFT-RADIUS-H'] = $p['BORDER-TOP-LEFT-RADIUS-H']; 14092 } 14093 if (isset($p['BORDER-TOP-LEFT-RADIUS-V'])) { 14094 $pb['BORDER-TOP-LEFT-RADIUS-V'] = $p['BORDER-TOP-LEFT-RADIUS-V']; 14095 } 14096 if (isset($p['BORDER-TOP-RIGHT-RADIUS-H'])) { 14097 $pb['BORDER-TOP-RIGHT-RADIUS-H'] = $p['BORDER-TOP-RIGHT-RADIUS-H']; 14098 } 14099 if (isset($p['BORDER-TOP-RIGHT-RADIUS-V'])) { 14100 $pb['BORDER-TOP-RIGHT-RADIUS-V'] = $p['BORDER-TOP-RIGHT-RADIUS-V']; 14101 } 14102 if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-H'])) { 14103 $pb['BORDER-BOTTOM-LEFT-RADIUS-H'] = $p['BORDER-BOTTOM-LEFT-RADIUS-H']; 14104 } 14105 if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-V'])) { 14106 $pb['BORDER-BOTTOM-LEFT-RADIUS-V'] = $p['BORDER-BOTTOM-LEFT-RADIUS-V']; 14107 } 14108 if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-H'])) { 14109 $pb['BORDER-BOTTOM-RIGHT-RADIUS-H'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-H']; 14110 } 14111 if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-V'])) { 14112 $pb['BORDER-BOTTOM-RIGHT-RADIUS-V'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-V']; 14113 } 14114 if (isset($p['BACKGROUND-COLOR'])) { 14115 $pb['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR']; 14116 } 14117 if (isset($p['BOX-SHADOW'])) { 14118 $pb['BOX-SHADOW'] = $p['BOX-SHADOW']; 14119 } 14120 /* -- BACKGROUNDS -- */ 14121 if (isset($p['BACKGROUND-IMAGE'])) { 14122 $pb['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE']; 14123 } 14124 if (isset($p['BACKGROUND-IMAGE-RESIZE'])) { 14125 $pb['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE']; 14126 } 14127 if (isset($p['BACKGROUND-IMAGE-OPACITY'])) { 14128 $pb['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY']; 14129 } 14130 if (isset($p['BACKGROUND-REPEAT'])) { 14131 $pb['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT']; 14132 } 14133 if (isset($p['BACKGROUND-POSITION'])) { 14134 $pb['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION']; 14135 } 14136 if (isset($p['BACKGROUND-GRADIENT'])) { 14137 $pb['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT']; 14138 } 14139 if (isset($p['BACKGROUND-SIZE'])) { 14140 $pb['BACKGROUND-SIZE'] = $p['BACKGROUND-SIZE']; 14141 } 14142 if (isset($p['BACKGROUND-ORIGIN'])) { 14143 $pb['BACKGROUND-ORIGIN'] = $p['BACKGROUND-ORIGIN']; 14144 } 14145 if (isset($p['BACKGROUND-CLIP'])) { 14146 $pb['BACKGROUND-CLIP'] = $p['BACKGROUND-CLIP']; 14147 } 14148 14149 /* -- END BACKGROUNDS -- */ 14150 14151 $this->setCSS($pb, 'BLOCK', $tag); 14152 14153 // ================================================================ 14154 $bbox_br = $this->blk[1]['border_right']['w']; 14155 $bbox_bl = $this->blk[1]['border_left']['w']; 14156 $bbox_bt = $this->blk[1]['border_top']['w']; 14157 $bbox_bb = $this->blk[1]['border_bottom']['w']; 14158 $bbox_pr = $this->blk[1]['padding_right']; 14159 $bbox_pl = $this->blk[1]['padding_left']; 14160 $bbox_pt = $this->blk[1]['padding_top']; 14161 $bbox_pb = $this->blk[1]['padding_bottom']; 14162 $bbox_mr = $this->blk[1]['margin_right']; 14163 if (isset($p['MARGIN-RIGHT']) && strtolower($p['MARGIN-RIGHT']) == 'auto') { 14164 $bbox_mr = 'auto'; 14165 } 14166 $bbox_ml = $this->blk[1]['margin_left']; 14167 if (isset($p['MARGIN-LEFT']) && strtolower($p['MARGIN-LEFT']) == 'auto') { 14168 $bbox_ml = 'auto'; 14169 } 14170 $bbox_mt = $this->blk[1]['margin_top']; 14171 if (isset($p['MARGIN-TOP']) && strtolower($p['MARGIN-TOP']) == 'auto') { 14172 $bbox_mt = 'auto'; 14173 } 14174 $bbox_mb = $this->blk[1]['margin_bottom']; 14175 if (isset($p['MARGIN-BOTTOM']) && strtolower($p['MARGIN-BOTTOM']) == 'auto') { 14176 $bbox_mb = 'auto'; 14177 } 14178 if (isset($p['LEFT']) && strtolower($p['LEFT']) != 'auto') { 14179 $bbox_left = $this->sizeConverter->convert($p['LEFT'], $cont_w, $this->FontSize, false); 14180 } else { 14181 $bbox_left = 'auto'; 14182 } 14183 if (isset($p['TOP']) && strtolower($p['TOP']) != 'auto') { 14184 $bbox_top = $this->sizeConverter->convert($p['TOP'], $cont_h, $this->FontSize, false); 14185 } else { 14186 $bbox_top = 'auto'; 14187 } 14188 if (isset($p['RIGHT']) && strtolower($p['RIGHT']) != 'auto') { 14189 $bbox_right = $this->sizeConverter->convert($p['RIGHT'], $cont_w, $this->FontSize, false); 14190 } else { 14191 $bbox_right = 'auto'; 14192 } 14193 if (isset($p['BOTTOM']) && strtolower($p['BOTTOM']) != 'auto') { 14194 $bbox_bottom = $this->sizeConverter->convert($p['BOTTOM'], $cont_h, $this->FontSize, false); 14195 } else { 14196 $bbox_bottom = 'auto'; 14197 } 14198 if (isset($p['WIDTH']) && strtolower($p['WIDTH']) != 'auto') { 14199 $inner_w = $this->sizeConverter->convert($p['WIDTH'], $cont_w, $this->FontSize, false); 14200 } else { 14201 $inner_w = 'auto'; 14202 } 14203 if (isset($p['HEIGHT']) && strtolower($p['HEIGHT']) != 'auto') { 14204 $inner_h = $this->sizeConverter->convert($p['HEIGHT'], $cont_h, $this->FontSize, false); 14205 } else { 14206 $inner_h = 'auto'; 14207 } 14208 14209 // If bottom or right pos are set and not left / top - save this to adjust rotated block later 14210 if ($rotate == 90 || $rotate == -90) { // mPDF 6 14211 if ($bbox_left === 'auto' && $bbox_right !== 'auto') { 14212 $rot_rpos = $bbox_right; 14213 } else { 14214 $rot_rpos = false; 14215 } 14216 if ($bbox_top === 'auto' && $bbox_bottom !== 'auto') { 14217 $rot_bpos = $bbox_bottom; 14218 } else { 14219 $rot_bpos = false; 14220 } 14221 } 14222 14223 // ================================================================ 14224 if ($checkinnerhtml == '' && $inner_h === 'auto') { 14225 $inner_h = 0.0001; 14226 } 14227 if ($checkinnerhtml == '' && $inner_w === 'auto') { 14228 $inner_w = 2 * $this->GetCharWidth('W', false); 14229 } 14230 // ================================================================ 14231 // Algorithm from CSS2.1 See http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height 14232 // mPD 5.3.14 14233 // Special case (not CSS) if all not specified, centre vertically on page 14234 $bbox_top_orig = ''; 14235 if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_mt === 'auto' && $bbox_mb === 'auto') { 14236 $bbox_top_orig = $bbox_top; 14237 if ($bbox_mt === 'auto') { 14238 $bbox_mt = 0; 14239 } 14240 if ($bbox_mb === 'auto') { 14241 $bbox_mb = 0; 14242 } 14243 $bbox_top = $orig_y0 - $bbox_mt - $cont_y; 14244 // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto' 14245 } // mPD 5.3.14 14246 elseif ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto') { 14247 $bbox_top_orig = $bbox_top = $orig_y0 - $cont_y; 14248 if ($bbox_mt === 'auto') { 14249 $bbox_mt = 0; 14250 } 14251 if ($bbox_mb === 'auto') { 14252 $bbox_mb = 0; 14253 } 14254 // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto' 14255 } elseif ($bbox_top !== 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') { 14256 if ($bbox_mt === 'auto' && $bbox_mb === 'auto') { 14257 $x = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom; 14258 $bbox_mt = $bbox_mb = ($x / 2); 14259 } elseif ($bbox_mt === 'auto') { 14260 $bbox_mt = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom; 14261 } elseif ($bbox_mb === 'auto') { 14262 $bbox_mb = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom; 14263 } else { 14264 $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt; 14265 } 14266 } else { 14267 if ($bbox_mt === 'auto') { 14268 $bbox_mt = 0; 14269 } 14270 if ($bbox_mb === 'auto') { 14271 $bbox_mb = 0; 14272 } 14273 if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom !== 'auto') { 14274 // solve for $bbox_top when content_h known - $inner_h=='auto' && $bbox_top =='auto' 14275 } elseif ($bbox_top === 'auto' && $bbox_bottom === 'auto' && $inner_h !== 'auto') { 14276 $bbox_top = $orig_y0 - $bbox_mt - $cont_y; 14277 $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt; 14278 } elseif ($inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_top !== 'auto') { 14279 // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto' 14280 } elseif ($bbox_top === 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') { 14281 $bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom; 14282 } elseif ($inner_h === 'auto' && $bbox_top !== 'auto' && $bbox_bottom !== 'auto') { 14283 $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom; 14284 } elseif ($bbox_bottom === 'auto' && $bbox_top !== 'auto' && $inner_h !== 'auto') { 14285 $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt; 14286 } 14287 } 14288 14289 // THEN DO SAME FOR WIDTH 14290 // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width 14291 if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right === 'auto') { 14292 if ($bbox_ml === 'auto') { 14293 $bbox_ml = 0; 14294 } 14295 if ($bbox_mr === 'auto') { 14296 $bbox_mr = 0; 14297 } 14298 // IF containing element RTL, should set $bbox_right 14299 $bbox_left = $orig_x0 - $bbox_ml - $cont_x; 14300 // solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto' 14301 } elseif ($bbox_left !== 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') { 14302 if ($bbox_ml === 'auto' && $bbox_mr === 'auto') { 14303 $x = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right; 14304 $bbox_ml = $bbox_mr = ($x / 2); 14305 } elseif ($bbox_ml === 'auto') { 14306 $bbox_ml = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right; 14307 } elseif ($bbox_mr === 'auto') { 14308 $bbox_mr = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right; 14309 } else { 14310 $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml; 14311 } 14312 } else { 14313 if ($bbox_ml === 'auto') { 14314 $bbox_ml = 0; 14315 } 14316 if ($bbox_mr === 'auto') { 14317 $bbox_mr = 0; 14318 } 14319 if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right !== 'auto') { 14320 // solve for $bbox_left when content_w known - $inner_w=='auto' && $bbox_left =='auto' 14321 } elseif ($bbox_left === 'auto' && $bbox_right === 'auto' && $inner_w !== 'auto') { 14322 // IF containing element RTL, should set $bbox_right 14323 $bbox_left = $orig_x0 - $bbox_ml - $cont_x; 14324 $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml; 14325 } elseif ($inner_w === 'auto' && $bbox_right === 'auto' && $bbox_left !== 'auto') { 14326 // solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto' 14327 } elseif ($bbox_left === 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') { 14328 $bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right; 14329 } elseif ($inner_w === 'auto' && $bbox_left !== 'auto' && $bbox_right !== 'auto') { 14330 $inner_w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right; 14331 } elseif ($bbox_right === 'auto' && $bbox_left !== 'auto' && $inner_w !== 'auto') { 14332 $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml; 14333 } 14334 } 14335 14336 // ================================================================ 14337 // ================================================================ 14338 /* -- BACKGROUNDS -- */ 14339 if (isset($pb['BACKGROUND-IMAGE']) && $pb['BACKGROUND-IMAGE']) { 14340 $ret = $this->SetBackground($pb, $this->blk[1]['inner_width']); 14341 if ($ret) { 14342 $this->blk[1]['background-image'] = $ret; 14343 } 14344 } 14345 /* -- END BACKGROUNDS -- */ 14346 14347 $bbox_top_auto = $bbox_top === 'auto'; 14348 $bbox_left_auto = $bbox_left === 'auto'; 14349 $bbox_right_auto = $bbox_right === 'auto'; 14350 $bbox_bottom_auto = $bbox_bottom === 'auto'; 14351 14352 $bbox_top = is_numeric($bbox_top) ? $bbox_top : 0; 14353 $bbox_left = is_numeric($bbox_left) ? $bbox_left : 0; 14354 $bbox_right = is_numeric($bbox_right) ? $bbox_right : 0; 14355 $bbox_bottom = is_numeric($bbox_bottom) ? $bbox_bottom : 0; 14356 14357 $y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt; 14358 $h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom; 14359 14360 $x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl; 14361 $w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right; 14362 14363 // Set (temporary) values for x y w h to do first paint, if values are auto 14364 if ($inner_h === 'auto' && $bbox_top_auto) { 14365 $y = $cont_y + $bbox_mt + $bbox_bt + $bbox_pt; 14366 $h = $cont_h - ($bbox_bottom + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb); 14367 } elseif ($inner_h === 'auto' && $bbox_bottom_auto) { 14368 $y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt; 14369 $h = $cont_h - ($bbox_top + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb); 14370 } 14371 if ($inner_w === 'auto' && $bbox_left_auto) { 14372 $x = $cont_x + $bbox_ml + $bbox_bl + $bbox_pl; 14373 $w = $cont_w - ($bbox_right + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr); 14374 } elseif ($inner_w === 'auto' && $bbox_right_auto) { 14375 $x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl; 14376 $w = $cont_w - ($bbox_left + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr); 14377 } 14378 14379 $bbox_y = $cont_y + $bbox_top + $bbox_mt; 14380 $bbox_x = $cont_x + $bbox_left + $bbox_ml; 14381 14382 $saved_block1 = $this->blk[1]; 14383 14384 unset($p); 14385 unset($pb); 14386 14387 // ================================================================ 14388 if ($inner_w === 'auto') { // do a first write 14389 $this->lMargin = $x; 14390 $this->rMargin = $this->w - $w - $x; 14391 14392 // SET POSITION & FONT VALUES 14393 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 14394 $this->pageoutput[$this->page] = []; 14395 $this->x = $x; 14396 $this->y = $y; 14397 $this->HTMLheaderPageLinks = []; 14398 $this->HTMLheaderPageAnnots = []; 14399 $this->HTMLheaderPageForms = []; 14400 $this->pageBackgrounds = []; 14401 $this->maxPosR = 0; 14402 $this->maxPosL = $this->w; // For RTL 14403 $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER); 14404 $inner_w = $this->maxPosR - $this->lMargin; 14405 if ($bbox_right_auto) { 14406 $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml; 14407 } elseif ($bbox_left_auto) { 14408 $bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right; 14409 $bbox_x = $cont_x + $bbox_left + $bbox_ml; 14410 $inner_x = $bbox_x + $bbox_bl + $bbox_pl; 14411 $x = $inner_x; 14412 } 14413 14414 $w = $inner_w; 14415 $bbox_y = $cont_y + $bbox_top + $bbox_mt; 14416 $bbox_x = $cont_x + $bbox_left + $bbox_ml; 14417 } 14418 14419 if ($inner_h === 'auto') { // do a first write 14420 14421 $this->lMargin = $x; 14422 $this->rMargin = $this->w - $w - $x; 14423 14424 // SET POSITION & FONT VALUES 14425 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 14426 $this->pageoutput[$this->page] = []; 14427 $this->x = $x; 14428 $this->y = $y; 14429 $this->HTMLheaderPageLinks = []; 14430 $this->HTMLheaderPageAnnots = []; 14431 $this->HTMLheaderPageForms = []; 14432 $this->pageBackgrounds = []; 14433 $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER); 14434 $inner_h = $this->y - $y; 14435 14436 if ($overflow != 'hidden' && $overflow != 'visible') { // constrained 14437 if (($this->y + $bbox_pb + $bbox_bb) > ($cont_y + $cont_h)) { 14438 $adj = ($this->y + $bbox_pb + $bbox_bb) - ($cont_y + $cont_h); 14439 $inner_h -= $adj; 14440 } 14441 } 14442 if ($bbox_bottom_auto && $bbox_top_orig === 'auto') { 14443 $bbox_bottom = $bbox_top = ($cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb) / 2; 14444 if ($overflow != 'hidden' && $overflow != 'visible') { // constrained 14445 if ($bbox_top < 0) { 14446 $bbox_top = 0; 14447 $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom; 14448 } 14449 } 14450 $bbox_y = $cont_y + $bbox_top + $bbox_mt; 14451 $inner_y = $bbox_y + $bbox_bt + $bbox_pt; 14452 $y = $inner_y; 14453 } elseif ($bbox_bottom_auto) { 14454 $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb; 14455 } elseif ($bbox_top_auto) { 14456 $bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom; 14457 if ($overflow != 'hidden' && $overflow != 'visible') { // constrained 14458 if ($bbox_top < 0) { 14459 $bbox_top = 0; 14460 $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom; 14461 } 14462 } 14463 $bbox_y = $cont_y + $bbox_top + $bbox_mt; 14464 $inner_y = $bbox_y + $bbox_bt + $bbox_pt; 14465 $y = $inner_y; 14466 } 14467 $h = $inner_h; 14468 $bbox_y = $cont_y + $bbox_top + $bbox_mt; 14469 $bbox_x = $cont_x + $bbox_left + $bbox_ml; 14470 } 14471 14472 $inner_w = $w; 14473 $inner_h = $h; 14474 } 14475 14476 $this->lMargin = $x; 14477 $this->rMargin = $this->w - $w - $x; 14478 14479 // SET POSITION & FONT VALUES 14480 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 14481 $this->pageoutput[$this->page] = []; 14482 14483 $this->x = $x; 14484 $this->y = $y; 14485 14486 $this->HTMLheaderPageLinks = []; 14487 $this->HTMLheaderPageAnnots = []; 14488 $this->HTMLheaderPageForms = []; 14489 14490 $this->pageBackgrounds = []; 14491 14492 $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER); 14493 14494 $actual_h = $this->y - $y; 14495 $use_w = $w; 14496 $use_h = $h; 14497 $ratio = $actual_h / $use_w; 14498 14499 if ($overflow != 'hidden' && $overflow != 'visible') { 14500 $target = $h / $w; 14501 if ($target > 0) { 14502 if (($ratio / $target) > 1) { 14503 $nl = ceil($actual_h / $this->lineheight); 14504 $l = $use_w * $nl; 14505 $est_w = sqrt(($l * $this->lineheight) / $target) * 0.8; 14506 $use_w += ($est_w - $use_w) - ($w / 100); 14507 } 14508 $bpcstart = ($ratio / $target); 14509 $bpcctr = 1; 14510 14511 while (($ratio / $target) > 1) { 14512 // @log 'Auto-sizing fixed-position block $bpcctr++ 14513 14514 $this->x = $x; 14515 $this->y = $y; 14516 14517 if (($ratio / $target) > 1.5 || ($ratio / $target) < 0.6) { 14518 $use_w += ($w / $this->incrementFPR1); 14519 } elseif (($ratio / $target) > 1.2 || ($ratio / $target) < 0.85) { 14520 $use_w += ($w / $this->incrementFPR2); 14521 } elseif (($ratio / $target) > 1.1 || ($ratio / $target) < 0.91) { 14522 $use_w += ($w / $this->incrementFPR3); 14523 } else { 14524 $use_w += ($w / $this->incrementFPR4); 14525 } 14526 14527 $use_h = $use_w * $target; 14528 $this->rMargin = $this->w - $use_w - $x; 14529 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 14530 $this->HTMLheaderPageLinks = []; 14531 $this->HTMLheaderPageAnnots = []; 14532 $this->HTMLheaderPageForms = []; 14533 $this->pageBackgrounds = []; 14534 $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER); 14535 $actual_h = $this->y - $y; 14536 $ratio = $actual_h / $use_w; 14537 } 14538 } 14539 } 14540 14541 $shrink_f = $w / $use_w; 14542 14543 // ================================================================ 14544 14545 $this->pages[$this->page] .= '___BEFORE_BORDERS___'; 14546 $block_s = $this->PrintPageBackgrounds(); // Save to print later inside clipping path 14547 $this->pageBackgrounds = []; 14548 14549 // ================================================================ 14550 14551 if ($rotate == 90 || $rotate == -90) { // mPDF 6 14552 $prerotw = $bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br; 14553 $preroth = $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb; 14554 $rot_start = " q\n"; 14555 if ($rotate == 90) { 14556 if ($rot_rpos !== false) { 14557 $adjw = $prerotw; 14558 } // width before rotation 14559 else { 14560 $adjw = $preroth; 14561 } // height before rotation 14562 if ($rot_bpos !== false) { 14563 $adjh = -$prerotw + $preroth; 14564 } else { 14565 $adjh = 0; 14566 } 14567 } else { 14568 if ($rot_rpos !== false) { 14569 $adjw = $prerotw - $preroth; 14570 } else { 14571 $adjw = 0; 14572 } 14573 if ($rot_bpos !== false) { 14574 $adjh = $preroth; 14575 } // height before rotation 14576 else { 14577 $adjh = $prerotw; 14578 } // width before rotation 14579 } 14580 $rot_start .= $this->transformTranslate($adjw, $adjh, true) . "\n"; 14581 $rot_start .= $this->transformRotate($rotate, $bbox_x, $bbox_y, true) . "\n"; 14582 $rot_end = " Q\n"; 14583 } elseif ($rotate == 180) { // mPDF 6 14584 $rot_start = " q\n"; 14585 $rot_start .= $this->transformTranslate($bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br, $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb, true) . "\n"; 14586 $rot_start .= $this->transformRotate(180, $bbox_x, $bbox_y, true) . "\n"; 14587 $rot_end = " Q\n"; 14588 } else { 14589 $rot_start = ''; 14590 $rot_end = ''; 14591 } 14592 14593 // ================================================================ 14594 if (!empty($bounding)) { 14595 // WHEN HEIGHT // BOTTOM EDGE IS KNOWN and $this->y is set to the bottom 14596 // Re-instate saved $this->blk[1] 14597 $this->blk[1] = $saved_block1; 14598 14599 // These are only needed when painting border/background 14600 $this->blk[1]['width'] = $bbox_w = $cont_w - $bbox_left - $bbox_ml - $bbox_mr - $bbox_right; 14601 $this->blk[1]['x0'] = $bbox_x; 14602 $this->blk[1]['y0'] = $bbox_y; 14603 $this->blk[1]['startpage'] = $this->page; 14604 $this->blk[1]['y1'] = $bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb; 14605 $this->writer->write($rot_start); 14606 $this->PaintDivBB('', 0, 1); // Prints borders and sets backgrounds in $this->pageBackgrounds 14607 $this->writer->write($rot_end); 14608 } 14609 14610 $s = $this->PrintPageBackgrounds(); 14611 $s = $rot_start . $s . $rot_end; 14612 $this->pages[$this->page] = preg_replace('/___BEFORE_BORDERS___/', "\n" . $s . "\n", $this->pages[$this->page]); 14613 $this->pageBackgrounds = []; 14614 14615 $this->writer->write($rot_start); 14616 14617 // Clipping Output 14618 if ($overflow == 'hidden') { 14619 // Bounding rectangle to clip 14620 $clip_y1 = $this->y; 14621 if (!empty($bounding) && ($this->y + $bbox_pb + $bbox_bb) > ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb )) { 14622 $clip_y1 = ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb ) - ($bbox_pb + $bbox_bb); 14623 } 14624 // $op = 'W* n'; // Clipping 14625 $op = 'W n'; // Clipping alternative mode 14626 $this->writer->write("q"); 14627 $ch = $clip_y1 - $y; 14628 $this->writer->write(sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$ch * Mpdf::SCALE, $op)); 14629 if (!empty($block_s)) { 14630 $tmp = "q\n" . sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$ch * Mpdf::SCALE, $op); 14631 $tmp .= "\n" . $block_s . "\nQ"; 14632 $block_s = $tmp; 14633 } 14634 } 14635 14636 14637 if (!empty($block_s)) { 14638 if ($shrink_f != 1) { // i.e. autofit has resized the box 14639 $tmp = "q\n" . $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y, true); 14640 $tmp .= "\n" . $block_s . "\nQ"; 14641 $block_s = $tmp; 14642 } 14643 $this->writer->write($block_s); 14644 } 14645 14646 14647 14648 if ($shrink_f != 1) { // i.e. autofit has resized the box 14649 $this->StartTransform(); 14650 $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y); 14651 } 14652 14653 $this->writer->write($this->headerbuffer); 14654 14655 if ($shrink_f != 1) { // i.e. autofit has resized the box 14656 $this->StopTransform(); 14657 } 14658 14659 if ($overflow == 'hidden') { 14660 // End clipping 14661 $this->writer->write("Q"); 14662 } 14663 14664 $this->writer->write($rot_end); 14665 14666 14667 // Page Links 14668 foreach ($this->HTMLheaderPageLinks as $lk) { 14669 if ($rotate) { 14670 $tmp = $lk[2]; // Switch h - w 14671 $lk[2] = $lk[3]; 14672 $lk[3] = $tmp; 14673 14674 $lx1 = (($lk[0] / Mpdf::SCALE)); 14675 $ly1 = (($this->h - ($lk[1] / Mpdf::SCALE))); 14676 if ($rotate == 90) { 14677 $adjx = -($lx1 - $bbox_x) + ($preroth - ($ly1 - $bbox_y)); 14678 $adjy = -($ly1 - $bbox_y) + ($lx1 - $bbox_x); 14679 $lk[2] = -$lk[2]; 14680 } elseif ($rotate == -90) { 14681 $adjx = -($lx1 - $bbox_x) + ($ly1 - $bbox_y); 14682 $adjy = -($ly1 - $bbox_y) - ($lx1 - $bbox_x) + $prerotw; 14683 $lk[3] = -$lk[3]; 14684 } 14685 if ($rot_rpos !== false) { 14686 $adjx += $prerotw - $preroth; 14687 } 14688 if ($rot_bpos !== false) { 14689 $adjy += $preroth - $prerotw; 14690 } 14691 $lx1 += $adjx; 14692 $ly1 += $adjy; 14693 14694 $lk[0] = $lx1 * Mpdf::SCALE; 14695 $lk[1] = ($this->h - $ly1) * Mpdf::SCALE; 14696 } 14697 if ($shrink_f != 1) { // i.e. autofit has resized the box 14698 $lx1 = (($lk[0] / Mpdf::SCALE) - $x); 14699 $lx2 = $x + ($lx1 * $shrink_f); 14700 $lk[0] = $lx2 * Mpdf::SCALE; 14701 $ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)) - $y); 14702 $ly2 = $y + ($ly1 * $shrink_f); 14703 $lk[1] = ($this->h - $ly2) * Mpdf::SCALE; 14704 $lk[2] *= $shrink_f; // width 14705 $lk[3] *= $shrink_f; // height 14706 } 14707 $this->PageLinks[$this->page][] = $lk; 14708 } 14709 14710 foreach ($this->HTMLheaderPageForms as $n => $f) { 14711 if ($shrink_f != 1) { // i.e. autofit has resized the box 14712 $f['x'] = $x + (($f['x'] - $x) * $shrink_f); 14713 $f['y'] = $y + (($f['y'] - $y) * $shrink_f); 14714 $f['w'] *= $shrink_f; 14715 $f['h'] *= $shrink_f; 14716 $f['style']['fontsize'] *= $shrink_f; 14717 } 14718 $this->form->forms[$f['n']] = $f; 14719 } 14720 // Page Annotations 14721 foreach ($this->HTMLheaderPageAnnots as $lk) { 14722 if ($rotate) { 14723 if ($rotate == 90) { 14724 $adjx = -($lk['x'] - $bbox_x) + ($preroth - ($lk['y'] - $bbox_y)); 14725 $adjy = -($lk['y'] - $bbox_y) + ($lk['x'] - $bbox_x); 14726 } elseif ($rotate == -90) { 14727 $adjx = -($lk['x'] - $bbox_x) + ($lk['y'] - $bbox_y); 14728 $adjy = -($lk['y'] - $bbox_y) - ($lk['x'] - $bbox_x) + $prerotw; 14729 } 14730 if ($rot_rpos !== false) { 14731 $adjx += $prerotw - $preroth; 14732 } 14733 if ($rot_bpos !== false) { 14734 $adjy += $preroth - $prerotw; 14735 } 14736 $lk['x'] += $adjx; 14737 $lk['y'] += $adjy; 14738 } 14739 if ($shrink_f != 1) { // i.e. autofit has resized the box 14740 $lk['x'] = $x + (($lk['x'] - $x) * $shrink_f); 14741 $lk['y'] = $y + (($lk['y'] - $y) * $shrink_f); 14742 } 14743 $this->PageAnnots[$this->page][] = $lk; 14744 } 14745 14746 // Restore 14747 $this->headerbuffer = ''; 14748 $this->HTMLheaderPageLinks = []; 14749 $this->HTMLheaderPageAnnots = []; 14750 $this->HTMLheaderPageForms = []; 14751 $this->pageBackgrounds = $save_bgs; 14752 $this->writingHTMLheader = false; 14753 14754 $this->writingHTMLfooter = false; 14755 $this->fullImageHeight = false; 14756 $this->ResetMargins(); 14757 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 14758 $this->SetXY($save_x, $save_y); 14759 $this->title2annots = $save_annots; // *ANNOTATIONS* 14760 $this->InFooter = false; // turns back on autopagebreaks 14761 $this->pageoutput[$this->page] = []; 14762 $this->pageoutput[$this->page]['Font'] = ''; 14763 /* -- COLUMNS -- */ 14764 if ($save_cols) { 14765 $this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap); 14766 } 14767 /* -- END COLUMNS -- */ 14768 } 14769 14770 /* -- END CSS-POSITION -- */ 14771 14772 function initialiseBlock(&$blk) 14773 { 14774 $blk['margin_top'] = 0; 14775 $blk['margin_left'] = 0; 14776 $blk['margin_bottom'] = 0; 14777 $blk['margin_right'] = 0; 14778 $blk['padding_top'] = 0; 14779 $blk['padding_left'] = 0; 14780 $blk['padding_bottom'] = 0; 14781 $blk['padding_right'] = 0; 14782 $blk['border_top']['w'] = 0; 14783 $blk['border_left']['w'] = 0; 14784 $blk['border_bottom']['w'] = 0; 14785 $blk['border_right']['w'] = 0; 14786 $blk['direction'] = 'ltr'; 14787 $blk['hide'] = false; 14788 $blk['outer_left_margin'] = 0; 14789 $blk['outer_right_margin'] = 0; 14790 $blk['cascadeCSS'] = []; 14791 $blk['block-align'] = false; 14792 $blk['bgcolor'] = false; 14793 $blk['page_break_after_avoid'] = false; 14794 $blk['keep_block_together'] = false; 14795 $blk['float'] = false; 14796 $blk['line_height'] = ''; 14797 $blk['margin_collapse'] = false; 14798 } 14799 14800 function border_details($bd) 14801 { 14802 $prop = preg_split('/\s+/', trim($bd)); 14803 14804 if (isset($this->blk[$this->blklvl]['inner_width'])) { 14805 $refw = $this->blk[$this->blklvl]['inner_width']; 14806 } elseif (isset($this->blk[$this->blklvl - 1]['inner_width'])) { 14807 $refw = $this->blk[$this->blklvl - 1]['inner_width']; 14808 } else { 14809 $refw = $this->w; 14810 } 14811 if (count($prop) == 1) { 14812 $bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false); 14813 if ($bsize > 0) { 14814 return ['s' => 1, 'w' => $bsize, 'c' => $this->colorConverter->convert(0, $this->PDFAXwarnings), 'style' => 'solid']; 14815 } else { 14816 return ['w' => 0, 's' => 0]; 14817 } 14818 } elseif (count($prop) == 2) { 14819 // 1px solid 14820 if (in_array($prop[1], $this->borderstyles) || $prop[1] == 'none' || $prop[1] == 'hidden') { 14821 $prop[2] = ''; 14822 } // solid #000000 14823 elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') { 14824 $prop[0] = ''; 14825 $prop[1] = $prop[0]; 14826 $prop[2] = $prop[1]; 14827 } // 1px #000000 14828 else { 14829 $prop[1] = ''; 14830 $prop[2] = $prop[1]; 14831 } 14832 } elseif (count($prop) == 3) { 14833 // Change #000000 1px solid to 1px solid #000000 (proper) 14834 if (substr($prop[0], 0, 1) == '#') { 14835 $tmp = $prop[0]; 14836 $prop[0] = $prop[1]; 14837 $prop[1] = $prop[2]; 14838 $prop[2] = $tmp; 14839 } // Change solid #000000 1px to 1px solid #000000 (proper) 14840 elseif (substr($prop[0], 1, 1) == '#') { 14841 $tmp = $prop[1]; 14842 $prop[0] = $prop[2]; 14843 $prop[1] = $prop[0]; 14844 $prop[2] = $tmp; 14845 } // Change solid 1px #000000 to 1px solid #000000 (proper) 14846 elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') { 14847 $tmp = $prop[0]; 14848 $prop[0] = $prop[1]; 14849 $prop[1] = $tmp; 14850 } 14851 } else { 14852 return ['w' => 0, 's' => 0]; 14853 } 14854 // Size 14855 $bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false); 14856 // color 14857 $coul = $this->colorConverter->convert($prop[2], $this->PDFAXwarnings); // returns array 14858 // Style 14859 $prop[1] = strtolower($prop[1]); 14860 if (in_array($prop[1], $this->borderstyles) && $bsize > 0) { 14861 $on = 1; 14862 } elseif ($prop[1] == 'hidden') { 14863 $on = 1; 14864 $bsize = 0; 14865 $coul = ''; 14866 } elseif ($prop[1] == 'none') { 14867 $on = 0; 14868 $bsize = 0; 14869 $coul = ''; 14870 } else { 14871 $on = 0; 14872 $bsize = 0; 14873 $coul = ''; 14874 $prop[1] = ''; 14875 } 14876 return ['s' => $on, 'w' => $bsize, 'c' => $coul, 'style' => $prop[1], 'dom' => 0]; 14877 } 14878 14879 /* -- END HTML-CSS -- */ 14880 14881 14882 /* -- BORDER-RADIUS -- */ 14883 14884 function _borderPadding($a, $b, &$px, &$py) 14885 { 14886 // $px and py are padding long axis (x) and short axis (y) 14887 $added = 0; // extra padding 14888 14889 $x = $a - $px; 14890 $y = $b - $py; 14891 // Check if Falls within ellipse of border radius 14892 if (( (($x + $added) * ($x + $added)) / ($a * $a) + (($y + $added) * ($y + $added)) / ($b * $b) ) <= 1) { 14893 return false; 14894 } 14895 14896 $t = atan2($y, $x); 14897 14898 $newx = $b / sqrt((($b * $b) / ($a * $a)) + ( tan($t) * tan($t) )); 14899 $newy = $a / sqrt((($a * $a) / ($b * $b)) + ( (1 / tan($t)) * (1 / tan($t)) )); 14900 $px = max($px, $a - $newx + $added); 14901 $py = max($py, $b - $newy + $added); 14902 } 14903 14904 /* -- END BORDER-RADIUS -- */ 14905 /* -- HTML-CSS -- */ 14906 /* -- CSS-PAGE -- */ 14907 14908 function SetPagedMediaCSS($name, $first, $oddEven) 14909 { 14910 if ($oddEven == 'E') { 14911 if ($this->directionality == 'rtl') { 14912 $side = 'R'; 14913 } else { 14914 $side = 'L'; 14915 } 14916 } else { 14917 if ($this->directionality == 'rtl') { 14918 $side = 'L'; 14919 } else { 14920 $side = 'R'; 14921 } 14922 } 14923 $name = strtoupper($name); 14924 $p = []; 14925 $p['SIZE'] = 'AUTO'; 14926 14927 // Uses mPDF original margins as default 14928 $p['MARGIN-RIGHT'] = strval($this->orig_rMargin) . 'mm'; 14929 $p['MARGIN-LEFT'] = strval($this->orig_lMargin) . 'mm'; 14930 $p['MARGIN-TOP'] = strval($this->orig_tMargin) . 'mm'; 14931 $p['MARGIN-BOTTOM'] = strval($this->orig_bMargin) . 'mm'; 14932 $p['MARGIN-HEADER'] = strval($this->orig_hMargin) . 'mm'; 14933 $p['MARGIN-FOOTER'] = strval($this->orig_fMargin) . 'mm'; 14934 14935 // Basic page + selector 14936 if (isset($this->cssManager->CSS['@PAGE'])) { 14937 $zp = $this->cssManager->CSS['@PAGE']; 14938 } else { 14939 $zp = []; 14940 } 14941 if (is_array($zp) && !empty($zp)) { 14942 $p = array_merge($p, $zp); 14943 } 14944 14945 if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') { 14946 $p['HEADER'] = $p['EVEN-HEADER-NAME']; 14947 unset($p['EVEN-HEADER-NAME']); 14948 } 14949 if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') { 14950 $p['HEADER'] = $p['ODD-HEADER-NAME']; 14951 unset($p['ODD-HEADER-NAME']); 14952 } 14953 if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') { 14954 $p['FOOTER'] = $p['EVEN-FOOTER-NAME']; 14955 unset($p['EVEN-FOOTER-NAME']); 14956 } 14957 if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') { 14958 $p['FOOTER'] = $p['ODD-FOOTER-NAME']; 14959 unset($p['ODD-FOOTER-NAME']); 14960 } 14961 14962 // If right/Odd page 14963 if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT']) && $side == 'R') { 14964 $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT']; 14965 } else { 14966 $zp = []; 14967 } 14968 if (isset($zp['SIZE'])) { 14969 unset($zp['SIZE']); 14970 } 14971 if (isset($zp['SHEET-SIZE'])) { 14972 unset($zp['SHEET-SIZE']); 14973 } 14974 // Disallow margin-left or -right on :LEFT or :RIGHT 14975 if (isset($zp['MARGIN-LEFT'])) { 14976 unset($zp['MARGIN-LEFT']); 14977 } 14978 if (isset($zp['MARGIN-RIGHT'])) { 14979 unset($zp['MARGIN-RIGHT']); 14980 } 14981 if (is_array($zp) && !empty($zp)) { 14982 $p = array_merge($p, $zp); 14983 } 14984 14985 // If left/Even page 14986 if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT']) && $side == 'L') { 14987 $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT']; 14988 } else { 14989 $zp = []; 14990 } 14991 if (isset($zp['SIZE'])) { 14992 unset($zp['SIZE']); 14993 } 14994 if (isset($zp['SHEET-SIZE'])) { 14995 unset($zp['SHEET-SIZE']); 14996 } 14997 // Disallow margin-left or -right on :LEFT or :RIGHT 14998 if (isset($zp['MARGIN-LEFT'])) { 14999 unset($zp['MARGIN-LEFT']); 15000 } 15001 if (isset($zp['MARGIN-RIGHT'])) { 15002 unset($zp['MARGIN-RIGHT']); 15003 } 15004 if (is_array($zp) && !empty($zp)) { 15005 $p = array_merge($p, $zp); 15006 } 15007 15008 // If first page 15009 if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']) && $first) { 15010 $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']; 15011 } else { 15012 $zp = []; 15013 } 15014 if (isset($zp['SIZE'])) { 15015 unset($zp['SIZE']); 15016 } 15017 if (isset($zp['SHEET-SIZE'])) { 15018 unset($zp['SHEET-SIZE']); 15019 } 15020 // Disallow margin-left or -right on :FIRST // mPDF 5.7.3 15021 if (isset($zp['MARGIN-LEFT'])) { 15022 unset($zp['MARGIN-LEFT']); 15023 } 15024 if (isset($zp['MARGIN-RIGHT'])) { 15025 unset($zp['MARGIN-RIGHT']); 15026 } 15027 if (is_array($zp) && !empty($zp)) { 15028 $p = array_merge($p, $zp); 15029 } 15030 15031 // If named page 15032 if ($name) { 15033 if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name])) { 15034 $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name]; 15035 } else { 15036 $zp = []; 15037 } 15038 if (is_array($zp) && !empty($zp)) { 15039 $p = array_merge($p, $zp); 15040 } 15041 15042 if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') { 15043 $p['HEADER'] = $p['EVEN-HEADER-NAME']; 15044 unset($p['EVEN-HEADER-NAME']); 15045 } 15046 if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') { 15047 $p['HEADER'] = $p['ODD-HEADER-NAME']; 15048 unset($p['ODD-HEADER-NAME']); 15049 } 15050 if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') { 15051 $p['FOOTER'] = $p['EVEN-FOOTER-NAME']; 15052 unset($p['EVEN-FOOTER-NAME']); 15053 } 15054 if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') { 15055 $p['FOOTER'] = $p['ODD-FOOTER-NAME']; 15056 unset($p['ODD-FOOTER-NAME']); 15057 } 15058 15059 // If named right/Odd page 15060 if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT']) && $side == 'R') { 15061 $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT']; 15062 } else { 15063 $zp = []; 15064 } 15065 if (isset($zp['SIZE'])) { 15066 unset($zp['SIZE']); 15067 } 15068 if (isset($zp['SHEET-SIZE'])) { 15069 unset($zp['SHEET-SIZE']); 15070 } 15071 // Disallow margin-left or -right on :LEFT or :RIGHT 15072 if (isset($zp['MARGIN-LEFT'])) { 15073 unset($zp['MARGIN-LEFT']); 15074 } 15075 if (isset($zp['MARGIN-RIGHT'])) { 15076 unset($zp['MARGIN-RIGHT']); 15077 } 15078 if (is_array($zp) && !empty($zp)) { 15079 $p = array_merge($p, $zp); 15080 } 15081 15082 // If named left/Even page 15083 if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT']) && $side == 'L') { 15084 $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT']; 15085 } else { 15086 $zp = []; 15087 } 15088 if (isset($zp['SIZE'])) { 15089 unset($zp['SIZE']); 15090 } 15091 if (isset($zp['SHEET-SIZE'])) { 15092 unset($zp['SHEET-SIZE']); 15093 } 15094 // Disallow margin-left or -right on :LEFT or :RIGHT 15095 if (isset($zp['MARGIN-LEFT'])) { 15096 unset($zp['MARGIN-LEFT']); 15097 } 15098 if (isset($zp['MARGIN-RIGHT'])) { 15099 unset($zp['MARGIN-RIGHT']); 15100 } 15101 if (is_array($zp) && !empty($zp)) { 15102 $p = array_merge($p, $zp); 15103 } 15104 15105 // If named first page 15106 if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST']) && $first) { 15107 $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST']; 15108 } else { 15109 $zp = []; 15110 } 15111 if (isset($zp['SIZE'])) { 15112 unset($zp['SIZE']); 15113 } 15114 if (isset($zp['SHEET-SIZE'])) { 15115 unset($zp['SHEET-SIZE']); 15116 } 15117 // Disallow margin-left or -right on :FIRST // mPDF 5.7.3 15118 if (isset($zp['MARGIN-LEFT'])) { 15119 unset($zp['MARGIN-LEFT']); 15120 } 15121 if (isset($zp['MARGIN-RIGHT'])) { 15122 unset($zp['MARGIN-RIGHT']); 15123 } 15124 if (is_array($zp) && !empty($zp)) { 15125 $p = array_merge($p, $zp); 15126 } 15127 } 15128 15129 $orientation = $mgl = $mgr = $mgt = $mgb = $mgh = $mgf = ''; 15130 $header = $footer = ''; 15131 $resetpagenum = $pagenumstyle = $suppress = ''; 15132 $marks = ''; 15133 $bg = []; 15134 15135 $newformat = ''; 15136 15137 15138 if (isset($p['SHEET-SIZE']) && is_array($p['SHEET-SIZE'])) { 15139 $newformat = $p['SHEET-SIZE']; 15140 if ($newformat[0] > $newformat[1]) { // landscape 15141 $newformat = array_reverse($newformat); 15142 $p['ORIENTATION'] = 'L'; 15143 } else { 15144 $p['ORIENTATION'] = 'P'; 15145 } 15146 $this->_setPageSize($newformat, $p['ORIENTATION']); 15147 } 15148 15149 if (isset($p['SIZE']) && is_array($p['SIZE']) && !$newformat) { 15150 if ($p['SIZE']['W'] > $p['SIZE']['H']) { 15151 $p['ORIENTATION'] = 'L'; 15152 } else { 15153 $p['ORIENTATION'] = 'P'; 15154 } 15155 } 15156 if (is_array($p['SIZE'])) { 15157 if ($p['SIZE']['W'] > $this->fw) { 15158 $p['SIZE']['W'] = $this->fw; 15159 } // mPD 4.2 use fw not fPt 15160 if ($p['SIZE']['H'] > $this->fh) { 15161 $p['SIZE']['H'] = $this->fh; 15162 } 15163 if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) { 15164 $outer_width_LR = ($this->fw - $p['SIZE']['W']) / 2; 15165 $outer_width_TB = ($this->fh - $p['SIZE']['H']) / 2; 15166 } else { 15167 $outer_width_LR = ($this->fh - $p['SIZE']['W']) / 2; 15168 $outer_width_TB = ($this->fw - $p['SIZE']['H']) / 2; 15169 } 15170 $pgw = $p['SIZE']['W']; 15171 $pgh = $p['SIZE']['H']; 15172 } else { // AUTO LANDSCAPE PORTRAIT 15173 $outer_width_LR = 0; 15174 $outer_width_TB = 0; 15175 if (!$newformat) { 15176 if (strtoupper($p['SIZE']) == 'AUTO') { 15177 $p['ORIENTATION'] = $this->DefOrientation; 15178 } elseif (strtoupper($p['SIZE']) == 'LANDSCAPE') { 15179 $p['ORIENTATION'] = 'L'; 15180 } else { 15181 $p['ORIENTATION'] = 'P'; 15182 } 15183 } 15184 if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) { 15185 $pgw = $this->fw; 15186 $pgh = $this->fh; 15187 } else { 15188 $pgw = $this->fh; 15189 $pgh = $this->fw; 15190 } 15191 } 15192 15193 if (isset($p['HEADER']) && $p['HEADER']) { 15194 $header = $p['HEADER']; 15195 } 15196 if (isset($p['FOOTER']) && $p['FOOTER']) { 15197 $footer = $p['FOOTER']; 15198 } 15199 if (isset($p['RESETPAGENUM']) && $p['RESETPAGENUM']) { 15200 $resetpagenum = $p['RESETPAGENUM']; 15201 } 15202 if (isset($p['PAGENUMSTYLE']) && $p['PAGENUMSTYLE']) { 15203 $pagenumstyle = $p['PAGENUMSTYLE']; 15204 } 15205 if (isset($p['SUPPRESS']) && $p['SUPPRESS']) { 15206 $suppress = $p['SUPPRESS']; 15207 } 15208 15209 if (isset($p['MARKS'])) { 15210 if (preg_match('/cross/i', $p['MARKS']) && preg_match('/crop/i', $p['MARKS'])) { 15211 $marks = 'CROPCROSS'; 15212 } elseif (strtoupper($p['MARKS']) == 'CROP') { 15213 $marks = 'CROP'; 15214 } elseif (strtoupper($p['MARKS']) == 'CROSS') { 15215 $marks = 'CROSS'; 15216 } 15217 } 15218 15219 if (isset($p['BACKGROUND-COLOR']) && $p['BACKGROUND-COLOR']) { 15220 $bg['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR']; 15221 } 15222 /* -- BACKGROUNDS -- */ 15223 if (isset($p['BACKGROUND-GRADIENT']) && $p['BACKGROUND-GRADIENT']) { 15224 $bg['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT']; 15225 } 15226 if (isset($p['BACKGROUND-IMAGE']) && $p['BACKGROUND-IMAGE']) { 15227 $bg['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE']; 15228 } 15229 if (isset($p['BACKGROUND-REPEAT']) && $p['BACKGROUND-REPEAT']) { 15230 $bg['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT']; 15231 } 15232 if (isset($p['BACKGROUND-POSITION']) && $p['BACKGROUND-POSITION']) { 15233 $bg['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION']; 15234 } 15235 if (isset($p['BACKGROUND-IMAGE-RESIZE']) && $p['BACKGROUND-IMAGE-RESIZE']) { 15236 $bg['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE']; 15237 } 15238 if (isset($p['BACKGROUND-IMAGE-OPACITY'])) { 15239 $bg['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY']; 15240 } 15241 /* -- END BACKGROUNDS -- */ 15242 15243 if (isset($p['MARGIN-LEFT'])) { 15244 $mgl = $this->sizeConverter->convert($p['MARGIN-LEFT'], $pgw) + $outer_width_LR; 15245 } 15246 if (isset($p['MARGIN-RIGHT'])) { 15247 $mgr = $this->sizeConverter->convert($p['MARGIN-RIGHT'], $pgw) + $outer_width_LR; 15248 } 15249 if (isset($p['MARGIN-BOTTOM'])) { 15250 $mgb = $this->sizeConverter->convert($p['MARGIN-BOTTOM'], $pgh) + $outer_width_TB; 15251 } 15252 if (isset($p['MARGIN-TOP'])) { 15253 $mgt = $this->sizeConverter->convert($p['MARGIN-TOP'], $pgh) + $outer_width_TB; 15254 } 15255 if (isset($p['MARGIN-HEADER'])) { 15256 $mgh = $this->sizeConverter->convert($p['MARGIN-HEADER'], $pgh) + $outer_width_TB; 15257 } 15258 if (isset($p['MARGIN-FOOTER'])) { 15259 $mgf = $this->sizeConverter->convert($p['MARGIN-FOOTER'], $pgh) + $outer_width_TB; 15260 } 15261 15262 if (isset($p['ORIENTATION']) && $p['ORIENTATION']) { 15263 $orientation = $p['ORIENTATION']; 15264 } 15265 $this->page_box['outer_width_LR'] = $outer_width_LR; // Used in MARKS:crop etc. 15266 $this->page_box['outer_width_TB'] = $outer_width_TB; 15267 15268 return [$orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $header, $footer, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat]; 15269 } 15270 15271 /* -- END CSS-PAGE -- */ 15272 15273 15274 15275 /* -- CSS-FLOAT -- */ 15276 15277 // Added mPDF 3.0 Float DIV - CLEAR 15278 function ClearFloats($clear, $blklvl = 0) 15279 { 15280 list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($blklvl, true); 15281 $end = $currpos = ($this->page * 1000 + $this->y); 15282 if ($clear == 'BOTH' && ($l_exists || $r_exists)) { 15283 $this->pageoutput[$this->page] = []; 15284 $end = max($l_max, $r_max, $currpos); 15285 } elseif ($clear == 'RIGHT' && $r_exists) { 15286 $this->pageoutput[$this->page] = []; 15287 $end = max($r_max, $currpos); 15288 } elseif ($clear == 'LEFT' && $l_exists) { 15289 $this->pageoutput[$this->page] = []; 15290 $end = max($l_max, $currpos); 15291 } else { 15292 return; 15293 } 15294 $old_page = $this->page; 15295 $new_page = intval($end / 1000); 15296 if ($old_page != $new_page) { 15297 $s = $this->PrintPageBackgrounds(); 15298 // Writes after the marker so not overwritten later by page background etc. 15299 $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]); 15300 $this->pageBackgrounds = []; 15301 $this->page = $new_page; 15302 } 15303 $this->ResetMargins(); 15304 $this->pageoutput[$this->page] = []; 15305 15306 $this->y = (round($end * 1000) % 1000000) / 1000; // mod changes operands to integers before processing 15307 } 15308 15309 // Added mPDF 3.0 Float DIV 15310 function GetFloatDivInfo($blklvl = 0, $clear = false) 15311 { 15312 // If blklvl specified, only returns floats at that level - for ClearFloats 15313 $l_exists = false; 15314 $r_exists = false; 15315 $l_max = 0; 15316 $r_max = 0; 15317 $l_width = 0; 15318 $r_width = 0; 15319 if (count($this->floatDivs)) { 15320 $currpos = ($this->page * 1000 + $this->y); 15321 foreach ($this->floatDivs as $f) { 15322 if (($clear && $f['blockContext'] == $this->blk[$blklvl]['blockContext']) || (!$clear && $currpos >= $f['startpos'] && $currpos < ($f['endpos'] - 0.001) && $f['blklvl'] > $blklvl && $f['blockContext'] == $this->blk[$blklvl]['blockContext'])) { 15323 if ($f['side'] == 'L') { 15324 $l_exists = true; 15325 $l_max = max($l_max, $f['endpos']); 15326 $l_width = max($l_width, $f['w']); 15327 } 15328 if ($f['side'] == 'R') { 15329 $r_exists = true; 15330 $r_max = max($r_max, $f['endpos']); 15331 $r_width = max($r_width, $f['w']); 15332 } 15333 } 15334 } 15335 } 15336 return [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width]; 15337 } 15338 15339 /* -- END CSS-FLOAT -- */ 15340 15341 // LIST MARKERS // mPDF 6 Lists 15342 function _setListMarker($listitemtype, $listitemimage, $listitemposition) 15343 { 15344 // if position:inside (and NOT table) - output now as a textbuffer; (so if next is block, will move to new line) 15345 // elseif position:outside (and NOT table) - output in front of first textbuffer output by setting listitem (cf. _saveTextBuffer) 15346 $e = ''; 15347 $this->listitem = ''; 15348 $spacer = ' '; 15349 // IMAGE 15350 if ($listitemimage && $listitemimage != 'none') { 15351 $listitemimage = trim(preg_replace('/url\(["\']*(.*?)["\']*\)/', '\\1', $listitemimage)); 15352 15353 // ? Restrict maximum height/width of list marker?? 15354 $maxWidth = 100; 15355 $maxHeight = 100; 15356 15357 $objattr = []; 15358 $objattr['margin_top'] = 0; 15359 $objattr['margin_bottom'] = 0; 15360 $objattr['margin_left'] = 0; 15361 $objattr['margin_right'] = 0; 15362 $objattr['padding_top'] = 0; 15363 $objattr['padding_bottom'] = 0; 15364 $objattr['padding_left'] = 0; 15365 $objattr['padding_right'] = 0; 15366 $objattr['width'] = 0; 15367 $objattr['height'] = 0; 15368 $objattr['border_top']['w'] = 0; 15369 $objattr['border_bottom']['w'] = 0; 15370 $objattr['border_left']['w'] = 0; 15371 $objattr['border_right']['w'] = 0; 15372 $objattr['visibility'] = 'visible'; 15373 $srcpath = $listitemimage; 15374 $orig_srcpath = $listitemimage; 15375 15376 $objattr['vertical-align'] = 'BS'; // vertical alignment of marker (baseline) 15377 $w = 0; 15378 $h = 0; 15379 15380 // Image file 15381 $info = $this->imageProcessor->getImage($srcpath, true, true, $orig_srcpath); 15382 if (!$info) { 15383 return; 15384 } 15385 15386 if ($info['w'] == 0 && $info['h'] == 0) { 15387 $info['h'] = $this->sizeConverter->convert('1em', $this->blk[$this->blklvl]['inner_width'], $this->FontSize, false); 15388 } 15389 15390 $objattr['file'] = $srcpath; 15391 15392 // Default width and height calculation if needed 15393 if ($w == 0 and $h == 0) { 15394 /* -- IMAGES-WMF -- */ 15395 if ($info['type'] == 'wmf') { 15396 // WMF units are twips (1/20pt) 15397 // divide by 20 to get points 15398 // divide by k to get user units 15399 $w = abs($info['w']) / (20 * Mpdf::SCALE); 15400 $h = abs($info['h']) / (20 * Mpdf::SCALE); 15401 } else { /* -- END IMAGES-WMF -- */ 15402 if ($info['type'] == 'svg') { 15403 // SVG units are pixels 15404 $w = abs($info['w']) / Mpdf::SCALE; 15405 $h = abs($info['h']) / Mpdf::SCALE; 15406 } else { 15407 // Put image at default image dpi 15408 $w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi); 15409 $h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi); 15410 } 15411 } 15412 } 15413 // IF WIDTH OR HEIGHT SPECIFIED 15414 if ($w == 0) { 15415 $w = abs($h * $info['w'] / $info['h']); 15416 } 15417 if ($h == 0) { 15418 $h = abs($w * $info['h'] / $info['w']); 15419 } 15420 15421 if ($w > $maxWidth) { 15422 $w = $maxWidth; 15423 $h = abs($w * $info['h'] / $info['w']); 15424 } 15425 15426 if ($h > $maxHeight) { 15427 $h = $maxHeight; 15428 $w = abs($h * $info['w'] / $info['h']); 15429 } 15430 15431 $objattr['type'] = 'image'; 15432 $objattr['itype'] = $info['type']; 15433 15434 $objattr['orig_h'] = $info['h']; 15435 $objattr['orig_w'] = $info['w']; 15436 15437 /* -- IMAGES-WMF -- */ 15438 if ($info['type'] == 'wmf') { 15439 $objattr['wmf_x'] = $info['x']; 15440 $objattr['wmf_y'] = $info['y']; 15441 } else { /* -- END IMAGES-WMF -- */ 15442 if ($info['type'] == 'svg') { 15443 $objattr['wmf_x'] = $info['x']; 15444 $objattr['wmf_y'] = $info['y']; 15445 } 15446 } 15447 15448 $objattr['height'] = $h; 15449 $objattr['width'] = $w; 15450 $objattr['image_height'] = $h; 15451 $objattr['image_width'] = $w; 15452 15453 $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr'); 15454 $objattr['listmarker'] = true; 15455 15456 $objattr['listmarkerposition'] = $listitemposition; 15457 15458 $e = "\xbb\xa4\xactype=image,objattr=" . serialize($objattr) . "\xbb\xa4\xac"; 15459 $this->_saveTextBuffer($e); 15460 15461 if ($listitemposition == 'inside') { 15462 $e = $spacer; 15463 $this->_saveTextBuffer($e); 15464 } 15465 } elseif ($listitemtype == 'disc' || $listitemtype == 'circle' || $listitemtype == 'square') { // SYMBOL (needs new font) 15466 $objattr = []; 15467 $objattr['type'] = 'listmarker'; 15468 $objattr['listmarkerposition'] = $listitemposition; 15469 $objattr['width'] = 0; 15470 $size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize); 15471 $objattr['size'] = $size; 15472 $objattr['offset'] = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize); 15473 15474 if ($listitemposition == 'inside') { 15475 $objattr['width'] = $size + $objattr['offset']; 15476 } 15477 15478 $objattr['height'] = $this->FontSize; 15479 $objattr['vertical-align'] = 'T'; 15480 $objattr['text'] = ''; 15481 $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr'); 15482 $objattr['bullet'] = $listitemtype; 15483 $objattr['colorarray'] = $this->colorarray; 15484 $objattr['fontfamily'] = $this->FontFamily; 15485 $objattr['fontsize'] = $this->FontSize; 15486 $objattr['fontsizept'] = $this->FontSizePt; 15487 $objattr['fontstyle'] = $this->FontStyle; 15488 15489 $e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac"; 15490 $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array 15491 15492 } elseif (preg_match('/U\+([a-fA-F0-9]+)/i', $listitemtype, $m)) { // SYMBOL 2 (needs new font) 15493 15494 if ($this->_charDefined($this->CurrentFont['cw'], hexdec($m[1]))) { 15495 $list_item_marker = UtfString::codeHex2utf($m[1]); 15496 } else { 15497 $list_item_marker = '-'; 15498 } 15499 if (preg_match('/rgb\(.*?\)/', $listitemtype, $m)) { 15500 $list_item_color = $this->colorConverter->convert($m[0], $this->PDFAXwarnings); 15501 } else { 15502 $list_item_color = ''; 15503 } 15504 15505 // SAVE then SET COLR 15506 $save_colorarray = $this->colorarray; 15507 if ($list_item_color) { 15508 $this->colorarray = $list_item_color; 15509 } 15510 15511 if ($listitemposition == 'inside') { 15512 $e = $list_item_marker . $spacer; 15513 $this->_saveTextBuffer($e); 15514 } else { 15515 $objattr = []; 15516 $objattr['type'] = 'listmarker'; 15517 $objattr['width'] = 0; 15518 $objattr['height'] = $this->FontSize; 15519 $objattr['vertical-align'] = 'T'; 15520 $objattr['text'] = $list_item_marker; 15521 $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr'); 15522 $objattr['colorarray'] = $this->colorarray; 15523 $objattr['fontfamily'] = $this->FontFamily; 15524 $objattr['fontsize'] = $this->FontSize; 15525 $objattr['fontsizept'] = $this->FontSizePt; 15526 $objattr['fontstyle'] = $this->FontStyle; 15527 $e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac"; 15528 $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array 15529 } 15530 15531 // RESET COLOR 15532 $this->colorarray = $save_colorarray; 15533 15534 } else { // TEXT 15535 $counter = $this->listcounter[$this->listlvl]; 15536 15537 if ($listitemtype == 'none') { 15538 return; 15539 } 15540 15541 $num = $this->_getStyledNumber($counter, $listitemtype, true); 15542 15543 if ($listitemposition == 'inside') { 15544 $e = $num . $this->list_number_suffix . $spacer; 15545 $this->_saveTextBuffer($e); 15546 } else { 15547 if (isset($this->blk[$this->blklvl]['direction']) && $this->blk[$this->blklvl]['direction'] == 'rtl') { 15548 // REPLACE MIRRORED RTL $this->list_number_suffix e.g. ) -> ( (NB could use Ucdn::$mirror_pairs) 15549 $m = strtr($this->list_number_suffix, ")]}", "([{") . $num; 15550 } else { 15551 $m = $num . $this->list_number_suffix; 15552 } 15553 15554 $objattr = []; 15555 $objattr['type'] = 'listmarker'; 15556 $objattr['width'] = 0; 15557 $objattr['height'] = $this->FontSize; 15558 $objattr['vertical-align'] = 'T'; 15559 $objattr['text'] = $m; 15560 $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr'); 15561 $objattr['colorarray'] = $this->colorarray; 15562 $objattr['fontfamily'] = $this->FontFamily; 15563 $objattr['fontsize'] = $this->FontSize; 15564 $objattr['fontsizept'] = $this->FontSizePt; 15565 $objattr['fontstyle'] = $this->FontStyle; 15566 $e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac"; 15567 15568 $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array 15569 } 15570 } 15571 } 15572 15573 // mPDF Lists 15574 function _getListMarkerWidth(&$currblk, &$a, &$i) 15575 { 15576 $blt_width = 0; 15577 15578 $markeroffset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize); 15579 15580 // Get Maximum number in the list 15581 $maxnum = $this->listcounter[$this->listlvl]; 15582 if ($currblk['list_style_type'] != 'disc' && $currblk['list_style_type'] != 'circle' && $currblk['list_style_type'] != 'square') { 15583 $lvl = 1; 15584 for ($j = $i + 2; $j < count($a); $j+=2) { 15585 $e = $a[$j]; 15586 if (!$e) { 15587 continue; 15588 } 15589 if ($e[0] == '/') { // end tag 15590 $e = strtoupper(substr($e, 1)); 15591 if ($e == 'OL' || $e == 'UL') { 15592 if ($lvl == 1) { 15593 break; 15594 } 15595 $lvl--; 15596 } 15597 } else { // opening tag 15598 if (strpos($e, ' ')) { 15599 $e = substr($e, 0, strpos($e, ' ')); 15600 } 15601 $e = strtoupper($e); 15602 if ($e == 'LI') { 15603 if ($lvl == 1) { 15604 $maxnum++; 15605 } 15606 } elseif ($e == 'OL' || $e == 'UL') { 15607 $lvl++; 15608 } 15609 } 15610 } 15611 } 15612 15613 $decToAlpha = new Conversion\DecToAlpha(); 15614 $decToRoman = new Conversion\DecToRoman(); 15615 $decToOther = new Conversion\DecToOther($this); 15616 15617 switch ($currblk['list_style_type']) { 15618 case 'decimal': 15619 case '1': 15620 $blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix); 15621 break; 15622 case 'none': 15623 $blt_width = 0; 15624 break; 15625 case 'upper-alpha': 15626 case 'upper-latin': 15627 case 'A': 15628 $maxnumA = $decToAlpha->convert($maxnum, true); 15629 if ($maxnum < 13) { 15630 $blt_width = $this->GetStringWidth('D' . $this->list_number_suffix); 15631 } else { 15632 $blt_width = $this->GetStringWidth(str_repeat('W', strlen($maxnumA)) . $this->list_number_suffix); 15633 } 15634 break; 15635 case 'lower-alpha': 15636 case 'lower-latin': 15637 case 'a': 15638 $maxnuma = $decToAlpha->convert($maxnum, false); 15639 if ($maxnum < 13) { 15640 $blt_width = $this->GetStringWidth('b' . $this->list_number_suffix); 15641 } else { 15642 $blt_width = $this->GetStringWidth(str_repeat('m', strlen($maxnuma)) . $this->list_number_suffix); 15643 } 15644 break; 15645 case 'upper-roman': 15646 case 'I': 15647 if ($maxnum > 87) { 15648 $bbit = 87; 15649 } elseif ($maxnum > 86) { 15650 $bbit = 86; 15651 } elseif ($maxnum > 37) { 15652 $bbit = 38; 15653 } elseif ($maxnum > 36) { 15654 $bbit = 37; 15655 } elseif ($maxnum > 27) { 15656 $bbit = 28; 15657 } elseif ($maxnum > 26) { 15658 $bbit = 27; 15659 } elseif ($maxnum > 17) { 15660 $bbit = 18; 15661 } elseif ($maxnum > 16) { 15662 $bbit = 17; 15663 } elseif ($maxnum > 7) { 15664 $bbit = 8; 15665 } elseif ($maxnum > 6) { 15666 $bbit = 7; 15667 } elseif ($maxnum > 3) { 15668 $bbit = 4; 15669 } else { 15670 $bbit = $maxnum; 15671 } 15672 15673 $maxlnum = $decToRoman->convert($bbit, true); 15674 $blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix); 15675 15676 break; 15677 case 'lower-roman': 15678 case 'i': 15679 if ($maxnum > 87) { 15680 $bbit = 87; 15681 } elseif ($maxnum > 86) { 15682 $bbit = 86; 15683 } elseif ($maxnum > 37) { 15684 $bbit = 38; 15685 } elseif ($maxnum > 36) { 15686 $bbit = 37; 15687 } elseif ($maxnum > 27) { 15688 $bbit = 28; 15689 } elseif ($maxnum > 26) { 15690 $bbit = 27; 15691 } elseif ($maxnum > 17) { 15692 $bbit = 18; 15693 } elseif ($maxnum > 16) { 15694 $bbit = 17; 15695 } elseif ($maxnum > 7) { 15696 $bbit = 8; 15697 } elseif ($maxnum > 6) { 15698 $bbit = 7; 15699 } elseif ($maxnum > 3) { 15700 $bbit = 4; 15701 } else { 15702 $bbit = $maxnum; 15703 } 15704 $maxlnum = $decToRoman->convert($bbit, false); 15705 $blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix); 15706 break; 15707 15708 case 'disc': 15709 case 'circle': 15710 case 'square': 15711 $size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize); 15712 $offset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize); 15713 $blt_width = $size + $offset; 15714 break; 15715 15716 case 'arabic-indic': 15717 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0660), strlen($maxnum)) . $this->list_number_suffix); 15718 break; 15719 case 'persian': 15720 case 'urdu': 15721 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x06F0), strlen($maxnum)) . $this->list_number_suffix); 15722 break; 15723 case 'bengali': 15724 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x09E6), strlen($maxnum)) . $this->list_number_suffix); 15725 break; 15726 case 'devanagari': 15727 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0966), strlen($maxnum)) . $this->list_number_suffix); 15728 break; 15729 case 'gujarati': 15730 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0AE6), strlen($maxnum)) . $this->list_number_suffix); 15731 break; 15732 case 'gurmukhi': 15733 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0A66), strlen($maxnum)) . $this->list_number_suffix); 15734 break; 15735 case 'kannada': 15736 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0CE6), strlen($maxnum)) . $this->list_number_suffix); 15737 break; 15738 case 'malayalam': 15739 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(6, 0x0D66), strlen($maxnum)) . $this->list_number_suffix); 15740 break; 15741 case 'oriya': 15742 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0B66), strlen($maxnum)) . $this->list_number_suffix); 15743 break; 15744 case 'telugu': 15745 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0C66), strlen($maxnum)) . $this->list_number_suffix); 15746 break; 15747 case 'tamil': 15748 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(9, 0x0BE6), strlen($maxnum)) . $this->list_number_suffix); 15749 break; 15750 case 'thai': 15751 $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(5, 0x0E50), strlen($maxnum)) . $this->list_number_suffix); 15752 break; 15753 default: 15754 $blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix); 15755 break; 15756 } 15757 15758 return ($blt_width + $markeroffset); 15759 } 15760 15761 function _saveTextBuffer($t, $link = '', $intlink = '', $return = false) 15762 { 15763 // mPDF 6 Lists 15764 $arr = []; 15765 $arr[0] = $t; 15766 if (isset($link) && $link) { 15767 $arr[1] = $link; 15768 } 15769 $arr[2] = $this->currentfontstyle; 15770 if (isset($this->colorarray) && $this->colorarray) { 15771 $arr[3] = $this->colorarray; 15772 } 15773 $arr[4] = $this->currentfontfamily; 15774 $arr[5] = $this->currentLang; // mPDF 6 15775 if (isset($intlink) && $intlink) { 15776 $arr[7] = $intlink; 15777 } 15778 // mPDF 6 15779 // If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script, 15780 // set for kerning via kern table 15781 // e.g. Latin script when useOTL set as 0x80 15782 if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) { 15783 $this->textvar = ($this->textvar | TextVars::FC_KERNING); 15784 } 15785 $arr[8] = $this->textvar; // mPDF 5.7.1 15786 if (isset($this->textparam) && $this->textparam) { 15787 $arr[9] = $this->textparam; 15788 } 15789 if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) { 15790 $arr[10] = $this->spanbgcolorarray; 15791 } 15792 $arr[11] = $this->currentfontsize; 15793 if (isset($this->ReqFontStyle) && $this->ReqFontStyle) { 15794 $arr[12] = $this->ReqFontStyle; 15795 } 15796 if (isset($this->lSpacingCSS) && $this->lSpacingCSS) { 15797 $arr[14] = $this->lSpacingCSS; 15798 } 15799 if (isset($this->wSpacingCSS) && $this->wSpacingCSS) { 15800 $arr[15] = $this->wSpacingCSS; 15801 } 15802 if (isset($this->spanborddet) && $this->spanborddet) { 15803 $arr[16] = $this->spanborddet; 15804 } 15805 if (isset($this->textshadow) && $this->textshadow) { 15806 $arr[17] = $this->textshadow; 15807 } 15808 if (isset($this->OTLdata) && $this->OTLdata) { 15809 $arr[18] = $this->OTLdata; 15810 $this->OTLdata = []; 15811 } // mPDF 5.7.1 15812 else { 15813 $arr[18] = null; 15814 } 15815 // mPDF 6 Lists 15816 if ($return) { 15817 return ($arr); 15818 } 15819 if ($this->listitem) { 15820 $this->textbuffer[] = $this->listitem; 15821 $this->listitem = []; 15822 } 15823 $this->textbuffer[] = $arr; 15824 } 15825 15826 function _saveCellTextBuffer($t, $link = '', $intlink = '') 15827 { 15828 $arr = []; 15829 $arr[0] = $t; 15830 if (isset($link) && $link) { 15831 $arr[1] = $link; 15832 } 15833 $arr[2] = $this->currentfontstyle; 15834 if (isset($this->colorarray) && $this->colorarray) { 15835 $arr[3] = $this->colorarray; 15836 } 15837 $arr[4] = $this->currentfontfamily; 15838 if (isset($intlink) && $intlink) { 15839 $arr[7] = $intlink; 15840 } 15841 // mPDF 6 15842 // If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script, 15843 // set for kerning via kern table 15844 // e.g. Latin script when useOTL set as 0x80 15845 if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) { 15846 $this->textvar = ($this->textvar | TextVars::FC_KERNING); 15847 } 15848 $arr[8] = $this->textvar; // mPDF 5.7.1 15849 if (isset($this->textparam) && $this->textparam) { 15850 $arr[9] = $this->textparam; 15851 } 15852 if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) { 15853 $arr[10] = $this->spanbgcolorarray; 15854 } 15855 $arr[11] = $this->currentfontsize; 15856 if (isset($this->ReqFontStyle) && $this->ReqFontStyle) { 15857 $arr[12] = $this->ReqFontStyle; 15858 } 15859 if (isset($this->lSpacingCSS) && $this->lSpacingCSS) { 15860 $arr[14] = $this->lSpacingCSS; 15861 } 15862 if (isset($this->wSpacingCSS) && $this->wSpacingCSS) { 15863 $arr[15] = $this->wSpacingCSS; 15864 } 15865 if (isset($this->spanborddet) && $this->spanborddet) { 15866 $arr[16] = $this->spanborddet; 15867 } 15868 if (isset($this->textshadow) && $this->textshadow) { 15869 $arr[17] = $this->textshadow; 15870 } 15871 if (isset($this->OTLdata) && $this->OTLdata) { 15872 $arr[18] = $this->OTLdata; 15873 $this->OTLdata = []; 15874 } // mPDF 5.7.1 15875 else { 15876 $arr[18] = null; 15877 } 15878 $this->cell[$this->row][$this->col]['textbuffer'][] = $arr; 15879 } 15880 15881 function printbuffer($arrayaux, $blockstate = 0, $is_table = false, $table_draft = false, $cell_dir = '') 15882 { 15883 // $blockstate = 0; // NO margins/padding 15884 // $blockstate = 1; // Top margins/padding only 15885 // $blockstate = 2; // Bottom margins/padding only 15886 // $blockstate = 3; // Top & bottom margins/padding 15887 $this->spanbgcolorarray = ''; 15888 $this->spanbgcolor = false; 15889 $this->spanborder = false; 15890 $this->spanborddet = []; 15891 $paint_ht_corr = 0; 15892 /* -- CSS-FLOAT -- */ 15893 if (count($this->floatDivs)) { 15894 list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl); 15895 if (($this->blk[$this->blklvl]['inner_width'] - $l_width - $r_width) < (2 * $this->GetCharWidth('W', false))) { 15896 // Too narrow to fit - try to move down past L or R float 15897 if ($l_max < $r_max && ($this->blk[$this->blklvl]['inner_width'] - $r_width) > (2 * $this->GetCharWidth('W', false))) { 15898 $this->ClearFloats('LEFT', $this->blklvl); 15899 } elseif ($r_max < $l_max && ($this->blk[$this->blklvl]['inner_width'] - $l_width) > (2 * $this->GetCharWidth('W', false))) { 15900 $this->ClearFloats('RIGHT', $this->blklvl); 15901 } else { 15902 $this->ClearFloats('BOTH', $this->blklvl); 15903 } 15904 } 15905 } 15906 /* -- END CSS-FLOAT -- */ 15907 $bak_y = $this->y; 15908 $bak_x = $this->x; 15909 $align = ''; 15910 if (!$is_table) { 15911 if (isset($this->blk[$this->blklvl]['align']) && $this->blk[$this->blklvl]['align']) { 15912 $align = $this->blk[$this->blklvl]['align']; 15913 } 15914 // Block-align is set by e.g. <.. align="center"> Takes priority for this block but not inherited 15915 if (isset($this->blk[$this->blklvl]['block-align']) && $this->blk[$this->blklvl]['block-align']) { 15916 $align = $this->blk[$this->blklvl]['block-align']; 15917 } 15918 if (isset($this->blk[$this->blklvl]['direction'])) { 15919 $blockdir = $this->blk[$this->blklvl]['direction']; 15920 } else { 15921 $blockdir = ""; 15922 } 15923 $this->divwidth = $this->blk[$this->blklvl]['width']; 15924 } else { 15925 $align = $this->cellTextAlign; 15926 $blockdir = $cell_dir; 15927 } 15928 $oldpage = $this->page; 15929 15930 // ADDED for Out of Block now done as Flowing Block 15931 if ($this->divwidth == 0) { 15932 $this->divwidth = $this->pgwidth; 15933 } 15934 15935 if (!$is_table) { 15936 $this->SetLineHeight($this->FontSizePt, $this->blk[$this->blklvl]['line_height']); 15937 } 15938 $this->divheight = $this->lineheight; 15939 $old_height = $this->divheight; 15940 15941 // As a failsafe - if font has been set but not output to page 15942 if (!$table_draft) { 15943 $this->SetFont($this->default_font, '', $this->default_font_size, true, true); // force output to page 15944 } 15945 15946 $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, true, $blockdir, $table_draft); 15947 15948 $array_size = count($arrayaux); 15949 15950 // Added - Otherwise <div><div><p> did not output top margins/padding for 1st/2nd div 15951 if ($array_size == 0) { 15952 $this->finishFlowingBlock(true); 15953 } // true = END of flowing block 15954 // mPDF 6 15955 // ALL the chunks of textbuffer need to have at least basic OTLdata set 15956 // First make sure each element/chunk has the OTLdata for Bidi set. 15957 for ($i = 0; $i < $array_size; $i++) { 15958 if (empty($arrayaux[$i][18])) { 15959 if (substr($arrayaux[$i][0], 0, 3) == "\xbb\xa4\xac") { // object identifier has been identified! 15960 $unicode = [0xFFFC]; // Object replacement character 15961 } else { 15962 $unicode = $this->UTF8StringToArray($arrayaux[$i][0], false); 15963 } 15964 $is_strong = false; 15965 $this->getBasicOTLdata($arrayaux[$i][18], $unicode, $is_strong); 15966 } 15967 // Gets messed up if try and use core fonts inside a paragraph of text which needs to be BiDi re-ordered or OTLdata set 15968 if (($blockdir == 'rtl' || $this->biDirectional) && isset($arrayaux[$i][4]) && in_array($arrayaux[$i][4], ['ccourier', 'ctimes', 'chelvetica', 'csymbol', 'czapfdingbats'])) { 15969 throw new \Mpdf\MpdfException("You cannot use core fonts in a document which contains RTL text."); 15970 } 15971 } 15972 // mPDF 6 15973 // Process bidirectional text ready for bidi-re-ordering (which is done after line-breaks are established in WriteFlowingBlock etc.) 15974 if (($blockdir == 'rtl' || $this->biDirectional) && !$table_draft) { 15975 if (empty($this->otl)) { 15976 $this->otl = new Otl($this, $this->fontCache); 15977 } 15978 $this->otl->bidiPrepare($arrayaux, $blockdir); 15979 $array_size = count($arrayaux); 15980 } 15981 15982 15983 // Remove empty items // mPDF 6 15984 for ($i = $array_size - 1; $i > 0; $i--) { 15985 if (empty($arrayaux[$i][0]) && (isset($arrayaux[$i][16]) && $arrayaux[$i][16] !== '0') && empty($arrayaux[$i][7])) { 15986 unset($arrayaux[$i]); 15987 } 15988 } 15989 15990 // Correct adjoining borders for inline elements 15991 if (isset($arrayaux[0][16])) { 15992 $lastspanborder = $arrayaux[0][16]; 15993 } else { 15994 $lastspanborder = false; 15995 } 15996 for ($i = 1; $i < $array_size; $i++) { 15997 if (isset($arrayaux[$i][16]) && $arrayaux[$i][16] == $lastspanborder && 15998 ((!isset($arrayaux[$i][9]['bord-decoration']) && !isset($arrayaux[$i - 1][9]['bord-decoration'])) || 15999 (isset($arrayaux[$i][9]['bord-decoration']) && isset($arrayaux[$i - 1][9]['bord-decoration']) && $arrayaux[$i][9]['bord-decoration'] == $arrayaux[$i - 1][9]['bord-decoration']) 16000 ) 16001 ) { 16002 if (isset($arrayaux[$i][16]['R'])) { 16003 $lastspanborder = $arrayaux[$i][16]; 16004 } else { 16005 $lastspanborder = false; 16006 } 16007 $arrayaux[$i][16]['L']['s'] = 0; 16008 $arrayaux[$i][16]['L']['w'] = 0; 16009 $arrayaux[$i - 1][16]['R']['s'] = 0; 16010 $arrayaux[$i - 1][16]['R']['w'] = 0; 16011 } else { 16012 if (isset($arrayaux[$i][16]['R'])) { 16013 $lastspanborder = $arrayaux[$i][16]; 16014 } else { 16015 $lastspanborder = false; 16016 } 16017 } 16018 } 16019 16020 for ($i = 0; $i < $array_size; $i++) { 16021 // COLS 16022 $oldcolumn = $this->CurrCol; 16023 $vetor = isset($arrayaux[$i]) ? $arrayaux[$i] : null; 16024 if ($i == 0 && $vetor[0] != "\n" && ! $this->ispre) { 16025 $vetor[0] = ltrim($vetor[0]); 16026 if (!empty($vetor[18])) { 16027 $this->otl->trimOTLdata($vetor[18], true, false); 16028 } // *OTL* 16029 } 16030 16031 // FIXED TO ALLOW IT TO SHOW '0' 16032 if (empty($vetor[0]) && !($vetor[0] === '0') && empty($vetor[7])) { // Ignore empty text and not carrying an internal link 16033 // Check if it is the last element. If so then finish printing the block 16034 if ($i == ($array_size - 1)) { 16035 $this->finishFlowingBlock(true); 16036 } // true = END of flowing block 16037 continue; 16038 } 16039 16040 16041 // Activating buffer properties 16042 if (isset($vetor[11]) && $vetor[11] != '') { // Font Size 16043 if ($is_table && $this->shrin_k) { 16044 $this->SetFontSize($vetor[11] / $this->shrin_k, false); 16045 } else { 16046 $this->SetFontSize($vetor[11], false); 16047 } 16048 } 16049 16050 if (isset($vetor[17]) && !empty($vetor[17])) { // TextShadow 16051 $this->textshadow = $vetor[17]; 16052 } 16053 if (isset($vetor[16]) && !empty($vetor[16])) { // Border 16054 $this->spanborddet = $vetor[16]; 16055 $this->spanborder = true; 16056 } 16057 16058 if (isset($vetor[15])) { // Word spacing 16059 $this->wSpacingCSS = $vetor[15]; 16060 if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') { 16061 $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3 16062 } 16063 } 16064 if (isset($vetor[14])) { // Letter spacing 16065 $this->lSpacingCSS = $vetor[14]; 16066 if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') { 16067 $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3 16068 } 16069 } 16070 16071 16072 if (isset($vetor[10]) and ! empty($vetor[10])) { // Background color 16073 $this->spanbgcolorarray = $vetor[10]; 16074 $this->spanbgcolor = true; 16075 } 16076 if (isset($vetor[9]) and ! empty($vetor[9])) { // Text parameters - Outline + hyphens 16077 $this->textparam = $vetor[9]; 16078 $this->SetTextOutline($this->textparam); 16079 // mPDF 5.7.3 inline text-decoration parameters 16080 if ($is_table && $this->shrin_k) { 16081 if (isset($this->textparam['text-baseline'])) { 16082 $this->textparam['text-baseline'] /= $this->shrin_k; 16083 } 16084 if (isset($this->textparam['decoration-baseline'])) { 16085 $this->textparam['decoration-baseline'] /= $this->shrin_k; 16086 } 16087 if (isset($this->textparam['decoration-fontsize'])) { 16088 $this->textparam['decoration-fontsize'] /= $this->shrin_k; 16089 } 16090 } 16091 } 16092 if (isset($vetor[8])) { // mPDF 5.7.1 16093 $this->textvar = $vetor[8]; 16094 } 16095 if (isset($vetor[7]) and $vetor[7] != '') { // internal target: <a name="anyvalue"> 16096 $ily = $this->y; 16097 if ($this->table_rotate) { 16098 $this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "tbrot" => true]; 16099 } elseif ($this->kwt) { 16100 $this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "kwt" => true]; 16101 } elseif ($this->ColActive) { 16102 $this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol]; 16103 } elseif (!$this->keep_block_together) { 16104 $this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page]; 16105 } 16106 if (empty($vetor[0])) { // Ignore empty text 16107 // Check if it is the last element. If so then finish printing the block 16108 if ($i == ($array_size - 1)) { 16109 $this->finishFlowingBlock(true); 16110 } // true = END of flowing block 16111 continue; 16112 } 16113 } 16114 if (isset($vetor[5]) and $vetor[5] != '') { // Language // mPDF 6 16115 $this->currentLang = $vetor[5]; 16116 } 16117 if (isset($vetor[4]) and $vetor[4] != '') { // Font Family 16118 $font = $this->SetFont($vetor[4], $this->FontStyle, 0, false); 16119 } 16120 if (!empty($vetor[3])) { // Font Color 16121 $cor = $vetor[3]; 16122 $this->SetTColor($cor); 16123 } 16124 if (isset($vetor[2]) and $vetor[2] != '') { // Bold,Italic styles 16125 $this->SetStyles($vetor[2]); 16126 } 16127 16128 if (isset($vetor[12]) and $vetor[12] != '') { // Requested Bold,Italic 16129 $this->ReqFontStyle = $vetor[12]; 16130 } 16131 if (isset($vetor[1]) and $vetor[1] != '') { // LINK 16132 if (strpos($vetor[1], ".") === false && strpos($vetor[1], "@") !== 0) { // assuming every external link has a dot indicating extension (e.g: .html .txt .zip www.somewhere.com etc.) 16133 // Repeated reference to same anchor? 16134 while (array_key_exists($vetor[1], $this->internallink)) { 16135 $vetor[1] = "#" . $vetor[1]; 16136 } 16137 $this->internallink[$vetor[1]] = $this->AddLink(); 16138 $vetor[1] = $this->internallink[$vetor[1]]; 16139 } 16140 $this->HREF = $vetor[1]; // HREF link style set here ****** 16141 } 16142 16143 // SPECIAL CONTENT - IMAGES & FORM OBJECTS 16144 // Print-out special content 16145 16146 if (substr($vetor[0], 0, 3) == "\xbb\xa4\xac") { // identifier has been identified! 16147 $objattr = $this->_getObjAttr($vetor[0]); 16148 16149 /* -- TABLES -- */ 16150 if ($objattr['type'] == 'nestedtable') { 16151 if ($objattr['nestedcontent']) { 16152 $level = $objattr['level']; 16153 $table = &$this->table[$level][$objattr['table']]; 16154 16155 if ($table_draft) { 16156 $this->y += $this->table[($level + 1)][$objattr['nestedcontent']]['h']; // nested table height 16157 $this->finishFlowingBlock(false, 'nestedtable'); 16158 } else { 16159 $cell = &$table['cells'][$objattr['row']][$objattr['col']]; 16160 $this->finishFlowingBlock(false, 'nestedtable'); 16161 $save_dw = $this->divwidth; 16162 $save_buffer = $this->cellBorderBuffer; 16163 $this->cellBorderBuffer = []; 16164 $ncx = $this->x; 16165 list($dummyx, $w) = $this->_tableGetWidth($table, $objattr['row'], $objattr['col']); 16166 $ntw = $this->table[($level + 1)][$objattr['nestedcontent']]['w']; // nested table width 16167 if (!$this->simpleTables) { 16168 if ($this->packTableData) { 16169 list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cell['borderbin']); 16170 } else { 16171 $br = $cell['border_details']['R']['w']; 16172 $bl = $cell['border_details']['L']['w']; 16173 } 16174 if ($table['borders_separate']) { 16175 $innerw = $w - $bl - $br - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H']; 16176 } else { 16177 $innerw = $w - $bl / 2 - $br / 2 - $cell['padding']['L'] - $cell['padding']['R']; 16178 } 16179 } elseif ($this->simpleTables) { 16180 if ($table['borders_separate']) { 16181 $innerw = $w - $table['simple']['border_details']['L']['w'] - $table['simple']['border_details']['R']['w'] - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H']; 16182 } else { 16183 $innerw = $w - $table['simple']['border_details']['L']['w'] / 2 - $table['simple']['border_details']['R']['w'] / 2 - $cell['padding']['L'] - $cell['padding']['R']; 16184 } 16185 } 16186 if ($cell['a'] == 'C' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'C') { 16187 $ncx += ($innerw - $ntw) / 2; 16188 } elseif ($cell['a'] == 'R' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'R') { 16189 $ncx += $innerw - $ntw; 16190 } 16191 $this->x = $ncx; 16192 16193 $this->_tableWrite($this->table[($level + 1)][$objattr['nestedcontent']]); 16194 $this->cellBorderBuffer = $save_buffer; 16195 $this->x = $bak_x; 16196 $this->divwidth = $save_dw; 16197 } 16198 16199 $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft); 16200 } 16201 } else { 16202 /* -- END TABLES -- */ 16203 if ($is_table) { // *TABLES* 16204 $maxWidth = $this->divwidth; // *TABLES* 16205 } // *TABLES* 16206 else { // *TABLES* 16207 $maxWidth = $this->divwidth - ($this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_right'] + $this->blk[$this->blklvl]['border_right']['w']); 16208 } // *TABLES* 16209 16210 /* -- CSS-IMAGE-FLOAT -- */ 16211 // If float (already) exists at this level 16212 if (isset($this->floatmargins['R']) && $this->y <= $this->floatmargins['R']['y1'] && $this->y >= $this->floatmargins['R']['y0']) { 16213 $maxWidth -= $this->floatmargins['R']['w']; 16214 } 16215 if (isset($this->floatmargins['L']) && $this->y <= $this->floatmargins['L']['y1'] && $this->y >= $this->floatmargins['L']['y0']) { 16216 $maxWidth -= $this->floatmargins['L']['w']; 16217 } 16218 /* -- END CSS-IMAGE-FLOAT -- */ 16219 16220 list($skipln) = $this->inlineObject($objattr['type'], '', $this->y, $objattr, $this->lMargin, ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE), $maxWidth, $this->flowingBlockAttr['height'], false, $is_table); 16221 // 1 -> New line needed because of width 16222 // -1 -> Will fit width on line but NEW PAGE REQUIRED because of height 16223 // -2 -> Will not fit on line therefore needs new line but thus NEW PAGE REQUIRED 16224 $iby = $this->y; 16225 $oldpage = $this->page; 16226 $oldcol = $this->CurrCol; 16227 if (($skipln == 1 || $skipln == -2) && !isset($objattr['float'])) { 16228 $this->finishFlowingBlock(false, $objattr['type']); 16229 $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft); 16230 } 16231 16232 if (!$table_draft) { 16233 $thispage = $this->page; 16234 if ($this->CurrCol != $oldcol) { 16235 $changedcol = true; 16236 } else { 16237 $changedcol = false; 16238 } 16239 16240 // the previous lines can already have triggered page break or column change 16241 if (!$changedcol && $skipln < 0 && $this->AcceptPageBreak() && $thispage == $oldpage) { 16242 $this->AddPage($this->CurOrientation); 16243 16244 // Added to correct Images already set on line before page advanced 16245 // i.e. if second inline image on line is higher than first and forces new page 16246 if (count($this->objectbuffer)) { 16247 $yadj = $iby - $this->y; 16248 foreach ($this->objectbuffer as $ib => $val) { 16249 if ($this->objectbuffer[$ib]['OUTER-Y']) { 16250 $this->objectbuffer[$ib]['OUTER-Y'] -= $yadj; 16251 } 16252 if ($this->objectbuffer[$ib]['BORDER-Y']) { 16253 $this->objectbuffer[$ib]['BORDER-Y'] -= $yadj; 16254 } 16255 if ($this->objectbuffer[$ib]['INNER-Y']) { 16256 $this->objectbuffer[$ib]['INNER-Y'] -= $yadj; 16257 } 16258 } 16259 } 16260 } 16261 16262 // Added to correct for OddEven Margins 16263 if ($this->page != $oldpage) { 16264 if (($this->page - $oldpage) % 2 == 1) { 16265 $bak_x += $this->MarginCorrection; 16266 } 16267 $oldpage = $this->page; 16268 $y = $this->tMargin - $paint_ht_corr; 16269 $this->oldy = $this->tMargin - $paint_ht_corr; 16270 $old_height = 0; 16271 } 16272 $this->x = $bak_x; 16273 /* -- COLUMNS -- */ 16274 // COLS 16275 // OR COLUMN CHANGE 16276 if ($this->CurrCol != $oldcolumn) { 16277 if ($this->directionality == 'rtl') { // *OTL* 16278 $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL* 16279 } // *OTL* 16280 else { // *OTL* 16281 $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); 16282 } // *OTL* 16283 $this->x = $bak_x; 16284 $oldcolumn = $this->CurrCol; 16285 $y = $this->y0 - $paint_ht_corr; 16286 $this->oldy = $this->y0 - $paint_ht_corr; 16287 $old_height = 0; 16288 } 16289 /* -- END COLUMNS -- */ 16290 } 16291 16292 /* -- CSS-IMAGE-FLOAT -- */ 16293 if ($objattr['type'] == 'image' && isset($objattr['float'])) { 16294 $fy = $this->y; 16295 16296 // DIV TOP MARGIN/BORDER/PADDING 16297 if ($this->flowingBlockAttr['newblock'] && ($this->flowingBlockAttr['blockstate'] == 1 || $this->flowingBlockAttr['blockstate'] == 3) && $this->flowingBlockAttr['lineCount'] == 0) { 16298 $fy += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w']; 16299 } 16300 16301 if ($objattr['float'] == 'R') { 16302 $fx = $this->w - $this->rMargin - $objattr['width'] - ($this->blk[$this->blklvl]['outer_right_margin'] + $this->blk[$this->blklvl]['border_right']['w'] + $this->blk[$this->blklvl]['padding_right']); 16303 } elseif ($objattr['float'] == 'L') { 16304 $fx = $this->lMargin + ($this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left']); 16305 } 16306 $w = $objattr['width']; 16307 $h = abs($objattr['height']); 16308 16309 $widthLeft = $maxWidth - ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE); 16310 $maxHeight = $this->h - ($this->tMargin + $this->margin_header + $this->bMargin + 10); 16311 // For Images 16312 $extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']); 16313 $extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']); 16314 16315 if ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg') { 16316 $file = $objattr['file']; 16317 $info = $this->formobjects[$file]; 16318 } else { 16319 $file = $objattr['file']; 16320 $info = $this->images[$file]; 16321 } 16322 $img_w = $w - $extraWidth; 16323 $img_h = $h - $extraHeight; 16324 if ($objattr['border_left']['w']) { 16325 $objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] + $objattr['border_right']['w']) / 2); 16326 $objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] + $objattr['border_bottom']['w']) / 2); 16327 $objattr['BORDER-X'] = $fx + $objattr['margin_left'] + (($objattr['border_left']['w']) / 2); 16328 $objattr['BORDER-Y'] = $fy + $objattr['margin_top'] + (($objattr['border_top']['w']) / 2); 16329 } 16330 $objattr['INNER-WIDTH'] = $img_w; 16331 $objattr['INNER-HEIGHT'] = $img_h; 16332 $objattr['INNER-X'] = $fx + $objattr['margin_left'] + ($objattr['border_left']['w']); 16333 $objattr['INNER-Y'] = $fy + $objattr['margin_top'] + ($objattr['border_top']['w']); 16334 $objattr['ID'] = $info['i']; 16335 $objattr['OUTER-WIDTH'] = $w; 16336 $objattr['OUTER-HEIGHT'] = $h; 16337 $objattr['OUTER-X'] = $fx; 16338 $objattr['OUTER-Y'] = $fy; 16339 if ($objattr['float'] == 'R') { 16340 // If R float already exists at this level 16341 $this->floatmargins['R']['skipline'] = false; 16342 if (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) { 16343 $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1 16344 } // If L float already exists at this level 16345 elseif (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) { 16346 // Final check distance between floats is not now too narrow to fit text 16347 $mw = 2 * $this->GetCharWidth('W', false); 16348 if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['L']['w']) < $mw) { 16349 $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1 16350 } else { 16351 $this->floatmargins['R']['x'] = $fx; 16352 $this->floatmargins['R']['w'] = $w; 16353 $this->floatmargins['R']['y0'] = $fy; 16354 $this->floatmargins['R']['y1'] = $fy + $h; 16355 if ($skipln == 1) { 16356 $this->floatmargins['R']['skipline'] = true; 16357 $this->floatmargins['R']['id'] = count($this->floatbuffer) + 0; 16358 $objattr['skipline'] = true; 16359 } 16360 $this->floatbuffer[] = $objattr; 16361 } 16362 } else { 16363 $this->floatmargins['R']['x'] = $fx; 16364 $this->floatmargins['R']['w'] = $w; 16365 $this->floatmargins['R']['y0'] = $fy; 16366 $this->floatmargins['R']['y1'] = $fy + $h; 16367 if ($skipln == 1) { 16368 $this->floatmargins['R']['skipline'] = true; 16369 $this->floatmargins['R']['id'] = count($this->floatbuffer) + 0; 16370 $objattr['skipline'] = true; 16371 } 16372 $this->floatbuffer[] = $objattr; 16373 } 16374 } elseif ($objattr['float'] == 'L') { 16375 // If L float already exists at this level 16376 $this->floatmargins['L']['skipline'] = false; 16377 if (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) { 16378 $this->floatmargins['L']['skipline'] = false; 16379 $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1 16380 } // If R float already exists at this level 16381 elseif (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) { 16382 // Final check distance between floats is not now too narrow to fit text 16383 $mw = 2 * $this->GetCharWidth('W', false); 16384 if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['R']['w']) < $mw) { 16385 $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1 16386 } else { 16387 $this->floatmargins['L']['x'] = $fx + $w; 16388 $this->floatmargins['L']['w'] = $w; 16389 $this->floatmargins['L']['y0'] = $fy; 16390 $this->floatmargins['L']['y1'] = $fy + $h; 16391 if ($skipln == 1) { 16392 $this->floatmargins['L']['skipline'] = true; 16393 $this->floatmargins['L']['id'] = count($this->floatbuffer) + 0; 16394 $objattr['skipline'] = true; 16395 } 16396 $this->floatbuffer[] = $objattr; 16397 } 16398 } else { 16399 $this->floatmargins['L']['x'] = $fx + $w; 16400 $this->floatmargins['L']['w'] = $w; 16401 $this->floatmargins['L']['y0'] = $fy; 16402 $this->floatmargins['L']['y1'] = $fy + $h; 16403 if ($skipln == 1) { 16404 $this->floatmargins['L']['skipline'] = true; 16405 $this->floatmargins['L']['id'] = count($this->floatbuffer) + 0; 16406 $objattr['skipline'] = true; 16407 } 16408 $this->floatbuffer[] = $objattr; 16409 } 16410 } 16411 } else { 16412 /* -- END CSS-IMAGE-FLOAT -- */ 16413 $this->WriteFlowingBlock($vetor[0], (isset($vetor[18]) ? $vetor[18] : null)); // mPDF 5.7.1 16414 /* -- CSS-IMAGE-FLOAT -- */ 16415 } 16416 /* -- END CSS-IMAGE-FLOAT -- */ 16417 } // *TABLES* 16418 } // END If special content 16419 else { // THE text 16420 if ($this->tableLevel) { 16421 $paint_ht_corr = 0; 16422 } // To move the y up when new column/page started if div border needed 16423 else { 16424 $paint_ht_corr = $this->blk[$this->blklvl]['border_top']['w']; 16425 } 16426 16427 if ($vetor[0] == "\n") { // We are reading a <BR> now turned into newline ("\n") 16428 if ($this->flowingBlockAttr['content']) { 16429 $this->finishFlowingBlock(false, 'br'); 16430 } elseif ($is_table) { 16431 $this->y+= $this->_computeLineheight($this->cellLineHeight); 16432 } elseif (!$is_table) { 16433 $this->DivLn($this->lineheight); 16434 if ($this->ColActive) { 16435 $this->breakpoints[$this->CurrCol][] = $this->y; 16436 } // *COLUMNS* 16437 } 16438 // Added to correct for OddEven Margins 16439 if ($this->page != $oldpage) { 16440 if (($this->page - $oldpage) % 2 == 1) { 16441 $bak_x += $this->MarginCorrection; 16442 } 16443 $oldpage = $this->page; 16444 $y = $this->tMargin - $paint_ht_corr; 16445 $this->oldy = $this->tMargin - $paint_ht_corr; 16446 $old_height = 0; 16447 } 16448 $this->x = $bak_x; 16449 /* -- COLUMNS -- */ 16450 // COLS 16451 // OR COLUMN CHANGE 16452 if ($this->CurrCol != $oldcolumn) { 16453 if ($this->directionality == 'rtl') { // *OTL* 16454 $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL* 16455 } // *OTL* 16456 else { // *OTL* 16457 $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); 16458 } // *OTL* 16459 $this->x = $bak_x; 16460 $oldcolumn = $this->CurrCol; 16461 $y = $this->y0 - $paint_ht_corr; 16462 $this->oldy = $this->y0 - $paint_ht_corr; 16463 $old_height = 0; 16464 } 16465 /* -- END COLUMNS -- */ 16466 $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft); 16467 } else { 16468 $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1 16469 // Added to correct for OddEven Margins 16470 if ($this->page != $oldpage) { 16471 if (($this->page - $oldpage) % 2 == 1) { 16472 $bak_x += $this->MarginCorrection; 16473 $this->x = $bak_x; 16474 } 16475 $oldpage = $this->page; 16476 $y = $this->tMargin - $paint_ht_corr; 16477 $this->oldy = $this->tMargin - $paint_ht_corr; 16478 $old_height = 0; 16479 } 16480 /* -- COLUMNS -- */ 16481 // COLS 16482 // OR COLUMN CHANGE 16483 if ($this->CurrCol != $oldcolumn) { 16484 if ($this->directionality == 'rtl') { // *OTL* 16485 $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL* 16486 } // *OTL* 16487 else { // *OTL* 16488 $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); 16489 } // *OTL* 16490 $this->x = $bak_x; 16491 $oldcolumn = $this->CurrCol; 16492 $y = $this->y0 - $paint_ht_corr; 16493 $this->oldy = $this->y0 - $paint_ht_corr; 16494 $old_height = 0; 16495 } 16496 /* -- END COLUMNS -- */ 16497 } 16498 } 16499 16500 // Check if it is the last element. If so then finish printing the block 16501 if ($i == ($array_size - 1)) { 16502 $this->finishFlowingBlock(true); // true = END of flowing block 16503 // Added to correct for OddEven Margins 16504 if ($this->page != $oldpage) { 16505 if (($this->page - $oldpage) % 2 == 1) { 16506 $bak_x += $this->MarginCorrection; 16507 $this->x = $bak_x; 16508 } 16509 $oldpage = $this->page; 16510 $y = $this->tMargin - $paint_ht_corr; 16511 $this->oldy = $this->tMargin - $paint_ht_corr; 16512 $old_height = 0; 16513 } 16514 16515 /* -- COLUMNS -- */ 16516 // COLS 16517 // OR COLUMN CHANGE 16518 if ($this->CurrCol != $oldcolumn) { 16519 if ($this->directionality == 'rtl') { // *OTL* 16520 $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL* 16521 } // *OTL* 16522 else { // *OTL* 16523 $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); 16524 } // *OTL* 16525 $this->x = $bak_x; 16526 $oldcolumn = $this->CurrCol; 16527 $y = $this->y0 - $paint_ht_corr; 16528 $this->oldy = $this->y0 - $paint_ht_corr; 16529 $old_height = 0; 16530 } 16531 /* -- END COLUMNS -- */ 16532 } 16533 16534 // RESETTING VALUES 16535 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 16536 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 16537 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 16538 $this->colorarray = ''; 16539 $this->spanbgcolorarray = ''; 16540 $this->spanbgcolor = false; 16541 $this->spanborder = false; 16542 $this->spanborddet = []; 16543 $this->HREF = ''; 16544 $this->textparam = []; 16545 $this->SetTextOutline(); 16546 16547 $this->textvar = 0x00; // mPDF 5.7.1 16548 $this->OTLtags = []; 16549 $this->textshadow = ''; 16550 16551 $this->currentfontfamily = ''; 16552 $this->currentfontsize = ''; 16553 $this->currentfontstyle = ''; 16554 $this->currentLang = $this->default_lang; // mPDF 6 16555 $this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6 16556 /* -- TABLES -- */ 16557 if ($this->tableLevel) { 16558 $this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES* 16559 } else { /* -- END TABLES -- */ 16560 if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) { 16561 $this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height 16562 } 16563 } 16564 $this->ResetStyles(); 16565 $this->lSpacingCSS = ''; 16566 $this->wSpacingCSS = ''; 16567 $this->fixedlSpacing = false; 16568 $this->minwSpacing = 0; 16569 $this->SetDash(); 16570 $this->dash_on = false; 16571 $this->dotted_on = false; 16572 }//end of for(i=0;i<arraysize;i++) 16573 16574 $this->Reset(); // mPDF 6 16575 // PAINT DIV BORDER // DISABLED IN COLUMNS AS DOESN'T WORK WHEN BROKEN ACROSS COLS?? 16576 if ((isset($this->blk[$this->blklvl]['border']) || isset($this->blk[$this->blklvl]['bgcolor']) || isset($this->blk[$this->blklvl]['box_shadow'])) && $blockstate && ($this->y != $this->oldy)) { 16577 $bottom_y = $this->y; // Does not include Bottom Margin 16578 if (isset($this->blk[$this->blklvl]['startpage']) && $this->blk[$this->blklvl]['startpage'] != $this->page && $blockstate != 1) { 16579 $this->PaintDivBB('pagetop', $blockstate); 16580 } elseif ($blockstate != 1) { 16581 $this->PaintDivBB('', $blockstate); 16582 } 16583 $this->y = $bottom_y; 16584 $this->x = $bak_x; 16585 } 16586 16587 // Reset Font 16588 $this->SetFontSize($this->default_font_size, false); 16589 if ($table_draft) { 16590 $ch = $this->y - $bak_y; 16591 $this->y = $bak_y; 16592 $this->x = $bak_x; 16593 return $ch; 16594 } 16595 } 16596 16597 function _setDashBorder($style, $div, $cp, $side) 16598 { 16599 if ($style == 'dashed' && (($side == 'L' || $side == 'R') || ($side == 'T' && $div != 'pagetop' && !$cp) || ($side == 'B' && $div != 'pagebottom') )) { 16600 $dashsize = 2; // final dash will be this + 1*linewidth 16601 $dashsizek = 1.5; // ratio of Dash/Blank 16602 $this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2)); 16603 } elseif ($style == 'dotted' || ($side == 'T' && ($div == 'pagetop' || $cp)) || ($side == 'B' && $div == 'pagebottom')) { 16604 // Round join and cap 16605 $this->SetLineJoin(1); 16606 $this->SetLineCap(1); 16607 $this->SetDash(0.001, ($this->LineWidth * 3)); 16608 } 16609 } 16610 16611 function _setBorderLine($b, $k = 1) 16612 { 16613 $this->SetLineWidth($b['w'] / $k); 16614 $this->SetDColor($b['c']); 16615 if ($b['c'][0] == 5) { // RGBa 16616 $this->SetAlpha(ord($b['c'][4]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2 16617 } elseif ($b['c'][0] == 6) { // CMYKa 16618 $this->SetAlpha(ord($b['c'][5]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2 16619 } 16620 } 16621 16622 function PaintDivBB($divider = '', $blockstate = 0, $blvl = 0) 16623 { 16624 // Borders & backgrounds are done elsewhere for columns - messes up the repositioning in printcolumnbuffer 16625 if ($this->ColActive) { 16626 return; 16627 } // *COLUMNS* 16628 if ($this->keep_block_together) { 16629 return; 16630 } // mPDF 6 16631 $save_y = $this->y; 16632 if (!$blvl) { 16633 $blvl = $this->blklvl; 16634 } 16635 $x0 = $x1 = $y0 = $y1 = 0; 16636 16637 // Added mPDF 3.0 Float DIV 16638 if (isset($this->blk[$blvl]['bb_painted'][$this->page]) && $this->blk[$blvl]['bb_painted'][$this->page]) { 16639 return; 16640 } // *CSS-FLOAT* 16641 16642 if (isset($this->blk[$blvl]['x0'])) { 16643 $x0 = $this->blk[$blvl]['x0']; 16644 } // left 16645 if (isset($this->blk[$blvl]['y1'])) { 16646 $y1 = $this->blk[$blvl]['y1']; 16647 } // bottom 16648 // Added mPDF 3.0 Float DIV - ensures backgrounds/borders are drawn to bottom of page 16649 if ($y1 == 0) { 16650 if ($divider == 'pagebottom') { 16651 $y1 = $this->h - $this->bMargin; 16652 } else { 16653 $y1 = $this->y; 16654 } 16655 } 16656 16657 $continuingpage = (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page); 16658 16659 if (isset($this->blk[$blvl]['y0'])) { 16660 $y0 = $this->blk[$blvl]['y0']; 16661 } 16662 $h = $y1 - $y0; 16663 $w = $this->blk[$blvl]['width']; 16664 $x1 = $x0 + $w; 16665 16666 // Set border-widths as used here 16667 $border_top = $this->blk[$blvl]['border_top']['w']; 16668 $border_bottom = $this->blk[$blvl]['border_bottom']['w']; 16669 $border_left = $this->blk[$blvl]['border_left']['w']; 16670 $border_right = $this->blk[$blvl]['border_right']['w']; 16671 if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) { 16672 $border_top = 0; 16673 } 16674 if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') { 16675 $border_bottom = 0; 16676 } 16677 16678 $brTL_H = 0; 16679 $brTL_V = 0; 16680 $brTR_H = 0; 16681 $brTR_V = 0; 16682 $brBL_H = 0; 16683 $brBL_V = 0; 16684 $brBR_H = 0; 16685 $brBR_V = 0; 16686 16687 $brset = false; 16688 /* -- BORDER-RADIUS -- */ 16689 if (isset($this->blk[$blvl]['border_radius_TL_H'])) { 16690 $brTL_H = $this->blk[$blvl]['border_radius_TL_H']; 16691 $brset = true; 16692 } 16693 if (isset($this->blk[$blvl]['border_radius_TL_V'])) { 16694 $brTL_V = $this->blk[$blvl]['border_radius_TL_V']; 16695 $brset = true; 16696 } 16697 if (isset($this->blk[$blvl]['border_radius_TR_H'])) { 16698 $brTR_H = $this->blk[$blvl]['border_radius_TR_H']; 16699 $brset = true; 16700 } 16701 if (isset($this->blk[$blvl]['border_radius_TR_V'])) { 16702 $brTR_V = $this->blk[$blvl]['border_radius_TR_V']; 16703 $brset = true; 16704 } 16705 if (isset($this->blk[$blvl]['border_radius_BR_H'])) { 16706 $brBR_H = $this->blk[$blvl]['border_radius_BR_H']; 16707 $brset = true; 16708 } 16709 if (isset($this->blk[$blvl]['border_radius_BR_V'])) { 16710 $brBR_V = $this->blk[$blvl]['border_radius_BR_V']; 16711 $brset = true; 16712 } 16713 if (isset($this->blk[$blvl]['border_radius_BL_H'])) { 16714 $brBL_H = $this->blk[$blvl]['border_radius_BL_H']; 16715 $brset = true; 16716 } 16717 if (isset($this->blk[$blvl]['border_radius_BL_V'])) { 16718 $brBL_V = $this->blk[$blvl]['border_radius_BL_V']; 16719 $brset = true; 16720 } 16721 16722 if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) { 16723 $brTL_H = 0; 16724 $brTL_V = 0; 16725 $brTR_H = 0; 16726 $brTR_V = 0; 16727 } 16728 if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') { 16729 $brBL_H = 0; 16730 $brBL_V = 0; 16731 $brBR_H = 0; 16732 $brBR_V = 0; 16733 } 16734 16735 // Disallow border-radius if it is smaller than the border width. 16736 if ($brTL_H < min($border_left, $border_top)) { 16737 $brTL_H = $brTL_V = 0; 16738 } 16739 if ($brTL_V < min($border_left, $border_top)) { 16740 $brTL_V = $brTL_H = 0; 16741 } 16742 if ($brTR_H < min($border_right, $border_top)) { 16743 $brTR_H = $brTR_V = 0; 16744 } 16745 if ($brTR_V < min($border_right, $border_top)) { 16746 $brTR_V = $brTR_H = 0; 16747 } 16748 if ($brBL_H < min($border_left, $border_bottom)) { 16749 $brBL_H = $brBL_V = 0; 16750 } 16751 if ($brBL_V < min($border_left, $border_bottom)) { 16752 $brBL_V = $brBL_H = 0; 16753 } 16754 if ($brBR_H < min($border_right, $border_bottom)) { 16755 $brBR_H = $brBR_V = 0; 16756 } 16757 if ($brBR_V < min($border_right, $border_bottom)) { 16758 $brBR_V = $brBR_H = 0; 16759 } 16760 16761 // CHECK FOR radii that sum to > width or height of div ******** 16762 $f = min($h / ($brTL_V + $brBL_V + 0.001), $h / ($brTR_V + $brBR_V + 0.001), $w / ($brTL_H + $brTR_H + 0.001), $w / ($brBL_H + $brBR_H + 0.001)); 16763 if ($f < 1) { 16764 $brTL_H *= $f; 16765 $brTL_V *= $f; 16766 $brTR_H *= $f; 16767 $brTR_V *= $f; 16768 $brBL_H *= $f; 16769 $brBL_V *= $f; 16770 $brBR_H *= $f; 16771 $brBR_V *= $f; 16772 } 16773 /* -- END BORDER-RADIUS -- */ 16774 16775 $tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings); 16776 for ($l = 0; $l <= $blvl; $l++) { 16777 if ($this->blk[$l]['bgcolor']) { 16778 $tbcol = $this->blk[$l]['bgcolorarray']; 16779 } 16780 } 16781 16782 // BORDERS 16783 if (isset($this->blk[$blvl]['y0']) && $this->blk[$blvl]['y0']) { 16784 $y0 = $this->blk[$blvl]['y0']; 16785 } 16786 $h = $y1 - $y0; 16787 $w = $this->blk[$blvl]['width']; 16788 16789 if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) { 16790 $tbd = $this->blk[$blvl]['border_top']; 16791 16792 $legend = ''; 16793 $legbreakL = 0; 16794 $legbreakR = 0; 16795 // BORDER LEGEND 16796 if (isset($this->blk[$blvl]['border_legend']) && $this->blk[$blvl]['border_legend']) { 16797 $legend = $this->blk[$blvl]['border_legend']; // Same structure array as textbuffer 16798 $txt = $legend[0] = ltrim($legend[0]); 16799 if (!empty($legend[18])) { 16800 $this->otl->trimOTLdata($legend[18], true, false); 16801 } // *OTL* 16802 // Set font, size, style, color 16803 $this->SetFont($legend[4], $legend[2], $legend[11]); 16804 if (isset($legend[3]) && $legend[3]) { 16805 $cor = $legend[3]; 16806 $this->SetTColor($cor); 16807 } 16808 $stringWidth = $this->GetStringWidth($txt, true, $legend[18], $legend[8]); 16809 $save_x = $this->x; 16810 $save_y = $this->y; 16811 $save_currentfontfamily = $this->FontFamily; 16812 $save_currentfontsize = $this->FontSizePt; 16813 $save_currentfontstyle = $this->FontStyle; 16814 $this->y = $y0 - $this->FontSize / 2 + $this->blk[$blvl]['border_top']['w'] / 2; 16815 $this->x = $x0 + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_left']['w']; 16816 16817 // Set the distance from the border line to the text ? make configurable variable 16818 $gap = 0.2 * $this->FontSize; 16819 $legbreakL = $this->x - $gap; 16820 $legbreakR = $this->x + $stringWidth + $gap; 16821 $this->magic_reverse_dir($txt, $this->blk[$blvl]['direction'], $legend[18]); 16822 $fill = ''; 16823 $this->Cell($stringWidth, $this->FontSize, $txt, '', 0, 'C', $fill, '', 0, 0, 0, 'M', $fill, false, $legend[18], $legend[8]); 16824 // Reset 16825 $this->x = $save_x; 16826 $this->y = $save_y; 16827 $this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize); 16828 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 16829 } 16830 16831 if (isset($tbd['s']) && $tbd['s']) { 16832 if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') { 16833 $this->writer->write('q'); 16834 $this->SetLineWidth(0); 16835 $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)); 16836 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE)); 16837 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE)); 16838 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)); 16839 $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path 16840 } 16841 16842 $this->_setBorderLine($tbd); 16843 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 16844 $legbreakL -= $border_top / 2; // because line cap different 16845 $legbreakR += $border_top / 2; 16846 $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'T'); 16847 } /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brTR_V && $brTR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') { 16848 $this->SetLineJoin(0); 16849 $this->SetLineCap(0); 16850 } 16851 $s = ''; 16852 if ($brTR_H && $brTR_V) { 16853 $s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_top / 2, $brTR_V - $border_top / 2, 1, 2, true)) . "\n"; 16854 } else { /* -- END BORDER-RADIUS -- */ 16855 if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 16856 $s .= (sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16857 } else { 16858 $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16859 } 16860 } 16861 /* -- BORDER-RADIUS -- */ 16862 if ($brTL_H && $brTL_V) { 16863 if ($legend) { 16864 if ($legbreakR < ($x0 + $w - $brTR_H)) { 16865 $s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16866 } 16867 if ($legbreakL > ($x0 + $brTL_H )) { 16868 $s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16869 $s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE) . "\n"); 16870 } else { 16871 $s .= (sprintf('%.3F %.3F m ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16872 } 16873 } else { 16874 $s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16875 } 16876 $s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_top / 2, $brTL_V - $border_top / 2, 2, 1)) . "\n"; 16877 } else { 16878 /* -- END BORDER-RADIUS -- */ 16879 if ($legend) { 16880 if ($legbreakR < ($x0 + $w)) { 16881 $s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16882 } 16883 if ($legbreakL > ($x0)) { 16884 $s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16885 if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 16886 $s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16887 } else { 16888 $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16889 } 16890 } elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 16891 $s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16892 } else { 16893 $s .= (sprintf('%.3F %.3F m ', ($x0 + $border_top / 2) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16894 } 16895 } elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 16896 $s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16897 } else { 16898 $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n"; 16899 } 16900 /* -- BORDER-RADIUS -- */ 16901 } 16902 /* -- END BORDER-RADIUS -- */ 16903 $s .= 'S' . "\n"; 16904 $this->writer->write($s); 16905 16906 if ($tbd['style'] == 'double') { 16907 $this->SetLineWidth($tbd['w'] / 3); 16908 $this->SetDColor($tbcol); 16909 $this->writer->write($s); 16910 } 16911 if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') { 16912 $this->writer->write('Q'); 16913 } 16914 16915 // Reset Corners and Dash off 16916 $this->SetLineWidth(0.1); 16917 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 16918 $this->SetLineJoin(2); 16919 $this->SetLineCap(2); 16920 $this->SetDash(); 16921 } 16922 } 16923 // Reinstate line above for dotted line divider when block border crosses a page 16924 // elseif ($divider == 'pagetop' || $continuingpage) { 16925 16926 if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') { 16927 $tbd = $this->blk[$blvl]['border_bottom']; 16928 if (isset($tbd['s']) && $tbd['s']) { 16929 if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') { 16930 $this->writer->write('q'); 16931 $this->SetLineWidth(0); 16932 $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)); 16933 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE)); 16934 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE)); 16935 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)); 16936 $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path 16937 } 16938 16939 $this->_setBorderLine($tbd); 16940 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 16941 $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'B'); 16942 } /* -- BORDER-RADIUS -- */ elseif (($brBL_V && $brBL_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') { 16943 $this->SetLineJoin(0); 16944 $this->SetLineCap(0); 16945 } 16946 $s = ''; 16947 if ($brBL_H && $brBL_V) { 16948 $s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_bottom / 2, $brBL_V - $border_bottom / 2, 3, 2, true)) . "\n"; 16949 } else { /* -- END BORDER-RADIUS -- */ 16950 if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 16951 $s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n"; 16952 } else { 16953 $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n"; 16954 } 16955 } 16956 /* -- BORDER-RADIUS -- */ 16957 if ($brBR_H && $brBR_V) { 16958 $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2) - $brBR_H ) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n"; 16959 $s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_bottom / 2, $brBR_V - $border_bottom / 2, 4, 1)) . "\n"; 16960 } else { /* -- END BORDER-RADIUS -- */ 16961 if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 16962 $s .= (sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n"; 16963 } else { 16964 $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n"; 16965 } 16966 } 16967 $s .= 'S' . "\n"; 16968 $this->writer->write($s); 16969 16970 if ($tbd['style'] == 'double') { 16971 $this->SetLineWidth($tbd['w'] / 3); 16972 $this->SetDColor($tbcol); 16973 $this->writer->write($s); 16974 } 16975 if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') { 16976 $this->writer->write('Q'); 16977 } 16978 16979 // Reset Corners and Dash off 16980 $this->SetLineWidth(0.1); 16981 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 16982 $this->SetLineJoin(2); 16983 $this->SetLineCap(2); 16984 $this->SetDash(); 16985 } 16986 } 16987 // Reinstate line below for dotted line divider when block border crosses a page 16988 // elseif ($blockstate == 1 || $divider == 'pagebottom') { 16989 16990 if ($this->blk[$blvl]['border_left']) { 16991 $tbd = $this->blk[$blvl]['border_left']; 16992 if (isset($tbd['s']) && $tbd['s']) { 16993 if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') { 16994 $this->writer->write('q'); 16995 $this->SetLineWidth(0); 16996 $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)); 16997 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE)); 16998 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE)); 16999 $this->writer->write(sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)); 17000 $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path 17001 } 17002 17003 $this->_setBorderLine($tbd); 17004 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17005 $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'L'); 17006 } /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brBL_V && $brBL_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') { 17007 $this->SetLineJoin(0); 17008 $this->SetLineCap(0); 17009 } 17010 $s = ''; 17011 if ($brTL_V && $brTL_H) { 17012 $s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_left / 2, $brTL_V - $border_left / 2, 2, 2, true)) . "\n"; 17013 } else { /* -- END BORDER-RADIUS -- */ 17014 if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 17015 $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)) . "\n"; 17016 } else { 17017 $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_left / 2))) * Mpdf::SCALE)) . "\n"; 17018 } 17019 } 17020 /* -- BORDER-RADIUS -- */ 17021 if ($brBL_V && $brBL_H) { 17022 $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2) - $brBL_V) ) * Mpdf::SCALE)) . "\n"; 17023 $s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_left / 2, $brBL_V - $border_left / 2, 3, 1)) . "\n"; 17024 } else { /* -- END BORDER-RADIUS -- */ 17025 if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 17026 $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h) ) * Mpdf::SCALE)) . "\n"; 17027 } else { 17028 $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2)) ) * Mpdf::SCALE)) . "\n"; 17029 } 17030 } 17031 $s .= 'S' . "\n"; 17032 $this->writer->write($s); 17033 17034 if ($tbd['style'] == 'double') { 17035 $this->SetLineWidth($tbd['w'] / 3); 17036 $this->SetDColor($tbcol); 17037 $this->writer->write($s); 17038 } 17039 if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') { 17040 $this->writer->write('Q'); 17041 } 17042 17043 // Reset Corners and Dash off 17044 $this->SetLineWidth(0.1); 17045 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 17046 $this->SetLineJoin(2); 17047 $this->SetLineCap(2); 17048 $this->SetDash(); 17049 } 17050 } 17051 if ($this->blk[$blvl]['border_right']) { 17052 $tbd = $this->blk[$blvl]['border_right']; 17053 if (isset($tbd['s']) && $tbd['s']) { 17054 if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') { 17055 $this->writer->write('q'); 17056 $this->SetLineWidth(0); 17057 $this->writer->write(sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)); 17058 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE)); 17059 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE)); 17060 $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)); 17061 $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path 17062 } 17063 17064 $this->_setBorderLine($tbd); 17065 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17066 $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'R'); 17067 } /* -- BORDER-RADIUS -- */ elseif (($brTR_V && $brTR_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') { 17068 $this->SetLineJoin(0); 17069 $this->SetLineCap(0); 17070 } 17071 $s = ''; 17072 if ($brBR_V && $brBR_H) { 17073 $s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_right / 2, $brBR_V - $border_right / 2, 4, 2, true)) . "\n"; 17074 } else { /* -- END BORDER-RADIUS -- */ 17075 if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 17076 $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)) . "\n"; 17077 } else { 17078 $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_right / 2))) * Mpdf::SCALE)) . "\n"; 17079 } 17080 } 17081 /* -- BORDER-RADIUS -- */ 17082 if ($brTR_V && $brTR_H) { 17083 $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2) + $brTR_V) ) * Mpdf::SCALE)) . "\n"; 17084 $s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_right / 2, $brTR_V - $border_right / 2, 1, 1)) . "\n"; 17085 } else { /* -- END BORDER-RADIUS -- */ 17086 if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') { 17087 $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0) ) * Mpdf::SCALE)) . "\n"; 17088 } else { 17089 $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2)) ) * Mpdf::SCALE)) . "\n"; 17090 } 17091 } 17092 $s .= 'S' . "\n"; 17093 $this->writer->write($s); 17094 17095 if ($tbd['style'] == 'double') { 17096 $this->SetLineWidth($tbd['w'] / 3); 17097 $this->SetDColor($tbcol); 17098 $this->writer->write($s); 17099 } 17100 if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') { 17101 $this->writer->write('Q'); 17102 } 17103 17104 // Reset Corners and Dash off 17105 $this->SetLineWidth(0.1); 17106 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 17107 $this->SetLineJoin(2); 17108 $this->SetLineCap(2); 17109 $this->SetDash(); 17110 } 17111 } 17112 17113 17114 $this->SetDash(); 17115 $this->y = $save_y; 17116 17117 17118 // BACKGROUNDS are disabled in columns/kbt/headers - messes up the repositioning in printcolumnbuffer 17119 if ($this->ColActive || $this->kwt || $this->keep_block_together) { 17120 return; 17121 } 17122 17123 17124 $bgx0 = $x0; 17125 $bgx1 = $x1; 17126 $bgy0 = $y0; 17127 $bgy1 = $y1; 17128 17129 // Defined br values represent the radius of the outer curve - need to take border-width/2 from each radius for drawing the borders 17130 if (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'padding-box') { 17131 $brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w']); 17132 $brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w']); 17133 $brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w']); 17134 $brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w']); 17135 $brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w']); 17136 $brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w']); 17137 $brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w']); 17138 $brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w']); 17139 $bgx0 += $this->blk[$blvl]['border_left']['w']; 17140 $bgx1 -= $this->blk[$blvl]['border_right']['w']; 17141 if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) { 17142 $bgy0 += $this->blk[$blvl]['border_top']['w']; 17143 } 17144 if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') { 17145 $bgy1 -= $this->blk[$blvl]['border_bottom']['w']; 17146 } 17147 } elseif (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'content-box') { 17148 $brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']); 17149 $brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']); 17150 $brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']); 17151 $brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']); 17152 $brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']); 17153 $brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']); 17154 $brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']); 17155 $brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']); 17156 $bgx0 += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left']; 17157 $bgx1 -= $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']; 17158 if (($this->blk[$blvl]['border_top']['w'] || $this->blk[$blvl]['padding_top']) && $divider != 'pagetop' && !$continuingpage) { 17159 $bgy0 += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top']; 17160 } 17161 if (($this->blk[$blvl]['border_bottom']['w'] || $this->blk[$blvl]['padding_bottom']) && $blockstate != 1 && $divider != 'pagebottom') { 17162 $bgy1 -= $this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']; 17163 } 17164 } else { 17165 $brbgTL_H = $brTL_H; 17166 $brbgTL_V = $brTL_V; 17167 $brbgTR_H = $brTR_H; 17168 $brbgTR_V = $brTR_V; 17169 $brbgBL_H = $brBL_H; 17170 $brbgBL_V = $brBL_V; 17171 $brbgBR_H = $brBR_H; 17172 $brbgBR_V = $brBR_V; 17173 } 17174 17175 // Set clipping path 17176 $s = ' q 0 w '; // Line width=0 17177 $s .= sprintf('%.3F %.3F m ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // start point TL before the arc 17178 /* -- BORDER-RADIUS -- */ 17179 if ($brbgTL_H || $brbgTL_V) { 17180 $s .= $this->_EllipseArc($bgx0 + $brbgTL_H, $bgy0 + $brbgTL_V, $brbgTL_H, $brbgTL_V, 2); // segment 2 TL 17181 } 17182 /* -- END BORDER-RADIUS -- */ 17183 $s .= sprintf('%.3F %.3F l ', ($bgx0) * Mpdf::SCALE, ($this->h - ($bgy1 - $brbgBL_V )) * Mpdf::SCALE); // line to BL 17184 /* -- BORDER-RADIUS -- */ 17185 if ($brbgBL_H || $brbgBL_V) { 17186 $s .= $this->_EllipseArc($bgx0 + $brbgBL_H, $bgy1 - $brbgBL_V, $brbgBL_H, $brbgBL_V, 3); // segment 3 BL 17187 } 17188 /* -- END BORDER-RADIUS -- */ 17189 $s .= sprintf('%.3F %.3F l ', ($bgx1 - $brbgBR_H ) * Mpdf::SCALE, ($this->h - ($bgy1)) * Mpdf::SCALE); // line to BR 17190 /* -- BORDER-RADIUS -- */ 17191 if ($brbgBR_H || $brbgBR_V) { 17192 $s .= $this->_EllipseArc($bgx1 - $brbgBR_H, $bgy1 - $brbgBR_V, $brbgBR_H, $brbgBR_V, 4); // segment 4 BR 17193 } 17194 /* -- END BORDER-RADIUS -- */ 17195 $s .= sprintf('%.3F %.3F l ', ($bgx1) * Mpdf::SCALE, ($this->h - ($bgy0 + $brbgTR_V)) * Mpdf::SCALE); // line to TR 17196 /* -- BORDER-RADIUS -- */ 17197 if ($brbgTR_H || $brbgTR_V) { 17198 $s .= $this->_EllipseArc($bgx1 - $brbgTR_H, $bgy0 + $brbgTR_V, $brbgTR_H, $brbgTR_V, 1); // segment 1 TR 17199 } 17200 /* -- END BORDER-RADIUS -- */ 17201 $s .= sprintf('%.3F %.3F l ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // line to TL 17202 // Box Shadow 17203 $shadow = ''; 17204 if (isset($this->blk[$blvl]['box_shadow']) && $this->blk[$blvl]['box_shadow'] && $h > 0) { 17205 foreach ($this->blk[$blvl]['box_shadow'] as $sh) { 17206 // Colors 17207 if ($sh['col'][0] == 1) { 17208 $colspace = 'Gray'; 17209 if ($sh['col'][2] == 1) { 17210 $col1 = '1' . $sh['col'][1] . '1' . $sh['col'][3]; 17211 } else { 17212 $col1 = '1' . $sh['col'][1] . '1' . chr(100); 17213 } 17214 $col2 = '1' . $sh['col'][1] . '1' . chr(0); 17215 } elseif ($sh['col'][0] == 4) { // CMYK 17216 $colspace = 'CMYK'; 17217 $col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(100); 17218 $col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0); 17219 } elseif ($sh['col'][0] == 5) { // RGBa 17220 $colspace = 'RGB'; 17221 $col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4]; 17222 $col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0); 17223 } elseif ($sh['col'][0] == 6) { // CMYKa 17224 $colspace = 'CMYK'; 17225 $col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . $sh['col'][5]; 17226 $col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0); 17227 } else { 17228 $colspace = 'RGB'; 17229 $col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(100); 17230 $col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0); 17231 } 17232 17233 // Use clipping path as set above (and rectangle around page) to clip area outside box 17234 $shadow .= $s; // Use the clipping path with W* 17235 $shadow .= sprintf('0 %.3F m %.3F %.3F l ', $this->h * Mpdf::SCALE, $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE); 17236 $shadow .= sprintf('%.3F 0 l 0 0 l 0 %.3F l ', $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE); 17237 $shadow .= 'W n' . "\n"; 17238 17239 $sh['blur'] = abs($sh['blur']); // cannot have negative blur value 17240 // Ensure spread/blur do not make effective shadow width/height < 0 17241 // Could do more complex things but this just adjusts spread value 17242 if (-$sh['spread'] + $sh['blur'] / 2 > min($w / 2, $h / 2)) { 17243 $sh['spread'] = $sh['blur'] / 2 - min($w / 2, $h / 2) + 0.01; 17244 } 17245 // Shadow Offset 17246 if ($sh['x'] || $sh['y']) { 17247 $shadow .= sprintf(' q 1 0 0 1 %.4F %.4F cm', $sh['x'] * Mpdf::SCALE, -$sh['y'] * Mpdf::SCALE) . "\n"; 17248 } 17249 17250 // Set path for INNER shadow 17251 $shadow .= ' q 0 w '; 17252 $shadow .= $this->SetFColor($col1, true) . "\n"; 17253 if ($col1[0] == 5 && ord($col1[4]) < 100) { // RGBa 17254 $shadow .= $this->SetAlpha(ord($col1[4]) / 100, 'Normal', true, 'F') . "\n"; 17255 } elseif ($col1[0] == 6 && ord($col1[5]) < 100) { // CMYKa 17256 $shadow .= $this->SetAlpha(ord($col1[5]) / 100, 'Normal', true, 'F') . "\n"; 17257 } elseif ($col1[0] == 1 && $col1[2] == 1 && ord($col1[3]) < 100) { // Gray 17258 $shadow .= $this->SetAlpha(ord($col1[3]) / 100, 'Normal', true, 'F') . "\n"; 17259 } 17260 17261 // Blur edges 17262 $mag = 0.551784; // Bezier Control magic number for 4-part spline for circle/ellipse 17263 $mag2 = 0.551784; // Bezier Control magic number to fill in edge of blurred rectangle 17264 $d1 = $sh['spread'] + $sh['blur'] / 2; 17265 $d2 = $sh['spread'] - $sh['blur'] / 2; 17266 $bl = $sh['blur']; 17267 $x00 = $x0 - $d1; 17268 $y00 = $y0 - $d1; 17269 $w00 = $w + $d1 * 2; 17270 $h00 = $h + $d1 * 2; 17271 17272 // If any border-radius is greater width-negative spread(inner edge), ignore radii for shadow or screws up 17273 $flatten = false; 17274 if (max($brbgTR_H, $brbgTL_H, $brbgBR_H, $brbgBL_H) >= $w + $d2) { 17275 $flatten = true; 17276 } 17277 if (max($brbgTR_V, $brbgTL_V, $brbgBR_V, $brbgBL_V) >= $h + $d2) { 17278 $flatten = true; 17279 } 17280 17281 17282 // TOP RIGHT corner 17283 $p1x = $x00 + $w00 - $d1 - $brbgTR_H; 17284 $p1c2x = $p1x + ($d2 + $brbgTR_H) * $mag; 17285 $p1y = $y00 + $bl; 17286 $p2x = $x00 + $w00 - $d1 - $brbgTR_H; 17287 $p2c2x = $p2x + ($d1 + $brbgTR_H) * $mag; 17288 $p2y = $y00; 17289 $p2c1y = $p2y + $bl / 2; 17290 $p3x = $x00 + $w00; 17291 $p3c2x = $p3x - $bl / 2; 17292 $p3y = $y00 + $d1 + $brbgTR_V; 17293 $p3c1y = $p3y - ($d1 + $brbgTR_V) * $mag; 17294 $p4x = $x00 + $w00 - $bl; 17295 $p4y = $y00 + $d1 + $brbgTR_V; 17296 $p4c2y = $p4y - ($d2 + $brbgTR_V) * $mag; 17297 if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) { 17298 $p1x = $x00 + $w00 - $bl; 17299 $p1c2x = $p1x; 17300 $p2x = $x00 + $w00 - $bl; 17301 $p2c2x = $p2x + $bl * $mag2; 17302 $p3y = $y00 + $bl; 17303 $p3c1y = $p3y - $bl * $mag2; 17304 $p4y = $y00 + $bl; 17305 $p4c2y = $p4y; 17306 } 17307 17308 $shadow .= sprintf('%.3F %.3F m ', ($p1x ) * Mpdf::SCALE, ($this->h - ($p1y )) * Mpdf::SCALE); 17309 $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1c2x) * Mpdf::SCALE, ($this->h - ($p1y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4c2y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE); 17310 $patch_array[0]['f'] = 0; 17311 $patch_array[0]['points'] = [$p1x, $p1y, $p1x, $p1y, 17312 $p2x, $p2c1y, $p2x, $p2y, $p2c2x, $p2y, 17313 $p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y, 17314 $p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y, 17315 $p1c2x, $p1y]; 17316 $patch_array[0]['colors'] = [$col1, $col2, $col2, $col1]; 17317 17318 17319 // RIGHT 17320 $p1x = $x00 + $w00; // control point only matches p3 preceding 17321 $p1y = $y00 + $d1 + $brbgTR_V; 17322 $p2x = $x00 + $w00 - $bl; // control point only matches p4 preceding 17323 $p2y = $y00 + $d1 + $brbgTR_V; 17324 $p3x = $x00 + $w00 - $bl; 17325 $p3y = $y00 + $h00 - $d1 - $brbgBR_V; 17326 $p4x = $x00 + $w00; 17327 $p4c1x = $p4x - $bl / 2; 17328 $p4y = $y00 + $h00 - $d1 - $brbgBR_V; 17329 if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) { 17330 $p1y = $y00 + $bl; 17331 $p2y = $y00 + $bl; 17332 } 17333 if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) { 17334 $p3y = $y00 + $h00 - $bl; 17335 $p4y = $y00 + $h00 - $bl; 17336 } 17337 17338 $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE); 17339 $patch_array[1]['f'] = 2; 17340 $patch_array[1]['points'] = [$p2x, $p2y, 17341 $p3x, $p3y, $p3x, $p3y, $p3x, $p3y, 17342 $p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y, 17343 $p1x, $p1y]; 17344 $patch_array[1]['colors'] = [$col1, $col2]; 17345 17346 17347 // BOTTOM RIGHT corner 17348 $p1x = $x00 + $w00 - $bl; // control points only matches p3 preceding 17349 $p1y = $y00 + $h00 - $d1 - $brbgBR_V; 17350 $p1c2y = $p1y + ($d2 + $brbgBR_V) * $mag; 17351 $p2x = $x00 + $w00; // control point only matches p4 preceding 17352 $p2y = $y00 + $h00 - $d1 - $brbgBR_V; 17353 $p2c2y = $p2y + ($d1 + $brbgBR_V) * $mag; 17354 $p3x = $x00 + $w00 - $d1 - $brbgBR_H; 17355 $p3c1x = $p3x + ($d1 + $brbgBR_H) * $mag; 17356 $p3y = $y00 + $h00; 17357 $p3c2y = $p3y - $bl / 2; 17358 $p4x = $x00 + $w00 - $d1 - $brbgBR_H; 17359 $p4c2x = $p4x + ($d2 + $brbgBR_H) * $mag; 17360 $p4y = $y00 + $h00 - $bl; 17361 17362 if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) { 17363 $p1y = $y00 + $h00 - $bl; 17364 $p1c2y = $p1y; 17365 $p2y = $y00 + $h00 - $bl; 17366 $p2c2y = $p2y + $bl * $mag2; 17367 $p3x = $x00 + $w00 - $bl; 17368 $p3c1x = $p3x + $bl * $mag2; 17369 $p4x = $x00 + $w00 - $bl; 17370 $p4c2x = $p4x; 17371 } 17372 17373 $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1x) * Mpdf::SCALE, ($this->h - ($p1c2y)) * Mpdf::SCALE, ($p4c2x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE); 17374 $patch_array[2]['f'] = 2; 17375 $patch_array[2]['points'] = [$p2x, $p2c2y, 17376 $p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y, 17377 $p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y, 17378 $p1x, $p1c2y]; 17379 $patch_array[2]['colors'] = [$col2, $col1]; 17380 17381 17382 17383 // BOTTOM 17384 $p1x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p3 preceding 17385 $p1y = $y00 + $h00; 17386 $p2x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p4 preceding 17387 $p2y = $y00 + $h00 - $bl; 17388 $p3x = $x00 + $d1 + $brbgBL_H; 17389 $p3y = $y00 + $h00 - $bl; 17390 $p4x = $x00 + $d1 + $brbgBL_H; 17391 $p4y = $y00 + $h00; 17392 $p4c1y = $p4y - $bl / 2; 17393 17394 if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) { 17395 $p1x = $x00 + $w00 - $bl; 17396 $p2x = $x00 + $w00 - $bl; 17397 } 17398 if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) { 17399 $p3x = $x00 + $bl; 17400 $p4x = $x00 + $bl; 17401 } 17402 17403 $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE); 17404 $patch_array[3]['f'] = 2; 17405 $patch_array[3]['points'] = [$p2x, $p2y, 17406 $p3x, $p3y, $p3x, $p3y, $p3x, $p3y, 17407 $p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y, 17408 $p1x, $p1y]; 17409 $patch_array[3]['colors'] = [$col1, $col2]; 17410 17411 // BOTTOM LEFT corner 17412 $p1x = $x00 + $d1 + $brbgBL_H; 17413 $p1c2x = $p1x - ($d2 + $brbgBL_H) * $mag; // control points only matches p3 preceding 17414 $p1y = $y00 + $h00 - $bl; 17415 $p2x = $x00 + $d1 + $brbgBL_H; 17416 $p2c2x = $p2x - ($d1 + $brbgBL_H) * $mag; // control point only matches p4 preceding 17417 $p2y = $y00 + $h00; 17418 $p3x = $x00; 17419 $p3c2x = $p3x + $bl / 2; 17420 $p3y = $y00 + $h00 - $d1 - $brbgBL_V; 17421 $p3c1y = $p3y + ($d1 + $brbgBL_V) * $mag; 17422 $p4x = $x00 + $bl; 17423 $p4y = $y00 + $h00 - $d1 - $brbgBL_V; 17424 $p4c2y = $p4y + ($d2 + $brbgBL_V) * $mag; 17425 if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) { 17426 $p1x = $x00 + $bl; 17427 $p1c2x = $p1x; 17428 $p2x = $x00 + $bl; 17429 $p2c2x = $p2x - $bl * $mag2; 17430 $p3y = $y00 + $h00 - $bl; 17431 $p3c1y = $p3y + $bl * $mag2; 17432 $p4y = $y00 + $h00 - $bl; 17433 $p4c2y = $p4y; 17434 } 17435 17436 $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1c2x) * Mpdf::SCALE, ($this->h - ($p1y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4c2y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE); 17437 $patch_array[4]['f'] = 2; 17438 $patch_array[4]['points'] = [$p2c2x, $p2y, 17439 $p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y, 17440 $p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y, 17441 $p1c2x, $p1y]; 17442 $patch_array[4]['colors'] = [$col2, $col1]; 17443 17444 17445 // LEFT - joins on the right (C3-C4 of previous): f = 2 17446 $p1x = $x00; // control point only matches p3 preceding 17447 $p1y = $y00 + $h00 - $d1 - $brbgBL_V; 17448 $p2x = $x00 + $bl; // control point only matches p4 preceding 17449 $p2y = $y00 + $h00 - $d1 - $brbgBL_V; 17450 $p3x = $x00 + $bl; 17451 $p3y = $y00 + $d1 + $brbgTL_V; 17452 $p4x = $x00; 17453 $p4c1x = $p4x + $bl / 2; 17454 $p4y = $y00 + $d1 + $brbgTL_V; 17455 if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) { 17456 $p1y = $y00 + $h00 - $bl; 17457 $p2y = $y00 + $h00 - $bl; 17458 } 17459 if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) { 17460 $p3y = $y00 + $bl; 17461 $p4y = $y00 + $bl; 17462 } 17463 17464 $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE); 17465 $patch_array[5]['f'] = 2; 17466 $patch_array[5]['points'] = [$p2x, $p2y, 17467 $p3x, $p3y, $p3x, $p3y, $p3x, $p3y, 17468 $p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y, 17469 $p1x, $p1y]; 17470 $patch_array[5]['colors'] = [$col1, $col2]; 17471 17472 // TOP LEFT corner 17473 $p1x = $x00 + $bl; // control points only matches p3 preceding 17474 $p1y = $y00 + $d1 + $brbgTL_V; 17475 $p1c2y = $p1y - ($d2 + $brbgTL_V) * $mag; 17476 $p2x = $x00; // control point only matches p4 preceding 17477 $p2y = $y00 + $d1 + $brbgTL_V; 17478 $p2c2y = $p2y - ($d1 + $brbgTL_V) * $mag; 17479 $p3x = $x00 + $d1 + $brbgTL_H; 17480 $p3c1x = $p3x - ($d1 + $brbgTL_H) * $mag; 17481 $p3y = $y00; 17482 $p3c2y = $p3y + $bl / 2; 17483 $p4x = $x00 + $d1 + $brbgTL_H; 17484 $p4c2x = $p4x - ($d2 + $brbgTL_H) * $mag; 17485 $p4y = $y00 + $bl; 17486 17487 if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) { 17488 $p1y = $y00 + $bl; 17489 $p1c2y = $p1y; 17490 $p2y = $y00 + $bl; 17491 $p2c2y = $p2y - $bl * $mag2; 17492 $p3x = $x00 + $bl; 17493 $p3c1x = $p3x - $bl * $mag2; 17494 $p4x = $x00 + $bl; 17495 $p4c2x = $p4x; 17496 } 17497 17498 $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1x) * Mpdf::SCALE, ($this->h - ($p1c2y)) * Mpdf::SCALE, ($p4c2x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE); 17499 $patch_array[6]['f'] = 2; 17500 $patch_array[6]['points'] = [$p2x, $p2c2y, 17501 $p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y, 17502 $p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y, 17503 $p1x, $p1c2y]; 17504 $patch_array[6]['colors'] = [$col2, $col1]; 17505 17506 17507 // TOP - joins on the right (C3-C4 of previous): f = 2 17508 $p1x = $x00 + $d1 + $brbgTL_H; // control point only matches p3 preceding 17509 $p1y = $y00; 17510 $p2x = $x00 + $d1 + $brbgTL_H; // control point only matches p4 preceding 17511 $p2y = $y00 + $bl; 17512 $p3x = $x00 + $w00 - $d1 - $brbgTR_H; 17513 $p3y = $y00 + $bl; 17514 $p4x = $x00 + $w00 - $d1 - $brbgTR_H; 17515 $p4y = $y00; 17516 $p4c1y = $p4y + $bl / 2; 17517 if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) { 17518 $p1x = $x00 + $bl; 17519 $p2x = $x00 + $bl; 17520 } 17521 if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) { 17522 $p3x = $x00 + $w00 - $bl; 17523 $p4x = $x00 + $w00 - $bl; 17524 } 17525 17526 $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE); 17527 $patch_array[7]['f'] = 2; 17528 $patch_array[7]['points'] = [$p2x, $p2y, 17529 $p3x, $p3y, $p3x, $p3y, $p3x, $p3y, 17530 $p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y, 17531 $p1x, $p1y]; 17532 $patch_array[7]['colors'] = [$col1, $col2]; 17533 17534 $shadow .= ' h f Q ' . "\n"; // Close path and Fill the inner solid shadow 17535 17536 if ($bl) { 17537 $shadow .= $this->gradient->CoonsPatchMesh($x00, $y00, $w00, $h00, $patch_array, $x00, $x00 + $w00, $y00, $y00 + $h00, $colspace, true); 17538 } 17539 17540 if ($sh['x'] || $sh['y']) { 17541 $shadow .= ' Q' . "\n"; // Shadow Offset 17542 } 17543 $shadow .= ' Q' . "\n"; // Ends path no-op & Sets the clipping path 17544 } 17545 } 17546 17547 $s .= ' W n '; // Ends path no-op & Sets the clipping path 17548 17549 if ($this->blk[$blvl]['bgcolor']) { 17550 $this->pageBackgrounds[$blvl][] = [ 17551 'x' => $x0, 17552 'y' => $y0, 17553 'w' => $w, 17554 'h' => $h, 17555 'col' => $this->blk[$blvl]['bgcolorarray'], 17556 'clippath' => $s, 17557 'visibility' => $this->visibility, 17558 'shadow' => $shadow, 17559 'z-index' => $this->current_layer, 17560 ]; 17561 } elseif ($shadow) { 17562 $this->pageBackgrounds[$blvl][] = [ 17563 'x' => 0, 17564 'y' => 0, 17565 'w' => 0, 17566 'h' => 0, 17567 'shadowonly' => true, 17568 'col' => '', 17569 'clippath' => '', 17570 'visibility' => $this->visibility, 17571 'shadow' => $shadow, 17572 'z-index' => $this->current_layer, 17573 ]; 17574 } 17575 17576 /* -- BACKGROUNDS -- */ 17577 if (isset($this->blk[$blvl]['gradient'])) { 17578 $g = $this->gradient->parseBackgroundGradient($this->blk[$blvl]['gradient']); 17579 if ($g) { 17580 $gx = $x0; 17581 $gy = $y0; 17582 $this->pageBackgrounds[$blvl][] = [ 17583 'gradient' => true, 17584 'x' => $gx, 17585 'y' => $gy, 17586 'w' => $w, 17587 'h' => $h, 17588 'gradtype' => $g['type'], 17589 'stops' => $g['stops'], 17590 'colorspace' => $g['colorspace'], 17591 'coords' => $g['coords'], 17592 'extend' => $g['extend'], 17593 'clippath' => $s, 17594 'visibility' => $this->visibility, 17595 'z-index' => $this->current_layer 17596 ]; 17597 } 17598 } 17599 17600 if (isset($this->blk[$blvl]['background-image'])) { 17601 if (isset($this->blk[$blvl]['background-image']['gradient']) && $this->blk[$blvl]['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->blk[$blvl]['background-image']['gradient'])) { 17602 $g = $this->gradient->parseMozGradient($this->blk[$blvl]['background-image']['gradient']); 17603 if ($g) { 17604 $gx = $x0; 17605 $gy = $y0; 17606 // origin specifies the background-positioning-area (bpa) 17607 if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') { 17608 $gx += $this->blk[$blvl]['border_left']['w']; 17609 $w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']); 17610 if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) { 17611 $gy += $this->blk[$blvl]['border_top']['w']; 17612 } 17613 if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') { 17614 $gy1 = $y1 - $this->blk[$blvl]['border_bottom']['w']; 17615 } else { 17616 $gy1 = $y1; 17617 } 17618 $h = $gy1 - $gy; 17619 } elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') { 17620 $gx += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left']; 17621 $w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']); 17622 if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) { 17623 $gy += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top']; 17624 } 17625 if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') { 17626 $gy1 = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']); 17627 } else { 17628 $gy1 = $y1 - $this->blk[$blvl]['padding_bottom']; 17629 } 17630 $h = $gy1 - $gy; 17631 } 17632 17633 if (isset($this->blk[$blvl]['background-image']['size']['w']) && $this->blk[$blvl]['background-image']['size']['w']) { 17634 $size = $this->blk[$blvl]['background-image']['size']; 17635 if ($size['w'] != 'contain' && $size['w'] != 'cover') { 17636 if (stristr($size['w'], '%')) { 17637 $size['w'] = (float) $size['w']; 17638 $size['w'] /= 100; 17639 $w *= $size['w']; 17640 } elseif ($size['w'] != 'auto') { 17641 $w = $size['w']; 17642 } 17643 if (stristr($size['h'], '%')) { 17644 $size['h'] = (float) $size['h']; 17645 $size['h'] /= 100; 17646 $h *= $size['h']; 17647 } elseif ($size['h'] != 'auto') { 17648 $h = $size['h']; 17649 } 17650 } 17651 } 17652 $this->pageBackgrounds[$blvl][] = [ 17653 'gradient' => true, 17654 'x' => $gx, 17655 'y' => $gy, 17656 'w' => $w, 17657 'h' => $h, 17658 'gradtype' => $g['type'], 17659 'stops' => $g['stops'], 17660 'colorspace' => $g['colorspace'], 17661 'coords' => $g['coords'], 17662 'extend' => $g['extend'], 17663 'clippath' => $s, 17664 'visibility' => $this->visibility, 17665 'z-index' => $this->current_layer 17666 ]; 17667 } 17668 17669 } else { 17670 17671 $image_id = $this->blk[$blvl]['background-image']['image_id']; 17672 $orig_w = $this->blk[$blvl]['background-image']['orig_w']; 17673 $orig_h = $this->blk[$blvl]['background-image']['orig_h']; 17674 $x_pos = $this->blk[$blvl]['background-image']['x_pos']; 17675 $y_pos = $this->blk[$blvl]['background-image']['y_pos']; 17676 $x_repeat = $this->blk[$blvl]['background-image']['x_repeat']; 17677 $y_repeat = $this->blk[$blvl]['background-image']['y_repeat']; 17678 $resize = $this->blk[$blvl]['background-image']['resize']; 17679 $opacity = $this->blk[$blvl]['background-image']['opacity']; 17680 $itype = $this->blk[$blvl]['background-image']['itype']; 17681 $size = $this->blk[$blvl]['background-image']['size']; 17682 // origin specifies the background-positioning-area (bpa) 17683 17684 $bpa = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h]; 17685 17686 if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') { 17687 17688 $bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w']; 17689 $bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']); 17690 if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) { 17691 $bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w']; 17692 } else { 17693 $bpa['y'] = $y0; 17694 } 17695 if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') { 17696 $bpay = $y1 - $this->blk[$blvl]['border_bottom']['w']; 17697 } else { 17698 $bpay = $y1; 17699 } 17700 $bpa['h'] = $bpay - $bpa['y']; 17701 17702 } elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') { 17703 17704 $bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left']; 17705 $bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']); 17706 if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) { 17707 $bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top']; 17708 } else { 17709 $bpa['y'] = $y0 + $this->blk[$blvl]['padding_top']; 17710 } 17711 if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') { 17712 $bpay = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']); 17713 } else { 17714 $bpay = $y1 - $this->blk[$blvl]['padding_bottom']; 17715 } 17716 $bpa['h'] = $bpay - $bpa['y']; 17717 17718 } 17719 17720 $this->pageBackgrounds[$blvl][] = [ 17721 'x' => $x0, 17722 'y' => $y0, 17723 'w' => $w, 17724 'h' => $h, 17725 'image_id' => $image_id, 17726 'orig_w' => $orig_w, 17727 'orig_h' => $orig_h, 17728 'x_pos' => $x_pos, 17729 'y_pos' => $y_pos, 17730 'x_repeat' => $x_repeat, 17731 'y_repeat' => $y_repeat, 17732 'clippath' => $s, 17733 'resize' => $resize, 17734 'opacity' => $opacity, 17735 'itype' => $itype, 17736 'visibility' => $this->visibility, 17737 'z-index' => $this->current_layer, 17738 'size' => $size, 17739 'bpa' => $bpa 17740 ]; 17741 } 17742 } 17743 /* -- END BACKGROUNDS -- */ 17744 17745 // Float DIV 17746 $this->blk[$blvl]['bb_painted'][$this->page] = true; 17747 } 17748 /* -- BORDER-RADIUS -- */ 17749 17750 function _EllipseArc($x0, $y0, $rx, $ry, $seg = 1, $part = false, $start = false) 17751 { 17752 // Anticlockwise segment 1-4 TR-TL-BL-BR (part=1 or 2) 17753 $s = ''; 17754 17755 if ($rx < 0) { 17756 $rx = 0; 17757 } 17758 17759 if ($ry < 0) { 17760 $ry = 0; 17761 } 17762 17763 $rx *= Mpdf::SCALE; 17764 $ry *= Mpdf::SCALE; 17765 17766 $astart = 0; 17767 17768 if ($seg == 1) { // Top Right 17769 $afinish = 90; 17770 $nSeg = 4; 17771 } elseif ($seg == 2) { // Top Left 17772 $afinish = 180; 17773 $nSeg = 8; 17774 } elseif ($seg == 3) { // Bottom Left 17775 $afinish = 270; 17776 $nSeg = 12; 17777 } else { // Bottom Right 17778 $afinish = 360; 17779 $nSeg = 16; 17780 } 17781 17782 $astart = deg2rad((float) $astart); 17783 $afinish = deg2rad((float) $afinish); 17784 17785 $totalAngle = $afinish - $astart; 17786 $dt = $totalAngle / $nSeg; // segment angle 17787 $dtm = $dt / 3; 17788 $x0 *= Mpdf::SCALE; 17789 $y0 = ($this->h - $y0) * Mpdf::SCALE; 17790 $t1 = $astart; 17791 $a0 = $x0 + ($rx * cos($t1)); 17792 $b0 = $y0 + ($ry * sin($t1)); 17793 $c0 = -$rx * sin($t1); 17794 $d0 = $ry * cos($t1); 17795 $op = false; 17796 17797 for ($i = 1; $i <= $nSeg; $i++) { 17798 // Draw this bit of the total curve 17799 $t1 = ($i * $dt) + $astart; 17800 $a1 = $x0 + ($rx * cos($t1)); 17801 $b1 = $y0 + ($ry * sin($t1)); 17802 $c1 = -$rx * sin($t1); 17803 $d1 = $ry * cos($t1); 17804 if ($i > ($nSeg - 4) && (!$part || ($part == 1 && $i <= $nSeg - 2) || ($part == 2 && $i > $nSeg - 2))) { 17805 if ($start && !$op) { 17806 $s .= sprintf('%.3F %.3F m ', $a0, $b0); 17807 } 17808 $s .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($a0 + ($c0 * $dtm)), ($b0 + ($d0 * $dtm)), ($a1 - ($c1 * $dtm)), ($b1 - ($d1 * $dtm)), $a1, $b1); 17809 $op = true; 17810 } 17811 $a0 = $a1; 17812 $b0 = $b1; 17813 $c0 = $c1; 17814 $d0 = $d1; 17815 } 17816 17817 return $s; 17818 } 17819 17820 /* -- END BORDER-RADIUS -- */ 17821 17822 function PaintDivLnBorder($state = 0, $blvl = 0, $h = 0) 17823 { 17824 // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom 17825 $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h; 17826 17827 $save_y = $this->y; 17828 17829 $w = $this->blk[$blvl]['width']; 17830 $x0 = $this->x; // left 17831 $y0 = $this->y; // top 17832 $x1 = $this->x + $w; // bottom 17833 $y1 = $this->y + $h; // bottom 17834 $continuingpage = (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page); 17835 17836 if ($this->blk[$blvl]['border_top'] && ($state == 1 || $state == 3)) { 17837 $tbd = $this->blk[$blvl]['border_top']; 17838 if (isset($tbd['s']) && $tbd['s']) { 17839 $this->_setBorderLine($tbd); 17840 $this->y = $y0 + ($tbd['w'] / 2); 17841 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17842 $this->_setDashBorder($tbd['style'], '', $continuingpage, 'T'); 17843 $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y); 17844 } else { 17845 $this->SetLineJoin(0); 17846 $this->SetLineCap(0); 17847 $this->Line($x0, $this->y, $x0 + $w, $this->y); 17848 } 17849 $this->y += $tbd['w']; 17850 // Reset Corners and Dash off 17851 $this->SetLineJoin(2); 17852 $this->SetLineCap(2); 17853 $this->SetDash(); 17854 } 17855 } 17856 if ($this->blk[$blvl]['border_left']) { 17857 $tbd = $this->blk[$blvl]['border_left']; 17858 if (isset($tbd['s']) && $tbd['s']) { 17859 $this->_setBorderLine($tbd); 17860 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17861 $this->y = $y0 + ($tbd['w'] / 2); 17862 $this->_setDashBorder($tbd['style'], '', $continuingpage, 'L'); 17863 $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2)); 17864 } else { 17865 $this->y = $y0; 17866 $this->SetLineJoin(0); 17867 $this->SetLineCap(0); 17868 $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h); 17869 } 17870 $this->y += $tbd['w']; 17871 // Reset Corners and Dash off 17872 $this->SetLineJoin(2); 17873 $this->SetLineCap(2); 17874 $this->SetDash(); 17875 } 17876 } 17877 if ($this->blk[$blvl]['border_right']) { 17878 $tbd = $this->blk[$blvl]['border_right']; 17879 if (isset($tbd['s']) && $tbd['s']) { 17880 $this->_setBorderLine($tbd); 17881 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17882 $this->y = $y0 + ($tbd['w'] / 2); 17883 $this->_setDashBorder($tbd['style'], '', $continuingpage, 'R'); 17884 $this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2)); 17885 } else { 17886 $this->y = $y0; 17887 $this->SetLineJoin(0); 17888 $this->SetLineCap(0); 17889 $this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h); 17890 } 17891 $this->y += $tbd['w']; 17892 // Reset Corners and Dash off 17893 $this->SetLineJoin(2); 17894 $this->SetLineCap(2); 17895 $this->SetDash(); 17896 } 17897 } 17898 if ($this->blk[$blvl]['border_bottom'] && $state > 1) { 17899 $tbd = $this->blk[$blvl]['border_bottom']; 17900 if (isset($tbd['s']) && $tbd['s']) { 17901 $this->_setBorderLine($tbd); 17902 $this->y = $y0 + $h - ($tbd['w'] / 2); 17903 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17904 $this->_setDashBorder($tbd['style'], '', $continuingpage, 'B'); 17905 $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y); 17906 } else { 17907 $this->SetLineJoin(0); 17908 $this->SetLineCap(0); 17909 $this->Line($x0, $this->y, $x0 + $w, $this->y); 17910 } 17911 $this->y += $tbd['w']; 17912 // Reset Corners and Dash off 17913 $this->SetLineJoin(2); 17914 $this->SetLineCap(2); 17915 $this->SetDash(); 17916 } 17917 } 17918 $this->SetDash(); 17919 $this->y = $save_y; 17920 } 17921 17922 function PaintImgBorder($objattr, $is_table) 17923 { 17924 // Borders are disabled in columns - messes up the repositioning in printcolumnbuffer 17925 if ($this->ColActive) { 17926 return; 17927 } // *COLUMNS* 17928 if ($is_table) { 17929 $k = $this->shrin_k; 17930 } else { 17931 $k = 1; 17932 } 17933 $h = (isset($objattr['BORDER-HEIGHT']) ? $objattr['BORDER-HEIGHT'] : 0); 17934 $w = (isset($objattr['BORDER-WIDTH']) ? $objattr['BORDER-WIDTH'] : 0); 17935 $x0 = (isset($objattr['BORDER-X']) ? $objattr['BORDER-X'] : 0); 17936 $y0 = (isset($objattr['BORDER-Y']) ? $objattr['BORDER-Y'] : 0); 17937 17938 // BORDERS 17939 if ($objattr['border_top']) { 17940 $tbd = $objattr['border_top']; 17941 if (!empty($tbd['s'])) { 17942 $this->_setBorderLine($tbd, $k); 17943 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17944 $this->_setDashBorder($tbd['style'], '', '', 'T'); 17945 } 17946 $this->Line($x0, $y0, $x0 + $w, $y0); 17947 // Reset Corners and Dash off 17948 $this->SetLineJoin(2); 17949 $this->SetLineCap(2); 17950 $this->SetDash(); 17951 } 17952 } 17953 if ($objattr['border_left']) { 17954 $tbd = $objattr['border_left']; 17955 if (!empty($tbd['s'])) { 17956 $this->_setBorderLine($tbd, $k); 17957 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17958 $this->_setDashBorder($tbd['style'], '', '', 'L'); 17959 } 17960 $this->Line($x0, $y0, $x0, $y0 + $h); 17961 // Reset Corners and Dash off 17962 $this->SetLineJoin(2); 17963 $this->SetLineCap(2); 17964 $this->SetDash(); 17965 } 17966 } 17967 if ($objattr['border_right']) { 17968 $tbd = $objattr['border_right']; 17969 if (!empty($tbd['s'])) { 17970 $this->_setBorderLine($tbd, $k); 17971 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17972 $this->_setDashBorder($tbd['style'], '', '', 'R'); 17973 } 17974 $this->Line($x0 + $w, $y0, $x0 + $w, $y0 + $h); 17975 // Reset Corners and Dash off 17976 $this->SetLineJoin(2); 17977 $this->SetLineCap(2); 17978 $this->SetDash(); 17979 } 17980 } 17981 if ($objattr['border_bottom']) { 17982 $tbd = $objattr['border_bottom']; 17983 if (!empty($tbd['s'])) { 17984 $this->_setBorderLine($tbd, $k); 17985 if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') { 17986 $this->_setDashBorder($tbd['style'], '', '', 'B'); 17987 } 17988 $this->Line($x0, $y0 + $h, $x0 + $w, $y0 + $h); 17989 // Reset Corners and Dash off 17990 $this->SetLineJoin(2); 17991 $this->SetLineCap(2); 17992 $this->SetDash(); 17993 } 17994 } 17995 $this->SetDash(); 17996 $this->SetAlpha(1); 17997 } 17998 17999 /* -- END HTML-CSS -- */ 18000 18001 function Reset() 18002 { 18003 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 18004 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 18005 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 18006 $this->SetAlpha(1); 18007 $this->colorarray = ''; 18008 18009 $this->spanbgcolorarray = ''; 18010 $this->spanbgcolor = false; 18011 $this->spanborder = false; 18012 $this->spanborddet = []; 18013 18014 $this->ResetStyles(); 18015 18016 $this->HREF = ''; 18017 $this->textparam = []; 18018 $this->SetTextOutline(); 18019 18020 $this->textvar = 0x00; // mPDF 5.7.1 18021 $this->OTLtags = []; 18022 $this->textshadow = ''; 18023 18024 $this->currentLang = $this->default_lang; // mPDF 6 18025 $this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6 18026 $this->SetFont($this->default_font, '', 0, false); 18027 $this->SetFontSize($this->default_font_size, false); 18028 18029 $this->currentfontfamily = ''; 18030 $this->currentfontsize = ''; 18031 $this->currentfontstyle = ''; 18032 18033 if ($this->tableLevel && isset($this->table[1][1]['cellLineHeight'])) { 18034 $this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); 18035 } else { 18036 if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) { 18037 $this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height 18038 } 18039 } 18040 18041 $this->lSpacingCSS = ''; 18042 $this->wSpacingCSS = ''; 18043 $this->fixedlSpacing = false; 18044 $this->minwSpacing = 0; 18045 $this->SetDash(); // restore to no dash 18046 $this->dash_on = false; 18047 $this->dotted_on = false; 18048 $this->divwidth = 0; 18049 $this->divheight = 0; 18050 $this->cellTextAlign = ''; 18051 $this->cellLineHeight = ''; 18052 $this->cellLineStackingStrategy = ''; 18053 $this->cellLineStackingShift = ''; 18054 $this->oldy = -1; 18055 18056 $bodystyle = []; 18057 18058 if (isset($this->cssManager->CSS['BODY']['FONT-STYLE'])) { 18059 $bodystyle['FONT-STYLE'] = $this->cssManager->CSS['BODY']['FONT-STYLE']; 18060 } 18061 18062 if (isset($this->cssManager->CSS['BODY']['FONT-WEIGHT'])) { 18063 $bodystyle['FONT-WEIGHT'] = $this->cssManager->CSS['BODY']['FONT-WEIGHT']; 18064 } 18065 18066 if (isset($this->cssManager->CSS['BODY']['COLOR'])) { 18067 $bodystyle['COLOR'] = $this->cssManager->CSS['BODY']['COLOR']; 18068 } 18069 18070 if (isset($bodystyle)) { 18071 $this->setCSS($bodystyle, 'BLOCK', 'BODY'); 18072 } 18073 } 18074 18075 /* -- HTML-CSS -- */ 18076 18077 function ReadMetaTags($html) 18078 { 18079 // changes anykey=anyvalue to anykey="anyvalue" (only do this when this happens inside tags) 18080 $regexp = '/ (\\w+?)=([^\\s>"]+)/si'; 18081 $html = preg_replace($regexp, " \$1=\"\$2\"", $html); 18082 if (preg_match('/<title>(.*?)<\/title>/si', $html, $m)) { 18083 $this->SetTitle($m[1]); 18084 } 18085 preg_match_all('/<meta [^>]*?(name|content)="([^>]*?)" [^>]*?(name|content)="([^>]*?)".*?>/si', $html, $aux); 18086 $firstattr = $aux[1]; 18087 $secondattr = $aux[3]; 18088 for ($i = 0; $i < count($aux[0]); $i++) { 18089 $name = ( strtoupper($firstattr[$i]) == "NAME" ) ? strtoupper($aux[2][$i]) : strtoupper($aux[4][$i]); 18090 $content = ( strtoupper($firstattr[$i]) == "CONTENT" ) ? $aux[2][$i] : $aux[4][$i]; 18091 switch ($name) { 18092 case "KEYWORDS": 18093 $this->SetKeywords($content); 18094 break; 18095 case "AUTHOR": 18096 $this->SetAuthor($content); 18097 break; 18098 case "DESCRIPTION": 18099 $this->SetSubject($content); 18100 break; 18101 } 18102 } 18103 } 18104 18105 function ReadCharset($html) 18106 { 18107 // Charset conversion 18108 if ($this->allow_charset_conversion) { 18109 if (preg_match('/<head.*charset=([^\'\"\s]*).*<\/head>/si', $html, $m)) { 18110 if (strtoupper($m[1]) != 'UTF-8') { 18111 $this->charset_in = strtoupper($m[1]); 18112 } 18113 } 18114 } 18115 } 18116 18117 function setCSS($arrayaux, $type = '', $tag = '') 18118 { 18119 // type= INLINE | BLOCK | TABLECELL // tag= BODY 18120 if (!is_array($arrayaux)) { 18121 return; // Removes PHP Warning 18122 } 18123 18124 // mPDF 5.7.3 inline text-decoration parameters 18125 $preceeding_fontkey = $this->FontFamily . $this->FontStyle; 18126 $preceeding_fontsize = $this->FontSize; 18127 $spanbordset = false; 18128 $spanbgset = false; 18129 // mPDF 6 18130 $prevlevel = (($this->blklvl == 0) ? 0 : $this->blklvl - 1); 18131 18132 // Set font size first so that e.g. MARGIN 0.83em works on font size for this element 18133 if (isset($arrayaux['FONT-SIZE'])) { 18134 $v = $arrayaux['FONT-SIZE']; 18135 $firstLetter = substr($v, 0, 1); 18136 if (is_numeric($firstLetter) || ($firstLetter === '.')) { 18137 if ($type == 'BLOCK' && $this->blklvl > 0 && isset($this->blk[$this->blklvl - 1]['InlineProperties']) && isset($this->blk[$this->blklvl - 1]['InlineProperties']['size'])) { 18138 $mmsize = $this->sizeConverter->convert($v, $this->blk[$this->blklvl - 1]['InlineProperties']['size']); 18139 } elseif ($type == 'TABLECELL') { 18140 $mmsize = $this->sizeConverter->convert($v, $this->default_font_size / Mpdf::SCALE); 18141 } else { 18142 $mmsize = $this->sizeConverter->convert($v, $this->FontSize); 18143 } 18144 $this->SetFontSize($mmsize * (Mpdf::SCALE), false); // Get size in points (pt) 18145 } else { 18146 $v = strtoupper($v); 18147 if (isset($this->fontsizes[$v])) { 18148 $this->SetFontSize($this->fontsizes[$v] * $this->default_font_size, false); 18149 } 18150 } 18151 if ($tag == 'BODY') { 18152 $this->SetDefaultFontSize($this->FontSizePt); 18153 } 18154 } 18155 18156 // mPDF 6 18157 if (isset($arrayaux['LANG']) && $arrayaux['LANG']) { 18158 if ($this->autoLangToFont && !$this->usingCoreFont) { 18159 if ($arrayaux['LANG'] != $this->default_lang && $arrayaux['LANG'] != 'UTF-8') { 18160 list ($coreSuitable, $mpdf_pdf_unifont) = $this->languageToFont->getLanguageOptions($arrayaux['LANG'], $this->useAdobeCJK); 18161 if ($mpdf_pdf_unifont) { 18162 $arrayaux['FONT-FAMILY'] = $mpdf_pdf_unifont; 18163 } 18164 if ($tag == 'BODY') { 18165 $this->default_lang = $arrayaux['LANG']; 18166 } 18167 } 18168 } 18169 $this->currentLang = $arrayaux['LANG']; 18170 } 18171 18172 // FOR INLINE and BLOCK OR 'BODY' 18173 if (isset($arrayaux['FONT-FAMILY'])) { 18174 $v = $arrayaux['FONT-FAMILY']; 18175 // If it is a font list, get all font types 18176 $aux_fontlist = explode(",", $v); 18177 $found = 0; 18178 foreach ($aux_fontlist as $f) { 18179 $fonttype = trim($f); 18180 $fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype); 18181 $fonttype = preg_replace('/ /', '', $fonttype); 18182 $v = strtolower(trim($fonttype)); 18183 if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) { 18184 $v = $this->fonttrans[$v]; 18185 } 18186 if ((!$this->onlyCoreFonts && in_array($v, $this->available_unifonts)) || 18187 in_array($v, ['ccourier', 'ctimes', 'chelvetica']) || 18188 ($this->onlyCoreFonts && in_array($v, ['courier', 'times', 'helvetica', 'arial'])) || 18189 in_array($v, ['sjis', 'uhc', 'big5', 'gb'])) { 18190 $fonttype = $v; 18191 $found = 1; 18192 break; 18193 } 18194 } 18195 if (!$found) { 18196 foreach ($aux_fontlist as $f) { 18197 $fonttype = trim($f); 18198 $fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype); 18199 $fonttype = preg_replace('/ /', '', $fonttype); 18200 $v = strtolower(trim($fonttype)); 18201 if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) { 18202 $v = $this->fonttrans[$v]; 18203 } 18204 if (in_array($v, $this->sans_fonts) || in_array($v, $this->serif_fonts) || in_array($v, $this->mono_fonts)) { 18205 $fonttype = $v; 18206 break; 18207 } 18208 } 18209 } 18210 18211 if ($tag == 'BODY') { 18212 $this->SetDefaultFont($fonttype); 18213 } 18214 $this->SetFont($fonttype, $this->currentfontstyle, 0, false); 18215 } else { 18216 $this->SetFont($this->currentfontfamily, $this->currentfontstyle, 0, false); 18217 } 18218 18219 foreach ($arrayaux as $k => $v) { 18220 if ($type != 'INLINE' && $tag != 'BODY' && $type != 'TABLECELL') { 18221 switch ($k) { 18222 // BORDERS 18223 case 'BORDER-TOP': 18224 $this->blk[$this->blklvl]['border_top'] = $this->border_details($v); 18225 if ($this->blk[$this->blklvl]['border_top']['s']) { 18226 $this->blk[$this->blklvl]['border'] = 1; 18227 } 18228 break; 18229 case 'BORDER-BOTTOM': 18230 $this->blk[$this->blklvl]['border_bottom'] = $this->border_details($v); 18231 if ($this->blk[$this->blklvl]['border_bottom']['s']) { 18232 $this->blk[$this->blklvl]['border'] = 1; 18233 } 18234 break; 18235 case 'BORDER-LEFT': 18236 $this->blk[$this->blklvl]['border_left'] = $this->border_details($v); 18237 if ($this->blk[$this->blklvl]['border_left']['s']) { 18238 $this->blk[$this->blklvl]['border'] = 1; 18239 } 18240 break; 18241 case 'BORDER-RIGHT': 18242 $this->blk[$this->blklvl]['border_right'] = $this->border_details($v); 18243 if ($this->blk[$this->blklvl]['border_right']['s']) { 18244 $this->blk[$this->blklvl]['border'] = 1; 18245 } 18246 break; 18247 18248 // PADDING 18249 case 'PADDING-TOP': 18250 $this->blk[$this->blklvl]['padding_top'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18251 break; 18252 case 'PADDING-BOTTOM': 18253 $this->blk[$this->blklvl]['padding_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18254 break; 18255 case 'PADDING-LEFT': 18256 if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') { 18257 $this->blk[$this->blklvl]['padding_left'] = 'auto'; 18258 break; 18259 } 18260 $this->blk[$this->blklvl]['padding_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18261 break; 18262 case 'PADDING-RIGHT': 18263 if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') { 18264 $this->blk[$this->blklvl]['padding_right'] = 'auto'; 18265 break; 18266 } 18267 $this->blk[$this->blklvl]['padding_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18268 break; 18269 18270 // MARGINS 18271 case 'MARGIN-TOP': 18272 $tmp = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18273 if (isset($this->blk[$this->blklvl]['lastbottommargin'])) { 18274 if ($tmp > $this->blk[$this->blklvl]['lastbottommargin']) { 18275 $tmp -= $this->blk[$this->blklvl]['lastbottommargin']; 18276 } else { 18277 $tmp = 0; 18278 } 18279 } 18280 $this->blk[$this->blklvl]['margin_top'] = $tmp; 18281 break; 18282 case 'MARGIN-BOTTOM': 18283 $this->blk[$this->blklvl]['margin_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18284 break; 18285 case 'MARGIN-LEFT': 18286 $this->blk[$this->blklvl]['margin_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18287 break; 18288 case 'MARGIN-RIGHT': 18289 $this->blk[$this->blklvl]['margin_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18290 break; 18291 18292 /* -- BORDER-RADIUS -- */ 18293 case 'BORDER-TOP-LEFT-RADIUS-H': 18294 $this->blk[$this->blklvl]['border_radius_TL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18295 break; 18296 case 'BORDER-TOP-LEFT-RADIUS-V': 18297 $this->blk[$this->blklvl]['border_radius_TL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18298 break; 18299 case 'BORDER-TOP-RIGHT-RADIUS-H': 18300 $this->blk[$this->blklvl]['border_radius_TR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18301 break; 18302 case 'BORDER-TOP-RIGHT-RADIUS-V': 18303 $this->blk[$this->blklvl]['border_radius_TR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18304 break; 18305 case 'BORDER-BOTTOM-LEFT-RADIUS-H': 18306 $this->blk[$this->blklvl]['border_radius_BL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18307 break; 18308 case 'BORDER-BOTTOM-LEFT-RADIUS-V': 18309 $this->blk[$this->blklvl]['border_radius_BL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18310 break; 18311 case 'BORDER-BOTTOM-RIGHT-RADIUS-H': 18312 $this->blk[$this->blklvl]['border_radius_BR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18313 break; 18314 case 'BORDER-BOTTOM-RIGHT-RADIUS-V': 18315 $this->blk[$this->blklvl]['border_radius_BR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18316 break; 18317 /* -- END BORDER-RADIUS -- */ 18318 18319 case 'BOX-SHADOW': 18320 $bs = $this->cssManager->setCSSboxshadow($v); 18321 if ($bs) { 18322 $this->blk[$this->blklvl]['box_shadow'] = $bs; 18323 } 18324 break; 18325 18326 case 'BACKGROUND-CLIP': 18327 if (strtoupper($v) == 'PADDING-BOX') { 18328 $this->blk[$this->blklvl]['background_clip'] = 'padding-box'; 18329 } elseif (strtoupper($v) == 'CONTENT-BOX') { 18330 $this->blk[$this->blklvl]['background_clip'] = 'content-box'; 18331 } 18332 break; 18333 18334 case 'PAGE-BREAK-AFTER': 18335 if (strtoupper($v) == 'AVOID') { 18336 $this->blk[$this->blklvl]['page_break_after_avoid'] = true; 18337 } elseif (strtoupper($v) == 'ALWAYS' || strtoupper($v) == 'LEFT' || strtoupper($v) == 'RIGHT') { 18338 $this->blk[$this->blklvl]['page_break_after'] = strtoupper($v); 18339 } 18340 break; 18341 18342 // mPDF 6 pagebreaktype 18343 case 'BOX-DECORATION-BREAK': 18344 if (strtoupper($v) == 'CLONE') { 18345 $this->blk[$this->blklvl]['box_decoration_break'] = 'clone'; 18346 } elseif (strtoupper($v) == 'SLICE') { 18347 $this->blk[$this->blklvl]['box_decoration_break'] = 'slice'; 18348 } 18349 break; 18350 18351 case 'WIDTH': 18352 if (strtoupper($v) != 'AUTO') { 18353 $this->blk[$this->blklvl]['css_set_width'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false); 18354 } 18355 break; 18356 18357 // mPDF 6 Lists 18358 // LISTS 18359 case 'LIST-STYLE-TYPE': 18360 $this->blk[$this->blklvl]['list_style_type'] = strtolower($v); 18361 break; 18362 case 'LIST-STYLE-IMAGE': 18363 $this->blk[$this->blklvl]['list_style_image'] = strtolower($v); 18364 break; 18365 case 'LIST-STYLE-POSITION': 18366 $this->blk[$this->blklvl]['list_style_position'] = strtolower($v); 18367 break; 18368 }//end of switch($k) 18369 } 18370 18371 18372 if ($type != 'INLINE' && $type != 'TABLECELL') { // All block-level, including BODY tag 18373 switch ($k) { 18374 case 'TEXT-INDENT': 18375 // Computed value - to inherit 18376 $this->blk[$this->blklvl]['text_indent'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false) . 'mm'; 18377 break; 18378 18379 case 'MARGIN-COLLAPSE': // Custom tag to collapse margins at top and bottom of page 18380 if (strtoupper($v) == 'COLLAPSE') { 18381 $this->blk[$this->blklvl]['margin_collapse'] = true; 18382 } 18383 break; 18384 18385 case 'LINE-HEIGHT': 18386 $this->blk[$this->blklvl]['line_height'] = $this->fixLineheight($v); 18387 if (!$this->blk[$this->blklvl]['line_height']) { 18388 $this->blk[$this->blklvl]['line_height'] = 'N'; 18389 } // mPDF 6 18390 break; 18391 18392 // mPDF 6 18393 case 'LINE-STACKING-STRATEGY': 18394 $this->blk[$this->blklvl]['line_stacking_strategy'] = strtolower($v); 18395 break; 18396 18397 case 'LINE-STACKING-SHIFT': 18398 $this->blk[$this->blklvl]['line_stacking_shift'] = strtolower($v); 18399 break; 18400 18401 case 'TEXT-ALIGN': // left right center justify 18402 switch (strtoupper($v)) { 18403 case 'LEFT': 18404 $this->blk[$this->blklvl]['align'] = "L"; 18405 break; 18406 case 'CENTER': 18407 $this->blk[$this->blklvl]['align'] = "C"; 18408 break; 18409 case 'RIGHT': 18410 $this->blk[$this->blklvl]['align'] = "R"; 18411 break; 18412 case 'JUSTIFY': 18413 $this->blk[$this->blklvl]['align'] = "J"; 18414 break; 18415 } 18416 break; 18417 18418 /* -- BACKGROUNDS -- */ 18419 case 'BACKGROUND-GRADIENT': 18420 if ($type == 'BLOCK') { 18421 $this->blk[$this->blklvl]['gradient'] = $v; 18422 } 18423 break; 18424 /* -- END BACKGROUNDS -- */ 18425 18426 case 'DIRECTION': 18427 if ($v) { 18428 $this->blk[$this->blklvl]['direction'] = strtolower($v); 18429 } 18430 break; 18431 } 18432 } 18433 18434 // FOR INLINE ONLY 18435 if ($type == 'INLINE') { 18436 switch ($k) { 18437 case 'DISPLAY': 18438 if (strtoupper($v) == 'NONE') { 18439 $this->inlineDisplayOff = true; 18440 } 18441 break; 18442 case 'DIRECTION': 18443 break; 18444 } 18445 } 18446 // FOR INLINE ONLY 18447 if ($type == 'INLINE') { 18448 switch ($k) { 18449 // BORDERS 18450 case 'BORDER-TOP': 18451 $this->spanborddet['T'] = $this->border_details($v); 18452 $this->spanborder = true; 18453 $spanbordset = true; 18454 break; 18455 case 'BORDER-BOTTOM': 18456 $this->spanborddet['B'] = $this->border_details($v); 18457 $this->spanborder = true; 18458 $spanbordset = true; 18459 break; 18460 case 'BORDER-LEFT': 18461 $this->spanborddet['L'] = $this->border_details($v); 18462 $this->spanborder = true; 18463 $spanbordset = true; 18464 break; 18465 case 'BORDER-RIGHT': 18466 $this->spanborddet['R'] = $this->border_details($v); 18467 $this->spanborder = true; 18468 $spanbordset = true; 18469 break; 18470 case 'VISIBILITY': // block is set in OpenTag 18471 $v = strtolower($v); 18472 if ($v == 'visible' || $v == 'hidden' || $v == 'printonly' || $v == 'screenonly') { 18473 $this->textparam['visibility'] = $v; 18474 } 18475 break; 18476 }//end of switch($k) 18477 } 18478 18479 if ($type != 'TABLECELL') { 18480 // FOR INLINE and BLOCK 18481 switch ($k) { 18482 case 'TEXT-ALIGN': // left right center justify 18483 if (strtoupper($v) == 'NOJUSTIFY' && $this->blk[$this->blklvl]['align'] == "J") { 18484 $this->blk[$this->blklvl]['align'] = ""; 18485 } 18486 break; 18487 // bgcolor only - to stay consistent with original html2fpdf 18488 case 'BACKGROUND': 18489 case 'BACKGROUND-COLOR': 18490 $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings); 18491 if ($cor) { 18492 if ($tag == 'BODY') { 18493 $this->bodyBackgroundColor = $cor; 18494 } elseif ($type == 'INLINE') { 18495 $this->spanbgcolorarray = $cor; 18496 $this->spanbgcolor = true; 18497 $spanbgset = true; 18498 } else { 18499 $this->blk[$this->blklvl]['bgcolorarray'] = $cor; 18500 $this->blk[$this->blklvl]['bgcolor'] = true; 18501 } 18502 } elseif ($type != 'INLINE') { 18503 if ($this->ColActive) { 18504 $this->blk[$this->blklvl]['bgcolorarray'] = $this->blk[$prevlevel]['bgcolorarray']; 18505 $this->blk[$this->blklvl]['bgcolor'] = $this->blk[$prevlevel]['bgcolor']; 18506 } 18507 } 18508 break; 18509 18510 case 'VERTICAL-ALIGN': // super and sub only dealt with here e.g. <SUB> and <SUP> 18511 switch (strtoupper($v)) { 18512 case 'SUPER': 18513 $this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT); // mPDF 5.7.1 18514 $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT); 18515 // mPDF 5.7.3 inline text-decoration parameters 18516 if (isset($this->textparam['text-baseline'])) { 18517 $this->textparam['text-baseline'] += ($this->baselineSup) * $preceeding_fontsize; 18518 } else { 18519 $this->textparam['text-baseline'] = ($this->baselineSup) * $preceeding_fontsize; 18520 } 18521 break; 18522 case 'SUB': 18523 $this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT); 18524 $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT); 18525 // mPDF 5.7.3 inline text-decoration parameters 18526 if (isset($this->textparam['text-baseline'])) { 18527 $this->textparam['text-baseline'] += ($this->baselineSub) * $preceeding_fontsize; 18528 } else { 18529 $this->textparam['text-baseline'] = ($this->baselineSub) * $preceeding_fontsize; 18530 } 18531 break; 18532 case 'BASELINE': 18533 $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT); 18534 $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT); 18535 // mPDF 5.7.3 inline text-decoration parameters 18536 if (isset($this->textparam['text-baseline'])) { 18537 unset($this->textparam['text-baseline']); 18538 } 18539 break; 18540 // mPDF 5.7.3 inline text-decoration parameters 18541 default: 18542 $lh = $this->_computeLineheight($this->blk[$this->blklvl]['line_height']); 18543 $sz = $this->sizeConverter->convert($v, $lh, $this->FontSize, false); 18544 $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT); 18545 $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT); 18546 if ($sz) { 18547 if ($sz > 0) { 18548 $this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT); 18549 } else { 18550 $this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT); 18551 } 18552 if (isset($this->textparam['text-baseline'])) { 18553 $this->textparam['text-baseline'] += $sz; 18554 } else { 18555 $this->textparam['text-baseline'] = $sz; 18556 } 18557 } 18558 } 18559 break; 18560 }//end of switch($k) 18561 } 18562 18563 18564 // FOR ALL 18565 switch ($k) { 18566 case 'LETTER-SPACING': 18567 $this->lSpacingCSS = $v; 18568 if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') { 18569 $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize); 18570 } 18571 break; 18572 18573 case 'WORD-SPACING': 18574 $this->wSpacingCSS = $v; 18575 if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') { 18576 $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize); 18577 } 18578 break; 18579 18580 case 'FONT-STYLE': // italic normal oblique 18581 switch (strtoupper($v)) { 18582 case 'ITALIC': 18583 case 'OBLIQUE': 18584 $this->SetStyle('I', true); 18585 break; 18586 case 'NORMAL': 18587 $this->SetStyle('I', false); 18588 break; 18589 } 18590 break; 18591 18592 case 'FONT-WEIGHT': // normal bold // Does not support: bolder, lighter, 100..900(step value=100) 18593 switch (strtoupper($v)) { 18594 case 'BOLD': 18595 $this->SetStyle('B', true); 18596 break; 18597 case 'NORMAL': 18598 $this->SetStyle('B', false); 18599 break; 18600 } 18601 break; 18602 18603 case 'FONT-KERNING': 18604 if (strtoupper($v) == 'NORMAL' || (strtoupper($v) == 'AUTO' && $this->useKerning)) { 18605 /* -- OTL -- */ 18606 if ($this->CurrentFont['haskernGPOS']) { 18607 if (isset($this->OTLtags['Plus'])) { 18608 $this->OTLtags['Plus'] .= ' kern'; 18609 } else { 18610 $this->OTLtags['Plus'] = ' kern'; 18611 } 18612 } /* -- END OTL -- */ else { // *OTL* 18613 $this->textvar = ($this->textvar | TextVars::FC_KERNING); 18614 } // *OTL* 18615 } elseif (strtoupper($v) == 'NONE' || (strtoupper($v) == 'AUTO' && !$this->useKerning)) { 18616 if (isset($this->OTLtags['Plus'])) { 18617 $this->OTLtags['Plus'] = str_replace('kern', '', $this->OTLtags['Plus']); // *OTL* 18618 } 18619 if (isset($this->OTLtags['FFPlus'])) { 18620 $this->OTLtags['FFPlus'] = preg_replace('/kern[\d]*/', '', $this->OTLtags['FFPlus']); 18621 } 18622 $this->textvar = ($this->textvar & ~TextVars::FC_KERNING); 18623 } 18624 break; 18625 18626 /* -- OTL -- */ 18627 case 'FONT-LANGUAGE-OVERRIDE': 18628 $v = strtoupper($v); 18629 if (strpos($v, 'NORMAL') !== false) { 18630 $this->fontLanguageOverride = ''; 18631 } else { 18632 $this->fontLanguageOverride = trim($v); 18633 } 18634 break; 18635 18636 18637 case 'FONT-VARIANT-POSITION': 18638 if (isset($this->OTLtags['Plus'])) { 18639 $this->OTLtags['Plus'] = str_replace(['sups', 'subs'], '', $this->OTLtags['Plus']); 18640 } 18641 switch (strtoupper($v)) { 18642 case 'SUPER': 18643 $this->OTLtags['Plus'] .= ' sups'; 18644 break; 18645 case 'SUB': 18646 $this->OTLtags['Plus'] .= ' subs'; 18647 break; 18648 case 'NORMAL': 18649 break; 18650 } 18651 break; 18652 18653 case 'FONT-VARIANT-CAPS': 18654 $v = strtoupper($v); 18655 if (!isset($this->OTLtags['Plus'])) { 18656 $this->OTLtags['Plus'] = ''; 18657 } 18658 $this->OTLtags['Plus'] = str_replace(['c2sc', 'smcp', 'c2pc', 'pcap', 'unic', 'titl'], '', $this->OTLtags['Plus']); 18659 $this->textvar = ($this->textvar & ~TextVars::FC_SMALLCAPS); // ?????????????? <small-caps> 18660 if (strpos($v, 'ALL-SMALL-CAPS') !== false) { 18661 $this->OTLtags['Plus'] .= ' c2sc smcp'; 18662 } elseif (strpos($v, 'SMALL-CAPS') !== false) { 18663 if (isset($this->CurrentFont['hassmallcapsGSUB']) && $this->CurrentFont['hassmallcapsGSUB']) { 18664 $this->OTLtags['Plus'] .= ' smcp'; 18665 } else { 18666 $this->textvar = ($this->textvar | TextVars::FC_SMALLCAPS); 18667 } 18668 } elseif (strpos($v, 'ALL-PETITE-CAPS') !== false) { 18669 $this->OTLtags['Plus'] .= ' c2pc pcap'; 18670 } elseif (strpos($v, 'PETITE-CAPS') !== false) { 18671 $this->OTLtags['Plus'] .= ' pcap'; 18672 } elseif (strpos($v, 'UNICASE') !== false) { 18673 $this->OTLtags['Plus'] .= ' unic'; 18674 } elseif (strpos($v, 'TITLING-CAPS') !== false) { 18675 $this->OTLtags['Plus'] .= ' titl'; 18676 } 18677 break; 18678 18679 case 'FONT-VARIANT-LIGATURES': 18680 $v = strtoupper($v); 18681 if (!isset($this->OTLtags['Plus'])) { 18682 $this->OTLtags['Plus'] = ''; 18683 } 18684 if (!isset($this->OTLtags['Minus'])) { 18685 $this->OTLtags['Minus'] = ''; 18686 } 18687 if (strpos($v, 'NORMAL') !== false) { 18688 $this->OTLtags['Minus'] = str_replace(['liga', 'clig', 'calt'], '', $this->OTLtags['Minus']); 18689 $this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']); 18690 } elseif (strpos($v, 'NONE') !== false) { 18691 $this->OTLtags['Minus'] .= ' liga clig calt'; 18692 $this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']); 18693 } 18694 if (strpos($v, 'NO-COMMON-LIGATURES') !== false) { 18695 $this->OTLtags['Minus'] .= ' liga clig'; 18696 } elseif (strpos($v, 'COMMON-LIGATURES') !== false) { 18697 $this->OTLtags['Minus'] = str_replace(['liga', 'clig'], '', $this->OTLtags['Minus']); 18698 } 18699 if (strpos($v, 'NO-CONTEXTUAL') !== false) { 18700 $this->OTLtags['Minus'] .= ' calt'; 18701 } elseif (strpos($v, 'CONTEXTUAL') !== false) { 18702 $this->OTLtags['Minus'] = str_replace('calt', '', $this->OTLtags['Minus']); 18703 } 18704 if (strpos($v, 'NO-DISCRETIONARY-LIGATURES') !== false) { 18705 $this->OTLtags['Plus'] = str_replace('dlig', '', $this->OTLtags['Plus']); 18706 } elseif (strpos($v, 'DISCRETIONARY-LIGATURES') !== false) { 18707 $this->OTLtags['Plus'] .= ' dlig'; 18708 } 18709 if (strpos($v, 'NO-HISTORICAL-LIGATURES') !== false) { 18710 $this->OTLtags['Plus'] = str_replace('hlig', '', $this->OTLtags['Plus']); 18711 } elseif (strpos($v, 'HISTORICAL-LIGATURES') !== false) { 18712 $this->OTLtags['Plus'] .= ' hlig'; 18713 } 18714 18715 break; 18716 18717 case 'FONT-VARIANT-NUMERIC': 18718 $v = strtoupper($v); 18719 if (!isset($this->OTLtags['Plus'])) { 18720 $this->OTLtags['Plus'] = ''; 18721 } 18722 if (strpos($v, 'NORMAL') !== false) { 18723 $this->OTLtags['Plus'] = str_replace(['ordn', 'zero', 'lnum', 'onum', 'pnum', 'tnum', 'frac', 'afrc'], '', $this->OTLtags['Plus']); 18724 } 18725 if (strpos($v, 'ORDINAL') !== false) { 18726 $this->OTLtags['Plus'] .= ' ordn'; 18727 } 18728 if (strpos($v, 'SLASHED-ZERO') !== false) { 18729 $this->OTLtags['Plus'] .= ' zero'; 18730 } 18731 if (strpos($v, 'LINING-NUMS') !== false) { 18732 $this->OTLtags['Plus'] .= ' lnum'; 18733 $this->OTLtags['Plus'] = str_replace('onum', '', $this->OTLtags['Plus']); 18734 } elseif (strpos($v, 'OLDSTYLE-NUMS') !== false) { 18735 $this->OTLtags['Plus'] .= ' onum'; 18736 $this->OTLtags['Plus'] = str_replace('lnum', '', $this->OTLtags['Plus']); 18737 } 18738 if (strpos($v, 'PROPORTIONAL-NUMS') !== false) { 18739 $this->OTLtags['Plus'] .= ' pnum'; 18740 $this->OTLtags['Plus'] = str_replace('tnum', '', $this->OTLtags['Plus']); 18741 } elseif (strpos($v, 'TABULAR-NUMS') !== false) { 18742 $this->OTLtags['Plus'] .= ' tnum'; 18743 $this->OTLtags['Plus'] = str_replace('pnum', '', $this->OTLtags['Plus']); 18744 } 18745 if (strpos($v, 'DIAGONAL-FRACTIONS') !== false) { 18746 $this->OTLtags['Plus'] .= ' frac'; 18747 $this->OTLtags['Plus'] = str_replace('afrc', '', $this->OTLtags['Plus']); 18748 } elseif (strpos($v, 'STACKED-FRACTIONS') !== false) { 18749 $this->OTLtags['Plus'] .= ' afrc'; 18750 $this->OTLtags['Plus'] = str_replace('frac', '', $this->OTLtags['Plus']); 18751 } 18752 break; 18753 18754 case 'FONT-VARIANT-ALTERNATES': // Only supports historical-forms 18755 $v = strtoupper($v); 18756 if (!isset($this->OTLtags['Plus'])) { 18757 $this->OTLtags['Plus'] = ''; 18758 } 18759 if (strpos($v, 'NORMAL') !== false) { 18760 $this->OTLtags['Plus'] = str_replace('hist', '', $this->OTLtags['Plus']); 18761 } 18762 if (strpos($v, 'HISTORICAL-FORMS') !== false) { 18763 $this->OTLtags['Plus'] .= ' hist'; 18764 } 18765 break; 18766 18767 18768 case 'FONT-FEATURE-SETTINGS': 18769 $v = strtolower($v); 18770 if (strpos($v, 'normal') !== false) { 18771 $this->OTLtags['FFMinus'] = ''; 18772 $this->OTLtags['FFPlus'] = ''; 18773 } else { 18774 if (!isset($this->OTLtags['FFPlus'])) { 18775 $this->OTLtags['FFPlus'] = ''; 18776 } 18777 if (!isset($this->OTLtags['FFMinus'])) { 18778 $this->OTLtags['FFMinus'] = ''; 18779 } 18780 $tags = preg_split('/[,]/', $v); 18781 foreach ($tags as $t) { 18782 if (preg_match('/[\"\']([a-zA-Z0-9]{4})[\"\']\s*(on|off|\d*){0,1}/', $t, $m)) { 18783 if ($m[2] == 'off' || $m[2] === '0') { 18784 if (strpos($this->OTLtags['FFMinus'], $m[1]) === false) { 18785 $this->OTLtags['FFMinus'] .= ' ' . $m[1]; 18786 } 18787 $this->OTLtags['FFPlus'] = preg_replace('/' . $m[1] . '[\d]*/', '', $this->OTLtags['FFPlus']); 18788 } else { 18789 if ($m[2] == 'on') { 18790 $m[2] = '1'; 18791 } 18792 if (strpos($this->OTLtags['FFPlus'], $m[1]) === false) { 18793 $this->OTLtags['FFPlus'] .= ' ' . $m[1] . $m[2]; 18794 } 18795 $this->OTLtags['FFMinus'] = str_replace($m[1], '', $this->OTLtags['FFMinus']); 18796 } 18797 } 18798 } 18799 } 18800 break; 18801 /* -- END OTL -- */ 18802 18803 18804 case 'TEXT-TRANSFORM': // none uppercase lowercase // Does support: capitalize 18805 switch (strtoupper($v)) { // Not working 100% 18806 case 'CAPITALIZE': 18807 $this->textvar = ($this->textvar | TextVars::FT_CAPITALIZE); // mPDF 5.7.1 18808 $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1 18809 $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1 18810 break; 18811 case 'UPPERCASE': 18812 $this->textvar = ($this->textvar | TextVars::FT_UPPERCASE); // mPDF 5.7.1 18813 $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1 18814 $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1 18815 break; 18816 case 'LOWERCASE': 18817 $this->textvar = ($this->textvar | TextVars::FT_LOWERCASE); // mPDF 5.7.1 18818 $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1 18819 $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1 18820 break; 18821 case 'NONE': 18822 break; 18823 $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1 18824 $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1 18825 $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1 18826 } 18827 break; 18828 18829 case 'TEXT-SHADOW': 18830 $ts = $this->cssManager->setCSStextshadow($v); 18831 if ($ts) { 18832 $this->textshadow = $ts; 18833 } 18834 break; 18835 18836 case 'HYPHENS': 18837 if (strtoupper($v) == 'NONE') { 18838 $this->textparam['hyphens'] = 2; 18839 } elseif (strtoupper($v) == 'AUTO') { 18840 $this->textparam['hyphens'] = 1; 18841 } elseif (strtoupper($v) == 'MANUAL') { 18842 $this->textparam['hyphens'] = 0; 18843 } 18844 break; 18845 18846 case 'TEXT-OUTLINE': 18847 if (strtoupper($v) == 'NONE') { 18848 $this->textparam['outline-s'] = false; 18849 } 18850 break; 18851 18852 case 'TEXT-OUTLINE-WIDTH': 18853 case 'OUTLINE-WIDTH': 18854 switch (strtoupper($v)) { 18855 case 'THIN': 18856 $v = '0.03em'; 18857 break; 18858 case 'MEDIUM': 18859 $v = '0.05em'; 18860 break; 18861 case 'THICK': 18862 $v = '0.07em'; 18863 break; 18864 } 18865 $w = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize); 18866 if ($w) { 18867 $this->textparam['outline-WIDTH'] = $w; 18868 $this->textparam['outline-s'] = true; 18869 } else { 18870 $this->textparam['outline-s'] = false; 18871 } 18872 break; 18873 18874 case 'TEXT-OUTLINE-COLOR': 18875 case 'OUTLINE-COLOR': 18876 if (strtoupper($v) == 'INVERT') { 18877 if ($this->colorarray) { 18878 $cor = $this->colorarray; 18879 $this->textparam['outline-COLOR'] = $this->colorConverter->invert($cor); 18880 } else { 18881 $this->textparam['outline-COLOR'] = $this->colorConverter->convert(255, $this->PDFAXwarnings); 18882 } 18883 } else { 18884 $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings); 18885 if ($cor) { 18886 $this->textparam['outline-COLOR'] = $cor; 18887 } 18888 } 18889 break; 18890 18891 case 'COLOR': // font color 18892 $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings); 18893 if ($cor) { 18894 $this->colorarray = $cor; 18895 $this->SetTColor($cor); 18896 } 18897 break; 18898 }//end of switch($k) 18899 }//end of foreach 18900 // mPDF 5.7.3 inline text-decoration parameters 18901 // Needs to be set at the end - after vertical-align = super/sub, so that textparam['text-baseline'] is set 18902 if (isset($arrayaux['TEXT-DECORATION'])) { 18903 $v = $arrayaux['TEXT-DECORATION']; // none underline line-through (strikeout) // Does not support: blink 18904 if (stristr($v, 'LINE-THROUGH')) { 18905 $this->textvar = ($this->textvar | TextVars::FD_LINETHROUGH); 18906 // mPDF 5.7.3 inline text-decoration parameters 18907 if (isset($this->textparam['text-baseline'])) { 18908 $this->textparam['s-decoration']['baseline'] = $this->textparam['text-baseline']; 18909 } else { 18910 $this->textparam['s-decoration']['baseline'] = 0; 18911 } 18912 $this->textparam['s-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle; 18913 $this->textparam['s-decoration']['fontsize'] = $this->FontSize; 18914 $this->textparam['s-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG 18915 } 18916 if (stristr($v, 'UNDERLINE')) { 18917 $this->textvar = ($this->textvar | TextVars::FD_UNDERLINE); 18918 // mPDF 5.7.3 inline text-decoration parameters 18919 if (isset($this->textparam['text-baseline'])) { 18920 $this->textparam['u-decoration']['baseline'] = $this->textparam['text-baseline']; 18921 } else { 18922 $this->textparam['u-decoration']['baseline'] = 0; 18923 } 18924 $this->textparam['u-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle; 18925 $this->textparam['u-decoration']['fontsize'] = $this->FontSize; 18926 $this->textparam['u-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG 18927 } 18928 if (stristr($v, 'OVERLINE')) { 18929 $this->textvar = ($this->textvar | TextVars::FD_OVERLINE); 18930 // mPDF 5.7.3 inline text-decoration parameters 18931 if (isset($this->textparam['text-baseline'])) { 18932 $this->textparam['o-decoration']['baseline'] = $this->textparam['text-baseline']; 18933 } else { 18934 $this->textparam['o-decoration']['baseline'] = 0; 18935 } 18936 $this->textparam['o-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle; 18937 $this->textparam['o-decoration']['fontsize'] = $this->FontSize; 18938 $this->textparam['o-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG 18939 } 18940 if (stristr($v, 'NONE')) { 18941 $this->textvar = ($this->textvar & ~TextVars::FD_UNDERLINE); 18942 $this->textvar = ($this->textvar & ~TextVars::FD_LINETHROUGH); 18943 $this->textvar = ($this->textvar & ~TextVars::FD_OVERLINE); 18944 // mPDF 5.7.3 inline text-decoration parameters 18945 if (isset($this->textparam['u-decoration'])) { 18946 unset($this->textparam['u-decoration']); 18947 } 18948 if (isset($this->textparam['s-decoration'])) { 18949 unset($this->textparam['s-decoration']); 18950 } 18951 if (isset($this->textparam['o-decoration'])) { 18952 unset($this->textparam['o-decoration']); 18953 } 18954 } 18955 } 18956 // mPDF 6 18957 if ($spanbordset) { // BORDER has been set on this INLINE element 18958 if (isset($this->textparam['text-baseline'])) { 18959 $this->textparam['bord-decoration']['baseline'] = $this->textparam['text-baseline']; 18960 } else { 18961 $this->textparam['bord-decoration']['baseline'] = 0; 18962 } 18963 $this->textparam['bord-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle; 18964 $this->textparam['bord-decoration']['fontsize'] = $this->FontSize; 18965 } 18966 if ($spanbgset) { // BACKGROUND[-COLOR] has been set on this INLINE element 18967 if (isset($this->textparam['text-baseline'])) { 18968 $this->textparam['bg-decoration']['baseline'] = $this->textparam['text-baseline']; 18969 } else { 18970 $this->textparam['bg-decoration']['baseline'] = 0; 18971 } 18972 $this->textparam['bg-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle; 18973 $this->textparam['bg-decoration']['fontsize'] = $this->FontSize; 18974 } 18975 } 18976 18977 /* -- END HTML-CSS -- */ 18978 18979 function SetStyle($tag, $enable) 18980 { 18981 $this->$tag = $enable; 18982 $style = ''; 18983 foreach (['B', 'I'] as $s) { 18984 if ($this->$s) { 18985 $style .= $s; 18986 } 18987 } 18988 $this->currentfontstyle = $style; 18989 $this->SetFont('', $style, 0, false); 18990 } 18991 18992 // Set multiple styles at one time 18993 function SetStylesArray($arr) 18994 { 18995 $style = ''; 18996 foreach (['B', 'I'] as $s) { 18997 if (isset($arr[$s])) { 18998 if ($arr[$s]) { 18999 $this->$s = true; 19000 $style .= $s; 19001 } else { 19002 $this->$s = false; 19003 } 19004 } elseif ($this->$s) { 19005 $style .= $s; 19006 } 19007 } 19008 $this->currentfontstyle = $style; 19009 $this->SetFont('', $style, 0, false); 19010 } 19011 19012 // Set multiple styles at one $str e.g. "BI" 19013 function SetStyles($str) 19014 { 19015 $style = ''; 19016 foreach (['B', 'I'] as $s) { 19017 if (strpos($str, $s) !== false) { 19018 $this->$s = true; 19019 $style .= $s; 19020 } else { 19021 $this->$s = false; 19022 } 19023 } 19024 $this->currentfontstyle = $style; 19025 $this->SetFont('', $style, 0, false); 19026 } 19027 19028 function ResetStyles() 19029 { 19030 foreach (['B', 'I'] as $s) { 19031 $this->$s = false; 19032 } 19033 $this->currentfontstyle = ''; 19034 $this->SetFont('', '', 0, false); 19035 } 19036 19037 function DisableTags($str = '') 19038 { 19039 if ($str == '') { // enable all tags 19040 // Insert new supported tags in the long string below. 19041 $this->enabledtags = "<a><acronym><address><article><aside><b><bdi><bdo><big><blockquote><br><caption><center><cite><code><del><details><dd><div><dl><dt><em><fieldset><figcaption><figure><font><form><h1><h2><h3><h4><h5><h6><hgroup><hr><i><img><input><ins><kbd><legend><li><main><mark><meter><nav><ol><option><p><pre><progress><q><s><samp><section><select><small><span><strike><strong><sub><summary><sup><table><tbody><td><template><textarea><tfoot><th><thead><time><tr><tt><u><ul><var><footer><header><annotation><bookmark><textcircle><barcode><dottab><indexentry><indexinsert><watermarktext><watermarkimage><tts><ttz><tta><column_break><columnbreak><newcolumn><newpage><page_break><pagebreak><formfeed><columns><toc><tocentry><tocpagebreak><pageheader><pagefooter><setpageheader><setpagefooter><sethtmlpageheader><sethtmlpagefooter>"; 19042 } else { 19043 $str = explode(",", $str); 19044 foreach ($str as $v) { 19045 $this->enabledtags = str_replace(trim($v), '', $this->enabledtags); 19046 } 19047 } 19048 } 19049 19050 /* -- TABLES -- */ 19051 19052 function TableCheckMinWidth($maxwidth, $forcewrap = 0, $textbuffer = [], $checkletter = false) 19053 { 19054 // mPDF 6 19055 $acclength = 0; // mPDF 6 (accumulated length across > 1 chunk) 19056 $acclongest = 0; // mPDF 6 (accumulated length max across > 1 chunk) 19057 $biggestword = 0; 19058 $toonarrow = false; 19059 if ((count($textbuffer) == 0) or ( (count($textbuffer) == 1) && ($textbuffer[0][0] == ''))) { 19060 return 0; 19061 } 19062 19063 foreach ($textbuffer as $chunk) { 19064 $line = $chunk[0]; 19065 $OTLdata = (isset($chunk[18]) ? $chunk[18] : null); 19066 19067 // mPDF ITERATION 19068 if ($this->iterationCounter) { 19069 $line = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $line); 19070 } 19071 19072 // IMAGES & FORM ELEMENTS 19073 if (substr($line, 0, 3) == "\xbb\xa4\xac") { // inline object - FORM element or IMAGE! 19074 $objattr = $this->_getObjAttr($line); 19075 if ($objattr['type'] != 'hr' && isset($objattr['width']) && ($objattr['width'] / $this->shrin_k) > ($maxwidth + 0.0001)) { 19076 if (($objattr['width'] / $this->shrin_k) > $biggestword) { 19077 $biggestword = ($objattr['width'] / $this->shrin_k); 19078 } 19079 $toonarrow = true; 19080 } 19081 continue; 19082 } 19083 19084 if ($line == "\n") { 19085 $acclength = 0; // mPDF 6 (accumulated length across > 1 chunk) 19086 continue; 19087 } 19088 $line = trim($line); 19089 if (!empty($OTLdata)) { 19090 $this->otl->trimOTLdata($OTLdata, true, true); 19091 } // *OTL* 19092 // SET FONT SIZE/STYLE from $chunk[n] 19093 // FONTSIZE 19094 if (isset($chunk[11]) and $chunk[11] != '') { 19095 if ($this->shrin_k) { 19096 $this->SetFontSize($chunk[11] / $this->shrin_k, false); 19097 } else { 19098 $this->SetFontSize($chunk[11], false); 19099 } 19100 } 19101 // FONTFAMILY 19102 if (isset($chunk[4]) and $chunk[4] != '') { 19103 $font = $this->SetFont($chunk[4], $this->FontStyle, 0, false); 19104 } 19105 // B I 19106 if (isset($chunk[2]) and $chunk[2] != '') { 19107 $this->SetStyles($chunk[2]); 19108 } 19109 19110 $lbw = $rbw = 0; // Border widths 19111 if (isset($chunk[16]) && !empty($chunk[16])) { // Border 19112 $this->spanborddet = $chunk[16]; 19113 $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0); 19114 $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0); 19115 } 19116 if (isset($chunk[15])) { // Word spacing 19117 $this->wSpacingCSS = $chunk[15]; 19118 if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') { 19119 $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3 19120 } 19121 } 19122 if (isset($chunk[14])) { // Letter spacing 19123 $this->lSpacingCSS = $chunk[14]; 19124 if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') { 19125 $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3 19126 } 19127 } 19128 if (isset($chunk[8])) { // mPDF 5.7.1 19129 $this->textvar = $chunk[8]; 19130 } 19131 19132 // mPDF 6 19133 // If overflow==wrap ($checkletter) OR (No word breaks and contains CJK) 19134 if ($checkletter || (!preg_match('/(\xe2\x80\x8b| )/', trim($line)) && preg_match("/([" . $this->pregCJKchars . "])/u", $line) )) { 19135 if (preg_match("/([" . $this->pregCJKchars . "])/u", $line)) { 19136 $checkCJK = true; 19137 } else { 19138 $checkCJK = false; 19139 } 19140 19141 $letters = preg_split('//u', $line); 19142 foreach ($letters as $k => $letter) { 19143 // mPDF 6 19144 if ($checkCJK) { 19145 if (preg_match("/[" . $this->CJKleading . "]/u", $letter) && $k > 0) { 19146 $letter = $letters[$k - 1] . $letter; 19147 } 19148 if (preg_match("/[" . $this->CJKfollowing . "]/u", $letter) && $k < (count($letters) - 1)) { 19149 $letter = $letter . $letters[$k + 1]; 19150 } 19151 } 19152 19153 $letterwidth = $this->GetStringWidth($letter, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here 19154 // so don't have to split OTLdata for each word 19155 if ($k == 0) { 19156 $letterwidth += $lbw; 19157 } 19158 if ($k == (count($letters) - 1)) { 19159 $letterwidth += $rbw; 19160 } 19161 19162 // Warn user that maxwidth is insufficient 19163 if ($letterwidth > $maxwidth + 0.0001) { 19164 if ($letterwidth > $biggestword) { 19165 $biggestword = $letterwidth; 19166 } 19167 $toonarrow = true; 19168 } 19169 } 19170 } else { 19171 // mPDF 6 19172 // Need to account for any XAdvance in GPOSinfo (OTLdata = $chunk[18]) 19173 $wordXAdvance = []; 19174 if (isset($chunk[18]) && $chunk[18]) { 19175 preg_match_all('/(\xe2\x80\x8b| )/', $line, $spaces, PREG_OFFSET_CAPTURE); // U+200B Zero Width word boundary, or space 19176 $lastoffset = 0; 19177 $k = -1; // Added so that if no spaces found, "last word" later is calculated for the one and only word 19178 foreach ($spaces[0] as $k => $m) { 19179 $offset = $m[1]; 19180 // ...TableCheckMinWidth... 19181 // At this point, BIDI not applied, Writing direction is not set, and XAdvanceL balances XAdvanceR 19182 for ($n = $lastoffset; $n < $offset; $n++) { 19183 if (isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) { 19184 if (isset($wordXAdvance[$k])) { 19185 $wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL']; 19186 } else { 19187 $wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL']; 19188 } 19189 } 19190 } 19191 $lastoffset = $offset + 1; 19192 } 19193 19194 $k++; // last word 19195 foreach ($chunk[18]['GPOSinfo'] as $n => $gpos) { 19196 if ($n >= $lastoffset && isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) { 19197 if (isset($wordXAdvance[$k])) { 19198 $wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL']; 19199 } else { 19200 $wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL']; 19201 } 19202 } 19203 } 19204 } 19205 19206 $words = preg_split('/(\xe2\x80\x8b| )/', $line); // U+200B Zero Width word boundary, or space 19207 foreach ($words as $k => $word) { 19208 $word = trim($word); 19209 $wordwidth = $this->GetStringWidth($word, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here 19210 // so don't have to split OTLdata for each word 19211 if (isset($wordXAdvance[$k])) { 19212 $wordwidth += ($wordXAdvance[$k] * 1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000); 19213 } 19214 if ($k == 0) { 19215 $wordwidth += $lbw; 19216 } 19217 if ($k == (count($words) - 1)) { 19218 $wordwidth += $rbw; 19219 } 19220 19221 // mPDF 6 19222 if (count($words) == 1 && substr($chunk[0], 0, 1) != ' ') { 19223 $acclength += $wordwidth; 19224 } elseif (count($words) > 1 && $k == 0 && substr($chunk[0], 0, 1) != ' ') { 19225 $acclength += $wordwidth; 19226 } else { 19227 $acclength = $wordwidth; 19228 } 19229 $acclongest = max($acclongest, $acclength); 19230 if (count($words) == 1 && substr($chunk[0], -1, 1) == ' ') { 19231 $acclength = 0; 19232 } elseif (count($words) > 1 && ($k != (count($words) - 1) || substr($chunk[0], -1, 1) == ' ')) { 19233 $acclength = 0; 19234 } 19235 19236 // Warn user that maxwidth is insufficient 19237 if ($wordwidth > $maxwidth + 0.0001) { 19238 if ($wordwidth > $biggestword) { 19239 $biggestword = $wordwidth; 19240 } 19241 $toonarrow = true; 19242 } 19243 } 19244 } 19245 19246 // mPDF 6 Accumulated length of biggest word - across multiple chunks 19247 if ($acclongest > $maxwidth + 0.0001) { 19248 if ($acclongest > $biggestword) { 19249 $biggestword = $acclongest; 19250 } 19251 $toonarrow = true; 19252 } 19253 19254 // RESET FONT SIZE/STYLE 19255 // RESETTING VALUES 19256 // Now we must deactivate what we have used 19257 if (isset($chunk[2]) and $chunk[2] != '') { 19258 $this->ResetStyles(); 19259 } 19260 if (isset($chunk[4]) and $chunk[4] != '') { 19261 $this->SetFont($this->default_font, $this->FontStyle, 0, false); 19262 } 19263 if (isset($chunk[11]) and $chunk[11] != '') { 19264 $this->SetFontSize($this->default_font_size, false); 19265 } 19266 $this->spanborddet = []; 19267 $this->textvar = 0x00; // mPDF 5.7.1 19268 $this->OTLtags = []; 19269 $this->lSpacingCSS = ''; 19270 $this->wSpacingCSS = ''; 19271 $this->fixedlSpacing = false; 19272 $this->minwSpacing = 0; 19273 } 19274 19275 // Return -(wordsize) if word is bigger than maxwidth 19276 // ADDED 19277 if (($toonarrow) && ($this->table_error_report)) { 19278 throw new \Mpdf\MpdfException("Word is too long to fit in table - " . $this->table_error_report_param); 19279 } 19280 if ($toonarrow) { 19281 return -$biggestword; 19282 } else { 19283 return 1; 19284 } 19285 } 19286 19287 function shrinkTable(&$table, $k) 19288 { 19289 $table['border_spacing_H'] /= $k; 19290 $table['border_spacing_V'] /= $k; 19291 19292 $table['padding']['T'] /= $k; 19293 $table['padding']['R'] /= $k; 19294 $table['padding']['B'] /= $k; 19295 $table['padding']['L'] /= $k; 19296 19297 $table['margin']['T'] /= $k; 19298 $table['margin']['R'] /= $k; 19299 $table['margin']['B'] /= $k; 19300 $table['margin']['L'] /= $k; 19301 19302 $table['border_details']['T']['w'] /= $k; 19303 $table['border_details']['R']['w'] /= $k; 19304 $table['border_details']['B']['w'] /= $k; 19305 $table['border_details']['L']['w'] /= $k; 19306 19307 if (isset($table['max_cell_border_width']['T'])) { 19308 $table['max_cell_border_width']['T'] /= $k; 19309 } 19310 if (isset($table['max_cell_border_width']['R'])) { 19311 $table['max_cell_border_width']['R'] /= $k; 19312 } 19313 if (isset($table['max_cell_border_width']['B'])) { 19314 $table['max_cell_border_width']['B'] /= $k; 19315 } 19316 if (isset($table['max_cell_border_width']['L'])) { 19317 $table['max_cell_border_width']['L'] /= $k; 19318 } 19319 19320 if ($this->simpleTables) { 19321 $table['simple']['border_details']['T']['w'] /= $k; 19322 $table['simple']['border_details']['R']['w'] /= $k; 19323 $table['simple']['border_details']['B']['w'] /= $k; 19324 $table['simple']['border_details']['L']['w'] /= $k; 19325 } 19326 19327 $table['miw'] /= $k; 19328 $table['maw'] /= $k; 19329 19330 for ($j = 0; $j < $table['nc']; $j++) { // columns 19331 19332 $table['wc'][$j]['miw'] = isset($table['wc'][$j]['miw']) ? $table['wc'][$j]['miw'] : 0; 19333 $table['wc'][$j]['maw'] = isset($table['wc'][$j]['maw']) ? $table['wc'][$j]['maw'] : 0; 19334 19335 $table['wc'][$j]['miw'] /= $k; 19336 $table['wc'][$j]['maw'] /= $k; 19337 19338 if (isset($table['decimal_align'][$j]['maxs0']) && $table['decimal_align'][$j]['maxs0']) { 19339 $table['decimal_align'][$j]['maxs0'] /= $k; 19340 } 19341 19342 if (isset($table['decimal_align'][$j]['maxs1']) && $table['decimal_align'][$j]['maxs1']) { 19343 $table['decimal_align'][$j]['maxs1'] /= $k; 19344 } 19345 19346 if (isset($table['wc'][$j]['absmiw']) && $table['wc'][$j]['absmiw']) { 19347 $table['wc'][$j]['absmiw'] /= $k; 19348 } 19349 19350 for ($i = 0; $i < $table['nr']; $i++) { // rows 19351 19352 $c = &$table['cells'][$i][$j]; 19353 19354 if (isset($c) && $c) { 19355 19356 if (!$this->simpleTables) { 19357 19358 if ($this->packTableData) { 19359 19360 $cell = $this->_unpackCellBorder($c['borderbin']); 19361 19362 $cell['border_details']['T']['w'] /= $k; 19363 $cell['border_details']['R']['w'] /= $k; 19364 $cell['border_details']['B']['w'] /= $k; 19365 $cell['border_details']['L']['w'] /= $k; 19366 $cell['border_details']['mbw']['TL'] /= $k; 19367 $cell['border_details']['mbw']['TR'] /= $k; 19368 $cell['border_details']['mbw']['BL'] /= $k; 19369 $cell['border_details']['mbw']['BR'] /= $k; 19370 $cell['border_details']['mbw']['LT'] /= $k; 19371 $cell['border_details']['mbw']['LB'] /= $k; 19372 $cell['border_details']['mbw']['RT'] /= $k; 19373 $cell['border_details']['mbw']['RB'] /= $k; 19374 19375 $c['borderbin'] = $this->_packCellBorder($cell); 19376 19377 } else { 19378 19379 $c['border_details']['T']['w'] /= $k; 19380 $c['border_details']['R']['w'] /= $k; 19381 $c['border_details']['B']['w'] /= $k; 19382 $c['border_details']['L']['w'] /= $k; 19383 $c['border_details']['mbw']['TL'] /= $k; 19384 $c['border_details']['mbw']['TR'] /= $k; 19385 $c['border_details']['mbw']['BL'] /= $k; 19386 $c['border_details']['mbw']['BR'] /= $k; 19387 $c['border_details']['mbw']['LT'] /= $k; 19388 $c['border_details']['mbw']['LB'] /= $k; 19389 $c['border_details']['mbw']['RT'] /= $k; 19390 $c['border_details']['mbw']['RB'] /= $k; 19391 } 19392 } 19393 19394 $c['padding']['T'] /= $k; 19395 $c['padding']['R'] /= $k; 19396 $c['padding']['B'] /= $k; 19397 $c['padding']['L'] /= $k; 19398 19399 $c['maxs'] = isset($c['maxs']) ? $c['maxs'] /= $k : null; 19400 $c['w'] = isset($c['w']) ? $c['w'] /= $k : null; 19401 19402 $c['s'] = isset($c['s']) ? $c['s'] /= $k : 0; 19403 $c['h'] = isset($c['h']) ? $c['h'] /= $k : null; 19404 19405 $c['miw'] = isset($c['miw']) ? $c['miw'] /= $k : 0; 19406 $c['maw'] = isset($c['maw']) ? $c['maw'] /= $k : 0; 19407 19408 $c['absmiw'] = isset($c['absmiw']) ? $c['absmiw'] /= $k : null; 19409 19410 $c['nestedmaw'] = isset($c['nestedmaw']) ? $c['nestedmaw'] /= $k : null; 19411 $c['nestedmiw'] = isset($c['nestedmiw']) ? $c['nestedmiw'] /= $k : null; 19412 19413 if (isset($c['textbuffer'])) { 19414 foreach ($c['textbuffer'] as $n => $tb) { 19415 if (!empty($tb[16])) { 19416 !isset($c['textbuffer'][$n][16]['T']) || $c['textbuffer'][$n][16]['T']['w'] /= $k; 19417 !isset($c['textbuffer'][$n][16]['B']) || $c['textbuffer'][$n][16]['B']['w'] /= $k; 19418 !isset($c['textbuffer'][$n][16]['L']) || $c['textbuffer'][$n][16]['L']['w'] /= $k; 19419 !isset($c['textbuffer'][$n][16]['R']) || $c['textbuffer'][$n][16]['R']['w'] /= $k; 19420 } 19421 } 19422 } 19423 19424 unset($c); 19425 } 19426 19427 } // rows 19428 } // columns 19429 } 19430 19431 function read_short(&$fh) 19432 { 19433 $s = fread($fh, 2); 19434 $a = (ord($s[0]) << 8) + ord($s[1]); 19435 if ($a & (1 << 15)) { 19436 $a = ($a - (1 << 16)); 19437 } 19438 return $a; 19439 } 19440 19441 function _packCellBorder($cell) 19442 { 19443 if (!is_array($cell) || !isset($cell)) { 19444 return ''; 19445 } 19446 19447 if (!$this->packTableData) { 19448 return $cell; 19449 } 19450 // = 186 bytes 19451 $bindata = pack("nnda6A10nnda6A10nnda6A10nnda6A10nd9", $cell['border'], $cell['border_details']['R']['s'], $cell['border_details']['R']['w'], $cell['border_details']['R']['c'], $cell['border_details']['R']['style'], $cell['border_details']['R']['dom'], $cell['border_details']['L']['s'], $cell['border_details']['L']['w'], $cell['border_details']['L']['c'], $cell['border_details']['L']['style'], $cell['border_details']['L']['dom'], $cell['border_details']['T']['s'], $cell['border_details']['T']['w'], $cell['border_details']['T']['c'], $cell['border_details']['T']['style'], $cell['border_details']['T']['dom'], $cell['border_details']['B']['s'], $cell['border_details']['B']['w'], $cell['border_details']['B']['c'], $cell['border_details']['B']['style'], $cell['border_details']['B']['dom'], $cell['border_details']['mbw']['BL'], $cell['border_details']['mbw']['BR'], $cell['border_details']['mbw']['RT'], $cell['border_details']['mbw']['RB'], $cell['border_details']['mbw']['TL'], $cell['border_details']['mbw']['TR'], $cell['border_details']['mbw']['LT'], $cell['border_details']['mbw']['LB'], (isset($cell['border_details']['cellposdom']) ? $cell['border_details']['cellposdom'] : 0)); 19452 return $bindata; 19453 } 19454 19455 function _getBorderWidths($bindata) 19456 { 19457 if (!$bindata) { 19458 return [0, 0, 0, 0]; 19459 } 19460 if (!$this->packTableData) { 19461 return [$bindata['border_details']['T']['w'], $bindata['border_details']['R']['w'], $bindata['border_details']['B']['w'], $bindata['border_details']['L']['w']]; 19462 } 19463 19464 $bd = unpack("nbord/nrs/drw/a6rca/A10rst/nrd/nls/dlw/a6lca/A10lst/nld/nts/dtw/a6tca/A10tst/ntd/nbs/dbw/a6bca/A10bst/nbd/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd", $bindata); 19465 $cell['border_details']['R']['w'] = $bd['rw']; 19466 $cell['border_details']['L']['w'] = $bd['lw']; 19467 $cell['border_details']['T']['w'] = $bd['tw']; 19468 $cell['border_details']['B']['w'] = $bd['bw']; 19469 return [$bd['tw'], $bd['rw'], $bd['bw'], $bd['lw']]; 19470 } 19471 19472 function _unpackCellBorder($bindata) 19473 { 19474 if (!$bindata) { 19475 return []; 19476 } 19477 if (!$this->packTableData) { 19478 return $bindata; 19479 } 19480 19481 $bd = unpack("nbord/nrs/drw/a6rca/A10rst/nrd/nls/dlw/a6lca/A10lst/nld/nts/dtw/a6tca/A10tst/ntd/nbs/dbw/a6bca/A10bst/nbd/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd", $bindata); 19482 19483 $cell['border'] = $bd['bord']; 19484 $cell['border_details']['R']['s'] = $bd['rs']; 19485 $cell['border_details']['R']['w'] = $bd['rw']; 19486 $cell['border_details']['R']['c'] = str_pad($bd['rca'], 6, "\x00"); 19487 $cell['border_details']['R']['style'] = trim($bd['rst']); 19488 $cell['border_details']['R']['dom'] = $bd['rd']; 19489 19490 $cell['border_details']['L']['s'] = $bd['ls']; 19491 $cell['border_details']['L']['w'] = $bd['lw']; 19492 $cell['border_details']['L']['c'] = str_pad($bd['lca'], 6, "\x00"); 19493 $cell['border_details']['L']['style'] = trim($bd['lst']); 19494 $cell['border_details']['L']['dom'] = $bd['ld']; 19495 19496 $cell['border_details']['T']['s'] = $bd['ts']; 19497 $cell['border_details']['T']['w'] = $bd['tw']; 19498 $cell['border_details']['T']['c'] = str_pad($bd['tca'], 6, "\x00"); 19499 $cell['border_details']['T']['style'] = trim($bd['tst']); 19500 $cell['border_details']['T']['dom'] = $bd['td']; 19501 19502 $cell['border_details']['B']['s'] = $bd['bs']; 19503 $cell['border_details']['B']['w'] = $bd['bw']; 19504 $cell['border_details']['B']['c'] = str_pad($bd['bca'], 6, "\x00"); 19505 $cell['border_details']['B']['style'] = trim($bd['bst']); 19506 $cell['border_details']['B']['dom'] = $bd['bd']; 19507 19508 $cell['border_details']['mbw']['BL'] = $bd['mbl']; 19509 $cell['border_details']['mbw']['BR'] = $bd['mbr']; 19510 $cell['border_details']['mbw']['RT'] = $bd['mrt']; 19511 $cell['border_details']['mbw']['RB'] = $bd['mrb']; 19512 $cell['border_details']['mbw']['TL'] = $bd['mtl']; 19513 $cell['border_details']['mbw']['TR'] = $bd['mtr']; 19514 $cell['border_details']['mbw']['LT'] = $bd['mlt']; 19515 $cell['border_details']['mbw']['LB'] = $bd['mlb']; 19516 $cell['border_details']['cellposdom'] = $bd['cpd']; 19517 19518 19519 return($cell); 19520 } 19521 19522 ////////////////////////TABLE CODE (from PDFTable)///////////////////////////////////// 19523 ////////////////////////TABLE CODE (from PDFTable)///////////////////////////////////// 19524 ////////////////////////TABLE CODE (from PDFTable)///////////////////////////////////// 19525 // table Array of (w, h, bc, nr, wc, hr, cells) 19526 // w Width of table 19527 // h Height of table 19528 // nc Number column 19529 // nr Number row 19530 // hr List of height of each row 19531 // wc List of width of each column 19532 // cells List of cells of each rows, cells[i][j] is a cell in the table 19533 function _tableColumnWidth(&$table, $firstpass = false) 19534 { 19535 $cs = &$table['cells']; 19536 19537 $nc = $table['nc']; 19538 $nr = $table['nr']; 19539 $listspan = []; 19540 19541 if ($table['borders_separate']) { 19542 $tblbw = $table['border_details']['L']['w'] + $table['border_details']['R']['w'] + $table['margin']['L'] + $table['margin']['R'] + $table['padding']['L'] + $table['padding']['R'] + $table['border_spacing_H']; 19543 } else { 19544 $tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R']; 19545 } 19546 19547 // ADDED table['l'][colno] 19548 // = total length of text approx (using $c['s']) in that column - used to approximately distribute col widths in _tableWidth 19549 // 19550 for ($j = 0; $j < $nc; $j++) { // columns 19551 $wc = &$table['wc'][$j]; 19552 for ($i = 0; $i < $nr; $i++) { // rows 19553 if (isset($cs[$i][$j]) && $cs[$i][$j]) { 19554 $c = &$cs[$i][$j]; 19555 19556 if ($this->simpleTables) { 19557 if ($table['borders_separate']) { // NB twice border width 19558 $extrcw = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H']; 19559 } else { 19560 $extrcw = $table['simple']['border_details']['L']['w'] / 2 + $table['simple']['border_details']['R']['w'] / 2 + $c['padding']['L'] + $c['padding']['R']; 19561 } 19562 } else { 19563 if ($this->packTableData) { 19564 list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']); 19565 } else { 19566 $br = $c['border_details']['R']['w']; 19567 $bl = $c['border_details']['L']['w']; 19568 } 19569 if ($table['borders_separate']) { // NB twice border width 19570 $extrcw = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H']; 19571 } else { 19572 $extrcw = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R']; 19573 } 19574 } 19575 19576 // $mw = $this->GetStringWidth('W') + $extrcw ; 19577 $mw = $extrcw; // mPDF 6 19578 if (substr($c['a'], 0, 1) == 'D') { 19579 $mw = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'] + $extrcw; 19580 } 19581 19582 $c['absmiw'] = $mw; 19583 19584 if (isset($c['R']) && $c['R']) { 19585 $c['maw'] = $c['miw'] = $this->FontSize + $extrcw; 19586 if (isset($c['w'])) { // If cell width is specified 19587 if ($c['miw'] < $c['w']) { 19588 $c['miw'] = $c['w']; 19589 } 19590 } 19591 if (!isset($c['colspan'])) { 19592 if ($wc['miw'] < $c['miw']) { 19593 $wc['miw'] = $c['miw']; 19594 } 19595 if ($wc['maw'] < $c['maw']) { 19596 $wc['maw'] = $c['maw']; 19597 } 19598 19599 if ($firstpass) { 19600 if (isset($table['l'][$j])) { 19601 $table['l'][$j] += $c['miw']; 19602 } else { 19603 $table['l'][$j] = $c['miw']; 19604 } 19605 } 19606 } 19607 if ($c['miw'] > $wc['miw']) { 19608 $wc['miw'] = $c['miw']; 19609 } 19610 if ($wc['miw'] > $wc['maw']) { 19611 $wc['maw'] = $wc['miw']; 19612 } 19613 continue; 19614 } 19615 19616 if ($firstpass) { 19617 if (isset($c['s'])) { 19618 $c['s'] += $extrcw; 19619 } 19620 if (isset($c['maxs'])) { 19621 $c['maxs'] += $extrcw; 19622 } 19623 if (isset($c['nestedmiw'])) { 19624 $c['nestedmiw'] += $extrcw; 19625 } 19626 if (isset($c['nestedmaw'])) { 19627 $c['nestedmaw'] += $extrcw; 19628 } 19629 } 19630 19631 19632 // If minimum width has already been set by a nested table or inline object (image/form), use it 19633 if (isset($c['nestedmiw']) && (!isset($this->table[1][1]['overflow']) || $this->table[1][1]['overflow'] != 'visible')) { 19634 $miw = $c['nestedmiw']; 19635 } else { 19636 $miw = $mw; 19637 } 19638 19639 if (isset($c['maxs']) && $c['maxs'] != '') { 19640 $c['s'] = $c['maxs']; 19641 } 19642 19643 // If maximum width has already been set by a nested table, use it 19644 if (isset($c['nestedmaw'])) { 19645 $c['maw'] = $c['nestedmaw']; 19646 } else { 19647 $c['maw'] = $c['s']; 19648 } 19649 19650 if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) { 19651 if (($c['maw'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) { 19652 $c['maw'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw; 19653 } 19654 } 19655 19656 if (isset($c['nowrap']) && $c['nowrap']) { 19657 $miw = $c['maw']; 19658 } 19659 19660 if (isset($c['wpercent']) && $firstpass) { 19661 if (isset($c['colspan'])) { // Not perfect - but % set on colspan is shared equally on cols. 19662 for ($k = 0; $k < $c['colspan']; $k++) { 19663 $table['wc'][($j + $k)]['wpercent'] = $c['wpercent'] / $c['colspan']; 19664 } 19665 } else { 19666 if (isset($table['w']) && $table['w']) { 19667 $c['w'] = $c['wpercent'] / 100 * ($table['w'] - $tblbw ); 19668 } 19669 $wc['wpercent'] = $c['wpercent']; 19670 } 19671 } 19672 19673 if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) { 19674 if (isset($c['w']) && ($c['w'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) { 19675 $c['w'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw; 19676 } 19677 } 19678 19679 19680 if (isset($c['w'])) { // If cell width is specified 19681 if ($miw < $c['w']) { 19682 $c['miw'] = $c['w']; 19683 } // Cell min width = that specified 19684 if ($miw > $c['w']) { 19685 $c['miw'] = $c['w'] = $miw; 19686 } // If width specified is less than minimum allowed (W) increase it 19687 // mPDF 5.7.4 Do not set column width in colspan 19688 // cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug 19689 if (!isset($c['colspan'])) { 19690 if (!isset($wc['w'])) { 19691 $wc['w'] = 1; 19692 } // If the Col width is not specified = set it to 1 19693 } 19694 // mPDF 5.7.3 cf. http://www.mpdf1.com/forum/discussion/1648/nested-table-bug- 19695 $c['maw'] = $c['w']; 19696 } else { 19697 $c['miw'] = $miw; 19698 } // If cell width not specified -> set Cell min width it to minimum allowed (W) 19699 19700 if (isset($c['miw']) && $c['maw'] < $c['miw']) { 19701 $c['maw'] = $c['miw']; 19702 } // If Cell max width < Minwidth - increase it to = 19703 if (!isset($c['colspan'])) { 19704 if (isset($c['miw']) && $wc['miw'] < $c['miw']) { 19705 $wc['miw'] = $c['miw']; 19706 } // Update Col Minimum and maximum widths 19707 if ($wc['maw'] < $c['maw']) { 19708 $wc['maw'] = $c['maw']; 19709 } 19710 if ((isset($wc['absmiw']) && $wc['absmiw'] < $c['absmiw']) || !isset($wc['absmiw'])) { 19711 $wc['absmiw'] = $c['absmiw']; 19712 } // Update Col Minimum and maximum widths 19713 19714 if (isset($table['l'][$j])) { 19715 $table['l'][$j] += $c['s']; 19716 } else { 19717 $table['l'][$j] = $c['s']; 19718 } 19719 } else { 19720 $listspan[] = [$i, $j]; 19721 } 19722 19723 // Check if minimum width of the whole column is big enough for largest word to fit 19724 // mPDF 6 19725 if (isset($c['textbuffer'])) { 19726 if (isset($table['overflow']) && $table['overflow'] == 'wrap') { 19727 $letter = true; 19728 } // check for maximum width of letters 19729 else { 19730 $letter = false; 19731 } 19732 $minwidth = $this->TableCheckMinWidth($wc['miw'] - $extrcw, 0, $c['textbuffer'], $letter); 19733 } else { 19734 $minwidth = 0; 19735 } 19736 if ($minwidth < 0) { 19737 // increase minimum width 19738 if (!isset($c['colspan'])) { 19739 $wc['miw'] = max((isset($wc['miw']) ? $wc['miw'] : 0), ((-$minwidth) + $extrcw)); 19740 } else { 19741 $c['miw'] = max((isset($c['miw']) ? $c['miw'] : 0), ((-$minwidth) + $extrcw)); 19742 } 19743 } 19744 if (!isset($c['colspan'])) { 19745 if ($wc['miw'] > $wc['maw']) { 19746 $wc['maw'] = $wc['miw']; 19747 } // update maximum width, if needed 19748 } 19749 } 19750 unset($c); 19751 }//rows 19752 }//columns 19753 // COLUMN SPANS 19754 $wc = &$table['wc']; 19755 foreach ($listspan as $span) { 19756 list($i, $j) = $span; 19757 $c = &$cs[$i][$j]; 19758 $lc = $j + $c['colspan']; 19759 if ($lc > $nc) { 19760 $lc = $nc; 19761 } 19762 $wis = $wisa = 0; 19763 $was = $wasa = 0; 19764 $list = []; 19765 for ($k = $j; $k < $lc; $k++) { 19766 if (isset($table['l'][$k])) { 19767 if ($c['R']) { 19768 $table['l'][$k] += $c['miw'] / $c['colspan']; 19769 } else { 19770 $table['l'][$k] += $c['s'] / $c['colspan']; 19771 } 19772 } else { 19773 if ($c['R']) { 19774 $table['l'][$k] = $c['miw'] / $c['colspan']; 19775 } else { 19776 $table['l'][$k] = $c['s'] / $c['colspan']; 19777 } 19778 } 19779 $wis += $wc[$k]['miw']; // $wis is the sum of the column miw in the colspan 19780 $was += $wc[$k]['maw']; // $was is the sum of the column maw in the colspan 19781 if (!isset($c['w'])) { 19782 $list[] = $k; 19783 $wisa += $wc[$k]['miw']; // $wisa is the sum of the column miw in cells with no width specified in the colspan 19784 $wasa += $wc[$k]['maw']; // $wasa is the sum of the column maw in cells with no width specified in the colspan 19785 } 19786 } 19787 if ($c['miw'] > $wis) { 19788 if (!$wis) { 19789 for ($k = $j; $k < $lc; $k++) { 19790 $wc[$k]['miw'] = $c['miw'] / $c['colspan']; 19791 } 19792 } elseif (!count($list) && $wis != 0) { 19793 $wi = $c['miw'] - $wis; 19794 for ($k = $j; $k < $lc; $k++) { 19795 $wc[$k]['miw'] += ($wc[$k]['miw'] / $wis) * $wi; 19796 } 19797 } else { 19798 $wi = $c['miw'] - $wis; 19799 // mPDF 5.7.2 Extra min width distributed proportionately to all cells in colspan without a specified width 19800 // cf. http://www.mpdf1.com/forum/discussion/1607#Item_4 19801 foreach ($list as $k) { 19802 if (!isset($wc[$k]['w']) || !$wc[$k]['w']) { 19803 $wc[$k]['miw'] += ($wc[$k]['miw'] / $wisa) * $wi; 19804 } 19805 } // mPDF 5.7.2 19806 } 19807 } 19808 if ($c['maw'] > $was) { 19809 if (!$wis) { 19810 for ($k = $j; $k < $lc; $k++) { 19811 $wc[$k]['maw'] = $c['maw'] / $c['colspan']; 19812 } 19813 } elseif (!count($list) && $was != 0) { 19814 $wi = $c['maw'] - $was; 19815 for ($k = $j; $k < $lc; $k++) { 19816 $wc[$k]['maw'] += ($wc[$k]['maw'] / $was) * $wi; 19817 } 19818 } else { 19819 $wi = $c['maw'] - $was; 19820 // mPDF 5.7.4 Extra max width distributed evenly to all cells in colspan without a specified width 19821 // cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug 19822 foreach ($list as $k) { 19823 $wc[$k]['maw'] += $wi / count($list); 19824 } 19825 } 19826 } 19827 unset($c); 19828 } 19829 19830 $checkminwidth = 0; 19831 $checkmaxwidth = 0; 19832 $totallength = 0; 19833 19834 for ($i = 0; $i < $nc; $i++) { 19835 $checkminwidth += $table['wc'][$i]['miw']; 19836 $checkmaxwidth += $table['wc'][$i]['maw']; 19837 $totallength += isset($table['l']) ? $table['l'][$i] : 0; 19838 } 19839 19840 if (!isset($table['w']) && $firstpass) { 19841 $sumpc = 0; 19842 $notset = 0; 19843 for ($i = 0; $i < $nc; $i++) { 19844 if (isset($table['wc'][$i]['wpercent']) && $table['wc'][$i]['wpercent']) { 19845 $sumpc += $table['wc'][$i]['wpercent']; 19846 } else { 19847 $notset++; 19848 } 19849 } 19850 19851 // If sum of widths as % >= 100% and not all columns are set 19852 // Set a nominal width of 1% for unset columns 19853 if ($sumpc >= 100 && $notset) { 19854 for ($i = 0; $i < $nc; $i++) { 19855 if ((!isset($table['wc'][$i]['wpercent']) || !$table['wc'][$i]['wpercent']) && 19856 (!isset($table['wc'][$i]['w']) || !$table['wc'][$i]['w'])) { 19857 $table['wc'][$i]['wpercent'] = 1; 19858 } 19859 } 19860 } 19861 19862 19863 if ($sumpc) { // if any percents are set 19864 $sumnonpc = (100 - $sumpc); 19865 $sumpc = max($sumpc, 100); 19866 $miwleft = 0; 19867 $miwleftcount = 0; 19868 $miwsurplusnonpc = 0; 19869 $maxcalcmiw = 0; 19870 $mawleft = 0; 19871 $mawleftcount = 0; 19872 $mawsurplusnonpc = 0; 19873 $maxcalcmaw = 0; 19874 $mawnon = 0; 19875 $miwnon = 0; 19876 for ($i = 0; $i < $nc; $i++) { 19877 if (isset($table['wc'][$i]['wpercent'])) { 19878 $maxcalcmiw = max($maxcalcmiw, ($table['wc'][$i]['miw'] * $sumpc / $table['wc'][$i]['wpercent'])); 19879 $maxcalcmaw = max($maxcalcmaw, ($table['wc'][$i]['maw'] * $sumpc / $table['wc'][$i]['wpercent'])); 19880 } else { 19881 $miwleft += $table['wc'][$i]['miw']; 19882 $mawleft += $table['wc'][$i]['maw']; 19883 if (!isset($table['wc'][$i]['w'])) { 19884 $miwleftcount++; 19885 $mawleftcount++; 19886 } 19887 } 19888 } 19889 if ($miwleft && $sumnonpc > 0) { 19890 $miwnon = $miwleft * 100 / $sumnonpc; 19891 } 19892 if ($mawleft && $sumnonpc > 0) { 19893 $mawnon = $mawleft * 100 / $sumnonpc; 19894 } 19895 if (($miwnon > $checkminwidth || $maxcalcmiw > $checkminwidth) && $this->keep_table_proportions) { 19896 if ($miwnon > $maxcalcmiw) { 19897 $miwsurplusnonpc = round((($miwnon * $sumnonpc / 100) - $miwleft), 3); 19898 $checkminwidth = $miwnon; 19899 } else { 19900 $checkminwidth = $maxcalcmiw; 19901 } 19902 for ($i = 0; $i < $nc; $i++) { 19903 if (isset($table['wc'][$i]['wpercent'])) { 19904 $newmiw = $checkminwidth * $table['wc'][$i]['wpercent'] / 100; 19905 if ($table['wc'][$i]['miw'] < $newmiw) { 19906 $table['wc'][$i]['miw'] = $newmiw; 19907 } 19908 $table['wc'][$i]['w'] = 1; 19909 } elseif ($miwsurplusnonpc && !$table['wc'][$i]['w']) { 19910 $table['wc'][$i]['miw'] += $miwsurplusnonpc / $miwleftcount; 19911 } 19912 } 19913 } 19914 if (($mawnon > $checkmaxwidth || $maxcalcmaw > $checkmaxwidth)) { 19915 if ($mawnon > $maxcalcmaw) { 19916 $mawsurplusnonpc = round((($mawnon * $sumnonpc / 100) - $mawleft), 3); 19917 $checkmaxwidth = $mawnon; 19918 } else { 19919 $checkmaxwidth = $maxcalcmaw; 19920 } 19921 for ($i = 0; $i < $nc; $i++) { 19922 if (isset($table['wc'][$i]['wpercent'])) { 19923 $newmaw = $checkmaxwidth * $table['wc'][$i]['wpercent'] / 100; 19924 if ($table['wc'][$i]['maw'] < $newmaw) { 19925 $table['wc'][$i]['maw'] = $newmaw; 19926 } 19927 $table['wc'][$i]['w'] = 1; 19928 } elseif ($mawsurplusnonpc && !$table['wc'][$i]['w']) { 19929 $table['wc'][$i]['maw'] += $mawsurplusnonpc / $mawleftcount; 19930 } 19931 if ($table['wc'][$i]['maw'] < $table['wc'][$i]['miw']) { 19932 $table['wc'][$i]['maw'] = $table['wc'][$i]['miw']; 19933 } 19934 } 19935 } 19936 if ($checkminwidth > $checkmaxwidth) { 19937 $checkmaxwidth = $checkminwidth; 19938 } 19939 } 19940 } 19941 19942 if (isset($table['wpercent']) && $table['wpercent']) { 19943 $checkminwidth *= (100 / $table['wpercent']); 19944 $checkmaxwidth *= (100 / $table['wpercent']); 19945 } 19946 19947 19948 $checkminwidth += $tblbw; 19949 $checkmaxwidth += $tblbw; 19950 19951 // Table['miw'] set by percent in first pass may be larger than sum of column miw 19952 if ((isset($table['miw']) && $checkminwidth > $table['miw']) || !isset($table['miw'])) { 19953 $table['miw'] = $checkminwidth; 19954 } 19955 if ((isset($table['maw']) && $checkmaxwidth > $table['maw']) || !isset($table['maw'])) { 19956 $table['maw'] = $checkmaxwidth; 19957 } 19958 $table['tl'] = $totallength; 19959 19960 // mPDF 6 19961 if ($this->table_rotate) { 19962 $mxw = $this->tbrot_maxw; 19963 } else { 19964 $mxw = $this->blk[$this->blklvl]['inner_width']; 19965 } 19966 19967 if (!isset($table['overflow'])) { 19968 $table['overflow'] = null; 19969 } 19970 19971 if ($table['overflow'] == 'visible') { 19972 return [0, 0]; 19973 } elseif ($table['overflow'] == 'hidden' && !$this->table_rotate && !$this->ColActive && $checkminwidth > $mxw) { 19974 $table['w'] = $table['miw']; 19975 return [0, 0]; 19976 } 19977 // elseif ($table['overflow']=='wrap') { return array(0,0); } // mPDF 6 19978 19979 if (isset($table['w']) && $table['w']) { 19980 19981 if ($table['w'] >= $checkminwidth && $table['w'] <= $mxw) { 19982 $table['maw'] = $mxw = $table['w']; 19983 } elseif ($table['w'] >= $checkminwidth && $table['w'] > $mxw && $this->keep_table_proportions) { 19984 $checkminwidth = $table['w']; 19985 } elseif ($table['w'] < $checkminwidth && $checkminwidth < $mxw && $this->keep_table_proportions) { 19986 $table['maw'] = $table['w'] = $checkminwidth; 19987 } else { 19988 unset($table['w']); 19989 } 19990 } 19991 19992 $ratio = $checkminwidth / $mxw; 19993 19994 if ($checkminwidth > $mxw) { 19995 return [($ratio + 0.001), $checkminwidth]; // 0.001 to allow for rounded numbers when resizing 19996 } 19997 19998 unset($cs); 19999 20000 return [0, 0]; 20001 } 20002 20003 function _tableWidth(&$table) 20004 { 20005 $widthcols = &$table['wc']; 20006 $numcols = $table['nc']; 20007 $tablewidth = 0; 20008 20009 if ($table['borders_separate']) { 20010 $tblbw = $table['border_details']['L']['w'] + $table['border_details']['R']['w'] + $table['margin']['L'] + $table['margin']['R'] + $table['padding']['L'] + $table['padding']['R'] + $table['border_spacing_H']; 20011 } else { 20012 $tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R']; 20013 } 20014 20015 if ($table['level'] > 1 && isset($table['w'])) { 20016 20017 if (isset($table['wpercent']) && $table['wpercent']) { 20018 $table['w'] = $temppgwidth = (($table['w'] - $tblbw) * $table['wpercent'] / 100) + $tblbw; 20019 } else { 20020 $temppgwidth = $table['w']; 20021 } 20022 20023 } elseif ($this->table_rotate) { 20024 20025 $temppgwidth = $this->tbrot_maxw; 20026 20027 // If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin) then allow for this 20028 $enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']; 20029 20030 if ($enddiv / $temppgwidth < 0.05) { 20031 $temppgwidth -= $enddiv; 20032 } 20033 20034 } else { 20035 20036 if (isset($table['w']) && $table['w'] < $this->blk[$this->blklvl]['inner_width']) { 20037 $notfullwidth = 1; 20038 $temppgwidth = $table['w']; 20039 } elseif ($table['overflow'] == 'visible' && $table['level'] == 1) { 20040 $temppgwidth = null; 20041 } elseif ($table['overflow'] == 'hidden' && !$this->ColActive && isset($table['w']) && $table['w'] > $this->blk[$this->blklvl]['inner_width'] && $table['w'] == $table) { 20042 // $temppgwidth = $this->blk[$this->blklvl]['inner_width']; 20043 $temppgwidth = $table['w']; 20044 } else { 20045 $temppgwidth = $this->blk[$this->blklvl]['inner_width']; 20046 } 20047 20048 } 20049 20050 $totaltextlength = 0; // Added - to sum $table['l'][colno] 20051 $totalatextlength = 0; // Added - to sum $table['l'][colno] for those columns where width not set 20052 $percentages_set = 0; 20053 20054 for ($i = 0; $i < $numcols; $i++) { 20055 if (isset($widthcols[$i]['wpercent'])) { 20056 $tablewidth += $widthcols[$i]['maw']; 20057 $percentages_set = 1; 20058 } elseif (isset($widthcols[$i]['w'])) { 20059 $tablewidth += $widthcols[$i]['miw']; 20060 } else { 20061 $tablewidth += $widthcols[$i]['maw']; 20062 } 20063 $totaltextlength += isset($table['l']) ? $table['l'][$i] : 0; 20064 } 20065 20066 if (!$totaltextlength) { 20067 $totaltextlength = 1; 20068 } 20069 20070 $tablewidth += $tblbw; // Outer half of table borders 20071 20072 if ($tablewidth > $temppgwidth) { 20073 $table['w'] = $temppgwidth; 20074 } elseif ($tablewidth < $temppgwidth && !isset($table['w']) && $percentages_set) { // if any widths set as percentages and max width fits < page width 20075 $table['w'] = $table['maw']; 20076 } 20077 20078 // if table width is set and is > allowed width 20079 if (isset($table['w']) && $table['w'] > $temppgwidth) { 20080 $table['w'] = $temppgwidth; 20081 } 20082 20083 // IF the table width is now set - Need to distribute columns widths 20084 // mPDF 5.7.3 20085 // If the table width is already set to the maximum width (e.g. nested table), then use maximum column widths exactly 20086 if (isset($table['w']) && ($table['w'] == $tablewidth) && !$percentages_set) { 20087 20088 // This sets the columns all to maximum width 20089 for ($i = 0; $i < $numcols; $i++) { 20090 $widthcols[$i] = $widthcols[$i]['maw']; 20091 } 20092 20093 } elseif (isset($table['w'])) { // elseif the table width is set distribute width using algorithm 20094 20095 $wis = $wisa = 0; 20096 $list = []; 20097 $notsetlist = []; 20098 20099 for ($i = 0; $i < $numcols; $i++) { 20100 $wis += $widthcols[$i]['miw']; 20101 if (!isset($widthcols[$i]['w']) || ($widthcols[$i]['w'] && $table['w'] > $temppgwidth && !$this->keep_table_proportions && !$notfullwidth )) { 20102 $list[] = $i; 20103 $wisa += $widthcols[$i]['miw']; 20104 $totalatextlength += $table['l'][$i]; 20105 } 20106 } 20107 20108 if (!$totalatextlength) { 20109 $totalatextlength = 1; 20110 } 20111 20112 // Allocate spare (more than col's minimum width) across the cols according to their approx total text length 20113 // Do it by setting minimum width here 20114 if ($table['w'] > $wis + $tblbw) { 20115 20116 // First set any cell widths set as percentages 20117 if ($table['w'] < $temppgwidth || $this->keep_table_proportions) { 20118 for ($k = 0; $k < $numcols; $k++) { 20119 if (isset($widthcols[$k]['wpercent'])) { 20120 $curr = $widthcols[$k]['miw']; 20121 $widthcols[$k]['miw'] = ($table['w'] - $tblbw) * $widthcols[$k]['wpercent'] / 100; 20122 $wis += $widthcols[$k]['miw'] - $curr; 20123 $wisa += $widthcols[$k]['miw'] - $curr; 20124 } 20125 } 20126 } 20127 20128 // Now allocate surplus up to maximum width of each column 20129 $surplus = 0; 20130 $ttl = 0; // number of surplus columns 20131 20132 if (!count($list)) { 20133 20134 $wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute 20135 20136 for ($k = 0; $k < $numcols; $k++) { 20137 20138 $spareratio = ($table['l'][$k] / $totaltextlength); // gives ratio to divide up free space 20139 20140 // Don't allocate more than Maximum required width - save rest in surplus 20141 if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3 20142 $surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']); 20143 $widthcols[$k]['miw'] = $widthcols[$k]['maw']; 20144 } else { 20145 $notsetlist[] = $k; 20146 $ttl += $table['l'][$k]; 20147 $widthcols[$k]['miw'] += ($wi * $spareratio); 20148 } 20149 } 20150 20151 } else { 20152 20153 $wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute 20154 20155 foreach ($list as $k) { 20156 20157 $spareratio = ($table['l'][$k] / $totalatextlength); // gives ratio to divide up free space 20158 20159 // Don't allocate more than Maximum required width - save rest in surplus 20160 if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3 20161 $surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']); 20162 $widthcols[$k]['miw'] = $widthcols[$k]['maw']; 20163 } else { 20164 $notsetlist[] = $k; 20165 $ttl += $table['l'][$k]; 20166 $widthcols[$k]['miw'] += ($wi * $spareratio); 20167 } 20168 } 20169 } 20170 20171 // If surplus still left over apportion it across columns 20172 if ($surplus) { 20173 20174 if (count($notsetlist) && count($notsetlist) < $numcols) { // if some are set only add to remaining - otherwise add to all of them 20175 foreach ($notsetlist as $i) { 20176 if ($ttl) { 20177 $widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl; 20178 } 20179 } 20180 } elseif (count($list) && count($list) < $numcols) { // If some widths are defined, and others have been added up to their maxmum 20181 foreach ($list as $i) { 20182 $widthcols[$i]['miw'] += $surplus / count($list); 20183 } 20184 } elseif ($numcols) { // If all columns 20185 $ttl = array_sum($table['l']); 20186 if ($ttl) { 20187 for ($i = 0; $i < $numcols; $i++) { 20188 $widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl; 20189 } 20190 } 20191 } 20192 } 20193 } 20194 20195 // This sets the columns all to minimum width (which has been increased above if appropriate) 20196 for ($i = 0; $i < $numcols; $i++) { 20197 $widthcols[$i] = $widthcols[$i]['miw']; 20198 } 20199 20200 // TABLE NOT WIDE ENOUGH EVEN FOR MINIMUM CONTENT WIDTH 20201 // If sum of column widths set are too wide for table 20202 $checktablewidth = 0; 20203 for ($i = 0; $i < $numcols; $i++) { 20204 $checktablewidth += $widthcols[$i]; 20205 } 20206 20207 if ($checktablewidth > ($temppgwidth + 0.001 - $tblbw)) { 20208 20209 $usedup = 0; 20210 $numleft = 0; 20211 20212 for ($i = 0; $i < $numcols; $i++) { 20213 if ((isset($widthcols[$i]) && $widthcols[$i] > (($temppgwidth - $tblbw) / $numcols)) && (!isset($widthcols[$i]['w']))) { 20214 $numleft++; 20215 unset($widthcols[$i]); 20216 } else { 20217 $usedup += $widthcols[$i]; 20218 } 20219 } 20220 20221 for ($i = 0; $i < $numcols; $i++) { 20222 if (!isset($widthcols[$i]) || !$widthcols[$i]) { 20223 $widthcols[$i] = ((($temppgwidth - $tblbw) - $usedup) / ($numleft)); 20224 } 20225 } 20226 } 20227 20228 } else { // table has no width defined 20229 20230 $table['w'] = $tablewidth; 20231 20232 for ($i = 0; $i < $numcols; $i++) { 20233 20234 if (isset($widthcols[$i]['wpercent']) && $this->keep_table_proportions) { 20235 $colwidth = $widthcols[$i]['maw']; 20236 } elseif (isset($widthcols[$i]['w'])) { 20237 $colwidth = $widthcols[$i]['miw']; 20238 } else { 20239 $colwidth = $widthcols[$i]['maw']; 20240 } 20241 20242 unset($widthcols[$i]); 20243 $widthcols[$i] = $colwidth; 20244 20245 } 20246 } 20247 20248 if ($table['overflow'] === 'visible' && $table['level'] == 1) { 20249 20250 if ($tablewidth > $this->blk[$this->blklvl]['inner_width']) { 20251 20252 for ($j = 0; $j < $numcols; $j++) { // columns 20253 20254 for ($i = 0; $i < $table['nr']; $i++) { // rows 20255 20256 if (isset($table['cells'][$i][$j]) && $table['cells'][$i][$j]) { 20257 20258 $colspan = (isset($table['cells'][$i][$j]['colspan']) ? $table['cells'][$i][$j]['colspan'] : 1); 20259 20260 if ($colspan > 1) { 20261 $w = 0; 20262 20263 for ($c = $j; $c < ($j + $colspan); $c++) { 20264 $w += $widthcols[$c]; 20265 } 20266 20267 if ($w > $this->blk[$this->blklvl]['inner_width']) { 20268 $diff = $w - ($this->blk[$this->blklvl]['inner_width'] - $tblbw); 20269 for ($c = $j; $c < ($j + $colspan); $c++) { 20270 $widthcols[$c] -= $diff * ($widthcols[$c] / $w); 20271 } 20272 $table['w'] -= $diff; 20273 $table['csp'][$j] = $w - $diff; 20274 } 20275 } 20276 } 20277 } 20278 } 20279 } 20280 20281 $pgNo = 0; 20282 $currWc = 0; 20283 20284 for ($i = 0; $i < $numcols; $i++) { // columns 20285 20286 if (isset($table['csp'][$i])) { 20287 $w = $table['csp'][$i]; 20288 unset($table['csp'][$i]); 20289 } else { 20290 $w = $widthcols[$i]; 20291 } 20292 20293 if (($currWc + $w + $tblbw) > $this->blk[$this->blklvl]['inner_width']) { 20294 $pgNo++; 20295 $currWc = $widthcols[$i]; 20296 } else { 20297 $currWc += $widthcols[$i]; 20298 } 20299 20300 $table['colPg'][$i] = $pgNo; 20301 } 20302 } 20303 } 20304 20305 function _tableHeight(&$table) 20306 { 20307 $level = $table['level']; 20308 $levelid = $table['levelid']; 20309 $cells = &$table['cells']; 20310 $numcols = $table['nc']; 20311 $numrows = $table['nr']; 20312 $listspan = []; 20313 $checkmaxheight = 0; 20314 $headerrowheight = 0; 20315 $checkmaxheightplus = 0; 20316 $headerrowheightplus = 0; 20317 $firstrowheight = 0; 20318 20319 $footerrowheight = 0; 20320 $footerrowheightplus = 0; 20321 if ($this->table_rotate) { 20322 $temppgheight = $this->tbrot_maxh; 20323 $remainingpage = $this->tbrot_maxh; 20324 } else { 20325 $temppgheight = ($this->h - $this->bMargin - $this->tMargin) - $this->kwt_height; 20326 $remainingpage = ($this->h - $this->bMargin - $this->y) - $this->kwt_height; 20327 20328 // If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin) 20329 // then allow for this 20330 $enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $table['margin']['B']; 20331 if ($remainingpage > $enddiv && $enddiv / $remainingpage < 0.05) { 20332 $remainingpage -= $enddiv; 20333 } elseif ($remainingpage == 0) { 20334 $remainingpage = 0.001; 20335 } 20336 if ($temppgheight > $enddiv && $enddiv / $temppgheight < 0.05) { 20337 $temppgheight -= $enddiv; 20338 } elseif ($temppgheight == 0) { 20339 $temppgheight = 0.001; 20340 } 20341 } 20342 if ($remainingpage < 0) { 20343 $remainingpage = 0.001; 20344 } 20345 if ($temppgheight < 0) { 20346 $temppgheight = 0.001; 20347 } 20348 20349 for ($i = 0; $i < $numrows; $i++) { // rows 20350 $heightrow = &$table['hr'][$i]; 20351 for ($j = 0; $j < $numcols; $j++) { // columns 20352 if (isset($cells[$i][$j]) && $cells[$i][$j]) { 20353 $c = &$cells[$i][$j]; 20354 20355 if ($this->simpleTables) { 20356 if ($table['borders_separate']) { // NB twice border width 20357 $extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) + ($c['padding']['L'] + $c['padding']['R']) + $table['border_spacing_H']; 20358 $extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) + ($c['padding']['T'] + $c['padding']['B']) + $table['border_spacing_V']; 20359 } else { 20360 $extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + ($c['padding']['L'] + $c['padding']['R']); 20361 $extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) / 2 + ($c['padding']['T'] + $c['padding']['B']); 20362 } 20363 } else { 20364 if ($this->packTableData) { 20365 list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']); 20366 } else { 20367 $bt = $c['border_details']['T']['w']; 20368 $bb = $c['border_details']['B']['w']; 20369 $br = $c['border_details']['R']['w']; 20370 $bl = $c['border_details']['L']['w']; 20371 } 20372 if ($table['borders_separate']) { // NB twice border width 20373 $extraWLR = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H']; 20374 $extrh = $bt + $bb + $c['padding']['T'] + $c['padding']['B'] + $table['border_spacing_V']; 20375 } else { 20376 $extraWLR = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R']; 20377 $extrh = $bt / 2 + $bb / 2 + $c['padding']['T'] + $c['padding']['B']; 20378 } 20379 } 20380 20381 if ($table['overflow'] == 'visible' && $level == 1) { 20382 list($x, $cw) = $this->_splitTableGetWidth($table, $i, $j); 20383 } else { 20384 list($x, $cw) = $this->_tableGetWidth($table, $i, $j); 20385 } 20386 20387 20388 // Get CELL HEIGHT 20389 // ++ extra parameter forces wrap to break word 20390 if ($c['R'] && isset($c['textbuffer'])) { 20391 $str = ''; 20392 foreach ($c['textbuffer'] as $t) { 20393 $str .= $t[0] . ' '; 20394 } 20395 $str = rtrim($str); 20396 $s_fs = $this->FontSizePt; 20397 $s_f = $this->FontFamily; 20398 $s_st = $this->FontStyle; 20399 $this->SetFont($c['textbuffer'][0][4], $c['textbuffer'][0][2], $c['textbuffer'][0][11] / $this->shrin_k, true, true); 20400 $tempch = $this->GetStringWidth($str, true, $c['textbuffer'][0][18], $c['textbuffer'][0][8]); 20401 if ($c['R'] >= 45 && $c['R'] < 90) { 20402 $tempch = ((sin(deg2rad($c['R']))) * $tempch ) + ((sin(deg2rad($c['R']))) * (($c['textbuffer'][0][11] / Mpdf::SCALE) / $this->shrin_k)); 20403 } 20404 $this->SetFont($s_f, $s_st, $s_fs, true, true); 20405 $ch = ($tempch ) + $extrh; 20406 } else { 20407 if (isset($c['textbuffer']) && !empty($c['textbuffer'])) { 20408 $this->cellLineHeight = $c['cellLineHeight']; 20409 $this->cellLineStackingStrategy = $c['cellLineStackingStrategy']; 20410 $this->cellLineStackingShift = $c['cellLineStackingShift']; 20411 $this->divwidth = $cw - $extraWLR; 20412 $tempch = $this->printbuffer($c['textbuffer'], '', true, true); 20413 } else { 20414 $tempch = 0; 20415 } 20416 20417 // Added cellpadding top and bottom. (Lineheight already adjusted) 20418 $ch = $tempch + $extrh; 20419 } 20420 // If height is defined and it is bigger than calculated $ch then update values 20421 if (isset($c['h']) && $c['h'] > $ch) { 20422 $c['mih'] = $ch; // in order to keep valign working 20423 $ch = $c['h']; 20424 } else { 20425 $c['mih'] = $ch; 20426 } 20427 if (isset($c['rowspan'])) { 20428 $listspan[] = [$i, $j]; 20429 } elseif ($heightrow < $ch) { 20430 $heightrow = $ch; 20431 } 20432 20433 // this is the extra used in _tableWrite to determine whether to trigger a page change 20434 if ($table['borders_separate']) { 20435 if ($i == ($numrows - 1) || (isset($c['rowspan']) && ($i + $c['rowspan']) == ($numrows))) { 20436 $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2; 20437 } else { 20438 $extra = $table['border_spacing_V'] / 2; 20439 } 20440 } else { 20441 if (!$this->simpleTables) { 20442 $extra = $bb / 2; 20443 } elseif ($this->simpleTables) { 20444 $extra = $table['simple']['border_details']['B']['w'] / 2; 20445 } 20446 } 20447 if (isset($table['is_thead'][$i]) && $table['is_thead'][$i]) { 20448 if ($j == 0) { 20449 $headerrowheight += $ch; 20450 $headerrowheightplus += $ch + $extra; 20451 } 20452 } elseif (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) { 20453 if ($j == 0) { 20454 $footerrowheight += $ch; 20455 $footerrowheightplus += $ch + $extra; 20456 } 20457 } else { 20458 $checkmaxheight = max($checkmaxheight, $ch); 20459 $checkmaxheightplus = max($checkmaxheightplus, $ch + $extra); 20460 } 20461 if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) { 20462 $firstrowheight = max($ch, $firstrowheight); 20463 } 20464 unset($c); 20465 } 20466 }//end of columns 20467 }//end of rows 20468 20469 $heightrow = &$table['hr']; 20470 foreach ($listspan as $span) { 20471 list($i, $j) = $span; 20472 $c = &$cells[$i][$j]; 20473 $lr = $i + $c['rowspan']; 20474 if ($lr > $numrows) { 20475 $lr = $numrows; 20476 } 20477 $hs = $hsa = 0; 20478 $list = []; 20479 for ($k = $i; $k < $lr; $k++) { 20480 $hs += $heightrow[$k]; 20481 // mPDF 6 20482 $sh = false; // specified height 20483 for ($m = 0; $m < $numcols; $m++) { // columns 20484 $tc = &$cells[$k][$m]; 20485 if (isset($tc['rowspan'])) { 20486 continue; 20487 } 20488 if (isset($tc['h'])) { 20489 $sh = true; 20490 break; 20491 } 20492 } 20493 if (!$sh) { 20494 $list[] = $k; 20495 } 20496 } 20497 20498 if ($table['borders_separate']) { 20499 if ($i == ($numrows - 1) || ($i + $c['rowspan']) == ($numrows)) { 20500 $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2; 20501 } else { 20502 $extra = $table['border_spacing_V'] / 2; 20503 } 20504 } else { 20505 if (!$this->simpleTables) { 20506 if ($this->packTableData) { 20507 list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']); 20508 } else { 20509 $bb = $c['border_details']['B']['w']; 20510 } 20511 $extra = $bb / 2; 20512 } elseif ($this->simpleTables) { 20513 $extra = $table['simple']['border_details']['B']['w'] / 2; 20514 } 20515 } 20516 if (!empty($table['is_thead'][$i])) { 20517 $headerrowheight = max($headerrowheight, $hs); 20518 $headerrowheightplus = max($headerrowheightplus, $hs + $extra); 20519 } elseif (!empty($table['is_tfoot'][$i])) { 20520 $footerrowheight = max($footerrowheight, $hs); 20521 $footerrowheightplus = max($footerrowheightplus, $hs + $extra); 20522 } else { 20523 $checkmaxheight = max($checkmaxheight, $hs); 20524 $checkmaxheightplus = max($checkmaxheightplus, $hs + $extra); 20525 } 20526 if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) { 20527 $firstrowheight = max($hs, $firstrowheight); 20528 } 20529 20530 if ($c['mih'] > $hs) { 20531 if (!$hs) { 20532 for ($k = $i; $k < $lr; $k++) { 20533 $heightrow[$k] = $c['mih'] / $c['rowspan']; 20534 } 20535 } elseif (!count($list)) { // no rows in the rowspan have a height specified, so share amongst all rows equally 20536 $hi = $c['mih'] - $hs; 20537 for ($k = $i; $k < $lr; $k++) { 20538 $heightrow[$k] += ($heightrow[$k] / $hs) * $hi; 20539 } 20540 } else { 20541 $hi = $c['mih'] - $hs; // mPDF 6 20542 foreach ($list as $k) { 20543 $heightrow[$k] += $hi / (count($list)); // mPDF 6 20544 } 20545 } 20546 } 20547 unset($c); 20548 20549 // If rowspans overlap so that one or more rows do not have a height set... 20550 // i.e. for one or more rows, the only cells (explicit) in that row have rowspan>1 20551 // so heightrow is still == 0 20552 if ($heightrow[$i] == 0) { 20553 // Get row extent to analyse above and below 20554 $top = $i; 20555 foreach ($listspan as $checkspan) { 20556 list($cki, $ckj) = $checkspan; 20557 $c = &$cells[$cki][$ckj]; 20558 if (isset($c['rowspan']) && $c['rowspan'] > 1) { 20559 if (($cki + $c['rowspan'] - 1) >= $i) { 20560 $top = min($top, $cki); 20561 } 20562 } 20563 } 20564 $bottom = $i + $c['rowspan'] - 1; 20565 // Check for overconstrained conditions 20566 for ($k = $top; $k <= $bottom; $k++) { 20567 // if ['hr'] for any of the others is also 0, then abort (too complicated) 20568 if ($k != $i && $heightrow[$k] == 0) { 20569 break(1); 20570 } 20571 // check again that top and bottom are not crossed by rowspans - or abort (too complicated) 20572 if ($k == $top) { 20573 // ???? take account of colspan as well??? 20574 for ($m = 0; $m < $numcols; $m++) { // columns 20575 if (!isset($cells[$k][$m]) || $cells[$k][$m] == 0) { 20576 break(2); 20577 } 20578 } 20579 } elseif ($k == $bottom) { 20580 // ???? take account of colspan as well??? 20581 for ($m = 0; $m < $numcols; $m++) { // columns 20582 $c = &$cells[$k][$m]; 20583 if (isset($c['rowspan']) && $c['rowspan'] > 1) { 20584 break(2); 20585 } 20586 } 20587 } 20588 } 20589 // By columns add up col height using ['h'] if set or ['mih'] if not 20590 // Intentionally do not substract border-spacing 20591 $colH = []; 20592 $extH = 0; 20593 $newhr = []; 20594 for ($m = 0; $m < $numcols; $m++) { // columns 20595 for ($k = $top; $k <= $bottom; $k++) { 20596 if (isset($cells[$k][$m]) && $cells[$k][$m] != 0) { 20597 $c = &$cells[$k][$m]; 20598 if (isset($c['h']) && $c['h']) { 20599 $useh = $c['h']; 20600 } // ???? take account of colspan as well??? 20601 else { 20602 $useh = $c['mih']; 20603 } 20604 if (isset($colH[$m])) { 20605 $colH[$m] += $useh; 20606 } else { 20607 $colH[$m] = $useh; 20608 } 20609 if (!isset($c['rowspan']) || $c['rowspan'] < 2) { 20610 $newhr[$k] = max((isset($newhr[$k]) ? $newhr[$k] : 0), $useh); 20611 } 20612 } 20613 } 20614 $extH = max($extH, $colH[$m]); // mPDF 6 20615 } 20616 $newhr[$i] = $extH - array_sum($newhr); 20617 for ($k = $top; $k <= $bottom; $k++) { 20618 $heightrow[$k] = $newhr[$k]; 20619 } 20620 } 20621 20622 20623 unset($c); 20624 } 20625 20626 $table['h'] = array_sum($heightrow); 20627 unset($heightrow); 20628 20629 if ($table['borders_separate']) { 20630 $table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['border_details']['T']['w'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] + $table['padding']['T'] + $table['padding']['B']; 20631 } else { 20632 $table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['max_cell_border_width']['T'] / 2 + $table['max_cell_border_width']['B'] / 2; 20633 } 20634 20635 $maxrowheight = $checkmaxheightplus + $headerrowheightplus + $footerrowheightplus; 20636 $maxfirstrowheight = $firstrowheight + $headerrowheightplus + $footerrowheightplus; // includes thead, 1st row and tfoot 20637 return [$table['h'], $maxrowheight, $temppgheight, $remainingpage, $maxfirstrowheight]; 20638 } 20639 20640 function _tableGetWidth(&$table, $i, $j) 20641 { 20642 $cell = &$table['cells'][$i][$j]; 20643 if ($cell) { 20644 if (isset($cell['x0'])) { 20645 return [$cell['x0'], $cell['w0']]; 20646 } 20647 $x = 0; 20648 $widthcols = &$table['wc']; 20649 for ($k = 0; $k < $j; $k++) { 20650 $x += $widthcols[$k]; 20651 } 20652 $w = $widthcols[$j]; 20653 if (isset($cell['colspan'])) { 20654 for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) { 20655 $w += $widthcols[$k]; 20656 } 20657 } 20658 $cell['x0'] = $x; 20659 $cell['w0'] = $w; 20660 return [$x, $w]; 20661 } 20662 return [0, 0]; 20663 } 20664 20665 function _splitTableGetWidth(&$table, $i, $j) 20666 { 20667 $cell = &$table['cells'][$i][$j]; 20668 if ($cell) { 20669 if (isset($cell['x0'])) { 20670 return [$cell['x0'], $cell['w0']]; 20671 } 20672 $x = 0; 20673 $widthcols = &$table['wc']; 20674 $pg = $table['colPg'][$j]; 20675 for ($k = 0; $k < $j; $k++) { 20676 if ($table['colPg'][$k] == $pg) { 20677 $x += $widthcols[$k]; 20678 } 20679 } 20680 $w = $widthcols[$j]; 20681 if (isset($cell['colspan'])) { 20682 for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) { 20683 if ($table['colPg'][$k] == $pg) { 20684 $w += $widthcols[$k]; 20685 } 20686 } 20687 } 20688 $cell['x0'] = $x; 20689 $cell['w0'] = $w; 20690 return [$x, $w]; 20691 } 20692 return [0, 0]; 20693 } 20694 20695 function _tableGetHeight(&$table, $i, $j) 20696 { 20697 $cell = &$table['cells'][$i][$j]; 20698 if ($cell) { 20699 if (isset($cell['y0'])) { 20700 return [$cell['y0'], $cell['h0']]; 20701 } 20702 $y = 0; 20703 $heightrow = &$table['hr']; 20704 for ($k = 0; $k < $i; $k++) { 20705 $y += $heightrow[$k]; 20706 } 20707 $h = $heightrow[$i]; 20708 if (isset($cell['rowspan'])) { 20709 for ($k = $i + $cell['rowspan'] - 1; $k > $i; $k--) { 20710 if (array_key_exists($k, $heightrow)) { 20711 $h += $heightrow[$k]; 20712 } else { 20713 $this->logger->debug('Possible non-wellformed HTML markup in a table', ['context' => LogContext::HTML_MARKUP]); 20714 } 20715 } 20716 } 20717 $cell['y0'] = $y; 20718 $cell['h0'] = $h; 20719 return [$y, $h]; 20720 } 20721 return [0, 0]; 20722 } 20723 20724 function _tableGetMaxRowHeight($table, $row) 20725 { 20726 if ($row == $table['nc'] - 1) { 20727 return $table['hr'][$row]; 20728 } 20729 $maxrowheight = $table['hr'][$row]; 20730 for ($i = $row + 1; $i < $table['nr']; $i++) { 20731 $cellsset = 0; 20732 for ($j = 0; $j < $table['nc']; $j++) { 20733 if ($table['cells'][$i][$j]) { 20734 if (isset($table['cells'][$i][$j]['colspan'])) { 20735 $cellsset += $table['cells'][$i][$j]['colspan']; 20736 } else { 20737 $cellsset += 1; 20738 } 20739 } 20740 } 20741 if ($cellsset == $table['nc']) { 20742 return $maxrowheight; 20743 } else { 20744 $maxrowheight += $table['hr'][$i]; 20745 } 20746 } 20747 return $maxrowheight; 20748 } 20749 20750 // CHANGED TO ALLOW TABLE BORDER TO BE SPECIFIED CORRECTLY - added border_details 20751 function _tableRect($x, $y, $w, $h, $bord = -1, $details = [], $buffer = false, $bSeparate = false, $cort = 'cell', $tablecorner = '', $bsv = 0, $bsh = 0) 20752 { 20753 $cellBorderOverlay = []; 20754 20755 if ($bord == -1) { 20756 $this->Rect($x, $y, $w, $h); 20757 } elseif ($this->simpleTables && ($cort == 'cell')) { 20758 $this->SetLineWidth($details['L']['w']); 20759 if ($details['L']['c']) { 20760 $this->SetDColor($details['L']['c']); 20761 } else { 20762 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 20763 } 20764 $this->SetLineJoin(0); 20765 $this->Rect($x, $y, $w, $h); 20766 } elseif ($bord) { 20767 if (!$bSeparate && $buffer) { 20768 $priority = 'LRTB'; 20769 for ($p = 0; $p < strlen($priority); $p++) { 20770 $side = $priority[$p]; 20771 $details['p'] = $side; 20772 20773 $dom = 0; 20774 if (isset($details[$side]['w'])) { 20775 $dom += ($details[$side]['w'] * 100000); 20776 } 20777 if (isset($details[$side]['style'])) { 20778 $dom += (array_search($details[$side]['style'], $this->borderstyles) * 100); 20779 } 20780 if (isset($details[$side]['dom'])) { 20781 $dom += ($details[$side]['dom'] * 10); 20782 } 20783 20784 // Precedence to darker colours at joins 20785 $coldom = 0; 20786 if (isset($details[$side]['c']) && is_array($details[$side]['c'])) { 20787 if ($details[$side]['c'][0] == 3) { // RGB 20788 $coldom = 10 - (((ord($details[$side]['c'][1]) * 1.00) + (ord($details[$side]['c'][2]) * 1.00) + (ord($details[$side]['c'][3]) * 1.00)) / 76.5); 20789 } 20790 } // 10 black - 0 white 20791 if ($coldom) { 20792 $dom += $coldom; 20793 } 20794 // Lastly precedence to RIGHT and BOTTOM cells at joins 20795 if (isset($details['cellposdom'])) { 20796 $dom += $details['cellposdom']; 20797 } 20798 20799 $save = false; 20800 if ($side == 'T' && $this->issetBorder($bord, Border::TOP)) { 20801 $cbord = Border::TOP; 20802 $save = true; 20803 } elseif ($side == 'L' && $this->issetBorder($bord, Border::LEFT)) { 20804 $cbord = Border::LEFT; 20805 $save = true; 20806 } elseif ($side == 'R' && $this->issetBorder($bord, Border::RIGHT)) { 20807 $cbord = Border::RIGHT; 20808 $save = true; 20809 } elseif ($side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) { 20810 $cbord = Border::BOTTOM; 20811 $save = true; 20812 } 20813 20814 if ($save) { 20815 $this->cellBorderBuffer[] = pack("A16nCnda6A10d14", str_pad(sprintf("%08.7f", $dom), 16, "0", STR_PAD_LEFT), $cbord, ord($side), $details[$side]['s'], $details[$side]['w'], $details[$side]['c'], $details[$side]['style'], $x, $y, $w, $h, $details['mbw']['BL'], $details['mbw']['BR'], $details['mbw']['RT'], $details['mbw']['RB'], $details['mbw']['TL'], $details['mbw']['TR'], $details['mbw']['LT'], $details['mbw']['LB'], $details['cellposdom'], 0); 20816 if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset' || $details[$side]['style'] == 'double') { 20817 $details[$side]['overlay'] = true; 20818 $this->cellBorderBuffer[] = pack("A16nCnda6A10d14", str_pad(sprintf("%08.7f", ($dom + 4)), 16, "0", STR_PAD_LEFT), $cbord, ord($side), $details[$side]['s'], $details[$side]['w'], $details[$side]['c'], $details[$side]['style'], $x, $y, $w, $h, $details['mbw']['BL'], $details['mbw']['BR'], $details['mbw']['RT'], $details['mbw']['RB'], $details['mbw']['TL'], $details['mbw']['TR'], $details['mbw']['LT'], $details['mbw']['LB'], $details['cellposdom'], 1); 20819 } 20820 } 20821 } 20822 return; 20823 } 20824 20825 if (isset($details['p']) && strlen($details['p']) > 1) { 20826 $priority = $details['p']; 20827 } else { 20828 $priority = 'LTRB'; 20829 } 20830 $Tw = 0; 20831 $Rw = 0; 20832 $Bw = 0; 20833 $Lw = 0; 20834 if (isset($details['T']['w'])) { 20835 $Tw = $details['T']['w']; 20836 } 20837 if (isset($details['R']['w'])) { 20838 $Rw = $details['R']['w']; 20839 } 20840 if (isset($details['B']['w'])) { 20841 $Bw = $details['B']['w']; 20842 } 20843 if (isset($details['L']['w'])) { 20844 $Lw = $details['L']['w']; 20845 } 20846 20847 $x2 = $x + $w; 20848 $y2 = $y + $h; 20849 $oldlinewidth = $this->LineWidth; 20850 20851 for ($p = 0; $p < strlen($priority); $p++) { 20852 $side = $priority[$p]; 20853 $xadj = 0; 20854 $xadj2 = 0; 20855 $yadj = 0; 20856 $yadj2 = 0; 20857 $print = false; 20858 if ($Tw && $side == 'T' && $this->issetBorder($bord, Border::TOP)) { // TOP 20859 $ly1 = $y; 20860 $ly2 = $y; 20861 $lx1 = $x; 20862 $lx2 = $x2; 20863 $this->SetLineWidth($Tw); 20864 if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) { 20865 if ($Tw > $Lw) { 20866 $xadj = ($Tw - $Lw) / 2; 20867 } 20868 if ($Tw < $Lw) { 20869 $xadj = ($Tw + $Lw) / 2; 20870 } 20871 } else { 20872 $xadj = $Tw / 2 - $bsh / 2; 20873 } 20874 if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) { 20875 if ($Tw > $Rw) { 20876 $xadj2 = ($Tw - $Rw) / 2; 20877 } 20878 if ($Tw < $Rw) { 20879 $xadj2 = ($Tw + $Rw) / 2; 20880 } 20881 } else { 20882 $xadj2 = $Tw / 2 - $bsh / 2; 20883 } 20884 if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TL'])) { 20885 $xadj = ($Tw - $details['mbw']['TL']) / 2; 20886 } 20887 if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TR'])) { 20888 $xadj2 = ($Tw - $details['mbw']['TR']) / 2; 20889 } 20890 $print = true; 20891 } 20892 if ($Lw && $side == 'L' && $this->issetBorder($bord, Border::LEFT)) { // LEFT 20893 $ly1 = $y; 20894 $ly2 = $y2; 20895 $lx1 = $x; 20896 $lx2 = $x; 20897 $this->SetLineWidth($Lw); 20898 if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) { 20899 if ($Lw > $Tw) { 20900 $yadj = ($Lw - $Tw) / 2; 20901 } 20902 if ($Lw < $Tw) { 20903 $yadj = ($Lw + $Tw) / 2; 20904 } 20905 } else { 20906 $yadj = $Lw / 2 - $bsv / 2; 20907 } 20908 if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) { 20909 if ($Lw > $Bw) { 20910 $yadj2 = ($Lw - $Bw) / 2; 20911 } 20912 if ($Lw < $Bw) { 20913 $yadj2 = ($Lw + $Bw) / 2; 20914 } 20915 } else { 20916 $yadj2 = $Lw / 2 - $bsv / 2; 20917 } 20918 if (!$bSeparate && $details['mbw']['LT']) { 20919 $yadj = ($Lw - $details['mbw']['LT']) / 2; 20920 } 20921 if (!$bSeparate && $details['mbw']['LB']) { 20922 $yadj2 = ($Lw - $details['mbw']['LB']) / 2; 20923 } 20924 $print = true; 20925 } 20926 if ($Rw && $side == 'R' && $this->issetBorder($bord, Border::RIGHT)) { // RIGHT 20927 $ly1 = $y; 20928 $ly2 = $y2; 20929 $lx1 = $x2; 20930 $lx2 = $x2; 20931 $this->SetLineWidth($Rw); 20932 if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) { 20933 if ($Rw < $Tw) { 20934 $yadj = ($Rw + $Tw) / 2; 20935 } 20936 if ($Rw > $Tw) { 20937 $yadj = ($Rw - $Tw) / 2; 20938 } 20939 } else { 20940 $yadj = $Rw / 2 - $bsv / 2; 20941 } 20942 20943 if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) { 20944 if ($Rw > $Bw) { 20945 $yadj2 = ($Rw - $Bw) / 2; 20946 } 20947 if ($Rw < $Bw) { 20948 $yadj2 = ($Rw + $Bw) / 2; 20949 } 20950 } else { 20951 $yadj2 = $Rw / 2 - $bsv / 2; 20952 } 20953 20954 if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RT'])) { 20955 $yadj = ($Rw - $details['mbw']['RT']) / 2; 20956 } 20957 if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RB'])) { 20958 $yadj2 = ($Rw - $details['mbw']['RB']) / 2; 20959 } 20960 $print = true; 20961 } 20962 if ($Bw && $side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) { // BOTTOM 20963 $ly1 = $y2; 20964 $ly2 = $y2; 20965 $lx1 = $x; 20966 $lx2 = $x2; 20967 $this->SetLineWidth($Bw); 20968 if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) { 20969 if ($Bw > $Lw) { 20970 $xadj = ($Bw - $Lw) / 2; 20971 } 20972 if ($Bw < $Lw) { 20973 $xadj = ($Bw + $Lw) / 2; 20974 } 20975 } else { 20976 $xadj = $Bw / 2 - $bsh / 2; 20977 } 20978 if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) { 20979 if ($Bw > $Rw) { 20980 $xadj2 = ($Bw - $Rw) / 2; 20981 } 20982 if ($Bw < $Rw) { 20983 $xadj2 = ($Bw + $Rw) / 2; 20984 } 20985 } else { 20986 $xadj2 = $Bw / 2 - $bsh / 2; 20987 } 20988 if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BL'])) { 20989 $xadj = ($Bw - $details['mbw']['BL']) / 2; 20990 } 20991 if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BR'])) { 20992 $xadj2 = ($Bw - $details['mbw']['BR']) / 2; 20993 } 20994 $print = true; 20995 } 20996 20997 // Now draw line 20998 if ($print) { 20999 /* -- TABLES-ADVANCED-BORDERS -- */ 21000 if ($details[$side]['style'] == 'double') { 21001 if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) { 21002 if ($details[$side]['c']) { 21003 $this->SetDColor($details[$side]['c']); 21004 } else { 21005 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 21006 } 21007 $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2); 21008 } 21009 if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) { 21010 if ($bSeparate && $cort == 'table') { 21011 if ($side == 'T') { 21012 $xadj -= $this->LineWidth / 2; 21013 $xadj2 -= $this->LineWidth; 21014 if ($this->issetBorder($bord, Border::LEFT)) { 21015 $xadj += $this->LineWidth / 2; 21016 } 21017 if ($this->issetBorder($bord, Border::RIGHT)) { 21018 $xadj2 += $this->LineWidth; 21019 } 21020 } 21021 if ($side == 'L') { 21022 $yadj -= $this->LineWidth / 2; 21023 $yadj2 -= $this->LineWidth; 21024 if ($this->issetBorder($bord, Border::TOP)) { 21025 $yadj += $this->LineWidth / 2; 21026 } 21027 if ($this->issetBorder($bord, Border::BOTTOM)) { 21028 $yadj2 += $this->LineWidth; 21029 } 21030 } 21031 if ($side == 'B') { 21032 $xadj -= $this->LineWidth / 2; 21033 $xadj2 -= $this->LineWidth; 21034 if ($this->issetBorder($bord, Border::LEFT)) { 21035 $xadj += $this->LineWidth / 2; 21036 } 21037 if ($this->issetBorder($bord, Border::RIGHT)) { 21038 $xadj2 += $this->LineWidth; 21039 } 21040 } 21041 if ($side == 'R') { 21042 $yadj -= $this->LineWidth / 2; 21043 $yadj2 -= $this->LineWidth; 21044 if ($this->issetBorder($bord, Border::TOP)) { 21045 $yadj += $this->LineWidth / 2; 21046 } 21047 if ($this->issetBorder($bord, Border::BOTTOM)) { 21048 $yadj2 += $this->LineWidth; 21049 } 21050 } 21051 } 21052 21053 $this->SetLineWidth($this->LineWidth / 3); 21054 21055 $tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings); 21056 for ($l = 0; $l <= $this->blklvl; $l++) { 21057 if ($this->blk[$l]['bgcolor']) { 21058 $tbcol = ($this->blk[$l]['bgcolorarray']); 21059 } 21060 } 21061 21062 if ($bSeparate) { 21063 $cellBorderOverlay[] = [ 21064 'x' => $lx1 + $xadj, 21065 'y' => $ly1 + $yadj, 21066 'x2' => $lx2 - $xadj2, 21067 'y2' => $ly2 - $yadj2, 21068 'col' => $tbcol, 21069 'lw' => $this->LineWidth, 21070 ]; 21071 } else { 21072 $this->SetDColor($tbcol); 21073 $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2); 21074 } 21075 } 21076 } elseif (isset($details[$side]['style']) && ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset')) { 21077 if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) { 21078 if ($details[$side]['c']) { 21079 $this->SetDColor($details[$side]['c']); 21080 } else { 21081 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 21082 } 21083 if ($details[$side]['style'] == 'outset' || $details[$side]['style'] == 'groove') { 21084 $nc = $this->colorConverter->darken($details[$side]['c']); 21085 $this->SetDColor($nc); 21086 } elseif ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') { 21087 $nc = $this->colorConverter->lighten($details[$side]['c']); 21088 $this->SetDColor($nc); 21089 } 21090 $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2); 21091 } 21092 if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) { 21093 if ($details[$side]['c']) { 21094 $this->SetDColor($details[$side]['c']); 21095 } else { 21096 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 21097 } 21098 $doubleadj = ($this->LineWidth) / 3; 21099 $this->SetLineWidth($this->LineWidth / 2); 21100 $xadj3 = $yadj3 = $wadj3 = $hadj3 = 0; 21101 21102 if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') { 21103 $nc = $this->colorConverter->darken($details[$side]['c']); 21104 21105 if ($bSeparate && $cort == 'table') { 21106 if ($side == 'T') { 21107 $yadj3 = $this->LineWidth / 2; 21108 $xadj3 = -$this->LineWidth / 2; 21109 $wadj3 = $this->LineWidth; 21110 if ($this->issetBorder($bord, Border::LEFT)) { 21111 $xadj3 += $this->LineWidth; 21112 $wadj3 -= $this->LineWidth; 21113 } 21114 if ($this->issetBorder($bord, Border::RIGHT)) { 21115 $wadj3 -= $this->LineWidth * 2; 21116 } 21117 } 21118 if ($side == 'L') { 21119 $xadj3 = $this->LineWidth / 2; 21120 $yadj3 = -$this->LineWidth / 2; 21121 $hadj3 = $this->LineWidth; 21122 if ($this->issetBorder($bord, Border::TOP)) { 21123 $yadj3 += $this->LineWidth; 21124 $hadj3 -= $this->LineWidth; 21125 } 21126 if ($this->issetBorder($bord, Border::BOTTOM)) { 21127 $hadj3 -= $this->LineWidth * 2; 21128 } 21129 } 21130 if ($side == 'B') { 21131 $yadj3 = $this->LineWidth / 2; 21132 $xadj3 = -$this->LineWidth / 2; 21133 $wadj3 = $this->LineWidth; 21134 } 21135 if ($side == 'R') { 21136 $xadj3 = $this->LineWidth / 2; 21137 $yadj3 = -$this->LineWidth / 2; 21138 $hadj3 = $this->LineWidth; 21139 } 21140 } elseif ($side == 'T') { 21141 $yadj3 = $this->LineWidth / 2; 21142 $xadj3 = $this->LineWidth / 2; 21143 $wadj3 = -$this->LineWidth * 2; 21144 } elseif ($side == 'L') { 21145 $xadj3 = $this->LineWidth / 2; 21146 $yadj3 = $this->LineWidth / 2; 21147 $hadj3 = -$this->LineWidth * 2; 21148 } elseif ($side == 'B' && $bSeparate) { 21149 $yadj3 = $this->LineWidth / 2; 21150 $wadj3 = $this->LineWidth / 2; 21151 } elseif ($side == 'R' && $bSeparate) { 21152 $xadj3 = $this->LineWidth / 2; 21153 $hadj3 = $this->LineWidth / 2; 21154 } elseif ($side == 'B') { 21155 $yadj3 = $this->LineWidth / 2; 21156 $xadj3 = $this->LineWidth / 2; 21157 } elseif ($side == 'R') { 21158 $xadj3 = $this->LineWidth / 2; 21159 $yadj3 = $this->LineWidth / 2; 21160 } 21161 } else { 21162 $nc = $this->colorConverter->lighten($details[$side]['c']); 21163 21164 if ($bSeparate && $cort == 'table') { 21165 if ($side == 'T') { 21166 $yadj3 = $this->LineWidth / 2; 21167 $xadj3 = -$this->LineWidth / 2; 21168 $wadj3 = $this->LineWidth; 21169 if ($this->issetBorder($bord, Border::LEFT)) { 21170 $xadj3 += $this->LineWidth; 21171 $wadj3 -= $this->LineWidth; 21172 } 21173 } 21174 if ($side == 'L') { 21175 $xadj3 = $this->LineWidth / 2; 21176 $yadj3 = -$this->LineWidth / 2; 21177 $hadj3 = $this->LineWidth; 21178 if ($this->issetBorder($bord, Border::TOP)) { 21179 $yadj3 += $this->LineWidth; 21180 $hadj3 -= $this->LineWidth; 21181 } 21182 } 21183 if ($side == 'B') { 21184 $yadj3 = $this->LineWidth / 2; 21185 $xadj3 = -$this->LineWidth / 2; 21186 $wadj3 = $this->LineWidth; 21187 if ($this->issetBorder($bord, Border::LEFT)) { 21188 $xadj3 += $this->LineWidth; 21189 $wadj3 -= $this->LineWidth; 21190 } 21191 } 21192 if ($side == 'R') { 21193 $xadj3 = $this->LineWidth / 2; 21194 $yadj3 = -$this->LineWidth / 2; 21195 $hadj3 = $this->LineWidth; 21196 if ($this->issetBorder($bord, Border::TOP)) { 21197 $yadj3 += $this->LineWidth; 21198 $hadj3 -= $this->LineWidth; 21199 } 21200 } 21201 } elseif ($side == 'T') { 21202 $yadj3 = $this->LineWidth / 2; 21203 $xadj3 = $this->LineWidth / 2; 21204 } elseif ($side == 'L') { 21205 $xadj3 = $this->LineWidth / 2; 21206 $yadj3 = $this->LineWidth / 2; 21207 } elseif ($side == 'B' && $bSeparate) { 21208 $yadj3 = $this->LineWidth / 2; 21209 $xadj3 = $this->LineWidth / 2; 21210 } elseif ($side == 'R' && $bSeparate) { 21211 $xadj3 = $this->LineWidth / 2; 21212 $yadj3 = $this->LineWidth / 2; 21213 } elseif ($side == 'B') { 21214 $yadj3 = $this->LineWidth / 2; 21215 $xadj3 = -$this->LineWidth / 2; 21216 $wadj3 = $this->LineWidth; 21217 } elseif ($side == 'R') { 21218 $xadj3 = $this->LineWidth / 2; 21219 $yadj3 = -$this->LineWidth / 2; 21220 $hadj3 = $this->LineWidth; 21221 } 21222 } 21223 21224 if ($bSeparate) { 21225 $cellBorderOverlay[] = [ 21226 'x' => $lx1 + $xadj + $xadj3, 21227 'y' => $ly1 + $yadj + $yadj3, 21228 'x2' => $lx2 - $xadj2 + $xadj3 + $wadj3, 21229 'y2' => $ly2 - $yadj2 + $yadj3 + $hadj3, 21230 'col' => $nc, 21231 'lw' => $this->LineWidth, 21232 ]; 21233 } else { 21234 $this->SetDColor($nc); 21235 $this->Line($lx1 + $xadj + $xadj3, $ly1 + $yadj + $yadj3, $lx2 - $xadj2 + $xadj3 + $wadj3, $ly2 - $yadj2 + $yadj3 + $hadj3); 21236 } 21237 } 21238 } else { 21239 /* -- END TABLES-ADVANCED-BORDERS -- */ 21240 if ($details[$side]['style'] == 'dashed') { 21241 $dashsize = 2; // final dash will be this + 1*linewidth 21242 $dashsizek = 1.5; // ratio of Dash/Blank 21243 $this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2)); 21244 } elseif ($details[$side]['style'] == 'dotted') { 21245 $this->SetLineJoin(1); 21246 $this->SetLineCap(1); 21247 $this->SetDash(0.001, ($this->LineWidth * 2)); 21248 } 21249 if ($details[$side]['c']) { 21250 $this->SetDColor($details[$side]['c']); 21251 } else { 21252 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 21253 } 21254 $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2); 21255 /* -- TABLES-ADVANCED-BORDERS -- */ 21256 } 21257 /* -- END TABLES-ADVANCED-BORDERS -- */ 21258 21259 // Reset Corners 21260 $this->SetDash(); 21261 // BUTT style line cap 21262 $this->SetLineCap(2); 21263 } 21264 } 21265 21266 if ($bSeparate && count($cellBorderOverlay)) { 21267 foreach ($cellBorderOverlay as $cbo) { 21268 $this->SetLineWidth($cbo['lw']); 21269 $this->SetDColor($cbo['col']); 21270 $this->Line($cbo['x'], $cbo['y'], $cbo['x2'], $cbo['y2']); 21271 } 21272 } 21273 21274 // $this->SetLineWidth($oldlinewidth); 21275 // $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 21276 } 21277 } 21278 21279 /* -- TABLES -- */ 21280 /* -- TABLES-ADVANCED-BORDERS -- */ 21281 21282 /* -- END TABLES-ADVANCED-BORDERS -- */ 21283 21284 function setBorder(&$var, $flag, $set = true) 21285 { 21286 $flag = intval($flag); 21287 if ($set) { 21288 $set = true; 21289 } 21290 $var = intval($var); 21291 $var = $set ? ($var | $flag) : ($var & ~$flag); 21292 } 21293 21294 function issetBorder($var, $flag) 21295 { 21296 $flag = intval($flag); 21297 $var = intval($var); 21298 return (($var & $flag) == $flag); 21299 } 21300 21301 function _table2cellBorder(&$tableb, &$cbdb, &$cellb, $bval) 21302 { 21303 if ($tableb && $tableb['w'] > $cbdb['w']) { 21304 $cbdb = $tableb; 21305 $this->setBorder($cellb, $bval); 21306 } elseif ($tableb && $tableb['w'] == $cbdb['w'] && array_search($tableb['style'], $this->borderstyles) > array_search($cbdb['style'], $this->borderstyles)) { 21307 $cbdb = $tableb; 21308 $this->setBorder($cellb, $bval); 21309 } 21310 } 21311 21312 // FIX BORDERS ******************************************** 21313 function _fixTableBorders(&$table) 21314 { 21315 if (!$table['borders_separate'] && $table['border_details']['L']['w']) { 21316 $table['max_cell_border_width']['L'] = $table['border_details']['L']['w']; 21317 } 21318 if (!$table['borders_separate'] && $table['border_details']['R']['w']) { 21319 $table['max_cell_border_width']['R'] = $table['border_details']['R']['w']; 21320 } 21321 if (!$table['borders_separate'] && $table['border_details']['T']['w']) { 21322 $table['max_cell_border_width']['T'] = $table['border_details']['T']['w']; 21323 } 21324 if (!$table['borders_separate'] && $table['border_details']['B']['w']) { 21325 $table['max_cell_border_width']['B'] = $table['border_details']['B']['w']; 21326 } 21327 if ($this->simpleTables) { 21328 return; 21329 } 21330 $cells = &$table['cells']; 21331 $numcols = $table['nc']; 21332 $numrows = $table['nr']; 21333 /* -- TABLES-ADVANCED-BORDERS -- */ 21334 if (isset($table['topntail']) && $table['topntail']) { 21335 $tntborddet = $this->border_details($table['topntail']); 21336 } 21337 if (isset($table['thead-underline']) && $table['thead-underline']) { 21338 $thuborddet = $this->border_details($table['thead-underline']); 21339 } 21340 /* -- END TABLES-ADVANCED-BORDERS -- */ 21341 21342 for ($i = 0; $i < $numrows; $i++) { // Rows 21343 for ($j = 0; $j < $numcols; $j++) { // Columns 21344 if (isset($cells[$i][$j]) && $cells[$i][$j]) { 21345 $cell = &$cells[$i][$j]; 21346 if ($this->packTableData) { 21347 $cbord = $this->_unpackCellBorder($cell['borderbin']); 21348 } else { 21349 $cbord = &$cells[$i][$j]; 21350 } 21351 21352 // mPDF 5.7.3 21353 if (!$cbord['border'] && $cbord['border'] !== 0 && isset($table['border']) && $table['border'] && $this->table_border_attr_set) { 21354 $cbord['border'] = $table['border']; 21355 $cbord['border_details'] = $table['border_details']; 21356 } 21357 21358 if (isset($cell['colspan']) && $cell['colspan'] > 1) { 21359 $ccolsp = $cell['colspan']; 21360 } else { 21361 $ccolsp = 1; 21362 } 21363 if (isset($cell['rowspan']) && $cell['rowspan'] > 1) { 21364 $crowsp = $cell['rowspan']; 21365 } else { 21366 $crowsp = 1; 21367 } 21368 21369 $cbord['border_details']['cellposdom'] = ((($i + 1) / $numrows) / 10000 ) + ((($j + 1) / $numcols) / 10 ); 21370 // Inherit Cell border from Table border 21371 if ($this->table_border_css_set && !$table['borders_separate']) { 21372 if ($i == 0) { 21373 $this->_table2cellBorder($table['border_details']['T'], $cbord['border_details']['T'], $cbord['border'], Border::TOP); 21374 } 21375 if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) { 21376 $this->_table2cellBorder($table['border_details']['B'], $cbord['border_details']['B'], $cbord['border'], Border::BOTTOM); 21377 } 21378 if ($j == 0) { 21379 $this->_table2cellBorder($table['border_details']['L'], $cbord['border_details']['L'], $cbord['border'], Border::LEFT); 21380 } 21381 if ($j == ($numcols - 1) || ($j + $ccolsp) == ($numcols)) { 21382 $this->_table2cellBorder($table['border_details']['R'], $cbord['border_details']['R'], $cbord['border'], Border::RIGHT); 21383 } 21384 } 21385 21386 /* -- TABLES-ADVANCED-BORDERS -- */ 21387 $fixbottom = true; 21388 if (isset($table['topntail']) && $table['topntail']) { 21389 if ($i == 0) { 21390 $cbord['border_details']['T'] = $tntborddet; 21391 $this->setBorder($cbord['border'], Border::TOP); 21392 } 21393 if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) { 21394 $cbord['border_details']['B'] = $tntborddet; 21395 $this->setBorder($cbord['border'], Border::BOTTOM); 21396 $fixbottom = false; 21397 } elseif ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows']) { 21398 if (!$table['borders_separate']) { 21399 $cbord['border_details']['T'] = $tntborddet; 21400 $this->setBorder($cbord['border'], Border::TOP); 21401 } 21402 } 21403 if ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'] - 1)) { 21404 if (!$table['borders_separate']) { 21405 $cbord['border_details']['B'] = $tntborddet; 21406 $this->setBorder($cbord['border'], Border::BOTTOM); 21407 $fixbottom = false; 21408 } 21409 } elseif ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'])) { 21410 $cbord['border_details']['T'] = $tntborddet; 21411 $this->setBorder($cbord['border'], Border::TOP); 21412 } 21413 if ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader 21414 if (!$table['borders_separate']) { 21415 $cbord['border_details']['T'] = $tntborddet; 21416 $this->setBorder($cbord['border'], Border::TOP); 21417 } 21418 } 21419 if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) { 21420 $cbord['border_details']['B'] = $tntborddet; 21421 $this->setBorder($cbord['border'], Border::BOTTOM); 21422 } 21423 } 21424 if (isset($table['thead-underline']) && $table['thead-underline']) { 21425 if ($table['borders_separate']) { 21426 if ($i == 0) { 21427 $cbord['border_details']['B'] = $thuborddet; 21428 $this->setBorder($cbord['border'], Border::BOTTOM); 21429 $fixbottom = false; 21430 } 21431 } else { 21432 if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) { 21433 $cbord['border_details']['T'] = $thuborddet; 21434 $this->setBorder($cbord['border'], Border::TOP); 21435 } elseif ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader 21436 $cbord['border_details']['T'] = $thuborddet; 21437 $this->setBorder($cbord['border'], Border::TOP); 21438 } 21439 } 21440 } 21441 21442 // Collapse Border - Algorithm for conflicting borders 21443 // Hidden >> Width >> double>solid>dashed>dotted... >> style set on cell>table >> top/left>bottom/right 21444 // Do not turn off border which is overridden 21445 // Needed for page break for TOP/BOTTOM both to be defined in Collapsed borders 21446 // Means it is painted twice. (Left/Right can still disable overridden border) 21447 if (!$table['borders_separate']) { 21448 21449 if (($i < ($numrows - 1) || ($i + $crowsp) < $numrows ) && $fixbottom) { // Bottom 21450 21451 for ($cspi = 0; $cspi < $ccolsp; $cspi++) { 21452 21453 // already defined Top for adjacent cell below 21454 if (isset($cells[($i + $crowsp)][$j + $cspi])) { 21455 if ($this->packTableData) { 21456 $adjc = $cells[($i + $crowsp)][$j + $cspi]; 21457 $celladj = $this->_unpackCellBorder($adjc['borderbin']); 21458 } else { 21459 $celladj = & $cells[($i + $crowsp)][$j + $cspi]; 21460 } 21461 } else { 21462 $celladj = false; 21463 } 21464 21465 if (isset($celladj['border_details']['T']['s']) && $celladj['border_details']['T']['s'] == 1) { 21466 21467 $csadj = $celladj['border_details']['T']['w']; 21468 $csthis = $cbord['border_details']['B']['w']; 21469 21470 // Hidden 21471 if ($cbord['border_details']['B']['style'] == 'hidden') { 21472 21473 $celladj['border_details']['T'] = $cbord['border_details']['B']; 21474 $this->setBorder($celladj['border'], Border::TOP, false); 21475 $this->setBorder($cbord['border'], Border::BOTTOM, false); 21476 21477 } elseif ($celladj['border_details']['T']['style'] == 'hidden') { 21478 21479 $cbord['border_details']['B'] = $celladj['border_details']['T']; 21480 $this->setBorder($cbord['border'], Border::BOTTOM, false); 21481 $this->setBorder($celladj['border'], Border::TOP, false); 21482 21483 } elseif ($csthis > $csadj) { // Width 21484 21485 if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span 21486 $celladj['border_details']['T'] = $cbord['border_details']['B']; 21487 $this->setBorder($cbord['border'], Border::BOTTOM); 21488 } 21489 21490 } elseif ($csadj > $csthis) { 21491 21492 if ($ccolsp < 2) { // don't overwrite this cell if it spans 21493 $cbord['border_details']['B'] = $celladj['border_details']['T']; 21494 $this->setBorder($celladj['border'], Border::TOP); 21495 } 21496 21497 } elseif (array_search($cbord['border_details']['B']['style'], $this->borderstyles) > array_search($celladj['border_details']['T']['style'], $this->borderstyles)) { // double>solid>dashed>dotted... 21498 21499 if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span 21500 $celladj['border_details']['T'] = $cbord['border_details']['B']; 21501 $this->setBorder($cbord['border'], Border::BOTTOM); 21502 } 21503 21504 } elseif (array_search($celladj['border_details']['T']['style'], $this->borderstyles) > array_search($cbord['border_details']['B']['style'], $this->borderstyles)) { 21505 21506 if ($ccolsp < 2) { // don't overwrite this cell if it spans 21507 $cbord['border_details']['B'] = $celladj['border_details']['T']; 21508 $this->setBorder($celladj['border'], Border::TOP); 21509 } 21510 21511 } elseif ($celladj['border_details']['T']['dom'] > $celladj['border_details']['B']['dom']) { // Style set on cell vs. table 21512 21513 if ($ccolsp < 2) { // don't overwrite this cell if it spans 21514 $cbord['border_details']['B'] = $celladj['border_details']['T']; 21515 $this->setBorder($celladj['border'], Border::TOP); 21516 } 21517 21518 } else { // Style set on cell vs. table - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT 21519 21520 if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span 21521 $celladj['border_details']['T'] = $cbord['border_details']['B']; 21522 $this->setBorder($cbord['border'], Border::BOTTOM); 21523 } 21524 21525 } 21526 21527 } elseif ($celladj) { 21528 21529 if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span 21530 $celladj['border_details']['T'] = $cbord['border_details']['B']; 21531 } 21532 21533 } 21534 21535 // mPDF 5.7.4 21536 if ($celladj && $this->packTableData) { 21537 $cells[$i + $crowsp][$j + $cspi]['borderbin'] = $this->_packCellBorder($celladj); 21538 } 21539 21540 unset($celladj); 21541 } 21542 } 21543 21544 if ($j < ($numcols - 1) || ($j + $ccolsp) < $numcols) { // Right-Left 21545 21546 for ($cspi = 0; $cspi < $crowsp; $cspi++) { 21547 21548 // already defined Left for adjacent cell to R 21549 if (isset($cells[($i + $cspi)][$j + $ccolsp])) { 21550 if ($this->packTableData) { 21551 $adjc = $cells[($i + $cspi)][$j + $ccolsp]; 21552 $celladj = $this->_unpackCellBorder($adjc['borderbin']); 21553 } else { 21554 $celladj = & $cells[$i + $cspi][$j + $ccolsp]; 21555 } 21556 } else { 21557 $celladj = false; 21558 } 21559 if ($celladj && $celladj['border_details']['L']['s'] == 1) { 21560 $csadj = $celladj['border_details']['L']['w']; 21561 $csthis = $cbord['border_details']['R']['w']; 21562 // Hidden 21563 if ($cbord['border_details']['R']['style'] == 'hidden') { 21564 $celladj['border_details']['L'] = $cbord['border_details']['R']; 21565 $this->setBorder($celladj['border'], Border::LEFT, false); 21566 $this->setBorder($cbord['border'], Border::RIGHT, false); 21567 } elseif ($celladj['border_details']['L']['style'] == 'hidden') { 21568 $cbord['border_details']['R'] = $celladj['border_details']['L']; 21569 $this->setBorder($cbord['border'], Border::RIGHT, false); 21570 $this->setBorder($celladj['border'], Border::LEFT, false); 21571 } // Width 21572 elseif ($csthis > $csadj) { 21573 if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span 21574 $celladj['border_details']['L'] = $cbord['border_details']['R']; 21575 $this->setBorder($cbord['border'], Border::RIGHT); 21576 $this->setBorder($celladj['border'], Border::LEFT, false); 21577 } 21578 } elseif ($csadj > $csthis) { 21579 if ($crowsp < 2) { // don't overwrite this cell if it spans 21580 $cbord['border_details']['R'] = $celladj['border_details']['L']; 21581 $this->setBorder($cbord['border'], Border::RIGHT, false); 21582 $this->setBorder($celladj['border'], Border::LEFT); 21583 } 21584 } // double>solid>dashed>dotted... 21585 elseif (array_search($cbord['border_details']['R']['style'], $this->borderstyles) > array_search($celladj['border_details']['L']['style'], $this->borderstyles)) { 21586 if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span 21587 $celladj['border_details']['L'] = $cbord['border_details']['R']; 21588 $this->setBorder($celladj['border'], Border::LEFT, false); 21589 $this->setBorder($cbord['border'], Border::RIGHT); 21590 } 21591 } elseif (array_search($celladj['border_details']['L']['style'], $this->borderstyles) > array_search($cbord['border_details']['R']['style'], $this->borderstyles)) { 21592 if ($crowsp < 2) { // don't overwrite this cell if it spans 21593 $cbord['border_details']['R'] = $celladj['border_details']['L']; 21594 $this->setBorder($cbord['border'], Border::RIGHT, false); 21595 $this->setBorder($celladj['border'], Border::LEFT); 21596 } 21597 } // Style set on cell vs. table 21598 elseif ($celladj['border_details']['L']['dom'] > $cbord['border_details']['R']['dom']) { 21599 if ($crowsp < 2) { // don't overwrite this cell if it spans 21600 $cbord['border_details']['R'] = $celladj['border_details']['L']; 21601 $this->setBorder($celladj['border'], Border::LEFT); 21602 } 21603 } // Style set on cell vs. table - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT 21604 else { 21605 if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span 21606 $celladj['border_details']['L'] = $cbord['border_details']['R']; 21607 $this->setBorder($cbord['border'], Border::RIGHT); 21608 } 21609 } 21610 } elseif ($celladj) { 21611 // if right-cell border is not set 21612 if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span 21613 $celladj['border_details']['L'] = $cbord['border_details']['R']; 21614 } 21615 } 21616 // mPDF 5.7.4 21617 if ($celladj && $this->packTableData) { 21618 $cells[$i + $cspi][$j + $ccolsp]['borderbin'] = $this->_packCellBorder($celladj); 21619 } 21620 unset($celladj); 21621 } 21622 } 21623 } 21624 21625 21626 // Set maximum cell border width meeting at LRTB edges of cell - used for extended cell border 21627 // ['border_details']['mbw']['LT'] = meeting border width - Left border - Top end 21628 if (!$table['borders_separate']) { 21629 21630 $cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $cbord['border_details']['L']['w']); 21631 $cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $cbord['border_details']['R']['w']); 21632 $cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $cbord['border_details']['T']['w']); 21633 $cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $cbord['border_details']['B']['w']); 21634 $cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $cbord['border_details']['L']['w']); 21635 $cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $cbord['border_details']['R']['w']); 21636 $cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $cbord['border_details']['T']['w']); 21637 $cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $cbord['border_details']['B']['w']); 21638 21639 if (($i + $crowsp) < $numrows && isset($cells[$i + $crowsp][$j])) { // Has Bottom adjoining cell 21640 21641 if ($this->packTableData) { 21642 $adjc = $cells[$i + $crowsp][$j]; 21643 $celladj = $this->_unpackCellBorder($adjc['borderbin']); 21644 } else { 21645 $celladj = & $cells[$i + $crowsp][$j]; 21646 } 21647 21648 $cbord['border_details']['mbw']['BL'] = max( 21649 $cbord['border_details']['mbw']['BL'], 21650 $celladj ? $celladj['border_details']['L']['w'] : 0, 21651 $celladj ? $celladj['border_details']['mbw']['TL']: 0 21652 ); 21653 21654 $cbord['border_details']['mbw']['BR'] = max( 21655 $cbord['border_details']['mbw']['BR'], 21656 $celladj ? $celladj['border_details']['R']['w'] : 0, 21657 $celladj ? $celladj['border_details']['mbw']['TR']: 0 21658 ); 21659 21660 $cbord['border_details']['mbw']['LB'] = max( 21661 $cbord['border_details']['mbw']['LB'], 21662 $celladj ? $celladj['border_details']['mbw']['LT'] : 0 21663 ); 21664 21665 $cbord['border_details']['mbw']['RB'] = max( 21666 $cbord['border_details']['mbw']['RB'], 21667 $celladj ? $celladj['border_details']['mbw']['RT'] : 0 21668 ); 21669 21670 unset($celladj); 21671 } 21672 21673 if (($j + $ccolsp) < $numcols && isset($cells[$i][$j + $ccolsp])) { // Has Right adjoining cell 21674 21675 if ($this->packTableData) { 21676 $adjc = $cells[$i][$j + $ccolsp]; 21677 $celladj = $this->_unpackCellBorder($adjc['borderbin']); 21678 } else { 21679 $celladj = & $cells[$i][$j + $ccolsp]; 21680 } 21681 21682 $cbord['border_details']['mbw']['RT'] = max( 21683 $cbord['border_details']['mbw']['RT'], 21684 $celladj ? $celladj['border_details']['T']['w'] : 0, 21685 $celladj ? $celladj['border_details']['mbw']['LT'] : 0 21686 ); 21687 21688 $cbord['border_details']['mbw']['RB'] = max( 21689 $cbord['border_details']['mbw']['RB'], 21690 $celladj ? $celladj['border_details']['B']['w'] : 0, 21691 $celladj ? $celladj['border_details']['mbw']['LB'] : 0 21692 ); 21693 21694 $cbord['border_details']['mbw']['TR'] = max( 21695 $cbord['border_details']['mbw']['TR'], 21696 $celladj ? $celladj['border_details']['mbw']['TL'] : 0 21697 ); 21698 21699 $cbord['border_details']['mbw']['BR'] = max( 21700 $cbord['border_details']['mbw']['BR'], 21701 $celladj ? $celladj['border_details']['mbw']['BL'] : 0 21702 ); 21703 21704 unset($celladj); 21705 } 21706 21707 if ($i > 0 && isset($cells[$i - 1][$j]) && is_array($cells[$i - 1][$j]) && (($this->packTableData && $cells[$i - 1][$j]['borderbin']) || $cells[$i - 1][$j]['border'])) { // Has Top adjoining cell 21708 21709 if ($this->packTableData) { 21710 $adjc = $cells[$i - 1][$j]; 21711 $celladj = $this->_unpackCellBorder($adjc['borderbin']); 21712 } else { 21713 $celladj = & $cells[$i - 1][$j]; 21714 } 21715 21716 $cbord['border_details']['mbw']['TL'] = max( 21717 $cbord['border_details']['mbw']['TL'], 21718 $celladj ? $celladj['border_details']['L']['w'] : 0, 21719 $celladj ? $celladj['border_details']['mbw']['BL'] : 0 21720 ); 21721 21722 $cbord['border_details']['mbw']['TR'] = max( 21723 $cbord['border_details']['mbw']['TR'], 21724 $celladj ? $celladj['border_details']['R']['w'] : 0, 21725 $celladj ? $celladj['border_details']['mbw']['BR'] : 0 21726 ); 21727 21728 $cbord['border_details']['mbw']['LT'] = max( 21729 $cbord['border_details']['mbw']['LT'], 21730 $celladj ? $celladj['border_details']['mbw']['LB'] : 0 21731 ); 21732 21733 $cbord['border_details']['mbw']['RT'] = max( 21734 $cbord['border_details']['mbw']['RT'], 21735 $celladj ? $celladj['border_details']['mbw']['RB'] : 0 21736 ); 21737 21738 if ($celladj['border_details']['mbw']['BL']) { 21739 $celladj['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['mbw']['BL']); 21740 } 21741 21742 if ($celladj['border_details']['mbw']['BR']) { 21743 $celladj['border_details']['mbw']['BR'] = max($celladj['border_details']['mbw']['BR'], $cbord['border_details']['mbw']['TR']); 21744 } 21745 21746 if ($this->packTableData) { 21747 $cells[$i - 1][$j]['borderbin'] = $this->_packCellBorder($celladj); 21748 } 21749 unset($celladj); 21750 } 21751 21752 if ($j > 0 && isset($cells[$i][$j - 1]) && is_array($cells[$i][$j - 1]) && (($this->packTableData && $cells[$i][$j - 1]['borderbin']) || $cells[$i][$j - 1]['border'])) { // Has Left adjoining cell 21753 21754 if ($this->packTableData) { 21755 $adjc = $cells[$i][$j - 1]; 21756 $celladj = $this->_unpackCellBorder($adjc['borderbin']); 21757 } else { 21758 $celladj = & $cells[$i][$j - 1]; 21759 } 21760 21761 $cbord['border_details']['mbw']['LT'] = max( 21762 $cbord['border_details']['mbw']['LT'], 21763 $celladj ? $celladj['border_details']['T']['w'] : 0, 21764 $celladj ? $celladj['border_details']['mbw']['RT'] : 0 21765 ); 21766 21767 $cbord['border_details']['mbw']['LB'] = max( 21768 $cbord['border_details']['mbw']['LB'], 21769 $celladj ? $celladj['border_details']['B']['w'] : 0, 21770 $celladj ? $celladj['border_details']['mbw']['RB'] : 0 21771 ); 21772 21773 $cbord['border_details']['mbw']['BL'] = max( 21774 $cbord['border_details']['mbw']['BL'], 21775 $celladj ? $celladj['border_details']['mbw']['BR'] : 0 21776 ); 21777 21778 $cbord['border_details']['mbw']['TL'] = max( 21779 $cbord['border_details']['mbw']['TL'], 21780 $celladj ? $celladj['border_details']['mbw']['TR'] : 0 21781 ); 21782 21783 if ($celladj['border_details']['mbw']['RT']) { 21784 $celladj['border_details']['mbw']['RT'] = max($celladj['border_details']['mbw']['RT'], $cbord['border_details']['mbw']['LT']); 21785 } 21786 21787 if ($celladj['border_details']['mbw']['RB']) { 21788 $celladj['border_details']['mbw']['RB'] = max($celladj['border_details']['mbw']['RB'], $cbord['border_details']['mbw']['LB']); 21789 } 21790 21791 if ($this->packTableData) { 21792 $cells[$i][$j - 1]['borderbin'] = $this->_packCellBorder($celladj); 21793 } 21794 21795 unset($celladj); 21796 } 21797 21798 21799 // Update maximum cell border width at LRTB edges of table - used for overall table width 21800 if ($j == 0 && $cbord['border_details']['L']['w']) { 21801 $table['max_cell_border_width']['L'] = max($table['max_cell_border_width']['L'], $cbord['border_details']['L']['w']); 21802 } 21803 if (($j == ($numcols - 1) || ($j + $ccolsp) == $numcols ) && $cbord['border_details']['R']['w']) { 21804 $table['max_cell_border_width']['R'] = max($table['max_cell_border_width']['R'], $cbord['border_details']['R']['w']); 21805 } 21806 if ($i == 0 && $cbord['border_details']['T']['w']) { 21807 $table['max_cell_border_width']['T'] = max($table['max_cell_border_width']['T'], $cbord['border_details']['T']['w']); 21808 } 21809 if (($i == ($numrows - 1) || ($i + $crowsp) == $numrows ) && $cbord['border_details']['B']['w']) { 21810 $table['max_cell_border_width']['B'] = max($table['max_cell_border_width']['B'], $cbord['border_details']['B']['w']); 21811 } 21812 } 21813 /* -- END TABLES-ADVANCED-BORDERS -- */ 21814 21815 if ($this->packTableData) { 21816 $cell['borderbin'] = $this->_packCellBorder($cbord); 21817 } 21818 21819 unset($cbord); 21820 21821 unset($cell); 21822 } 21823 } 21824 } 21825 unset($cell); 21826 } 21827 21828 // END FIX BORDERS ************************************************************************************ 21829 21830 function _reverseTableDir(&$table) 21831 { 21832 $cells = &$table['cells']; 21833 $numcols = $table['nc']; 21834 $numrows = $table['nr']; 21835 for ($i = 0; $i < $numrows; $i++) { // Rows 21836 $row = []; 21837 for ($j = ($numcols - 1); $j >= 0; $j--) { // Columns 21838 if (isset($cells[$i][$j]) && $cells[$i][$j]) { 21839 $cell = &$cells[$i][$j]; 21840 $col = $numcols - $j - 1; 21841 if (isset($cell['colspan']) && $cell['colspan'] > 1) { 21842 $col -= ($cell['colspan'] - 1); 21843 } 21844 // Nested content 21845 if (isset($cell['textbuffer'])) { 21846 for ($n = 0; $n < count($cell['textbuffer']); $n++) { 21847 $t = $cell['textbuffer'][$n][0]; 21848 if (substr($t, 0, 19) == "\xbb\xa4\xactype=nestedtable") { 21849 $objattr = $this->_getObjAttr($t); 21850 $objattr['col'] = $col; 21851 $cell['textbuffer'][$n][0] = "\xbb\xa4\xactype=nestedtable,objattr=" . serialize($objattr) . "\xbb\xa4\xac"; 21852 $this->table[($this->tableLevel + 1)][$objattr['nestedcontent']]['nestedpos'][1] = $col; 21853 } 21854 } 21855 } 21856 $row[$col] = $cells[$i][$j]; 21857 unset($cell); 21858 } 21859 } 21860 for ($f = 0; $f < $numcols; $f++) { 21861 if (!isset($row[$f])) { 21862 $row[$f] = 0; 21863 } 21864 } 21865 $table['cells'][$i] = $row; 21866 } 21867 } 21868 21869 function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $splitpg = 0, $rety = 0) 21870 { 21871 $level = $table['level']; 21872 $levelid = $table['levelid']; 21873 21874 $cells = &$table['cells']; 21875 $numcols = $table['nc']; 21876 $numrows = $table['nr']; 21877 $maxbwtop = 0; 21878 if ($this->ColActive && $level == 1) { 21879 $this->breakpoints[$this->CurrCol][] = $this->y; 21880 } // *COLUMNS* 21881 21882 if (!$split || ($startrow == 0 && $splitpg == 0) || $startrow > 0) { 21883 // TABLE TOP MARGIN 21884 if ($table['margin']['T']) { 21885 if (!$this->table_rotate && $level == 1) { 21886 $this->DivLn($table['margin']['T'], $this->blklvl, true, 1); // collapsible 21887 } else { 21888 $this->y += ($table['margin']['T']); 21889 } 21890 } 21891 // Advance down page by half width of top border 21892 if ($table['borders_separate']) { 21893 if ($startrow > 0 && (!isset($table['is_thead']) || count($table['is_thead']) == 0)) { 21894 $adv = $table['border_spacing_V'] / 2; 21895 } else { 21896 $adv = $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2; 21897 } 21898 } else { 21899 $adv = $table['max_cell_border_width']['T'] / 2; 21900 } 21901 if (!$this->table_rotate && $level == 1) { 21902 $this->DivLn($adv); 21903 } else { 21904 $this->y += $adv; 21905 } 21906 } 21907 21908 if ($level == 1) { 21909 $this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w']; 21910 $x0 = $this->x; 21911 $y0 = $this->y; 21912 $right = $x0 + $this->blk[$this->blklvl]['inner_width']; 21913 $outerfilled = $this->y; // Keep track of how far down the outer DIV bgcolor is painted (NB rowspans) 21914 $this->outerfilled = $this->y; 21915 $this->colsums = []; 21916 } else { 21917 $x0 = $this->x; 21918 $y0 = $this->y; 21919 $right = $x0 + $table['w']; 21920 } 21921 21922 if ($this->table_rotate) { 21923 $temppgwidth = $this->tbrot_maxw; 21924 $this->PageBreakTrigger = $pagetrigger = $y0 + ($this->blk[$this->blklvl]['inner_width']); 21925 if ($level == 1) { 21926 $this->tbrot_y0 = $this->y - $adv - $table['margin']['T']; 21927 $this->tbrot_x0 = $this->x; 21928 $this->tbrot_w = $table['w']; 21929 if ($table['borders_separate']) { 21930 $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2; 21931 } else { 21932 $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['max_cell_border_width']['T']; 21933 } 21934 } 21935 } else { 21936 $this->PageBreakTrigger = $pagetrigger = ($this->h - $this->bMargin); 21937 if ($level == 1) { 21938 $temppgwidth = $this->blk[$this->blklvl]['inner_width']; 21939 if (isset($table['a']) and ( $table['w'] < $this->blk[$this->blklvl]['inner_width'])) { 21940 if ($table['a'] == 'C') { 21941 $x0 += ((($right - $x0) - $table['w']) / 2); 21942 } elseif ($table['a'] == 'R') { 21943 $x0 = $right - $table['w']; 21944 } 21945 } 21946 } else { 21947 $temppgwidth = $table['w']; 21948 } 21949 } 21950 if (!isset($table['overflow'])) { 21951 $table['overflow'] = null; 21952 } 21953 if ($table['overflow'] == 'hidden' && $level == 1 && !$this->table_rotate && !$this->ColActive) { 21954 // Bounding rectangle to clip 21955 $this->tableClipPath = sprintf('q %.3F %.3F %.3F %.3F re W n', $x0 * Mpdf::SCALE, $this->h * Mpdf::SCALE, $this->blk[$this->blklvl]['inner_width'] * Mpdf::SCALE, -$this->h * Mpdf::SCALE); 21956 $this->writer->write($this->tableClipPath); 21957 } else { 21958 $this->tableClipPath = ''; 21959 } 21960 21961 21962 if ($table['borders_separate']) { 21963 $indent = $table['margin']['L'] + $table['border_details']['L']['w'] + $table['padding']['L'] + $table['border_spacing_H'] / 2; 21964 } else { 21965 $indent = $table['margin']['L'] + $table['max_cell_border_width']['L'] / 2; 21966 } 21967 $x0 += $indent; 21968 21969 $returny = 0; 21970 $lastCol = 0; 21971 $tableheader = []; 21972 $tablefooter = []; 21973 $tableheaderrowheight = 0; 21974 $tablefooterrowheight = 0; 21975 $footery = 0; 21976 21977 // mPD 3.0 Set the Page & Column where table starts 21978 if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN 21979 $tablestartpage = 'EVEN'; 21980 } elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD 21981 $tablestartpage = 'ODD'; 21982 } else { 21983 $tablestartpage = ''; 21984 } 21985 if ($this->ColActive) { 21986 $tablestartcolumn = $this->CurrCol; 21987 } else { 21988 $tablestartcolumn = ''; 21989 } 21990 21991 $y = $h = 0; 21992 for ($i = 0; $i < $numrows; $i++) { // Rows 21993 if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i] && $level == 1) { 21994 $tablefooterrowheight += $table['hr'][$i]; 21995 $tablefooter[$i][0]['trbackground-images'] = $table['trbackground-images'][$i]; 21996 $tablefooter[$i][0]['trgradients'] = $table['trgradients'][$i]; 21997 $tablefooter[$i][0]['trbgcolor'] = $table['bgcolor'][$i]; 21998 for ($j = $startcol; $j < $numcols; $j++) { // Columns 21999 if (isset($cells[$i][$j]) && $cells[$i][$j]) { 22000 $cell = &$cells[$i][$j]; 22001 if ($split) { 22002 if ($table['colPg'][$j] != $splitpg) { 22003 continue; 22004 } 22005 list($x, $w) = $this->_splitTableGetWidth($table, $i, $j); 22006 $js = $j - $startcol; 22007 } else { 22008 list($x, $w) = $this->_tableGetWidth($table, $i, $j); 22009 $js = $j; 22010 } 22011 22012 list($y, $h) = $this->_tableGetHeight($table, $i, $j); 22013 $x += $x0; 22014 $y += $y0; 22015 // Get info of tfoot ==>> table footer 22016 $tablefooter[$i][$js]['x'] = $x; 22017 $tablefooter[$i][$js]['y'] = $y; 22018 $tablefooter[$i][$js]['h'] = $h; 22019 $tablefooter[$i][$js]['w'] = $w; 22020 if (isset($cell['textbuffer'])) { 22021 $tablefooter[$i][$js]['textbuffer'] = $cell['textbuffer']; 22022 } else { 22023 $tablefooter[$i][$js]['textbuffer'] = ''; 22024 } 22025 $tablefooter[$i][$js]['a'] = $cell['a']; 22026 $tablefooter[$i][$js]['R'] = $cell['R']; 22027 $tablefooter[$i][$js]['va'] = $cell['va']; 22028 $tablefooter[$i][$js]['mih'] = $cell['mih']; 22029 if (isset($cell['gradient'])) { 22030 $tablefooter[$i][$js]['gradient'] = $cell['gradient']; // *BACKGROUNDS* 22031 } 22032 if (isset($cell['background-image'])) { 22033 $tablefooter[$i][$js]['background-image'] = $cell['background-image']; // *BACKGROUNDS* 22034 } 22035 22036 // CELL FILL BGCOLOR 22037 if (!$this->simpleTables) { 22038 if ($this->packTableData) { 22039 $c = $this->_unpackCellBorder($cell['borderbin']); 22040 $tablefooter[$i][$js]['border'] = $c['border']; 22041 $tablefooter[$i][$js]['border_details'] = $c['border_details']; 22042 } else { 22043 $tablefooter[$i][$js]['border'] = $cell['border']; 22044 $tablefooter[$i][$js]['border_details'] = $cell['border_details']; 22045 } 22046 } elseif ($this->simpleTables) { 22047 $tablefooter[$i][$js]['border'] = $table['simple']['border']; 22048 $tablefooter[$i][$js]['border_details'] = $table['simple']['border_details']; 22049 } 22050 $tablefooter[$i][$js]['bgcolor'] = $cell['bgcolor']; 22051 $tablefooter[$i][$js]['padding'] = $cell['padding']; 22052 if (isset($cell['rowspan'])) { 22053 $tablefooter[$i][$js]['rowspan'] = $cell['rowspan']; 22054 } 22055 if (isset($cell['colspan'])) { 22056 $tablefooter[$i][$js]['colspan'] = $cell['colspan']; 22057 } 22058 if (isset($cell['direction'])) { 22059 $tablefooter[$i][$js]['direction'] = $cell['direction']; 22060 } 22061 if (isset($cell['cellLineHeight'])) { 22062 $tablefooter[$i][$js]['cellLineHeight'] = $cell['cellLineHeight']; 22063 } 22064 if (isset($cell['cellLineStackingStrategy'])) { 22065 $tablefooter[$i][$js]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy']; 22066 } 22067 if (isset($cell['cellLineStackingShift'])) { 22068 $tablefooter[$i][$js]['cellLineStackingShift'] = $cell['cellLineStackingShift']; 22069 } 22070 } 22071 } 22072 } 22073 } 22074 22075 if ($level == 1) { 22076 $this->writer->write('___TABLE___BACKGROUNDS' . $this->uniqstr); 22077 } 22078 $tableheaderadj = 0; 22079 $tablefooteradj = 0; 22080 22081 $tablestartpageno = $this->page; 22082 22083 // Draw Table Contents and Borders 22084 for ($i = 0; $i < $numrows; $i++) { // Rows 22085 if ($split && $startrow > 0) { 22086 $thnr = (isset($table['is_thead']) ? count($table['is_thead']) : 0); 22087 if ($i >= $thnr && $i < $startrow) { 22088 continue; 22089 } 22090 if ($i == $startrow) { 22091 $returny = $rety - $tableheaderrowheight; 22092 } 22093 } 22094 22095 // Get Maximum row/cell height in row - including rowspan>1 + 1 overlapping 22096 $maxrowheight = $this->_tableGetMaxRowHeight($table, $i); 22097 22098 $skippage = false; 22099 $newpagestarted = false; 22100 for ($j = $startcol; $j < $numcols; $j++) { // Columns 22101 if ($split) { 22102 if ($table['colPg'][$j] > $splitpg) { 22103 break; 22104 } 22105 $lastCol = $j; 22106 } 22107 if (isset($cells[$i][$j]) && $cells[$i][$j]) { 22108 $cell = &$cells[$i][$j]; 22109 if ($split) { 22110 $lastCol = $j + (isset($cell['colspan']) ? ($cell['colspan'] - 1) : 0); 22111 list($x, $w) = $this->_splitTableGetWidth($table, $i, $j); 22112 } else { 22113 list($x, $w) = $this->_tableGetWidth($table, $i, $j); 22114 } 22115 22116 list($y, $h) = $this->_tableGetHeight($table, $i, $j); 22117 $x += $x0; 22118 $y += $y0; 22119 $y -= $returny; 22120 22121 if ($table['borders_separate']) { 22122 if (!empty($tablefooter) || $i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) { 22123 $extra = $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2; 22124 // $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V']/2; 22125 } else { 22126 $extra = $table['border_spacing_V'] / 2; 22127 } 22128 } else { 22129 $extra = $table['max_cell_border_width']['B'] / 2; 22130 } 22131 22132 if ($j == $startcol && ((($y + $maxrowheight + $extra ) > ($pagetrigger + 0.001)) || (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && ($y + $maxrowheight + $tablefooterrowheight + $extra) > $pagetrigger) && ($this->tableLevel == 1 && $i < ($numrows - $table['headernrows']))) && ($y0 > 0 || $x0 > 0) && !$this->InFooter && $this->autoPageBreak) { 22133 if (!$skippage) { 22134 $finalSpread = true; 22135 $firstSpread = true; 22136 if ($split) { 22137 for ($t = $startcol; $t < $numcols; $t++) { 22138 // Are there more columns to print on a next page? 22139 if ($table['colPg'][$t] > $splitpg) { 22140 $finalSpread = false; 22141 break; 22142 } 22143 } 22144 if ($startcol > 0) { 22145 $firstSpread = false; 22146 } 22147 } 22148 22149 if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) { 22150 $this->y = $y; 22151 $ya = $this->y; 22152 $this->TableHeaderFooter($tablefooter, $tablestartpage, $tablestartcolumn, 'F', $level, $firstSpread, $finalSpread); 22153 if ($this->table_rotate) { 22154 $this->tbrot_h += $this->y - $ya; 22155 } 22156 $tablefooteradj = $this->y - $ya; 22157 } 22158 $y -= $y0; 22159 $returny += $y; 22160 22161 $oldcolumn = $this->CurrCol; 22162 if ($this->AcceptPageBreak()) { 22163 $newpagestarted = true; 22164 $this->y = $y + $y0; 22165 22166 // Move down to account for border-spacing or 22167 // extra half border width in case page breaks in middle 22168 if ($i > 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) { 22169 if ($table['borders_separate']) { 22170 $adv = $table['border_spacing_V'] / 2; 22171 // If table footer 22172 if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) { 22173 $adv += ($table['padding']['B'] + $table['border_details']['B']['w']); 22174 } 22175 } else { 22176 $maxbwtop = 0; 22177 $maxbwbottom = 0; 22178 if (!$this->simpleTables) { 22179 if (!empty($tablefooter)) { 22180 $maxbwbottom = $table['max_cell_border_width']['B']; 22181 } else { 22182 $brow = $i - 1; 22183 for ($ctj = 0; $ctj < $numcols; $ctj++) { 22184 if (isset($cells[$brow][$ctj]) && $cells[$brow][$ctj]) { 22185 if ($this->packTableData) { 22186 list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$brow][$ctj]['borderbin']); 22187 } else { 22188 $bb = $cells[$brow][$ctj]['border_details']['B']['w']; 22189 } 22190 $maxbwbottom = max($maxbwbottom, $bb); 22191 } 22192 } 22193 } 22194 if (!empty($tableheader)) { 22195 $maxbwtop = $table['max_cell_border_width']['T']; 22196 } else { 22197 $trow = $i - 1; 22198 for ($ctj = 0; $ctj < $numcols; $ctj++) { 22199 if (isset($cells[$trow][$ctj]) && $cells[$trow][$ctj]) { 22200 if ($this->packTableData) { 22201 list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$trow][$ctj]['borderbin']); 22202 } else { 22203 $bt = $cells[$trow][$ctj]['border_details']['T']['w']; 22204 } 22205 $maxbwtop = max($maxbwtop, $bt); 22206 } 22207 } 22208 } 22209 } elseif ($this->simpleTables) { 22210 $maxbwtop = $table['simple']['border_details']['T']['w']; 22211 $maxbwbottom = $table['simple']['border_details']['B']['w']; 22212 } 22213 $adv = $maxbwbottom / 2; 22214 } 22215 $this->y += $adv; 22216 } 22217 22218 // Rotated table split over pages - needs this->y for borders/backgrounds 22219 if ($i > 0 && $this->table_rotate && $level == 1) { 22220 // $this->y = $y0 + $this->tbrot_w; 22221 } 22222 22223 if ($this->tableClipPath) { 22224 $this->writer->write("Q"); 22225 } 22226 22227 $bx = $x0; 22228 $by = $y0; 22229 22230 if ($table['borders_separate']) { 22231 $bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2); 22232 if ($tablestartpageno != $this->page) { // IF already broken across a previous pagebreak 22233 $by += $table['max_cell_border_width']['T'] / 2; 22234 if (empty($tableheader)) { 22235 $by -= ($table['border_spacing_V'] / 2); 22236 } 22237 } else { 22238 $by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2); 22239 } 22240 } elseif ($tablestartpageno != $this->page && !empty($tableheader)) { 22241 $by += $maxbwtop / 2; 22242 } 22243 22244 $by -= $tableheaderadj; 22245 $bh = $this->y - $by + $tablefooteradj; 22246 if (!$table['borders_separate']) { 22247 $bh -= $adv; 22248 } 22249 if ($split) { 22250 $bw = 0; 22251 for ($t = $startcol; $t < $numcols; $t++) { 22252 if ($table['colPg'][$t] == $splitpg) { 22253 $bw += $table['wc'][$t]; 22254 } 22255 if ($table['colPg'][$t] > $splitpg) { 22256 break; 22257 } 22258 } 22259 if ($table['borders_separate']) { 22260 if ($firstSpread) { 22261 $bw += $table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H']; 22262 } else { 22263 $bx += ($table['padding']['L'] + $table['border_details']['L']['w']); 22264 $bw += $table['border_spacing_H']; 22265 } 22266 if ($finalSpread) { 22267 $bw += $table['padding']['R'] + $table['border_details']['R']['w'] / 2 + $table['border_spacing_H']; 22268 } 22269 } 22270 } else { 22271 $bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R']; 22272 } 22273 22274 if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tablefooter) && $i > 0 && $table['border_details']['B']['w']) { 22275 $prevDrawColor = $this->DrawColor; 22276 $lw = $this->LineWidth; 22277 $this->SetLineWidth($this->splitTableBorderWidth); 22278 $this->SetDColor($table['border_details']['B']['c']); 22279 $this->SetLineJoin(0); 22280 $this->SetLineCap(0); 22281 $blx = $bx; 22282 $blw = $bw; 22283 if (!$table['borders_separate']) { 22284 $blx -= ($table['max_cell_border_width']['L'] / 2); 22285 $blw += ($table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2); 22286 } 22287 $this->Line($blx, $this->y + ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y + ($this->splitTableBorderWidth / 2)); 22288 $this->DrawColor = $prevDrawColor; 22289 $this->writer->write($this->DrawColor); 22290 $this->SetLineWidth($lw); 22291 $this->SetLineJoin(2); 22292 $this->SetLineCap(2); 22293 } 22294 22295 if (!$this->ColActive && ($i > 0 || $j > 0)) { 22296 if (isset($table['bgcolor'][-1])) { 22297 $color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings); 22298 if ($color) { 22299 if (!$table['borders_separate']) { 22300 $bh -= $table['max_cell_border_width']['B'] / 2; 22301 } 22302 $this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color]; 22303 } 22304 } 22305 22306 /* -- BACKGROUNDS -- */ 22307 if (isset($table['gradient'])) { 22308 $g = $this->gradient->parseBackgroundGradient($table['gradient']); 22309 if ($g) { 22310 $this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 22311 } 22312 } 22313 22314 if (isset($table['background-image'])) { 22315 if ($table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) { 22316 $g = $this->gradient->parseMozGradient($table['background-image']['gradient']); 22317 if ($g) { 22318 $this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 22319 } 22320 } else { 22321 $image_id = $table['background-image']['image_id']; 22322 $orig_w = $table['background-image']['orig_w']; 22323 $orig_h = $table['background-image']['orig_h']; 22324 $x_pos = $table['background-image']['x_pos']; 22325 $y_pos = $table['background-image']['y_pos']; 22326 $x_repeat = $table['background-image']['x_repeat']; 22327 $y_repeat = $table['background-image']['y_repeat']; 22328 $resize = $table['background-image']['resize']; 22329 $opacity = $table['background-image']['opacity']; 22330 $itype = $table['background-image']['itype']; 22331 $this->tableBackgrounds[$level * 9 + 2][] = ['x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, '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, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype]; 22332 } 22333 } 22334 /* -- END BACKGROUNDS -- */ 22335 } 22336 22337 // $this->AcceptPageBreak() has moved tablebuffer to $this->pages content 22338 if ($this->tableBackgrounds) { 22339 $s = $this->PrintTableBackgrounds(); 22340 if ($this->bufferoutput) { 22341 $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer); 22342 $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer); 22343 } else { 22344 $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]); 22345 $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]); 22346 } 22347 $this->tableBackgrounds = []; 22348 } 22349 22350 if ($split) { 22351 if ($i == 0 && $j == 0) { 22352 $y0 = -1; 22353 } elseif ($finalSpread) { 22354 $splitpg = 0; 22355 $startcol = 0; 22356 $startrow = $i; 22357 } else { 22358 $splitpg++; 22359 $startcol = $t; 22360 $returny -= $y; 22361 } 22362 return [false, $startrow, $startcol, $splitpg, $returny, $y0]; 22363 } 22364 22365 $this->AddPage($this->CurOrientation); 22366 22367 $this->writer->write('___TABLE___BACKGROUNDS' . $this->uniqstr); 22368 22369 22370 if ($this->tableClipPath) { 22371 $this->writer->write($this->tableClipPath); 22372 } 22373 22374 // Added to correct for OddEven Margins 22375 $x = $x + $this->MarginCorrection; 22376 $x0 = $x0 + $this->MarginCorrection; 22377 22378 if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tableheader) && $i > 0 && $table['border_details']['T']['w']) { 22379 $prevDrawColor = $this->DrawColor; 22380 $lw = $this->LineWidth; 22381 $this->SetLineWidth($this->splitTableBorderWidth); 22382 $this->SetDColor($table['border_details']['T']['c']); 22383 $this->SetLineJoin(0); 22384 $this->SetLineCap(0); 22385 $blx += $this->MarginCorrection; 22386 $this->Line($blx, $this->y - ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y - ($this->splitTableBorderWidth / 2)); 22387 $this->DrawColor = $prevDrawColor; 22388 $this->writer->write($this->DrawColor); 22389 $this->SetLineWidth($lw); 22390 $this->SetLineJoin(2); 22391 $this->SetLineCap(2); 22392 } 22393 22394 // Move down to account for half of top border-spacing or 22395 // extra half border width in case page was broken in middle 22396 if ($i > 0 && !$this->table_rotate && $level == 1 && $table['headernrows'] == 0) { 22397 if ($table['borders_separate']) { 22398 $adv = $table['border_spacing_V'] / 2; 22399 } else { 22400 $maxbwtop = 0; 22401 for ($ctj = 0; $ctj < $numcols; $ctj++) { 22402 if (isset($cells[$i][$ctj]) && $cells[$i][$ctj]) { 22403 if (!$this->simpleTables) { 22404 if ($this->packTableData) { 22405 list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$i][$ctj]['borderbin']); 22406 } else { 22407 $bt = $cells[$i][$ctj]['border_details']['T']['w']; 22408 } 22409 $maxbwtop = max($maxbwtop, $bt); 22410 } elseif ($this->simpleTables) { 22411 $maxbwtop = max($maxbwtop, $table['simple']['border_details']['T']['w']); 22412 } 22413 } 22414 } 22415 $adv = $maxbwtop / 2; 22416 } 22417 $this->y += $adv; 22418 } 22419 22420 22421 if ($this->table_rotate) { 22422 $this->tbrot_x0 = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w']; 22423 if ($table['borders_separate']) { 22424 $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2; 22425 } else { 22426 $this->tbrot_h = $table['margin']['T'] + $table['max_cell_border_width']['T']; 22427 } 22428 $this->tbrot_y0 = $this->y; 22429 $pagetrigger = $y0 - $tableheaderadj + ($this->blk[$this->blklvl]['inner_width']); 22430 } else { 22431 $pagetrigger = $this->PageBreakTrigger; 22432 } 22433 22434 if ($this->kwt_saved && $level == 1) { 22435 $this->kwt_moved = true; 22436 } 22437 22438 22439 if (!empty($tableheader)) { 22440 $ya = $this->y; 22441 $this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level); 22442 if ($this->table_rotate) { 22443 $this->tbrot_h = $this->y - $ya; 22444 } 22445 $tableheaderadj = $this->y - $ya; 22446 } elseif ($i == 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) { 22447 // Advance down page 22448 if ($table['borders_separate']) { 22449 $adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T']; 22450 } else { 22451 $adv = $table['max_cell_border_width']['T'] / 2; 22452 } 22453 if ($adv) { 22454 if ($this->table_rotate) { 22455 $this->y += ($adv); 22456 } else { 22457 $this->DivLn($adv, $this->blklvl, true); 22458 } 22459 } 22460 } 22461 22462 $outerfilled = 0; 22463 $y = $y0 = $this->y; 22464 } 22465 22466 /* -- COLUMNS -- */ 22467 // COLS 22468 // COLUMN CHANGE 22469 if ($this->CurrCol != $oldcolumn) { 22470 // Added to correct for Columns 22471 $x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 22472 $x0 += $this->ChangeColumn * ($this->ColWidth + $this->ColGap); 22473 if ($this->CurrCol == 0) { // just added a page - possibly with tableheader 22474 $y0 = $this->y; // this->y0 is global used by Columns - $y0 is internal to tablewrite 22475 } else { 22476 $y0 = $this->y0; // this->y0 is global used by Columns - $y0 is internal to tablewrite 22477 } 22478 $y = $y0; 22479 $outerfilled = 0; 22480 if ($this->CurrCol != 0 && ($this->keepColumns && $this->ColActive) && !empty($tableheader) && $i > 0) { 22481 $this->x = $x; 22482 $this->y = $y; 22483 $this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level); 22484 $y0 = $y = $this->y; 22485 } 22486 } 22487 /* -- END COLUMNS -- */ 22488 } 22489 $skippage = true; 22490 } 22491 22492 $this->x = $x; 22493 $this->y = $y; 22494 22495 if ($this->kwt_saved && $level == 1) { 22496 $this->printkwtbuffer(); 22497 $x0 = $x = $this->x; 22498 $y0 = $y = $this->y; 22499 $this->kwt_moved = false; 22500 $this->kwt_saved = false; 22501 } 22502 22503 22504 // Set the Page & Column where table actually starts 22505 if ($i == 0 && $j == 0 && $level == 1) { 22506 if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN 22507 $tablestartpage = 'EVEN'; 22508 } elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD 22509 $tablestartpage = 'ODD'; 22510 } else { 22511 $tablestartpage = ''; 22512 } 22513 $tablestartpageno = $this->page; 22514 if ($this->ColActive) { 22515 $tablestartcolumn = $this->CurrCol; 22516 } // *COLUMNS* 22517 } 22518 22519 // ALIGN 22520 $align = $cell['a']; 22521 22522 /* -- COLUMNS -- */ 22523 // If outside columns, this is done in PaintDivBB 22524 if ($this->ColActive) { 22525 // OUTER FILL BGCOLOR of DIVS 22526 if ($this->blklvl > 0 && ($j == 0) && !$this->table_rotate && $level == 1) { 22527 $firstblockfill = $this->GetFirstBlockFill(); 22528 if ($firstblockfill && $this->blklvl >= $firstblockfill) { 22529 $divh = $maxrowheight; 22530 // Last row 22531 if ((!isset($cell['rowspan']) && $i == $numrows - 1) || (isset($cell['rowspan']) && (($i == $numrows - 1 && $cell['rowspan'] < 2) || ($cell['rowspan'] > 1 && ($i + $cell['rowspan'] - 1) == $numrows - 1)))) { 22532 if ($table['borders_separate']) { 22533 $adv = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2; 22534 } else { 22535 $adv = $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2; 22536 } 22537 $divh += $adv; // last row: fill bottom half of bottom border (y advanced at end) 22538 } 22539 22540 if (($this->y + $divh) > $outerfilled) { // if not already painted by previous rowspan 22541 $bak_x = $this->x; 22542 $bak_y = $this->y; 22543 if ($outerfilled > $this->y) { 22544 $divh = ($this->y + $divh) - $outerfilled; 22545 $this->y = $outerfilled; 22546 } 22547 22548 $this->DivLn($divh, -3, false); 22549 $outerfilled = $this->y + $divh; 22550 // Reset current block fill 22551 $bcor = $this->blk[$this->blklvl]['bgcolorarray']; 22552 if ($bcor) { 22553 $this->SetFColor($bcor); 22554 } 22555 $this->x = $bak_x; 22556 $this->y = $bak_y; 22557 } 22558 } 22559 } 22560 } 22561 22562 // TABLE BACKGROUND FILL BGCOLOR - for cellSpacing 22563 if ($this->ColActive) { 22564 if ($table['borders_separate']) { 22565 $fill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0; 22566 if ($fill) { 22567 $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings); 22568 if ($color) { 22569 $xadj = ($table['border_spacing_H'] / 2); 22570 $yadj = ($table['border_spacing_V'] / 2); 22571 $wadj = $table['border_spacing_H']; 22572 $hadj = $table['border_spacing_V']; 22573 if ($i == 0) { // Top 22574 $yadj += $table['padding']['T'] + $table['border_details']['T']['w']; 22575 $hadj += $table['padding']['T'] + $table['border_details']['T']['w']; 22576 } 22577 if ($j == 0) { // Left 22578 $xadj += $table['padding']['L'] + $table['border_details']['L']['w']; 22579 $wadj += $table['padding']['L'] + $table['border_details']['L']['w']; 22580 } 22581 if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) { // Bottom 22582 $hadj += $table['padding']['B'] + $table['border_details']['B']['w']; 22583 } 22584 if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols) || (!isset($cell['colspan']) && ($j + 1) == $numcols)) { // Right 22585 $wadj += $table['padding']['R'] + $table['border_details']['R']['w']; 22586 } 22587 $this->SetFColor($color); 22588 $this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F'); 22589 } 22590 } 22591 } 22592 } 22593 /* -- END COLUMNS -- */ 22594 22595 if ($table['empty_cells'] != 'hide' || !empty($cell['textbuffer']) || (isset($cell['nestedcontent']) && $cell['nestedcontent']) || !$table['borders_separate']) { 22596 $paintcell = true; 22597 } else { 22598 $paintcell = false; 22599 } 22600 22601 // Set Borders 22602 $bord = 0; 22603 $bord_det = []; 22604 22605 if (!$this->simpleTables) { 22606 if ($this->packTableData) { 22607 $c = $this->_unpackCellBorder($cell['borderbin']); 22608 $bord = $c['border']; 22609 $bord_det = $c['border_details']; 22610 } else { 22611 $bord = $cell['border']; 22612 $bord_det = $cell['border_details']; 22613 } 22614 } elseif ($this->simpleTables) { 22615 $bord = $table['simple']['border']; 22616 $bord_det = $table['simple']['border_details']; 22617 } 22618 22619 // TABLE ROW OR CELL FILL BGCOLOR 22620 $fill = 0; 22621 if (isset($cell['bgcolor']) && $cell['bgcolor'] && $cell['bgcolor'] != 'transparent') { 22622 $fill = $cell['bgcolor']; 22623 $leveladj = 6; 22624 } elseif (isset($table['bgcolor'][$i]) && $table['bgcolor'][$i] && $table['bgcolor'][$i] != 'transparent') { // Row color 22625 $fill = $table['bgcolor'][$i]; 22626 $leveladj = 3; 22627 } 22628 if ($fill && $paintcell) { 22629 $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings); 22630 if ($color) { 22631 if ($table['borders_separate']) { 22632 if ($this->ColActive) { 22633 $this->SetFColor($color); 22634 $this->Rect($x + ($table['border_spacing_H'] / 2), $y + ($table['border_spacing_V'] / 2), $w - $table['border_spacing_H'], $h - $table['border_spacing_V'], 'F'); 22635 } else { 22636 $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => ($x + ($table['border_spacing_H'] / 2)), 'y' => ($y + ($table['border_spacing_V'] / 2)), 'w' => ($w - $table['border_spacing_H']), 'h' => ($h - $table['border_spacing_V']), 'col' => $color]; 22637 } 22638 } else { 22639 if ($this->ColActive) { 22640 $this->SetFColor($color); 22641 $this->Rect($x, $y, $w, $h, 'F'); 22642 } else { 22643 $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color]; 22644 } 22645 } 22646 } 22647 } 22648 22649 /* -- BACKGROUNDS -- */ 22650 if (isset($cell['gradient']) && $cell['gradient'] && $paintcell) { 22651 $g = $this->gradient->parseBackgroundGradient($cell['gradient']); 22652 if ($g) { 22653 if ($table['borders_separate']) { 22654 $px = $x + ($table['border_spacing_H'] / 2); 22655 $py = $y + ($table['border_spacing_V'] / 2); 22656 $pw = $w - $table['border_spacing_H']; 22657 $ph = $h - $table['border_spacing_V']; 22658 } else { 22659 $px = $x; 22660 $py = $y; 22661 $pw = $w; 22662 $ph = $h; 22663 } 22664 if ($this->ColActive) { 22665 $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']); 22666 } else { 22667 $this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 22668 } 22669 } 22670 } 22671 22672 if (isset($cell['background-image']) && $paintcell) { 22673 if (isset($cell['background-image']['gradient']) && $cell['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $cell['background-image']['gradient'])) { 22674 $g = $this->gradient->parseMozGradient($cell['background-image']['gradient']); 22675 if ($g) { 22676 if ($table['borders_separate']) { 22677 $px = $x + ($table['border_spacing_H'] / 2); 22678 $py = $y + ($table['border_spacing_V'] / 2); 22679 $pw = $w - $table['border_spacing_H']; 22680 $ph = $h - $table['border_spacing_V']; 22681 } else { 22682 $px = $x; 22683 $py = $y; 22684 $pw = $w; 22685 $ph = $h; 22686 } 22687 if ($this->ColActive) { 22688 $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']); 22689 } else { 22690 $this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 22691 } 22692 } 22693 } elseif (isset($cell['background-image']['image_id']) && $cell['background-image']['image_id']) { // Background pattern 22694 $n = count($this->patterns) + 1; 22695 if ($table['borders_separate']) { 22696 $px = $x + ($table['border_spacing_H'] / 2); 22697 $py = $y + ($table['border_spacing_V'] / 2); 22698 $pw = $w - $table['border_spacing_H']; 22699 $ph = $h - $table['border_spacing_V']; 22700 } else { 22701 $px = $x; 22702 $py = $y; 22703 $pw = $w; 22704 $ph = $h; 22705 } 22706 if ($this->ColActive) { 22707 list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($cell['background-image']['orig_w'], $cell['background-image']['orig_h'], $pw, $ph, $cell['background-image']['resize'], $cell['background-image']['x_repeat'], $cell['background-image']['y_repeat']); 22708 $this->patterns[$n] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'pgh' => $this->h, 'image_id' => $cell['background-image']['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $cell['background-image']['x_pos'], 'y_pos' => $cell['background-image']['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat]; 22709 if ($cell['background-image']['opacity'] > 0 && $cell['background-image']['opacity'] < 1) { 22710 $opac = $this->SetAlpha($cell['background-image']['opacity'], 'Normal', true); 22711 } else { 22712 $opac = ''; 22713 } 22714 $this->writer->write(sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $px * Mpdf::SCALE, ($this->h - $py) * Mpdf::SCALE, $pw * Mpdf::SCALE, -$ph * Mpdf::SCALE)); 22715 } else { 22716 $image_id = $cell['background-image']['image_id']; 22717 $orig_w = $cell['background-image']['orig_w']; 22718 $orig_h = $cell['background-image']['orig_h']; 22719 $x_pos = $cell['background-image']['x_pos']; 22720 $y_pos = $cell['background-image']['y_pos']; 22721 $x_repeat = $cell['background-image']['x_repeat']; 22722 $y_repeat = $cell['background-image']['y_repeat']; 22723 $resize = $cell['background-image']['resize']; 22724 $opacity = $cell['background-image']['opacity']; 22725 $itype = $cell['background-image']['itype']; 22726 $this->tableBackgrounds[$level * 9 + 8][] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, '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, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype]; 22727 } 22728 } 22729 } 22730 /* -- END BACKGROUNDS -- */ 22731 22732 if (isset($cell['colspan']) && $cell['colspan'] > 1) { 22733 $ccolsp = $cell['colspan']; 22734 } else { 22735 $ccolsp = 1; 22736 } 22737 if (isset($cell['rowspan']) && $cell['rowspan'] > 1) { 22738 $crowsp = $cell['rowspan']; 22739 } else { 22740 $crowsp = 1; 22741 } 22742 22743 22744 // but still need to do this for repeated headers... 22745 if (!$table['borders_separate'] && $this->tabletheadjustfinished && !$this->simpleTables) { 22746 if (isset($table['topntail']) && $table['topntail']) { 22747 $bord_det['T'] = $this->border_details($table['topntail']); 22748 $bord_det['T']['w'] /= $this->shrin_k; 22749 $this->setBorder($bord, Border::TOP); 22750 } 22751 if (isset($table['thead-underline']) && $table['thead-underline']) { 22752 $bord_det['T'] = $this->border_details($table['thead-underline']); 22753 $bord_det['T']['w'] /= $this->shrin_k; 22754 $this->setBorder($bord, Border::TOP); 22755 } 22756 } 22757 22758 22759 // Get info of first row ==>> table header 22760 // Use > 1 row if THEAD 22761 if (isset($table['is_thead'][$i]) && $table['is_thead'][$i] && $level == 1) { 22762 if ($j == 0) { 22763 $tableheaderrowheight += $table['hr'][$i]; 22764 } 22765 $tableheader[$i][0]['trbackground-images'] = (isset($table['trbackground-images'][$i]) ? $table['trbackground-images'][$i] : null); 22766 $tableheader[$i][0]['trgradients'] = (isset($table['trgradients'][$i]) ? $table['trgradients'][$i] : null); 22767 $tableheader[$i][0]['trbgcolor'] = (isset($table['bgcolor'][$i]) ? $table['bgcolor'][$i] : null); 22768 $tableheader[$i][$j]['x'] = $x; 22769 $tableheader[$i][$j]['y'] = $y; 22770 $tableheader[$i][$j]['h'] = $h; 22771 $tableheader[$i][$j]['w'] = $w; 22772 if (isset($cell['textbuffer'])) { 22773 $tableheader[$i][$j]['textbuffer'] = $cell['textbuffer']; 22774 } else { 22775 $tableheader[$i][$j]['textbuffer'] = ''; 22776 } 22777 $tableheader[$i][$j]['a'] = $cell['a']; 22778 $tableheader[$i][$j]['R'] = $cell['R']; 22779 22780 $tableheader[$i][$j]['va'] = $cell['va']; 22781 $tableheader[$i][$j]['mih'] = $cell['mih']; 22782 $tableheader[$i][$j]['gradient'] = (isset($cell['gradient']) ? $cell['gradient'] : null); // *BACKGROUNDS* 22783 $tableheader[$i][$j]['background-image'] = (isset($cell['background-image']) ? $cell['background-image'] : null); // *BACKGROUNDS* 22784 $tableheader[$i][$j]['rowspan'] = (isset($cell['rowspan']) ? $cell['rowspan'] : null); 22785 $tableheader[$i][$j]['colspan'] = (isset($cell['colspan']) ? $cell['colspan'] : null); 22786 $tableheader[$i][$j]['bgcolor'] = $cell['bgcolor']; 22787 22788 if (!$this->simpleTables) { 22789 $tableheader[$i][$j]['border'] = $bord; 22790 $tableheader[$i][$j]['border_details'] = $bord_det; 22791 } elseif ($this->simpleTables) { 22792 $tableheader[$i][$j]['border'] = $table['simple']['border']; 22793 $tableheader[$i][$j]['border_details'] = $table['simple']['border_details']; 22794 } 22795 $tableheader[$i][$j]['padding'] = $cell['padding']; 22796 if (isset($cell['direction'])) { 22797 $tableheader[$i][$j]['direction'] = $cell['direction']; 22798 } 22799 if (isset($cell['cellLineHeight'])) { 22800 $tableheader[$i][$j]['cellLineHeight'] = $cell['cellLineHeight']; 22801 } 22802 if (isset($cell['cellLineStackingStrategy'])) { 22803 $tableheader[$i][$j]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy']; 22804 } 22805 if (isset($cell['cellLineStackingShift'])) { 22806 $tableheader[$i][$j]['cellLineStackingShift'] = $cell['cellLineStackingShift']; 22807 } 22808 } 22809 22810 // CELL BORDER 22811 if ($bord) { 22812 if ($table['borders_separate'] && $paintcell) { 22813 $this->_tableRect($x + ($table['border_spacing_H'] / 2) + ($bord_det['L']['w'] / 2), $y + ($table['border_spacing_V'] / 2) + ($bord_det['T']['w'] / 2), $w - $table['border_spacing_H'] - ($bord_det['L']['w'] / 2) - ($bord_det['R']['w'] / 2), $h - $table['border_spacing_V'] - ($bord_det['T']['w'] / 2) - ($bord_det['B']['w'] / 2), $bord, $bord_det, false, $table['borders_separate']); 22814 } elseif (!$table['borders_separate']) { 22815 $this->_tableRect($x, $y, $w, $h, $bord, $bord_det, true, $table['borders_separate']); // true causes buffer 22816 } 22817 } 22818 22819 // VERTICAL ALIGN 22820 if ($cell['R'] && intval($cell['R']) > 0 && intval($cell['R']) < 90 && isset($cell['va']) && $cell['va'] != 'B') { 22821 $cell['va'] = 'B'; 22822 } 22823 if (!isset($cell['va']) || $cell['va'] == 'M') { 22824 $this->y += ($h - $cell['mih']) / 2; 22825 } elseif (isset($cell['va']) && $cell['va'] == 'B') { 22826 $this->y += $h - $cell['mih']; 22827 } 22828 22829 // NESTED CONTENT 22830 // TEXT (and nested tables) 22831 22832 $this->divwidth = $w; 22833 if (!empty($cell['textbuffer'])) { 22834 $this->cellTextAlign = $align; 22835 $this->cellLineHeight = $cell['cellLineHeight']; 22836 $this->cellLineStackingStrategy = $cell['cellLineStackingStrategy']; 22837 $this->cellLineStackingShift = $cell['cellLineStackingShift']; 22838 if ($level == 1) { 22839 if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) { 22840 if (preg_match('/{colsum([0-9]*)[_]*}/', $cell['textbuffer'][0][0], $m)) { 22841 $rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$j]); 22842 $cell['textbuffer'][0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $cell['textbuffer'][0][0]); 22843 } 22844 } elseif (!isset($table['is_thead'][$i])) { 22845 if (isset($this->colsums[$j])) { 22846 $this->colsums[$j] += $this->toFloat($cell['textbuffer'][0][0]); 22847 } else { 22848 $this->colsums[$j] = $this->toFloat($cell['textbuffer'][0][0]); 22849 } 22850 } 22851 } 22852 $opy = $this->y; 22853 // mPDF ITERATION 22854 if ($this->iterationCounter) { 22855 foreach ($cell['textbuffer'] as $k => $t) { 22856 if (preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) { 22857 $vname = '__' . $m[1] . '_'; 22858 if (!isset($this->$vname)) { 22859 $this->$vname = 1; 22860 } else { 22861 $this->$vname++; 22862 } 22863 $cell['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $cell['textbuffer'][$k][0]); 22864 } 22865 } 22866 } 22867 22868 22869 if ($cell['R']) { 22870 $cellPtSize = $cell['textbuffer'][0][11] / $this->shrin_k; 22871 if (!$cellPtSize) { 22872 $cellPtSize = $this->default_font_size; 22873 } 22874 $cellFontHeight = ($cellPtSize / Mpdf::SCALE); 22875 $opx = $this->x; 22876 $angle = intval($cell['R']); 22877 // Only allow 45 to 89 degrees (when bottom-aligned) or exactly 90 or -90 22878 if ($angle > 90) { 22879 $angle = 90; 22880 } elseif ($angle > 0 && $angle < 45) { 22881 $angle = 45; 22882 } elseif ($angle < 0) { 22883 $angle = -90; 22884 } 22885 $offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight); 22886 if (isset($cell['a']) && $cell['a'] == 'R') { 22887 $this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($cell['padding']['R'] + ($table['border_spacing_H'] / 2)); 22888 } elseif (!isset($cell['a']) || $cell['a'] == 'C') { 22889 $this->x += ($w / 2) + ($offset); 22890 } else { 22891 $this->x += ($offset) + ($cellFontHeight / 3) + ($cell['padding']['L'] + ($table['border_spacing_H'] / 2)); 22892 } 22893 $str = ''; 22894 foreach ($cell['textbuffer'] as $t) { 22895 $str .= $t[0] . ' '; 22896 } 22897 $str = rtrim($str); 22898 if (!isset($cell['va']) || $cell['va'] == 'M') { 22899 $this->y -= ($h - $cell['mih']) / 2; // Undo what was added earlier VERTICAL ALIGN 22900 if ($angle > 0) { 22901 $this->y += (($h - $cell['mih']) / 2) + $cell['padding']['T'] + ($cell['mih'] - ($cell['padding']['T'] + $cell['padding']['B'])); 22902 } elseif ($angle < 0) { 22903 $this->y += (($h - $cell['mih']) / 2) + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2)); 22904 } 22905 } elseif (isset($cell['va']) && $cell['va'] == 'B') { 22906 $this->y -= $h - $cell['mih']; // Undo what was added earlier VERTICAL ALIGN 22907 if ($angle > 0) { 22908 $this->y += $h - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2)); 22909 } elseif ($angle < 0) { 22910 $this->y += $h - $cell['mih'] + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2)); 22911 } 22912 } elseif (isset($cell['va']) && $cell['va'] == 'T') { 22913 if ($angle > 0) { 22914 $this->y += $cell['mih'] - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2)); 22915 } elseif ($angle < 0) { 22916 $this->y += ($cell['padding']['T'] + ($table['border_spacing_V'] / 2)); 22917 } 22918 } 22919 $this->Rotate($angle, $this->x, $this->y); 22920 $s_fs = $this->FontSizePt; 22921 $s_f = $this->FontFamily; 22922 $s_st = $this->FontStyle; 22923 if (!empty($cell['textbuffer'][0][3])) { // Font Color 22924 $cor = $cell['textbuffer'][0][3]; 22925 $this->SetTColor($cor); 22926 } 22927 $this->SetFont($cell['textbuffer'][0][4], $cell['textbuffer'][0][2], $cellPtSize, true, true); 22928 22929 $this->magic_reverse_dir($str, $this->directionality, $cell['textbuffer'][0][18]); 22930 $this->Text($this->x, $this->y, $str, $cell['textbuffer'][0][18], $cell['textbuffer'][0][8]); // textvar 22931 $this->Rotate(0); 22932 $this->SetFont($s_f, $s_st, $s_fs, true, true); 22933 $this->SetTColor(0); 22934 $this->x = $opx; 22935 } else { 22936 if (!$this->simpleTables) { 22937 if ($bord_det) { 22938 $btlw = $bord_det['L']['w']; 22939 $btrw = $bord_det['R']['w']; 22940 $bttw = $bord_det['T']['w']; 22941 } else { 22942 $btlw = 0; 22943 $btrw = 0; 22944 $bttw = 0; 22945 } 22946 if ($table['borders_separate']) { 22947 $xadj = $btlw + $cell['padding']['L'] + ($table['border_spacing_H'] / 2); 22948 $wadj = $btlw + $btrw + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H']; 22949 $yadj = $bttw + $cell['padding']['T'] + ($table['border_spacing_H'] / 2); 22950 } else { 22951 $xadj = $btlw / 2 + $cell['padding']['L']; 22952 $wadj = ($btlw + $btrw) / 2 + $cell['padding']['L'] + $cell['padding']['R']; 22953 $yadj = $bttw / 2 + $cell['padding']['T']; 22954 } 22955 } elseif ($this->simpleTables) { 22956 if ($table['borders_separate']) { // NB twice border width 22957 $xadj = $table['simple']['border_details']['L']['w'] + $cell['padding']['L'] + ($table['border_spacing_H'] / 2); 22958 $wadj = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H']; 22959 $yadj = $table['simple']['border_details']['T']['w'] + $cell['padding']['T'] + ($table['border_spacing_H'] / 2); 22960 } else { 22961 $xadj = $table['simple']['border_details']['L']['w'] / 2 + $cell['padding']['L']; 22962 $wadj = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + $cell['padding']['L'] + $cell['padding']['R']; 22963 $yadj = $table['simple']['border_details']['T']['w'] / 2 + $cell['padding']['T']; 22964 } 22965 } 22966 $this->decimal_offset = 0; 22967 if (substr($cell['a'], 0, 1) == 'D') { 22968 if (isset($cell['colspan']) && $cell['colspan'] > 1) { 22969 $this->cellTextAlign = $c['a'] = substr($cell['a'], 2, 1); 22970 } else { 22971 $smax = $table['decimal_align'][$j]['maxs0']; 22972 $d_content = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1']; 22973 $this->decimal_offset = $smax; 22974 $extra = ($w - $d_content - $wadj); 22975 if ($extra > 0) { 22976 if (substr($cell['a'], 2, 1) == 'R') { 22977 $this->decimal_offset += $extra; 22978 } elseif (substr($cell['a'], 2, 1) == 'C') { 22979 $this->decimal_offset += ($extra) / 2; 22980 } 22981 } 22982 } 22983 } 22984 $this->divwidth = $w - $wadj; 22985 if ($this->divwidth == 0) { 22986 $this->divwidth = 0.0001; 22987 } 22988 $this->x += $xadj; 22989 $this->y += $yadj; 22990 $this->printbuffer($cell['textbuffer'], '', true, false, $cell['direction']); 22991 } 22992 $this->y = $opy; 22993 } 22994 22995 /* -- BACKGROUNDS -- */ 22996 if (!$this->ColActive) { 22997 if (isset($table['trgradients'][$i]) && ($j == 0 || $table['borders_separate'])) { 22998 $g = $this->gradient->parseBackgroundGradient($table['trgradients'][$i]); 22999 if ($g) { 23000 $gx = $x0; 23001 $gy = $y; 23002 $gh = $h; 23003 $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R']; 23004 if ($table['borders_separate']) { 23005 $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']); 23006 $clx = $x + ($table['border_spacing_H'] / 2); 23007 $cly = $y + ($table['border_spacing_V'] / 2); 23008 $clw = $w - $table['border_spacing_H']; 23009 $clh = $h - $table['border_spacing_V']; 23010 // Set clipping path 23011 $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6 23012 $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s]; 23013 } else { 23014 $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 23015 } 23016 } 23017 } 23018 if (isset($table['trbackground-images'][$i]) && ($j == 0 || $table['borders_separate'])) { 23019 if (isset($table['trbackground-images'][$i]['gradient']) && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['trbackground-images'][$i]['gradient'])) { 23020 $g = $this->gradient->parseMozGradient($table['trbackground-images'][$i]['gradient']); 23021 if ($g) { 23022 $gx = $x0; 23023 $gy = $y; 23024 $gh = $h; 23025 $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R']; 23026 if ($table['borders_separate']) { 23027 $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']); 23028 $clx = $x + ($table['border_spacing_H'] / 2); 23029 $cly = $y + ($table['border_spacing_V'] / 2); 23030 $clw = $w - $table['border_spacing_H']; 23031 $clh = $h - $table['border_spacing_V']; 23032 // Set clipping path 23033 $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6 23034 $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s]; 23035 } else { 23036 $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 23037 } 23038 } 23039 } else { 23040 $image_id = $table['trbackground-images'][$i]['image_id']; 23041 $orig_w = $table['trbackground-images'][$i]['orig_w']; 23042 $orig_h = $table['trbackground-images'][$i]['orig_h']; 23043 $x_pos = $table['trbackground-images'][$i]['x_pos']; 23044 $y_pos = $table['trbackground-images'][$i]['y_pos']; 23045 $x_repeat = $table['trbackground-images'][$i]['x_repeat']; 23046 $y_repeat = $table['trbackground-images'][$i]['y_repeat']; 23047 $resize = $table['trbackground-images'][$i]['resize']; 23048 $opacity = $table['trbackground-images'][$i]['opacity']; 23049 $itype = $table['trbackground-images'][$i]['itype']; 23050 $clippath = ''; 23051 $gx = $x0; 23052 $gy = $y; 23053 $gh = $h; 23054 $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R']; 23055 if ($table['borders_separate']) { 23056 $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']); 23057 $clx = $x + ($table['border_spacing_H'] / 2); 23058 $cly = $y + ($table['border_spacing_V'] / 2); 23059 $clw = $w - $table['border_spacing_H']; 23060 $clh = $h - $table['border_spacing_V']; 23061 // Set clipping path 23062 $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6 23063 $this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], '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, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype]; 23064 } else { 23065 $this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, '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, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype]; 23066 } 23067 } 23068 } 23069 } 23070 23071 /* -- END BACKGROUNDS -- */ 23072 23073 // TABLE BORDER - if separate 23074 if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) { 23075 $halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2); 23076 $halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2); 23077 $halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2); 23078 $halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2); 23079 $tbx = $x; 23080 $tby = $y; 23081 $tbw = $w; 23082 $tbh = $h; 23083 $tab_bord = 0; 23084 23085 $corner = ''; 23086 if ($i == 0) { // Top 23087 $tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2); 23088 $tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2); 23089 $this->setBorder($tab_bord, Border::TOP); 23090 $corner .= 'T'; 23091 } 23092 if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows)) { // Bottom 23093 $tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2); 23094 $this->setBorder($tab_bord, Border::BOTTOM); 23095 $corner .= 'B'; 23096 } 23097 if ($j == 0) { // Left 23098 $tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2); 23099 $tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2); 23100 $this->setBorder($tab_bord, Border::LEFT); 23101 $corner .= 'L'; 23102 } 23103 if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols)) { // Right 23104 $tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2); 23105 $this->setBorder($tab_bord, Border::RIGHT); 23106 $corner .= 'R'; 23107 } 23108 $this->_tableRect($tbx, $tby, $tbw, $tbh, $tab_bord, $table['border_details'], false, $table['borders_separate'], 'table', $corner, $table['border_spacing_V'], $table['border_spacing_H']); 23109 } 23110 23111 unset($cell); 23112 // Reset values 23113 $this->Reset(); 23114 }//end of (if isset(cells)...) 23115 }// end of columns 23116 23117 $newpagestarted = false; 23118 $this->tabletheadjustfinished = false; 23119 23120 /* -- COLUMNS -- */ 23121 if ($this->ColActive) { 23122 if (!$this->table_keep_together && $i < $numrows - 1 && $level == 1) { 23123 $this->breakpoints[$this->CurrCol][] = $y + $h; 23124 } // mPDF 6 23125 if (count($this->cellBorderBuffer)) { 23126 $this->printcellbuffer(); 23127 } 23128 } 23129 /* -- END COLUMNS -- */ 23130 23131 if ($i == $numrows - 1) { 23132 $this->y = $y + $h; 23133 } // last row jump (update this->y position) 23134 if ($this->table_rotate && $level == 1) { 23135 $this->tbrot_h += $h; 23136 } 23137 } // end of rows 23138 23139 if (count($this->cellBorderBuffer)) { 23140 $this->printcellbuffer(); 23141 } 23142 23143 23144 if ($this->tableClipPath) { 23145 $this->writer->write("Q"); 23146 } 23147 $this->tableClipPath = ''; 23148 23149 // Advance down page by half width of bottom border 23150 if ($table['borders_separate']) { 23151 $this->y += $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2; 23152 } else { 23153 $this->y += $table['max_cell_border_width']['B'] / 2; 23154 } 23155 23156 if ($table['borders_separate'] && $level == 1) { 23157 $this->tbrot_h += $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2; 23158 } elseif ($level == 1) { 23159 $this->tbrot_h += $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2; 23160 } 23161 23162 $bx = $x0; 23163 $by = $y0; 23164 if ($table['borders_separate']) { 23165 $bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2); 23166 if ($tablestartpageno != $this->page) { // IF broken across page 23167 $by += $table['max_cell_border_width']['T'] / 2; 23168 if (empty($tableheader)) { 23169 $by -= ($table['border_spacing_V'] / 2); 23170 } 23171 } elseif ($split && $startrow > 0 && empty($tableheader)) { 23172 $by -= ($table['border_spacing_V'] / 2); 23173 } else { 23174 $by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2); 23175 } 23176 } elseif ($tablestartpageno != $this->page && !empty($tableheader)) { 23177 $by += $maxbwtop / 2; 23178 } 23179 $by -= $tableheaderadj; 23180 $bh = $this->y - $by; 23181 if (!$table['borders_separate']) { 23182 $bh -= $table['max_cell_border_width']['B'] / 2; 23183 } 23184 23185 if ($split) { 23186 $bw = 0; 23187 $finalSpread = true; 23188 for ($t = $startcol; $t < $numcols; $t++) { 23189 if ($table['colPg'][$t] == $splitpg) { 23190 $bw += $table['wc'][$t]; 23191 } 23192 if ($table['colPg'][$t] > $splitpg) { 23193 $finalSpread = false; 23194 break; 23195 } 23196 } 23197 if ($startcol == 0) { 23198 $firstSpread = true; 23199 } else { 23200 $firstSpread = false; 23201 } 23202 if ($table['borders_separate']) { 23203 $bw += $table['border_spacing_H']; 23204 if ($firstSpread) { 23205 $bw += $table['padding']['L'] + $table['border_details']['L']['w']; 23206 } else { 23207 $bx += ($table['padding']['L'] + $table['border_details']['L']['w']); 23208 } 23209 if ($finalSpread) { 23210 $bw += $table['padding']['R'] + $table['border_details']['R']['w']; 23211 } 23212 } 23213 } else { 23214 $bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R']; 23215 } 23216 23217 if (!$this->ColActive) { 23218 if (isset($table['bgcolor'][-1])) { 23219 $color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings); 23220 if ($color) { 23221 $this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color]; 23222 } 23223 } 23224 23225 /* -- BACKGROUNDS -- */ 23226 if (isset($table['gradient'])) { 23227 $g = $this->gradient->parseBackgroundGradient($table['gradient']); 23228 if ($g) { 23229 $this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 23230 } 23231 } 23232 23233 if (isset($table['background-image'])) { 23234 if (isset($table['background-image']['gradient']) && $table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) { 23235 $g = $this->gradient->parseMozGradient($table['background-image']['gradient']); 23236 if ($g) { 23237 $this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '']; 23238 } 23239 } else { 23240 $image_id = $table['background-image']['image_id']; 23241 $orig_w = $table['background-image']['orig_w']; 23242 $orig_h = $table['background-image']['orig_h']; 23243 $x_pos = $table['background-image']['x_pos']; 23244 $y_pos = $table['background-image']['y_pos']; 23245 $x_repeat = $table['background-image']['x_repeat']; 23246 $y_repeat = $table['background-image']['y_repeat']; 23247 $resize = $table['background-image']['resize']; 23248 $opacity = $table['background-image']['opacity']; 23249 $itype = $table['background-image']['itype']; 23250 $this->tableBackgrounds[$level * 9 + 2][] = ['x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, '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, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype]; 23251 } 23252 } 23253 /* -- END BACKGROUNDS -- */ 23254 } 23255 23256 if ($this->tableBackgrounds && $level == 1) { 23257 $s = $this->PrintTableBackgrounds(); 23258 if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) { 23259 $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->tablebuffer); 23260 if ($level == 1) { 23261 $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->tablebuffer); 23262 } 23263 } elseif ($this->bufferoutput) { 23264 $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer); 23265 if ($level == 1) { 23266 $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer); 23267 } 23268 } else { 23269 $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]); 23270 if ($level == 1) { 23271 $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]); 23272 } 23273 } 23274 $this->tableBackgrounds = []; 23275 } 23276 23277 23278 // TABLE BOTTOM MARGIN 23279 if ($table['margin']['B']) { 23280 if (!$this->table_rotate && $level == 1) { 23281 $this->DivLn($table['margin']['B'], $this->blklvl, true); // collapsible 23282 } else { 23283 $this->y += ($table['margin']['B']); 23284 } 23285 } 23286 23287 if ($this->ColActive && $level == 1) { 23288 $this->breakpoints[$this->CurrCol][] = $this->y; 23289 } // *COLUMNS* 23290 23291 if ($split) { 23292 // Are there more columns to print on a next page? 23293 if ($lastCol < $numcols - 1) { 23294 $splitpg++; 23295 $startcol = $lastCol + 1; 23296 return [false, $startrow, $startcol, $splitpg, $returny, $y0]; 23297 } else { 23298 return [true, 0, 0, 0, false, false]; 23299 } 23300 } 23301 } 23302 23303 // END OF FUNCTION _tableWrite() 23304 /////////////////////////END OF TABLE CODE////////////////////////////////// 23305 /* -- END TABLES -- */ 23306 23307 function _putextgstates() 23308 { 23309 for ($i = 1; $i <= count($this->extgstates); $i++) { 23310 $this->writer->object(); 23311 $this->extgstates[$i]['n'] = $this->n; 23312 $this->writer->write('<</Type /ExtGState'); 23313 foreach ($this->extgstates[$i]['parms'] as $k => $v) { 23314 $this->writer->write('/' . $k . ' ' . $v); 23315 } 23316 $this->writer->write('>>'); 23317 $this->writer->write('endobj'); 23318 } 23319 } 23320 23321 function SetProtection($permissions = [], $user_pass = '', $owner_pass = null, $length = 40) 23322 { 23323 $this->encrypted = $this->protection->setProtection($permissions, $user_pass, $owner_pass, $length); 23324 } 23325 23326 // ========================================= 23327 // FROM class PDF_Bookmark 23328 function Bookmark($txt, $level = 0, $y = 0) 23329 { 23330 $txt = $this->purify_utf8_text($txt); 23331 if ($this->text_input_as_HTML) { 23332 $txt = $this->all_entities_to_utf8($txt); 23333 } 23334 if ($y == -1) { 23335 if (!$this->ColActive) { 23336 $y = $this->y; 23337 } else { 23338 $y = $this->y0; 23339 } // If columns are on - mark top of columns 23340 } 23341 23342 // else y is used as set, or =0 i.e. top of page 23343 // DIRECTIONALITY RTL 23344 $bmo = ['t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->page]; 23345 23346 if ($this->keep_block_together) { 23347 // do nothing 23348 } elseif ($this->table_rotate) { 23349 $this->tbrot_BMoutlines[] = $bmo; 23350 } elseif ($this->kwt) { 23351 $this->kwt_BMoutlines[] = $bmo; 23352 } elseif ($this->ColActive) { 23353 $this->col_BMoutlines[] = $bmo; 23354 } else { 23355 $this->BMoutlines[] = $bmo; 23356 } 23357 } 23358 23359 /** 23360 * Initiate, and Mark a place for the Table of Contents to be inserted 23361 */ 23362 function TOC( 23363 $tocfont = '', 23364 $tocfontsize = 0, 23365 $tocindent = 0, 23366 $resetpagenum = '', 23367 $pagenumstyle = '', 23368 $suppress = '', 23369 $toc_orientation = '', 23370 $TOCusePaging = true, 23371 $TOCuseLinking = false, 23372 $toc_id = 0, 23373 $tocoutdent = '' 23374 ) { 23375 23376 $this->tableOfContents->TOC( 23377 $tocfont, 23378 $tocfontsize, 23379 $tocindent, 23380 $resetpagenum, 23381 $pagenumstyle, 23382 $suppress, 23383 $toc_orientation, 23384 $TOCusePaging, 23385 $TOCuseLinking, 23386 $toc_id, 23387 $tocoutdent 23388 ); 23389 } 23390 23391 function TOCpagebreakByArray($a) 23392 { 23393 if (!is_array($a)) { 23394 $a = []; 23395 } 23396 $tocoutdent = (isset($a['tocoutdent']) ? $a['tocoutdent'] : (isset($a['outdent']) ? $a['outdent'] : '')); 23397 $TOCusePaging = (isset($a['TOCusePaging']) ? $a['TOCusePaging'] : (isset($a['paging']) ? $a['paging'] : true)); 23398 $TOCuseLinking = (isset($a['TOCuseLinking']) ? $a['TOCuseLinking'] : (isset($a['links']) ? $a['links'] : '')); 23399 $toc_orientation = (isset($a['toc_orientation']) ? $a['toc_orientation'] : (isset($a['toc-orientation']) ? $a['toc-orientation'] : '')); 23400 $toc_mgl = (isset($a['toc_mgl']) ? $a['toc_mgl'] : (isset($a['toc-margin-left']) ? $a['toc-margin-left'] : '')); 23401 $toc_mgr = (isset($a['toc_mgr']) ? $a['toc_mgr'] : (isset($a['toc-margin-right']) ? $a['toc-margin-right'] : '')); 23402 $toc_mgt = (isset($a['toc_mgt']) ? $a['toc_mgt'] : (isset($a['toc-margin-top']) ? $a['toc-margin-top'] : '')); 23403 $toc_mgb = (isset($a['toc_mgb']) ? $a['toc_mgb'] : (isset($a['toc-margin-bottom']) ? $a['toc-margin-bottom'] : '')); 23404 $toc_mgh = (isset($a['toc_mgh']) ? $a['toc_mgh'] : (isset($a['toc-margin-header']) ? $a['toc-margin-header'] : '')); 23405 $toc_mgf = (isset($a['toc_mgf']) ? $a['toc_mgf'] : (isset($a['toc-margin-footer']) ? $a['toc-margin-footer'] : '')); 23406 $toc_ohname = (isset($a['toc_ohname']) ? $a['toc_ohname'] : (isset($a['toc-odd-header-name']) ? $a['toc-odd-header-name'] : '')); 23407 $toc_ehname = (isset($a['toc_ehname']) ? $a['toc_ehname'] : (isset($a['toc-even-header-name']) ? $a['toc-even-header-name'] : '')); 23408 $toc_ofname = (isset($a['toc_ofname']) ? $a['toc_ofname'] : (isset($a['toc-odd-footer-name']) ? $a['toc-odd-footer-name'] : '')); 23409 $toc_efname = (isset($a['toc_efname']) ? $a['toc_efname'] : (isset($a['toc-even-footer-name']) ? $a['toc-even-footer-name'] : '')); 23410 $toc_ohvalue = (isset($a['toc_ohvalue']) ? $a['toc_ohvalue'] : (isset($a['toc-odd-header-value']) ? $a['toc-odd-header-value'] : 0)); 23411 $toc_ehvalue = (isset($a['toc_ehvalue']) ? $a['toc_ehvalue'] : (isset($a['toc-even-header-value']) ? $a['toc-even-header-value'] : 0)); 23412 $toc_ofvalue = (isset($a['toc_ofvalue']) ? $a['toc_ofvalue'] : (isset($a['toc-odd-footer-value']) ? $a['toc-odd-footer-value'] : 0)); 23413 $toc_efvalue = (isset($a['toc_efvalue']) ? $a['toc_efvalue'] : (isset($a['toc-even-footer-value']) ? $a['toc-even-footer-value'] : 0)); 23414 $toc_preHTML = (isset($a['toc_preHTML']) ? $a['toc_preHTML'] : (isset($a['toc-preHTML']) ? $a['toc-preHTML'] : '')); 23415 $toc_postHTML = (isset($a['toc_postHTML']) ? $a['toc_postHTML'] : (isset($a['toc-postHTML']) ? $a['toc-postHTML'] : '')); 23416 $toc_bookmarkText = (isset($a['toc_bookmarkText']) ? $a['toc_bookmarkText'] : (isset($a['toc-bookmarkText']) ? $a['toc-bookmarkText'] : '')); 23417 $resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : ''); 23418 $pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : ''); 23419 $suppress = (isset($a['suppress']) ? $a['suppress'] : ''); 23420 $orientation = (isset($a['orientation']) ? $a['orientation'] : ''); 23421 $mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : '')); 23422 $mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : '')); 23423 $mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : '')); 23424 $mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : '')); 23425 $mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : '')); 23426 $mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : '')); 23427 $ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : '')); 23428 $ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : '')); 23429 $ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : '')); 23430 $efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : '')); 23431 $ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0)); 23432 $ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0)); 23433 $ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0)); 23434 $efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0)); 23435 $toc_id = (isset($a['toc_id']) ? $a['toc_id'] : (isset($a['name']) ? $a['name'] : 0)); 23436 $pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : '')); 23437 $toc_pagesel = (isset($a['toc_pagesel']) ? $a['toc_pagesel'] : (isset($a['toc-pageselector']) ? $a['toc-pageselector'] : '')); 23438 $sheetsize = (isset($a['sheetsize']) ? $a['sheetsize'] : (isset($a['sheet-size']) ? $a['sheet-size'] : '')); 23439 $toc_sheetsize = (isset($a['toc_sheetsize']) ? $a['toc_sheetsize'] : (isset($a['toc-sheet-size']) ? $a['toc-sheet-size'] : '')); 23440 23441 $this->TOCpagebreak('', '', '', $TOCusePaging, $TOCuseLinking, $toc_orientation, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_preHTML, $toc_postHTML, $toc_bookmarkText, $resetpagenum, $pagenumstyle, $suppress, $orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $toc_id, $pagesel, $toc_pagesel, $sheetsize, $toc_sheetsize, $tocoutdent); 23442 } 23443 23444 function TOCpagebreak($tocfont = '', $tocfontsize = '', $tocindent = '', $TOCusePaging = true, $TOCuseLinking = '', $toc_orientation = '', $toc_mgl = '', $toc_mgr = '', $toc_mgt = '', $toc_mgb = '', $toc_mgh = '', $toc_mgf = '', $toc_ohname = '', $toc_ehname = '', $toc_ofname = '', $toc_efname = '', $toc_ohvalue = 0, $toc_ehvalue = 0, $toc_ofvalue = 0, $toc_efvalue = 0, $toc_preHTML = '', $toc_postHTML = '', $toc_bookmarkText = '', $resetpagenum = '', $pagenumstyle = '', $suppress = '', $orientation = '', $mgl = '', $mgr = '', $mgt = '', $mgb = '', $mgh = '', $mgf = '', $ohname = '', $ehname = '', $ofname = '', $efname = '', $ohvalue = 0, $ehvalue = 0, $ofvalue = 0, $efvalue = 0, $toc_id = 0, $pagesel = '', $toc_pagesel = '', $sheetsize = '', $toc_sheetsize = '', $tocoutdent = '') 23445 { 23446 // Start a new page 23447 if ($this->state == 0) { 23448 $this->AddPage(); 23449 } 23450 if ($this->y == $this->tMargin && (!$this->mirrorMargins || ($this->mirrorMargins && $this->page % 2 == 1))) { 23451 // Don't add a page 23452 if ($this->page == 1 && count($this->PageNumSubstitutions) == 0) { 23453 if (!$suppress) { 23454 $suppress = 'off'; 23455 } 23456 // $this->PageNumSubstitutions[] = array('from'=>1, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=> $suppress); 23457 } 23458 $this->PageNumSubstitutions[] = ['from' => $this->page, 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress]; 23459 } else { 23460 $this->AddPage($orientation, 'NEXT-ODD', $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $sheetsize); 23461 } 23462 $this->tableOfContents->TOCpagebreak($tocfont, $tocfontsize, $tocindent, $TOCusePaging, $TOCuseLinking, $toc_orientation, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_preHTML, $toc_postHTML, $toc_bookmarkText, $resetpagenum, $pagenumstyle, $suppress, $orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $toc_id, $pagesel, $toc_pagesel, $sheetsize, $toc_sheetsize, $tocoutdent); 23463 } 23464 23465 function TOC_Entry($txt, $level = 0, $toc_id = 0) 23466 { 23467 if ($this->ColActive) { 23468 $ily = $this->y0; 23469 } else { 23470 $ily = $this->y; 23471 } // use top of columns 23472 23473 $linkn = $this->AddLink(); 23474 $uid = '__mpdfinternallink_' . $linkn; 23475 if ($this->table_rotate) { 23476 $this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "tbrot" => true]; 23477 } elseif ($this->kwt) { 23478 $this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "kwt" => true]; 23479 } elseif ($this->ColActive) { 23480 $this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol]; 23481 } elseif (!$this->keep_block_together) { 23482 $this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page]; 23483 } 23484 $this->internallink['#' . $uid] = $linkn; 23485 $this->SetLink($linkn, $ily, $this->page); 23486 23487 if (strtoupper($toc_id) == 'ALL') { 23488 $toc_id = '_mpdf_all'; 23489 } elseif (!$toc_id) { 23490 $toc_id = 0; 23491 } else { 23492 $toc_id = strtolower($toc_id); 23493 } 23494 $btoc = ['t' => $txt, 'l' => $level, 'p' => $this->page, 'link' => $linkn, 'toc_id' => $toc_id]; 23495 if ($this->keep_block_together) { 23496 // do nothing 23497 } /* -- TABLES -- */ elseif ($this->table_rotate) { 23498 $this->tbrot_toc[] = $btoc; 23499 } elseif ($this->kwt) { 23500 $this->kwt_toc[] = $btoc; 23501 } /* -- END TABLES -- */ elseif ($this->ColActive) { // *COLUMNS* 23502 $this->col_toc[] = $btoc; // *COLUMNS* 23503 } // *COLUMNS* 23504 else { 23505 $this->tableOfContents->_toc[] = $btoc; 23506 } 23507 } 23508 23509 /* -- END TOC -- */ 23510 23511 // ====================================================== 23512 function MovePages($target_page, $start_page, $end_page = -1) 23513 { 23514 // move a page/pages EARLIER in the document 23515 if ($end_page < 1) { 23516 $end_page = $start_page; 23517 } 23518 $n_toc = $end_page - $start_page + 1; 23519 23520 // Set/Update PageNumSubstitutions changes before moving anything 23521 if (count($this->PageNumSubstitutions)) { 23522 $tp_present = false; 23523 $sp_present = false; 23524 $ep_present = false; 23525 foreach ($this->PageNumSubstitutions as $k => $v) { 23526 if ($this->PageNumSubstitutions[$k]['from'] == $target_page) { 23527 $tp_present = true; 23528 if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) { 23529 $this->PageNumSubstitutions[$k]['suppress'] = 'off'; 23530 } 23531 } 23532 if ($this->PageNumSubstitutions[$k]['from'] == $start_page) { 23533 $sp_present = true; 23534 if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) { 23535 $this->PageNumSubstitutions[$k]['suppress'] = 'off'; 23536 } 23537 } 23538 if ($this->PageNumSubstitutions[$k]['from'] == ($end_page + 1)) { 23539 $ep_present = true; 23540 if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) { 23541 $this->PageNumSubstitutions[$k]['suppress'] = 'off'; 23542 } 23543 } 23544 } 23545 23546 if (!$tp_present) { 23547 list($tp_type, $tp_suppress, $tp_reset) = $this->docPageSettings($target_page); 23548 } 23549 if (!$sp_present) { 23550 list($sp_type, $sp_suppress, $sp_reset) = $this->docPageSettings($start_page); 23551 } 23552 if (!$ep_present) { 23553 list($ep_type, $ep_suppress, $ep_reset) = $this->docPageSettings($start_page - 1); 23554 } 23555 } 23556 23557 $last = []; 23558 // store pages 23559 for ($i = $start_page; $i <= $end_page; $i++) { 23560 $last[] = $this->pages[$i]; 23561 } 23562 // move pages 23563 for ($i = $start_page - 1; $i >= ($target_page); $i--) { 23564 $this->pages[$i + $n_toc] = $this->pages[$i]; 23565 } 23566 // Put toc pages at insert point 23567 for ($i = 0; $i < $n_toc; $i++) { 23568 $this->pages[$target_page + $i] = $last[$i]; 23569 } 23570 23571 /* -- BOOKMARKS -- */ 23572 // Update Bookmarks 23573 foreach ($this->BMoutlines as $i => $o) { 23574 if ($o['p'] >= $target_page) { 23575 $this->BMoutlines[$i]['p'] += $n_toc; 23576 } 23577 } 23578 /* -- END BOOKMARKS -- */ 23579 23580 // Update Page Links 23581 if (count($this->PageLinks)) { 23582 $newarr = []; 23583 foreach ($this->PageLinks as $i => $o) { 23584 foreach ($this->PageLinks[$i] as $key => $pl) { 23585 if (strpos($pl[4], '@') === 0) { 23586 $p = substr($pl[4], 1); 23587 if ($p >= $start_page && $p <= $end_page) { 23588 $this->PageLinks[$i][$key][4] = '@' . ($p + ($target_page - $start_page)); 23589 } elseif ($p >= $target_page && $p < $start_page) { 23590 $this->PageLinks[$i][$key][4] = '@' . ($p + $n_toc); 23591 } 23592 } 23593 } 23594 if ($i >= $start_page && $i <= $end_page) { 23595 $newarr[($i + ($target_page - $start_page))] = $this->PageLinks[$i]; 23596 } elseif ($i >= $target_page && $i < $start_page) { 23597 $newarr[($i + $n_toc)] = $this->PageLinks[$i]; 23598 } else { 23599 $newarr[$i] = $this->PageLinks[$i]; 23600 } 23601 } 23602 $this->PageLinks = $newarr; 23603 } 23604 23605 // OrientationChanges 23606 if (count($this->OrientationChanges)) { 23607 $newarr = []; 23608 foreach ($this->OrientationChanges as $p => $v) { 23609 if ($p >= $start_page && $p <= $end_page) { 23610 $newarr[($p + ($target_page - $start_page))] = $this->OrientationChanges[$p]; 23611 } elseif ($p >= $target_page && $p < $start_page) { 23612 $newarr[$p + $n_toc] = $this->OrientationChanges[$p]; 23613 } else { 23614 $newarr[$p] = $this->OrientationChanges[$p]; 23615 } 23616 } 23617 ksort($newarr); 23618 $this->OrientationChanges = $newarr; 23619 } 23620 23621 // Page Dimensions 23622 if (count($this->pageDim)) { 23623 $newarr = []; 23624 foreach ($this->pageDim as $p => $v) { 23625 if ($p >= $start_page && $p <= $end_page) { 23626 $newarr[($p + ($target_page - $start_page))] = $this->pageDim[$p]; 23627 } elseif ($p >= $target_page && $p < $start_page) { 23628 $newarr[$p + $n_toc] = $this->pageDim[$p]; 23629 } else { 23630 $newarr[$p] = $this->pageDim[$p]; 23631 } 23632 } 23633 ksort($newarr); 23634 $this->pageDim = $newarr; 23635 } 23636 23637 // HTML Headers & Footers 23638 if (count($this->saveHTMLHeader)) { 23639 $newarr = []; 23640 foreach ($this->saveHTMLHeader as $p => $v) { 23641 if ($p >= $start_page && $p <= $end_page) { 23642 $newarr[($p + ($target_page - $start_page))] = $this->saveHTMLHeader[$p]; 23643 } elseif ($p >= $target_page && $p < $start_page) { 23644 $newarr[$p + $n_toc] = $this->saveHTMLHeader[$p]; 23645 } else { 23646 $newarr[$p] = $this->saveHTMLHeader[$p]; 23647 } 23648 } 23649 ksort($newarr); 23650 $this->saveHTMLHeader = $newarr; 23651 } 23652 if (count($this->saveHTMLFooter)) { 23653 $newarr = []; 23654 foreach ($this->saveHTMLFooter as $p => $v) { 23655 if ($p >= $start_page && $p <= $end_page) { 23656 $newarr[($p + ($target_page - $start_page))] = $this->saveHTMLFooter[$p]; 23657 } elseif ($p >= $target_page && $p < $start_page) { 23658 $newarr[$p + $n_toc] = $this->saveHTMLFooter[$p]; 23659 } else { 23660 $newarr[$p] = $this->saveHTMLFooter[$p]; 23661 } 23662 } 23663 ksort($newarr); 23664 $this->saveHTMLFooter = $newarr; 23665 } 23666 23667 // Update Internal Links 23668 if (count($this->internallink)) { 23669 foreach ($this->internallink as $key => $o) { 23670 if (is_array($o) && $o['PAGE'] >= $start_page && $o['PAGE'] <= $end_page) { 23671 $this->internallink[$key]['PAGE'] += ($target_page - $start_page); 23672 } elseif (is_array($o) && $o['PAGE'] >= $target_page && $o['PAGE'] < $start_page) { 23673 $this->internallink[$key]['PAGE'] += $n_toc; 23674 } 23675 } 23676 } 23677 23678 // Update Links 23679 if (count($this->links)) { 23680 foreach ($this->links as $key => $o) { 23681 if ($o[0] >= $start_page && $o[0] <= $end_page) { 23682 $this->links[$key][0] += ($target_page - $start_page); 23683 } 23684 if ($o[0] >= $target_page && $o[0] < $start_page) { 23685 $this->links[$key][0] += $n_toc; 23686 } 23687 } 23688 } 23689 23690 // Update Form fields 23691 if (count($this->form->forms)) { 23692 foreach ($this->form->forms as $key => $f) { 23693 if ($f['page'] >= $start_page && $f['page'] <= $end_page) { 23694 $this->form->forms[$key]['page'] += ($target_page - $start_page); 23695 } 23696 if ($f['page'] >= $target_page && $f['page'] < $start_page) { 23697 $this->form->forms[$key]['page'] += $n_toc; 23698 } 23699 } 23700 } 23701 23702 /* -- ANNOTATIONS -- */ 23703 // Update Annotations 23704 if (count($this->PageAnnots)) { 23705 $newarr = []; 23706 foreach ($this->PageAnnots as $p => $anno) { 23707 if ($p >= $start_page && $p <= $end_page) { 23708 $np = $p + ($target_page - $start_page); 23709 foreach ($anno as $o) { 23710 $newarr[$np][] = $o; 23711 } 23712 } elseif ($p >= $target_page && $p < $start_page) { 23713 $np = $p + $n_toc; 23714 foreach ($anno as $o) { 23715 $newarr[$np][] = $o; 23716 } 23717 } else { 23718 $newarr[$p] = $this->PageAnnots[$p]; 23719 } 23720 } 23721 $this->PageAnnots = $newarr; 23722 unset($newarr); 23723 } 23724 /* -- END ANNOTATIONS -- */ 23725 23726 // Update TOC pages 23727 if (count($this->tableOfContents->_toc)) { 23728 foreach ($this->tableOfContents->_toc as $key => $t) { 23729 if ($t['p'] >= $start_page && $t['p'] <= $end_page) { 23730 $this->tableOfContents->_toc[$key]['p'] += ($target_page - $start_page); 23731 } 23732 if ($t['p'] >= $target_page && $t['p'] < $start_page) { 23733 $this->tableOfContents->_toc[$key]['p'] += $n_toc; 23734 } 23735 } 23736 } 23737 23738 // Update PageNumSubstitutions 23739 if (count($this->PageNumSubstitutions)) { 23740 $newarr = []; 23741 foreach ($this->PageNumSubstitutions as $k => $v) { 23742 if ($this->PageNumSubstitutions[$k]['from'] >= $start_page && $this->PageNumSubstitutions[$k]['from'] <= $end_page) { 23743 $this->PageNumSubstitutions[$k]['from'] += ($target_page - $start_page); 23744 $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k]; 23745 } elseif ($this->PageNumSubstitutions[$k]['from'] >= $target_page && $this->PageNumSubstitutions[$k]['from'] < $start_page) { 23746 $this->PageNumSubstitutions[$k]['from'] += $n_toc; 23747 $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k]; 23748 } else { 23749 $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k]; 23750 } 23751 } 23752 23753 if (!$sp_present) { 23754 $newarr[$target_page] = ['from' => $target_page, 'suppress' => $sp_suppress, 'reset' => $sp_reset, 'type' => $sp_type]; 23755 } 23756 if (!$tp_present) { 23757 $newarr[($target_page + $n_toc)] = ['from' => ($target_page + $n_toc), 'suppress' => $tp_suppress, 'reset' => $tp_reset, 'type' => $tp_type]; 23758 } 23759 if (!$ep_present && $end_page > count($this->pages)) { 23760 $newarr[($end_page + 1)] = ['from' => ($end_page + 1), 'suppress' => $ep_suppress, 'reset' => $ep_reset, 'type' => $ep_type]; 23761 } 23762 ksort($newarr); 23763 $this->PageNumSubstitutions = []; 23764 foreach ($newarr as $v) { 23765 $this->PageNumSubstitutions[] = $v; 23766 } 23767 } 23768 } 23769 23770 function DeletePages($start_page, $end_page = -1) 23771 { 23772 // move a page/pages EARLIER in the document 23773 if ($end_page < 1) { 23774 $end_page = $start_page; 23775 } 23776 $n_tod = $end_page - $start_page + 1; 23777 $last_page = count($this->pages); 23778 $n_atend = $last_page - $end_page + 1; 23779 23780 // move pages 23781 for ($i = 0; $i < $n_atend; $i++) { 23782 $this->pages[$start_page + $i] = $this->pages[$end_page + 1 + $i]; 23783 } 23784 // delete pages 23785 for ($i = 0; $i < $n_tod; $i++) { 23786 unset($this->pages[$last_page - $i]); 23787 } 23788 23789 23790 /* -- BOOKMARKS -- */ 23791 // Update Bookmarks 23792 foreach ($this->BMoutlines as $i => $o) { 23793 if ($o['p'] >= $end_page) { 23794 $this->BMoutlines[$i]['p'] -= $n_tod; 23795 } elseif ($p < $start_page) { 23796 unset($this->BMoutlines[$i]); 23797 } 23798 } 23799 /* -- END BOOKMARKS -- */ 23800 23801 // Update Page Links 23802 if (count($this->PageLinks)) { 23803 $newarr = []; 23804 foreach ($this->PageLinks as $i => $o) { 23805 foreach ($this->PageLinks[$i] as $key => $pl) { 23806 if (strpos($pl[4], '@') === 0) { 23807 $p = substr($pl[4], 1); 23808 if ($p > $end_page) { 23809 $this->PageLinks[$i][$key][4] = '@' . ($p - $n_tod); 23810 } elseif ($p < $start_page) { 23811 unset($this->PageLinks[$i][$key]); 23812 } 23813 } 23814 } 23815 if ($i > $end_page) { 23816 $newarr[($i - $n_tod)] = $this->PageLinks[$i]; 23817 } elseif ($p < $start_page) { 23818 $newarr[$i] = $this->PageLinks[$i]; 23819 } 23820 } 23821 $this->PageLinks = $newarr; 23822 } 23823 23824 // OrientationChanges 23825 if (count($this->OrientationChanges)) { 23826 $newarr = []; 23827 foreach ($this->OrientationChanges as $p => $v) { 23828 if ($p > $end_page) { 23829 $newarr[($p - $t_tod)] = $this->OrientationChanges[$p]; 23830 } elseif ($p < $start_page) { 23831 $newarr[$p] = $this->OrientationChanges[$p]; 23832 } 23833 } 23834 ksort($newarr); 23835 $this->OrientationChanges = $newarr; 23836 } 23837 23838 // Page Dimensions 23839 if (count($this->pageDim)) { 23840 $newarr = []; 23841 foreach ($this->pageDim as $p => $v) { 23842 if ($p > $end_page) { 23843 $newarr[($p - $n_tod)] = $this->pageDim[$p]; 23844 } elseif ($p < $start_page) { 23845 $newarr[$p] = $this->pageDim[$p]; 23846 } 23847 } 23848 ksort($newarr); 23849 $this->pageDim = $newarr; 23850 } 23851 23852 // HTML Headers & Footers 23853 if (count($this->saveHTMLHeader)) { 23854 foreach ($this->saveHTMLHeader as $p => $v) { 23855 if ($p > $end_page) { 23856 $newarr[($p - $n_tod)] = $this->saveHTMLHeader[$p]; 23857 } // mPDF 5.7.3 23858 elseif ($p < $start_page) { 23859 $newarr[$p] = $this->saveHTMLHeader[$p]; 23860 } 23861 } 23862 ksort($newarr); 23863 $this->saveHTMLHeader = $newarr; 23864 } 23865 if (count($this->saveHTMLFooter)) { 23866 $newarr = []; 23867 foreach ($this->saveHTMLFooter as $p => $v) { 23868 if ($p > $end_page) { 23869 $newarr[($p - $n_tod)] = $this->saveHTMLFooter[$p]; 23870 } elseif ($p < $start_page) { 23871 $newarr[$p] = $this->saveHTMLFooter[$p]; 23872 } 23873 } 23874 ksort($newarr); 23875 $this->saveHTMLFooter = $newarr; 23876 } 23877 23878 // Update Internal Links 23879 foreach ($this->internallink as $key => $o) { 23880 if ($o['PAGE'] > $end_page) { 23881 $this->internallink[$key]['PAGE'] -= $n_tod; 23882 } elseif ($o['PAGE'] < $start_page) { 23883 unset($this->internallink[$key]); 23884 } 23885 } 23886 23887 // Update Links 23888 foreach ($this->links as $key => $o) { 23889 if ($o[0] > $end_page) { 23890 $this->links[$key][0] -= $n_tod; 23891 } elseif ($o[0] < $start_page) { 23892 unset($this->links[$key]); 23893 } 23894 } 23895 23896 // Update Form fields 23897 foreach ($this->form->forms as $key => $f) { 23898 if ($f['page'] > $end_page) { 23899 $this->form->forms[$key]['page'] -= $n_tod; 23900 } elseif ($f['page'] < $start_page) { 23901 unset($this->form->forms[$key]); 23902 } 23903 } 23904 23905 /* -- ANNOTATIONS -- */ 23906 // Update Annotations 23907 if (count($this->PageAnnots)) { 23908 $newarr = []; 23909 foreach ($this->PageAnnots as $p => $anno) { 23910 if ($p > $end_page) { 23911 foreach ($anno as $o) { 23912 $newarr[($p - $n_tod)][] = $o; 23913 } 23914 } elseif ($p < $start_page) { 23915 $newarr[$p] = $this->PageAnnots[$p]; 23916 } 23917 } 23918 ksort($newarr); 23919 $this->PageAnnots = $newarr; 23920 } 23921 /* -- END ANNOTATIONS -- */ 23922 23923 // Update PageNumSubstitutions 23924 foreach ($this->PageNumSubstitutions as $k => $v) { 23925 if ($this->PageNumSubstitutions[$k]['from'] > $end_page) { 23926 $this->PageNumSubstitutions[$k]['from'] -= $n_tod; 23927 } elseif ($this->PageNumSubstitutions[$k]['from'] < $start_page) { 23928 unset($this->PageNumSubstitutions[$k]); 23929 } 23930 } 23931 23932 unset($newarr); 23933 $this->page = count($this->pages); 23934 } 23935 23936 // ====================================================== 23937 /* -- INDEX -- */ 23938 // FROM class PDF_Ref == INDEX 23939 23940 function IndexEntry($txt, $xref = '') 23941 { 23942 if ($xref) { 23943 $this->IndexEntrySee($txt, $xref); 23944 return; 23945 } 23946 23947 // Search the reference (AND Ref/PageNo) in the array 23948 $Present = false; 23949 if ($this->keep_block_together) { 23950 // do nothing 23951 } /* -- TABLES -- */ elseif ($this->kwt) { 23952 $size = count($this->kwt_Reference); 23953 for ($i = 0; $i < $size; $i++) { 23954 if (isset($this->kwt_Reference[$i]['t']) && $this->kwt_Reference[$i]['t'] == $txt) { 23955 $Present = true; 23956 if ($this->page != $this->kwt_Reference[$i]['op']) { 23957 $this->kwt_Reference[$i]['op'] = $this->page; 23958 } 23959 } 23960 } 23961 if (!$Present) { // If not found, add it 23962 $this->kwt_Reference[] = ['t' => $txt, 'op' => $this->page]; 23963 } 23964 } /* -- END TABLES -- */ else { 23965 $size = count($this->Reference); 23966 for ($i = 0; $i < $size; $i++) { 23967 if (isset($this->Reference[$i]['t']) && $this->Reference[$i]['t'] == $txt) { 23968 $Present = true; 23969 if (!in_array($this->page, $this->Reference[$i]['p'])) { 23970 $this->Reference[$i]['p'][] = $this->page; 23971 } 23972 } 23973 } 23974 if (!$Present) { // If not found, add it 23975 $this->Reference[] = ['t' => $txt, 'p' => [$this->page]]; 23976 } 23977 } 23978 } 23979 23980 // Added function to add a reference "Elephants. See Chickens" 23981 function IndexEntrySee($txta, $txtb) 23982 { 23983 if ($this->directionality == 'rtl') { // *OTL* 23984 // ONLY DO THIS IF NOT IN TAGS 23985 if ($txta == strip_tags($txta)) { 23986 $txta = str_replace(':', ' - ', $txta); // *OTL* 23987 } 23988 if ($txtb == strip_tags($txtb)) { 23989 $txtb = str_replace(':', ' - ', $txtb); // *OTL* 23990 } 23991 } // *OTL* 23992 else { // *OTL* 23993 if ($txta == strip_tags($txta)) { 23994 $txta = str_replace(':', ', ', $txta); 23995 } 23996 if ($txtb == strip_tags($txtb)) { 23997 $txtb = str_replace(':', ', ', $txtb); 23998 } 23999 } // *OTL* 24000 $this->Reference[] = ['t' => $txta . ' - see ' . $txtb, 'p' => []]; 24001 } 24002 24003 private function filesInDir($directory) 24004 { 24005 $files = []; 24006 foreach ((new \DirectoryIterator($directory)) as $v) { 24007 if ($v->isDir() || $v->isDot()) { 24008 continue; 24009 } 24010 24011 $files[] = $v->getPathname(); 24012 } 24013 24014 return $files; 24015 } 24016 24017 function InsertIndex($usedivletters = 1, $useLinking = false, $indexCollationLocale = '', $indexCollationGroup = '') 24018 { 24019 $size = count($this->Reference); 24020 if ($size == 0) { 24021 return false; 24022 } 24023 24024 // $spacer used after named entry 24025 // $sep separates number [groups], $joiner joins numbers in range 24026 // e.g. "elephant 73, 97-99" = elephant[$spacer]73[$sep]97[$joiner]99 24027 // $subEntrySeparator separates main and subentry (if $this->indexUseSubentries == false;) e.g. 24028 // Mammal:elephant => Mammal[$subEntrySeparator]elephant 24029 // $subEntryInset specifies what precedes a subentry (if $this->indexUseSubentries == true;) e.g. 24030 // Mammal:elephant => [$subEntryInset]elephant 24031 $spacer = "\xc2\xa0 "; 24032 if ($this->directionality == 'rtl') { 24033 $sep = '، '; 24034 $joiner = '-'; 24035 $subEntrySeparator = '، '; 24036 $subEntryInset = ' - '; 24037 } else { 24038 $sep = ', '; 24039 $joiner = '-'; 24040 $subEntrySeparator = ', '; 24041 $subEntryInset = ' - '; 24042 } 24043 24044 for ($i = 0; $i < $size; $i++) { 24045 $txt = $this->Reference[$i]['t']; 24046 $txt = strip_tags($txt); // mPDF 6 24047 $txt = $this->purify_utf8($txt); 24048 $this->Reference[$i]['uf'] = $txt; // Unformatted e.g. pure utf-8 encoded characters, no mark-up/tags 24049 // Used for ordering and collation 24050 } 24051 24052 if ($usedivletters) { 24053 if ($indexCollationGroup && \in_array(strtolower($indexCollationGroup), array_map(function ($v) { 24054 return strtolower(basename($v, '.php')); 24055 }, $this->filesInDir(__DIR__ . '/../data/collations/')))) { 24056 $collation = require __DIR__ . '/../data/collations/' . $indexCollationGroup . '.php'; 24057 } else { 24058 $collation = []; 24059 } 24060 for ($i = 0; $i < $size; $i++) { 24061 if ($this->Reference[$i]['uf']) { 24062 $l = mb_substr($this->Reference[$i]['uf'], 0, 1, 'UTF-8'); 24063 if (isset($indexCollationGroup) && $indexCollationGroup) { 24064 $uni = $this->UTF8StringToArray($l); 24065 $ucode = $uni[0]; 24066 if (isset($collation[$ucode])) { 24067 $this->Reference[$i]['d'] = UtfString::code2utf($collation[$ucode]); 24068 } else { 24069 $this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8'); 24070 } 24071 } else { 24072 $this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8'); 24073 } 24074 } 24075 } 24076 } 24077 24078 // Alphabetic sort of the references 24079 $originalLocale = setlocale(LC_COLLATE, 0); 24080 if ($indexCollationLocale) { 24081 setlocale(LC_COLLATE, $indexCollationLocale); 24082 } 24083 24084 usort($this->Reference, function ($a, $b) { 24085 return strcoll(strtolower($a['uf']), strtolower($b['uf'])); 24086 }); 24087 24088 if ($indexCollationLocale) { 24089 setlocale(LC_COLLATE, $originalLocale); 24090 } 24091 24092 $html = '<div class="mpdf_index_main">'; 24093 24094 $lett = ''; 24095 $last_lett = ''; 24096 $mainentry = ''; 24097 for ($i = 0; $i < $size; $i++) { 24098 if ($this->Reference[$i]['t']) { 24099 if ($usedivletters) { 24100 $lett = $this->Reference[$i]['d']; 24101 if ($lett != $last_lett) { 24102 $html .= '<div class="mpdf_index_letter">' . $lett . '</div>'; 24103 } 24104 } 24105 $txt = $this->Reference[$i]['t']; 24106 24107 // Sub-entries e.g. Mammals:elephant 24108 // But allow for tags e.g. <b>Mammal</b>:elephants 24109 $a = preg_split('/(<.*?>)/', $txt, -1, PREG_SPLIT_DELIM_CAPTURE); 24110 $txt = ''; 24111 $marker = false; 24112 foreach ($a as $k => $e) { 24113 if ($k % 2 == 0 && !$marker) { 24114 if (strpos($e, ':') !== false) { // == SubEntry 24115 if ($this->indexUseSubentries) { 24116 // If the Main entry does not have any page numbers associated with it 24117 // create and insert an entry 24118 list($txtmain, $sub) = preg_split('/[:]/', $e, 2); 24119 if (strip_tags($txt . $txtmain) != $mainentry) { 24120 $html .= '<div class="mpdf_index_entry">' . $txt . $txtmain . '</div>'; 24121 $mainentry = strip_tags($txt . $txtmain); 24122 } 24123 24124 $txt = $subEntryInset; 24125 $e = $sub; // Only replace first one 24126 } else { 24127 $e = preg_replace('/[:]/', $subEntrySeparator, $e, 1); // Only replace first one 24128 } 24129 $marker = true; // Don't replace any more once the subentry marker has been found 24130 } 24131 } 24132 $txt .= $e; 24133 } 24134 24135 if (!$marker) { 24136 $mainentry = strip_tags($txt); 24137 } 24138 24139 $html .= '<div class="mpdf_index_entry">'; 24140 $html .= $txt; 24141 $ppp = $this->Reference[$i]['p']; // = array of page numbers to point to 24142 if (count($ppp)) { 24143 sort($ppp); 24144 $newarr = []; 24145 $range_start = $ppp[0]; 24146 $range_end = 0; 24147 24148 $html .= $spacer; 24149 24150 for ($zi = 1; $zi < count($ppp); $zi++) { 24151 if ($ppp[$zi] == ($ppp[($zi - 1)] + 1)) { 24152 $range_end = $ppp[$zi]; 24153 } else { 24154 if ($range_end) { 24155 if ($range_end == $range_start + 1) { 24156 if ($useLinking) { 24157 $html .= '<a class="mpdf_index_link" href="@' . $range_start . '">'; 24158 } 24159 $html .= $this->docPageNum($range_start); 24160 if ($useLinking) { 24161 $html .= '</a>'; 24162 } 24163 $html .= $sep; 24164 24165 if ($useLinking) { 24166 $html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">'; 24167 } 24168 $html .= $this->docPageNum($ppp[$zi - 1]); 24169 if ($useLinking) { 24170 $html .= '</a>'; 24171 } 24172 $html .= $sep; 24173 } 24174 } else { 24175 if ($useLinking) { 24176 $html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">'; 24177 } 24178 $html .= $this->docPageNum($ppp[$zi - 1]); 24179 if ($useLinking) { 24180 $html .= '</a>'; 24181 } 24182 $html .= $sep; 24183 } 24184 $range_start = $ppp[$zi]; 24185 $range_end = 0; 24186 } 24187 } 24188 24189 if ($range_end) { 24190 if ($useLinking) { 24191 $html .= '<a class="mpdf_index_link" href="@' . $range_start . '">'; 24192 } 24193 $html .= $this->docPageNum($range_start); 24194 if ($range_end == $range_start + 1) { 24195 if ($useLinking) { 24196 $html .= '</a>'; 24197 } 24198 $html .= $sep; 24199 if ($useLinking) { 24200 $html .= '<a class="mpdf_index_link" href="@' . $range_end . '">'; 24201 } 24202 $html .= $this->docPageNum($range_end); 24203 if ($useLinking) { 24204 $html .= '</a>'; 24205 } 24206 } else { 24207 $html .= $joiner; 24208 $html .= $this->docPageNum($range_end); 24209 if ($useLinking) { 24210 $html .= '</a>'; 24211 } 24212 } 24213 } else { 24214 if ($useLinking) { 24215 $html .= '<a class="mpdf_index_link" href="@' . $ppp[(count($ppp) - 1)] . '">'; 24216 } 24217 $html .= $this->docPageNum($ppp[(count($ppp) - 1)]); 24218 if ($useLinking) { 24219 $html .= '</a>'; 24220 } 24221 } 24222 } 24223 } 24224 $html .= '</div>'; 24225 $last_lett = $lett; 24226 } 24227 $html .= '</div>'; 24228 $save_fpb = $this->fixedPosBlockSave; 24229 $this->WriteHTML($html); 24230 $this->fixedPosBlockSave = $save_fpb; 24231 24232 $this->breakpoints[$this->CurrCol][] = $this->y; // *COLUMNS* 24233 } 24234 24235 /* -- END INDEX -- */ 24236 24237 function AcceptPageBreak() 24238 { 24239 if (count($this->cellBorderBuffer)) { 24240 $this->printcellbuffer(); 24241 } // *TABLES* 24242 /* -- COLUMNS -- */ 24243 if ($this->ColActive == 1) { 24244 if ($this->CurrCol < $this->NbCol - 1) { 24245 // Go to the next column 24246 $this->CurrCol++; 24247 $this->SetCol($this->CurrCol); 24248 $this->y = $this->y0; 24249 $this->ChangeColumn = 1; // Number (and direction) of columns changed +1, +2, -2 etc. 24250 // DIRECTIONALITY RTL 24251 if ($this->directionality == 'rtl') { 24252 $this->ChangeColumn = -($this->ChangeColumn); 24253 } // *OTL* 24254 // Stay on the page 24255 return false; 24256 } else { 24257 // Go back to the first column - NEW PAGE 24258 if (count($this->columnbuffer)) { 24259 $this->printcolumnbuffer(); 24260 } 24261 $this->SetCol(0); 24262 $this->y0 = $this->tMargin; 24263 $this->ChangeColumn = -($this->NbCol - 1); 24264 // DIRECTIONALITY RTL 24265 if ($this->directionality == 'rtl') { 24266 $this->ChangeColumn = -($this->ChangeColumn); 24267 } // *OTL* 24268 // Page break 24269 return true; 24270 } 24271 } /* -- END COLUMNS -- */ 24272 /* -- TABLES -- */ elseif ($this->table_rotate) { 24273 if ($this->tablebuffer) { 24274 $this->printtablebuffer(); 24275 } 24276 return true; 24277 } /* -- END TABLES -- */ else { // *COLUMNS* 24278 $this->ChangeColumn = 0; 24279 return $this->autoPageBreak; 24280 } // *COLUMNS* 24281 return $this->autoPageBreak; 24282 } 24283 24284 // ----------- COLUMNS --------------------- 24285 /* -- COLUMNS -- */ 24286 24287 function SetColumns($NbCol, $vAlign = '', $gap = 5) 24288 { 24289 // NbCol = number of columns 24290 // Anything less than 2 turns columns off 24291 if ($NbCol < 2) { // SET COLUMNS OFF 24292 if ($this->ColActive) { 24293 $this->ColActive = 0; 24294 if (count($this->columnbuffer)) { 24295 $this->printcolumnbuffer(); 24296 } 24297 $this->NbCol = 1; 24298 $this->ResetMargins(); 24299 $this->pgwidth = $this->w - $this->lMargin - $this->rMargin; 24300 $this->divwidth = 0; 24301 $this->Ln(); 24302 } 24303 $this->ColActive = 0; 24304 $this->columnbuffer = []; 24305 $this->ColDetails = []; 24306 $this->columnLinks = []; 24307 $this->columnAnnots = []; 24308 $this->columnForms = []; 24309 $this->col_BMoutlines = []; 24310 $this->col_toc = []; 24311 $this->breakpoints = []; 24312 } else { // SET COLUMNS ON 24313 if ($this->ColActive) { 24314 $this->ColActive = 0; 24315 if (count($this->columnbuffer)) { 24316 $this->printcolumnbuffer(); 24317 } 24318 $this->ResetMargins(); 24319 } 24320 if (isset($this->y) && $this->y > $this->tMargin) { 24321 $this->Ln(); 24322 } 24323 $this->NbCol = $NbCol; 24324 $this->ColGap = $gap; 24325 $this->divwidth = 0; 24326 $this->ColActive = 1; 24327 $this->ColumnAdjust = true; // enables column height adjustment for the page 24328 $this->columnbuffer = []; 24329 $this->ColDetails = []; 24330 $this->columnLinks = []; 24331 $this->columnAnnots = []; 24332 $this->columnForms = []; 24333 $this->col_BMoutlines = []; 24334 $this->col_toc = []; 24335 $this->breakpoints = []; 24336 if ((strtoupper($vAlign) == 'J') || (strtoupper($vAlign) == 'JUSTIFY')) { 24337 $vAlign = 'J'; 24338 } else { 24339 $vAlign = ''; 24340 } 24341 $this->colvAlign = $vAlign; 24342 // Save the ordinate 24343 $absL = $this->DeflMargin - ($gap / 2); 24344 $absR = $this->DefrMargin - ($gap / 2); 24345 $PageWidth = $this->w - $absL - $absR; // virtual pagewidth for calculation only 24346 $ColWidth = (($PageWidth - ($gap * ($NbCol))) / $NbCol); 24347 $this->ColWidth = $ColWidth; 24348 /* -- OTL -- */ 24349 24350 if ($this->directionality == 'rtl') { 24351 for ($i = 0; $i < $this->NbCol; $i++) { 24352 $this->ColL[$i] = $absL + ($gap / 2) + (($NbCol - ($i + 1)) * ($PageWidth / $NbCol)); 24353 $this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos 24354 } 24355 } else { 24356 /* -- END OTL -- */ 24357 for ($i = 0; $i < $this->NbCol; $i++) { 24358 $this->ColL[$i] = $absL + ($gap / 2) + ($i * ($PageWidth / $NbCol) ); 24359 $this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos 24360 } 24361 } // *OTL* 24362 $this->pgwidth = $ColWidth; 24363 $this->SetCol(0); 24364 $this->y0 = $this->y; 24365 } 24366 $this->x = $this->lMargin; 24367 } 24368 24369 function SetCol($CurrCol) 24370 { 24371 // Used internally to set column by number: 0 is 1st column 24372 // Set position on a column 24373 $this->CurrCol = $CurrCol; 24374 $x = $this->ColL[$CurrCol]; 24375 $xR = $this->ColR[$CurrCol]; // NB This is not R margin -> R pos 24376 if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN 24377 $x += $this->MarginCorrection; 24378 $xR += $this->MarginCorrection; 24379 } 24380 $this->SetMargins($x, ($this->w - $xR), $this->tMargin); 24381 } 24382 24383 function AddColumn() 24384 { 24385 $this->NewColumn(); 24386 $this->ColumnAdjust = false; // disables all column height adjustment for the page. 24387 } 24388 24389 function NewColumn() 24390 { 24391 if ($this->ColActive == 1) { 24392 if ($this->CurrCol < $this->NbCol - 1) { 24393 // Go to the next column 24394 $this->CurrCol++; 24395 $this->SetCol($this->CurrCol); 24396 $this->y = $this->y0; 24397 $this->ChangeColumn = 1; 24398 // DIRECTIONALITY RTL 24399 if ($this->directionality == 'rtl') { 24400 $this->ChangeColumn = -($this->ChangeColumn); 24401 } // *OTL* 24402 // Stay on the page 24403 } else { 24404 // Go back to the first column 24405 // Page break 24406 if (count($this->columnbuffer)) { 24407 $this->printcolumnbuffer(); 24408 } 24409 $this->AddPage($this->CurOrientation); 24410 $this->SetCol(0); 24411 $this->y0 = $this->tMargin; 24412 $this->ChangeColumn = -($this->NbCol - 1); 24413 // DIRECTIONALITY RTL 24414 if ($this->directionality == 'rtl') { 24415 $this->ChangeColumn = -($this->ChangeColumn); 24416 } // *OTL* 24417 } 24418 $this->x = $this->lMargin; 24419 } else { 24420 $this->AddPage($this->CurOrientation); 24421 } 24422 } 24423 24424 function printcolumnbuffer() 24425 { 24426 // Columns ended (but page not ended) -> try to match all columns - unless disabled by using a custom column-break 24427 if (!$this->ColActive && $this->ColumnAdjust && !$this->keepColumns) { 24428 // Calculate adjustment to add to each column to calculate rel_y value 24429 $this->ColDetails[0]['add_y'] = 0; 24430 $last_col = 0; 24431 // Recursively add previous column's height 24432 for ($i = 1; $i < $this->NbCol; $i++) { 24433 if (isset($this->ColDetails[$i]['bottom_margin']) && $this->ColDetails[$i]['bottom_margin']) { // If any entries in the column 24434 $this->ColDetails[$i]['add_y'] = ($this->ColDetails[$i - 1]['bottom_margin'] - $this->y0) + $this->ColDetails[$i - 1]['add_y']; 24435 $last_col = $i; // Last column actually printed 24436 } 24437 } 24438 24439 // Calculate value for each position sensitive entry as though for one column 24440 foreach ($this->columnbuffer as $key => $s) { 24441 $t = $s['s']; 24442 if ($t == 'ACROFORM') { 24443 $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0; 24444 $this->columnbuffer[$key]['s'] = ''; 24445 } elseif (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) { 24446 $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0; 24447 } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) { 24448 $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0; 24449 } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) { 24450 $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0; 24451 } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) { 24452 $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0; 24453 } elseif (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) { 24454 $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0; 24455 } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t)) { 24456 $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0; 24457 } 24458 } 24459 foreach ($this->internallink as $key => $f) { 24460 if (is_array($f) && isset($f['col'])) { 24461 $this->internallink[$key]['rel_y'] = $f['Y'] + $this->ColDetails[$f['col']]['add_y'] - $this->y0; 24462 } 24463 } 24464 24465 $breaks = []; 24466 foreach ($this->breakpoints as $c => $bpa) { 24467 foreach ($bpa as $rely) { 24468 $breaks[] = $rely + $this->ColDetails[$c]['add_y'] - $this->y0; 24469 } 24470 } 24471 24472 24473 if (isset($this->ColDetails[$last_col]['bottom_margin'])) { 24474 $lcbm = $this->ColDetails[$last_col]['bottom_margin']; 24475 } else { 24476 $lcbm = 0; 24477 } 24478 $sum_h = $this->ColDetails[$last_col]['add_y'] + $lcbm - $this->y0; 24479 // $sum_h = max($this->ColDetails[$last_col]['add_y'] + $this->ColDetails[$last_col]['bottom_margin'] - $this->y0, end($breaks)); 24480 $target_h = ($sum_h / $this->NbCol); 24481 24482 $cbr = []; 24483 for ($i = 1; $i < $this->NbCol; $i++) { 24484 $th = ($sum_h * $i / $this->NbCol); 24485 foreach ($breaks as $bk => $val) { 24486 if ($val > $th) { 24487 if (($val - $th) < ($th - $breaks[$bk - 1])) { 24488 $cbr[$i - 1] = $val; 24489 } else { 24490 $cbr[$i - 1] = $breaks[$bk - 1]; 24491 } 24492 break; 24493 } 24494 } 24495 } 24496 $cbr[($this->NbCol - 1)] = $sum_h; 24497 24498 // mPDF 6 24499 // Avoid outputing with 1st column empty 24500 if (isset($cbr[0]) && $cbr[0] == 0) { 24501 for ($i = 0; $i < $this->NbCol - 1; $i++) { 24502 $cbr[$i] = $cbr[$i + 1]; 24503 } 24504 } 24505 24506 // Now update the columns - divide into columns of approximately equal value 24507 $last_new_col = 0; 24508 $yadj = 0; // mm 24509 $xadj = 0; 24510 $last_col_bottom = 0; 24511 $lowest_bottom_y = 0; 24512 $block_bottom = 0; 24513 $newcolumn = 0; 24514 foreach ($this->columnbuffer as $key => $s) { 24515 if (isset($s['rel_y'])) { // only process position sensitive data 24516 if ($s['rel_y'] >= $cbr[$newcolumn]) { 24517 $newcolumn++; 24518 } else { 24519 $newcolumn = $last_new_col; 24520 } 24521 24522 24523 $block_bottom = max($block_bottom, ($s['rel_y'] + $s['h'])); 24524 24525 if ($this->directionality == 'rtl') { // *OTL* 24526 $xadj = -(($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap)); // *OTL* 24527 } // *OTL* 24528 else { // *OTL* 24529 $xadj = ($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap); 24530 } // *OTL* 24531 24532 if ($last_new_col != $newcolumn) { // Added new column 24533 $last_col_bottom = $this->columnbuffer[$key]['rel_y']; 24534 $block_bottom = 0; 24535 } 24536 $yadj = ($s['rel_y'] - $s['y']) - ($last_col_bottom) + $this->y0; 24537 // callback function 24538 $t = $s['s']; 24539 24540 // mPDF 5.7+ 24541 $t = $this->columnAdjustPregReplace('Td', $xadj, $yadj, '/BT (\d+\.\d\d+) (\d+\.\d\d+) Td/', $t); 24542 $t = $this->columnAdjustPregReplace('re', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) ([\-]{0,1}\d+\.\d\d+) re/', $t); 24543 $t = $this->columnAdjustPregReplace('l', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) l/', $t); 24544 $t = $this->columnAdjustPregReplace('img', $xadj, $yadj, '/q (\d+\.\d\d+) 0 0 (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) cm \/(I|FO)/', $t); 24545 $t = $this->columnAdjustPregReplace('draw', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) m/', $t); 24546 $t = $this->columnAdjustPregReplace('bezier', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) c/', $t); 24547 24548 $this->columnbuffer[$key]['s'] = $t; 24549 $this->columnbuffer[$key]['newcol'] = $newcolumn; 24550 $this->columnbuffer[$key]['newy'] = $s['y'] + $yadj; 24551 $last_new_col = $newcolumn; 24552 $clb = $s['y'] + $yadj + $s['h']; // bottom_margin of current 24553 if ((isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb > $this->ColDetails[$newcolumn]['max_bottom']) || (!isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb)) { 24554 $this->ColDetails[$newcolumn]['max_bottom'] = $clb; 24555 } 24556 if ($clb > $lowest_bottom_y) { 24557 $lowest_bottom_y = $clb; 24558 } 24559 // Adjust LINKS 24560 if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) { 24561 $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]; 24562 $this->PageLinks[$this->page][$ref][0] += ($xadj * Mpdf::SCALE); 24563 $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); 24564 unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]); 24565 } 24566 // Adjust FORM FIELDS 24567 if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) { 24568 $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]; 24569 $this->form->forms[$ref]['x'] += ($xadj); 24570 $this->form->forms[$ref]['y'] += ($yadj); 24571 unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]); 24572 } 24573 /* -- ANNOTATIONS -- */ 24574 if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) { 24575 $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]; 24576 if ($this->PageAnnots[$this->page][$ref]['x'] < 0) { 24577 $this->PageAnnots[$this->page][$ref]['x'] -= ($xadj); 24578 } else { 24579 $this->PageAnnots[$this->page][$ref]['x'] += ($xadj); 24580 } 24581 $this->PageAnnots[$this->page][$ref]['y'] += ($yadj); // unlike PageLinks, Page annots has y values from top in mm 24582 unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]); 24583 } 24584 /* -- END ANNOTATIONS -- */ 24585 } 24586 } 24587 24588 /* -- BOOKMARKS -- */ 24589 // Adjust Bookmarks 24590 foreach ($this->col_BMoutlines as $v) { 24591 $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']]; 24592 } 24593 /* -- END BOOKMARKS -- */ 24594 24595 /* -- TOC -- */ 24596 24597 // Adjust ToC 24598 foreach ($this->col_toc as $v) { 24599 $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']]; 24600 $this->links[$v['link']][1] = $this->y0; 24601 } 24602 /* -- END TOC -- */ 24603 24604 // Adjust column length to be equal 24605 if ($this->colvAlign == 'J') { 24606 foreach ($this->columnbuffer as $key => $s) { 24607 if (isset($s['rel_y'])) { // only process position sensitive data 24608 // Set ratio to expand y values or heights 24609 if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) { 24610 $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0)); 24611 } else { 24612 $ratio = 1; 24613 } 24614 if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) { 24615 $yadj = ($s['newy'] - $this->y0) * ($ratio - 1); 24616 24617 // Adjust LINKS 24618 if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) { 24619 $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]; 24620 $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value 24621 $this->PageLinks[$this->page][$ref][3] *= $ratio; // height 24622 unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]); 24623 } 24624 // Adjust FORM FIELDS 24625 if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) { 24626 $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]; 24627 $this->form->forms[$ref]['x'] += ($xadj); 24628 $this->form->forms[$ref]['y'] += ($yadj); 24629 unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]); 24630 } 24631 /* -- ANNOTATIONS -- */ 24632 if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) { 24633 $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]; 24634 $this->PageAnnots[$this->page][$ref]['y'] += ($yadj); 24635 unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]); 24636 } 24637 /* -- END ANNOTATIONS -- */ 24638 } 24639 } 24640 } 24641 foreach ($this->internallink as $key => $f) { 24642 if (is_array($f) && isset($f['col'])) { 24643 $last_col_bottom = 0; 24644 for ($nbc = 0; $nbc < $this->NbCol; $nbc++) { 24645 if ($f['rel_y'] >= $cbr[$nbc]) { 24646 $last_col_bottom = $cbr[$nbc]; 24647 } 24648 } 24649 $yadj = ($f['rel_y'] - $f['Y']) - $last_col_bottom + $this->y0; 24650 $f['Y'] += $yadj; 24651 unset($f['col']); 24652 unset($f['rel_y']); 24653 $this->internallink[$key] = $f; 24654 } 24655 } 24656 24657 $last_col = -1; 24658 $trans_on = false; 24659 foreach ($this->columnbuffer as $key => $s) { 24660 if (isset($s['rel_y'])) { // only process position sensitive data 24661 // Set ratio to expand y values or heights 24662 if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) { 24663 $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0)); 24664 } else { 24665 $ratio = 1; 24666 } 24667 if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) { 24668 // Start Transformation 24669 $this->pages[$this->page] .= $this->StartTransform(true) . "\n"; 24670 $this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n"; 24671 $trans_on = true; 24672 } 24673 } 24674 // Now output the adjusted values 24675 $this->pages[$this->page] .= $s['s'] . "\n"; 24676 if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) { // only process position sensitive data 24677 // Stop Transformation 24678 $this->pages[$this->page] .= $this->StopTransform(true) . "\n"; 24679 $trans_on = false; 24680 } 24681 } 24682 if ($trans_on) { 24683 $this->pages[$this->page] .= $this->StopTransform(true) . "\n"; 24684 } 24685 } else { // if NOT $this->colvAlign == 'J' 24686 // Now output the adjusted values 24687 foreach ($this->columnbuffer as $s) { 24688 $this->pages[$this->page] .= $s['s'] . "\n"; 24689 } 24690 } 24691 if ($lowest_bottom_y > 0) { 24692 $this->y = $lowest_bottom_y; 24693 } 24694 } // Columns not ended but new page -> align columns (can leave the columns alone - just tidy up the height) 24695 elseif ($this->colvAlign == 'J' && $this->ColumnAdjust && !$this->keepColumns) { 24696 // calculate the lowest bottom margin 24697 $lowest_bottom_y = 0; 24698 foreach ($this->columnbuffer as $key => $s) { 24699 // Only process output data 24700 $t = $s['s']; 24701 if ($t == 'ACROFORM' || (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) || 24702 (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) || 24703 (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) || 24704 (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) || 24705 (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) { 24706 $clb = $s['y'] + $s['h']; 24707 if ((isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom']) || !isset($this->ColDetails[$s['col']]['max_bottom'])) { 24708 $this->ColDetails[$s['col']]['max_bottom'] = $clb; 24709 } 24710 if ($clb > $lowest_bottom_y) { 24711 $lowest_bottom_y = $clb; 24712 } 24713 $this->columnbuffer[$key]['rel_y'] = $s['y']; // Marks position sensitive data to process later 24714 if ($t == 'ACROFORM') { 24715 $this->columnbuffer[$key]['s'] = ''; 24716 } 24717 } 24718 } 24719 // Adjust column length equal 24720 foreach ($this->columnbuffer as $key => $s) { 24721 // Set ratio to expand y values or heights 24722 if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) { 24723 $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0)); 24724 } else { 24725 $ratio = 1; 24726 } 24727 if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) { 24728 $yadj = ($s['y'] - $this->y0) * ($ratio - 1); 24729 24730 // Adjust LINKS 24731 if (isset($s['rel_y'])) { // only process position sensitive data 24732 // otherwise triggers for all entries in column buffer (.e.g. formatting) and makes below adjustments more than once 24733 if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) { 24734 $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]; 24735 $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value 24736 $this->PageLinks[$this->page][$ref][3] *= $ratio; // height 24737 unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]); 24738 } 24739 // Adjust FORM FIELDS 24740 if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) { 24741 $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]; 24742 $this->form->forms[$ref]['x'] += ($xadj); 24743 $this->form->forms[$ref]['y'] += ($yadj); 24744 unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]); 24745 } 24746 /* -- ANNOTATIONS -- */ 24747 if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) { 24748 $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]; 24749 $this->PageAnnots[$this->page][$ref]['y'] += ($yadj); 24750 unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]); 24751 } 24752 /* -- END ANNOTATIONS -- */ 24753 } 24754 } 24755 } 24756 24757 /* -- BOOKMARKS -- */ 24758 24759 // Adjust Bookmarks 24760 foreach ($this->col_BMoutlines as $v) { 24761 $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']]; 24762 } 24763 /* -- END BOOKMARKS -- */ 24764 24765 /* -- TOC -- */ 24766 24767 // Adjust ToC 24768 foreach ($this->col_toc as $v) { 24769 $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']]; 24770 $this->links[$v['link']][1] = $this->y0; 24771 } 24772 /* -- END TOC -- */ 24773 24774 $trans_on = false; 24775 foreach ($this->columnbuffer as $key => $s) { 24776 24777 if (isset($s['rel_y'])) { // only process position sensitive data 24778 24779 // Set ratio to expand y values or heights 24780 if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) { 24781 $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0)); 24782 } else { 24783 $ratio = 1; 24784 } 24785 24786 if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) { 24787 // Start Transformation 24788 $this->pages[$this->page] .= $this->StartTransform(true) . "\n"; 24789 $this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n"; 24790 $trans_on = true; 24791 } 24792 } 24793 24794 // Now output the adjusted values 24795 $this->pages[$this->page] .= $s['s'] . "\n"; 24796 if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) { 24797 // Stop Transformation 24798 $this->pages[$this->page] .= $this->StopTransform(true) . "\n"; 24799 $trans_on = false; 24800 } 24801 } 24802 24803 if ($trans_on) { 24804 $this->pages[$this->page] .= $this->StopTransform(true) . "\n"; 24805 } 24806 24807 if ($lowest_bottom_y > 0) { 24808 $this->y = $lowest_bottom_y; 24809 } 24810 24811 } else { // Just reproduce the page as it was 24812 24813 // If page has not ended but height adjustment was disabled by custom column-break - adjust y 24814 $lowest_bottom_y = 0; 24815 24816 if (!$this->ColActive && (!$this->ColumnAdjust || $this->keepColumns)) { 24817 24818 // calculate the lowest bottom margin 24819 foreach ($this->columnbuffer as $key => $s) { 24820 24821 // Only process output data 24822 $t = $s['s']; 24823 if ($t === 'ACROFORM' 24824 || (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) 24825 || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) 24826 || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) 24827 || (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) 24828 || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) 24829 || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) { 24830 24831 $clb = $s['y'] + $s['h']; 24832 24833 if (isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom'] || (!isset($this->ColDetails[$s['col']]['max_bottom']) && $clb)) { 24834 $this->ColDetails[$s['col']]['max_bottom'] = $clb; 24835 } 24836 24837 if ($clb > $lowest_bottom_y) { 24838 $lowest_bottom_y = $clb; 24839 } 24840 } 24841 } 24842 } 24843 24844 foreach ($this->columnbuffer as $key => $s) { 24845 if ($s['s'] != 'ACROFORM') { 24846 $this->pages[$this->page] .= $s['s'] . "\n"; 24847 } 24848 } 24849 24850 if ($lowest_bottom_y > 0) { 24851 $this->y = $lowest_bottom_y; 24852 } 24853 24854 /* -- BOOKMARKS -- */ 24855 // Output Bookmarks 24856 foreach ($this->col_BMoutlines as $v) { 24857 $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']]; 24858 } 24859 /* -- END BOOKMARKS -- */ 24860 24861 /* -- TOC -- */ 24862 // Output ToC 24863 foreach ($this->col_toc as $v) { 24864 $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']]; 24865 } 24866 /* -- END TOC -- */ 24867 } 24868 24869 foreach ($this->internallink as $key => $f) { 24870 24871 if (isset($this->internallink[$key]['col'])) { 24872 unset($this->internallink[$key]['col']); 24873 } 24874 24875 if (isset($this->internallink[$key]['rel_y'])) { 24876 unset($this->internallink[$key]['rel_y']); 24877 } 24878 } 24879 24880 $this->columnbuffer = []; 24881 $this->ColDetails = []; 24882 $this->columnLinks = []; 24883 $this->columnAnnots = []; 24884 $this->columnForms = []; 24885 24886 $this->col_BMoutlines = []; 24887 $this->col_toc = []; 24888 $this->breakpoints = []; 24889 } 24890 24891 // mPDF 5.7+ 24892 function columnAdjustPregReplace($type, $xadj, $yadj, $pattern, $subject) 24893 { 24894 preg_match($pattern, $subject, $matches); 24895 24896 if (!count($matches)) { 24897 return $subject; 24898 } 24899 24900 if (!isset($matches[3])) { 24901 $matches[3] = 0; 24902 } 24903 24904 if (!isset($matches[4])) { 24905 $matches[4] = 0; 24906 } 24907 24908 if (!isset($matches[5])) { 24909 $matches[5] = 0; 24910 } 24911 24912 if (!isset($matches[6])) { 24913 $matches[6] = 0; 24914 } 24915 24916 return str_replace($matches[0], $this->columnAdjustAdd($type, Mpdf::SCALE, $xadj, $yadj, $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]), $subject); 24917 } 24918 /* -- END COLUMNS -- */ 24919 24920 // ================================================================== 24921 /* -- TABLES -- */ 24922 function printcellbuffer() 24923 { 24924 if (count($this->cellBorderBuffer)) { 24925 24926 sort($this->cellBorderBuffer); 24927 24928 foreach ($this->cellBorderBuffer as $cbb) { 24929 24930 $cba = unpack("A16dom/nbord/A1side/ns/dbw/a6ca/A10style/dx/dy/dw/dh/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd/dover/", $cbb); 24931 $side = $cba['side']; 24932 $color = str_pad($cba['ca'], 6, "\x00"); 24933 24934 $details = []; 24935 24936 $details[$side]['dom'] = (float) $cba['dom']; 24937 $details[$side]['s'] = $cba['s']; 24938 $details[$side]['w'] = $cba['bw']; 24939 $details[$side]['c'] = $color; 24940 $details[$side]['style'] = trim($cba['style']); 24941 24942 $details['mbw']['BL'] = $cba['mbl']; 24943 $details['mbw']['BR'] = $cba['mbr']; 24944 $details['mbw']['RT'] = $cba['mrt']; 24945 $details['mbw']['RB'] = $cba['mrb']; 24946 $details['mbw']['TL'] = $cba['mtl']; 24947 $details['mbw']['TR'] = $cba['mtr']; 24948 $details['mbw']['LT'] = $cba['mlt']; 24949 $details['mbw']['LB'] = $cba['mlb']; 24950 24951 $details['cellposdom'] = $cba['cpd']; 24952 24953 $details['p'] = $side; 24954 24955 if ($cba['over'] == 1) { 24956 $details[$side]['overlay'] = true; 24957 } else { 24958 $details[$side]['overlay'] = false; 24959 } 24960 24961 $this->_tableRect($cba['x'], $cba['y'], $cba['w'], $cba['h'], $cba['bord'], $details, false, false); 24962 } 24963 24964 $this->cellBorderBuffer = []; 24965 } 24966 } 24967 24968 // ================================================================== 24969 function printtablebuffer() 24970 { 24971 24972 if (!$this->table_rotate) { 24973 24974 $this->pages[$this->page] .= $this->tablebuffer; 24975 24976 foreach ($this->tbrot_Links as $p => $l) { 24977 foreach ($l as $v) { 24978 $this->PageLinks[$p][] = $v; 24979 } 24980 } 24981 $this->tbrot_Links = []; 24982 24983 /* -- ANNOTATIONS -- */ 24984 foreach ($this->tbrot_Annots as $p => $l) { 24985 foreach ($l as $v) { 24986 $this->PageAnnots[$p][] = $v; 24987 } 24988 } 24989 $this->tbrot_Annots = []; 24990 /* -- END ANNOTATIONS -- */ 24991 24992 /* -- BOOKMARKS -- */ 24993 // Output Bookmarks 24994 foreach ($this->tbrot_BMoutlines as $v) { 24995 $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']]; 24996 } 24997 $this->tbrot_BMoutlines = []; 24998 /* -- END BOOKMARKS -- */ 24999 25000 /* -- TOC -- */ 25001 // Output ToC 25002 foreach ($this->tbrot_toc as $v) { 25003 $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']]; 25004 } 25005 $this->tbrot_toc = []; 25006 /* -- END TOC -- */ 25007 25008 return; 25009 } 25010 25011 // elseif rotated 25012 $lm = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left']; 25013 $pw = $this->blk[$this->blklvl]['inner_width']; 25014 25015 // Start Transformation 25016 $this->pages[$this->page] .= $this->StartTransform(true) . "\n"; 25017 25018 if ($this->table_rotate > 1) { // clockwise 25019 25020 if ($this->tbrot_align == 'L') { 25021 $xadj = $this->tbrot_h; // align L (as is) 25022 } elseif ($this->tbrot_align == 'R') { 25023 $xadj = $lm - $this->tbrot_x0 + ($pw); // align R 25024 } else { 25025 $xadj = $lm - $this->tbrot_x0 + (($pw + $this->tbrot_h) / 2); // align C 25026 } 25027 25028 $yadj = 0; 25029 25030 } else { // anti-clockwise 25031 25032 if ($this->tbrot_align == 'L') { 25033 $xadj = 0; // align L (as is) 25034 } elseif ($this->tbrot_align == 'R') { 25035 $xadj = $lm - $this->tbrot_x0 + ($pw - $this->tbrot_h); // align R 25036 } else { 25037 $xadj = $lm - $this->tbrot_x0 + (($pw - $this->tbrot_h) / 2); // align C 25038 } 25039 25040 $yadj = $this->tbrot_w; 25041 } 25042 25043 25044 $this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n"; 25045 $this->pages[$this->page] .= $this->transformRotate($this->table_rotate, $this->tbrot_x0, $this->tbrot_y0, true) . "\n"; 25046 25047 // Now output the adjusted values 25048 $this->pages[$this->page] .= $this->tablebuffer; 25049 25050 foreach ($this->tbrot_Links as $p => $l) { 25051 25052 foreach ($l as $v) { 25053 25054 $w = $v[2] / Mpdf::SCALE; 25055 $h = $v[3] / Mpdf::SCALE; 25056 $ax = ($v[0] / Mpdf::SCALE) - $this->tbrot_x0; 25057 $ay = (($this->hPt - $v[1]) / Mpdf::SCALE) - $this->tbrot_y0; 25058 25059 if ($this->table_rotate > 1) { // clockwise 25060 $bx = $this->tbrot_x0 + $xadj - $ay - $h; 25061 $by = $this->tbrot_y0 + $yadj + $ax; 25062 } else { 25063 $bx = $this->tbrot_x0 + $xadj + $ay; 25064 $by = $this->tbrot_y0 + $yadj - $ax - $w; 25065 } 25066 25067 $v[0] = $bx * Mpdf::SCALE; 25068 $v[1] = ($this->h - $by) * Mpdf::SCALE; 25069 $v[2] = $h * Mpdf::SCALE; // swap width and height 25070 $v[3] = $w * Mpdf::SCALE; 25071 25072 $this->PageLinks[$p][] = $v; 25073 } 25074 } 25075 25076 $this->tbrot_Links = []; 25077 foreach ($this->internallink as $key => $f) { 25078 if (is_array($f) && isset($f['tbrot'])) { 25079 $f['Y'] = $this->tbrot_y0; 25080 $f['PAGE'] = $this->page; 25081 unset($f['tbrot']); 25082 $this->internallink[$key] = $f; 25083 } 25084 } 25085 25086 /* -- ANNOTATIONS -- */ 25087 foreach ($this->tbrot_Annots as $p => $l) { 25088 foreach ($l as $v) { 25089 $ax = abs($v['x']) - $this->tbrot_x0; // abs because -ve values are internally set and held for reference if annotMargin set 25090 $ay = $v['y'] - $this->tbrot_y0; 25091 25092 if ($this->table_rotate > 1) { // clockwise 25093 $bx = $this->tbrot_x0 + $xadj - $ay; 25094 $by = $this->tbrot_y0 + $yadj + $ax; 25095 } else { 25096 $bx = $this->tbrot_x0 + $xadj + $ay; 25097 $by = $this->tbrot_y0 + $yadj - $ax; 25098 } 25099 25100 if ($v['x'] < 0) { 25101 $v['x'] = -$bx; 25102 } else { 25103 $v['x'] = $bx; 25104 } 25105 25106 $v['y'] = ($by); 25107 $this->PageAnnots[$p][] = $v; 25108 } 25109 } 25110 25111 $this->tbrot_Annots = []; 25112 /* -- END ANNOTATIONS -- */ 25113 25114 /* -- BOOKMARKS -- */ 25115 // Adjust Bookmarks 25116 foreach ($this->tbrot_BMoutlines as $v) { 25117 $v['y'] = $this->tbrot_y0; 25118 $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page]; 25119 } 25120 /* -- END BOOKMARKS -- */ 25121 25122 /* -- TOC -- */ 25123 // Adjust ToC - uses document page number 25124 foreach ($this->tbrot_toc as $v) { 25125 $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']]; 25126 $this->links[$v['link']][1] = $this->tbrot_y0; 25127 } 25128 /* -- END TOC -- */ 25129 25130 $this->tbrot_BMoutlines = []; 25131 $this->tbrot_toc = []; 25132 25133 // Stop Transformation 25134 $this->pages[$this->page] .= $this->StopTransform(true) . "\n"; 25135 25136 $this->y = $this->tbrot_y0 + $this->tbrot_w; 25137 $this->x = $this->lMargin; 25138 25139 $this->tablebuffer = ''; 25140 } 25141 25142 /** 25143 * Keep-with-table This buffers contents of h1-6 to keep on page with table 25144 */ 25145 function printkwtbuffer() 25146 { 25147 if (!$this->kwt_moved) { 25148 25149 foreach ($this->kwt_buffer as $s) { 25150 $this->pages[$this->page] .= $s['s'] . "\n"; 25151 } 25152 25153 foreach ($this->kwt_Links as $p => $l) { 25154 foreach ($l as $v) { 25155 $this->PageLinks[$p][] = $v; 25156 } 25157 } 25158 25159 $this->kwt_Links = []; 25160 25161 /* -- ANNOTATIONS -- */ 25162 foreach ($this->kwt_Annots as $p => $l) { 25163 foreach ($l as $v) { 25164 $this->PageAnnots[$p][] = $v; 25165 } 25166 } 25167 $this->kwt_Annots = []; 25168 /* -- END ANNOTATIONS -- */ 25169 25170 /* -- INDEX -- */ 25171 // Output Reference (index) 25172 foreach ($this->kwt_Reference as $v) { 25173 25174 $Present = 0; 25175 25176 for ($i = 0; $i < count($this->Reference); $i++) { 25177 if ($this->Reference[$i]['t'] == $v['t']) { 25178 $Present = 1; 25179 if (!in_array($v['op'], $this->Reference[$i]['p'])) { 25180 $this->Reference[$i]['p'][] = $v['op']; 25181 } 25182 } 25183 } 25184 25185 if ($Present == 0) { 25186 $this->Reference[] = ['t' => $v['t'], 'p' => [$v['op']]]; 25187 } 25188 } 25189 $this->kwt_Reference = []; 25190 /* -- END INDEX -- */ 25191 25192 /* -- BOOKMARKS -- */ 25193 // Output Bookmarks 25194 foreach ($this->kwt_BMoutlines as $v) { 25195 $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']]; 25196 } 25197 $this->kwt_BMoutlines = []; 25198 /* -- END BOOKMARKS -- */ 25199 25200 /* -- TOC -- */ 25201 // Output ToC 25202 foreach ($this->kwt_toc as $v) { 25203 $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']]; 25204 } 25205 $this->kwt_toc = []; 25206 /* -- END TOC -- */ 25207 25208 $this->pageoutput[$this->page] = []; // mPDF 6 25209 25210 return; 25211 } 25212 25213 // Start Transformation 25214 $this->pages[$this->page] .= $this->StartTransform(true) . "\n"; 25215 $xadj = $this->lMargin - $this->kwt_x0; 25216 // $yadj = $this->y - $this->kwt_y0 ; 25217 $yadj = $this->tMargin - $this->kwt_y0; 25218 25219 $this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n"; 25220 25221 // Now output the adjusted values 25222 foreach ($this->kwt_buffer as $s) { 25223 $this->pages[$this->page] .= $s['s'] . "\n"; 25224 } 25225 25226 // Adjust hyperLinks 25227 foreach ($this->kwt_Links as $p => $l) { 25228 foreach ($l as $v) { 25229 $bx = $this->kwt_x0 + $xadj; 25230 $by = $this->kwt_y0 + $yadj; 25231 $v[0] = $bx * Mpdf::SCALE; 25232 $v[1] = ($this->h - $by) * Mpdf::SCALE; 25233 $this->PageLinks[$p][] = $v; 25234 } 25235 } 25236 25237 foreach ($this->internallink as $key => $f) { 25238 if (is_array($f) && isset($f['kwt'])) { 25239 $f['Y'] += $yadj; 25240 $f['PAGE'] = $this->page; 25241 unset($f['kwt']); 25242 $this->internallink[$key] = $f; 25243 } 25244 } 25245 25246 /* -- ANNOTATIONS -- */ 25247 foreach ($this->kwt_Annots as $p => $l) { 25248 foreach ($l as $v) { 25249 $bx = $this->kwt_x0 + $xadj; 25250 $by = $this->kwt_y0 + $yadj; 25251 if ($v['x'] < 0) { 25252 $v['x'] = -$bx; 25253 } else { 25254 $v['x'] = $bx; 25255 } 25256 $v['y'] = $by; 25257 $this->PageAnnots[$p][] = $v; 25258 } 25259 } 25260 /* -- END ANNOTATIONS -- */ 25261 25262 /* -- BOOKMARKS -- */ 25263 25264 // Adjust Bookmarks 25265 foreach ($this->kwt_BMoutlines as $v) { 25266 if ($v['y'] != 0) { 25267 $v['y'] += $yadj; 25268 } 25269 $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page]; 25270 } 25271 /* -- END BOOKMARKS -- */ 25272 25273 /* -- INDEX -- */ 25274 // Adjust Reference (index) 25275 foreach ($this->kwt_Reference as $v) { 25276 25277 $Present = 0; 25278 25279 // Search the reference (AND Ref/PageNo) in the array 25280 for ($i = 0; $i < count($this->Reference); $i++) { 25281 if ($this->Reference[$i]['t'] == $v['t']) { 25282 $Present = 1; 25283 if (!in_array($this->page, $this->Reference[$i]['p'])) { 25284 $this->Reference[$i]['p'][] = $this->page; 25285 } 25286 } 25287 } 25288 25289 if ($Present == 0) { 25290 $this->Reference[] = ['t' => $v['t'], 'p' => [$this->page]]; 25291 } 25292 } 25293 /* -- END INDEX -- */ 25294 25295 /* -- TOC -- */ 25296 25297 // Adjust ToC 25298 foreach ($this->kwt_toc as $v) { 25299 $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']]; 25300 $this->links[$v['link']][0] = $this->page; 25301 $this->links[$v['link']][1] += $yadj; 25302 } 25303 /* -- END TOC -- */ 25304 25305 25306 $this->kwt_Links = []; 25307 $this->kwt_Annots = []; 25308 25309 $this->kwt_Reference = []; 25310 $this->kwt_BMoutlines = []; 25311 $this->kwt_toc = []; 25312 25313 // Stop Transformation 25314 $this->pages[$this->page] .= $this->StopTransform(true) . "\n"; 25315 25316 $this->kwt_buffer = []; 25317 25318 $this->y += $this->kwt_height; 25319 $this->pageoutput[$this->page] = []; // mPDF 6 25320 } 25321 /* -- END TABLES -- */ 25322 25323 function printfloatbuffer() 25324 { 25325 if (count($this->floatbuffer)) { 25326 $this->objectbuffer = $this->floatbuffer; 25327 $this->printobjectbuffer(false); 25328 $this->objectbuffer = []; 25329 $this->floatbuffer = []; 25330 $this->floatmargins = []; 25331 } 25332 } 25333 25334 function Circle($x, $y, $r, $style = 'S') 25335 { 25336 $this->Ellipse($x, $y, $r, $r, $style); 25337 } 25338 25339 function Ellipse($x, $y, $rx, $ry, $style = 'S') 25340 { 25341 if ($style === 'F') { 25342 $op = 'f'; 25343 } elseif ($style === 'FD' or $style === 'DF') { 25344 $op = 'B'; 25345 } else { 25346 $op = 'S'; 25347 } 25348 25349 $lx = 4 / 3 * (M_SQRT2 - 1) * $rx; 25350 $ly = 4 / 3 * (M_SQRT2 - 1) * $ry; 25351 25352 $h = $this->h; 25353 25354 $this->writer->write(sprintf('%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c', ($x + $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - ($y - $ly)) * Mpdf::SCALE, ($x + $lx) * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE, $x * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE)); 25355 $this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c', ($x - $lx) * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE, ($x - $rx) * Mpdf::SCALE, ($h - ($y - $ly)) * Mpdf::SCALE, ($x - $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE)); 25356 $this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c', ($x - $rx) * Mpdf::SCALE, ($h - ($y + $ly)) * Mpdf::SCALE, ($x - $lx) * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE, $x * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE)); 25357 $this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c %s', ($x + $lx) * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - ($y + $ly)) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE, $op)); 25358 } 25359 25360 /* -- DIRECTW -- */ 25361 function AutosizeText($text, $w, $font, $style, $szfont = 72) 25362 { 25363 25364 $text = ' ' . $text . ' '; 25365 25366 $this->SetFont($font, $style, $szfont, false); 25367 25368 $text = $this->purify_utf8_text($text); 25369 if ($this->text_input_as_HTML) { 25370 $text = $this->all_entities_to_utf8($text); 25371 } 25372 if ($this->usingCoreFont) { 25373 $text = mb_convert_encoding($text, $this->mb_enc, 'UTF-8'); 25374 } 25375 25376 // DIRECTIONALITY 25377 if (preg_match("/([" . $this->pregRTLchars . "])/u", $text)) { 25378 $this->biDirectional = true; 25379 } 25380 25381 $textvar = 0; 25382 $save_OTLtags = $this->OTLtags; 25383 $this->OTLtags = []; 25384 25385 if ($this->useKerning) { 25386 if ($this->CurrentFont['haskernGPOS']) { 25387 $this->OTLtags['Plus'] .= ' kern'; 25388 } else { 25389 $textvar = ($textvar | TextVars::FC_KERNING); 25390 } 25391 } 25392 25393 /* -- OTL -- */ 25394 // Use OTL OpenType Table Layout - GSUB & GPOS 25395 if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) { 25396 $text = $this->otl->applyOTL($text, $this->CurrentFont['useOTL']); 25397 $OTLdata = $this->otl->OTLdata; 25398 } 25399 /* -- END OTL -- */ 25400 25401 $this->OTLtags = $save_OTLtags; 25402 25403 $this->magic_reverse_dir($text, $this->directionality, $OTLdata); 25404 25405 $width = $this->sizeConverter->convert($w); 25406 $loop = 0; 25407 25408 while ($loop == 0) { 25409 25410 $this->SetFont($font, $style, $szfont, false); 25411 $sz = $this->GetStringWidth($text, true, $OTLdata, $textvar); 25412 25413 if ($sz > $w) { 25414 $szfont --; 25415 } else { 25416 $loop ++; 25417 } 25418 } 25419 25420 $this->SetFont($font, $style, $szfont, true, true); 25421 $this->Cell($w, 0, $text, 0, 0, "C", 0, '', 0, 0, 0, 'M', 0, false, $OTLdata, $textvar); 25422 } 25423 /* -- END DIRECTW -- */ 25424 25425 // ==================================================== 25426 // ==================================================== 25427 25428 function magic_reverse_dir(&$chunk, $dir, &$chunkOTLdata) 25429 { 25430 /* -- OTL -- */ 25431 if ($this->usingCoreFont) { 25432 return 0; 25433 } 25434 25435 if ($chunk == '') { 25436 return 0; 25437 } 25438 25439 if ($this->biDirectional || $dir == 'rtl') { 25440 25441 // check if string contains RTL text 25442 // including any added from OTL tables (in PUA) 25443 $pregRTLchars = $this->pregRTLchars; 25444 25445 if (isset($this->CurrentFont['rtlPUAstr']) && $this->CurrentFont['rtlPUAstr']) { 25446 $pregRTLchars .= $this->CurrentFont['rtlPUAstr']; 25447 } 25448 25449 if (!preg_match("/[" . $pregRTLchars . "]/u", $chunk) && $dir != 'rtl') { 25450 return 0; 25451 } // Chunk doesn't contain RTL characters 25452 25453 $unicode = $this->UTF8StringToArray($chunk, false); 25454 25455 $isStrong = false; 25456 if (empty($chunkOTLdata)) { 25457 $this->getBasicOTLdata($chunkOTLdata, $unicode, $isStrong); 25458 } 25459 25460 $useGPOS = isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0x80); 25461 25462 // NB Returned $chunk may be a shorter string (with adjusted $cOTLdata) by removal of LRE, RLE etc embedding codes. 25463 list($chunk, $rtl_content) = $this->otl->bidiSort($unicode, $chunk, $dir, $chunkOTLdata, $useGPOS); 25464 25465 return $rtl_content; 25466 } 25467 25468 /* -- END OTL -- */ 25469 return 0; 25470 } 25471 25472 /* -- OTL -- */ 25473 25474 function getBasicOTLdata(&$chunkOTLdata, $unicode, &$is_strong) 25475 { 25476 if (empty($this->otl)) { 25477 $this->otl = new Otl($this, $this->fontCache); 25478 } 25479 25480 $chunkOTLdata['group'] = ''; 25481 $chunkOTLdata['GPOSinfo'] = []; 25482 $chunkOTLdata['char_data'] = []; 25483 25484 foreach ($unicode as $char) { 25485 25486 $ucd_record = Ucdn::get_ucd_record($char); 25487 $chunkOTLdata['char_data'][] = ['bidi_class' => $ucd_record[2], 'uni' => $char]; 25488 25489 if ($ucd_record[2] == 0 || $ucd_record[2] == 3 || $ucd_record[2] == 4) { 25490 $is_strong = true; 25491 } // contains strong character 25492 25493 if ($ucd_record[0] == Ucdn::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { 25494 $chunkOTLdata['group'] .= 'M'; 25495 } elseif ($char == 32 || $char == 12288) { 25496 $chunkOTLdata['group'] .= 'S'; 25497 } else { 25498 $chunkOTLdata['group'] .= 'C'; 25499 } 25500 } 25501 } 25502 25503 function _setBidiCodes($mode = 'start', $bdf = '') 25504 { 25505 $s = ''; 25506 25507 if ($mode == 'end') { 25508 25509 // PDF comes before PDI to close isolate-override (e.g. "LRILROPDFPDI") 25510 if (strpos($bdf, 'PDF') !== false) { 25511 $s .= UtfString::code2utf(0x202C); 25512 } // POP DIRECTIONAL FORMATTING 25513 25514 if (strpos($bdf, 'PDI') !== false) { 25515 $s .= UtfString::code2utf(0x2069); 25516 } // POP DIRECTIONAL ISOLATE 25517 25518 } elseif ($mode == 'start') { 25519 25520 // LRI comes before LRO to open isolate-override (e.g. "LRILROPDFPDI") 25521 if (strpos($bdf, 'LRI') !== false) { // U+2066 LRI 25522 $s .= UtfString::code2utf(0x2066); 25523 } elseif (strpos($bdf, 'RLI') !== false) { // U+2067 RLI 25524 $s .= UtfString::code2utf(0x2067); 25525 } elseif (strpos($bdf, 'FSI') !== false) { // U+2068 FSI 25526 $s .= UtfString::code2utf(0x2068); 25527 } 25528 25529 if (strpos($bdf, 'LRO') !== false) { // U+202D LRO 25530 $s .= UtfString::code2utf(0x202D); 25531 } elseif (strpos($bdf, 'RLO') !== false) { // U+202E RLO 25532 $s .= UtfString::code2utf(0x202E); 25533 } elseif (strpos($bdf, 'LRE') !== false) { // U+202A LRE 25534 $s .= UtfString::code2utf(0x202A); 25535 } elseif (strpos($bdf, 'RLE') !== false) { // U+202B RLE 25536 $s .= UtfString::code2utf(0x202B); 25537 } 25538 } 25539 25540 return $s; 25541 } 25542 /* -- END OTL -- */ 25543 25544 function SetSubstitutions() 25545 { 25546 $subsarray = []; 25547 require __DIR__ . '/../data/subs_win-1252.php'; 25548 $this->substitute = []; 25549 foreach ($subsarray as $key => $val) { 25550 $this->substitute[UtfString::code2utf($key)] = $val; 25551 } 25552 } 25553 25554 function SubstituteChars($html) 25555 { 25556 // only substitute characters between tags 25557 if (count($this->substitute)) { 25558 $a = preg_split('/(<.*?>)/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE); 25559 $html = ''; 25560 foreach ($a as $i => $e) { 25561 if ($i % 2 == 0) { 25562 $e = strtr($e, $this->substitute); 25563 } 25564 $html .= $e; 25565 } 25566 } 25567 25568 return $html; 25569 } 25570 25571 function SubstituteCharsSIP(&$writehtml_a, &$writehtml_i, &$writehtml_e) 25572 { 25573 if (preg_match("/^(.*?)([\x{20000}-\x{2FFFF}]+)(.*)/u", $writehtml_e, $m)) { 25574 if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) { 25575 $font = $this->CurrentFont['sipext']; 25576 if (!in_array($font, $this->available_unifonts)) { 25577 return 0; 25578 } 25579 $writehtml_a[$writehtml_i] = $writehtml_e = $m[1]; 25580 array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); 25581 $this->subPos = $writehtml_i; 25582 return 4; 25583 } 25584 } 25585 25586 return 0; 25587 } 25588 25589 /** 25590 * If core font is selected in document which is not onlyCoreFonts - substitute with non-core font 25591 */ 25592 function SubstituteCharsNonCore(&$writehtml_a, &$writehtml_i, &$writehtml_e) 25593 { 25594 // Ignore if in Textarea 25595 if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') { 25596 return 0; 25597 } 25598 25599 if (mb_convert_encoding(mb_convert_encoding($writehtml_e, $this->mb_enc, "UTF-8"), "UTF-8", $this->mb_enc) == $writehtml_e) { 25600 return 0; 25601 } 25602 25603 $cw = &$this->CurrentFont['cw']; 25604 $unicode = $this->UTF8StringToArray($writehtml_e, false); 25605 $start = -1; 25606 $end = 0; 25607 $flag = 0; 25608 $ftype = ''; 25609 $u = []; 25610 25611 if (!$this->subArrMB) { 25612 25613 require __DIR__ . '/../data/subs_core.php'; 25614 25615 $this->subArrMB['a'] = $aarr; 25616 $this->subArrMB['s'] = $sarr; 25617 $this->subArrMB['z'] = $zarr; 25618 } 25619 25620 foreach ($unicode as $c => $char) { 25621 25622 if (($char > 127 || ($flag == 1 && $char == 32)) && $char != 173 && (!isset($this->subArrMB['a'][$char]) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) { 25623 if ($flag == 0) { 25624 $start = $c; 25625 } 25626 $flag = 1; 25627 $u[] = $char; 25628 } elseif ($flag > 0) { 25629 $end = $c - 1; 25630 break; 25631 } 25632 } 25633 25634 if ($flag > 0 && !$end) { 25635 $end = count($unicode) - 1; 25636 } 25637 25638 if ($start == -1) { 25639 return 0; 25640 } 25641 25642 // Try in backup subs font 25643 if (!is_array($this->backupSubsFont)) { 25644 $this->backupSubsFont = ["$this->backupSubsFont"]; 25645 } 25646 25647 foreach ($this->backupSubsFont as $bsfctr => $bsf) { 25648 25649 if ($this->fonttrans[$bsf] == 'chelvetica' || $this->fonttrans[$bsf] == 'ctimes' || $this->fonttrans[$bsf] == 'ccourier') { 25650 continue; 25651 } 25652 25653 $font = $bsf; 25654 unset($cw); 25655 $cw = ''; 25656 25657 if (isset($this->fonts[$font])) { 25658 $cw = &$this->fonts[$font]['cw']; 25659 } elseif ($this->fontCache->has($font . '.cw.dat')) { 25660 $cw = $this->fontCache->load($font . '.cw.dat'); 25661 } else { 25662 $prevFontFamily = $this->FontFamily; 25663 $prevFontStyle = $this->currentfontstyle; 25664 $prevFontSizePt = $this->FontSizePt; 25665 $this->SetFont($bsf, '', '', false); 25666 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false); 25667 } 25668 25669 if (!$cw) { 25670 continue; 25671 } 25672 25673 $l = 0; 25674 foreach ($u as $char) { 25675 if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) { 25676 $l++; 25677 } else { 25678 if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font 25679 $cont = mb_substr($writehtml_e, $start + 1); 25680 $writehtml_e = mb_substr($writehtml_e, 0, $start + 1, 'UTF-8'); 25681 array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]); 25682 $this->subPos = $writehtml_i + 1; 25683 25684 return 2; 25685 } else { 25686 break; 25687 } 25688 } 25689 } 25690 25691 if ($l > 0) { 25692 $patt = mb_substr($writehtml_e, $start, $l, 'UTF-8'); 25693 if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { 25694 $writehtml_e = $m[1]; 25695 array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); 25696 $this->subPos = $writehtml_i + 3; 25697 25698 return 4; 25699 } 25700 } 25701 } 25702 25703 unset($cw); 25704 25705 return 0; 25706 } 25707 25708 function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e) 25709 { 25710 // Ignore if in Textarea 25711 if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') { 25712 return 0; 25713 } 25714 25715 $cw = &$this->CurrentFont['cw']; 25716 $unicode = $this->UTF8StringToArray($writehtml_e, false); 25717 $start = -1; 25718 $end = 0; 25719 $flag = 0; 25720 $ftype = ''; 25721 $u = []; 25722 25723 foreach ($unicode as $c => $char) { 25724 25725 if (($flag == 0 || $flag == 2) && (!$this->_charDefined($cw, $char) || ($flag == 2 && $char == 32)) && $this->checkSIP && $char > 131071) { // Unicode Plane 2 (SIP) 25726 25727 if (in_array($this->FontFamily, $this->available_CJK_fonts)) { 25728 return 0; 25729 } 25730 25731 if ($flag == 0) { 25732 $start = $c; 25733 } 25734 25735 $flag = 2; 25736 $u[] = $char; 25737 25738 // elseif (($flag == 0 || $flag==1) && $char != 173 && !$this->_charDefined($cw,$char) && ($char<1423 || ($char>3583 && $char < 11263))) { 25739 25740 } elseif (($flag == 0 || $flag == 1) && $char != 173 && (!$this->_charDefined($cw, $char) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) { 25741 25742 if ($flag == 0) { 25743 $start = $c; 25744 } 25745 25746 $flag = 1; 25747 $u[] = $char; 25748 25749 } elseif ($flag > 0) { 25750 25751 $end = $c - 1; 25752 break; 25753 25754 } 25755 } 25756 25757 if ($flag > 0 && !$end) { 25758 $end = count($unicode) - 1; 25759 } 25760 25761 if ($start == -1) { 25762 return 0; 25763 } 25764 25765 if ($flag == 2) { // SIP 25766 25767 // Check if current CJK font has a ext-B related font 25768 if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) { 25769 $font = $this->CurrentFont['sipext']; 25770 unset($cw); 25771 $cw = ''; 25772 25773 if (isset($this->fonts[$font])) { 25774 $cw = &$this->fonts[$font]['cw']; 25775 } elseif ($this->fontCache->has($font . '.cw.dat')) { 25776 $cw = $this->fontCache->load($font . '.cw.dat'); 25777 } else { 25778 $prevFontFamily = $this->FontFamily; 25779 $prevFontStyle = $this->currentfontstyle; 25780 $prevFontSizePt = $this->FontSizePt; 25781 $this->SetFont($font, '', '', false); 25782 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false); 25783 } 25784 25785 if (!$cw) { 25786 return 0; 25787 } 25788 25789 $l = 0; 25790 foreach ($u as $char) { 25791 if ($this->_charDefined($cw, $char) || $char > 131071) { 25792 $l++; 25793 } else { 25794 break; 25795 } 25796 } 25797 25798 if ($l > 0) { 25799 $patt = mb_substr($writehtml_e, $start, $l); 25800 if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { 25801 $writehtml_e = $m[1]; 25802 array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); 25803 $this->subPos = $writehtml_i + 3; 25804 return 4; 25805 } 25806 } 25807 } 25808 25809 // Check Backup SIP font (defined in Config\FontVariables) 25810 if (isset($this->backupSIPFont) && $this->backupSIPFont) { 25811 25812 if ($this->currentfontfamily != $this->backupSIPFont) { 25813 $font = $this->backupSIPFont; 25814 } else { 25815 unset($cw); 25816 return 0; 25817 } 25818 25819 unset($cw); 25820 $cw = ''; 25821 25822 if (isset($this->fonts[$font])) { 25823 $cw = &$this->fonts[$font]['cw']; 25824 } elseif ($this->fontCache->has($font . '.cw.dat')) { 25825 $cw = $this->fontCache->load($font . '.cw.dat'); 25826 } else { 25827 $prevFontFamily = $this->FontFamily; 25828 $prevFontStyle = $this->currentfontstyle; 25829 $prevFontSizePt = $this->FontSizePt; 25830 $this->SetFont($this->backupSIPFont, '', '', false); 25831 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false); 25832 } 25833 25834 if (!$cw) { 25835 return 0; 25836 } 25837 25838 $l = 0; 25839 foreach ($u as $char) { 25840 if ($this->_charDefined($cw, $char) || $char > 131071) { 25841 $l++; 25842 } else { 25843 break; 25844 } 25845 } 25846 25847 if ($l > 0) { 25848 $patt = mb_substr($writehtml_e, $start, $l); 25849 if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { 25850 $writehtml_e = $m[1]; 25851 array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); 25852 $this->subPos = $writehtml_i + 3; 25853 return 4; 25854 } 25855 } 25856 } 25857 25858 return 0; 25859 } 25860 25861 // FIRST TRY CORE FONTS (when appropriate) 25862 if (!$this->PDFA && !$this->PDFX && !$this->biDirectional) { // mPDF 6 25863 $repl = []; 25864 if (!$this->subArrMB) { 25865 require __DIR__ . '/../data/subs_core.php'; 25866 $this->subArrMB['a'] = $aarr; 25867 $this->subArrMB['s'] = $sarr; 25868 $this->subArrMB['z'] = $zarr; 25869 } 25870 if (isset($this->subArrMB['a'][$u[0]])) { 25871 $font = 'tta'; 25872 $ftype = 'C'; 25873 foreach ($u as $char) { 25874 if (isset($this->subArrMB['a'][$char])) { 25875 $repl[] = $this->subArrMB['a'][$char]; 25876 } else { 25877 break; 25878 } 25879 } 25880 } elseif (isset($this->subArrMB['z'][$u[0]])) { 25881 $font = 'ttz'; 25882 $ftype = 'C'; 25883 foreach ($u as $char) { 25884 if (isset($this->subArrMB['z'][$char])) { 25885 $repl[] = $this->subArrMB['z'][$char]; 25886 } else { 25887 break; 25888 } 25889 } 25890 } elseif (isset($this->subArrMB['s'][$u[0]])) { 25891 $font = 'tts'; 25892 $ftype = 'C'; 25893 foreach ($u as $char) { 25894 if (isset($this->subArrMB['s'][$char])) { 25895 $repl[] = $this->subArrMB['s'][$char]; 25896 } else { 25897 break; 25898 } 25899 } 25900 } 25901 if ($ftype == 'C') { 25902 $patt = mb_substr($writehtml_e, $start, count($repl)); 25903 if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { 25904 $writehtml_e = $m[1]; 25905 array_splice($writehtml_a, $writehtml_i + 1, 0, [$font, implode('|', $repl), '/' . $font, $m[3]]); // e.g. <tts> 25906 $this->subPos = $writehtml_i + 3; 25907 return 4; 25908 } 25909 return 0; 25910 } 25911 } 25912 25913 // LASTLY TRY IN BACKUP SUBS FONT 25914 if (!is_array($this->backupSubsFont)) { 25915 $this->backupSubsFont = ["$this->backupSubsFont"]; 25916 } 25917 25918 foreach ($this->backupSubsFont as $bsfctr => $bsf) { 25919 if ($this->currentfontfamily != $bsf) { 25920 $font = $bsf; 25921 } else { 25922 continue; 25923 } 25924 25925 unset($cw); 25926 $cw = ''; 25927 25928 if (isset($this->fonts[$font])) { 25929 $cw = &$this->fonts[$font]['cw']; 25930 } elseif ($this->fontCache->has($font . '.cw.dat')) { 25931 $cw = $this->fontCache->load($font . '.cw.dat'); 25932 } else { 25933 $prevFontFamily = $this->FontFamily; 25934 $prevFontStyle = $this->currentfontstyle; 25935 $prevFontSizePt = $this->FontSizePt; 25936 $this->SetFont($bsf, '', '', false); 25937 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false); 25938 if ($this->fontCache->has($font . '.cw.dat')) { 25939 $cw = $this->fontCache->load($font . '.cw.dat'); 25940 } 25941 } 25942 25943 if (!$cw) { 25944 continue; 25945 } 25946 25947 $l = 0; 25948 foreach ($u as $char) { 25949 if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) { // Arabic and Indic 25950 $l++; 25951 } else { 25952 if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font 25953 $cont = mb_substr($writehtml_e, $start + 1); 25954 $writehtml_e = mb_substr($writehtml_e, 0, $start + 1); 25955 array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]); 25956 $this->subPos = $writehtml_i + 1; 25957 return 2; 25958 } else { 25959 break; 25960 } 25961 } 25962 } 25963 25964 if ($l > 0) { 25965 $patt = mb_substr($writehtml_e, $start, $l); 25966 if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) { 25967 $writehtml_e = $m[1]; 25968 array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]); 25969 $this->subPos = $writehtml_i + 3; 25970 return 4; 25971 } 25972 } 25973 } 25974 25975 unset($cw); 25976 25977 return 0; 25978 } 25979 25980 function setHiEntitySubstitutions() 25981 { 25982 $entarr = include __DIR__ . '/../data/entity_substitutions.php'; 25983 25984 foreach ($entarr as $key => $val) { 25985 $this->entsearch[] = '&' . $key . ';'; 25986 $this->entsubstitute[] = UtfString::code2utf($val); 25987 } 25988 } 25989 25990 function SubstituteHiEntities($html) 25991 { 25992 // converts html_entities > ASCII 127 to unicode 25993 // Leaves in particular < to distinguish from tag marker 25994 if (count($this->entsearch)) { 25995 $html = str_replace($this->entsearch, $this->entsubstitute, $html); 25996 } 25997 25998 return $html; 25999 } 26000 26001 /** 26002 * Edited v1.2 Pass by reference; option to continue if invalid UTF-8 chars 26003 */ 26004 function is_utf8(&$string) 26005 { 26006 if ($string === mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32")) { 26007 return true; 26008 } 26009 26010 if ($this->ignore_invalid_utf8) { 26011 $string = mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32"); 26012 return true; 26013 } 26014 26015 return false; 26016 } 26017 26018 /** 26019 * For HTML 26020 * 26021 * Checks string is valid UTF-8 encoded 26022 * converts html_entities > ASCII 127 to UTF-8 26023 * Only exception - leaves low ASCII entities e.g. < & etc. 26024 * Leaves in particular < to distinguish from tag marker 26025 */ 26026 function purify_utf8($html, $lo = true) 26027 { 26028 if (!$this->is_utf8($html)) { 26029 26030 while (mb_convert_encoding(mb_convert_encoding($html, "UTF-32", "UTF-8"), "UTF-8", "UTF-32") != $html) { 26031 26032 $a = @iconv('UTF-8', 'UTF-8', $html); 26033 $error = error_get_last(); 26034 if ($error && $error['message'] === 'iconv(): Detected an illegal character in input string') { 26035 throw new \Mpdf\MpdfException('Invalid input characters. Did you set $mpdf->in_charset properly?'); 26036 } 26037 26038 $pos = $start = strlen($a); 26039 $err = ''; 26040 while (ord(substr($html, $pos, 1)) > 128) { 26041 $err .= '[[#' . ord(substr($html, $pos, 1)) . ']]'; 26042 $pos++; 26043 } 26044 26045 $this->logger->error($err, ['context' => LogContext::UTF8]); 26046 $html = substr($html, $pos); 26047 } 26048 26049 throw new \Mpdf\MpdfException("HTML contains invalid UTF-8 character(s). See log for further details"); 26050 } 26051 26052 $html = preg_replace("/\r/", "", $html); 26053 26054 // converts html_entities > ASCII 127 to UTF-8 26055 // Leaves in particular < to distinguish from tag marker 26056 $html = $this->SubstituteHiEntities($html); 26057 26058 // converts all &#nnn; or &#xHHH; to UTF-8 multibyte 26059 // If $lo==true then includes ASCII < 128 26060 $html = UtfString::strcode2utf($html, $lo); 26061 26062 return $html; 26063 } 26064 26065 /** 26066 * For TEXT 26067 */ 26068 function purify_utf8_text($txt) 26069 { 26070 // Make sure UTF-8 string of characters 26071 if (!$this->is_utf8($txt)) { 26072 throw new \Mpdf\MpdfException("Text contains invalid UTF-8 character(s)"); 26073 } 26074 26075 $txt = preg_replace("/\r/", "", $txt); 26076 26077 return ($txt); 26078 } 26079 26080 function all_entities_to_utf8($txt) 26081 { 26082 // converts txt_entities > ASCII 127 to UTF-8 26083 // Leaves in particular < to distinguish from tag marker 26084 $txt = $this->SubstituteHiEntities($txt); 26085 26086 // converts all &#nnn; or &#xHHH; to UTF-8 multibyte 26087 $txt = UtfString::strcode2utf($txt); 26088 26089 $txt = $this->lesser_entity_decode($txt); 26090 return ($txt); 26091 } 26092 26093 /* -- BARCODES -- */ 26094 /** 26095 * UPC/EAN barcode 26096 * 26097 * EAN13, EAN8, UPCA, UPCE, ISBN, ISSN 26098 * Accepts 12 or 13 digits with or without - hyphens 26099 */ 26100 function WriteBarcode($code, $showtext = 1, $x = '', $y = '', $size = 1, $border = 0, $paddingL = 1, $paddingR = 1, $paddingT = 2, $paddingB = 2, $height = 1, $bgcol = false, $col = false, $btype = 'ISBN', $supplement = '0', $supplement_code = '', $k = 1) 26101 { 26102 if (empty($code)) { 26103 return; 26104 } 26105 26106 $codestr = $code; 26107 $code = preg_replace('/\-/', '', $code); 26108 26109 $this->barcode = new Barcode(); 26110 if ($btype == 'ISSN' || $btype == 'ISBN') { 26111 $arrcode = $this->barcode->getBarcodeArray($code, 'EAN13'); 26112 } else { 26113 $arrcode = $this->barcode->getBarcodeArray($code, $btype); 26114 } 26115 26116 if ($arrcode === false) { 26117 throw new \Mpdf\MpdfException('Error in barcode string: ' . $codestr); 26118 } 26119 26120 if ((($btype === 'EAN13' || $btype === 'ISBN' || $btype === 'ISSN') && strlen($code) === 12) 26121 || ($btype == 'UPCA' && strlen($code) === 11) 26122 || ($btype == 'UPCE' && strlen($code) === 11) 26123 || ($btype == 'EAN8' && strlen($code) === 7)) { 26124 26125 $code .= $arrcode['checkdigit']; 26126 26127 if (stristr($codestr, '-')) { 26128 $codestr .= '-' . $arrcode['checkdigit']; 26129 } else { 26130 $codestr .= $arrcode['checkdigit']; 26131 } 26132 } 26133 26134 if ($btype === 'ISBN') { 26135 $codestr = 'ISBN ' . $codestr; 26136 } 26137 26138 if ($btype === 'ISSN') { 26139 $codestr = 'ISSN ' . $codestr; 26140 } 26141 26142 if (empty($x)) { 26143 $x = $this->x; 26144 } 26145 26146 if (empty($y)) { 26147 $y = $this->y; 26148 } 26149 26150 // set foreground color 26151 $prevDrawColor = $this->DrawColor; 26152 $prevTextColor = $this->TextColor; 26153 $prevFillColor = $this->FillColor; 26154 26155 $lw = $this->LineWidth; 26156 $this->SetLineWidth(0.01); 26157 26158 $size /= $k; // in case resized in a table 26159 26160 $xres = $arrcode['nom-X'] * $size; 26161 $llm = $arrcode['lightmL'] * $arrcode['nom-X'] * $size; // Left Light margin 26162 $rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin 26163 26164 $bcw = ($arrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size 26165 26166 $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins 26167 $ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding 26168 26169 $fbwi = $fbw - 2; // Full barcode width incl. light margins - 2mm - for isbn string 26170 // cf. http://www.gs1uk.org/downloads/bar_code/Bar coding getting it right.pdf 26171 $num_height = 3 * $size; // Height of numerals 26172 $fbh = $arrcode['nom-H'] * $size * $height; // Full barcode height incl. numerals 26173 $bch = $fbh - (1.5 * $size); // Barcode height of bars (3mm for numerals) 26174 26175 if (($btype == 'EAN13' && $showtext) || $btype == 'ISSN' || $btype == 'ISBN') { // Add height for ISBN string + margin from top of bars 26176 $tisbnm = 1.5 * $size; // Top margin between isbn (if shown) & bars 26177 $codestr_fontsize = 2.1 * $size; 26178 $paddingT += $codestr_fontsize + $tisbnm; 26179 } 26180 26181 $oh = $fbh + $paddingT + $paddingB; // Full overall height incl. user-defined padding 26182 26183 // PRINT border background color 26184 $xpos = $x; 26185 $ypos = $y; 26186 26187 if ($col) { 26188 $this->SetDColor($col); 26189 $this->SetTColor($col); 26190 } else { 26191 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 26192 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 26193 } 26194 26195 if ($bgcol) { 26196 $this->SetFColor($bgcol); 26197 } else { 26198 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 26199 } 26200 26201 if (!$bgcol && !$col) { // fn. called directly - not via HTML 26202 26203 if ($border) { 26204 $fillb = 'DF'; 26205 } else { 26206 $fillb = 'F'; 26207 } 26208 26209 $this->Rect($xpos, $ypos, $ow, $oh, $fillb); 26210 } 26211 26212 26213 // PRINT BARS 26214 $xpos = $x + $paddingL + $llm; 26215 $ypos = $y + $paddingT; 26216 26217 if ($col) { 26218 $this->SetFColor($col); 26219 } else { 26220 $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 26221 } 26222 26223 if ($arrcode !== false) { 26224 foreach ($arrcode["bcode"] as $v) { 26225 $bw = ($v["w"] * $xres); 26226 if ($v["t"]) { 26227 // draw a vertical bar 26228 $this->Rect($xpos, $ypos, $bw, $bch, 'F'); 26229 } 26230 $xpos += $bw; 26231 } 26232 } 26233 26234 // print text 26235 $prevFontFamily = $this->FontFamily; 26236 $prevFontStyle = $this->FontStyle; 26237 $prevFontSizePt = $this->FontSizePt; 26238 26239 // ISBN string 26240 if (($btype === 'EAN13' && $showtext) || $btype === 'ISBN' || $btype === 'ISSN') { 26241 26242 if ($this->onlyCoreFonts) { 26243 $this->SetFont('chelvetica'); 26244 } else { 26245 $this->SetFont('sans'); 26246 } 26247 26248 if ($bgcol) { 26249 $this->SetFColor($bgcol); 26250 } else { 26251 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 26252 } 26253 26254 $this->x = $x + $paddingL + 1; // 1mm left margin (cf. $fbwi above) 26255 26256 // max width is $fbwi 26257 $loop = 0; 26258 while ($loop == 0) { 26259 $this->SetFontSize($codestr_fontsize * 1.4 * Mpdf::SCALE, false); // don't write 26260 $sz = $this->GetStringWidth($codestr); 26261 26262 if ($sz > $fbwi) { 26263 $codestr_fontsize -= 0.1; 26264 } else { 26265 $loop ++; 26266 } 26267 } 26268 26269 $this->SetFont('', '', $codestr_fontsize * 1.4 * Mpdf::SCALE, true, true); // * 1.4 because font height is only 7/10 of given mm 26270 // WORD SPACING 26271 if ($fbwi > $sz) { 26272 $xtra = $fbwi - $sz; 26273 $charspacing = $xtra / (strlen($codestr) - 1); 26274 if ($charspacing) { 26275 $this->writer->write(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE)); 26276 } 26277 } 26278 26279 $this->y = $y + $paddingT - ($codestr_fontsize ) - $tisbnm; 26280 $this->Cell($fbw, $codestr_fontsize, $codestr); 26281 26282 if ($charspacing) { 26283 $this->writer->write('BT 0 Tc ET'); 26284 } 26285 } 26286 26287 26288 // Bottom NUMERALS 26289 // mPDF 5.7.4 26290 if ($this->onlyCoreFonts) { 26291 $this->SetFont('ccourier'); 26292 $fh = 1.3; 26293 } else { 26294 $this->SetFont('ocrb'); 26295 $fh = 1.06; 26296 } 26297 26298 $charRO = ''; 26299 26300 if ($btype === 'EAN13' || $btype === 'ISBN' || $btype === 'ISSN') { 26301 26302 $outerfontsize = 3; // Inner fontsize = 3 26303 $outerp = $xres * 4; 26304 $innerp = $xres * 2.5; 26305 $textw = ($bcw * 0.5) - $outerp - $innerp; 26306 $chars = 6; // number of numerals in each half 26307 $charLO = substr($code, 0, 1); // Left Outer 26308 $charLI = substr($code, 1, 6); // Left Inner 26309 $charRI = substr($code, 7, 6); // Right Inner 26310 26311 if (!$supplement) { 26312 $charRO = '>'; // Right Outer 26313 } 26314 26315 } elseif ($btype === 'UPCA') { 26316 26317 $outerfontsize = 2.3; // Inner fontsize = 3 26318 $outerp = $xres * 10; 26319 $innerp = $xres * 2.5; 26320 $textw = ($bcw * 0.5) - $outerp - $innerp; 26321 $chars = 5; 26322 $charLO = substr($code, 0, 1); // Left Outer 26323 $charLI = substr($code, 1, 5); // Left Inner 26324 $charRI = substr($code, 6, 5); // Right Inner 26325 $charRO = substr($code, 11, 1); // Right Outer 26326 26327 } elseif ($btype === 'UPCE') { 26328 26329 $outerfontsize = 2.3; // Inner fontsize = 3 26330 $outerp = $xres * 4; 26331 $innerp = 0; 26332 $textw = ($bcw * 0.5) - $outerp - $innerp; 26333 $chars = 3; 26334 $upce_code = $arrcode['code']; 26335 $charLO = substr($code, 0, 1); // Left Outer 26336 $charLI = substr($upce_code, 0, 3); // Left Inner 26337 $charRI = substr($upce_code, 3, 3); // Right Inner 26338 $charRO = substr($code, 11, 1); // Right Outer 26339 26340 } elseif ($btype === 'EAN8') { 26341 26342 $outerfontsize = 3; // Inner fontsize = 3 26343 $outerp = $xres * 4; 26344 $innerp = $xres * 2.5; 26345 $textw = ($bcw * 0.5) - $outerp - $innerp; 26346 $chars = 4; 26347 $charLO = '<'; // Left Outer 26348 $charLI = substr($code, 0, 4); // Left Inner 26349 $charRI = substr($code, 4, 4); // Right Inner 26350 26351 if (!$supplement) { 26352 $charRO = '>'; // Right Outer 26353 } 26354 } 26355 26356 $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters) 26357 26358 if (!$this->usingCoreFont) { // character width at 3mm 26359 $cw = $this->_getCharWidth($this->CurrentFont['cw'], 32) * 3 * $fh * $size / 1000; 26360 } else { 26361 $cw = 600 * 3 * $fh * $size / 1000; 26362 } 26363 26364 // Outer left character 26365 $y_text = $y + $paddingT + $bch - ($num_height / 2); 26366 $y_text_outer = $y + $paddingT + $bch - ($num_height * ($outerfontsize / 3) / 2); 26367 26368 $this->x = $x + $paddingL - ($cw * ($outerfontsize / 3) * 0.1); // 0.1 is correction as char does not fill full width; 26369 $this->y = $y_text_outer; 26370 $this->Cell($cw, $num_height, $charLO); 26371 26372 // WORD SPACING for inner chars 26373 $xtra = $textw - ($cw * $chars); 26374 $charspacing = $xtra / ($chars - 1); 26375 if ($charspacing) { 26376 $this->writer->write(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE)); 26377 } 26378 26379 if ($bgcol) { 26380 $this->SetFColor($bgcol); 26381 } else { 26382 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 26383 } 26384 26385 $this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters) 26386 26387 // Inner left half characters 26388 $this->x = $x + $paddingL + $llm + $outerp; 26389 $this->y = $y_text; 26390 $this->Cell($textw, $num_height, $charLI, 0, 0, '', 1); 26391 26392 // Inner right half characters 26393 $this->x = $x + $paddingL + $llm + ($bcw * 0.5) + $innerp; 26394 $this->y = $y_text; 26395 $this->Cell($textw, $num_height, $charRI, 0, 0, '', 1); 26396 26397 if ($charspacing) { 26398 $this->writer->write('BT 0 Tc ET'); 26399 } 26400 26401 // Outer Right character 26402 $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters) 26403 26404 $this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * ($outerfontsize / 3) * 0.9); // 0.9 is correction as char does not fill full width 26405 $this->y = $y_text_outer; 26406 $this->Cell($cw * ($outerfontsize / 3), $num_height, $charRO, 0, 0, 'R'); 26407 26408 if ($supplement) { // EAN-2 or -5 Supplement 26409 // PRINT BARS 26410 $supparrcode = $this->barcode->getBarcodeArray($supplement_code, 'EAN' . $supplement); 26411 26412 if ($supparrcode === false) { 26413 throw new \Mpdf\MpdfException('Error in barcode string (supplement): ' . $codestr . ' ' . $supplement_code); 26414 } 26415 26416 if (strlen($supplement_code) != $supplement) { 26417 throw new \Mpdf\MpdfException('Barcode supplement incorrect: ' . $supplement_code); 26418 } 26419 26420 $llm = $fbw - (($arrcode['lightmR'] - $supparrcode['sepM']) * $arrcode['nom-X'] * $size); // Left Light margin 26421 $rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin 26422 26423 $bcw = ($supparrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size 26424 26425 $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins 26426 $ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding 26427 $bch = $fbh - (1.5 * $size) - ($num_height + 0.5); // Barcode height of bars (3mm for numerals) 26428 26429 $xpos = $x + $paddingL + $llm; 26430 $ypos = $y + $paddingT + $num_height + 0.5; 26431 26432 if ($col) { 26433 $this->SetFColor($col); 26434 } else { 26435 $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 26436 } 26437 26438 if ($supparrcode !== false) { 26439 foreach ($supparrcode["bcode"] as $v) { 26440 $bw = ($v["w"] * $xres); 26441 if ($v["t"]) { 26442 // draw a vertical bar 26443 $this->Rect($xpos, $ypos, $bw, $bch, 'F'); 26444 } 26445 $xpos += $bw; 26446 } 26447 } 26448 26449 // Characters 26450 if ($bgcol) { 26451 $this->SetFColor($bgcol); 26452 } else { 26453 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 26454 } 26455 26456 $this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters) 26457 $this->x = $x + $paddingL + $llm; 26458 $this->y = $y + $paddingT; 26459 $this->Cell($bcw, $num_height, $supplement_code, 0, 0, 'C'); 26460 26461 // Outer Right character (light margin) 26462 $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters) 26463 $this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * 0.9); // 0.9 is correction as char does not fill full width 26464 $this->y = $y + $paddingT; 26465 $this->Cell($cw * ($outerfontsize / 3), $num_height, '>', 0, 0, 'R'); 26466 } 26467 26468 // Restore ************** 26469 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt); 26470 $this->DrawColor = $prevDrawColor; 26471 $this->TextColor = $prevTextColor; 26472 $this->FillColor = $prevFillColor; 26473 $this->SetLineWidth($lw); 26474 $this->SetY($y); 26475 } 26476 26477 /** 26478 * POSTAL and OTHER barcodes 26479 */ 26480 function WriteBarcode2($code, $x = '', $y = '', $size = 1, $height = 1, $bgcol = false, $col = false, $btype = 'IMB', $print_ratio = '', $k = 1, $quiet_zone_left = null, $quiet_zone_right = null) 26481 { 26482 if (empty($code)) { 26483 return; 26484 } 26485 26486 $this->barcode = new Barcode(); 26487 $arrcode = $this->barcode->getBarcodeArray($code, $btype, $print_ratio, $quiet_zone_left, $quiet_zone_right); 26488 26489 if (empty($x)) { 26490 $x = $this->x; 26491 } 26492 26493 if (empty($y)) { 26494 $y = $this->y; 26495 } 26496 26497 $prevDrawColor = $this->DrawColor; 26498 $prevTextColor = $this->TextColor; 26499 $prevFillColor = $this->FillColor; 26500 $lw = $this->LineWidth; 26501 $this->SetLineWidth(0.01); 26502 $size /= $k; // in case resized in a table 26503 $xres = $arrcode['nom-X'] * $size; 26504 26505 if ($btype === 'IMB' || $btype === 'RM4SCC' || $btype === 'KIX' || $btype === 'POSTNET' || $btype === 'PLANET') { 26506 $llm = $arrcode['quietL'] / $k; // Left Quiet margin 26507 $rlm = $arrcode['quietR'] / $k; // Right Quiet margin 26508 $tlm = $blm = $arrcode['quietTB'] / $k; 26509 $height = 1; // Overrides 26510 } elseif (in_array($btype, ['C128A', 'C128B', 'C128C', 'C128RAW', 'EAN128A', 'EAN128B', 'EAN128C', 'C39', 'C39+', 'C39E', 'C39E+', 'S25', 'S25+', 'I25', 'I25+', 'I25B', 'I25B+', 'C93', 'MSI', 'MSI+', 'CODABAR', 'CODE11'])) { 26511 $llm = $arrcode['lightmL'] * $xres; // Left Quiet margin 26512 $rlm = $arrcode['lightmR'] * $xres; // Right Quiet margin 26513 $tlm = $blm = $arrcode['lightTB'] * $xres * $height; 26514 } 26515 26516 $bcw = ($arrcode["maxw"] * $xres); 26517 $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins 26518 26519 $bch = ($arrcode["nom-H"] * $size * $height); 26520 $fbh = $bch + $tlm + $blm; // Full barcode height 26521 26522 // PRINT border background color 26523 $xpos = $x; 26524 $ypos = $y; 26525 26526 if ($col) { 26527 $this->SetDColor($col); 26528 $this->SetTColor($col); 26529 } else { 26530 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 26531 $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 26532 } 26533 26534 if ($bgcol) { 26535 $this->SetFColor($bgcol); 26536 } else { 26537 $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings)); 26538 } 26539 26540 // PRINT BARS 26541 if ($col) { 26542 $this->SetFColor($col); 26543 } else { 26544 $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 26545 } 26546 $xpos = $x + $llm; 26547 26548 if ($arrcode !== false) { 26549 foreach ($arrcode["bcode"] as $v) { 26550 $bw = ($v["w"] * $xres); 26551 if ($v["t"]) { 26552 $ypos = $y + $tlm + ($bch * $v['p'] / $arrcode['maxh']); 26553 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $bch / $arrcode['maxh']), 'F'); 26554 } 26555 $xpos += $bw; 26556 } 26557 } 26558 26559 // PRINT BEARER BARS 26560 if ($btype == 'I25B' || $btype == 'I25B+') { 26561 $this->Rect($x, $y, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F'); 26562 $this->Rect($x, $y + $tlm + $bch, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F'); 26563 } 26564 26565 // Restore ************** 26566 $this->DrawColor = $prevDrawColor; 26567 $this->TextColor = $prevTextColor; 26568 $this->FillColor = $prevFillColor; 26569 $this->SetLineWidth($lw); 26570 $this->SetY($y); 26571 } 26572 /* -- END BARCODES -- */ 26573 26574 function StartTransform($returnstring = false) 26575 { 26576 if ($returnstring) { 26577 return('q'); 26578 } else { 26579 $this->writer->write('q'); 26580 } 26581 } 26582 26583 function StopTransform($returnstring = false) 26584 { 26585 if ($returnstring) { 26586 return('Q'); 26587 } else { 26588 $this->writer->write('Q'); 26589 } 26590 } 26591 26592 function transformScale($s_x, $s_y, $x = '', $y = '', $returnstring = false) 26593 { 26594 if ($x === '') { 26595 $x = $this->x; 26596 } 26597 26598 if ($y === '') { 26599 $y = $this->y; 26600 } 26601 26602 if (($s_x == 0) or ( $s_y == 0)) { 26603 throw new \Mpdf\MpdfException('Please do not use values equal to zero for scaling'); 26604 } 26605 26606 $y = ($this->h - $y) * Mpdf::SCALE; 26607 $x *= Mpdf::SCALE; 26608 26609 // calculate elements of transformation matrix 26610 $s_x /= 100; 26611 $s_y /= 100; 26612 $tm = []; 26613 $tm[0] = $s_x; 26614 $tm[1] = 0; 26615 $tm[2] = 0; 26616 $tm[3] = $s_y; 26617 $tm[4] = $x * (1 - $s_x); 26618 $tm[5] = $y * (1 - $s_y); 26619 26620 // scale the coordinate system 26621 if ($returnstring) { 26622 return($this->_transform($tm, true)); 26623 } else { 26624 $this->_transform($tm); 26625 } 26626 } 26627 26628 function transformTranslate($t_x, $t_y, $returnstring = false) 26629 { 26630 // calculate elements of transformation matrix 26631 $tm = []; 26632 $tm[0] = 1; 26633 $tm[1] = 0; 26634 $tm[2] = 0; 26635 $tm[3] = 1; 26636 $tm[4] = $t_x * Mpdf::SCALE; 26637 $tm[5] = -$t_y * Mpdf::SCALE; 26638 26639 // translate the coordinate system 26640 if ($returnstring) { 26641 return($this->_transform($tm, true)); 26642 } else { 26643 $this->_transform($tm); 26644 } 26645 } 26646 26647 function transformRotate($angle, $x = '', $y = '', $returnstring = false) 26648 { 26649 if ($x === '') { 26650 $x = $this->x; 26651 } 26652 26653 if ($y === '') { 26654 $y = $this->y; 26655 } 26656 26657 $angle = -$angle; 26658 $y = ($this->h - $y) * Mpdf::SCALE; 26659 $x *= Mpdf::SCALE; 26660 26661 // calculate elements of transformation matrix 26662 $tm = []; 26663 $tm[0] = cos(deg2rad($angle)); 26664 $tm[1] = sin(deg2rad($angle)); 26665 $tm[2] = -$tm[1]; 26666 $tm[3] = $tm[0]; 26667 $tm[4] = $x + $tm[1] * $y - $tm[0] * $x; 26668 $tm[5] = $y - $tm[0] * $y - $tm[1] * $x; 26669 26670 // rotate the coordinate system around ($x,$y) 26671 if ($returnstring) { 26672 return $this->_transform($tm, true); 26673 } else { 26674 $this->_transform($tm); 26675 } 26676 } 26677 26678 /** 26679 * mPDF 5.7.3 TRANSFORMS 26680 */ 26681 function transformSkew($angle_x, $angle_y, $x = '', $y = '', $returnstring = false) 26682 { 26683 if ($x === '') { 26684 $x = $this->x; 26685 } 26686 26687 if ($y === '') { 26688 $y = $this->y; 26689 } 26690 26691 $angle_x = -$angle_x; 26692 $angle_y = -$angle_y; 26693 26694 $x *= Mpdf::SCALE; 26695 $y = ($this->h - $y) * Mpdf::SCALE; 26696 26697 // calculate elements of transformation matrix 26698 $tm = []; 26699 $tm[0] = 1; 26700 $tm[1] = tan(deg2rad($angle_y)); 26701 $tm[2] = tan(deg2rad($angle_x)); 26702 $tm[3] = 1; 26703 $tm[4] = -$tm[2] * $y; 26704 $tm[5] = -$tm[1] * $x; 26705 26706 // skew the coordinate system 26707 if ($returnstring) { 26708 return $this->_transform($tm, true); 26709 } else { 26710 $this->_transform($tm); 26711 } 26712 } 26713 26714 function _transform($tm, $returnstring = false) 26715 { 26716 if ($returnstring) { 26717 return(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); 26718 } else { 26719 $this->writer->write(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); 26720 } 26721 } 26722 26723 // AUTOFONT ========================= 26724 function markScriptToLang($html) 26725 { 26726 if ($this->onlyCoreFonts) { 26727 return $html; 26728 } 26729 26730 $n = ''; 26731 $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE); 26732 foreach ($a as $i => $e) { 26733 if ($i % 2 == 0) { 26734 26735 // ignore if in Textarea 26736 if ($i > 0 && strtolower(substr($a[$i - 1], 1, 8)) == 'textarea') { 26737 $a[$i] = $e; 26738 continue; 26739 } 26740 26741 $e = UtfString::strcode2utf($e); 26742 $e = $this->lesser_entity_decode($e); 26743 26744 $earr = $this->UTF8StringToArray($e, false); 26745 26746 $scriptblock = 0; 26747 $scriptblocks = []; 26748 $scriptblocks[0] = 0; 26749 $chardata = []; 26750 $subchunk = 0; 26751 $charctr = 0; 26752 26753 foreach ($earr as $char) { 26754 26755 $ucd_record = Ucdn::get_ucd_record($char); 26756 $sbl = $ucd_record[6]; 26757 26758 if ($sbl && $sbl != 40 && $sbl != 102) { 26759 if ($scriptblock == 0) { 26760 $scriptblock = $sbl; 26761 $scriptblocks[$subchunk] = $scriptblock; 26762 } elseif ($scriptblock > 0 && $scriptblock != $sbl) { 26763 // NEW (non-common) Script encountered in this chunk. 26764 // Start a new subchunk 26765 $subchunk++; 26766 $scriptblock = $sbl; 26767 $charctr = 0; 26768 $scriptblocks[$subchunk] = $scriptblock; 26769 } 26770 } 26771 26772 $chardata[$subchunk][$charctr]['script'] = $sbl; 26773 $chardata[$subchunk][$charctr]['uni'] = $char; 26774 $charctr++; 26775 } 26776 26777 // If scriptblock[x] = common & non-baseScript 26778 // and scriptblock[x+1] = baseScript 26779 // Move common script from end of x to start of x+1 26780 for ($sch = 0; $sch < $subchunk; $sch++) { 26781 if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $scriptblocks[$sch + 1] == $this->baseScript) { 26782 $end = count($chardata[$sch]) - 1; 26783 while ($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script 26784 $tmp = array_pop($chardata[$sch]); 26785 array_unshift($chardata[$sch + 1], $tmp); 26786 $end--; 26787 } 26788 } 26789 } 26790 26791 $o = ''; 26792 for ($sch = 0; $sch <= $subchunk; $sch++) { 26793 26794 if (isset($chardata[$sch])) { 26795 $s = ''; 26796 for ($j = 0; $j < count($chardata[$sch]); $j++) { 26797 $s .= UtfString::code2utf($chardata[$sch][$j]['uni']); 26798 } 26799 26800 // ZZZ99 Undo lesser_entity_decode as above - but only for <>& 26801 $s = str_replace("&", "&", $s); 26802 $s = str_replace("<", "<", $s); 26803 $s = str_replace(">", ">", $s); 26804 26805 // Check Vietnamese if Latin script - even if Basescript 26806 if ($scriptblocks[$sch] == Ucdn::SCRIPT_LATIN && $this->autoVietnamese && preg_match("/([" . $this->scriptToLanguage->getLanguageDelimiters('viet') . "])/u", $s)) { 26807 $o .= '<span lang="vi" class="lang_vi">' . $s . '</span>'; 26808 } elseif ($scriptblocks[$sch] == Ucdn::SCRIPT_ARABIC && $this->autoArabic) { // Check Arabic for different languages if Arabic script - even if Basescript 26809 if (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('sindhi') . "]/u", $s)) { 26810 $o .= '<span lang="sd" class="lang_sd">' . $s . '</span>'; 26811 } elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('urdu') . "]/u", $s)) { 26812 $o .= '<span lang="ur" class="lang_ur">' . $s . '</span>'; 26813 } elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('pashto') . "]/u", $s)) { 26814 $o .= '<span lang="ps" class="lang_ps">' . $s . '</span>'; 26815 } elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('persian') . "]/u", $s)) { 26816 $o .= '<span lang="fa" class="lang_fa">' . $s . '</span>'; 26817 } elseif ($this->baseScript != Ucdn::SCRIPT_ARABIC && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) { 26818 $o .= '<span lang="' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '" class="lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '">' . $s . '</span>'; 26819 } else { 26820 // Just output chars 26821 $o .= $s; 26822 } 26823 } elseif ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) { // Identify Script block if not Basescript, and mark up as language 26824 // Encase in <span> 26825 $o .= '<span lang="' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '" class="lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '">'; 26826 $o .= $s; 26827 $o .= '</span>'; 26828 } else { 26829 // Just output chars 26830 $o .= $s; 26831 } 26832 } 26833 } 26834 26835 $a[$i] = $o; 26836 } else { 26837 $a[$i] = '<' . $e . '>'; 26838 } 26839 } 26840 26841 $n = implode('', $a); 26842 26843 return $n; 26844 } 26845 26846 /* -- COLUMNS -- */ 26847 /** 26848 * Callback function from function printcolumnbuffer in mpdf 26849 */ 26850 function columnAdjustAdd($type, $k, $xadj, $yadj, $a, $b, $c = 0, $d = 0, $e = 0, $f = 0) 26851 { 26852 if ($type === 'Td') { // xpos,ypos 26853 26854 $a += ($xadj * $k); 26855 $b -= ($yadj * $k); 26856 26857 return 'BT ' . sprintf('%.3F %.3F', $a, $b) . ' Td'; 26858 26859 } elseif ($type === 're') { // xpos,ypos,width,height 26860 26861 $a += ($xadj * $k); 26862 $b -= ($yadj * $k); 26863 26864 return sprintf('%.3F %.3F %.3F %.3F', $a, $b, $c, $d) . ' re'; 26865 26866 } elseif ($type === 'l') { // xpos,ypos,x2pos,y2pos 26867 26868 $a += ($xadj * $k); 26869 $b -= ($yadj * $k); 26870 26871 return sprintf('%.3F %.3F l', $a, $b); 26872 26873 } elseif ($type === 'img') { // width,height,xpos,ypos 26874 26875 $c += ($xadj * $k); 26876 $d -= ($yadj * $k); 26877 26878 return sprintf('q %.3F 0 0 %.3F %.3F %.3F', $a, $b, $c, $d) . ' cm /' . $e; 26879 26880 } elseif ($type === 'draw') { // xpos,ypos 26881 26882 $a += ($xadj * $k); 26883 $b -= ($yadj * $k); 26884 26885 return sprintf('%.3F %.3F m', $a, $b); 26886 26887 } elseif ($type === 'bezier') { // xpos,ypos,x2pos,y2pos,x3pos,y3pos 26888 26889 $a += ($xadj * $k); 26890 $b -= ($yadj * $k); 26891 $c += ($xadj * $k); 26892 $d -= ($yadj * $k); 26893 $e += ($xadj * $k); 26894 $f -= ($yadj * $k); 26895 26896 return sprintf('%.3F %.3F %.3F %.3F %.3F %.3F', $a, $b, $c, $d, $e, $f) . ' c'; 26897 } 26898 } 26899 26900 /* -- END COLUMNS -- */ 26901 26902 // mPDF 5.7.3 TRANSFORMS 26903 function ConvertAngle($s, $makepositive = true) 26904 { 26905 if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i', $s, $m)) { 26906 26907 $angle = $m[1] + 0; 26908 26909 if (strtolower($m[2]) == 'deg') { 26910 $angle = $angle; 26911 } elseif (strtolower($m[2]) == 'grad') { 26912 $angle *= (360 / 400); 26913 } elseif (strtolower($m[2]) == 'rad') { 26914 $angle = rad2deg($angle); 26915 } 26916 26917 while ($angle >= 360) { 26918 $angle -= 360; 26919 } 26920 26921 while ($angle <= -360) { 26922 $angle += 360; 26923 } 26924 26925 if ($makepositive) { // always returns an angle between 0 and 360deg 26926 if ($angle < 0) { 26927 $angle += 360; 26928 } 26929 } 26930 26931 } else { 26932 $angle = $s + 0; 26933 } 26934 26935 return $angle; 26936 } 26937 26938 function lesser_entity_decode($html) 26939 { 26940 // supports the most used entity codes (only does ascii safe characters) 26941 $html = str_replace("<", "<", $html); 26942 $html = str_replace(">", ">", $html); 26943 26944 $html = str_replace("'", "'", $html); 26945 $html = str_replace(""", '"', $html); 26946 $html = str_replace("&", "&", $html); 26947 26948 return $html; 26949 } 26950 26951 function AdjustHTML($html, $tabSpaces = 8) 26952 { 26953 $limit = ini_get('pcre.backtrack_limit'); 26954 26955 if (0 >= (int) $limit) { 26956 throw new \Mpdf\MpdfException(sprintf( 26957 'mPDF will not process HTML with disabled pcre.backtrack_limit to prevent unexpected behaviours, please set a positive backtrack limit.', 26958 $limit 26959 )); 26960 } 26961 26962 if (strlen($html) > (int) $limit) { 26963 throw new \Mpdf\MpdfException(sprintf( 26964 'The HTML code size is larger than pcre.backtrack_limit %d. You should use WriteHTML() with smaller string lengths.', 26965 $limit 26966 )); 26967 } 26968 26969 preg_match_all("/(<annotation.*?>)/si", $html, $m); 26970 if (count($m[1])) { 26971 for ($i = 0; $i < count($m[1]); $i++) { 26972 $sub = preg_replace("/\n/si", "\xbb\xa4\xac", $m[1][$i]); 26973 $html = preg_replace('/' . preg_quote($m[1][$i], '/') . '/si', $sub, $html); 26974 } 26975 } 26976 26977 preg_match_all("/(<svg.*?<\/svg>)/si", $html, $svgi); 26978 if (count($svgi[0])) { 26979 for ($i = 0; $i < count($svgi[0]); $i++) { 26980 $file = $this->cache->write('/_tempSVG' . uniqid(random_int(1, 100000), true) . '_' . $i . '.svg', $svgi[0][$i]); 26981 $html = str_replace($svgi[0][$i], '<img src="' . $file . '" />', $html); 26982 } 26983 } 26984 26985 // Remove javascript code from HTML (should not appear in the PDF file) 26986 $html = preg_replace('/<script.*?<\/script>/is', '', $html); 26987 26988 // Remove special comments 26989 $html = preg_replace('/<!--mpdf/i', '', $html); 26990 $html = preg_replace('/mpdf-->/i', '', $html); 26991 26992 // Remove comments from HTML (should not appear in the PDF file) 26993 $html = preg_replace('/<!--.*?-->/s', '', $html); 26994 26995 $html = preg_replace('/\f/', '', $html); // replace formfeed by nothing 26996 $html = preg_replace('/\r/', '', $html); // replace carriage return by nothing 26997 26998 // Well formed XHTML end tags 26999 $html = preg_replace('/<(br|hr)>/i', "<\\1 />", $html); // mPDF 6 27000 $html = preg_replace('/<(br|hr)\/>/i', "<\\1 />", $html); 27001 27002 // Get rid of empty <thead></thead> etc 27003 $html = preg_replace('/<tr>\s*<\/tr>/i', '', $html); 27004 $html = preg_replace('/<thead>\s*<\/thead>/i', '', $html); 27005 $html = preg_replace('/<tfoot>\s*<\/tfoot>/i', '', $html); 27006 $html = preg_replace('/<table[^>]*>\s*<\/table>/i', '', $html); 27007 27008 // Remove spaces at end of table cells 27009 $html = preg_replace("/[ \n\r]+<\/t(d|h)/", '</t\\1', $html); 27010 27011 $html = preg_replace("/[ ]*<dottab\s*[\/]*>[ ]*/", '<dottab />', $html); 27012 27013 // Concatenates any Substitute characters from symbols/dingbats 27014 $html = str_replace('</tts><tts>', '|', $html); 27015 $html = str_replace('</ttz><ttz>', '|', $html); 27016 $html = str_replace('</tta><tta>', '|', $html); 27017 27018 $html = preg_replace('/<br \/>\s*/is', "<br />", $html); 27019 27020 $html = preg_replace('/<wbr[ \/]*>\s*/is', "­", $html); 27021 27022 // Preserve '\n's in content between the tags <pre> and </pre> 27023 if (preg_match('/<pre/', $html)) { 27024 27025 $html_a = preg_split('/(\<\/?pre[^\>]*\>)/', $html, -1, 2); 27026 $h = []; 27027 $c = 0; 27028 27029 foreach ($html_a as $s) { 27030 if ($c > 1 && preg_match('/^<\/pre/i', $s)) { 27031 $c--; 27032 $s = preg_replace('/<\/pre/i', '</innerpre', $s); 27033 } elseif ($c > 0 && preg_match('/^<pre/i', $s)) { 27034 $c++; 27035 $s = preg_replace('/<pre/i', '<innerpre', $s); 27036 } elseif (preg_match('/^<pre/i', $s)) { 27037 $c++; 27038 } elseif (preg_match('/^<\/pre/i', $s)) { 27039 $c--; 27040 } 27041 array_push($h, $s); 27042 } 27043 27044 $html = implode('', $h); 27045 } 27046 27047 $thereispre = preg_match_all('#<pre(.*?)>(.*?)</pre>#si', $html, $temp); 27048 27049 // Preserve '\n's in content between the tags <textarea> and </textarea> 27050 $thereistextarea = preg_match_all('#<textarea(.*?)>(.*?)</textarea>#si', $html, $temp2); 27051 $html = preg_replace('/[\n]/', ' ', $html); // replace linefeed by spaces 27052 $html = preg_replace('/[\t]/', ' ', $html); // replace tabs by spaces 27053 27054 // Converts < to < when not a tag 27055 $html = preg_replace('/<([^!\/a-zA-Z_:])/i', '<\\1', $html); // mPDF 5.7.3 27056 $html = preg_replace("/[ ]+/", ' ', $html); 27057 27058 $html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html); 27059 $html = preg_replace('/\/(u|o)l>\s+<\/li/i', '/\\1l></li', $html); 27060 $html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html); 27061 $html = preg_replace('/\/li>\s+<li/i', '/li><li', $html); 27062 $html = preg_replace('/<(u|o)l([^>]*)>[ ]+/i', '<\\1l\\2>', $html); 27063 $html = preg_replace('/[ ]+<(u|o)l/i', '<\\1l', $html); 27064 27065 // Make self closing tabs valid XHTML 27066 // Tags which are self-closing: 1) Replaceable and 2) Non-replaced items 27067 $selftabs = 'input|hr|img|br|barcode|dottab'; 27068 $selftabs2 = 'indexentry|indexinsert|bookmark|watermarktext|watermarkimage|column_break|columnbreak|newcolumn|newpage|page_break|pagebreak|formfeed|columns|toc|tocpagebreak|setpageheader|setpagefooter|sethtmlpageheader|sethtmlpagefooter|annotation'; 27069 27070 // Fix self-closing tags which don't close themselves 27071 $html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . ')[^>\/]*)>/i', '\\1 />', $html); 27072 27073 // Fix self-closing tags that don't include a space between the tag name and the closing slash 27074 $html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . '))\/>/i', '\\1 />', $html); 27075 27076 $iterator = 0; 27077 while ($thereispre) { // Recover <pre attributes>content</pre> 27078 $temp[2][$iterator] = preg_replace('/<([^!\/a-zA-Z_:])/', '<\\1', $temp[2][$iterator]); // mPDF 5.7.2 // mPDF 5.7.3 27079 27080 $temp[2][$iterator] = preg_replace_callback("/^([^\n\t]*?)\t/m", [$this, 'tabs2spaces_callback'], $temp[2][$iterator]); // mPDF 5.7+ 27081 $temp[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp[2][$iterator]); 27082 27083 $temp[2][$iterator] = preg_replace('/\n/', "<br />", $temp[2][$iterator]); 27084 $temp[2][$iterator] = str_replace('\\', "\\\\", $temp[2][$iterator]); 27085 // $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.$temp[2][$iterator].'</erp>',$html,1); 27086 $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si', '<erp' . $temp[1][$iterator] . '>' . str_replace('$', '\$', $temp[2][$iterator]) . '</erp>', $html, 1); // mPDF 5.7+ 27087 $thereispre--; 27088 $iterator++; 27089 } 27090 27091 $iterator = 0; 27092 while ($thereistextarea) { // Recover <textarea attributes>content</textarea> 27093 $temp2[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp2[2][$iterator]); 27094 $temp2[2][$iterator] = str_replace('\\', "\\\\", $temp2[2][$iterator]); 27095 $html = preg_replace('#<textarea(.*?)>(.*?)</textarea>#si', '<aeratxet' . $temp2[1][$iterator] . '>' . trim($temp2[2][$iterator]) . '</aeratxet>', $html, 1); 27096 $thereistextarea--; 27097 $iterator++; 27098 } 27099 27100 // Restore original tag names 27101 $html = str_replace("<erp", "<pre", $html); 27102 $html = str_replace("</erp>", "</pre>", $html); 27103 $html = str_replace("<aeratxet", "<textarea", $html); 27104 $html = str_replace("</aeratxet>", "</textarea>", $html); 27105 $html = str_replace("</innerpre", "</pre", $html); 27106 $html = str_replace("<innerpre", "<pre", $html); 27107 27108 $html = preg_replace('/<textarea([^>]*)><\/textarea>/si', '<textarea\\1> </textarea>', $html); 27109 $html = preg_replace('/(<table[^>]*>)\s*(<caption)(.*?<\/caption>)(.*?<\/table>)/si', '\\2 position="top"\\3\\1\\4\\2 position="bottom"\\3', $html); // *TABLES* 27110 $html = preg_replace('/<(h[1-6])([^>]*)(>(?:(?!h[1-6]).)*?<\/\\1>\s*<table)/si', '<\\1\\2 keep-with-table="1"\\3', $html); // *TABLES* 27111 $html = preg_replace("/\xbb\xa4\xac/", "\n", $html); 27112 27113 // Fixes <p>₹</p> which browser copes with even though it is wrong! 27114 $html = preg_replace("/(&#[x]{0,1}[0-9a-f]{1,5})</i", "\\1;<", $html); 27115 27116 return $html; 27117 } 27118 27119 // mPDF 5.7+ 27120 function tabs2spaces_callback($matches) 27121 { 27122 return (stripslashes($matches[1]) . str_repeat(' ', $this->tabSpaces - (mb_strlen(stripslashes($matches[1])) % $this->tabSpaces))); 27123 } 27124 27125 // mPDF 5.7+ 27126 function date_callback($matches) 27127 { 27128 return date($matches[1]); 27129 } 27130 27131 // ========== OVERWRITE SEARCH STRING IN A PDF FILE ================ 27132 function OverWrite($file_in, $search, $replacement, $dest = Destination::DOWNLOAD, $file_out = "mpdf") 27133 { 27134 $pdf = file_get_contents($file_in); 27135 27136 if (!is_array($search)) { 27137 $x = $search; 27138 $search = [$x]; 27139 } 27140 if (!is_array($replacement)) { 27141 $x = $replacement; 27142 $replacement = [$x]; // mPDF 5.7.4 27143 } 27144 27145 if (!$this->onlyCoreFonts && !$this->usingCoreFont) { 27146 foreach ($search as $k => $val) { 27147 $search[$k] = $this->writer->utf8ToUtf16BigEndian($search[$k], false); 27148 $search[$k] = $this->writer->escape($search[$k]); 27149 $replacement[$k] = $this->writer->utf8ToUtf16BigEndian($replacement[$k], false); 27150 $replacement[$k] = $this->writer->escape($replacement[$k]); 27151 } 27152 } else { 27153 foreach ($replacement as $k => $val) { 27154 $replacement[$k] = mb_convert_encoding($replacement[$k], $this->mb_enc, 'utf-8'); 27155 $replacement[$k] = $this->writer->escape($replacement[$k]); 27156 } 27157 } 27158 27159 // Get xref into array 27160 $xref = []; 27161 preg_match("/xref\n0 (\d+)\n(.*?)\ntrailer/s", $pdf, $m); 27162 $xref_objid = $m[1]; 27163 preg_match_all('/(\d{10}) (\d{5}) (f|n)/', $m[2], $x); 27164 for ($i = 0; $i < count($x[0]); $i++) { 27165 $xref[] = [intval($x[1][$i]), $x[2][$i], $x[3][$i]]; 27166 } 27167 27168 $changes = []; 27169 preg_match("/<<\s*\/Type\s*\/Pages\s*\/Kids\s*\[(.*?)\]\s*\/Count/s", $pdf, $m); 27170 preg_match_all("/(\d+) 0 R /s", $m[1], $o); 27171 $objlist = $o[1]; 27172 27173 foreach ($objlist as $obj) { 27174 if ($this->compress) { 27175 preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Filter\s*\/FlateDecode\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m); 27176 } else { 27177 preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m); 27178 } 27179 27180 $s = $m[2]; 27181 if (!$s) { 27182 continue; 27183 } 27184 27185 $oldlen = $m[1]; 27186 27187 if ($this->encrypted) { 27188 $s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s); 27189 } 27190 27191 if ($this->compress) { 27192 $s = gzuncompress($s); 27193 } 27194 27195 foreach ($search as $k => $val) { 27196 $s = str_replace($search[$k], $replacement[$k], $s); 27197 } 27198 27199 if ($this->compress) { 27200 $s = gzcompress($s); 27201 } 27202 27203 if ($this->encrypted) { 27204 $s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s); 27205 } 27206 27207 $newlen = strlen($s); 27208 27209 $changes[($xref[$obj + 1][0])] = ($newlen - $oldlen) + (strlen($newlen) - strlen($oldlen)); 27210 27211 if ($this->compress) { 27212 $newstr = ($obj + 1) . " 0 obj\n<</Filter /FlateDecode /Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n"; 27213 } else { 27214 $newstr = ($obj + 1) . " 0 obj\n<</Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n"; 27215 } 27216 27217 $pdf = str_replace($m[0], $newstr, $pdf); 27218 } 27219 27220 // Update xref in PDF 27221 krsort($changes); 27222 $newxref = "xref\n0 " . $xref_objid . "\n"; 27223 foreach ($xref as $v) { 27224 foreach ($changes as $ck => $cv) { 27225 if ($v[0] > $ck) { 27226 $v[0] += $cv; 27227 } 27228 } 27229 $newxref .= sprintf('%010d', $v[0]) . ' ' . $v[1] . ' ' . $v[2] . " \n"; 27230 } 27231 $newxref .= "trailer"; 27232 $pdf = preg_replace("/xref\n0 \d+\n.*?\ntrailer/s", $newxref, $pdf); 27233 27234 // Update startxref in PDF 27235 preg_match("/startxref\n(\d+)\n%%EOF/s", $pdf, $m); 27236 $startxref = $m[1]; 27237 $startxref += array_sum($changes); 27238 $pdf = preg_replace("/startxref\n(\d+)\n%%EOF/s", "startxref\n" . $startxref . "\n%%EOF", $pdf); 27239 27240 // OUTPUT 27241 switch ($dest) { 27242 case Destination::INLINE: 27243 if (isset($_SERVER['SERVER_NAME'])) { 27244 // We send to a browser 27245 header('Content-Type: application/pdf'); 27246 header('Content-Length: ' . strlen($pdf)); 27247 header('Content-disposition: inline; filename=' . $file_out); 27248 } 27249 27250 echo $pdf; 27251 27252 break; 27253 27254 case Destination::FILE: 27255 if (!$file_out) { 27256 $file_out = 'mpdf.pdf'; 27257 } 27258 27259 $f = fopen($file_out, 'wb'); 27260 27261 if (!$f) { 27262 throw new \Mpdf\MpdfException('Unable to create output file: ' . $file_out); 27263 } 27264 27265 fwrite($f, $pdf, strlen($pdf)); 27266 27267 fclose($f); 27268 27269 break; 27270 27271 case Destination::STRING_RETURN: 27272 return $pdf; 27273 27274 case Destination::DOWNLOAD: // Download file 27275 default: 27276 if (isset($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) { 27277 header('Content-Type: application/force-download'); 27278 } else { 27279 header('Content-Type: application/octet-stream'); 27280 } 27281 27282 header('Content-Length: ' . strlen($pdf)); 27283 header('Content-disposition: attachment; filename=' . $file_out); 27284 27285 echo $pdf; 27286 27287 break; 27288 } 27289 } 27290 27291 27292 function Thumbnail($file, $npr = 3, $spacing = 10) 27293 { 27294 // $npr = number per row 27295 $w = (($this->pgwidth + $spacing) / $npr) - $spacing; 27296 $oldlinewidth = $this->LineWidth; 27297 $this->SetLineWidth(0.02); 27298 $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings)); 27299 $h = 0; 27300 $maxh = 0; 27301 $x = $_x = $this->lMargin; 27302 $_y = $this->tMargin; 27303 27304 if ($this->y == 0) { 27305 $y = $_y; 27306 } else { 27307 $y = $this->y; 27308 } 27309 27310 $pagecount = $this->setSourceFile($file); 27311 27312 for ($n = 1; $n <= $pagecount; $n++) { 27313 $tplidx = $this->importPage($n); 27314 $size = $this->useTemplate($tplidx, $x, $y, $w); 27315 $this->Rect($x, $y, $size['width'], $size['height']); 27316 $h = max($h, $size['height']); 27317 $maxh = max($h, $maxh); 27318 27319 if ($n % $npr == 0) { 27320 if (($y + $h + $spacing + $maxh) > $this->PageBreakTrigger && $n != $pagecount) { 27321 $this->AddPage(); 27322 $x = $_x; 27323 $y = $_y; 27324 } else { 27325 $y += $h + $spacing; 27326 $x = $_x; 27327 $h = 0; 27328 } 27329 } else { 27330 $x += $w + $spacing; 27331 } 27332 } 27333 $this->SetLineWidth($oldlinewidth); 27334 } 27335 27336 function SetPageTemplate($tplidx = '') 27337 { 27338 if (!isset($this->importedPages[$tplidx])) { 27339 $this->pageTemplate = ''; 27340 return false; 27341 } 27342 $this->pageTemplate = $tplidx; 27343 } 27344 27345 function SetDocTemplate($file = '', $continue = 0, $continue2pages = 0) 27346 { 27347 $this->docTemplate = $file; 27348 $this->docTemplateContinue = $continue; 27349 $this->docTemplateContinue2pages = $continue2pages; 27350 27351 if ($this->docTemplateContinue2pages) { // Enable continue when continue2pages is set 27352 $this->docTemplateContinue = $this->docTemplateContinue2pages; 27353 } 27354 } 27355 27356 /* -- END IMPORTS -- */ 27357 27358 // JAVASCRIPT 27359 function _set_object_javascript($string) 27360 { 27361 $this->writer->object(); 27362 $this->writer->write('<<'); 27363 $this->writer->write('/S /JavaScript '); 27364 $this->writer->write('/JS ' . $this->writer->string($string)); 27365 $this->writer->write('>>'); 27366 $this->writer->write('endobj'); 27367 } 27368 27369 function SetJS($script) 27370 { 27371 $this->js = $script; 27372 } 27373 27374 /** 27375 * This function takes the last comma or dot (if any) to make a clean float, ignoring thousand separator, currency or any other letter 27376 * 27377 * @param string $num 27378 * @see http://php.net/manual/de/function.floatval.php#114486 27379 * @return float 27380 */ 27381 public function toFloat($num) 27382 { 27383 $dotPos = strrpos($num, '.'); 27384 $commaPos = strrpos($num, ','); 27385 $sep = (($dotPos > $commaPos) && $dotPos) ? $dotPos : ((($commaPos > $dotPos) && $commaPos) ? $commaPos : false); 27386 27387 if (!$sep) { 27388 return floatval(preg_replace('/[^0-9]/', '', $num)); 27389 } 27390 27391 return floatval( 27392 preg_replace('/[^0-9]/', '', substr($num, 0, $sep)) . '.' . 27393 preg_replace('/[^0-9]/', '', substr($num, $sep+1, strlen($num))) 27394 ); 27395 } 27396 27397 public function getFontDescriptor() 27398 { 27399 return $this->fontDescriptor; 27400 } 27401 27402 /** 27403 * Temporarily return the method to preserve example 44 yearbook 27404 */ 27405 public function _out($s) 27406 { 27407 $this->writer->write($s); 27408 } 27409 27410 /** 27411 * @param string $html 27412 * @param string $PAGENO 27413 * @param string $NbPgGp 27414 * @param string $NbPg 27415 * @return string 27416 */ 27417 protected function aliasReplace($html, $PAGENO, $NbPgGp, $NbPg) 27418 { 27419 // Replaces for header and footer 27420 $html = str_replace('{PAGENO}', $PAGENO, $html); 27421 $html = str_replace($this->aliasNbPgGp, $NbPgGp, $html); // {nbpg} 27422 $html = str_replace($this->aliasNbPg, $NbPg, $html); // {nb} 27423 27424 // Replaces for the body 27425 $html = str_replace(mb_convert_encoding('{PAGENO}', 'UTF-16BE', 'UTF-8'), mb_convert_encoding($PAGENO, 'UTF-16BE', 'UTF-8'), $html); 27426 $html = str_replace(mb_convert_encoding($this->aliasNbPgGp, 'UTF-16BE', 'UTF-8'), mb_convert_encoding($NbPgGp, 'UTF-16BE', 'UTF-8'), $html); // {nbpg} 27427 $html = str_replace(mb_convert_encoding($this->aliasNbPg, 'UTF-16BE', 'UTF-8'), mb_convert_encoding($NbPg, 'UTF-16BE', 'UTF-8'), $html); // {nb} 27428 27429 // Date replace 27430 $html = preg_replace_callback('/\{DATE\s+(.*?)\}/', [$this, 'date_callback'], $html); // mPDF 5.7 27431 27432 return $html; 27433 } 27434 27435} 27436