vendor/mpdf/mpdf/src/Mpdf.php line 1075

Open in your IDE?
  1. <?php
  2. namespace Mpdf;
  3. use Mpdf\Config\ConfigVariables;
  4. use Mpdf\Config\FontVariables;
  5. use Mpdf\Conversion;
  6. use Mpdf\Css\Border;
  7. use Mpdf\Css\TextVars;
  8. use Mpdf\Log\Context as LogContext;
  9. use Mpdf\Fonts\MetricsGenerator;
  10. use Mpdf\Output\Destination;
  11. use Mpdf\PsrLogAwareTrait\MpdfPsrLogAwareTrait;
  12. use Mpdf\QrCode;
  13. use Mpdf\Utils\Arrays;
  14. use Mpdf\Utils\NumericString;
  15. use Mpdf\Utils\UtfString;
  16. use Psr\Log\NullLogger;
  17. /**
  18. * mPDF, PHP library generating PDF files from UTF-8 encoded HTML
  19. *
  20. * based on FPDF by Olivier Plathey
  21. * and HTML2FPDF by Renato Coelho
  22. *
  23. * @license GPL-2.0
  24. */
  25. class Mpdf implements \Psr\Log\LoggerAwareInterface
  26. {
  27. use Strict;
  28. use FpdiTrait;
  29. use MpdfPsrLogAwareTrait;
  30. const VERSION = '8.2.5';
  31. const SCALE = 72 / 25.4;
  32. const OBJECT_IDENTIFIER = "\xbb\xa4\xac";
  33. var $useFixedNormalLineHeight; // mPDF 6
  34. var $useFixedTextBaseline; // mPDF 6
  35. var $adjustFontDescLineheight; // mPDF 6
  36. var $interpolateImages; // mPDF 6
  37. var $defaultPagebreakType; // mPDF 6 pagebreaktype
  38. var $indexUseSubentries; // mPDF 6
  39. var $autoScriptToLang; // mPDF 6
  40. var $baseScript; // mPDF 6
  41. var $autoVietnamese; // mPDF 6
  42. var $autoArabic; // mPDF 6
  43. var $CJKforceend;
  44. var $h2bookmarks;
  45. var $h2toc;
  46. var $decimal_align;
  47. var $margBuffer;
  48. var $splitTableBorderWidth;
  49. var $bookmarkStyles;
  50. var $useActiveForms;
  51. var $repackageTTF;
  52. var $allowCJKorphans;
  53. var $allowCJKoverflow;
  54. var $useKerning;
  55. var $restrictColorSpace;
  56. var $bleedMargin;
  57. var $crossMarkMargin;
  58. var $cropMarkMargin;
  59. var $cropMarkLength;
  60. var $nonPrintMargin;
  61. var $PDFX;
  62. var $PDFXauto;
  63. var $PDFA;
  64. var $PDFAversion = '1-B';
  65. var $PDFAauto;
  66. var $ICCProfile;
  67. var $printers_info;
  68. var $iterationCounter;
  69. var $smCapsScale;
  70. var $smCapsStretch;
  71. var $backupSubsFont;
  72. var $backupSIPFont;
  73. var $fonttrans;
  74. var $debugfonts;
  75. var $useAdobeCJK;
  76. var $percentSubset;
  77. var $maxTTFFilesize;
  78. var $BMPonly;
  79. var $tableMinSizePriority;
  80. var $dpi;
  81. var $watermarkImgAlphaBlend;
  82. var $watermarkImgBehind;
  83. var $justifyB4br;
  84. var $packTableData;
  85. var $pgsIns;
  86. var $simpleTables;
  87. var $enableImports;
  88. var $debug;
  89. var $setAutoTopMargin;
  90. var $setAutoBottomMargin;
  91. var $autoMarginPadding;
  92. var $collapseBlockMargins;
  93. var $falseBoldWeight;
  94. var $normalLineheight;
  95. var $incrementFPR1;
  96. var $incrementFPR2;
  97. var $incrementFPR3;
  98. var $incrementFPR4;
  99. var $SHYlang;
  100. var $SHYleftmin;
  101. var $SHYrightmin;
  102. var $SHYcharmin;
  103. var $SHYcharmax;
  104. var $SHYlanguages;
  105. // PageNumber Conditional Text
  106. var $pagenumPrefix;
  107. var $pagenumSuffix;
  108. var $nbpgPrefix;
  109. var $nbpgSuffix;
  110. var $showImageErrors;
  111. var $allow_output_buffering;
  112. var $autoPadding;
  113. var $tabSpaces;
  114. var $autoLangToFont;
  115. var $watermarkTextAlpha;
  116. var $watermarkImageAlpha;
  117. var $watermark_size;
  118. var $watermark_pos;
  119. var $annotSize;
  120. var $annotMargin;
  121. var $annotOpacity;
  122. var $title2annots;
  123. var $keepColumns;
  124. var $keep_table_proportions;
  125. var $ignore_table_widths;
  126. var $ignore_table_percents;
  127. var $list_number_suffix;
  128. var $list_auto_mode; // mPDF 6
  129. var $list_indent_first_level; // mPDF 6
  130. var $list_indent_default; // mPDF 6
  131. var $list_indent_default_mpdf;
  132. var $list_marker_offset; // mPDF 6
  133. var $list_symbol_size;
  134. var $useSubstitutions;
  135. var $CSSselectMedia;
  136. var $forcePortraitHeaders;
  137. var $forcePortraitMargins;
  138. var $displayDefaultOrientation;
  139. var $ignore_invalid_utf8;
  140. var $allowedCSStags;
  141. var $onlyCoreFonts;
  142. var $allow_charset_conversion;
  143. var $jSWord;
  144. var $jSmaxChar;
  145. var $jSmaxCharLast;
  146. var $jSmaxWordLast;
  147. var $max_colH_correction;
  148. var $table_error_report;
  149. var $table_error_report_param;
  150. var $biDirectional;
  151. var $text_input_as_HTML;
  152. var $anchor2Bookmark;
  153. var $shrink_tables_to_fit;
  154. var $allow_html_optional_endtags;
  155. var $img_dpi;
  156. var $whitelistStreamWrappers;
  157. var $defaultheaderfontsize;
  158. var $defaultheaderfontstyle;
  159. var $defaultheaderline;
  160. var $defaultfooterfontsize;
  161. var $defaultfooterfontstyle;
  162. var $defaultfooterline;
  163. var $header_line_spacing;
  164. var $footer_line_spacing;
  165. var $pregCJKchars;
  166. var $pregRTLchars;
  167. var $pregCURSchars; // mPDF 6
  168. var $mirrorMargins;
  169. var $watermarkText;
  170. var $watermarkAngle;
  171. var $watermarkImage;
  172. var $showWatermarkText;
  173. var $showWatermarkImage;
  174. var $svgAutoFont;
  175. var $svgClasses;
  176. var $fontsizes;
  177. var $defaultPageNumStyle; // mPDF 6
  178. //////////////////////
  179. // INTERNAL VARIABLES
  180. //////////////////////
  181. var $extrapagebreak; // mPDF 6 pagebreaktype
  182. var $uniqstr; // mPDF 5.7.2
  183. var $hasOC;
  184. var $textvar; // mPDF 5.7.1
  185. var $fontLanguageOverride; // mPDF 5.7.1
  186. var $OTLtags; // mPDF 5.7.1
  187. var $OTLdata; // mPDF 5.7.1
  188. var $useDictionaryLBR;
  189. var $useTibetanLBR;
  190. var $writingToC;
  191. var $layers;
  192. var $layerDetails;
  193. var $current_layer;
  194. var $open_layer_pane;
  195. var $decimal_offset;
  196. var $inMeter;
  197. var $CJKleading;
  198. var $CJKfollowing;
  199. var $CJKoverflow;
  200. var $textshadow;
  201. var $colsums;
  202. var $spanborder;
  203. var $spanborddet;
  204. var $visibility;
  205. var $kerning;
  206. var $fixedlSpacing;
  207. var $minwSpacing;
  208. var $lSpacingCSS;
  209. var $wSpacingCSS;
  210. var $spotColorIDs;
  211. var $SVGcolors;
  212. var $spotColors;
  213. var $defTextColor;
  214. var $defDrawColor;
  215. var $defFillColor;
  216. var $tableBackgrounds;
  217. var $inlineDisplayOff;
  218. var $kt_y00;
  219. var $kt_p00;
  220. var $upperCase;
  221. var $checkSIP;
  222. var $checkSMP;
  223. var $checkCJK;
  224. var $watermarkImgAlpha;
  225. var $PDFAXwarnings;
  226. var $MetadataRoot;
  227. var $OutputIntentRoot;
  228. var $InfoRoot;
  229. var $associatedFilesRoot;
  230. var $pdf_version;
  231. private $fontDir;
  232. var $tempDir;
  233. var $cacheCleanupInterval;
  234. var $allowAnnotationFiles;
  235. var $fontdata;
  236. var $noImageFile;
  237. var $lastblockbottommargin;
  238. var $baselineC;
  239. // mPDF 5.7.3 inline text-decoration parameters
  240. var $baselineSup;
  241. var $baselineSub;
  242. var $baselineS;
  243. var $baselineO;
  244. var $subPos;
  245. var $subArrMB;
  246. var $ReqFontStyle;
  247. var $tableClipPath;
  248. var $fullImageHeight;
  249. var $inFixedPosBlock; // Internal flag for position:fixed block
  250. var $fixedPosBlock; // Buffer string for position:fixed block
  251. var $fixedPosBlockDepth;
  252. var $fixedPosBlockBBox;
  253. var $fixedPosBlockSave;
  254. var $maxPosL;
  255. var $maxPosR;
  256. var $loaded;
  257. var $extraFontSubsets;
  258. var $docTemplateStart; // Internal flag for page (page no. -1) that docTemplate starts on
  259. var $time0;
  260. var $hyphenationDictionaryFile;
  261. var $spanbgcolorarray;
  262. var $default_font;
  263. var $headerbuffer;
  264. var $lastblocklevelchange;
  265. var $nestedtablejustfinished;
  266. var $linebreakjustfinished;
  267. var $cell_border_dominance_L;
  268. var $cell_border_dominance_R;
  269. var $cell_border_dominance_T;
  270. var $cell_border_dominance_B;
  271. var $table_keep_together;
  272. var $plainCell_properties;
  273. var $shrin_k1;
  274. var $outerfilled;
  275. var $blockContext;
  276. var $floatDivs;
  277. var $patterns;
  278. var $pageBackgrounds;
  279. var $bodyBackgroundGradient;
  280. var $bodyBackgroundImage;
  281. var $bodyBackgroundColor;
  282. var $writingHTMLheader; // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
  283. var $writingHTMLfooter;
  284. var $angle;
  285. var $gradients;
  286. var $kwt_Reference;
  287. var $kwt_BMoutlines;
  288. var $kwt_toc;
  289. var $tbrot_BMoutlines;
  290. var $tbrot_toc;
  291. var $col_BMoutlines;
  292. var $col_toc;
  293. var $floatbuffer;
  294. var $floatmargins;
  295. var $bullet;
  296. var $bulletarray;
  297. var $currentLang;
  298. var $default_lang;
  299. var $default_available_fonts;
  300. var $pageTemplate;
  301. var $docTemplate;
  302. var $docTemplateContinue;
  303. var $docTemplateContinue2pages;
  304. var $arabGlyphs;
  305. var $arabHex;
  306. var $persianGlyphs;
  307. var $persianHex;
  308. var $arabVowels;
  309. var $arabPrevLink;
  310. var $arabNextLink;
  311. var $formobjects; // array of Form Objects for WMF
  312. var $InlineProperties;
  313. var $InlineAnnots;
  314. var $InlineBDF; // mPDF 6 Bidirectional formatting
  315. var $InlineBDFctr; // mPDF 6
  316. var $ktAnnots;
  317. var $tbrot_Annots;
  318. var $kwt_Annots;
  319. var $columnAnnots;
  320. var $columnForms;
  321. var $tbrotForms;
  322. var $PageAnnots;
  323. var $pageDim; // Keep track of page wxh for orientation changes - set in _beginpage, used in _putannots
  324. var $breakpoints;
  325. var $tableLevel;
  326. var $tbctr;
  327. var $innermostTableLevel;
  328. var $saveTableCounter;
  329. var $cellBorderBuffer;
  330. var $saveHTMLFooter_height;
  331. var $saveHTMLFooterE_height;
  332. var $firstPageBoxHeader;
  333. var $firstPageBoxHeaderEven;
  334. var $firstPageBoxFooter;
  335. var $firstPageBoxFooterEven;
  336. var $page_box;
  337. var $show_marks; // crop or cross marks
  338. var $basepathIsLocal;
  339. var $use_kwt;
  340. var $kwt;
  341. var $kwt_height;
  342. var $kwt_y0;
  343. var $kwt_x0;
  344. var $kwt_buffer;
  345. var $kwt_Links;
  346. var $kwt_moved;
  347. var $kwt_saved;
  348. var $PageNumSubstitutions;
  349. var $table_borders_separate;
  350. var $base_table_properties;
  351. var $borderstyles;
  352. var $blockjustfinished;
  353. var $orig_bMargin;
  354. var $orig_tMargin;
  355. var $orig_lMargin;
  356. var $orig_rMargin;
  357. var $orig_hMargin;
  358. var $orig_fMargin;
  359. var $pageHTMLheaders;
  360. var $pageHTMLfooters;
  361. var $saveHTMLHeader;
  362. var $saveHTMLFooter;
  363. var $HTMLheaderPageLinks;
  364. var $HTMLheaderPageAnnots;
  365. var $HTMLheaderPageForms;
  366. // See Config\FontVariables for these next 5 values
  367. var $available_unifonts;
  368. var $sans_fonts;
  369. var $serif_fonts;
  370. var $mono_fonts;
  371. var $defaultSubsFont;
  372. // List of ALL available CJK fonts (incl. styles) (Adobe add-ons) hw removed
  373. var $available_CJK_fonts;
  374. var $HTMLHeader;
  375. var $HTMLFooter;
  376. var $HTMLHeaderE;
  377. var $HTMLFooterE;
  378. var $bufferoutput;
  379. // CJK fonts
  380. var $Big5_widths;
  381. var $GB_widths;
  382. var $SJIS_widths;
  383. var $UHC_widths;
  384. // SetProtection
  385. var $encrypted;
  386. var $enc_obj_id; // encryption object id
  387. // Bookmark
  388. var $BMoutlines;
  389. var $OutlineRoot;
  390. // INDEX
  391. var $ColActive;
  392. var $Reference;
  393. var $CurrCol;
  394. var $NbCol;
  395. var $y0; // Top ordinate of columns
  396. var $ColL;
  397. var $ColWidth;
  398. var $ColGap;
  399. // COLUMNS
  400. var $ColR;
  401. var $ChangeColumn;
  402. var $columnbuffer;
  403. var $ColDetails;
  404. var $columnLinks;
  405. var $colvAlign;
  406. // Substitutions
  407. var $substitute; // Array of substitution strings e.g. <ttz>112</ttz>
  408. var $entsearch; // Array of HTML entities (>ASCII 127) to substitute
  409. var $entsubstitute; // Array of substitution decimal unicode for the Hi entities
  410. // Default values if no style sheet offered (cf. http://www.w3.org/TR/CSS21/sample.html)
  411. var $defaultCSS;
  412. var $defaultCssFile;
  413. var $lastoptionaltag; // Save current block item which HTML specifies optionsl endtag
  414. var $pageoutput;
  415. var $charset_in;
  416. var $blk;
  417. var $blklvl;
  418. var $ColumnAdjust;
  419. var $ws; // Word spacing
  420. var $HREF;
  421. var $pgwidth;
  422. var $fontlist;
  423. var $oldx;
  424. var $oldy;
  425. var $B;
  426. var $I;
  427. var $tdbegin;
  428. var $table;
  429. var $cell;
  430. var $col;
  431. var $row;
  432. var $divbegin;
  433. var $divwidth;
  434. var $divheight;
  435. var $spanbgcolor;
  436. // mPDF 6 Used for table cell (block-type) properties
  437. var $cellTextAlign;
  438. var $cellLineHeight;
  439. var $cellLineStackingStrategy;
  440. var $cellLineStackingShift;
  441. // mPDF 6 Lists
  442. var $listcounter;
  443. var $listlvl;
  444. var $listtype;
  445. var $listitem;
  446. var $pjustfinished;
  447. var $ignorefollowingspaces;
  448. var $SMALL;
  449. var $BIG;
  450. var $dash_on;
  451. var $dotted_on;
  452. var $textbuffer;
  453. var $currentfontstyle;
  454. var $currentfontfamily;
  455. var $currentfontsize;
  456. var $colorarray;
  457. var $bgcolorarray;
  458. var $internallink;
  459. var $enabledtags;
  460. var $lineheight;
  461. var $default_lineheight_correction;
  462. var $basepath;
  463. var $textparam;
  464. var $specialcontent;
  465. var $selectoption;
  466. var $objectbuffer;
  467. // Table Rotation
  468. var $table_rotate;
  469. var $tbrot_maxw;
  470. var $tbrot_maxh;
  471. var $tablebuffer;
  472. var $tbrot_align;
  473. var $tbrot_Links;
  474. var $keep_block_together; // Keep a Block from page-break-inside: avoid
  475. var $tbrot_y0;
  476. var $tbrot_x0;
  477. var $tbrot_w;
  478. var $tbrot_h;
  479. var $mb_enc;
  480. var $originalMbEnc;
  481. var $originalMbRegexEnc;
  482. var $directionality;
  483. var $extgstates; // Used for alpha channel - Transparency (Watermark)
  484. var $mgl;
  485. var $mgt;
  486. var $mgr;
  487. var $mgb;
  488. var $tts;
  489. var $ttz;
  490. var $tta;
  491. // Best to alter the below variables using default stylesheet above
  492. var $page_break_after_avoid;
  493. var $margin_bottom_collapse;
  494. var $default_font_size; // in pts
  495. var $original_default_font_size; // used to save default sizes when using table default
  496. var $original_default_font;
  497. var $watermark_font;
  498. var $defaultAlign;
  499. // TABLE
  500. var $defaultTableAlign;
  501. var $tablethead;
  502. var $thead_font_weight;
  503. var $thead_font_style;
  504. var $thead_font_smCaps;
  505. var $thead_valign_default;
  506. var $thead_textalign_default;
  507. var $tabletfoot;
  508. var $tfoot_font_weight;
  509. var $tfoot_font_style;
  510. var $tfoot_font_smCaps;
  511. var $tfoot_valign_default;
  512. var $tfoot_textalign_default;
  513. var $trow_text_rotate;
  514. var $cellPaddingL;
  515. var $cellPaddingR;
  516. var $cellPaddingT;
  517. var $cellPaddingB;
  518. var $table_border_attr_set;
  519. var $table_border_css_set;
  520. var $shrin_k; // factor with which to shrink tables - used internally - do not change
  521. var $shrink_this_table_to_fit; // 0 or false to disable; value (if set) gives maximum factor to reduce fontsize
  522. var $MarginCorrection; // corrects for OddEven Margins
  523. var $margin_footer;
  524. var $margin_header;
  525. var $tabletheadjustfinished;
  526. var $usingCoreFont;
  527. var $charspacing;
  528. var $js;
  529. /**
  530. * Set timeout for cURL
  531. *
  532. * @var int
  533. */
  534. var $curlTimeout;
  535. /**
  536. * Set execution timeout for cURL
  537. *
  538. * @var int
  539. */
  540. var $curlExecutionTimeout;
  541. /**
  542. * Set to true to follow redirects with cURL.
  543. *
  544. * @var bool
  545. */
  546. var $curlFollowLocation;
  547. /**
  548. * Set your own CA certificate store for SSL Certificate verification when using cURL
  549. *
  550. * Useful setting to use on hosts with outdated CA certificates.
  551. *
  552. * Download the latest CA certificate from https://curl.haxx.se/docs/caextract.html
  553. *
  554. * @var string The absolute path to the pem file
  555. */
  556. var $curlCaCertificate;
  557. /**
  558. * Set to true to allow unsafe SSL HTTPS requests.
  559. *
  560. * Can be useful when using CDN with HTTPS and if you don't want to configure settings with SSL certificates.
  561. *
  562. * @var bool
  563. */
  564. var $curlAllowUnsafeSslRequests;
  565. /**
  566. * Set the proxy for cURL.
  567. *
  568. * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html
  569. *
  570. * @var string
  571. */
  572. var $curlProxy;
  573. /**
  574. * Set the proxy auth for cURL.
  575. *
  576. * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXYUSERPWD.html
  577. *
  578. * @var string
  579. */
  580. var $curlProxyAuth;
  581. /**
  582. * Set the User-Agent header in the HTTP requests sent by cURL.
  583. *
  584. * @see https://curl.haxx.se/libcurl/c/CURLOPT_USERAGENT.html
  585. *
  586. * @var string User Agent header
  587. */
  588. var $curlUserAgent;
  589. // Private properties FROM FPDF
  590. var $DisplayPreferences;
  591. var $flowingBlockAttr;
  592. var $page; // current page number
  593. var $n; // current object number
  594. var $n_js; // current object number
  595. var $n_ocg_hidden;
  596. var $n_ocg_print;
  597. var $n_ocg_view;
  598. var $offsets; // array of object offsets
  599. var $buffer; // buffer holding in-memory PDF
  600. var $pages; // array containing pages
  601. var $state; // current document state
  602. var $compress; // compression flag
  603. var $DefOrientation; // default orientation
  604. var $CurOrientation; // current orientation
  605. var $OrientationChanges; // array indicating orientation changes
  606. var $fwPt;
  607. var $fhPt; // dimensions of page format in points
  608. var $fw;
  609. var $fh; // dimensions of page format in user unit
  610. var $wPt;
  611. var $hPt; // current dimensions of page in points
  612. var $w;
  613. var $h; // current dimensions of page in user unit
  614. var $lMargin; // left margin
  615. var $tMargin; // top margin
  616. var $rMargin; // right margin
  617. var $bMargin; // page break margin
  618. var $cMarginL; // cell margin Left
  619. var $cMarginR; // cell margin Right
  620. var $cMarginT; // cell margin Left
  621. var $cMarginB; // cell margin Right
  622. var $DeflMargin; // Default left margin
  623. var $DefrMargin; // Default right margin
  624. var $x;
  625. var $y; // current position in user unit for cell positioning
  626. var $lasth; // height of last cell printed
  627. var $LineWidth; // line width in user unit
  628. var $CoreFonts; // array of standard font names
  629. var $fonts; // array of used fonts
  630. var $FontFiles; // array of font files
  631. var $images; // array of used images
  632. var $imageVars = []; // array of image vars
  633. var $PageLinks; // array of links in pages
  634. var $links; // array of internal links
  635. var $FontFamily; // current font family
  636. var $FontStyle; // current font style
  637. var $CurrentFont; // current font info
  638. var $FontSizePt; // current font size in points
  639. var $FontSize; // current font size in user unit
  640. var $DrawColor; // commands for drawing color
  641. var $FillColor; // commands for filling color
  642. var $TextColor; // commands for text color
  643. var $ColorFlag; // indicates whether fill and text colors are different
  644. var $autoPageBreak; // automatic page breaking
  645. var $PageBreakTrigger; // threshold used to trigger page breaks
  646. var $InFooter; // flag set when processing footer
  647. var $InHTMLFooter;
  648. var $processingFooter; // flag set when processing footer - added for columns
  649. var $processingHeader; // flag set when processing header - added for columns
  650. var $ZoomMode; // zoom display mode
  651. var $LayoutMode; // layout display mode
  652. var $title; // title
  653. var $subject; // subject
  654. var $author; // author
  655. var $keywords; // keywords
  656. var $creator; // creator
  657. var $customProperties; // array of custom document properties
  658. var $associatedFiles; // associated files (see SetAssociatedFiles below)
  659. var $additionalXmpRdf; // additional rdf added in xmp
  660. var $aliasNbPg; // alias for total number of pages
  661. var $aliasNbPgGp; // alias for total number of pages in page group
  662. var $ispre;
  663. var $outerblocktags;
  664. var $innerblocktags;
  665. public $exposeVersion;
  666. private $preambleWritten = false;
  667. private $watermarkTextObject;
  668. private $watermarkImageObject;
  669. /**
  670. * @var string
  671. */
  672. private $fontDescriptor;
  673. /**
  674. * @var \Mpdf\Otl
  675. */
  676. private $otl;
  677. /**
  678. * @var \Mpdf\CssManager
  679. */
  680. private $cssManager;
  681. /**
  682. * @var \Mpdf\Gradient
  683. */
  684. private $gradient;
  685. /**
  686. * @var \Mpdf\Image\Bmp
  687. */
  688. private $bmp;
  689. /**
  690. * @var \Mpdf\Image\Wmf
  691. */
  692. private $wmf;
  693. /**
  694. * @var \Mpdf\TableOfContents
  695. */
  696. private $tableOfContents;
  697. /**
  698. * @var \Mpdf\Form
  699. */
  700. private $form;
  701. /**
  702. * @var \Mpdf\DirectWrite
  703. */
  704. private $directWrite;
  705. /**
  706. * @var \Mpdf\Cache
  707. */
  708. private $cache;
  709. /**
  710. * @var \Mpdf\Fonts\FontCache
  711. */
  712. private $fontCache;
  713. /**
  714. * @var \Mpdf\Fonts\FontFileFinder
  715. */
  716. private $fontFileFinder;
  717. /**
  718. * @var \Mpdf\Tag
  719. */
  720. private $tag;
  721. /**
  722. * @var \Mpdf\Barcode
  723. * @todo solve Tag dependency and make private
  724. */
  725. public $barcode;
  726. /**
  727. * @var \Mpdf\QrCode\QrCode
  728. */
  729. private $qrcode;
  730. /**
  731. * @var \Mpdf\SizeConverter
  732. */
  733. private $sizeConverter;
  734. /**
  735. * @var \Mpdf\Color\ColorConverter
  736. */
  737. private $colorConverter;
  738. /**
  739. * @var \Mpdf\Color\ColorModeConverter
  740. */
  741. private $colorModeConverter;
  742. /**
  743. * @var \Mpdf\Color\ColorSpaceRestrictor
  744. */
  745. private $colorSpaceRestrictor;
  746. /**
  747. * @var \Mpdf\Hyphenator
  748. */
  749. private $hyphenator;
  750. /**
  751. * @var \Mpdf\Pdf\Protection
  752. */
  753. private $protection;
  754. /**
  755. * @var \Mpdf\Http\ClientInterface
  756. */
  757. private $httpClient;
  758. /**
  759. * @var \Mpdf\File\LocalContentLoaderInterface
  760. */
  761. private $localContentLoader;
  762. /**
  763. * @var \Mpdf\AssetFetcher
  764. */
  765. private $assetFetcher;
  766. /**
  767. * @var \Mpdf\Image\ImageProcessor
  768. */
  769. private $imageProcessor;
  770. /**
  771. * @var \Mpdf\Language\LanguageToFontInterface
  772. */
  773. private $languageToFont;
  774. /**
  775. * @var \Mpdf\Language\ScriptToLanguageInterface
  776. */
  777. private $scriptToLanguage;
  778. /**
  779. * @var \Mpdf\Writer\BaseWriter
  780. */
  781. private $writer;
  782. /**
  783. * @var \Mpdf\Writer\FontWriter
  784. */
  785. private $fontWriter;
  786. /**
  787. * @var \Mpdf\Writer\MetadataWriter
  788. */
  789. private $metadataWriter;
  790. /**
  791. * @var \Mpdf\Writer\ImageWriter
  792. */
  793. private $imageWriter;
  794. /**
  795. * @var \Mpdf\Writer\FormWriter
  796. */
  797. private $formWriter;
  798. /**
  799. * @var \Mpdf\Writer\PageWriter
  800. */
  801. private $pageWriter;
  802. /**
  803. * @var \Mpdf\Writer\BookmarkWriter
  804. */
  805. private $bookmarkWriter;
  806. /**
  807. * @var \Mpdf\Writer\OptionalContentWriter
  808. */
  809. private $optionalContentWriter;
  810. /**
  811. * @var \Mpdf\Writer\ColorWriter
  812. */
  813. private $colorWriter;
  814. /**
  815. * @var \Mpdf\Writer\BackgroundWriter
  816. */
  817. private $backgroundWriter;
  818. /**
  819. * @var \Mpdf\Writer\JavaScriptWriter
  820. */
  821. private $javaScriptWriter;
  822. /**
  823. * @var \Mpdf\Writer\ResourceWriter
  824. */
  825. private $resourceWriter;
  826. /**
  827. * @var string[]
  828. */
  829. private $services;
  830. /**
  831. * @var \Mpdf\Container\ContainerInterface
  832. */
  833. private $container;
  834. /**
  835. * @param mixed[] $config
  836. * @param \Mpdf\Container\ContainerInterface|null $container Experimental container to override internal services
  837. */
  838. public function __construct(array $config = [], $container = null)
  839. {
  840. $this->_dochecks();
  841. assert(!$container || $container instanceof \Mpdf\Container\ContainerInterface);
  842. list(
  843. $mode,
  844. $format,
  845. $default_font_size,
  846. $default_font,
  847. $mgl,
  848. $mgr,
  849. $mgt,
  850. $mgb,
  851. $mgh,
  852. $mgf,
  853. $orientation
  854. ) = $this->initConstructorParams($config);
  855. $this->logger = new NullLogger();
  856. $originalConfig = $config;
  857. $config = $this->initConfig($originalConfig);
  858. $serviceFactory = new ServiceFactory($container);
  859. $services = $serviceFactory->getServices(
  860. $this,
  861. $this->logger,
  862. $config,
  863. $this->languageToFont,
  864. $this->scriptToLanguage,
  865. $this->fontDescriptor,
  866. $this->bmp,
  867. $this->directWrite,
  868. $this->wmf
  869. );
  870. $this->container = $container;
  871. $this->services = [];
  872. foreach ($services as $key => $service) {
  873. $this->{$key} = $service;
  874. $this->services[] = $key;
  875. }
  876. $this->time0 = microtime(true);
  877. $this->writingToC = false;
  878. $this->layers = [];
  879. $this->current_layer = 0;
  880. $this->open_layer_pane = false;
  881. $this->visibility = 'visible';
  882. $this->tableBackgrounds = [];
  883. $this->uniqstr = '20110230'; // mPDF 5.7.2
  884. $this->kt_y00 = 0;
  885. $this->kt_p00 = 0;
  886. $this->BMPonly = [];
  887. $this->page = 0;
  888. $this->n = 2;
  889. $this->buffer = '';
  890. $this->objectbuffer = [];
  891. $this->pages = [];
  892. $this->OrientationChanges = [];
  893. $this->state = 0;
  894. $this->fonts = [];
  895. $this->FontFiles = [];
  896. $this->images = [];
  897. $this->links = [];
  898. $this->InFooter = false;
  899. $this->processingFooter = false;
  900. $this->processingHeader = false;
  901. $this->lasth = 0;
  902. $this->FontFamily = '';
  903. $this->FontStyle = '';
  904. $this->FontSizePt = 9;
  905. // Small Caps
  906. $this->inMeter = false;
  907. $this->decimal_offset = 0;
  908. $this->PDFAXwarnings = [];
  909. $this->defTextColor = $this->TextColor = $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true);
  910. $this->defDrawColor = $this->DrawColor = $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true);
  911. $this->defFillColor = $this->FillColor = $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings), true);
  912. $this->upperCase = require __DIR__ . '/../data/upperCase.php';
  913. $this->extrapagebreak = true; // mPDF 6 pagebreaktype
  914. $this->ColorFlag = false;
  915. $this->extgstates = [];
  916. $this->mb_enc = 'windows-1252';
  917. $this->originalMbEnc = mb_internal_encoding();
  918. $this->originalMbRegexEnc = mb_regex_encoding();
  919. $this->directionality = 'ltr';
  920. $this->defaultAlign = 'L';
  921. $this->defaultTableAlign = 'L';
  922. $this->fixedPosBlockSave = [];
  923. $this->extraFontSubsets = 0;
  924. $this->blockContext = 1;
  925. $this->floatDivs = [];
  926. $this->DisplayPreferences = '';
  927. // Tiling patterns used for backgrounds
  928. $this->patterns = [];
  929. $this->pageBackgrounds = [];
  930. $this->gradients = [];
  931. // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
  932. $this->writingHTMLheader = false;
  933. // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
  934. $this->writingHTMLfooter = false;
  935. $this->kwt_Reference = [];
  936. $this->kwt_BMoutlines = [];
  937. $this->kwt_toc = [];
  938. $this->tbrot_BMoutlines = [];
  939. $this->tbrot_toc = [];
  940. $this->col_BMoutlines = [];
  941. $this->col_toc = [];
  942. $this->pgsIns = [];
  943. $this->PDFAXwarnings = [];
  944. $this->inlineDisplayOff = false;
  945. $this->lSpacingCSS = '';
  946. $this->wSpacingCSS = '';
  947. $this->fixedlSpacing = false;
  948. $this->minwSpacing = 0;
  949. // Baseline for text
  950. $this->baselineC = 0.35;
  951. // mPDF 5.7.3 inline text-decoration parameters
  952. // Sets default change in baseline for <sup> text as factor of preceeding fontsize
  953. // 0.35 has been recommended; 0.5 matches applications like MS Word
  954. $this->baselineSup = 0.5;
  955. // Sets default change in baseline for <sub> text as factor of preceeding fontsize
  956. $this->baselineSub = -0.2;
  957. // Sets default height for <strike> text as factor of fontsize
  958. $this->baselineS = 0.3;
  959. // Sets default height for overline text as factor of fontsize
  960. $this->baselineO = 1.1;
  961. $this->noImageFile = __DIR__ . '/../data/no_image.jpg';
  962. $this->subPos = 0;
  963. $this->fullImageHeight = false;
  964. $this->floatbuffer = [];
  965. $this->floatmargins = [];
  966. $this->formobjects = []; // array of Form Objects for WMF
  967. $this->InlineProperties = [];
  968. $this->InlineAnnots = [];
  969. $this->InlineBDF = []; // mPDF 6
  970. $this->InlineBDFctr = 0; // mPDF 6
  971. $this->tbrot_Annots = [];
  972. $this->kwt_Annots = [];
  973. $this->columnAnnots = [];
  974. $this->PageLinks = [];
  975. $this->OrientationChanges = [];
  976. $this->pageDim = [];
  977. $this->saveHTMLHeader = [];
  978. $this->saveHTMLFooter = [];
  979. $this->PageAnnots = [];
  980. $this->PageNumSubstitutions = [];
  981. $this->breakpoints = []; // used in columnbuffer
  982. $this->tableLevel = 0;
  983. $this->tbctr = []; // counter for nested tables at each level
  984. $this->page_box = new PageBox();
  985. $this->show_marks = ''; // crop or cross marks
  986. $this->kwt = false;
  987. $this->kwt_height = 0;
  988. $this->kwt_y0 = 0;
  989. $this->kwt_x0 = 0;
  990. $this->kwt_buffer = [];
  991. $this->kwt_Links = [];
  992. $this->kwt_moved = false;
  993. $this->kwt_saved = false;
  994. $this->PageNumSubstitutions = [];
  995. $this->base_table_properties = [];
  996. $this->borderstyles = ['inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double'];
  997. $this->tbrot_align = 'C';
  998. $this->pageHTMLheaders = [];
  999. $this->pageHTMLfooters = [];
  1000. $this->HTMLheaderPageLinks = [];
  1001. $this->HTMLheaderPageAnnots = [];
  1002. $this->HTMLheaderPageForms = [];
  1003. $this->columnForms = [];
  1004. $this->tbrotForms = [];
  1005. $this->pageoutput = [];
  1006. $this->bufferoutput = false;
  1007. $this->encrypted = false;
  1008. $this->BMoutlines = [];
  1009. $this->ColActive = 0; // Flag indicating that columns are on (the index is being processed)
  1010. $this->Reference = []; // Array containing the references
  1011. $this->CurrCol = 0; // Current column number
  1012. $this->ColL = [0]; // Array of Left pos of columns - absolute - needs Margin correction for Odd-Even
  1013. $this->ColR = [0]; // Array of Right pos of columns - absolute pos - needs Margin correction for Odd-Even
  1014. $this->ChangeColumn = 0;
  1015. $this->columnbuffer = [];
  1016. $this->ColDetails = []; // Keeps track of some column details
  1017. $this->columnLinks = []; // Cross references PageLinks
  1018. $this->substitute = []; // Array of substitution strings e.g. <ttz>112</ttz>
  1019. $this->entsearch = []; // Array of HTML entities (>ASCII 127) to substitute
  1020. $this->entsubstitute = []; // Array of substitution decimal unicode for the Hi entities
  1021. $this->lastoptionaltag = '';
  1022. $this->charset_in = '';
  1023. $this->blk = [];
  1024. $this->blklvl = 0;
  1025. $this->tts = false;
  1026. $this->ttz = false;
  1027. $this->tta = false;
  1028. $this->ispre = false;
  1029. $this->checkSIP = false;
  1030. $this->checkSMP = false;
  1031. $this->checkCJK = false;
  1032. $this->page_break_after_avoid = false;
  1033. $this->margin_bottom_collapse = false;
  1034. $this->tablethead = 0;
  1035. $this->tabletfoot = 0;
  1036. $this->table_border_attr_set = 0;
  1037. $this->table_border_css_set = 0;
  1038. $this->shrin_k = 1.0;
  1039. $this->shrink_this_table_to_fit = 0;
  1040. $this->MarginCorrection = 0;
  1041. $this->tabletheadjustfinished = false;
  1042. $this->usingCoreFont = false;
  1043. $this->charspacing = 0;
  1044. $this->autoPageBreak = true;
  1045. $this->_setPageSize($format, $orientation);
  1046. $this->DefOrientation = $orientation;
  1047. $this->margin_header = $mgh;
  1048. $this->margin_footer = $mgf;
  1049. $bmargin = $mgb;
  1050. $this->DeflMargin = $mgl;
  1051. $this->DefrMargin = $mgr;
  1052. $this->orig_tMargin = $mgt;
  1053. $this->orig_bMargin = $bmargin;
  1054. $this->orig_lMargin = $this->DeflMargin;
  1055. $this->orig_rMargin = $this->DefrMargin;
  1056. $this->orig_hMargin = $this->margin_header;
  1057. $this->orig_fMargin = $this->margin_footer;
  1058. if ($this->setAutoTopMargin == 'pad') {
  1059. $mgt += $this->margin_header;
  1060. }
  1061. if ($this->setAutoBottomMargin == 'pad') {
  1062. $mgb += $this->margin_footer;
  1063. }
  1064. // sets l r t margin
  1065. $this->SetMargins($this->DeflMargin, $this->DefrMargin, $mgt);
  1066. // Automatic page break
  1067. // sets $this->bMargin & PageBreakTrigger
  1068. $this->SetAutoPageBreak($this->autoPageBreak, $bmargin);
  1069. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  1070. // Interior cell margin (1 mm) ? not used
  1071. $this->cMarginL = 1;
  1072. $this->cMarginR = 1;
  1073. // Line width (0.2 mm)
  1074. $this->LineWidth = .567 / Mpdf::SCALE;
  1075. // Enable all tags as default
  1076. $this->DisableTags();
  1077. // Full width display mode
  1078. $this->SetDisplayMode(100); // fullwidth? 'fullpage'
  1079. // Compression
  1080. $this->SetCompression(true);
  1081. // Set default display preferences
  1082. $this->SetDisplayPreferences('');
  1083. $this->initFontConfig($originalConfig);
  1084. // Available fonts
  1085. $this->available_unifonts = [];
  1086. foreach ($this->fontdata as $f => $fs) {
  1087. if (isset($fs['R']) && $fs['R']) {
  1088. $this->available_unifonts[] = $f;
  1089. }
  1090. if (isset($fs['B']) && $fs['B']) {
  1091. $this->available_unifonts[] = $f . 'B';
  1092. }
  1093. if (isset($fs['I']) && $fs['I']) {
  1094. $this->available_unifonts[] = $f . 'I';
  1095. }
  1096. if (isset($fs['BI']) && $fs['BI']) {
  1097. $this->available_unifonts[] = $f . 'BI';
  1098. }
  1099. }
  1100. $this->default_available_fonts = $this->available_unifonts;
  1101. $optcore = false;
  1102. $onlyCoreFonts = false;
  1103. if (preg_match('/([\-+])aCJK/i', $mode, $m)) {
  1104. $mode = preg_replace('/([\-+])aCJK/i', '', $mode); // mPDF 6
  1105. if ($m[1] == '+') {
  1106. $this->useAdobeCJK = true;
  1107. } else {
  1108. $this->useAdobeCJK = false;
  1109. }
  1110. }
  1111. if (strlen($mode) == 1) {
  1112. if ($mode == 's') {
  1113. $this->percentSubset = 100;
  1114. $mode = '';
  1115. } elseif ($mode == 'c') {
  1116. $onlyCoreFonts = true;
  1117. $mode = '';
  1118. }
  1119. } elseif (substr($mode, -2) == '-s') {
  1120. $this->percentSubset = 100;
  1121. $mode = substr($mode, 0, strlen($mode) - 2);
  1122. } elseif (substr($mode, -2) == '-c') {
  1123. $onlyCoreFonts = true;
  1124. $mode = substr($mode, 0, strlen($mode) - 2);
  1125. } elseif (substr($mode, -2) == '-x') {
  1126. $optcore = true;
  1127. $mode = substr($mode, 0, strlen($mode) - 2);
  1128. }
  1129. // Autodetect if mode is a language_country string (en-GB or en_GB or en)
  1130. if ($mode && $mode != 'UTF-8') { // mPDF 6
  1131. list ($coreSuitable, $mpdf_pdf_unifont) = $this->languageToFont->getLanguageOptions($mode, $this->useAdobeCJK);
  1132. if ($coreSuitable && $optcore) {
  1133. $onlyCoreFonts = true;
  1134. }
  1135. if ($mpdf_pdf_unifont) { // mPDF 6
  1136. $default_font = $mpdf_pdf_unifont;
  1137. }
  1138. $this->currentLang = $mode;
  1139. $this->default_lang = $mode;
  1140. }
  1141. $this->onlyCoreFonts = $onlyCoreFonts;
  1142. if ($this->onlyCoreFonts) {
  1143. $this->setMBencoding('windows-1252'); // sets $this->mb_enc
  1144. } else {
  1145. $this->setMBencoding('UTF-8'); // sets $this->mb_enc
  1146. }
  1147. @mb_regex_encoding('UTF-8'); // required only for mb_ereg... and mb_split functions
  1148. // Adobe CJK fonts
  1149. $this->available_CJK_fonts = [
  1150. 'gb',
  1151. 'big5',
  1152. 'sjis',
  1153. 'uhc',
  1154. 'gbB',
  1155. 'big5B',
  1156. 'sjisB',
  1157. 'uhcB',
  1158. 'gbI',
  1159. 'big5I',
  1160. 'sjisI',
  1161. 'uhcI',
  1162. 'gbBI',
  1163. 'big5BI',
  1164. 'sjisBI',
  1165. 'uhcBI',
  1166. ];
  1167. // Standard fonts
  1168. $this->CoreFonts = [
  1169. 'ccourier' => 'Courier',
  1170. 'ccourierB' => 'Courier-Bold',
  1171. 'ccourierI' => 'Courier-Oblique',
  1172. 'ccourierBI' => 'Courier-BoldOblique',
  1173. 'chelvetica' => 'Helvetica',
  1174. 'chelveticaB' => 'Helvetica-Bold',
  1175. 'chelveticaI' => 'Helvetica-Oblique',
  1176. 'chelveticaBI' => 'Helvetica-BoldOblique',
  1177. 'ctimes' => 'Times-Roman',
  1178. 'ctimesB' => 'Times-Bold',
  1179. 'ctimesI' => 'Times-Italic',
  1180. 'ctimesBI' => 'Times-BoldItalic',
  1181. 'csymbol' => 'Symbol',
  1182. 'czapfdingbats' => 'ZapfDingbats'
  1183. ];
  1184. $this->fontlist = [
  1185. "ctimes",
  1186. "ccourier",
  1187. "chelvetica",
  1188. "csymbol",
  1189. "czapfdingbats"
  1190. ];
  1191. // Substitutions
  1192. $this->setHiEntitySubstitutions();
  1193. if ($this->onlyCoreFonts) {
  1194. $this->useSubstitutions = true;
  1195. $this->SetSubstitutions();
  1196. } else {
  1197. $this->useSubstitutions = $config['useSubstitutions'];
  1198. }
  1199. if (file_exists($this->defaultCssFile)) {
  1200. $css = file_get_contents($this->defaultCssFile);
  1201. $this->cssManager->ReadCSS('<style> ' . $css . ' </style>');
  1202. } else {
  1203. throw new \Mpdf\MpdfException(sprintf('Unable to read default CSS file "%s"', $this->defaultCssFile));
  1204. }
  1205. if ($default_font == '') {
  1206. if ($this->onlyCoreFonts) {
  1207. if (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->mono_fonts)) {
  1208. $default_font = 'ccourier';
  1209. } elseif (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->sans_fonts)) {
  1210. $default_font = 'chelvetica';
  1211. } else {
  1212. $default_font = 'ctimes';
  1213. }
  1214. } else {
  1215. $default_font = $this->defaultCSS['BODY']['FONT-FAMILY'];
  1216. }
  1217. }
  1218. if (!$default_font_size) {
  1219. $mmsize = $this->sizeConverter->convert($this->defaultCSS['BODY']['FONT-SIZE']);
  1220. $default_font_size = $mmsize * (Mpdf::SCALE);
  1221. }
  1222. if ($default_font) {
  1223. $this->SetDefaultFont($default_font);
  1224. }
  1225. if ($default_font_size) {
  1226. $this->SetDefaultFontSize($default_font_size);
  1227. }
  1228. $this->SetLineHeight(); // lineheight is in mm
  1229. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  1230. $this->HREF = '';
  1231. $this->oldy = -1;
  1232. $this->B = 0;
  1233. $this->I = 0;
  1234. // mPDF 6 Lists
  1235. $this->listlvl = 0;
  1236. $this->listtype = [];
  1237. $this->listitem = [];
  1238. $this->listcounter = [];
  1239. $this->tdbegin = false;
  1240. $this->table = [];
  1241. $this->cell = [];
  1242. $this->col = -1;
  1243. $this->row = -1;
  1244. $this->cellBorderBuffer = [];
  1245. $this->divbegin = false;
  1246. // mPDF 6
  1247. $this->cellTextAlign = '';
  1248. $this->cellLineHeight = '';
  1249. $this->cellLineStackingStrategy = '';
  1250. $this->cellLineStackingShift = '';
  1251. $this->divwidth = 0;
  1252. $this->divheight = 0;
  1253. $this->spanbgcolor = false;
  1254. $this->spanborder = false;
  1255. $this->spanborddet = [];
  1256. $this->blockjustfinished = false;
  1257. $this->ignorefollowingspaces = true; // in order to eliminate exceeding left-side spaces
  1258. $this->dash_on = false;
  1259. $this->dotted_on = false;
  1260. $this->textshadow = '';
  1261. $this->currentfontfamily = '';
  1262. $this->currentfontsize = '';
  1263. $this->currentfontstyle = '';
  1264. $this->colorarray = ''; // mPDF 6
  1265. $this->spanbgcolorarray = ''; // mPDF 6
  1266. $this->textbuffer = [];
  1267. $this->internallink = [];
  1268. $this->basepath = "";
  1269. $this->SetBasePath('');
  1270. $this->textparam = [];
  1271. $this->specialcontent = '';
  1272. $this->selectoption = [];
  1273. }
  1274. public function cleanup()
  1275. {
  1276. mb_internal_encoding($this->originalMbEnc);
  1277. @mb_regex_encoding($this->originalMbRegexEnc);
  1278. // this will free up the readers, based on code from Setasign's FpdiTrait::cleanUp()
  1279. foreach ($this->createdReaders as $id) {
  1280. $this->readers[$id]->getParser()->getStreamReader()->cleanUp();
  1281. unset($this->readers[$id]);
  1282. }
  1283. $this->createdReaders = [];
  1284. }
  1285. private function initConfig(array $config)
  1286. {
  1287. $configObject = new ConfigVariables();
  1288. $defaults = $configObject->getDefaults();
  1289. $config = array_intersect_key($config + $defaults, $defaults);
  1290. foreach ($config as $var => $val) {
  1291. $this->{$var} = $val;
  1292. }
  1293. return $config;
  1294. }
  1295. private function initConstructorParams(array $config)
  1296. {
  1297. $constructor = [
  1298. 'mode' => '',
  1299. 'format' => 'A4',
  1300. 'default_font_size' => 0,
  1301. 'default_font' => '',
  1302. 'margin_left' => 15,
  1303. 'margin_right' => 15,
  1304. 'margin_top' => 16,
  1305. 'margin_bottom' => 16,
  1306. 'margin_header' => 9,
  1307. 'margin_footer' => 9,
  1308. 'orientation' => 'P',
  1309. ];
  1310. foreach ($constructor as $key => $val) {
  1311. if (isset($config[$key])) {
  1312. $constructor[$key] = $config[$key];
  1313. }
  1314. }
  1315. return array_values($constructor);
  1316. }
  1317. private function initFontConfig(array $config)
  1318. {
  1319. $configObject = new FontVariables();
  1320. $defaults = $configObject->getDefaults();
  1321. $config = array_intersect_key($config + $defaults, $defaults);
  1322. foreach ($config as $var => $val) {
  1323. $this->{$var} = $val;
  1324. }
  1325. return $config;
  1326. }
  1327. function _setPageSize($format, &$orientation)
  1328. {
  1329. if (is_string($format)) {
  1330. if (empty($format)) {
  1331. $format = 'A4';
  1332. }
  1333. // e.g. A4-L = A4 landscape, A4-P = A4 portrait
  1334. $orientation = $orientation ?: 'P';
  1335. if (preg_match('/([0-9a-zA-Z]*)-([P,L])/i', $format, $m)) {
  1336. list(, $format, $orientation) = $m;
  1337. }
  1338. $format = PageFormat::getSizeFromName($format);
  1339. $this->fwPt = $format[0];
  1340. $this->fhPt = $format[1];
  1341. } else {
  1342. if (!$format[0] || !$format[1]) {
  1343. throw new \Mpdf\MpdfException('Invalid page format: ' . $format[0] . ' ' . $format[1]);
  1344. }
  1345. $this->fwPt = $format[0] * Mpdf::SCALE;
  1346. $this->fhPt = $format[1] * Mpdf::SCALE;
  1347. }
  1348. $this->fw = $this->fwPt / Mpdf::SCALE;
  1349. $this->fh = $this->fhPt / Mpdf::SCALE;
  1350. // Page orientation
  1351. $orientation = strtolower($orientation);
  1352. if ($orientation === 'p' || $orientation === 'portrait') {
  1353. $orientation = 'P';
  1354. $this->wPt = $this->fwPt;
  1355. $this->hPt = $this->fhPt;
  1356. } elseif ($orientation === 'l' || $orientation === 'landscape') {
  1357. $orientation = 'L';
  1358. $this->wPt = $this->fhPt;
  1359. $this->hPt = $this->fwPt;
  1360. } else {
  1361. throw new \Mpdf\MpdfException('Incorrect orientation: ' . $orientation);
  1362. }
  1363. $this->CurOrientation = $orientation;
  1364. $this->w = $this->wPt / Mpdf::SCALE;
  1365. $this->h = $this->hPt / Mpdf::SCALE;
  1366. }
  1367. function RestrictUnicodeFonts($res)
  1368. {
  1369. // $res = array of (Unicode) fonts to restrict to: e.g. norasi|norasiB - language specific
  1370. if (count($res)) { // Leave full list of available fonts if passed blank array
  1371. $this->available_unifonts = $res;
  1372. } else {
  1373. $this->available_unifonts = $this->default_available_fonts;
  1374. }
  1375. if (count($this->available_unifonts) == 0) {
  1376. $this->available_unifonts[] = $this->default_available_fonts[0];
  1377. }
  1378. $this->available_unifonts = array_values($this->available_unifonts);
  1379. }
  1380. function setMBencoding($enc)
  1381. {
  1382. if ($this->mb_enc != $enc) {
  1383. $this->mb_enc = $enc;
  1384. mb_internal_encoding($this->mb_enc);
  1385. }
  1386. }
  1387. function SetMargins($left, $right, $top)
  1388. {
  1389. // Set left, top and right margins
  1390. $this->lMargin = $left;
  1391. $this->rMargin = $right;
  1392. $this->tMargin = $top;
  1393. }
  1394. function ResetMargins()
  1395. {
  1396. // ReSet left, top margins
  1397. if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P' && $this->CurOrientation == 'L') {
  1398. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  1399. $this->tMargin = $this->orig_rMargin;
  1400. $this->bMargin = $this->orig_lMargin;
  1401. } else { // ODD // OR NOT MIRRORING MARGINS/FOOTERS
  1402. $this->tMargin = $this->orig_lMargin;
  1403. $this->bMargin = $this->orig_rMargin;
  1404. }
  1405. $this->lMargin = $this->DeflMargin;
  1406. $this->rMargin = $this->DefrMargin;
  1407. $this->MarginCorrection = 0;
  1408. $this->PageBreakTrigger = $this->h - $this->bMargin;
  1409. } elseif (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  1410. $this->lMargin = $this->DefrMargin;
  1411. $this->rMargin = $this->DeflMargin;
  1412. $this->MarginCorrection = $this->DefrMargin - $this->DeflMargin;
  1413. } else { // ODD // OR NOT MIRRORING MARGINS/FOOTERS
  1414. $this->lMargin = $this->DeflMargin;
  1415. $this->rMargin = $this->DefrMargin;
  1416. if ($this->mirrorMargins) {
  1417. $this->MarginCorrection = $this->DeflMargin - $this->DefrMargin;
  1418. }
  1419. }
  1420. $this->x = $this->lMargin;
  1421. }
  1422. function SetLeftMargin($margin)
  1423. {
  1424. // Set left margin
  1425. $this->lMargin = $margin;
  1426. if ($this->page > 0 and $this->x < $margin) {
  1427. $this->x = $margin;
  1428. }
  1429. }
  1430. function SetTopMargin($margin)
  1431. {
  1432. // Set top margin
  1433. $this->tMargin = $margin;
  1434. }
  1435. function SetRightMargin($margin)
  1436. {
  1437. // Set right margin
  1438. $this->rMargin = $margin;
  1439. }
  1440. function SetAutoPageBreak($auto, $margin = 0)
  1441. {
  1442. // Set auto page break mode and triggering margin
  1443. $this->autoPageBreak = $auto;
  1444. $this->bMargin = $margin;
  1445. $this->PageBreakTrigger = $this->h - $margin;
  1446. }
  1447. function SetDisplayMode($zoom, $layout = 'continuous')
  1448. {
  1449. $allowedZoomModes = ['fullpage', 'fullwidth', 'real', 'default', 'none'];
  1450. if (in_array($zoom, $allowedZoomModes, true) || is_numeric($zoom)) {
  1451. $this->ZoomMode = $zoom;
  1452. } else {
  1453. throw new \Mpdf\MpdfException('Incorrect zoom display mode: ' . $zoom);
  1454. }
  1455. $allowedLayoutModes = ['single', 'continuous', 'two', 'twoleft', 'tworight', 'default'];
  1456. if (in_array($layout, $allowedLayoutModes, true)) {
  1457. $this->LayoutMode = $layout;
  1458. } else {
  1459. throw new \Mpdf\MpdfException('Incorrect layout display mode: ' . $layout);
  1460. }
  1461. }
  1462. function SetCompression($compress)
  1463. {
  1464. // Set page compression
  1465. if (function_exists('gzcompress')) {
  1466. $this->compress = $compress;
  1467. } else {
  1468. $this->compress = false;
  1469. }
  1470. }
  1471. function SetTitle($title)
  1472. {
  1473. // Title of document // Arrives as UTF-8
  1474. $this->title = $title;
  1475. }
  1476. function SetSubject($subject)
  1477. {
  1478. // Subject of document
  1479. $this->subject = $subject;
  1480. }
  1481. function SetAuthor($author)
  1482. {
  1483. // Author of document
  1484. $this->author = $author;
  1485. }
  1486. function SetKeywords($keywords)
  1487. {
  1488. // Keywords of document
  1489. $this->keywords = $keywords;
  1490. }
  1491. function SetCreator($creator)
  1492. {
  1493. // Creator of document
  1494. $this->creator = $creator;
  1495. }
  1496. function AddCustomProperty($key, $value)
  1497. {
  1498. $this->customProperties[$key] = $value;
  1499. }
  1500. /**
  1501. * Set one or multiple associated file ("/AF" as required by PDF/A-3)
  1502. *
  1503. * param $files is an array of hash containing:
  1504. * path: file path on FS
  1505. * content: file content
  1506. * name: file name (not necessarily the same as the file on FS)
  1507. * mime (optional): file mime type (will show up as /Subtype in the PDF)
  1508. * description (optional): file description
  1509. * AFRelationship (optional): PDF/A-3 AFRelationship (e.g. "Alternative")
  1510. *
  1511. * e.g. to associate 1 file:
  1512. * [[
  1513. * 'path' => 'tmp/1234.xml',
  1514. * 'content' => 'file content',
  1515. * 'name' => 'public_name.xml',
  1516. * 'mime' => 'text/xml',
  1517. * 'description' => 'foo',
  1518. * 'AFRelationship' => 'Alternative',
  1519. * ]]
  1520. *
  1521. * @param mixed[] $files Array of arrays of associated files. See above
  1522. */
  1523. function SetAssociatedFiles(array $files)
  1524. {
  1525. $this->associatedFiles = $files;
  1526. }
  1527. function SetAdditionalXmpRdf($s)
  1528. {
  1529. $this->additionalXmpRdf = $s;
  1530. }
  1531. function SetAnchor2Bookmark($x)
  1532. {
  1533. $this->anchor2Bookmark = $x;
  1534. }
  1535. public function AliasNbPages($alias = '{nb}')
  1536. {
  1537. // Define an alias for total number of pages
  1538. $this->aliasNbPg = $alias;
  1539. }
  1540. public function AliasNbPageGroups($alias = '{nbpg}')
  1541. {
  1542. // Define an alias for total number of pages in a group
  1543. $this->aliasNbPgGp = $alias;
  1544. }
  1545. function SetAlpha($alpha, $bm = 'Normal', $return = false, $mode = 'B')
  1546. {
  1547. // alpha: real value from 0 (transparent) to 1 (opaque)
  1548. // bm: blend mode, one of the following:
  1549. // Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn,
  1550. // HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
  1551. // set alpha for stroking (CA) and non-stroking (ca) operations
  1552. // mode determines F (fill) S (stroke) B (both)
  1553. if (($this->PDFA || $this->PDFX) && $alpha != 1) {
  1554. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  1555. $this->PDFAXwarnings[] = "Image opacity must be 100% (Opacity changed to 100%)";
  1556. }
  1557. $alpha = 1;
  1558. }
  1559. $a = ['BM' => '/' . $bm];
  1560. if ($mode == 'F' || $mode == 'B') {
  1561. $a['ca'] = $alpha; // mPDF 5.7.2
  1562. }
  1563. if ($mode == 'S' || $mode == 'B') {
  1564. $a['CA'] = $alpha; // mPDF 5.7.2
  1565. }
  1566. $gs = $this->AddExtGState($a);
  1567. if ($return) {
  1568. return sprintf('/GS%d gs', $gs);
  1569. } else {
  1570. $this->writer->write(sprintf('/GS%d gs', $gs));
  1571. }
  1572. }
  1573. function AddExtGState($parms)
  1574. {
  1575. $n = count($this->extgstates);
  1576. // check if graphics state already exists
  1577. for ($i = 1; $i <= $n; $i++) {
  1578. if (count($this->extgstates[$i]['parms']) == count($parms)) {
  1579. $same = true;
  1580. foreach ($this->extgstates[$i]['parms'] as $k => $v) {
  1581. if (!isset($parms[$k]) || $parms[$k] != $v) {
  1582. $same = false;
  1583. break;
  1584. }
  1585. }
  1586. if ($same) {
  1587. return $i;
  1588. }
  1589. }
  1590. }
  1591. $n++;
  1592. $this->extgstates[$n]['parms'] = $parms;
  1593. return $n;
  1594. }
  1595. function SetVisibility($v)
  1596. {
  1597. if (($this->PDFA || $this->PDFX) && $this->visibility != 'visible') {
  1598. $this->PDFAXwarnings[] = "Cannot set visibility to anything other than full when using PDFA or PDFX";
  1599. return '';
  1600. } elseif (!$this->PDFA && !$this->PDFX) {
  1601. $this->pdf_version = '1.5';
  1602. }
  1603. if ($this->visibility != 'visible') {
  1604. $this->writer->write('EMC');
  1605. $this->hasOC = intval($this->hasOC);
  1606. }
  1607. if ($v == 'printonly') {
  1608. $this->writer->write('/OC /OC1 BDC');
  1609. $this->hasOC = ($this->hasOC | 1);
  1610. } elseif ($v == 'screenonly') {
  1611. $this->writer->write('/OC /OC2 BDC');
  1612. $this->hasOC = ($this->hasOC | 2);
  1613. } elseif ($v == 'hidden') {
  1614. $this->writer->write('/OC /OC3 BDC');
  1615. $this->hasOC = ($this->hasOC | 4);
  1616. } elseif ($v != 'visible') {
  1617. throw new \Mpdf\MpdfException('Incorrect visibility: ' . $v);
  1618. }
  1619. $this->visibility = $v;
  1620. }
  1621. function Open()
  1622. {
  1623. // Begin document
  1624. if ($this->state == 0) {
  1625. $this->state = 1;
  1626. if (false === $this->preambleWritten) {
  1627. $this->writer->write('%PDF-' . $this->pdf_version);
  1628. $this->writer->write('%' . chr(226) . chr(227) . chr(207) . chr(211)); // 4 chars > 128 to show binary file
  1629. $this->preambleWritten = true;
  1630. }
  1631. }
  1632. }
  1633. function Close()
  1634. {
  1635. // @log Closing last page
  1636. // Terminate document
  1637. if ($this->state == 3) {
  1638. return;
  1639. }
  1640. if ($this->page == 0) {
  1641. $this->AddPage($this->CurOrientation);
  1642. }
  1643. if (count($this->cellBorderBuffer)) {
  1644. $this->printcellbuffer();
  1645. }
  1646. // *TABLES*
  1647. if ($this->tablebuffer) {
  1648. $this->printtablebuffer();
  1649. }
  1650. /* -- COLUMNS -- */
  1651. if ($this->ColActive) {
  1652. $this->SetColumns(0);
  1653. $this->ColActive = 0;
  1654. if (count($this->columnbuffer)) {
  1655. $this->printcolumnbuffer();
  1656. }
  1657. }
  1658. /* -- END COLUMNS -- */
  1659. // BODY Backgrounds
  1660. $s = '';
  1661. $s .= $this->PrintBodyBackgrounds();
  1662. $s .= $this->PrintPageBackgrounds();
  1663. $this->pages[$this->page] = preg_replace(
  1664. '/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/',
  1665. "\n" . $s . "\n" . '\\1',
  1666. $this->pages[$this->page]
  1667. );
  1668. $this->pageBackgrounds = [];
  1669. if ($this->visibility != 'visible') {
  1670. $this->SetVisibility('visible');
  1671. }
  1672. $this->EndLayer();
  1673. if (!$this->tableOfContents->TOCmark) { // Page footer
  1674. $this->InFooter = true;
  1675. $this->Footer();
  1676. $this->InFooter = false;
  1677. }
  1678. if ($this->tableOfContents->TOCmark || count($this->tableOfContents->m_TOC)) {
  1679. $this->tableOfContents->insertTOC();
  1680. }
  1681. // Close page
  1682. $this->_endpage();
  1683. // Close document
  1684. $this->_enddoc();
  1685. }
  1686. /* -- BACKGROUNDS -- */
  1687. function _resizeBackgroundImage($imw, $imh, $cw, $ch, $resize, $repx, $repy, $pba = [], $size = [])
  1688. {
  1689. // pba is background positioning area (from CSS background-origin) may not always be set [x,y,w,h]
  1690. // size is from CSS3 background-size - takes precendence over old resize
  1691. // $w - absolute length or % or auto or cover | contain
  1692. // $h - absolute length or % or auto or cover | contain
  1693. if (isset($pba['w'])) {
  1694. $cw = $pba['w'];
  1695. }
  1696. if (isset($pba['h'])) {
  1697. $ch = $pba['h'];
  1698. }
  1699. $cw = $cw * Mpdf::SCALE;
  1700. $ch = $ch * Mpdf::SCALE;
  1701. if (empty($size) && !$resize) {
  1702. return [$imw, $imh, $repx, $repy];
  1703. }
  1704. if (isset($size['w']) && $size['w']) {
  1705. if ($size['w'] == 'contain') {
  1706. // Scale the image, while preserving its intrinsic aspect ratio (if any),
  1707. // to the largest size such that both its width and its height can fit inside the background positioning area.
  1708. // Same as resize==3
  1709. $h = $imh * $cw / $imw;
  1710. $w = $cw;
  1711. if ($h > $ch) {
  1712. $w = $w * $ch / $h;
  1713. $h = $ch;
  1714. }
  1715. } elseif ($size['w'] == 'cover') {
  1716. // Scale the image, while preserving its intrinsic aspect ratio (if any),
  1717. // to the smallest size such that both its width and its height can completely cover the background positioning area.
  1718. $h = $imh * $cw / $imw;
  1719. $w = $cw;
  1720. if ($h < $ch) {
  1721. $w = $w * $h / $ch;
  1722. $h = $ch;
  1723. }
  1724. } else {
  1725. if (stristr($size['w'], '%')) {
  1726. $size['w'] = (float) $size['w'];
  1727. $size['w'] /= 100;
  1728. $size['w'] = ($cw * $size['w']);
  1729. }
  1730. if (stristr($size['h'], '%')) {
  1731. $size['h'] = (float) $size['h'];
  1732. $size['h'] /= 100;
  1733. $size['h'] = ($ch * $size['h']);
  1734. }
  1735. if ($size['w'] == 'auto' && $size['h'] == 'auto') {
  1736. $w = $imw;
  1737. $h = $imh;
  1738. } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
  1739. $w = $imw * $size['h'] / $imh;
  1740. $h = $size['h'];
  1741. } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
  1742. $h = $imh * $size['w'] / $imw;
  1743. $w = $size['w'];
  1744. } else {
  1745. $w = $size['w'];
  1746. $h = $size['h'];
  1747. }
  1748. }
  1749. return [$w, $h, $repx, $repy];
  1750. } elseif ($resize == 1 && $imw > $cw) {
  1751. $h = $imh * $cw / $imw;
  1752. return [$cw, $h, $repx, $repy];
  1753. } elseif ($resize == 2 && $imh > $ch) {
  1754. $w = $imw * $ch / $imh;
  1755. return [$w, $ch, $repx, $repy];
  1756. } elseif ($resize == 3) {
  1757. $w = $imw;
  1758. $h = $imh;
  1759. if ($w > $cw) {
  1760. $h = $h * $cw / $w;
  1761. $w = $cw;
  1762. }
  1763. if ($h > $ch) {
  1764. $w = $w * $ch / $h;
  1765. $h = $ch;
  1766. }
  1767. return [$w, $h, $repx, $repy];
  1768. } elseif ($resize == 4) {
  1769. $h = $imh * $cw / $imw;
  1770. return [$cw, $h, $repx, $repy];
  1771. } elseif ($resize == 5) {
  1772. $w = $imw * $ch / $imh;
  1773. return [$w, $ch, $repx, $repy];
  1774. } elseif ($resize == 6) {
  1775. return [$cw, $ch, $repx, $repy];
  1776. }
  1777. return [$imw, $imh, $repx, $repy];
  1778. }
  1779. function SetBackground(&$properties, &$maxwidth)
  1780. {
  1781. if (isset($properties['BACKGROUND-ORIGIN']) && ($properties['BACKGROUND-ORIGIN'] == 'border-box' || $properties['BACKGROUND-ORIGIN'] == 'content-box')) {
  1782. $origin = $properties['BACKGROUND-ORIGIN'];
  1783. } else {
  1784. $origin = 'padding-box';
  1785. }
  1786. if (isset($properties['BACKGROUND-SIZE'])) {
  1787. if (stristr($properties['BACKGROUND-SIZE'], 'contain')) {
  1788. $bsw = $bsh = 'contain';
  1789. } elseif (stristr($properties['BACKGROUND-SIZE'], 'cover')) {
  1790. $bsw = $bsh = 'cover';
  1791. } else {
  1792. $bsw = $bsh = 'auto';
  1793. $sz = preg_split('/\s+/', trim($properties['BACKGROUND-SIZE']));
  1794. if (count($sz) == 2) {
  1795. $bsw = $sz[0];
  1796. $bsh = $sz[1];
  1797. } else {
  1798. $bsw = $sz[0];
  1799. }
  1800. if (!stristr($bsw, '%') && !stristr($bsw, 'auto')) {
  1801. $bsw = $this->sizeConverter->convert($bsw, $maxwidth, $this->FontSize);
  1802. }
  1803. if (!stristr($bsh, '%') && !stristr($bsh, 'auto')) {
  1804. $bsh = $this->sizeConverter->convert($bsh, $maxwidth, $this->FontSize);
  1805. }
  1806. }
  1807. $size = ['w' => $bsw, 'h' => $bsh];
  1808. } else {
  1809. $size = false;
  1810. } // mPDF 6
  1811. if (preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $properties['BACKGROUND-IMAGE'])) {
  1812. return ['gradient' => $properties['BACKGROUND-IMAGE'], 'origin' => $origin, 'size' => $size];
  1813. } else {
  1814. $file = $properties['BACKGROUND-IMAGE'];
  1815. $sizesarray = $this->Image($file, 0, 0, 0, 0, '', '', false, false, false, false, true);
  1816. if (isset($sizesarray['IMAGE_ID'])) {
  1817. $image_id = $sizesarray['IMAGE_ID'];
  1818. $orig_w = $sizesarray['WIDTH'] * Mpdf::SCALE; // in user units i.e. mm
  1819. $orig_h = $sizesarray['HEIGHT'] * Mpdf::SCALE; // (using $this->img_dpi)
  1820. if (isset($properties['BACKGROUND-IMAGE-RESOLUTION'])) {
  1821. if (preg_match('/from-image/i', $properties['BACKGROUND-IMAGE-RESOLUTION']) && isset($sizesarray['set-dpi']) && $sizesarray['set-dpi'] > 0) {
  1822. $orig_w *= $this->img_dpi / $sizesarray['set-dpi'];
  1823. $orig_h *= $this->img_dpi / $sizesarray['set-dpi'];
  1824. } elseif (preg_match('/(\d+)dpi/i', $properties['BACKGROUND-IMAGE-RESOLUTION'], $m)) {
  1825. $dpi = $m[1];
  1826. if ($dpi > 0) {
  1827. $orig_w *= $this->img_dpi / $dpi;
  1828. $orig_h *= $this->img_dpi / $dpi;
  1829. }
  1830. }
  1831. }
  1832. $x_repeat = true;
  1833. $y_repeat = true;
  1834. if (isset($properties['BACKGROUND-REPEAT'])) {
  1835. if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-x') {
  1836. $y_repeat = false;
  1837. }
  1838. if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-y') {
  1839. $x_repeat = false;
  1840. }
  1841. }
  1842. $x_pos = 0;
  1843. $y_pos = 0;
  1844. if (isset($properties['BACKGROUND-POSITION'])) {
  1845. $ppos = preg_split('/\s+/', $properties['BACKGROUND-POSITION']);
  1846. $x_pos = $ppos[0];
  1847. $y_pos = $ppos[1];
  1848. if (!stristr($x_pos, '%')) {
  1849. $x_pos = $this->sizeConverter->convert($x_pos, $maxwidth, $this->FontSize);
  1850. }
  1851. if (!stristr($y_pos, '%')) {
  1852. $y_pos = $this->sizeConverter->convert($y_pos, $maxwidth, $this->FontSize);
  1853. }
  1854. }
  1855. if (isset($properties['BACKGROUND-IMAGE-RESIZE'])) {
  1856. $resize = $properties['BACKGROUND-IMAGE-RESIZE'];
  1857. } else {
  1858. $resize = 0;
  1859. }
  1860. if (isset($properties['BACKGROUND-IMAGE-OPACITY'])) {
  1861. $opacity = $properties['BACKGROUND-IMAGE-OPACITY'];
  1862. } else {
  1863. $opacity = 1;
  1864. }
  1865. 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];
  1866. }
  1867. }
  1868. return false;
  1869. }
  1870. /* -- END BACKGROUNDS -- */
  1871. function PrintBodyBackgrounds()
  1872. {
  1873. $s = '';
  1874. $clx = 0;
  1875. $cly = 0;
  1876. $clw = $this->w;
  1877. $clh = $this->h;
  1878. // If using bleed and trim margins in paged media
  1879. if ($this->pageDim[$this->page]['outer_width_LR'] || $this->pageDim[$this->page]['outer_width_TB']) {
  1880. $clx = $this->pageDim[$this->page]['outer_width_LR'] - $this->pageDim[$this->page]['bleedMargin'];
  1881. $cly = $this->pageDim[$this->page]['outer_width_TB'] - $this->pageDim[$this->page]['bleedMargin'];
  1882. $clw = $this->w - 2 * $clx;
  1883. $clh = $this->h - 2 * $cly;
  1884. }
  1885. if ($this->bodyBackgroundColor) {
  1886. $s .= 'q ' . $this->SetFColor($this->bodyBackgroundColor, true) . "\n";
  1887. if ($this->bodyBackgroundColor[0] == 5) { // RGBa
  1888. $s .= $this->SetAlpha(ord($this->bodyBackgroundColor[4]) / 100, 'Normal', true, 'F') . "\n";
  1889. } elseif ($this->bodyBackgroundColor[0] == 6) { // CMYKa
  1890. $s .= $this->SetAlpha(ord($this->bodyBackgroundColor[5]) / 100, 'Normal', true, 'F') . "\n";
  1891. }
  1892. $s .= sprintf('%.3F %.3F %.3F %.3F re f Q', ($clx * Mpdf::SCALE), ($cly * Mpdf::SCALE), $clw * Mpdf::SCALE, $clh * Mpdf::SCALE) . "\n";
  1893. }
  1894. /* -- BACKGROUNDS -- */
  1895. if ($this->bodyBackgroundGradient) {
  1896. $g = $this->gradient->parseBackgroundGradient($this->bodyBackgroundGradient);
  1897. if ($g) {
  1898. $s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, (isset($g['gradtype']) ? $g['gradtype'] : null), $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);
  1899. }
  1900. }
  1901. if ($this->bodyBackgroundImage) {
  1902. if (isset($this->bodyBackgroundImage['gradient']) && $this->bodyBackgroundImage['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->bodyBackgroundImage['gradient'])) {
  1903. $g = $this->gradient->parseMozGradient($this->bodyBackgroundImage['gradient']);
  1904. if ($g) {
  1905. $s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);
  1906. }
  1907. } elseif ($this->bodyBackgroundImage['image_id']) { // Background pattern
  1908. $n = count($this->patterns) + 1;
  1909. // If using resize, uses TrimBox (not including the bleed)
  1910. 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']);
  1911. $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']];
  1912. if (($this->bodyBackgroundImage['opacity'] > 0 || $this->bodyBackgroundImage['opacity'] === '0') && $this->bodyBackgroundImage['opacity'] < 1) {
  1913. $opac = $this->SetAlpha($this->bodyBackgroundImage['opacity'], 'Normal', true);
  1914. } else {
  1915. $opac = '';
  1916. }
  1917. $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";
  1918. }
  1919. }
  1920. /* -- END BACKGROUNDS -- */
  1921. return $s;
  1922. }
  1923. function _setClippingPath($clx, $cly, $clw, $clh)
  1924. {
  1925. $s = ' q 0 w '; // Line width=0
  1926. $s .= sprintf('%.3F %.3F m ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // start point TL before the arc
  1927. $s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BL
  1928. $s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BR
  1929. $s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TR
  1930. $s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TL
  1931. $s .= ' W n '; // Ends path no-op & Sets the clipping path
  1932. return $s;
  1933. }
  1934. function PrintPageBackgrounds($adjustmenty = 0)
  1935. {
  1936. $s = '';
  1937. ksort($this->pageBackgrounds);
  1938. foreach ($this->pageBackgrounds as $bl => $pbs) {
  1939. foreach ($pbs as $pb) {
  1940. if ((!isset($pb['image_id']) && !isset($pb['gradient'])) || isset($pb['shadowonly'])) { // Background colour or boxshadow
  1941. if ($pb['z-index'] > 0) {
  1942. $this->current_layer = $pb['z-index'];
  1943. $s .= "\n" . '/OCBZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n";
  1944. }
  1945. if ($pb['visibility'] != 'visible') {
  1946. if ($pb['visibility'] == 'printonly') {
  1947. $s .= '/OC /OC1 BDC' . "\n";
  1948. } elseif ($pb['visibility'] == 'screenonly') {
  1949. $s .= '/OC /OC2 BDC' . "\n";
  1950. } elseif ($pb['visibility'] == 'hidden') {
  1951. $s .= '/OC /OC3 BDC' . "\n";
  1952. }
  1953. }
  1954. // Box shadow
  1955. if (isset($pb['shadow']) && $pb['shadow']) {
  1956. $s .= $pb['shadow'] . "\n";
  1957. }
  1958. if (isset($pb['clippath']) && $pb['clippath']) {
  1959. $s .= $pb['clippath'] . "\n";
  1960. }
  1961. $s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n";
  1962. if ($pb['col'] && $pb['col'][0] === '5') { // RGBa
  1963. $s .= $this->SetAlpha(ord($pb['col'][4]) / 100, 'Normal', true, 'F') . "\n";
  1964. } elseif ($pb['col'] && $pb['col'][0] === '6') { // CMYKa
  1965. $s .= $this->SetAlpha(ord($pb['col'][5]) / 100, 'Normal', true, 'F') . "\n";
  1966. }
  1967. $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";
  1968. if (isset($pb['clippath']) && $pb['clippath']) {
  1969. $s .= 'Q' . "\n";
  1970. }
  1971. if ($pb['visibility'] != 'visible') {
  1972. $s .= 'EMC' . "\n";
  1973. }
  1974. if ($pb['z-index'] > 0) {
  1975. $s .= "\n" . 'EMCBZ-index' . "\n";
  1976. $this->current_layer = 0;
  1977. }
  1978. }
  1979. }
  1980. /* -- BACKGROUNDS -- */
  1981. foreach ($pbs as $pb) {
  1982. if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {
  1983. if ($pb['z-index'] > 0) {
  1984. $this->current_layer = $pb['z-index'];
  1985. $s .= "\n" . '/OCGZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n";
  1986. }
  1987. if ($pb['visibility'] != 'visible') {
  1988. if ($pb['visibility'] == 'printonly') {
  1989. $s .= '/OC /OC1 BDC' . "\n";
  1990. } elseif ($pb['visibility'] == 'screenonly') {
  1991. $s .= '/OC /OC2 BDC' . "\n";
  1992. } elseif ($pb['visibility'] == 'hidden') {
  1993. $s .= '/OC /OC3 BDC' . "\n";
  1994. }
  1995. }
  1996. }
  1997. if (isset($pb['gradient']) && $pb['gradient']) {
  1998. if (isset($pb['clippath']) && $pb['clippath']) {
  1999. $s .= $pb['clippath'] . "\n";
  2000. }
  2001. $s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);
  2002. if (isset($pb['clippath']) && $pb['clippath']) {
  2003. $s .= 'Q' . "\n";
  2004. }
  2005. } elseif (isset($pb['image_id']) && $pb['image_id']) { // Background Image
  2006. $pb['y'] -= $adjustmenty;
  2007. $pb['h'] += $adjustmenty;
  2008. $n = count($this->patterns) + 1;
  2009. 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']);
  2010. $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']];
  2011. $x = $pb['x'] * Mpdf::SCALE;
  2012. $y = ($this->h - $pb['y']) * Mpdf::SCALE;
  2013. $w = $pb['w'] * Mpdf::SCALE;
  2014. $h = -$pb['h'] * Mpdf::SCALE;
  2015. if (isset($pb['clippath']) && $pb['clippath']) {
  2016. $s .= $pb['clippath'] . "\n";
  2017. }
  2018. if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern
  2019. $iw = $pb['orig_w'] / Mpdf::SCALE;
  2020. $ih = $pb['orig_h'] / Mpdf::SCALE;
  2021. $w = $pb['w'];
  2022. $h = $pb['h'];
  2023. $x0 = $pb['x'];
  2024. $y0 = $pb['y'];
  2025. if (isset($pb['bpa']) && $pb['bpa']) {
  2026. $w = $pb['bpa']['w'];
  2027. $h = $pb['bpa']['h'];
  2028. $x0 = $pb['bpa']['x'];
  2029. $y0 = $pb['bpa']['y'];
  2030. }
  2031. if (isset($pb['size']['w']) && $pb['size']['w']) {
  2032. $size = $pb['size'];
  2033. if ($size['w'] == 'contain') {
  2034. // Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest
  2035. // size such that both its width and its height can fit inside the background positioning area.
  2036. // Same as resize==3
  2037. $ih = $ih * $pb['bpa']['w'] / $iw;
  2038. $iw = $pb['bpa']['w'];
  2039. if ($ih > $pb['bpa']['h']) {
  2040. $iw = $iw * $pb['bpa']['h'] / $ih;
  2041. $ih = $pb['bpa']['h'];
  2042. }
  2043. } elseif ($size['w'] == 'cover') {
  2044. // Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest
  2045. // size such that both its width and its height can completely cover the background positioning area.
  2046. $ih = $ih * $pb['bpa']['w'] / $iw;
  2047. $iw = $pb['bpa']['w'];
  2048. if ($ih < $pb['bpa']['h']) {
  2049. $iw = $iw * $ih / $pb['bpa']['h'];
  2050. $ih = $pb['bpa']['h'];
  2051. }
  2052. } else {
  2053. if (NumericString::containsPercentChar($size['w'])) {
  2054. $size['w'] = NumericString::removePercentChar($size['w']);
  2055. $size['w'] /= 100;
  2056. $size['w'] = ($pb['bpa']['w'] * $size['w']);
  2057. }
  2058. if (NumericString::containsPercentChar($size['h'])) {
  2059. $size['h'] = NumericString::removePercentChar($size['h']);
  2060. $size['h'] /= 100;
  2061. $size['h'] = ($pb['bpa']['h'] * $size['h']);
  2062. }
  2063. if ($size['w'] == 'auto' && $size['h'] == 'auto') {
  2064. $iw = $iw;
  2065. $ih = $ih;
  2066. } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
  2067. $iw = $iw * $size['h'] / $ih;
  2068. $ih = $size['h'];
  2069. } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
  2070. $ih = $ih * $size['w'] / $iw;
  2071. $iw = $size['w'];
  2072. } else {
  2073. $iw = $size['w'];
  2074. $ih = $size['h'];
  2075. }
  2076. }
  2077. }
  2078. // Number to repeat
  2079. if ($pb['x_repeat']) {
  2080. $nx = ceil($pb['w'] / $iw) + 1;
  2081. } else {
  2082. $nx = 1;
  2083. }
  2084. if ($pb['y_repeat']) {
  2085. $ny = ceil($pb['h'] / $ih) + 1;
  2086. } else {
  2087. $ny = 1;
  2088. }
  2089. $x_pos = $pb['x_pos'];
  2090. if (stristr($x_pos, '%')) {
  2091. $x_pos = (float) $x_pos;
  2092. $x_pos /= 100;
  2093. $x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
  2094. }
  2095. $y_pos = $pb['y_pos'];
  2096. if (stristr($y_pos, '%')) {
  2097. $y_pos = (float) $y_pos;
  2098. $y_pos /= 100;
  2099. $y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
  2100. }
  2101. if ($nx > 1) {
  2102. while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {
  2103. $x_pos -= $iw;
  2104. }
  2105. }
  2106. if ($ny > 1) {
  2107. while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {
  2108. $y_pos -= $ih;
  2109. }
  2110. }
  2111. for ($xi = 0; $xi < $nx; $xi++) {
  2112. for ($yi = 0; $yi < $ny; $yi++) {
  2113. $x = $x0 + $x_pos + ($iw * $xi);
  2114. $y = $y0 + $y_pos + ($ih * $yi);
  2115. if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {
  2116. $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
  2117. } else {
  2118. $opac = '';
  2119. }
  2120. $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";
  2121. }
  2122. }
  2123. } else {
  2124. if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {
  2125. $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
  2126. } else {
  2127. $opac = '';
  2128. }
  2129. $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n";
  2130. }
  2131. if (isset($pb['clippath']) && $pb['clippath']) {
  2132. $s .= 'Q' . "\n";
  2133. }
  2134. }
  2135. if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {
  2136. if ($pb['visibility'] != 'visible') {
  2137. $s .= 'EMC' . "\n";
  2138. }
  2139. if ($pb['z-index'] > 0) {
  2140. $s .= "\n" . 'EMCGZ-index' . "\n";
  2141. $this->current_layer = 0;
  2142. }
  2143. }
  2144. }
  2145. /* -- END BACKGROUNDS -- */
  2146. }
  2147. return $s;
  2148. }
  2149. function PrintTableBackgrounds($adjustmenty = 0)
  2150. {
  2151. $s = '';
  2152. /* -- BACKGROUNDS -- */
  2153. ksort($this->tableBackgrounds);
  2154. foreach ($this->tableBackgrounds as $bl => $pbs) {
  2155. foreach ($pbs as $pb) {
  2156. if ((!isset($pb['gradient']) || !$pb['gradient']) && (!isset($pb['image_id']) || !$pb['image_id'])) {
  2157. $s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n";
  2158. if ($pb['col'][0] == 5) { // RGBa
  2159. $s .= $this->SetAlpha(ord($pb['col'][4]) / 100, 'Normal', true, 'F') . "\n";
  2160. } elseif ($pb['col'][0] == 6) { // CMYKa
  2161. $s .= $this->SetAlpha(ord($pb['col'][5]) / 100, 'Normal', true, 'F') . "\n";
  2162. }
  2163. $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";
  2164. }
  2165. if (isset($pb['gradient']) && $pb['gradient']) {
  2166. if (isset($pb['clippath']) && $pb['clippath']) {
  2167. $s .= $pb['clippath'] . "\n";
  2168. }
  2169. $s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);
  2170. if (isset($pb['clippath']) && $pb['clippath']) {
  2171. $s .= 'Q' . "\n";
  2172. }
  2173. }
  2174. if (isset($pb['image_id']) && $pb['image_id']) { // Background pattern
  2175. $pb['y'] -= $adjustmenty;
  2176. $pb['h'] += $adjustmenty;
  2177. $n = count($this->patterns) + 1;
  2178. 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']);
  2179. $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']];
  2180. $x = $pb['x'] * Mpdf::SCALE;
  2181. $y = ($this->h - $pb['y']) * Mpdf::SCALE;
  2182. $w = $pb['w'] * Mpdf::SCALE;
  2183. $h = -$pb['h'] * Mpdf::SCALE;
  2184. // mPDF 5.7.3
  2185. if (($this->writingHTMLfooter || $this->writingHTMLheader) && (!isset($pb['clippath']) || $pb['clippath'] == '')) {
  2186. // Set clipping path
  2187. $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);
  2188. }
  2189. if (isset($pb['clippath']) && $pb['clippath']) {
  2190. $s .= $pb['clippath'] . "\n";
  2191. }
  2192. // mPDF 5.7.3
  2193. if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern
  2194. $iw = $pb['orig_w'] / Mpdf::SCALE;
  2195. $ih = $pb['orig_h'] / Mpdf::SCALE;
  2196. $w = $pb['w'];
  2197. $h = $pb['h'];
  2198. $x0 = $pb['x'];
  2199. $y0 = $pb['y'];
  2200. if (isset($pb['bpa']) && $pb['bpa']) {
  2201. $w = $pb['bpa']['w'];
  2202. $h = $pb['bpa']['h'];
  2203. $x0 = $pb['bpa']['x'];
  2204. $y0 = $pb['bpa']['y'];
  2205. } // At present 'bpa' (background page area) is not set for tablebackgrounds - only pagebackgrounds
  2206. // For now, just set it as:
  2207. else {
  2208. $pb['bpa'] = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h];
  2209. }
  2210. if (isset($pb['size']['w']) && $pb['size']['w']) {
  2211. $size = $pb['size'];
  2212. if ($size['w'] == 'contain') {
  2213. // 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.
  2214. // Same as resize==3
  2215. $ih = $ih * $pb['bpa']['w'] / $iw;
  2216. $iw = $pb['bpa']['w'];
  2217. if ($ih > $pb['bpa']['h']) {
  2218. $iw = $iw * $pb['bpa']['h'] / $ih;
  2219. $ih = $pb['bpa']['h'];
  2220. }
  2221. } elseif ($size['w'] == 'cover') {
  2222. // 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.
  2223. $ih = $ih * $pb['bpa']['w'] / $iw;
  2224. $iw = $pb['bpa']['w'];
  2225. if ($ih < $pb['bpa']['h']) {
  2226. $iw = $iw * $ih / $pb['bpa']['h'];
  2227. $ih = $pb['bpa']['h'];
  2228. }
  2229. } else {
  2230. if (NumericString::containsPercentChar($size['w'])) {
  2231. $size['w'] = NumericString::removePercentChar($size['w']);
  2232. $size['w'] /= 100;
  2233. $size['w'] = ($pb['bpa']['w'] * $size['w']);
  2234. }
  2235. if (NumericString::containsPercentChar($size['h'])) {
  2236. $size['h'] = NumericString::removePercentChar($size['h']);
  2237. $size['h'] /= 100;
  2238. $size['h'] = ($pb['bpa']['h'] * $size['h']);
  2239. }
  2240. if ($size['w'] == 'auto' && $size['h'] == 'auto') {
  2241. $iw = $iw;
  2242. $ih = $ih;
  2243. } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
  2244. $iw = $iw * $size['h'] / $ih;
  2245. $ih = $size['h'];
  2246. } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
  2247. $ih = $ih * $size['w'] / $iw;
  2248. $iw = $size['w'];
  2249. } else {
  2250. $iw = $size['w'];
  2251. $ih = $size['h'];
  2252. }
  2253. }
  2254. }
  2255. // Number to repeat
  2256. if (isset($pb['x_repeat']) && $pb['x_repeat']) {
  2257. $nx = ceil($pb['w'] / $iw) + 1;
  2258. } else {
  2259. $nx = 1;
  2260. }
  2261. if (isset($pb['y_repeat']) && $pb['y_repeat']) {
  2262. $ny = ceil($pb['h'] / $ih) + 1;
  2263. } else {
  2264. $ny = 1;
  2265. }
  2266. $x_pos = $pb['x_pos'];
  2267. if (NumericString::containsPercentChar($x_pos)) {
  2268. $x_pos = NumericString::removePercentChar($x_pos);
  2269. $x_pos /= 100;
  2270. $x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
  2271. }
  2272. $y_pos = $pb['y_pos'];
  2273. if (NumericString::containsPercentChar($y_pos)) {
  2274. $y_pos = NumericString::removePercentChar($y_pos);
  2275. $y_pos /= 100;
  2276. $y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
  2277. }
  2278. if ($nx > 1) {
  2279. while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {
  2280. $x_pos -= $iw;
  2281. }
  2282. }
  2283. if ($ny > 1) {
  2284. while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {
  2285. $y_pos -= $ih;
  2286. }
  2287. }
  2288. for ($xi = 0; $xi < $nx; $xi++) {
  2289. for ($yi = 0; $yi < $ny; $yi++) {
  2290. $x = $x0 + $x_pos + ($iw * $xi);
  2291. $y = $y0 + $y_pos + ($ih * $yi);
  2292. if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {
  2293. $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
  2294. } else {
  2295. $opac = '';
  2296. }
  2297. $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";
  2298. }
  2299. }
  2300. } else {
  2301. if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {
  2302. $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
  2303. } else {
  2304. $opac = '';
  2305. }
  2306. $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n";
  2307. }
  2308. if (isset($pb['clippath']) && $pb['clippath']) {
  2309. $s .= 'Q' . "\n";
  2310. }
  2311. }
  2312. }
  2313. }
  2314. /* -- END BACKGROUNDS -- */
  2315. return $s;
  2316. }
  2317. function BeginLayer($id)
  2318. {
  2319. if ($this->current_layer > 0) {
  2320. $this->EndLayer();
  2321. }
  2322. if ($id < 1) {
  2323. return false;
  2324. }
  2325. if (!isset($this->layers[$id])) {
  2326. $this->layers[$id] = ['name' => 'Layer ' . ($id)];
  2327. if (($this->PDFA || $this->PDFX)) {
  2328. $this->PDFAXwarnings[] = "Cannot use layers when using PDFA or PDFX";
  2329. return '';
  2330. } elseif (!$this->PDFA && !$this->PDFX) {
  2331. $this->pdf_version = '1.5';
  2332. }
  2333. }
  2334. $this->current_layer = $id;
  2335. $this->writer->write('/OCZ-index /ZI' . $id . ' BDC');
  2336. $this->pageoutput[$this->page] = [];
  2337. }
  2338. function EndLayer()
  2339. {
  2340. if ($this->current_layer > 0) {
  2341. $this->writer->write('EMCZ-index');
  2342. $this->current_layer = 0;
  2343. }
  2344. }
  2345. function AddPageByArray($a)
  2346. {
  2347. if (!is_array($a)) {
  2348. $a = [];
  2349. }
  2350. $orientation = (isset($a['orientation']) ? $a['orientation'] : '');
  2351. $condition = (isset($a['condition']) ? $a['condition'] : (isset($a['type']) ? $a['type'] : ''));
  2352. $resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');
  2353. $pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');
  2354. $suppress = (isset($a['suppress']) ? $a['suppress'] : '');
  2355. $mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));
  2356. $mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));
  2357. $mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));
  2358. $mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));
  2359. $mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));
  2360. $mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));
  2361. $ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));
  2362. $ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));
  2363. $ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));
  2364. $efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));
  2365. $ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));
  2366. $ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));
  2367. $ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));
  2368. $efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));
  2369. $pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));
  2370. $newformat = (isset($a['newformat']) ? $a['newformat'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));
  2371. $this->AddPage($orientation, $condition, $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);
  2372. }
  2373. // mPDF 6 pagebreaktype
  2374. function _preForcedPagebreak($pagebreaktype)
  2375. {
  2376. if ($pagebreaktype == 'cloneall') {
  2377. // Close any open block tags
  2378. $arr = [];
  2379. $ai = 0;
  2380. for ($b = $this->blklvl; $b > 0; $b--) {
  2381. $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
  2382. }
  2383. if ($this->blklvl == 0 && !empty($this->textbuffer)) { // Output previously buffered content
  2384. $this->printbuffer($this->textbuffer, 1);
  2385. $this->textbuffer = [];
  2386. }
  2387. } elseif ($pagebreaktype == 'clonebycss') {
  2388. // Close open block tags whilst box-decoration-break==clone
  2389. $arr = [];
  2390. $ai = 0;
  2391. for ($b = $this->blklvl; $b > 0; $b--) {
  2392. if (isset($this->blk[$b]['box_decoration_break']) && $this->blk[$b]['box_decoration_break'] == 'clone') {
  2393. $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
  2394. } else {
  2395. if ($b == $this->blklvl && !empty($this->textbuffer)) { // Output previously buffered content
  2396. $this->printbuffer($this->textbuffer, 1);
  2397. $this->textbuffer = [];
  2398. }
  2399. break;
  2400. }
  2401. }
  2402. } elseif (!empty($this->textbuffer)) { // Output previously buffered content
  2403. $this->printbuffer($this->textbuffer, 1);
  2404. $this->textbuffer = [];
  2405. }
  2406. }
  2407. // mPDF 6 pagebreaktype
  2408. function _postForcedPagebreak($pagebreaktype, $startpage, $save_blk, $save_blklvl)
  2409. {
  2410. if ($pagebreaktype == 'cloneall') {
  2411. $this->blk = [];
  2412. $this->blk[0] = $save_blk[0];
  2413. // Re-open block tags
  2414. $this->blklvl = 0;
  2415. $arr = [];
  2416. $i = 0;
  2417. for ($b = 1; $b <= $save_blklvl; $b++) {
  2418. $this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);
  2419. }
  2420. } elseif ($pagebreaktype == 'clonebycss') {
  2421. $this->blk = [];
  2422. $this->blk[0] = $save_blk[0];
  2423. // Don't re-open tags for lowest level elements - so need to do some adjustments
  2424. for ($b = 1; $b <= $this->blklvl; $b++) {
  2425. $this->blk[$b] = $save_blk[$b];
  2426. $this->blk[$b]['startpage'] = 0;
  2427. $this->blk[$b]['y0'] = $this->y; // ?? $this->tMargin
  2428. if (($this->page - $startpage) % 2) {
  2429. if (isset($this->blk[$b]['x0'])) {
  2430. $this->blk[$b]['x0'] += $this->MarginCorrection;
  2431. } else {
  2432. $this->blk[$b]['x0'] = $this->MarginCorrection;
  2433. }
  2434. }
  2435. // for Float DIV
  2436. $this->blk[$b]['marginCorrected'][$this->page] = true;
  2437. }
  2438. // Re-open block tags for any that have box_decoration_break==clone
  2439. $arr = [];
  2440. $i = 0;
  2441. for ($b = $this->blklvl + 1; $b <= $save_blklvl; $b++) {
  2442. if ($b < $this->blklvl) {
  2443. $this->lastblocklevelchange = -1;
  2444. }
  2445. $this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);
  2446. }
  2447. if ($this->blk[$this->blklvl]['box_decoration_break'] != 'clone') {
  2448. $this->lastblocklevelchange = -1;
  2449. }
  2450. } else {
  2451. $this->lastblocklevelchange = -1;
  2452. }
  2453. }
  2454. function AddPage(
  2455. $orientation = '',
  2456. $condition = '',
  2457. $resetpagenum = '',
  2458. $pagenumstyle = '',
  2459. $suppress = '',
  2460. $mgl = '',
  2461. $mgr = '',
  2462. $mgt = '',
  2463. $mgb = '',
  2464. $mgh = '',
  2465. $mgf = '',
  2466. $ohname = '',
  2467. $ehname = '',
  2468. $ofname = '',
  2469. $efname = '',
  2470. $ohvalue = 0,
  2471. $ehvalue = 0,
  2472. $ofvalue = 0,
  2473. $efvalue = 0,
  2474. $pagesel = '',
  2475. $newformat = ''
  2476. ) {
  2477. /* -- CSS-FLOAT -- */
  2478. // Float DIV
  2479. // Cannot do with columns on, or if any change in page orientation/margins etc.
  2480. // If next page already exists - i.e background /headers and footers already written
  2481. if ($this->state > 0 && $this->page < count($this->pages)) {
  2482. $bak_cml = $this->cMarginL;
  2483. $bak_cmr = $this->cMarginR;
  2484. $bak_dw = $this->divwidth;
  2485. // Paint Div Border if necessary
  2486. if ($this->blklvl > 0) {
  2487. $save_tr = $this->table_rotate; // *TABLES*
  2488. $this->table_rotate = 0; // *TABLES*
  2489. if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0']) {
  2490. $this->blk[$this->blklvl]['startpage'] ++;
  2491. }
  2492. if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table']) {
  2493. $toplvl = $this->blklvl;
  2494. } else {
  2495. $toplvl = $this->blklvl - 1;
  2496. }
  2497. $sy = $this->y;
  2498. for ($bl = 1; $bl <= $toplvl; $bl++) {
  2499. $this->PaintDivBB('pagebottom', 0, $bl);
  2500. }
  2501. $this->y = $sy;
  2502. $this->table_rotate = $save_tr; // *TABLES*
  2503. }
  2504. $s = $this->PrintPageBackgrounds();
  2505. // Writes after the marker so not overwritten later by page background etc.
  2506. $this->pages[$this->page] = preg_replace(
  2507. '/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/',
  2508. '\\1' . "\n" . $s . "\n",
  2509. $this->pages[$this->page]
  2510. );
  2511. $this->pageBackgrounds = [];
  2512. $family = $this->FontFamily;
  2513. $style = $this->FontStyle;
  2514. $size = $this->FontSizePt;
  2515. $lw = $this->LineWidth;
  2516. $dc = $this->DrawColor;
  2517. $fc = $this->FillColor;
  2518. $tc = $this->TextColor;
  2519. $cf = $this->ColorFlag;
  2520. $this->printfloatbuffer();
  2521. // Move to next page
  2522. $this->page++;
  2523. $this->ResetMargins();
  2524. $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
  2525. $this->x = $this->lMargin;
  2526. $this->y = $this->tMargin;
  2527. $this->FontFamily = '';
  2528. $this->writer->write('2 J');
  2529. $this->LineWidth = $lw;
  2530. $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));
  2531. if ($family) {
  2532. $this->SetFont($family, $style, $size, true, true);
  2533. }
  2534. $this->DrawColor = $dc;
  2535. if ($dc != $this->defDrawColor) {
  2536. $this->writer->write($dc);
  2537. }
  2538. $this->FillColor = $fc;
  2539. if ($fc != $this->defFillColor) {
  2540. $this->writer->write($fc);
  2541. }
  2542. $this->TextColor = $tc;
  2543. $this->ColorFlag = $cf;
  2544. for ($bl = 1; $bl <= $this->blklvl; $bl++) {
  2545. $this->blk[$bl]['y0'] = $this->y;
  2546. // Don't correct more than once for background DIV containing a Float
  2547. if (!isset($this->blk[$bl]['marginCorrected'][$this->page])) {
  2548. if (isset($this->blk[$bl]['x0'])) {
  2549. $this->blk[$bl]['x0'] += $this->MarginCorrection;
  2550. } else {
  2551. $this->blk[$bl]['x0'] = $this->MarginCorrection;
  2552. }
  2553. }
  2554. $this->blk[$bl]['marginCorrected'][$this->page] = true;
  2555. }
  2556. $this->cMarginL = $bak_cml;
  2557. $this->cMarginR = $bak_cmr;
  2558. $this->divwidth = $bak_dw;
  2559. return '';
  2560. }
  2561. /* -- END CSS-FLOAT -- */
  2562. // Start a new page
  2563. if ($this->state == 0) {
  2564. $this->Open();
  2565. }
  2566. $bak_cml = $this->cMarginL;
  2567. $bak_cmr = $this->cMarginR;
  2568. $bak_dw = $this->divwidth;
  2569. $bak_lh = $this->lineheight;
  2570. $orientation = substr(strtoupper($orientation), 0, 1);
  2571. $condition = strtoupper($condition);
  2572. if ($condition == 'E') { // only adds new page if needed to create an Even page
  2573. if (!$this->mirrorMargins || ($this->page) % 2 == 0) {
  2574. return false;
  2575. }
  2576. } elseif ($condition == 'O') { // only adds new page if needed to create an Odd page
  2577. if (!$this->mirrorMargins || ($this->page) % 2 == 1) {
  2578. return false;
  2579. }
  2580. } elseif ($condition == 'NEXT-EVEN') { // always adds at least one new page to create an Even page
  2581. if (!$this->mirrorMargins) {
  2582. $condition = '';
  2583. } else {
  2584. if ($pagesel) {
  2585. $pbch = $pagesel;
  2586. $pagesel = '';
  2587. } // *CSS-PAGE*
  2588. else {
  2589. $pbch = false;
  2590. } // *CSS-PAGE*
  2591. $this->AddPage($this->CurOrientation, 'O');
  2592. $this->extrapagebreak = true; // mPDF 6 pagebreaktype
  2593. if ($pbch) {
  2594. $pagesel = $pbch;
  2595. } // *CSS-PAGE*
  2596. $condition = '';
  2597. }
  2598. } elseif ($condition == 'NEXT-ODD') { // always adds at least one new page to create an Odd page
  2599. if (!$this->mirrorMargins) {
  2600. $condition = '';
  2601. } else {
  2602. if ($pagesel) {
  2603. $pbch = $pagesel;
  2604. $pagesel = '';
  2605. } // *CSS-PAGE*
  2606. else {
  2607. $pbch = false;
  2608. } // *CSS-PAGE*
  2609. $this->AddPage($this->CurOrientation, 'E');
  2610. $this->extrapagebreak = true; // mPDF 6 pagebreaktype
  2611. if ($pbch) {
  2612. $pagesel = $pbch;
  2613. } // *CSS-PAGE*
  2614. $condition = '';
  2615. }
  2616. }
  2617. if ($resetpagenum || $pagenumstyle || $suppress) {
  2618. $this->PageNumSubstitutions[] = ['from' => ($this->page + 1), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
  2619. }
  2620. $save_tr = $this->table_rotate; // *TABLES*
  2621. $this->table_rotate = 0; // *TABLES*
  2622. $save_kwt = $this->kwt;
  2623. $this->kwt = 0;
  2624. $save_layer = $this->current_layer;
  2625. $save_vis = $this->visibility;
  2626. if ($this->visibility != 'visible') {
  2627. $this->SetVisibility('visible');
  2628. }
  2629. $this->EndLayer();
  2630. // Paint Div Border if necessary
  2631. // PAINTS BACKGROUND COLOUR OR BORDERS for DIV - DISABLED FOR COLUMNS (cf. AcceptPageBreak) AT PRESENT in ->PaintDivBB
  2632. if (!$this->ColActive && $this->blklvl > 0) {
  2633. if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0'] && !$this->extrapagebreak) { // mPDF 6 pagebreaktype
  2634. if (isset($this->blk[$this->blklvl]['startpage'])) {
  2635. $this->blk[$this->blklvl]['startpage'] ++;
  2636. } else {
  2637. $this->blk[$this->blklvl]['startpage'] = 1;
  2638. }
  2639. }
  2640. if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table'] || $this->extrapagebreak) {
  2641. $toplvl = $this->blklvl;
  2642. } // mPDF 6 pagebreaktype
  2643. else {
  2644. $toplvl = $this->blklvl - 1;
  2645. }
  2646. $sy = $this->y;
  2647. for ($bl = 1; $bl <= $toplvl; $bl++) {
  2648. if (isset($this->blk[$bl]['z-index']) && $this->blk[$bl]['z-index'] > 0) {
  2649. $this->BeginLayer($this->blk[$bl]['z-index']);
  2650. }
  2651. if (isset($this->blk[$bl]['visibility']) && $this->blk[$bl]['visibility'] && $this->blk[$bl]['visibility'] != 'visible') {
  2652. $this->SetVisibility($this->blk[$bl]['visibility']);
  2653. }
  2654. $this->PaintDivBB('pagebottom', 0, $bl);
  2655. }
  2656. $this->y = $sy;
  2657. // RESET block y0 and x0 - see below
  2658. }
  2659. $this->extrapagebreak = false; // mPDF 6 pagebreaktype
  2660. if ($this->visibility != 'visible') {
  2661. $this->SetVisibility('visible');
  2662. }
  2663. $this->EndLayer();
  2664. // BODY Backgrounds
  2665. if ($this->page > 0) {
  2666. $s = '';
  2667. $s .= $this->PrintBodyBackgrounds();
  2668. $s .= $this->PrintPageBackgrounds();
  2669. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $s . "\n" . '\\1', $this->pages[$this->page]);
  2670. $this->pageBackgrounds = [];
  2671. }
  2672. $save_kt = $this->keep_block_together;
  2673. $this->keep_block_together = 0;
  2674. $save_cols = false;
  2675. /* -- COLUMNS -- */
  2676. if ($this->ColActive) {
  2677. $save_cols = true;
  2678. $save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off
  2679. $this->SetColumns(0);
  2680. }
  2681. /* -- END COLUMNS -- */
  2682. $family = $this->FontFamily;
  2683. $style = $this->FontStyle;
  2684. $size = $this->FontSizePt;
  2685. $this->ColumnAdjust = true; // enables column height adjustment for the page
  2686. $lw = $this->LineWidth;
  2687. $dc = $this->DrawColor;
  2688. $fc = $this->FillColor;
  2689. $tc = $this->TextColor;
  2690. $cf = $this->ColorFlag;
  2691. if ($this->page > 0) {
  2692. // Page footer
  2693. $this->InFooter = true;
  2694. $this->Reset();
  2695. $this->pageoutput[$this->page] = [];
  2696. $this->Footer();
  2697. // Close page
  2698. $this->_endpage();
  2699. }
  2700. // Start new page
  2701. $pageBeforeNewPage = $this->page;
  2702. $this->_beginpage($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);
  2703. $isNewPage = $pageBeforeNewPage !== $this->page;
  2704. if ($this->docTemplate) {
  2705. $currentReaderId = $this->currentReaderId;
  2706. $pagecount = $this->setSourceFile($this->docTemplate);
  2707. if (($this->page - $this->docTemplateStart) > $pagecount) {
  2708. if ($this->docTemplateContinue) {
  2709. if ($this->docTemplateContinue2pages && $pagecount >= 2 && (0 === $this->page % 2)) {
  2710. $tplIdx = $this->importPage(($pagecount - 1));
  2711. $this->useTemplate($tplIdx);
  2712. } else {
  2713. $tplIdx = $this->importPage($pagecount);
  2714. $this->useTemplate($tplIdx);
  2715. }
  2716. }
  2717. } else {
  2718. $tplIdx = $this->importPage(($this->page - $this->docTemplateStart));
  2719. $this->useTemplate($tplIdx);
  2720. }
  2721. $this->currentReaderId = $currentReaderId;
  2722. }
  2723. if ($this->pageTemplate) {
  2724. $this->useTemplate($this->pageTemplate);
  2725. }
  2726. // Only add the headers if it's a new page
  2727. if ($isNewPage) {
  2728. // Tiling Patterns
  2729. $this->writer->write('___PAGE___START' . $this->uniqstr);
  2730. $this->writer->write('___BACKGROUND___PATTERNS' . $this->uniqstr);
  2731. $this->writer->write('___HEADER___MARKER' . $this->uniqstr);
  2732. }
  2733. $this->pageBackgrounds = [];
  2734. // Set line cap style to square
  2735. $this->SetLineCap(2);
  2736. // Set line width
  2737. $this->LineWidth = $lw;
  2738. $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));
  2739. // Set font
  2740. if ($family) {
  2741. $this->SetFont($family, $style, $size, true, true); // forces write
  2742. }
  2743. // Set colors
  2744. $this->DrawColor = $dc;
  2745. if ($dc != $this->defDrawColor) {
  2746. $this->writer->write($dc);
  2747. }
  2748. $this->FillColor = $fc;
  2749. if ($fc != $this->defFillColor) {
  2750. $this->writer->write($fc);
  2751. }
  2752. $this->TextColor = $tc;
  2753. $this->ColorFlag = $cf;
  2754. // Page header
  2755. $this->Header();
  2756. // Restore line width
  2757. if ($this->LineWidth != $lw) {
  2758. $this->LineWidth = $lw;
  2759. $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));
  2760. }
  2761. // Restore font
  2762. if ($family) {
  2763. $this->SetFont($family, $style, $size, true, true); // forces write
  2764. }
  2765. // Restore colors
  2766. if ($this->DrawColor != $dc) {
  2767. $this->DrawColor = $dc;
  2768. $this->writer->write($dc);
  2769. }
  2770. if ($this->FillColor != $fc) {
  2771. $this->FillColor = $fc;
  2772. $this->writer->write($fc);
  2773. }
  2774. $this->TextColor = $tc;
  2775. $this->ColorFlag = $cf;
  2776. $this->InFooter = false;
  2777. if ($save_layer > 0) {
  2778. $this->BeginLayer($save_layer);
  2779. }
  2780. if ($save_vis != 'visible') {
  2781. $this->SetVisibility($save_vis);
  2782. }
  2783. /* -- COLUMNS -- */
  2784. if ($save_cols) {
  2785. // Restore columns
  2786. $this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);
  2787. }
  2788. if ($this->ColActive) {
  2789. $this->SetCol(0);
  2790. }
  2791. /* -- END COLUMNS -- */
  2792. // RESET BLOCK BORDER TOP
  2793. if (!$this->ColActive) {
  2794. for ($bl = 1; $bl <= $this->blklvl; $bl++) {
  2795. $this->blk[$bl]['y0'] = $this->y;
  2796. if (isset($this->blk[$bl]['x0'])) {
  2797. $this->blk[$bl]['x0'] += $this->MarginCorrection;
  2798. } else {
  2799. $this->blk[$bl]['x0'] = $this->MarginCorrection;
  2800. }
  2801. // Added mPDF 3.0 Float DIV
  2802. $this->blk[$bl]['marginCorrected'][$this->page] = true;
  2803. }
  2804. }
  2805. $this->table_rotate = $save_tr; // *TABLES*
  2806. $this->kwt = $save_kwt;
  2807. $this->keep_block_together = $save_kt;
  2808. $this->cMarginL = $bak_cml;
  2809. $this->cMarginR = $bak_cmr;
  2810. $this->divwidth = $bak_dw;
  2811. $this->lineheight = $bak_lh;
  2812. }
  2813. /**
  2814. * Get current page number
  2815. *
  2816. * @return int
  2817. */
  2818. function PageNo()
  2819. {
  2820. return $this->page;
  2821. }
  2822. function AddSpotColorsFromFile($file)
  2823. {
  2824. $colors = @file($file);
  2825. if (!$colors) {
  2826. throw new \Mpdf\MpdfException("Cannot load spot colors file - " . $file);
  2827. }
  2828. foreach ($colors as $sc) {
  2829. list($name, $c, $m, $y, $k) = preg_split("/\t/", $sc);
  2830. $c = intval($c);
  2831. $m = intval($m);
  2832. $y = intval($y);
  2833. $k = intval($k);
  2834. $this->AddSpotColor($name, $c, $m, $y, $k);
  2835. }
  2836. }
  2837. function AddSpotColor($name, $c, $m, $y, $k)
  2838. {
  2839. $name = strtoupper(trim($name));
  2840. if (!isset($this->spotColors[$name])) {
  2841. $i = count($this->spotColors) + 1;
  2842. $this->spotColors[$name] = ['i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k];
  2843. $this->spotColorIDs[$i] = $name;
  2844. }
  2845. }
  2846. function SetColor($col, $type = '')
  2847. {
  2848. $out = '';
  2849. if (!$col) {
  2850. return '';
  2851. } // mPDF 6
  2852. if ($col[0] == 3 || $col[0] == 5) { // RGB / RGBa
  2853. $out = sprintf('%.3F %.3F %.3F rg', ord($col[1]) / 255, ord($col[2]) / 255, ord($col[3]) / 255);
  2854. } elseif ($col[0] == 1) { // GRAYSCALE
  2855. $out = sprintf('%.3F g', ord($col[1]) / 255);
  2856. } elseif ($col[0] == 2) { // SPOT COLOR
  2857. $out = sprintf('/CS%d cs %.3F scn', ord($col[1]), ord($col[2]) / 100);
  2858. } elseif ($col[0] == 4 || $col[0] == 6) { // CMYK / CMYKa
  2859. $out = sprintf('%.3F %.3F %.3F %.3F k', ord($col[1]) / 100, ord($col[2]) / 100, ord($col[3]) / 100, ord($col[4]) / 100);
  2860. }
  2861. if ($type == 'Draw') {
  2862. $out = strtoupper($out);
  2863. } // e.g. rg => RG
  2864. elseif ($type == 'CodeOnly') {
  2865. $out = preg_replace('/\s(rg|g|k)/', '', $out);
  2866. }
  2867. return $out;
  2868. }
  2869. function SetDColor($col, $return = false)
  2870. {
  2871. $out = $this->SetColor($col, 'Draw');
  2872. if ($return) {
  2873. return $out;
  2874. }
  2875. if ($out == '') {
  2876. return '';
  2877. }
  2878. $this->DrawColor = $out;
  2879. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['DrawColor']) && $this->pageoutput[$this->page]['DrawColor'] != $this->DrawColor) || !isset($this->pageoutput[$this->page]['DrawColor']))) {
  2880. $this->writer->write($this->DrawColor);
  2881. }
  2882. $this->pageoutput[$this->page]['DrawColor'] = $this->DrawColor;
  2883. }
  2884. function SetFColor($col, $return = false)
  2885. {
  2886. $out = $this->SetColor($col, 'Fill');
  2887. if ($return) {
  2888. return $out;
  2889. }
  2890. if ($out == '') {
  2891. return '';
  2892. }
  2893. $this->FillColor = $out;
  2894. $this->ColorFlag = ($out != $this->TextColor);
  2895. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor']))) {
  2896. $this->writer->write($this->FillColor);
  2897. }
  2898. $this->pageoutput[$this->page]['FillColor'] = $this->FillColor;
  2899. }
  2900. function SetTColor($col, $return = false)
  2901. {
  2902. $out = $this->SetColor($col, 'Text');
  2903. if ($return) {
  2904. return $out;
  2905. }
  2906. if ($out == '') {
  2907. return '';
  2908. }
  2909. $this->TextColor = $out;
  2910. $this->ColorFlag = ($this->FillColor != $out);
  2911. }
  2912. function SetDrawColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
  2913. {
  2914. // Set color for all stroking operations
  2915. $col = [];
  2916. if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
  2917. $col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
  2918. } elseif ($col4 == -1) {
  2919. $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
  2920. } else {
  2921. $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
  2922. }
  2923. $out = $this->SetDColor($col, $return);
  2924. return $out;
  2925. }
  2926. function SetFillColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
  2927. {
  2928. // Set color for all filling operations
  2929. $col = [];
  2930. if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
  2931. $col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
  2932. } elseif ($col4 == -1) {
  2933. $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
  2934. } else {
  2935. $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
  2936. }
  2937. $out = $this->SetFColor($col, $return);
  2938. return $out;
  2939. }
  2940. function SetTextColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
  2941. {
  2942. // Set color for text
  2943. $col = [];
  2944. if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
  2945. $col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
  2946. } elseif ($col4 == -1) {
  2947. $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
  2948. } else {
  2949. $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
  2950. }
  2951. $out = $this->SetTColor($col, $return);
  2952. return $out;
  2953. }
  2954. function _getCharWidth(&$cw, $u, $isdef = true)
  2955. {
  2956. $w = 0;
  2957. if ($u == 0) {
  2958. $w = false;
  2959. } elseif (isset($cw[$u * 2 + 1])) {
  2960. $w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);
  2961. }
  2962. if ($w == 65535) {
  2963. return 0;
  2964. } elseif ($w) {
  2965. return $w;
  2966. } elseif ($isdef) {
  2967. return false;
  2968. } else {
  2969. return 0;
  2970. }
  2971. }
  2972. function _charDefined(&$cw, $u)
  2973. {
  2974. $w = 0;
  2975. if ($u == 0) {
  2976. return false;
  2977. }
  2978. if (isset($cw[$u * 2 + 1])) {
  2979. $w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);
  2980. }
  2981. return (bool) $w;
  2982. }
  2983. function GetCharWidthCore($c)
  2984. {
  2985. // Get width of a single character in the current Core font
  2986. $c = (string) $c;
  2987. $w = 0;
  2988. // Soft Hyphens chr(173)
  2989. if ($c == chr(173) && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  2990. return 0;
  2991. } elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($c)])) { // mPDF 5.7.1
  2992. $charw = $this->CurrentFont['cw'][chr($this->upperCase[ord($c)])];
  2993. if ($charw !== false) {
  2994. $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
  2995. $w+=$charw;
  2996. }
  2997. } elseif (isset($this->CurrentFont['cw'][$c])) {
  2998. $w += $this->CurrentFont['cw'][$c];
  2999. } elseif (isset($this->CurrentFont['cw'][ord($c)])) {
  3000. $w += $this->CurrentFont['cw'][ord($c)];
  3001. }
  3002. $w *= ($this->FontSize / 1000);
  3003. if ($this->minwSpacing || $this->fixedlSpacing) {
  3004. if ($c == ' ') {
  3005. $nb_spaces = 1;
  3006. } else {
  3007. $nb_spaces = 0;
  3008. }
  3009. $w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);
  3010. }
  3011. return ($w);
  3012. }
  3013. function GetCharWidthNonCore($c, $addSubset = true)
  3014. {
  3015. // Get width of a single character in the current Non-Core font
  3016. $c = (string) $c;
  3017. $w = 0;
  3018. $unicode = $this->UTF8StringToArray($c, $addSubset);
  3019. $char = $unicode[0];
  3020. /* -- CJK-FONTS -- */
  3021. if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts
  3022. if ($char == 173) {
  3023. return 0;
  3024. } // Soft Hyphens
  3025. elseif (isset($this->CurrentFont['cw'][$char])) {
  3026. $w+=$this->CurrentFont['cw'][$char];
  3027. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3028. $w += $this->CurrentFont['MissingWidth'];
  3029. } else {
  3030. $w += 500;
  3031. }
  3032. } else {
  3033. /* -- END CJK-FONTS -- */
  3034. if ($char == 173) {
  3035. return 0;
  3036. } // Soft Hyphens
  3037. elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) { // mPDF 5.7.1
  3038. $charw = $this->_getCharWidth($this->CurrentFont['cw'], $this->upperCase[$char]);
  3039. if ($charw !== false) {
  3040. $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
  3041. $w+=$charw;
  3042. } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
  3043. $w += $this->CurrentFont['desc']['MissingWidth'];
  3044. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3045. $w += $this->CurrentFont['MissingWidth'];
  3046. } else {
  3047. $w += 500;
  3048. }
  3049. } else {
  3050. $charw = $this->_getCharWidth($this->CurrentFont['cw'], $char);
  3051. if ($charw !== false) {
  3052. $w+=$charw;
  3053. } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
  3054. $w += $this->CurrentFont['desc']['MissingWidth'];
  3055. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3056. $w += $this->CurrentFont['MissingWidth'];
  3057. } else {
  3058. $w += 500;
  3059. }
  3060. }
  3061. } // *CJK-FONTS*
  3062. $w *= ($this->FontSize / 1000);
  3063. if ($this->minwSpacing || $this->fixedlSpacing) {
  3064. if ($c == ' ') {
  3065. $nb_spaces = 1;
  3066. } else {
  3067. $nb_spaces = 0;
  3068. }
  3069. $w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);
  3070. }
  3071. return ($w);
  3072. }
  3073. function GetCharWidth($c, $addSubset = true)
  3074. {
  3075. if (!$this->usingCoreFont) {
  3076. return $this->GetCharWidthNonCore($c, $addSubset);
  3077. } else {
  3078. return $this->GetCharWidthCore($c);
  3079. }
  3080. }
  3081. function GetStringWidth($s, $addSubset = true, $OTLdata = false, $textvar = 0, $includeKashida = false)
  3082. {
  3083. // mPDF 5.7.1
  3084. // Get width of a string in the current font
  3085. $s = (string) $s;
  3086. $cw = &$this->CurrentFont['cw'];
  3087. $w = 0;
  3088. $kerning = 0;
  3089. $lastchar = 0;
  3090. $nb_carac = 0;
  3091. $nb_spaces = 0;
  3092. $kashida = 0;
  3093. // mPDF ITERATION
  3094. if ($this->iterationCounter) {
  3095. $s = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $s);
  3096. }
  3097. if (!$this->usingCoreFont) {
  3098. $discards = substr_count($s, "\xc2\xad"); // mPDF 6 soft hyphens [U+00AD]
  3099. $unicode = $this->UTF8StringToArray($s, $addSubset);
  3100. if ($this->minwSpacing || $this->fixedlSpacing) {
  3101. $nb_spaces = mb_substr_count($s, ' ', $this->mb_enc);
  3102. $nb_carac = count($unicode) - $discards; // mPDF 6
  3103. // mPDF 5.7.1
  3104. // Use GPOS OTL
  3105. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  3106. if (isset($OTLdata['group']) && $OTLdata['group']) {
  3107. $nb_carac -= substr_count($OTLdata['group'], 'M');
  3108. }
  3109. }
  3110. }
  3111. /* -- CJK-FONTS -- */
  3112. if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts
  3113. foreach ($unicode as $char) {
  3114. if ($char == 0x00AD) {
  3115. continue;
  3116. } // mPDF 6 soft hyphens [U+00AD]
  3117. if (isset($cw[$char])) {
  3118. $w+=$cw[$char];
  3119. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3120. $w += $this->CurrentFont['MissingWidth'];
  3121. } else {
  3122. $w += 500;
  3123. }
  3124. }
  3125. } else {
  3126. /* -- END CJK-FONTS -- */
  3127. foreach ($unicode as $i => $char) {
  3128. if ($char == 0x00AD) {
  3129. continue;
  3130. } // mPDF 6 soft hyphens [U+00AD]
  3131. if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) {
  3132. $charw = $this->_getCharWidth($cw, $this->upperCase[$char]);
  3133. if ($charw !== false) {
  3134. $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
  3135. $w+=$charw;
  3136. } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
  3137. $w += $this->CurrentFont['desc']['MissingWidth'];
  3138. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3139. $w += $this->CurrentFont['MissingWidth'];
  3140. } else {
  3141. $w += 500;
  3142. }
  3143. } else {
  3144. $charw = $this->_getCharWidth($cw, $char);
  3145. if ($charw !== false) {
  3146. $w+=$charw;
  3147. } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
  3148. $w += $this->CurrentFont['desc']['MissingWidth'];
  3149. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3150. $w += $this->CurrentFont['MissingWidth'];
  3151. } else {
  3152. $w += 500;
  3153. }
  3154. // mPDF 5.7.1
  3155. // Use GPOS OTL
  3156. // ...GetStringWidth...
  3157. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata)) {
  3158. if (isset($OTLdata['GPOSinfo'][$i]['wDir']) && $OTLdata['GPOSinfo'][$i]['wDir'] == 'RTL') {
  3159. if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceR']) && $OTLdata['GPOSinfo'][$i]['XAdvanceR']) {
  3160. $w += $OTLdata['GPOSinfo'][$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
  3161. }
  3162. } else {
  3163. if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceL']) && $OTLdata['GPOSinfo'][$i]['XAdvanceL']) {
  3164. $w += $OTLdata['GPOSinfo'][$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
  3165. }
  3166. }
  3167. // Kashida from GPOS
  3168. // Kashida is set as an absolute length value (already set as a proportion based on useKashida %)
  3169. if ($includeKashida && isset($OTLdata['GPOSinfo'][$i]['kashida_space']) && $OTLdata['GPOSinfo'][$i]['kashida_space']) {
  3170. $kashida += $OTLdata['GPOSinfo'][$i]['kashida_space'];
  3171. }
  3172. }
  3173. if (($textvar & TextVars::FC_KERNING) && $lastchar) {
  3174. if (isset($this->CurrentFont['kerninfo'][$lastchar][$char])) {
  3175. $kerning += $this->CurrentFont['kerninfo'][$lastchar][$char];
  3176. }
  3177. }
  3178. $lastchar = $char;
  3179. }
  3180. }
  3181. } // *CJK-FONTS*
  3182. } else {
  3183. if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  3184. $s = str_replace(chr(173), '', $s);
  3185. }
  3186. $nb_carac = $l = strlen($s);
  3187. if ($this->minwSpacing || $this->fixedlSpacing) {
  3188. $nb_spaces = substr_count($s, ' ');
  3189. }
  3190. for ($i = 0; $i < $l; $i++) {
  3191. if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($s[$i])])) { // mPDF 5.7.1
  3192. $charw = $cw[chr($this->upperCase[ord($s[$i])])];
  3193. if ($charw !== false) {
  3194. $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
  3195. $w+=$charw;
  3196. }
  3197. } elseif (isset($cw[$s[$i]])) {
  3198. $w += $cw[$s[$i]];
  3199. } elseif (isset($cw[ord($s[$i])])) {
  3200. $w += $cw[ord($s[$i])];
  3201. }
  3202. if (($textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
  3203. if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]])) {
  3204. $kerning += $this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]];
  3205. }
  3206. }
  3207. }
  3208. }
  3209. unset($cw);
  3210. if ($textvar & TextVars::FC_KERNING) {
  3211. $w += $kerning;
  3212. } // mPDF 5.7.1
  3213. $w *= ($this->FontSize / 1000);
  3214. $w += (($nb_carac + $nb_spaces) * $this->fixedlSpacing) + ($nb_spaces * $this->minwSpacing);
  3215. $w += $kashida / Mpdf::SCALE;
  3216. return ($w);
  3217. }
  3218. function SetLineWidth($width)
  3219. {
  3220. // Set line width
  3221. $this->LineWidth = $width;
  3222. $lwout = (sprintf('%.3F w', $width * Mpdf::SCALE));
  3223. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineWidth']) && $this->pageoutput[$this->page]['LineWidth'] != $lwout) || !isset($this->pageoutput[$this->page]['LineWidth']))) {
  3224. $this->writer->write($lwout);
  3225. }
  3226. $this->pageoutput[$this->page]['LineWidth'] = $lwout;
  3227. }
  3228. function Line($x1, $y1, $x2, $y2)
  3229. {
  3230. // Draw a line
  3231. $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));
  3232. }
  3233. function Arrow($x1, $y1, $x2, $y2, $headsize = 3, $fill = 'B', $angle = 25)
  3234. {
  3235. // F == fill // S == stroke // B == stroke and fill
  3236. // angle = splay of arrowhead - 1 - 89 degrees
  3237. if ($fill == 'F') {
  3238. $fill = 'f';
  3239. } elseif ($fill == 'FD' or $fill == 'DF' or $fill == 'B') {
  3240. $fill = 'B';
  3241. } else {
  3242. $fill = 'S';
  3243. }
  3244. $a = atan2(($y2 - $y1), ($x2 - $x1));
  3245. $b = $a + deg2rad($angle);
  3246. $c = $a - deg2rad($angle);
  3247. $x3 = $x2 - ($headsize * cos($b));
  3248. $y3 = $this->h - ($y2 - ($headsize * sin($b)));
  3249. $x4 = $x2 - ($headsize * cos($c));
  3250. $y4 = $this->h - ($y2 - ($headsize * sin($c)));
  3251. $x5 = $x3 - ($x3 - $x4) / 2; // mid point of base of arrowhead - to join arrow line to
  3252. $y5 = $y3 - ($y3 - $y4) / 2;
  3253. $s = '';
  3254. $s .= sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE);
  3255. $this->writer->write($s);
  3256. $s = '';
  3257. $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);
  3258. $s .= $fill;
  3259. $this->writer->write($s);
  3260. }
  3261. function Rect($x, $y, $w, $h, $style = '')
  3262. {
  3263. // Draw a rectangle
  3264. if ($style == 'F') {
  3265. $op = 'f';
  3266. } elseif ($style == 'FD' or $style == 'DF') {
  3267. $op = 'B';
  3268. } else {
  3269. $op = 'S';
  3270. }
  3271. $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));
  3272. }
  3273. function AddFontDirectory($directory)
  3274. {
  3275. $this->fontDir[] = $directory;
  3276. $this->fontFileFinder->setDirectories($this->fontDir);
  3277. }
  3278. function AddFont($family, $style = '')
  3279. {
  3280. if (empty($family)) {
  3281. return;
  3282. }
  3283. $family = strtolower($family);
  3284. $style = strtoupper($style);
  3285. $style = str_replace('U', '', $style);
  3286. if ($style == 'IB') {
  3287. $style = 'BI';
  3288. }
  3289. $fontkey = $family . $style;
  3290. // check if the font has been already added
  3291. if (isset($this->fonts[$fontkey])) {
  3292. return;
  3293. }
  3294. /* -- CJK-FONTS -- */
  3295. if (in_array($family, $this->available_CJK_fonts)) {
  3296. if (empty($this->Big5_widths)) {
  3297. require __DIR__ . '/../data/CJKdata.php';
  3298. }
  3299. $this->AddCJKFont($family); // don't need to add style
  3300. return;
  3301. }
  3302. /* -- END CJK-FONTS -- */
  3303. if ($this->usingCoreFont) {
  3304. throw new \Mpdf\MpdfException("mPDF Error - problem with Font management");
  3305. }
  3306. $stylekey = $style;
  3307. if (!$style) {
  3308. $stylekey = 'R';
  3309. }
  3310. if (!isset($this->fontdata[$family][$stylekey]) || !$this->fontdata[$family][$stylekey]) {
  3311. throw new \Mpdf\MpdfException(sprintf('Font "%s%s%s" is not supported', $family, $style ? ' - ' : '', $style));
  3312. }
  3313. /* Setup defaults */
  3314. $font = [
  3315. 'name' => '',
  3316. 'type' => '',
  3317. 'desc' => '',
  3318. 'panose' => '',
  3319. 'unitsPerEm' => '',
  3320. 'up' => '',
  3321. 'ut' => '',
  3322. 'strs' => '',
  3323. 'strp' => '',
  3324. 'sip' => false,
  3325. 'smp' => false,
  3326. 'useOTL' => 0,
  3327. 'fontmetrics' => '',
  3328. 'haskerninfo' => false,
  3329. 'haskernGPOS' => false,
  3330. 'hassmallcapsGSUB' => false,
  3331. 'BMPselected' => false,
  3332. 'GSUBScriptLang' => [],
  3333. 'GSUBFeatures' => [],
  3334. 'GSUBLookups' => [],
  3335. 'GPOSScriptLang' => [],
  3336. 'GPOSFeatures' => [],
  3337. 'GPOSLookups' => [],
  3338. 'rtlPUAstr' => '',
  3339. ];
  3340. $fontCacheFilename = $fontkey . '.mtx.json';
  3341. if ($this->fontCache->jsonHas($fontCacheFilename)) {
  3342. $font = $this->fontCache->jsonLoad($fontCacheFilename);
  3343. }
  3344. $ttffile = $this->fontFileFinder->findFontFile($this->fontdata[$family][$stylekey]);
  3345. $ttfstat = stat($ttffile);
  3346. $TTCfontID = isset($this->fontdata[$family]['TTCfontID'][$stylekey]) ? isset($this->fontdata[$family]['TTCfontID'][$stylekey]) : 0;
  3347. $fontUseOTL = isset($this->fontdata[$family]['useOTL']) ? $this->fontdata[$family]['useOTL'] : false;
  3348. $BMPonly = in_array($family, $this->BMPonly) ? true : false;
  3349. $regenerate = false;
  3350. if ($BMPonly && !$font['BMPselected']) {
  3351. $regenerate = true;
  3352. } elseif (!$BMPonly && $font['BMPselected']) {
  3353. $regenerate = true;
  3354. }
  3355. if ($fontUseOTL && $font['useOTL'] != $fontUseOTL) {
  3356. $regenerate = true;
  3357. $font['useOTL'] = $fontUseOTL;
  3358. } elseif (!$fontUseOTL && $font['useOTL']) {
  3359. $regenerate = true;
  3360. $font['useOTL'] = 0;
  3361. }
  3362. if ($this->fontDescriptor != $font['fontmetrics']) {
  3363. $regenerate = true;
  3364. } // mPDF 6
  3365. if (empty($font['name']) || $font['originalsize'] != $ttfstat['size'] || $regenerate) {
  3366. $generator = new MetricsGenerator($this->fontCache, $this->fontDescriptor);
  3367. $generator->generateMetrics(
  3368. $ttffile,
  3369. $ttfstat,
  3370. $fontkey,
  3371. $TTCfontID,
  3372. $this->debugfonts,
  3373. $BMPonly,
  3374. $font['useOTL'],
  3375. $fontUseOTL
  3376. );
  3377. $font = $this->fontCache->jsonLoad($fontCacheFilename);
  3378. $cw = $this->fontCache->load($fontkey . '.cw.dat');
  3379. $glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat');
  3380. } else {
  3381. if ($this->fontCache->has($fontkey . '.cw.dat')) {
  3382. $cw = $this->fontCache->load($fontkey . '.cw.dat');
  3383. }
  3384. if ($this->fontCache->has($fontkey . '.gid.dat')) {
  3385. $glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat');
  3386. }
  3387. }
  3388. if (isset($this->fontdata[$family]['sip-ext']) && $this->fontdata[$family]['sip-ext']) {
  3389. $sipext = $this->fontdata[$family]['sip-ext'];
  3390. } else {
  3391. $sipext = '';
  3392. }
  3393. // Override with values from config_font.php
  3394. if (isset($this->fontdata[$family]['Ascent']) && $this->fontdata[$family]['Ascent']) {
  3395. $desc['Ascent'] = $this->fontdata[$family]['Ascent'];
  3396. }
  3397. if (isset($this->fontdata[$family]['Descent']) && $this->fontdata[$family]['Descent']) {
  3398. $desc['Descent'] = $this->fontdata[$family]['Descent'];
  3399. }
  3400. if (isset($this->fontdata[$family]['Leading']) && $this->fontdata[$family]['Leading']) {
  3401. $desc['Leading'] = $this->fontdata[$family]['Leading'];
  3402. }
  3403. $i = count($this->fonts) + $this->extraFontSubsets + 1;
  3404. $this->fonts[$fontkey] = [
  3405. 'i' => $i,
  3406. 'name' => $font['name'],
  3407. 'type' => $font['type'],
  3408. 'desc' => $font['desc'],
  3409. 'panose' => $font['panose'],
  3410. 'unitsPerEm' => $font['unitsPerEm'],
  3411. 'up' => $font['up'],
  3412. 'ut' => $font['ut'],
  3413. 'strs' => $font['strs'],
  3414. 'strp' => $font['strp'],
  3415. 'cw' => $cw,
  3416. 'ttffile' => $ttffile,
  3417. 'fontkey' => $fontkey,
  3418. 'used' => false,
  3419. 'sip' => $font['sip'],
  3420. 'sipext' => $sipext,
  3421. 'smp' => $font['smp'],
  3422. 'TTCfontID' => $TTCfontID,
  3423. 'useOTL' => $fontUseOTL,
  3424. 'useKashida' => (isset($this->fontdata[$family]['useKashida']) ? $this->fontdata[$family]['useKashida'] : false),
  3425. 'GSUBScriptLang' => $font['GSUBScriptLang'],
  3426. 'GSUBFeatures' => $font['GSUBFeatures'],
  3427. 'GSUBLookups' => $font['GSUBLookups'],
  3428. 'GPOSScriptLang' => $font['GPOSScriptLang'],
  3429. 'GPOSFeatures' => $font['GPOSFeatures'],
  3430. 'GPOSLookups' => $font['GPOSLookups'],
  3431. 'rtlPUAstr' => $font['rtlPUAstr'],
  3432. 'glyphIDtoUni' => $glyphIDtoUni,
  3433. 'haskerninfo' => $font['haskerninfo'],
  3434. 'haskernGPOS' => $font['haskernGPOS'],
  3435. 'hassmallcapsGSUB' => $font['hassmallcapsGSUB'],
  3436. ];
  3437. if (!$font['sip'] && !$font['smp']) {
  3438. $subsetRange = range(32, 127);
  3439. $this->fonts[$fontkey]['subset'] = array_combine($subsetRange, $subsetRange);
  3440. } else {
  3441. $this->fonts[$fontkey]['subsets'] = [0 => range(0, 127)];
  3442. $this->fonts[$fontkey]['subsetfontids'] = [$i];
  3443. }
  3444. if ($font['haskerninfo']) {
  3445. $this->fonts[$fontkey]['kerninfo'] = $font['kerninfo'];
  3446. }
  3447. $this->FontFiles[$fontkey] = [
  3448. 'length1' => $font['originalsize'],
  3449. 'type' => 'TTF',
  3450. 'ttffile' => $ttffile,
  3451. 'sip' => $font['sip'],
  3452. 'smp' => $font['smp'],
  3453. ];
  3454. unset($cw);
  3455. }
  3456. function SetFont($family, $style = '', $size = 0, $write = true, $forcewrite = false)
  3457. {
  3458. $family = strtolower($family);
  3459. if (!$this->onlyCoreFonts) {
  3460. if ($family == 'sans' || $family == 'sans-serif') {
  3461. $family = $this->sans_fonts[0];
  3462. }
  3463. if ($family == 'serif') {
  3464. $family = $this->serif_fonts[0];
  3465. }
  3466. if ($family == 'mono' || $family == 'monospace') {
  3467. $family = $this->mono_fonts[0];
  3468. }
  3469. }
  3470. if (isset($this->fonttrans[$family]) && $this->fonttrans[$family]) {
  3471. $family = $this->fonttrans[$family];
  3472. }
  3473. if ($family == '') {
  3474. if ($this->FontFamily) {
  3475. $family = $this->FontFamily;
  3476. } elseif ($this->default_font) {
  3477. $family = $this->default_font;
  3478. } else {
  3479. throw new \Mpdf\MpdfException("No font or default font set!");
  3480. }
  3481. }
  3482. $this->ReqFontStyle = $style; // required or requested style - used later for artificial bold/italic
  3483. if (($family == 'csymbol') || ($family == 'czapfdingbats') || ($family == 'ctimes') || ($family == 'ccourier') || ($family == 'chelvetica')) {
  3484. if ($this->PDFA || $this->PDFX) {
  3485. if ($family == 'csymbol' || $family == 'czapfdingbats') {
  3486. throw new \Mpdf\MpdfException("Symbol and Zapfdingbats cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a).");
  3487. }
  3488. if ($family == 'ctimes' || $family == 'ccourier' || $family == 'chelvetica') {
  3489. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  3490. $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.)";
  3491. }
  3492. if ($family == 'chelvetica') {
  3493. $family = 'sans';
  3494. }
  3495. if ($family == 'ctimes') {
  3496. $family = 'serif';
  3497. }
  3498. if ($family == 'ccourier') {
  3499. $family = 'mono';
  3500. }
  3501. }
  3502. $this->usingCoreFont = false;
  3503. } else {
  3504. $this->usingCoreFont = true;
  3505. }
  3506. if ($family == 'csymbol' || $family == 'czapfdingbats') {
  3507. $style = '';
  3508. }
  3509. } else {
  3510. $this->usingCoreFont = false;
  3511. }
  3512. // mPDF 5.7.1
  3513. if ($style) {
  3514. $style = strtoupper($style);
  3515. if ($style == 'IB') {
  3516. $style = 'BI';
  3517. }
  3518. }
  3519. if (!$size) {
  3520. $size = $this->FontSizePt;
  3521. }
  3522. $fontkey = $family . $style;
  3523. $stylekey = $style;
  3524. if (!$stylekey) {
  3525. $stylekey = "R";
  3526. }
  3527. if (!$this->onlyCoreFonts && !$this->usingCoreFont) {
  3528. if (!isset($this->fonts[$fontkey]) || count($this->default_available_fonts) != count($this->available_unifonts)) { // not already added
  3529. /* -- CJK-FONTS -- */
  3530. if (in_array($fontkey, $this->available_CJK_fonts)) {
  3531. if (!isset($this->fonts[$fontkey])) { // already added
  3532. if (empty($this->Big5_widths)) {
  3533. require __DIR__ . '/../data/CJKdata.php';
  3534. }
  3535. $this->AddCJKFont($family); // don't need to add style
  3536. }
  3537. } else { // Test to see if requested font/style is available - or substitute /* -- END CJK-FONTS -- */
  3538. if (!in_array($fontkey, $this->available_unifonts)) {
  3539. // If font[nostyle] exists - set it
  3540. if (in_array($family, $this->available_unifonts)) {
  3541. $style = '';
  3542. } // elseif only one font available - set it (assumes if only one font available it will not have a style)
  3543. elseif (count($this->available_unifonts) == 1) {
  3544. $family = $this->available_unifonts[0];
  3545. $style = '';
  3546. } else {
  3547. $found = 0;
  3548. // else substitute font of similar type
  3549. if (in_array($family, $this->sans_fonts)) {
  3550. $i = array_intersect($this->sans_fonts, $this->available_unifonts);
  3551. if (count($i)) {
  3552. $i = array_values($i);
  3553. // with requested style if possible
  3554. if (!in_array(($i[0] . $style), $this->available_unifonts)) {
  3555. $style = '';
  3556. }
  3557. $family = $i[0];
  3558. $found = 1;
  3559. }
  3560. } elseif (in_array($family, $this->serif_fonts)) {
  3561. $i = array_intersect($this->serif_fonts, $this->available_unifonts);
  3562. if (count($i)) {
  3563. $i = array_values($i);
  3564. // with requested style if possible
  3565. if (!in_array(($i[0] . $style), $this->available_unifonts)) {
  3566. $style = '';
  3567. }
  3568. $family = $i[0];
  3569. $found = 1;
  3570. }
  3571. } elseif (in_array($family, $this->mono_fonts)) {
  3572. $i = array_intersect($this->mono_fonts, $this->available_unifonts);
  3573. if (count($i)) {
  3574. $i = array_values($i);
  3575. // with requested style if possible
  3576. if (!in_array(($i[0] . $style), $this->available_unifonts)) {
  3577. $style = '';
  3578. }
  3579. $family = $i[0];
  3580. $found = 1;
  3581. }
  3582. }
  3583. if (!$found) {
  3584. // set first available font
  3585. $fs = $this->available_unifonts[0];
  3586. preg_match('/^([a-z_0-9\-]+)([BI]{0,2})$/', $fs, $fas); // Allow "-"
  3587. // with requested style if possible
  3588. $ws = $fas[1] . $style;
  3589. if (in_array($ws, $this->available_unifonts)) {
  3590. $family = $fas[1]; // leave $style as is
  3591. } elseif (in_array($fas[1], $this->available_unifonts)) {
  3592. // or without style
  3593. $family = $fas[1];
  3594. $style = '';
  3595. } else {
  3596. // or with the style specified
  3597. $family = $fas[1];
  3598. $style = $fas[2];
  3599. }
  3600. }
  3601. }
  3602. $fontkey = $family . $style;
  3603. }
  3604. }
  3605. }
  3606. // try to add font (if not already added)
  3607. $this->AddFont($family, $style);
  3608. // Test if font is already selected
  3609. if ($this->FontFamily == $family && $this->FontFamily == $this->currentfontfamily && $this->FontStyle == $style && $this->FontStyle == $this->currentfontstyle && $this->FontSizePt == $size && $this->FontSizePt == $this->currentfontsize && !$forcewrite) {
  3610. return $family;
  3611. }
  3612. $fontkey = $family . $style;
  3613. // Select it
  3614. $this->FontFamily = $family;
  3615. $this->FontStyle = $style;
  3616. $this->FontSizePt = $size;
  3617. $this->FontSize = $size / Mpdf::SCALE;
  3618. $this->CurrentFont = &$this->fonts[$fontkey];
  3619. if ($write) {
  3620. $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  3621. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
  3622. $this->writer->write($fontout);
  3623. }
  3624. $this->pageoutput[$this->page]['Font'] = $fontout;
  3625. }
  3626. // Added - currentfont (lowercase) used in HTML2PDF
  3627. $this->currentfontfamily = $family;
  3628. $this->currentfontsize = $size;
  3629. $this->currentfontstyle = $style;
  3630. $this->setMBencoding('UTF-8');
  3631. } else { // if using core fonts
  3632. if ($this->PDFA || $this->PDFX) {
  3633. 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.');
  3634. }
  3635. $this->setMBencoding('windows-1252');
  3636. // Test if font is already selected
  3637. if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) {
  3638. return $family;
  3639. }
  3640. if (!isset($this->CoreFonts[$fontkey])) {
  3641. if (in_array($family, $this->serif_fonts)) {
  3642. $family = 'ctimes';
  3643. } elseif (in_array($family, $this->mono_fonts)) {
  3644. $family = 'ccourier';
  3645. } else {
  3646. $family = 'chelvetica';
  3647. }
  3648. $this->usingCoreFont = true;
  3649. $fontkey = $family . $style;
  3650. }
  3651. if (!isset($this->fonts[$fontkey])) {
  3652. // STANDARD CORE FONTS
  3653. if (isset($this->CoreFonts[$fontkey])) {
  3654. // Load metric file
  3655. $file = $family;
  3656. if ($family == 'ctimes' || $family == 'chelvetica' || $family == 'ccourier') {
  3657. $file .= strtolower($style);
  3658. }
  3659. require __DIR__ . '/../data/font/' . $file . '.php';
  3660. if (!isset($cw)) {
  3661. throw new \Mpdf\MpdfException(sprintf('Could not include font metric file "%s"', $file));
  3662. }
  3663. $i = count($this->fonts) + $this->extraFontSubsets + 1;
  3664. $this->fonts[$fontkey] = ['i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw];
  3665. if ($this->useKerning && isset($kerninfo)) {
  3666. $this->fonts[$fontkey]['kerninfo'] = $kerninfo;
  3667. }
  3668. } else {
  3669. throw new \Mpdf\MpdfException(sprintf('Font %s not defined', $fontkey));
  3670. }
  3671. }
  3672. // Test if font is already selected
  3673. if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) {
  3674. return $family;
  3675. }
  3676. // Select it
  3677. $this->FontFamily = $family;
  3678. $this->FontStyle = $style;
  3679. $this->FontSizePt = $size;
  3680. $this->FontSize = $size / Mpdf::SCALE;
  3681. $this->CurrentFont = &$this->fonts[$fontkey];
  3682. if ($write) {
  3683. $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  3684. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
  3685. $this->writer->write($fontout);
  3686. }
  3687. $this->pageoutput[$this->page]['Font'] = $fontout;
  3688. }
  3689. // Added - currentfont (lowercase) used in HTML2PDF
  3690. $this->currentfontfamily = $family;
  3691. $this->currentfontsize = $size;
  3692. $this->currentfontstyle = $style;
  3693. }
  3694. return $family;
  3695. }
  3696. function SetFontSize($size, $write = true)
  3697. {
  3698. // Set font size in points
  3699. if ($this->FontSizePt == $size) {
  3700. return;
  3701. }
  3702. $this->FontSizePt = $size;
  3703. $this->FontSize = $size / Mpdf::SCALE;
  3704. $this->currentfontsize = $size;
  3705. if ($write) {
  3706. $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  3707. // Edited mPDF 3.0
  3708. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
  3709. $this->writer->write($fontout);
  3710. }
  3711. $this->pageoutput[$this->page]['Font'] = $fontout;
  3712. }
  3713. }
  3714. function AddLink()
  3715. {
  3716. // Create a new internal link
  3717. $n = count($this->links) + 1;
  3718. $this->links[$n] = [0, 0];
  3719. return $n;
  3720. }
  3721. function SetLink($link, $y = 0, $page = -1)
  3722. {
  3723. // Set destination of internal link
  3724. if ($y == -1) {
  3725. $y = $this->y;
  3726. }
  3727. if ($page == -1) {
  3728. $page = $this->page;
  3729. }
  3730. $this->links[$link] = [$page, $y];
  3731. }
  3732. function Link($x, $y, $w, $h, $link)
  3733. {
  3734. $l = [$x * Mpdf::SCALE, $this->hPt - $y * Mpdf::SCALE, $w * Mpdf::SCALE, $h * Mpdf::SCALE, $link];
  3735. if ($this->keep_block_together) { // don't write yet
  3736. return;
  3737. } elseif ($this->table_rotate) { // *TABLES*
  3738. $this->tbrot_Links[$this->page][] = $l; // *TABLES*
  3739. return; // *TABLES*
  3740. } // *TABLES*
  3741. elseif ($this->kwt) {
  3742. $this->kwt_Links[$this->page][] = $l;
  3743. return;
  3744. }
  3745. if ($this->writingHTMLheader || $this->writingHTMLfooter) {
  3746. $this->HTMLheaderPageLinks[] = $l;
  3747. return;
  3748. }
  3749. // Put a link on the page
  3750. $this->PageLinks[$this->page][] = $l;
  3751. // Save cross-reference to Column buffer
  3752. $ref = count($this->PageLinks[$this->page]) - 1; // *COLUMNS*
  3753. $this->columnLinks[$this->CurrCol][(int) $this->x][(int) $this->y] = $ref; // *COLUMNS*
  3754. }
  3755. function Text($x, $y, $txt, $OTLdata = [], $textvar = 0, $aixextra = '', $coordsys = '', $return = false)
  3756. {
  3757. // Output (or return) a string
  3758. // Called (internally) by Watermark() & _tableWrite() [rotated cells] & TableHeaderFooter() & WriteText()
  3759. // Called also from classes/svg.php
  3760. // Expects Font to be set
  3761. // Expects input to be mb_encoded if necessary and RTL reversed & OTL processed
  3762. // ARTIFICIAL BOLD AND ITALIC
  3763. $s = 'q ';
  3764. if ($this->falseBoldWeight && strpos($this->ReqFontStyle, "B") !== false && strpos($this->FontStyle, "B") === false) {
  3765. $s .= '2 Tr 1 J 1 j ';
  3766. $s .= sprintf('%.3F w ', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight);
  3767. $tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  3768. if ($this->FillColor != $tc) {
  3769. $s .= $tc . ' ';
  3770. } // stroke (outline) = same colour as text(fill)
  3771. }
  3772. if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) {
  3773. $aix = '1 0 0.261799 1 %.3F %.3F Tm';
  3774. } else {
  3775. $aix = '%.3F %.3F Td';
  3776. }
  3777. $aix = $aixextra . $aix;
  3778. if ($this->ColorFlag) {
  3779. $s .= $this->TextColor . ' ';
  3780. }
  3781. $this->CurrentFont['used'] = true;
  3782. if ($this->usingCoreFont) {
  3783. $txt2 = str_replace(chr(160), chr(32), $txt);
  3784. } else {
  3785. $txt2 = str_replace(chr(194) . chr(160), chr(32), $txt);
  3786. }
  3787. $px = $x;
  3788. $py = $y;
  3789. if ($coordsys != 'SVG') {
  3790. $px = $x * Mpdf::SCALE;
  3791. $py = ($this->h - $y) * Mpdf::SCALE;
  3792. }
  3793. /** ************** SIMILAR TO Cell() ************************ */
  3794. // IF corefonts AND NOT SmCaps AND NOT Kerning
  3795. // Just output text
  3796. if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) {
  3797. $txt2 = $this->writer->escape($txt2);
  3798. $s .= sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
  3799. } // IF NOT corefonts [AND NO wordspacing] AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL
  3800. // Just output text
  3801. elseif (!$this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {
  3802. // IF SIP/SMP
  3803. if ($this->CurrentFont['sip'] || $this->CurrentFont['smp']) {
  3804. $txt2 = $this->UTF8toSubset($txt2);
  3805. $s .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);
  3806. } // NOT SIP/SMP
  3807. else {
  3808. $txt2 = $this->writer->utf8ToUtf16BigEndian($txt2, false);
  3809. $txt2 = $this->writer->escape($txt2);
  3810. $s .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
  3811. }
  3812. } // IF NOT corefonts [AND IS wordspacing] AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL
  3813. // Not required here (cf. Cell() )
  3814. // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]
  3815. else {
  3816. $s .= $this->applyGPOSpdf($txt2, $aix, $px, $py, $OTLdata, $textvar);
  3817. }
  3818. /* * ************** END ************************ */
  3819. $s .= ' ';
  3820. if (($textvar & TextVars::FD_UNDERLINE) && $txt != '') { // mPDF 5.7.1
  3821. $c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  3822. if ($this->FillColor != $c) {
  3823. $s.= ' ' . $c . ' ';
  3824. }
  3825. if (isset($this->CurrentFont['up']) && $this->CurrentFont['up']) {
  3826. $up = $this->CurrentFont['up'];
  3827. } else {
  3828. $up = -100;
  3829. }
  3830. $adjusty = (-$up / 1000 * $this->FontSize);
  3831. if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {
  3832. $ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;
  3833. } else {
  3834. $ut = 60 / 1000 * $this->FontSize;
  3835. }
  3836. $olw = $this->LineWidth;
  3837. $s .= ' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE));
  3838. $s .= ' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);
  3839. $s .= ' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE));
  3840. if ($this->FillColor != $c) {
  3841. $s.= ' ' . $this->FillColor . ' ';
  3842. }
  3843. }
  3844. // STRIKETHROUGH
  3845. if (($textvar & TextVars::FD_LINETHROUGH) && $txt != '') { // mPDF 5.7.1
  3846. $c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  3847. if ($this->FillColor != $c) {
  3848. $s.= ' ' . $c . ' ';
  3849. }
  3850. // Superscript and Subscript Y coordinate adjustment (now for striked-through texts)
  3851. if (isset($this->CurrentFont['desc']['CapHeight']) && $this->CurrentFont['desc']['CapHeight']) {
  3852. $ch = $this->CurrentFont['desc']['CapHeight'];
  3853. } else {
  3854. $ch = 700;
  3855. }
  3856. $adjusty = (-$ch / 1000 * $this->FontSize) * 0.35;
  3857. if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {
  3858. $ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;
  3859. } else {
  3860. $ut = 60 / 1000 * $this->FontSize;
  3861. }
  3862. $olw = $this->LineWidth;
  3863. $s .= ' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE));
  3864. $s .= ' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);
  3865. $s .= ' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE));
  3866. if ($this->FillColor != $c) {
  3867. $s.= ' ' . $this->FillColor . ' ';
  3868. }
  3869. }
  3870. $s .= 'Q';
  3871. if ($return) {
  3872. return $s . " \n";
  3873. }
  3874. $this->writer->write($s);
  3875. }
  3876. /* -- DIRECTW -- */
  3877. function WriteText($x, $y, $txt)
  3878. {
  3879. // Output a string using Text() but does encoding and text reversing of RTL
  3880. $txt = $this->purify_utf8_text($txt);
  3881. if ($this->text_input_as_HTML) {
  3882. $txt = $this->all_entities_to_utf8($txt);
  3883. }
  3884. if ($this->usingCoreFont) {
  3885. $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
  3886. }
  3887. // DIRECTIONALITY
  3888. if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
  3889. $this->biDirectional = true;
  3890. } // *OTL*
  3891. $textvar = 0;
  3892. $save_OTLtags = $this->OTLtags;
  3893. $this->OTLtags = [];
  3894. if ($this->useKerning) {
  3895. if ($this->CurrentFont['haskernGPOS']) {
  3896. $this->OTLtags['Plus'] .= ' kern';
  3897. } else {
  3898. $textvar = ($textvar | TextVars::FC_KERNING);
  3899. }
  3900. }
  3901. /* -- OTL -- */
  3902. // Use OTL OpenType Table Layout - GSUB & GPOS
  3903. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  3904. $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
  3905. $OTLdata = $this->otl->OTLdata;
  3906. }
  3907. /* -- END OTL -- */
  3908. $this->OTLtags = $save_OTLtags;
  3909. $this->magic_reverse_dir($txt, $this->directionality, $OTLdata);
  3910. $this->Text($x, $y, $txt, $OTLdata, $textvar);
  3911. }
  3912. function WriteCell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0)
  3913. {
  3914. // Output a cell using Cell() but does encoding and text reversing of RTL
  3915. $txt = $this->purify_utf8_text($txt);
  3916. if ($this->text_input_as_HTML) {
  3917. $txt = $this->all_entities_to_utf8($txt);
  3918. }
  3919. if ($this->usingCoreFont) {
  3920. $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
  3921. }
  3922. // DIRECTIONALITY
  3923. if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
  3924. $this->biDirectional = true;
  3925. } // *OTL*
  3926. $textvar = 0;
  3927. $save_OTLtags = $this->OTLtags;
  3928. $this->OTLtags = [];
  3929. if ($this->useKerning) {
  3930. if ($this->CurrentFont['haskernGPOS']) {
  3931. $this->OTLtags['Plus'] .= ' kern';
  3932. } else {
  3933. $textvar = ($textvar | TextVars::FC_KERNING);
  3934. }
  3935. }
  3936. /* -- OTL -- */
  3937. // Use OTL OpenType Table Layout - GSUB & GPOS
  3938. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  3939. $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
  3940. $OTLdata = $this->otl->OTLdata;
  3941. }
  3942. /* -- END OTL -- */
  3943. $this->OTLtags = $save_OTLtags;
  3944. $this->magic_reverse_dir($txt, $this->directionality, $OTLdata);
  3945. $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link, $currentx, 0, 0, 'M', 0, false, $OTLdata, $textvar);
  3946. }
  3947. /* -- END DIRECTW -- */
  3948. function ResetSpacing()
  3949. {
  3950. if ($this->ws != 0) {
  3951. $this->writer->write('BT 0 Tw ET');
  3952. }
  3953. $this->ws = 0;
  3954. if ($this->charspacing != 0) {
  3955. $this->writer->write('BT 0 Tc ET');
  3956. }
  3957. $this->charspacing = 0;
  3958. }
  3959. function SetSpacing($cs, $ws)
  3960. {
  3961. if (intval($cs * 1000) == 0) {
  3962. $cs = 0;
  3963. }
  3964. if ($cs) {
  3965. $this->writer->write(sprintf('BT %.3F Tc ET', $cs));
  3966. } elseif ($this->charspacing != 0) {
  3967. $this->writer->write('BT 0 Tc ET');
  3968. }
  3969. $this->charspacing = $cs;
  3970. if (intval($ws * 1000) == 0) {
  3971. $ws = 0;
  3972. }
  3973. if ($ws) {
  3974. $this->writer->write(sprintf('BT %.3F Tw ET', $ws));
  3975. } elseif ($this->ws != 0) {
  3976. $this->writer->write('BT 0 Tw ET');
  3977. }
  3978. $this->ws = $ws;
  3979. }
  3980. // WORD SPACING
  3981. function GetJspacing($nc, $ns, $w, $inclCursive, &$cOTLdata)
  3982. {
  3983. $kashida_present = false;
  3984. $kashida_space = 0;
  3985. if ($w > 0 && $inclCursive && isset($this->CurrentFont['useKashida']) && $this->CurrentFont['useKashida'] && !empty($cOTLdata)) {
  3986. for ($c = 0; $c < count($cOTLdata); $c++) {
  3987. for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
  3988. if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
  3989. $kashida_present = true;
  3990. break 2;
  3991. }
  3992. }
  3993. }
  3994. }
  3995. if ($kashida_present) {
  3996. $k_ctr = 0; // Number of kashida points
  3997. $k_total = 0; // Total of kashida values (priority)
  3998. // Reset word
  3999. $max_kashida_in_word = 0;
  4000. $last_kashida_in_word = -1;
  4001. for ($c = 0; $c < count($cOTLdata); $c++) {
  4002. for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
  4003. if ($cOTLdata[$c]['group'][$i] == 'S') {
  4004. // Save from last word
  4005. if ($max_kashida_in_word) {
  4006. $k_ctr++;
  4007. $k_total = $max_kashida_in_word;
  4008. }
  4009. // Reset word
  4010. $max_kashida_in_word = 0;
  4011. $last_kashida_in_word = -1;
  4012. }
  4013. if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
  4014. if ($max_kashida_in_word) {
  4015. if ($cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > $max_kashida_in_word) {
  4016. $max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];
  4017. $cOTLdata[$c]['GPOSinfo'][$last_kashida_in_word]['kashida'] = 0;
  4018. $last_kashida_in_word = $i;
  4019. } else {
  4020. $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] = 0;
  4021. }
  4022. } else {
  4023. $max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];
  4024. $last_kashida_in_word = $i;
  4025. }
  4026. }
  4027. }
  4028. }
  4029. // Save from last word
  4030. if ($max_kashida_in_word) {
  4031. $k_ctr++;
  4032. $k_total = $max_kashida_in_word;
  4033. }
  4034. // Number of kashida points = $k_ctr
  4035. // $useKashida is a % value from CurrentFont/config_fonts.php
  4036. // % ratio divided between word-spacing and kashida-spacing
  4037. $kashida_space_ratio = intval($this->CurrentFont['useKashida']) / 100;
  4038. $kashida_space = $w * $kashida_space_ratio;
  4039. $tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);
  4040. // Only use kashida if each allocated kashida width is > 0.01 x width of a tatweel
  4041. // Otherwise fontstretch is too small and errors
  4042. // If not just leave to adjust word-spacing
  4043. if ($tatw && (($kashida_space / $k_ctr) / $tatw) > 0.01) {
  4044. for ($c = 0; $c < count($cOTLdata); $c++) {
  4045. for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
  4046. if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
  4047. // At this point kashida is a number representing priority (higher number - higher priority)
  4048. // We are now going to set it as an actual length
  4049. // This shares it equally amongst words:
  4050. $cOTLdata[$c]['GPOSinfo'][$i]['kashida_space'] = (1 / $k_ctr) * $kashida_space;
  4051. }
  4052. }
  4053. }
  4054. $w -= $kashida_space;
  4055. }
  4056. }
  4057. $ws = 0;
  4058. $charspacing = 0;
  4059. $ww = $this->jSWord;
  4060. $ncx = $nc - 1;
  4061. if ($nc == 0) {
  4062. return [0, 0, 0];
  4063. } // Only word spacing allowed / possible
  4064. elseif ($this->fixedlSpacing !== false || $inclCursive) {
  4065. if ($ns) {
  4066. $ws = $w / $ns;
  4067. }
  4068. } elseif ($nc == 1) {
  4069. $charspacing = $w;
  4070. } elseif (!$ns) {
  4071. $charspacing = $w / ($ncx );
  4072. if (($this->jSmaxChar > 0) && ($charspacing > $this->jSmaxChar)) {
  4073. $charspacing = $this->jSmaxChar;
  4074. }
  4075. } elseif ($ns == ($ncx )) {
  4076. $charspacing = $w / $ns;
  4077. } else {
  4078. if ($this->usingCoreFont) {
  4079. $cs = ($w * (1 - $this->jSWord)) / ($ncx );
  4080. if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {
  4081. $cs = $this->jSmaxChar;
  4082. $ww = 1 - (($cs * ($ncx )) / $w);
  4083. }
  4084. $charspacing = $cs;
  4085. $ws = ($w * ($ww) ) / $ns;
  4086. } else {
  4087. $cs = ($w * (1 - $this->jSWord)) / ($ncx - $ns);
  4088. if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {
  4089. $cs = $this->jSmaxChar;
  4090. $ww = 1 - (($cs * ($ncx - $ns)) / $w);
  4091. }
  4092. $charspacing = $cs;
  4093. $ws = (($w * ($ww) ) / $ns) - $charspacing;
  4094. }
  4095. }
  4096. return [$charspacing, $ws, $kashida_space];
  4097. }
  4098. /**
  4099. * Output a cell
  4100. *
  4101. * Expects input to be mb_encoded if necessary and RTL reversed
  4102. *
  4103. * @since mPDF 5.7.1
  4104. */
  4105. 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)
  4106. {
  4107. // NON_BREAKING SPACE
  4108. if ($this->usingCoreFont) {
  4109. $txt = str_replace(chr(160), chr(32), $txt);
  4110. } else {
  4111. $txt = str_replace(chr(194) . chr(160), chr(32), $txt);
  4112. }
  4113. $oldcolumn = $this->CurrCol;
  4114. // Automatic page break
  4115. // Allows PAGE-BREAK-AFTER = avoid to work
  4116. if (isset($this->blk[$this->blklvl])) {
  4117. $bottom = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['margin_bottom'];
  4118. } else {
  4119. $bottom = 0;
  4120. }
  4121. if (!$this->tableLevel
  4122. && (
  4123. ($this->y + $this->divheight > $this->PageBreakTrigger)
  4124. || ($this->y + $h > $this->PageBreakTrigger)
  4125. || (
  4126. $this->y + ($h * 2) + $bottom > $this->PageBreakTrigger
  4127. && (isset($this->blk[$this->blklvl]['page_break_after_avoid']) && $this->blk[$this->blklvl]['page_break_after_avoid'])
  4128. )
  4129. )
  4130. && !$this->InFooter
  4131. && $this->AcceptPageBreak()
  4132. ) { // mPDF 5.7.2
  4133. $x = $this->x; // Current X position
  4134. // WORD SPACING
  4135. $ws = $this->ws; // Word Spacing
  4136. $charspacing = $this->charspacing; // Character Spacing
  4137. $this->ResetSpacing();
  4138. $this->AddPage($this->CurOrientation);
  4139. // Added to correct for OddEven Margins
  4140. $x += $this->MarginCorrection;
  4141. if ($currentx) {
  4142. $currentx += $this->MarginCorrection;
  4143. }
  4144. $this->x = $x;
  4145. // WORD SPACING
  4146. $this->SetSpacing($charspacing, $ws);
  4147. }
  4148. // Test: to put line through centre of cell: $this->Line($this->x,$this->y+($h/2),$this->x+50,$this->y+($h/2));
  4149. // Test: to put border around cell as it is specified: $border='LRTB';
  4150. /* -- COLUMNS -- */
  4151. // COLS
  4152. // COLUMN CHANGE
  4153. if ($this->CurrCol != $oldcolumn) {
  4154. if ($currentx) {
  4155. $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  4156. }
  4157. $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  4158. }
  4159. // COLUMNS Update/overwrite the lowest bottom of printing y value for a column
  4160. if ($this->ColActive) {
  4161. if ($h) {
  4162. $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
  4163. } else {
  4164. $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $this->divheight;
  4165. }
  4166. }
  4167. /* -- END COLUMNS -- */
  4168. if ($w == 0) {
  4169. $w = $this->w - $this->rMargin - $this->x;
  4170. }
  4171. $s = '';
  4172. if ($fill == 1 && $this->FillColor) {
  4173. if ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor'])) {
  4174. $s .= $this->FillColor . ' ';
  4175. }
  4176. $this->pageoutput[$this->page]['FillColor'] = $this->FillColor;
  4177. }
  4178. if ($lineBox && isset($lineBox['boxtop']) && $txt) { // i.e. always from WriteFlowingBlock/finishFlowingBlock (but not objects -
  4179. // which only have $lineBox['top'] set)
  4180. $boxtop = $this->y + $lineBox['boxtop'];
  4181. $boxbottom = $this->y + $lineBox['boxbottom'];
  4182. $glyphYorigin = $lineBox['glyphYorigin'];
  4183. $baseline_shift = $lineBox['baseline-shift'];
  4184. $bord_boxtop = $bg_boxtop = $boxtop = $boxtop - $baseline_shift;
  4185. $bord_boxbottom = $bg_boxbottom = $boxbottom = $boxbottom - $baseline_shift;
  4186. $bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;
  4187. // If inline element BACKGROUND has bounding box set by parent element:
  4188. if (isset($lineBox['background-boxtop'])) {
  4189. $bg_boxtop = $this->y + $lineBox['background-boxtop'] - $lineBox['background-baseline-shift'];
  4190. $bg_boxbottom = $this->y + $lineBox['background-boxbottom'] - $lineBox['background-baseline-shift'];
  4191. $bg_boxheight = $bg_boxbottom - $bg_boxtop;
  4192. }
  4193. // If inline element BORDER has bounding box set by parent element:
  4194. if (isset($lineBox['border-boxtop'])) {
  4195. $bord_boxtop = $this->y + $lineBox['border-boxtop'] - $lineBox['border-baseline-shift'];
  4196. $bord_boxbottom = $this->y + $lineBox['border-boxbottom'] - $lineBox['border-baseline-shift'];
  4197. $bord_boxheight = $bord_boxbottom - $bord_boxtop;
  4198. }
  4199. } else {
  4200. $boxtop = $this->y;
  4201. $boxheight = $h;
  4202. $boxbottom = $this->y + $h;
  4203. $baseline_shift = 0;
  4204. if ($txt != '') {
  4205. // FONT SIZE - this determines the baseline caculation
  4206. $bfs = $this->FontSize;
  4207. // Calculate baseline Superscript and Subscript Y coordinate adjustment
  4208. $bfx = $this->baselineC;
  4209. $baseline = $bfx * $bfs;
  4210. if ($textvar & TextVars::FA_SUPERSCRIPT) {
  4211. $baseline_shift = $this->textparam['text-baseline'];
  4212. } elseif ($textvar & TextVars::FA_SUBSCRIPT) {
  4213. $baseline_shift = $this->textparam['text-baseline'];
  4214. } elseif ($this->bullet) {
  4215. $baseline += ($bfx - 0.7) * $this->FontSize;
  4216. }
  4217. // Vertical align (for Images)
  4218. if ($valign == 'T') {
  4219. $va = (0.5 * $bfs * $this->normalLineheight);
  4220. } elseif ($valign == 'B') {
  4221. $va = $h - (0.5 * $bfs * $this->normalLineheight);
  4222. } else {
  4223. $va = 0.5 * $h;
  4224. } // Middle
  4225. // ONLY SET THESE IF WANT TO CONFINE BORDER +/- FILL TO FIT FONTSIZE - NOT FULL CELL AS IS ORIGINAL FUNCTION
  4226. // spanfill or spanborder are set in FlowingBlock functions
  4227. if ($spanfill || !empty($this->spanborddet) || $link != '') {
  4228. $exth = 0.2; // Add to fontsize to increase height of background / link / border
  4229. $boxtop = $this->y + $baseline + $va - ($this->FontSize * (1 + $exth / 2) * (0.5 + $bfx));
  4230. $boxheight = $this->FontSize * (1 + $exth);
  4231. $boxbottom = $boxtop + $boxheight;
  4232. }
  4233. $glyphYorigin = $baseline + $va;
  4234. }
  4235. $boxtop -= $baseline_shift;
  4236. $boxbottom -= $baseline_shift;
  4237. $bord_boxtop = $bg_boxtop = $boxtop;
  4238. $bord_boxbottom = $bg_boxbottom = $boxbottom;
  4239. $bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;
  4240. }
  4241. $bbw = $tbw = $lbw = $rbw = 0; // Border widths
  4242. if (!empty($this->spanborddet)) {
  4243. if (!isset($this->spanborddet['B'])) {
  4244. $this->spanborddet['B'] = ['s' => 0, 'style' => '', 'w' => 0];
  4245. }
  4246. if (!isset($this->spanborddet['T'])) {
  4247. $this->spanborddet['T'] = ['s' => 0, 'style' => '', 'w' => 0];
  4248. }
  4249. if (!isset($this->spanborddet['L'])) {
  4250. $this->spanborddet['L'] = ['s' => 0, 'style' => '', 'w' => 0];
  4251. }
  4252. if (!isset($this->spanborddet['R'])) {
  4253. $this->spanborddet['R'] = ['s' => 0, 'style' => '', 'w' => 0];
  4254. }
  4255. $bbw = $this->spanborddet['B']['w'];
  4256. $tbw = $this->spanborddet['T']['w'];
  4257. $lbw = $this->spanborddet['L']['w'];
  4258. $rbw = $this->spanborddet['R']['w'];
  4259. }
  4260. if ($fill == 1 || $border == 1 || !empty($this->spanborddet)) {
  4261. if (!empty($this->spanborddet)) {
  4262. if ($fill == 1) {
  4263. $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);
  4264. }
  4265. $s.= ' q ';
  4266. $dashon = 3;
  4267. $dashoff = 3.5;
  4268. $dot = 2.5;
  4269. if ($tbw) {
  4270. $short = 0;
  4271. if ($this->spanborddet['T']['style'] == 'dashed') {
  4272. $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $tbw * $dashon * Mpdf::SCALE, $tbw * $dashoff * Mpdf::SCALE);
  4273. } elseif ($this->spanborddet['T']['style'] == 'dotted') {
  4274. $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $tbw * $dot * Mpdf::SCALE, -$tbw / 2 * Mpdf::SCALE);
  4275. $short = $tbw / 2;
  4276. } else {
  4277. $s .= ' 0 j 0 J [] 0 d ';
  4278. }
  4279. if ($this->spanborddet['T']['style'] != 'dotted') {
  4280. $s .= 'q ';
  4281. $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
  4282. $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
  4283. $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
  4284. $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
  4285. $s .= ' h W n '; // Ends path no-op & Sets the clipping path
  4286. }
  4287. $c = $this->SetDColor($this->spanborddet['T']['c'], true);
  4288. if ($this->spanborddet['T']['style'] == 'double') {
  4289. $s .= sprintf(' %s %.3F w ', $c, $tbw / 3 * Mpdf::SCALE);
  4290. $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);
  4291. $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);
  4292. } elseif ($this->spanborddet['T']['style'] == 'dotted') {
  4293. $s .= sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE);
  4294. $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);
  4295. } else {
  4296. $s .= sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE);
  4297. $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);
  4298. }
  4299. if ($this->spanborddet['T']['style'] != 'dotted') {
  4300. $s .= ' Q ';
  4301. }
  4302. }
  4303. if ($bbw) {
  4304. $short = 0;
  4305. if ($this->spanborddet['B']['style'] == 'dashed') {
  4306. $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $bbw * $dashon * Mpdf::SCALE, $bbw * $dashoff * Mpdf::SCALE);
  4307. } elseif ($this->spanborddet['B']['style'] == 'dotted') {
  4308. $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $bbw * $dot * Mpdf::SCALE, -$bbw / 2 * Mpdf::SCALE);
  4309. $short = $bbw / 2;
  4310. } else {
  4311. $s .= ' 0 j 0 J [] 0 d ';
  4312. }
  4313. if ($this->spanborddet['B']['style'] != 'dotted') {
  4314. $s .= 'q ';
  4315. $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
  4316. $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
  4317. $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
  4318. $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
  4319. $s .= ' h W n '; // Ends path no-op & Sets the clipping path
  4320. }
  4321. $c = $this->SetDColor($this->spanborddet['B']['c'], true);
  4322. if ($this->spanborddet['B']['style'] == 'double') {
  4323. $s .= sprintf(' %s %.3F w ', $c, $bbw / 3 * Mpdf::SCALE);
  4324. $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);
  4325. $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);
  4326. } elseif ($this->spanborddet['B']['style'] == 'dotted') {
  4327. $s .= sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE);
  4328. $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);
  4329. } else {
  4330. $s .= sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE);
  4331. $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);
  4332. }
  4333. if ($this->spanborddet['B']['style'] != 'dotted') {
  4334. $s .= ' Q ';
  4335. }
  4336. }
  4337. if ($lbw) {
  4338. $short = 0;
  4339. if ($this->spanborddet['L']['style'] == 'dashed') {
  4340. $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $lbw * $dashon * Mpdf::SCALE, $lbw * $dashoff * Mpdf::SCALE);
  4341. } elseif ($this->spanborddet['L']['style'] == 'dotted') {
  4342. $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $lbw * $dot * Mpdf::SCALE, -$lbw / 2 * Mpdf::SCALE);
  4343. $short = $lbw / 2;
  4344. } else {
  4345. $s .= ' 0 j 0 J [] 0 d ';
  4346. }
  4347. if ($this->spanborddet['L']['style'] != 'dotted') {
  4348. $s .= 'q ';
  4349. $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
  4350. $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
  4351. $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
  4352. $s .= sprintf('%.3F %.3F l ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
  4353. $s .= ' h W n '; // Ends path no-op & Sets the clipping path
  4354. }
  4355. $c = $this->SetDColor($this->spanborddet['L']['c'], true);
  4356. if ($this->spanborddet['L']['style'] == 'double') {
  4357. $s .= sprintf(' %s %.3F w ', $c, $lbw / 3 * Mpdf::SCALE);
  4358. $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);
  4359. $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);
  4360. } elseif ($this->spanborddet['L']['style'] == 'dotted') {
  4361. $s .= sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE);
  4362. $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);
  4363. } else {
  4364. $s .= sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE);
  4365. $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);
  4366. }
  4367. if ($this->spanborddet['L']['style'] != 'dotted') {
  4368. $s .= ' Q ';
  4369. }
  4370. }
  4371. if ($rbw) {
  4372. $short = 0;
  4373. if ($this->spanborddet['R']['style'] == 'dashed') {
  4374. $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $rbw * $dashon * Mpdf::SCALE, $rbw * $dashoff * Mpdf::SCALE);
  4375. } elseif ($this->spanborddet['R']['style'] == 'dotted') {
  4376. $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $rbw * $dot * Mpdf::SCALE, -$rbw / 2 * Mpdf::SCALE);
  4377. $short = $rbw / 2;
  4378. } else {
  4379. $s .= ' 0 j 0 J [] 0 d ';
  4380. }
  4381. if ($this->spanborddet['R']['style'] != 'dotted') {
  4382. $s .= 'q ';
  4383. $s .= sprintf('%.3F %.3F m ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
  4384. $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
  4385. $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
  4386. $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
  4387. $s .= ' h W n '; // Ends path no-op & Sets the clipping path
  4388. }
  4389. $c = $this->SetDColor($this->spanborddet['R']['c'], true);
  4390. if ($this->spanborddet['R']['style'] == 'double') {
  4391. $s .= sprintf(' %s %.3F w ', $c, $rbw / 3 * Mpdf::SCALE);
  4392. $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);
  4393. $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);
  4394. } elseif ($this->spanborddet['R']['style'] == 'dotted') {
  4395. $s .= sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE);
  4396. $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);
  4397. } else {
  4398. $s .= sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE);
  4399. $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);
  4400. }
  4401. if ($this->spanborddet['R']['style'] != 'dotted') {
  4402. $s .= ' Q ';
  4403. }
  4404. }
  4405. $s.= ' Q ';
  4406. } else { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock
  4407. if ($fill == 1) {
  4408. $op = ($border == 1) ? 'B' : 'f';
  4409. } else {
  4410. $op = 'S';
  4411. }
  4412. $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);
  4413. }
  4414. }
  4415. if (is_string($border)) { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock
  4416. $x = $this->x;
  4417. $y = $this->y;
  4418. if (is_int(strpos($border, 'L'))) {
  4419. $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);
  4420. }
  4421. if (is_int(strpos($border, 'T'))) {
  4422. $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);
  4423. }
  4424. if (is_int(strpos($border, 'R'))) {
  4425. $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);
  4426. }
  4427. if (is_int(strpos($border, 'B'))) {
  4428. $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);
  4429. }
  4430. }
  4431. if ($txt != '') {
  4432. if ($exactWidth) {
  4433. $stringWidth = $w;
  4434. } else {
  4435. $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 );
  4436. }
  4437. // Set x OFFSET FOR PRINTING
  4438. if ($align == 'R') {
  4439. $dx = $w - $this->cMarginR - $stringWidth - $lcpaddingR;
  4440. } elseif ($align == 'C') {
  4441. $dx = (($w - $stringWidth ) / 2);
  4442. } elseif ($align == 'L' or $align == 'J') {
  4443. $dx = $this->cMarginL + $lcpaddingL;
  4444. } else {
  4445. $dx = 0;
  4446. }
  4447. if ($this->ColorFlag) {
  4448. $s .='q ' . $this->TextColor . ' ';
  4449. }
  4450. // OUTLINE
  4451. if (isset($this->textparam['outline-s']) && $this->textparam['outline-s'] && !($textvar & TextVars::FC_SMALLCAPS)) { // mPDF 5.7.1
  4452. $s .=' ' . sprintf('%.3F w', $this->LineWidth * Mpdf::SCALE) . ' ';
  4453. $s .=" $this->DrawColor ";
  4454. $s .=" 2 Tr ";
  4455. } 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 ???
  4456. $s .= ' 2 Tr 1 J 1 j ';
  4457. $s .= ' ' . sprintf('%.3F w', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight) . ' ';
  4458. $tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  4459. if ($this->FillColor != $tc) {
  4460. $s .= ' ' . $tc . ' ';
  4461. } // stroke (outline) = same colour as text(fill)
  4462. } else {
  4463. $s .=" 0 Tr ";
  4464. }
  4465. if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) { // Artificial italic
  4466. $aix = '1 0 0.261799 1 %.3F %.3F Tm ';
  4467. } else {
  4468. $aix = '%.3F %.3F Td ';
  4469. }
  4470. $px = ($this->x + $dx) * Mpdf::SCALE;
  4471. $py = ($this->h - ($this->y + $glyphYorigin - $baseline_shift)) * Mpdf::SCALE;
  4472. // THE TEXT
  4473. $txt2 = $txt;
  4474. $sub = '';
  4475. $this->CurrentFont['used'] = true;
  4476. /* * ************** SIMILAR TO Text() ************************ */
  4477. // IF corefonts AND NOT SmCaps AND NOT Kerning
  4478. // Just output text; charspacing and wordspacing already set by charspacing (Tc) and ws (Tw)
  4479. if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) {
  4480. $txt2 = $this->writer->escape($txt2);
  4481. $sub .= sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
  4482. } // IF NOT corefonts AND NO wordspacing AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL
  4483. // Just output text
  4484. elseif (!$this->usingCoreFont && !$this->ws && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {
  4485. // IF SIP/SMP
  4486. if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {
  4487. $txt2 = $this->UTF8toSubset($txt2);
  4488. $sub .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);
  4489. } // NOT SIP/SMP
  4490. else {
  4491. $txt2 = $this->writer->utf8ToUtf16BigEndian($txt2, false);
  4492. $txt2 = $this->writer->escape($txt2);
  4493. $sub .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
  4494. }
  4495. } // IF NOT corefonts AND IS wordspacing AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL
  4496. // Output text word by word with an adjustment to the intercharacter spacing for SPACEs to form word spacing
  4497. // IF multibyte - Tw has no effect - need to do word spacing using an adjustment before each space
  4498. 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)) )) {
  4499. $space = " ";
  4500. $space = $this->writer->utf8ToUtf16BigEndian($space, false);
  4501. $space = $this->writer->escape($space);
  4502. $sub .=sprintf('BT ' . $aix . ' %.3F Tc [', $px, $py, $this->charspacing);
  4503. $t = explode(' ', $txt2);
  4504. $numt = count($t);
  4505. for ($i = 0; $i < $numt; $i++) {
  4506. $tx = $t[$i];
  4507. $tx = $this->writer->utf8ToUtf16BigEndian($tx, false);
  4508. $tx = $this->writer->escape($tx);
  4509. $sub .=sprintf('(%s) ', $tx);
  4510. if (($i + 1) < $numt) {
  4511. $adj = -($this->ws) * 1000 / $this->FontSizePt;
  4512. $sub .=sprintf('%d(%s) ', $adj, $space);
  4513. }
  4514. }
  4515. $sub .='] TJ ';
  4516. $sub .=' ET';
  4517. } // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]
  4518. else {
  4519. $sub = $this->applyGPOSpdf($txt, $aix, $px, $py, $OTLdata, $textvar);
  4520. }
  4521. /** ************** END SIMILAR TO Text() ************************ */
  4522. if ($this->shrin_k > 1) {
  4523. $shrin_k = $this->shrin_k;
  4524. } else {
  4525. $shrin_k = 1;
  4526. }
  4527. // UNDERLINE
  4528. if ($textvar & TextVars::FD_UNDERLINE) { // mPDF 5.7.1 // mPDF 6
  4529. // mPDF 5.7.3 inline text-decoration parameters
  4530. $c = isset($this->textparam['u-decoration']['color']) ? $this->textparam['u-decoration']['color'] : '';
  4531. if ($this->FillColor != $c) {
  4532. $sub .= ' ' . $c . ' ';
  4533. }
  4534. // mPDF 5.7.3 inline text-decoration parameters
  4535. $decorationfontkey = isset($this->textparam['u-decoration']['fontkey']) ? $this->textparam['u-decoration']['fontkey'] : '';
  4536. $decorationfontsize = isset($this->textparam['u-decoration']['fontsize']) ? $this->textparam['u-decoration']['fontsize'] / $shrin_k : 0;
  4537. if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
  4538. $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
  4539. } else {
  4540. $ut = 60 / 1000 * $decorationfontsize;
  4541. }
  4542. if (isset($this->fonts[$decorationfontkey]['up']) && $this->fonts[$decorationfontkey]['up']) {
  4543. $up = $this->fonts[$decorationfontkey]['up'];
  4544. } else {
  4545. $up = -100;
  4546. }
  4547. $adjusty = (-$up / 1000 * $decorationfontsize) + $ut / 2;
  4548. $ubaseline = isset($this->textparam['u-decoration']['baseline'])
  4549. ? $glyphYorigin - $this->textparam['u-decoration']['baseline'] / $shrin_k
  4550. : $glyphYorigin;
  4551. $olw = $this->LineWidth;
  4552. $sub .= ' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
  4553. $sub .= ' ' . $this->_dounderline($this->x + $dx, $this->y + $ubaseline + $adjusty, $txt, $OTLdata, $textvar);
  4554. $sub .= ' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
  4555. if ($this->FillColor != $c) {
  4556. $sub .= ' ' . $this->FillColor . ' ';
  4557. }
  4558. }
  4559. // STRIKETHROUGH
  4560. if ($textvar & TextVars::FD_LINETHROUGH) { // mPDF 5.7.1 // mPDF 6
  4561. // mPDF 5.7.3 inline text-decoration parameters
  4562. $c = $this->textparam['s-decoration']['color'];
  4563. if ($this->FillColor != $c) {
  4564. $sub .= ' ' . $c . ' ';
  4565. }
  4566. // mPDF 5.7.3 inline text-decoration parameters
  4567. $decorationfontkey = $this->textparam['s-decoration']['fontkey'];
  4568. $decorationfontsize = $this->textparam['s-decoration']['fontsize'] / $shrin_k;
  4569. // Use yStrikeoutSize from OS/2 if available
  4570. if (isset($this->fonts[$decorationfontkey]['strs']) && $this->fonts[$decorationfontkey]['strs']) {
  4571. $ut = $this->fonts[$decorationfontkey]['strs'] / 1000 * $decorationfontsize;
  4572. } // else use underlineThickness from post if available
  4573. elseif (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
  4574. $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
  4575. } else {
  4576. $ut = 50 / 1000 * $decorationfontsize;
  4577. }
  4578. // Use yStrikeoutPosition from OS/2 if available
  4579. if (isset($this->fonts[$decorationfontkey]['strp']) && $this->fonts[$decorationfontkey]['strp']) {
  4580. $up = $this->fonts[$decorationfontkey]['strp'];
  4581. $adjusty = (-$up / 1000 * $decorationfontsize);
  4582. } // else use a fraction ($this->baselineS) of CapHeight
  4583. else {
  4584. if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {
  4585. $ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];
  4586. } else {
  4587. $ch = 700;
  4588. }
  4589. $adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineS;
  4590. }
  4591. $sbaseline = $glyphYorigin - $this->textparam['s-decoration']['baseline'] / $shrin_k;
  4592. $olw = $this->LineWidth;
  4593. $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
  4594. $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $sbaseline + $adjusty, $txt, $OTLdata, $textvar);
  4595. $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
  4596. if ($this->FillColor != $c) {
  4597. $sub .= ' ' . $this->FillColor . ' ';
  4598. }
  4599. }
  4600. // mPDF 5.7.3 inline text-decoration parameters
  4601. // OVERLINE
  4602. if ($textvar & TextVars::FD_OVERLINE) { // mPDF 5.7.1 // mPDF 6
  4603. // mPDF 5.7.3 inline text-decoration parameters
  4604. $c = $this->textparam['o-decoration']['color'];
  4605. if ($this->FillColor != $c) {
  4606. $sub .= ' ' . $c . ' ';
  4607. }
  4608. // mPDF 5.7.3 inline text-decoration parameters
  4609. $decorationfontkey = (int) (((float) $this->textparam['o-decoration']['fontkey']) / $shrin_k);
  4610. $decorationfontsize = $this->textparam['o-decoration']['fontsize'];
  4611. if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
  4612. $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
  4613. } else {
  4614. $ut = 60 / 1000 * $decorationfontsize;
  4615. }
  4616. if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {
  4617. $ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];
  4618. } else {
  4619. $ch = 700;
  4620. }
  4621. $adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineO;
  4622. $obaseline = $glyphYorigin - $this->textparam['o-decoration']['baseline'] / $shrin_k;
  4623. $olw = $this->LineWidth;
  4624. $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
  4625. $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $obaseline + $adjusty, $txt, $OTLdata, $textvar);
  4626. $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
  4627. if ($this->FillColor != $c) {
  4628. $sub .= ' ' . $this->FillColor . ' ';
  4629. }
  4630. }
  4631. // TEXT SHADOW
  4632. if ($this->textshadow) { // First to process is last in CSS comma separated shadows
  4633. foreach ($this->textshadow as $ts) {
  4634. $s .= ' q ';
  4635. $s .= $this->SetTColor($ts['col'], true) . "\n";
  4636. if ($ts['col'][0] == 5 && ord($ts['col'][4]) < 100) { // RGBa
  4637. $s .= $this->SetAlpha(ord($ts['col'][4]) / 100, 'Normal', true, 'F') . "\n";
  4638. } elseif ($ts['col'][0] == 6 && ord($ts['col'][5]) < 100) { // CMYKa
  4639. $s .= $this->SetAlpha(ord($ts['col'][5]) / 100, 'Normal', true, 'F') . "\n";
  4640. } elseif ($ts['col'][0] == 1 && $ts['col'][2] == 1 && ord($ts['col'][3]) < 100) { // Gray
  4641. $s .= $this->SetAlpha(ord($ts['col'][3]) / 100, 'Normal', true, 'F') . "\n";
  4642. }
  4643. $s .= sprintf(' 1 0 0 1 %.4F %.4F cm', $ts['x'] * Mpdf::SCALE, -$ts['y'] * Mpdf::SCALE) . "\n";
  4644. $s .= $sub;
  4645. $s .= ' Q ';
  4646. }
  4647. }
  4648. $s .= $sub;
  4649. // COLOR
  4650. if ($this->ColorFlag) {
  4651. $s .=' Q';
  4652. }
  4653. // LINK
  4654. if ($link != '') {
  4655. $this->Link($this->x, $boxtop, $w, $boxheight, $link);
  4656. }
  4657. }
  4658. if ($s) {
  4659. $this->writer->write($s);
  4660. }
  4661. // WORD SPACING
  4662. if ($this->ws && !$this->usingCoreFont) {
  4663. $this->writer->write(sprintf('BT %.3F Tc ET', $this->charspacing));
  4664. }
  4665. $this->lasth = $h;
  4666. if (strpos($txt, "\n") !== false) {
  4667. $ln = 1; // cell recognizes \n from <BR> tag
  4668. }
  4669. if ($ln > 0) {
  4670. // Go to next line
  4671. $this->y += $h;
  4672. if ($ln == 1) {
  4673. // Move to next line
  4674. if ($currentx != 0) {
  4675. $this->x = $currentx;
  4676. } else {
  4677. $this->x = $this->lMargin;
  4678. }
  4679. }
  4680. } else {
  4681. $this->x+=$w;
  4682. }
  4683. }
  4684. function applyGPOSpdf($txt, $aix, $x, $y, $OTLdata, $textvar = 0)
  4685. {
  4686. $sipset = (isset($this->CurrentFont['sip']) && $this->CurrentFont['sip'])
  4687. || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp']);
  4688. $smcaps = ($textvar & TextVars::FC_SMALLCAPS);
  4689. $fontid = $sipset
  4690. ? $last_fontid = $original_fontid = $this->CurrentFont['subsetfontids'][0]
  4691. : $last_fontid = $original_fontid = $this->CurrentFont['i'];
  4692. $SmallCapsON = false; // state: uppercase/not
  4693. $lastSmallCapsON = false; // state: uppercase/not
  4694. $last_fontsize = $fontsize = $this->FontSizePt;
  4695. $last_fontstretch = $fontstretch = 100;
  4696. $groupBreak = false;
  4697. $unicode = $this->UTF8StringToArray($txt);
  4698. $GPOSinfo = (isset($OTLdata['GPOSinfo']) ? $OTLdata['GPOSinfo'] : []);
  4699. $charspacing = ($this->charspacing * 1000 / $this->FontSizePt);
  4700. $wordspacing = ($this->ws * 1000 / $this->FontSizePt);
  4701. $XshiftBefore = 0;
  4702. $XshiftAfter = 0;
  4703. $lastYPlacement = 0;
  4704. $tj = $sipset
  4705. ? '<'
  4706. : '(';
  4707. for ($i = 0; $i < count($unicode); $i++) {
  4708. $c = $unicode[$i];
  4709. $tx = '';
  4710. $XshiftBefore = $XshiftAfter;
  4711. $XshiftAfter = 0;
  4712. $YPlacement = 0;
  4713. $groupBreak = false;
  4714. $kashida = 0;
  4715. if (!empty($OTLdata)) {
  4716. // YPlacement from GPOS
  4717. if (isset($GPOSinfo[$i]['YPlacement']) && $GPOSinfo[$i]['YPlacement']) {
  4718. $YPlacement = $GPOSinfo[$i]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];
  4719. $groupBreak = true;
  4720. }
  4721. // XPlacement from GPOS
  4722. if (isset($GPOSinfo[$i]['XPlacement']) && $GPOSinfo[$i]['XPlacement']) {
  4723. if (!isset($GPOSinfo[$i]['wDir']) || $GPOSinfo[$i]['wDir'] !== 'RTL') {
  4724. if (isset($GPOSinfo[$i]['BaseWidth'])) {
  4725. $GPOSinfo[$i]['XPlacement'] -= $GPOSinfo[$i]['BaseWidth'];
  4726. }
  4727. }
  4728. // Convert to PDF Text space (thousandths of a unit );
  4729. $XshiftBefore += $GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
  4730. $XshiftAfter += -$GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
  4731. }
  4732. // Kashida from GPOS
  4733. // Kashida is set as an absolute length value, but to adjust text needs to be converted to
  4734. // font-related size
  4735. if (isset($GPOSinfo[$i]['kashida_space']) && $GPOSinfo[$i]['kashida_space']) {
  4736. $kashida = $GPOSinfo[$i]['kashida_space'];
  4737. }
  4738. if ($c == 32) { // word spacing
  4739. $XshiftAfter += $wordspacing;
  4740. }
  4741. if (substr($OTLdata['group'], ($i + 1), 1) !== 'M') { // Don't add inter-character spacing before Marks
  4742. $XshiftAfter += $charspacing;
  4743. }
  4744. // ...applyGPOSpdf...
  4745. // XAdvance from GPOS - Convert to PDF Text space (thousandths of a unit );
  4746. if (((isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] !== 'RTL') || !isset($GPOSinfo[$i]['wDir'])) && isset($GPOSinfo[$i]['XAdvanceL']) && $GPOSinfo[$i]['XAdvanceL']) {
  4747. $XshiftAfter += $GPOSinfo[$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
  4748. } elseif (isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] === 'RTL' && isset($GPOSinfo[$i]['XAdvanceR']) && $GPOSinfo[$i]['XAdvanceR']) {
  4749. $XshiftAfter += $GPOSinfo[$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
  4750. }
  4751. } else { // Character & Word spacing - if NOT OTL
  4752. $XshiftAfter += $charspacing;
  4753. if ($c == 32) {
  4754. $XshiftAfter += $wordspacing;
  4755. }
  4756. }
  4757. // IF Kerning done using pairs rather than OTL
  4758. if ($textvar & TextVars::FC_KERNING) {
  4759. if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {
  4760. $XshiftBefore += $this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];
  4761. }
  4762. }
  4763. if ($YPlacement !== $lastYPlacement) {
  4764. $groupBreak = true;
  4765. }
  4766. if ($XshiftBefore) { // +ve value in PDF moves to the left
  4767. // If Fontstretch is ongoing, need to adjust X adjustments because these will be stretched out.
  4768. $XshiftBefore *= 100 / $last_fontstretch;
  4769. if ($sipset) {
  4770. $tj .= sprintf('>%d<', (-$XshiftBefore));
  4771. } else {
  4772. $tj .= sprintf(')%d(', (-$XshiftBefore));
  4773. }
  4774. }
  4775. // Small-Caps
  4776. if ($smcaps) {
  4777. if (isset($this->upperCase[$c])) {
  4778. $c = $this->upperCase[$c];
  4779. // $this->CurrentFont['subset'][$this->upperCase[$c]] = $this->upperCase[$c]; // add the CAP to subset
  4780. $SmallCapsON = true;
  4781. // For $sipset
  4782. if (!$lastSmallCapsON) { // Turn ON SmallCaps
  4783. $groupBreak = true;
  4784. $fontstretch = $this->smCapsStretch;
  4785. $fontsize = $this->FontSizePt * $this->smCapsScale;
  4786. }
  4787. } else {
  4788. $SmallCapsON = false;
  4789. if ($lastSmallCapsON) { // Turn OFF SmallCaps
  4790. $groupBreak = true;
  4791. $fontstretch = 100;
  4792. $fontsize = $this->FontSizePt;
  4793. }
  4794. }
  4795. }
  4796. // Prepare Text and Select Font ID
  4797. if ($sipset) {
  4798. for ($j = 0; $j < 99; $j++) {
  4799. $init = array_search($c, $this->CurrentFont['subsets'][$j]);
  4800. if ($init !== false) {
  4801. if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
  4802. $groupBreak = true;
  4803. $fontid = $this->CurrentFont['subsetfontids'][$j];
  4804. }
  4805. $tx = sprintf("%02s", strtoupper(dechex($init)));
  4806. break;
  4807. }
  4808. if (count($this->CurrentFont['subsets'][$j]) < 255) {
  4809. $n = count($this->CurrentFont['subsets'][$j]);
  4810. $this->CurrentFont['subsets'][$j][$n] = $c;
  4811. if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
  4812. $groupBreak = true;
  4813. $fontid = $this->CurrentFont['subsetfontids'][$j];
  4814. }
  4815. $tx = sprintf("%02s", strtoupper(dechex($n)));
  4816. break;
  4817. }
  4818. if (!isset($this->CurrentFont['subsets'][($j + 1)])) {
  4819. $this->CurrentFont['subsets'][($j + 1)] = [0 => 0];
  4820. $this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;
  4821. $this->extraFontSubsets++;
  4822. }
  4823. }
  4824. } else {
  4825. $tx = UtfString::code2utf($c);
  4826. if ($this->usingCoreFont) {
  4827. $tx = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $tx);
  4828. } else {
  4829. $tx = $this->writer->utf8ToUtf16BigEndian($tx, false);
  4830. }
  4831. $tx = $this->writer->escape($tx);
  4832. }
  4833. // If any settings require a new Text Group
  4834. if ($groupBreak || $fontstretch != $last_fontstretch) {
  4835. $tj .= $sipset
  4836. ? '>] TJ '
  4837. : ')] TJ ';
  4838. if ($fontid != $last_fontid || $fontsize != $last_fontsize) {
  4839. $tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);
  4840. }
  4841. if ($fontstretch != $last_fontstretch) {
  4842. $tj .= sprintf('%d Tz ', $fontstretch);
  4843. }
  4844. if ($YPlacement != $lastYPlacement) {
  4845. $tj .= sprintf('%.3F Ts ', $YPlacement);
  4846. }
  4847. $tj .= $sipset
  4848. ? '[<'
  4849. : '[(';
  4850. }
  4851. // Output the code for the txt character
  4852. $tj .= $tx;
  4853. $lastSmallCapsON = $SmallCapsON;
  4854. $last_fontid = $fontid;
  4855. $last_fontsize = $fontsize;
  4856. $last_fontstretch = $fontstretch;
  4857. // Kashida
  4858. if ($kashida) {
  4859. $c = 0x0640; // add the Tatweel U+0640
  4860. if (isset($this->CurrentFont['subset'])) {
  4861. $this->CurrentFont['subset'][$c] = $c;
  4862. }
  4863. $kashida *= 1000 / $this->FontSizePt;
  4864. $tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);
  4865. // Get YPlacement from next Base character
  4866. $nextbase = $i + 1;
  4867. while ($OTLdata['group'][$nextbase] !== 'C') {
  4868. $nextbase++;
  4869. }
  4870. if (isset($GPOSinfo[$nextbase]) && isset($GPOSinfo[$nextbase]['YPlacement']) && $GPOSinfo[$nextbase]['YPlacement']) {
  4871. $YPlacement = $GPOSinfo[$nextbase]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];
  4872. }
  4873. // Prepare Text and Select Font ID
  4874. if ($sipset) {
  4875. for ($j = 0; $j < 99; $j++) {
  4876. $init = array_search($c, $this->CurrentFont['subsets'][$j]);
  4877. if ($init !== false) {
  4878. if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
  4879. $fontid = $this->CurrentFont['subsetfontids'][$j];
  4880. }
  4881. $tx = sprintf("%02s", strtoupper(dechex($init)));
  4882. break;
  4883. }
  4884. if (count($this->CurrentFont['subsets'][$j]) < 255) {
  4885. $n = count($this->CurrentFont['subsets'][$j]);
  4886. $this->CurrentFont['subsets'][$j][$n] = $c;
  4887. if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
  4888. $fontid = $this->CurrentFont['subsetfontids'][$j];
  4889. }
  4890. $tx = sprintf("%02s", strtoupper(dechex($n)));
  4891. break;
  4892. }
  4893. if (!isset($this->CurrentFont['subsets'][($j + 1)])) {
  4894. $this->CurrentFont['subsets'][($j + 1)] = [0 => 0];
  4895. $this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;
  4896. $this->extraFontSubsets++;
  4897. }
  4898. }
  4899. } else {
  4900. $tx = UtfString::code2utf($c);
  4901. $tx = $this->writer->utf8ToUtf16BigEndian($tx, false);
  4902. $tx = $this->writer->escape($tx);
  4903. }
  4904. if ($kashida > $tatw) {
  4905. // Insert multiple tatweel characters, repositioning the last one to give correct total length
  4906. $fontstretch = 100;
  4907. $nt = (int) ($kashida / $tatw);
  4908. $nudgeback = (($nt + 1) * $tatw) - $kashida;
  4909. $optx = str_repeat($tx, $nt);
  4910. if ($sipset) {
  4911. $optx .= sprintf('>%d<', ($nudgeback));
  4912. } else {
  4913. $optx .= sprintf(')%d(', ($nudgeback));
  4914. }
  4915. $optx .= $tx; // #last
  4916. } else {
  4917. // Insert single tatweel character and use fontstretch to get correct length
  4918. $fontstretch = ($kashida / $tatw) * 100;
  4919. $optx = $tx;
  4920. }
  4921. $tj .= $sipset
  4922. ? '>] TJ '
  4923. : ')] TJ ';
  4924. if ($fontid != $last_fontid || $fontsize != $last_fontsize) {
  4925. $tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);
  4926. }
  4927. if ($fontstretch != $last_fontstretch) {
  4928. $tj .= sprintf('%d Tz ', $fontstretch);
  4929. }
  4930. $tj .= sprintf('%.3F Ts ', $YPlacement);
  4931. $tj .= $sipset
  4932. ? '[<'
  4933. : '[(';
  4934. // Output the code for the txt character(s)
  4935. $tj .= $optx;
  4936. $last_fontid = $fontid;
  4937. $last_fontstretch = $fontstretch;
  4938. $fontstretch = 100;
  4939. }
  4940. $lastYPlacement = $YPlacement;
  4941. }
  4942. $tj .= $sipset
  4943. ? '>'
  4944. : ')';
  4945. if ($XshiftAfter) {
  4946. $tj .= sprintf('%d', (-$XshiftAfter));
  4947. }
  4948. if ($last_fontid != $original_fontid) {
  4949. $tj .= '] TJ ';
  4950. $tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);
  4951. $tj .= '[';
  4952. }
  4953. $tj = $sipset
  4954. ? preg_replace('/([^\\\])<>/', '\\1 ', $tj)
  4955. : preg_replace('/([^\\\])\(\)/', '\\1 ', $tj);
  4956. return sprintf(' BT ' . $aix . ' 0 Tc 0 Tw [%s] TJ ET ', $x, $y, $tj);
  4957. }
  4958. function _kern($txt, $mode, $aix, $x, $y)
  4959. {
  4960. if ($mode === 'MBTw') { // Multibyte requiring word spacing
  4961. $space = ' ';
  4962. // Convert string to UTF-16BE without BOM
  4963. $space = $this->writer->utf8ToUtf16BigEndian($space, false);
  4964. $space = $this->writer->escape($space);
  4965. $s = sprintf(' BT ' . $aix, $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE);
  4966. $t = explode(' ', $txt);
  4967. foreach ($t as $i => $iValue) {
  4968. $tx = $iValue;
  4969. $tj = '(';
  4970. $unicode = $this->UTF8StringToArray($tx);
  4971. foreach ($unicode as $ti => $tiValue) {
  4972. if ($ti > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$tiValue])) {
  4973. $kern = -$this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$tiValue];
  4974. $tj .= sprintf(')%d(', $kern);
  4975. }
  4976. $tc = UtfString::code2utf($tiValue);
  4977. $tc = $this->writer->utf8ToUtf16BigEndian($tc, false);
  4978. $tj .= $this->writer->escape($tc);
  4979. }
  4980. $tj .= ')';
  4981. $s .= sprintf(' %.3F Tc [%s] TJ', $this->charspacing, $tj);
  4982. if (($i + 1) < count($t)) {
  4983. $s .= sprintf(' %.3F Tc (%s) Tj', $this->ws + $this->charspacing, $space);
  4984. }
  4985. }
  4986. $s .= ' ET ';
  4987. return $s;
  4988. }
  4989. if (!$this->usingCoreFont) {
  4990. $s = '';
  4991. $tj = '(';
  4992. $unicode = $this->UTF8StringToArray($txt);
  4993. foreach ($unicode as $i => $iValue) {
  4994. if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$iValue])) {
  4995. $kern = -$this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$iValue];
  4996. $tj .= sprintf(')%d(', $kern);
  4997. }
  4998. $tx = UtfString::code2utf($iValue);
  4999. $tx = $this->writer->utf8ToUtf16BigEndian($tx, false);
  5000. $tj .= $this->writer->escape($tx);
  5001. }
  5002. $tj .= ')';
  5003. $s .= sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj);
  5004. return $s;
  5005. }
  5006. $s = '';
  5007. $tj = '(';
  5008. $l = strlen($txt);
  5009. for ($i = 0; $i < $l; $i++) {
  5010. if ($i > 0 && isset($this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]])) {
  5011. $kern = -$this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]];
  5012. $tj .= sprintf(')%d(', $kern);
  5013. }
  5014. $tj .= $this->writer->escape($txt[$i]);
  5015. }
  5016. $tj .= ')';
  5017. $s .= sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj);
  5018. return $s;
  5019. }
  5020. function MultiCell(
  5021. $w,
  5022. $h,
  5023. $txt,
  5024. $border = 0,
  5025. $align = '',
  5026. $fill = 0,
  5027. $link = '',
  5028. $directionality = 'ltr',
  5029. $encoded = false,
  5030. $OTLdata = false,
  5031. $maxrows = false
  5032. ) {
  5033. // maxrows is called from mpdfform->TEXTAREA
  5034. // Parameter (pre-)encoded - When called internally from form::textarea -
  5035. // mb_encoding already done and OTL - but not reverse RTL
  5036. if (!$encoded) {
  5037. $txt = $this->purify_utf8_text($txt);
  5038. if ($this->text_input_as_HTML) {
  5039. $txt = $this->all_entities_to_utf8($txt);
  5040. }
  5041. if ($this->usingCoreFont) {
  5042. $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
  5043. }
  5044. if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
  5045. $this->biDirectional = true;
  5046. }
  5047. /* -- OTL -- */
  5048. if (!is_array($OTLdata)) {
  5049. unset($OTLdata);
  5050. }
  5051. // Use OTL OpenType Table Layout - GSUB & GPOS
  5052. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  5053. $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
  5054. $OTLdata = $this->otl->OTLdata;
  5055. }
  5056. if ($directionality == 'rtl' || $this->biDirectional) {
  5057. if (!isset($OTLdata)) {
  5058. $unicode = $this->UTF8StringToArray($txt, false);
  5059. $is_strong = false;
  5060. $this->getBasicOTLdata($OTLdata, $unicode, $is_strong);
  5061. }
  5062. }
  5063. /* -- END OTL -- */
  5064. }
  5065. if (!$align) {
  5066. $align = $this->defaultAlign;
  5067. }
  5068. // Output text with automatic or explicit line breaks
  5069. $cw = &$this->CurrentFont['cw'];
  5070. if ($w == 0) {
  5071. $w = $this->w - $this->rMargin - $this->x;
  5072. }
  5073. $wmax = ($w - ($this->cMarginL + $this->cMarginR));
  5074. if ($this->usingCoreFont) {
  5075. $s = str_replace("\r", '', $txt);
  5076. $nb = strlen($s);
  5077. while ($nb > 0 and $s[$nb - 1] == "\n") {
  5078. $nb--;
  5079. }
  5080. } else {
  5081. $s = str_replace("\r", '', $txt);
  5082. $nb = mb_strlen($s, $this->mb_enc);
  5083. while ($nb > 0 and mb_substr($s, $nb - 1, 1, $this->mb_enc) == "\n") {
  5084. $nb--;
  5085. }
  5086. }
  5087. $b = 0;
  5088. if ($border) {
  5089. if ($border == 1) {
  5090. $border = 'LTRB';
  5091. $b = 'LRT';
  5092. $b2 = 'LR';
  5093. } else {
  5094. $b2 = '';
  5095. if (is_int(strpos($border, 'L'))) {
  5096. $b2 .= 'L';
  5097. }
  5098. if (is_int(strpos($border, 'R'))) {
  5099. $b2 .= 'R';
  5100. }
  5101. $b = is_int(strpos($border, 'T')) ? $b2 . 'T' : $b2;
  5102. }
  5103. }
  5104. $sep = -1;
  5105. $i = 0;
  5106. $j = 0;
  5107. $l = 0;
  5108. $ns = 0;
  5109. $nl = 1;
  5110. $rows = 0;
  5111. $start_y = $this->y;
  5112. if (!$this->usingCoreFont) {
  5113. $inclCursive = false;
  5114. if (preg_match("/([" . $this->pregCURSchars . "])/u", $s)) {
  5115. $inclCursive = true;
  5116. }
  5117. while ($i < $nb) {
  5118. // Get next character
  5119. $c = mb_substr($s, $i, 1, $this->mb_enc);
  5120. if ($c === "\n") { // Explicit line break
  5121. // WORD SPACING
  5122. $this->ResetSpacing();
  5123. $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
  5124. $tmpOTLdata = false;
  5125. /* -- OTL -- */
  5126. if (isset($OTLdata)) {
  5127. $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
  5128. $this->otl->trimOTLdata($tmpOTLdata, false, true);
  5129. $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
  5130. }
  5131. /* -- END OTL -- */
  5132. $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
  5133. if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
  5134. return false;
  5135. }
  5136. $i++;
  5137. $sep = -1;
  5138. $j = $i;
  5139. $l = 0;
  5140. $ns = 0;
  5141. $nl++;
  5142. if ($border and $nl == 2) {
  5143. $b = $b2;
  5144. }
  5145. continue;
  5146. }
  5147. if ($c == " ") {
  5148. $sep = $i;
  5149. $ls = $l;
  5150. $ns++;
  5151. }
  5152. $l += $this->GetCharWidthNonCore($c);
  5153. if ($l > $wmax) {
  5154. // Automatic line break
  5155. if ($sep == -1) { // Only one word
  5156. if ($i == $j) {
  5157. $i++;
  5158. }
  5159. // WORD SPACING
  5160. $this->ResetSpacing();
  5161. $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
  5162. $tmpOTLdata = false;
  5163. /* -- OTL -- */
  5164. if (isset($OTLdata)) {
  5165. $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
  5166. $this->otl->trimOTLdata($tmpOTLdata, false, true);
  5167. $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
  5168. }
  5169. /* -- END OTL -- */
  5170. $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
  5171. } else {
  5172. $tmp = rtrim(mb_substr($s, $j, $sep - $j, $this->mb_enc));
  5173. $tmpOTLdata = false;
  5174. /* -- OTL -- */
  5175. if (isset($OTLdata)) {
  5176. $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $sep - $j);
  5177. $this->otl->trimOTLdata($tmpOTLdata, false, true);
  5178. }
  5179. /* -- END OTL -- */
  5180. if ($align === 'J') {
  5181. // JUSTIFY J using Unicode fonts (Word spacing doesn't work)
  5182. // WORD SPACING UNICODE
  5183. // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
  5184. $tmp = str_replace(chr(194) . chr(160), chr(32), $tmp);
  5185. $len_ligne = $this->GetStringWidth($tmp, false, $tmpOTLdata);
  5186. $nb_carac = mb_strlen($tmp, $this->mb_enc);
  5187. $nb_spaces = mb_substr_count($tmp, ' ', $this->mb_enc);
  5188. // Take off number of Marks
  5189. // Use GPOS OTL
  5190. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'])) {
  5191. if (isset($tmpOTLdata['group']) && $tmpOTLdata['group']) {
  5192. $nb_carac -= substr_count($tmpOTLdata['group'], 'M');
  5193. }
  5194. }
  5195. list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), $inclCursive, $tmpOTLdata);
  5196. $this->SetSpacing($charspacing, $ws);
  5197. }
  5198. if (isset($OTLdata)) {
  5199. $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
  5200. }
  5201. $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
  5202. $i = $sep + 1;
  5203. }
  5204. if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
  5205. return false;
  5206. }
  5207. $sep = -1;
  5208. $j = $i;
  5209. $l = 0;
  5210. $ns = 0;
  5211. $nl++;
  5212. if ($border and $nl == 2) {
  5213. $b = $b2;
  5214. }
  5215. } else {
  5216. $i++;
  5217. }
  5218. }
  5219. // Last chunk
  5220. // WORD SPACING
  5221. $this->ResetSpacing();
  5222. } else {
  5223. while ($i < $nb) {
  5224. // Get next character
  5225. $c = $s[$i];
  5226. if ($c === "\n") {
  5227. // Explicit line break
  5228. // WORD SPACING
  5229. $this->ResetSpacing();
  5230. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
  5231. if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
  5232. return false;
  5233. }
  5234. $i++;
  5235. $sep = -1;
  5236. $j = $i;
  5237. $l = 0;
  5238. $ns = 0;
  5239. $nl++;
  5240. if ($border and $nl == 2) {
  5241. $b = $b2;
  5242. }
  5243. continue;
  5244. }
  5245. if ($c === ' ') {
  5246. $sep = $i;
  5247. $ls = $l;
  5248. $ns++;
  5249. }
  5250. $l += $this->GetCharWidthCore($c);
  5251. if ($l > $wmax) {
  5252. // Automatic line break
  5253. if ($sep == -1) {
  5254. if ($i == $j) {
  5255. $i++;
  5256. }
  5257. // WORD SPACING
  5258. $this->ResetSpacing();
  5259. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
  5260. } else {
  5261. if ($align === 'J') {
  5262. $tmp = rtrim(substr($s, $j, $sep - $j));
  5263. // JUSTIFY J using Unicode fonts (Word spacing doesn't work)
  5264. // WORD SPACING NON_UNICODE/CJK
  5265. // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
  5266. $tmp = str_replace(chr(160), chr(32), $tmp);
  5267. $len_ligne = $this->GetStringWidth($tmp);
  5268. $nb_carac = strlen($tmp);
  5269. $nb_spaces = substr_count($tmp, ' ');
  5270. $tmpOTLdata = [];
  5271. list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), false, $tmpOTLdata);
  5272. $this->SetSpacing($charspacing, $ws);
  5273. }
  5274. $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill, $link);
  5275. $i = $sep + 1;
  5276. }
  5277. if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
  5278. return false;
  5279. }
  5280. $sep = -1;
  5281. $j = $i;
  5282. $l = 0;
  5283. $ns = 0;
  5284. $nl++;
  5285. if ($border and $nl == 2) {
  5286. $b = $b2;
  5287. }
  5288. } else {
  5289. $i++;
  5290. }
  5291. }
  5292. // Last chunk
  5293. // WORD SPACING
  5294. $this->ResetSpacing();
  5295. }
  5296. // Last chunk
  5297. if ($border and is_int(strpos($border, 'B'))) {
  5298. $b .= 'B';
  5299. }
  5300. if (!$this->usingCoreFont) {
  5301. $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
  5302. $tmpOTLdata = false;
  5303. /* -- OTL -- */
  5304. if (isset($OTLdata)) {
  5305. $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
  5306. $this->otl->trimOTLdata($tmpOTLdata, false, true);
  5307. $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
  5308. }
  5309. /* -- END OTL -- */
  5310. $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
  5311. } else {
  5312. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
  5313. }
  5314. $this->x = $this->lMargin;
  5315. }
  5316. /* -- DIRECTW -- */
  5317. function Write($h, $txt, $currentx = 0, $link = '', $directionality = 'ltr', $align = '', $fill = 0)
  5318. {
  5319. if (empty($this->directWrite)) {
  5320. $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
  5321. }
  5322. $this->directWrite->Write($h, $txt, $currentx, $link, $directionality, $align, $fill);
  5323. }
  5324. /* -- END DIRECTW -- */
  5325. /* -- HTML-CSS -- */
  5326. function saveInlineProperties()
  5327. {
  5328. $saved = [];
  5329. $saved['family'] = $this->FontFamily;
  5330. $saved['style'] = $this->FontStyle;
  5331. $saved['sizePt'] = $this->FontSizePt;
  5332. $saved['size'] = $this->FontSize;
  5333. $saved['HREF'] = $this->HREF;
  5334. $saved['textvar'] = $this->textvar; // mPDF 5.7.1
  5335. $saved['OTLtags'] = $this->OTLtags; // mPDF 5.7.1
  5336. $saved['textshadow'] = $this->textshadow;
  5337. $saved['linewidth'] = $this->LineWidth;
  5338. $saved['drawcolor'] = $this->DrawColor;
  5339. $saved['textparam'] = $this->textparam;
  5340. $saved['lSpacingCSS'] = $this->lSpacingCSS;
  5341. $saved['wSpacingCSS'] = $this->wSpacingCSS;
  5342. $saved['I'] = $this->I;
  5343. $saved['B'] = $this->B;
  5344. $saved['colorarray'] = $this->colorarray;
  5345. $saved['bgcolorarray'] = $this->spanbgcolorarray;
  5346. $saved['border'] = $this->spanborddet;
  5347. $saved['color'] = $this->TextColor;
  5348. $saved['bgcolor'] = $this->FillColor;
  5349. $saved['lang'] = $this->currentLang;
  5350. $saved['fontLanguageOverride'] = $this->fontLanguageOverride; // mPDF 5.7.1
  5351. $saved['display_off'] = $this->inlineDisplayOff;
  5352. return $saved;
  5353. }
  5354. function restoreInlineProperties(&$saved)
  5355. {
  5356. $FontFamily = $saved['family'];
  5357. $this->FontStyle = $saved['style'];
  5358. $this->FontSizePt = $saved['sizePt'];
  5359. $this->FontSize = $saved['size'];
  5360. $this->currentLang = $saved['lang'];
  5361. $this->fontLanguageOverride = $saved['fontLanguageOverride']; // mPDF 5.7.1
  5362. $this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well
  5363. $this->HREF = $saved['HREF'];
  5364. $this->textvar = $saved['textvar']; // mPDF 5.7.1
  5365. $this->OTLtags = $saved['OTLtags']; // mPDF 5.7.1
  5366. $this->textshadow = $saved['textshadow'];
  5367. $this->LineWidth = $saved['linewidth'];
  5368. $this->DrawColor = $saved['drawcolor'];
  5369. $this->textparam = $saved['textparam'];
  5370. $this->inlineDisplayOff = $saved['display_off'];
  5371. $this->lSpacingCSS = $saved['lSpacingCSS'];
  5372. if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
  5373. $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize);
  5374. } else {
  5375. $this->fixedlSpacing = false;
  5376. }
  5377. $this->wSpacingCSS = $saved['wSpacingCSS'];
  5378. if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
  5379. $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize);
  5380. } else {
  5381. $this->minwSpacing = 0;
  5382. }
  5383. $this->SetFont($FontFamily, $saved['style'], $saved['sizePt'], false);
  5384. $this->currentfontstyle = $saved['style'];
  5385. $this->currentfontsize = $saved['sizePt'];
  5386. $this->SetStylesArray(['B' => $saved['B'], 'I' => $saved['I']]); // mPDF 5.7.1
  5387. $this->TextColor = $saved['color'];
  5388. $this->FillColor = $saved['bgcolor'];
  5389. $this->colorarray = $saved['colorarray'];
  5390. $cor = $saved['colorarray'];
  5391. if ($cor) {
  5392. $this->SetTColor($cor);
  5393. }
  5394. $this->spanbgcolorarray = $saved['bgcolorarray'];
  5395. $cor = $saved['bgcolorarray'];
  5396. if ($cor) {
  5397. $this->SetFColor($cor);
  5398. }
  5399. $this->spanborddet = $saved['border'];
  5400. }
  5401. // Used when ColActive for tables - updated to return first block with background fill OR borders
  5402. function GetFirstBlockFill()
  5403. {
  5404. // Returns the first blocklevel that uses a bgcolor fill
  5405. $startfill = 0;
  5406. for ($i = 1; $i <= $this->blklvl; $i++) {
  5407. 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']) {
  5408. $startfill = $i;
  5409. break;
  5410. }
  5411. }
  5412. return $startfill;
  5413. }
  5414. // -------------------------FLOWING BLOCK------------------------------------//
  5415. // The following functions were originally written by Damon Kohler //
  5416. // --------------------------------------------------------------------------//
  5417. function saveFont()
  5418. {
  5419. $saved = [];
  5420. $saved['family'] = $this->FontFamily;
  5421. $saved['style'] = $this->FontStyle;
  5422. $saved['sizePt'] = $this->FontSizePt;
  5423. $saved['size'] = $this->FontSize;
  5424. $saved['curr'] = &$this->CurrentFont;
  5425. $saved['lang'] = $this->currentLang; // mPDF 6
  5426. $saved['color'] = $this->TextColor;
  5427. $saved['spanbgcolor'] = $this->spanbgcolor;
  5428. $saved['spanbgcolorarray'] = $this->spanbgcolorarray;
  5429. $saved['bord'] = $this->spanborder;
  5430. $saved['border'] = $this->spanborddet;
  5431. $saved['HREF'] = $this->HREF;
  5432. $saved['textvar'] = $this->textvar; // mPDF 5.7.1
  5433. $saved['textshadow'] = $this->textshadow;
  5434. $saved['linewidth'] = $this->LineWidth;
  5435. $saved['drawcolor'] = $this->DrawColor;
  5436. $saved['textparam'] = $this->textparam;
  5437. $saved['ReqFontStyle'] = $this->ReqFontStyle;
  5438. $saved['fixedlSpacing'] = $this->fixedlSpacing;
  5439. $saved['minwSpacing'] = $this->minwSpacing;
  5440. return $saved;
  5441. }
  5442. function restoreFont(&$saved, $write = true)
  5443. {
  5444. if (!isset($saved) || empty($saved)) {
  5445. return;
  5446. }
  5447. $this->FontFamily = $saved['family'];
  5448. $this->FontStyle = $saved['style'];
  5449. $this->FontSizePt = $saved['sizePt'];
  5450. $this->FontSize = $saved['size'];
  5451. $this->CurrentFont = &$saved['curr'];
  5452. $this->currentLang = $saved['lang']; // mPDF 6
  5453. $this->TextColor = $saved['color'];
  5454. $this->spanbgcolor = $saved['spanbgcolor'];
  5455. $this->spanbgcolorarray = $saved['spanbgcolorarray'];
  5456. $this->spanborder = $saved['bord'];
  5457. $this->spanborddet = $saved['border'];
  5458. $this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well
  5459. $this->HREF = $saved['HREF'];
  5460. $this->fixedlSpacing = $saved['fixedlSpacing'];
  5461. $this->minwSpacing = $saved['minwSpacing'];
  5462. $this->textvar = $saved['textvar']; // mPDF 5.7.1
  5463. $this->textshadow = $saved['textshadow'];
  5464. $this->LineWidth = $saved['linewidth'];
  5465. $this->DrawColor = $saved['drawcolor'];
  5466. $this->textparam = $saved['textparam'];
  5467. if ($write) {
  5468. $this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], true, true); // force output
  5469. $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  5470. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
  5471. $this->writer->write($fontout);
  5472. }
  5473. $this->pageoutput[$this->page]['Font'] = $fontout;
  5474. } else {
  5475. $this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], false);
  5476. }
  5477. $this->ReqFontStyle = $saved['ReqFontStyle'];
  5478. }
  5479. function newFlowingBlock($w, $h, $a = '', $is_table = false, $blockstate = 0, $newblock = true, $blockdir = 'ltr', $table_draft = false)
  5480. {
  5481. if (!$a) {
  5482. if ($blockdir == 'rtl') {
  5483. $a = 'R';
  5484. } else {
  5485. $a = 'L';
  5486. }
  5487. }
  5488. $this->flowingBlockAttr['width'] = ($w * Mpdf::SCALE);
  5489. // line height in user units
  5490. $this->flowingBlockAttr['is_table'] = $is_table;
  5491. $this->flowingBlockAttr['table_draft'] = $table_draft;
  5492. $this->flowingBlockAttr['height'] = $h;
  5493. $this->flowingBlockAttr['lineCount'] = 0;
  5494. $this->flowingBlockAttr['align'] = $a;
  5495. $this->flowingBlockAttr['font'] = [];
  5496. $this->flowingBlockAttr['content'] = [];
  5497. $this->flowingBlockAttr['contentB'] = [];
  5498. $this->flowingBlockAttr['contentWidth'] = 0;
  5499. $this->flowingBlockAttr['blockstate'] = $blockstate;
  5500. $this->flowingBlockAttr['newblock'] = $newblock;
  5501. $this->flowingBlockAttr['valign'] = 'M';
  5502. $this->flowingBlockAttr['blockdir'] = $blockdir;
  5503. $this->flowingBlockAttr['cOTLdata'] = []; // mPDF 5.7.1
  5504. $this->flowingBlockAttr['lastBidiText'] = ''; // mPDF 5.7.1
  5505. if (!empty($this->otl)) {
  5506. $this->otl->lastBidiStrongType = '';
  5507. } // *OTL*
  5508. }
  5509. function finishFlowingBlock($endofblock = false, $next = '')
  5510. {
  5511. $currentx = $this->x;
  5512. // prints out the last chunk
  5513. $is_table = $this->flowingBlockAttr['is_table'];
  5514. $table_draft = $this->flowingBlockAttr['table_draft'];
  5515. $maxWidth = & $this->flowingBlockAttr['width'];
  5516. $stackHeight = & $this->flowingBlockAttr['height'];
  5517. $align = & $this->flowingBlockAttr['align'];
  5518. $content = & $this->flowingBlockAttr['content'];
  5519. $contentB = & $this->flowingBlockAttr['contentB'];
  5520. $font = & $this->flowingBlockAttr['font'];
  5521. $contentWidth = & $this->flowingBlockAttr['contentWidth'];
  5522. $lineCount = & $this->flowingBlockAttr['lineCount'];
  5523. $valign = & $this->flowingBlockAttr['valign'];
  5524. $blockstate = $this->flowingBlockAttr['blockstate'];
  5525. $cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1
  5526. $newblock = $this->flowingBlockAttr['newblock'];
  5527. $blockdir = $this->flowingBlockAttr['blockdir'];
  5528. // *********** BLOCK BACKGROUND COLOR *****************//
  5529. if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {
  5530. $fill = 0;
  5531. } else {
  5532. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  5533. $fill = 0;
  5534. }
  5535. $hanger = '';
  5536. // Always right trim!
  5537. // Right trim last content and adjust width if needed to justify (later)
  5538. if (isset($content[count($content) - 1]) && preg_match('/[ ]+$/', $content[count($content) - 1], $m)) {
  5539. $strip = strlen($m[0]);
  5540. $content[count($content) - 1] = substr($content[count($content) - 1], 0, (strlen($content[count($content) - 1]) - $strip));
  5541. /* -- OTL -- */
  5542. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  5543. $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true);
  5544. }
  5545. /* -- END OTL -- */
  5546. }
  5547. // the amount of space taken up so far in user units
  5548. $usedWidth = 0;
  5549. // COLS
  5550. $oldcolumn = $this->CurrCol;
  5551. if ($this->ColActive && !$is_table) {
  5552. $this->breakpoints[$this->CurrCol][] = $this->y;
  5553. } // *COLUMNS*
  5554. // Print out each chunk
  5555. /* -- TABLES -- */
  5556. if ($is_table) {
  5557. $ipaddingL = 0;
  5558. $ipaddingR = 0;
  5559. $paddingL = 0;
  5560. $paddingR = 0;
  5561. } else {
  5562. /* -- END TABLES -- */
  5563. $ipaddingL = $this->blk[$this->blklvl]['padding_left'];
  5564. $ipaddingR = $this->blk[$this->blklvl]['padding_right'];
  5565. $paddingL = ($ipaddingL * Mpdf::SCALE);
  5566. $paddingR = ($ipaddingR * Mpdf::SCALE);
  5567. $this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];
  5568. $this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];
  5569. // Added mPDF 3.0 Float DIV
  5570. $fpaddingR = 0;
  5571. $fpaddingL = 0;
  5572. /* -- CSS-FLOAT -- */
  5573. if (count($this->floatDivs)) {
  5574. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  5575. if ($r_exists) {
  5576. $fpaddingR = $r_width;
  5577. }
  5578. if ($l_exists) {
  5579. $fpaddingL = $l_width;
  5580. }
  5581. }
  5582. /* -- END CSS-FLOAT -- */
  5583. $usey = $this->y + 0.002;
  5584. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
  5585. $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  5586. }
  5587. /* -- CSS-IMAGE-FLOAT -- */
  5588. // If float exists at this level
  5589. if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
  5590. $fpaddingR += $this->floatmargins['R']['w'];
  5591. }
  5592. if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
  5593. $fpaddingL += $this->floatmargins['L']['w'];
  5594. }
  5595. /* -- END CSS-IMAGE-FLOAT -- */
  5596. } // *TABLES*
  5597. $lineBox = [];
  5598. $this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);
  5599. if ($is_table && count($content) == 0) {
  5600. $stackHeight = 0;
  5601. }
  5602. if ($table_draft) {
  5603. $this->y += $stackHeight;
  5604. $this->objectbuffer = [];
  5605. return 0;
  5606. }
  5607. // While we're at it, check if contains cursive text
  5608. // Change NBSP to SPACE.
  5609. // Re-calculate contentWidth
  5610. $contentWidth = 0;
  5611. foreach ($content as $k => $chunk) {
  5612. $this->restoreFont($font[$k], false);
  5613. if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
  5614. // Soft Hyphens chr(173)
  5615. if (!$this->usingCoreFont) {
  5616. /* -- OTL -- */
  5617. // mPDF 5.7.1
  5618. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  5619. $this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad");
  5620. $this->otl->replaceSpace($chunk, $cOTLdata[$k]);
  5621. $content[$k] = $chunk;
  5622. } /* -- END OTL -- */ else { // *OTL*
  5623. $content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk);
  5624. $content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);
  5625. } // *OTL*
  5626. } elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  5627. $content[$k] = $chunk = str_replace(chr(173), '', $chunk);
  5628. $content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);
  5629. }
  5630. $contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE;
  5631. } elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
  5632. // LIST MARKERS // mPDF 6 Lists
  5633. if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
  5634. // do nothing
  5635. } else {
  5636. $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
  5637. }
  5638. }
  5639. }
  5640. if (isset($font[count($font) - 1])) {
  5641. $lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');
  5642. $lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');
  5643. } else {
  5644. $lastfontreqstyle = null;
  5645. $lastfontstyle = null;
  5646. }
  5647. if ($blockdir == 'ltr' && $lastfontreqstyle && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic
  5648. $lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE;
  5649. } else {
  5650. $lastitalic = 0;
  5651. }
  5652. // Get PAGEBREAK TO TEST for height including the bottom border/padding
  5653. $check_h = max($this->divheight, $stackHeight);
  5654. // This fixes a proven bug...
  5655. if ($endofblock && $newblock && $blockstate == 0 && !$content) {
  5656. $check_h = 0;
  5657. }
  5658. // but ? needs to fix potentially more widespread...
  5659. // if (!$content) { $check_h = 0; }
  5660. if ($this->blklvl > 0 && !$is_table) {
  5661. if ($endofblock && $blockstate > 1) {
  5662. if ($this->blk[$this->blklvl]['page_break_after_avoid']) {
  5663. $check_h += $stackHeight;
  5664. }
  5665. $check_h += ($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);
  5666. }
  5667. if (($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0) || ($endofblock && $blockstate == 3 && $lineCount == 0)) {
  5668. $check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);
  5669. }
  5670. }
  5671. // Force PAGE break if column height cannot take check-height
  5672. if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {
  5673. $this->SetCol($this->NbCol - 1);
  5674. }
  5675. // Avoid just border/background-color moved on to next page
  5676. if ($endofblock && $blockstate > 1 && !$content) {
  5677. $buff = $this->margBuffer;
  5678. } else {
  5679. $buff = 0;
  5680. }
  5681. // PAGEBREAK
  5682. if (!$is_table && ($this->y + $check_h) > ($this->PageBreakTrigger + $buff) and ! $this->InFooter and $this->AcceptPageBreak()) {
  5683. $bak_x = $this->x; // Current X position
  5684. // WORD SPACING
  5685. $ws = $this->ws; // Word Spacing
  5686. $charspacing = $this->charspacing; // Character Spacing
  5687. $this->ResetSpacing();
  5688. $this->AddPage($this->CurOrientation);
  5689. $this->x = $bak_x;
  5690. // Added to correct for OddEven Margins
  5691. $currentx += $this->MarginCorrection;
  5692. $this->x += $this->MarginCorrection;
  5693. // WORD SPACING
  5694. $this->SetSpacing($charspacing, $ws);
  5695. }
  5696. /* -- COLUMNS -- */
  5697. // COLS
  5698. // COLUMN CHANGE
  5699. if ($this->CurrCol != $oldcolumn) {
  5700. $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  5701. $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  5702. $oldcolumn = $this->CurrCol;
  5703. }
  5704. if ($this->ColActive && !$is_table) {
  5705. $this->breakpoints[$this->CurrCol][] = $this->y;
  5706. }
  5707. /* -- END COLUMNS -- */
  5708. // TOP MARGIN
  5709. if ($newblock && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && $lineCount == 0 && !$is_table) {
  5710. $this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
  5711. if ($this->ColActive) {
  5712. $this->breakpoints[$this->CurrCol][] = $this->y;
  5713. } // *COLUMNS*
  5714. }
  5715. if ($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0 && !$is_table) {
  5716. $this->blk[$this->blklvl]['y0'] = $this->y;
  5717. $this->blk[$this->blklvl]['startpage'] = $this->page;
  5718. if ($this->blk[$this->blklvl]['float']) {
  5719. $this->blk[$this->blklvl]['float_start_y'] = $this->y;
  5720. }
  5721. if ($this->ColActive) {
  5722. $this->breakpoints[$this->CurrCol][] = $this->y;
  5723. } // *COLUMNS*
  5724. }
  5725. // Paragraph INDENT
  5726. $WidthCorrection = 0;
  5727. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
  5728. $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
  5729. $WidthCorrection = ($ti * Mpdf::SCALE);
  5730. }
  5731. // PADDING and BORDER spacing/fill
  5732. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 0) && (!$is_table)) {
  5733. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  5734. $this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);
  5735. if ($this->ColActive) {
  5736. $this->breakpoints[$this->CurrCol][] = $this->y;
  5737. } // *COLUMNS*
  5738. $this->x = $currentx;
  5739. }
  5740. // Added mPDF 3.0 Float DIV
  5741. $fpaddingR = 0;
  5742. $fpaddingL = 0;
  5743. /* -- CSS-FLOAT -- */
  5744. if (count($this->floatDivs)) {
  5745. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  5746. if ($r_exists) {
  5747. $fpaddingR = $r_width;
  5748. }
  5749. if ($l_exists) {
  5750. $fpaddingL = $l_width;
  5751. }
  5752. }
  5753. /* -- END CSS-FLOAT -- */
  5754. $usey = $this->y + 0.002;
  5755. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
  5756. $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  5757. }
  5758. /* -- CSS-IMAGE-FLOAT -- */
  5759. // If float exists at this level
  5760. if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
  5761. $fpaddingR += $this->floatmargins['R']['w'];
  5762. }
  5763. if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
  5764. $fpaddingL += $this->floatmargins['L']['w'];
  5765. }
  5766. /* -- END CSS-IMAGE-FLOAT -- */
  5767. if ($content) {
  5768. // In FinishFlowing Block no lines are justified as it is always last line
  5769. // but if CJKorphan has allowed content width to go over max width, use J charspacing to compress line
  5770. // JUSTIFICATION J - NOT!
  5771. $nb_carac = 0;
  5772. $nb_spaces = 0;
  5773. $jcharspacing = 0;
  5774. $jkashida = 0;
  5775. $jws = 0;
  5776. $inclCursive = false;
  5777. $dottab = false;
  5778. foreach ($content as $k => $chunk) {
  5779. if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
  5780. $nb_carac += mb_strlen($chunk, $this->mb_enc);
  5781. $nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);
  5782. // mPDF 6
  5783. // Use GPOS OTL
  5784. $this->restoreFont($font[$k], false);
  5785. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  5786. if (isset($cOTLdata[$k]['group']) && $cOTLdata[$k]['group']) {
  5787. $nb_marks = substr_count($cOTLdata[$k]['group'], 'M');
  5788. $nb_carac -= $nb_marks;
  5789. }
  5790. if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) {
  5791. $inclCursive = true;
  5792. }
  5793. }
  5794. } else {
  5795. $nb_carac ++; // mPDF 6 allow spacing for inline object
  5796. if ($this->objectbuffer[$k]['type'] == 'dottab') {
  5797. $dottab = $this->objectbuffer[$k]['outdent'];
  5798. }
  5799. }
  5800. }
  5801. // DIRECTIONALITY RTL
  5802. $chunkorder = range(0, count($content) - 1); // mPDF 6
  5803. /* -- OTL -- */
  5804. // mPDF 6
  5805. if ($blockdir == 'rtl' || $this->biDirectional) {
  5806. $this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);
  5807. // From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to
  5808. // $this->objectbuffer and $font ($chunkorder contains the mapping)
  5809. }
  5810. /* -- END OTL -- */
  5811. // Remove any XAdvance from OTL data at end of line
  5812. // And correct for XPlacement on last character
  5813. // BIDI is applied
  5814. foreach ($chunkorder as $aord => $k) {
  5815. if (count($cOTLdata)) {
  5816. $this->restoreFont($font[$k], false);
  5817. // ...FinishFlowingBlock...
  5818. if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line
  5819. $nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character
  5820. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {
  5821. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {
  5822. $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
  5823. } else {
  5824. $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
  5825. }
  5826. $w *= ($this->FontSize / 1000);
  5827. $contentWidth -= $w * Mpdf::SCALE;
  5828. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;
  5829. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;
  5830. }
  5831. // If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it
  5832. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {
  5833. $w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
  5834. $w *= ($this->FontSize / 1000);
  5835. $contentWidth -= $w * Mpdf::SCALE;
  5836. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
  5837. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
  5838. }
  5839. }
  5840. }
  5841. }
  5842. // 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)
  5843. // If "orphans" in fact is just a final space - ignore this
  5844. $lastchar = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);
  5845. if (preg_match("/[" . $this->CJKoverflow . "]/u", $lastchar)) {
  5846. $CJKoverflow = true;
  5847. } else {
  5848. $CJKoverflow = false;
  5849. }
  5850. if ((((($contentWidth + $lastitalic) > $maxWidth) && ($content[(count($chunkorder) - 1)] != ' ') ) ||
  5851. (!$endofblock && $align == 'J' && ($next == 'image' || $next == 'select' || $next == 'input' || $next == 'textarea' || ($next == 'br' && $this->justifyB4br)))) && !($CJKoverflow && $this->allowCJKoverflow)) {
  5852. // WORD SPACING
  5853. 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);
  5854. } /* -- CJK-FONTS -- */ elseif ($this->checkCJK && $align == 'J' && $CJKoverflow && $this->allowCJKoverflow && $this->CJKforceend) {
  5855. // force-end overhang
  5856. $hanger = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);
  5857. if (preg_match("/[" . $this->CJKoverflow . "]/u", $hanger)) {
  5858. $content[(count($chunkorder) - 1)] = mb_substr($content[(count($chunkorder) - 1)], 0, mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, $this->mb_enc);
  5859. $this->restoreFont($font[$chunkorder[count($chunkorder) - 1]], false);
  5860. $contentWidth -= $this->GetStringWidth($hanger) * Mpdf::SCALE;
  5861. $nb_carac -= 1;
  5862. 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);
  5863. }
  5864. } /* -- END CJK-FONTS -- */
  5865. // Check if will fit at word/char spacing of previous line - if so continue it
  5866. // but only allow a maximum of $this->jSmaxWordLast and $this->jSmaxCharLast
  5867. elseif ($contentWidth < ($maxWidth - $lastitalic - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE))) && !$this->fixedlSpacing) {
  5868. if ($this->ws > $this->jSmaxWordLast) {
  5869. $jws = $this->jSmaxWordLast;
  5870. }
  5871. if ($this->charspacing > $this->jSmaxCharLast) {
  5872. $jcharspacing = $this->jSmaxCharLast;
  5873. }
  5874. $check = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) - ( $jcharspacing * $nb_carac) - ( $jws * $nb_spaces);
  5875. if ($check <= 0) {
  5876. $jcharspacing = 0;
  5877. $jws = 0;
  5878. }
  5879. }
  5880. $empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) );
  5881. $empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1
  5882. $empty -= ($jws * $nb_spaces);
  5883. $empty -= ($jkashida);
  5884. $empty /= Mpdf::SCALE;
  5885. if (!$is_table) {
  5886. $this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin'] - $empty));
  5887. $this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $empty));
  5888. }
  5889. $arraysize = count($chunkorder);
  5890. $margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );
  5891. if (!$is_table) {
  5892. $this->DivLn($stackHeight, $this->blklvl, false);
  5893. } // false -> don't advance y
  5894. $this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;
  5895. if ($dottab !== false && $blockdir == 'rtl') {
  5896. $this->x -= $dottab;
  5897. } elseif ($align == 'R') {
  5898. $this->x += $empty;
  5899. } elseif ($align == 'J' && $blockdir == 'rtl') {
  5900. $this->x += $empty;
  5901. } elseif ($align == 'C') {
  5902. $this->x += ($empty / 2);
  5903. }
  5904. // Paragraph INDENT
  5905. $WidthCorrection = 0;
  5906. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
  5907. $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
  5908. if ($blockdir != 'rtl') {
  5909. $this->x += $ti;
  5910. } // mPDF 6
  5911. }
  5912. foreach ($chunkorder as $aord => $k) { // mPDF 5.7
  5913. $chunk = $content[$aord];
  5914. if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
  5915. $xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];
  5916. $this->objectbuffer[$k]['OUTER-X'] += $xadj;
  5917. $this->objectbuffer[$k]['BORDER-X'] += $xadj;
  5918. $this->objectbuffer[$k]['INNER-X'] += $xadj;
  5919. if ($this->objectbuffer[$k]['type'] == 'listmarker') {
  5920. $this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin
  5921. }
  5922. $yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];
  5923. if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
  5924. $this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin
  5925. }
  5926. if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB
  5927. $yadj += $lineBox[$k]['top'];
  5928. }
  5929. $this->objectbuffer[$k]['OUTER-Y'] += $yadj;
  5930. $this->objectbuffer[$k]['BORDER-Y'] += $yadj;
  5931. $this->objectbuffer[$k]['INNER-Y'] += $yadj;
  5932. }
  5933. $this->restoreFont($font[$k]); // mPDF 5.7
  5934. if ($is_table && substr($align, 0, 1) == 'D' && $aord == 0) {
  5935. $dp = $this->decimal_align[substr($align, 0, 2)];
  5936. $s = preg_split('/' . preg_quote($dp, '/') . '/', $content[0], 2); // ? needs to be /u if not core
  5937. $s0 = $this->GetStringWidth($s[0], false);
  5938. $this->x += ($this->decimal_offset - $s0);
  5939. }
  5940. $this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws);
  5941. $this->fixedlSpacing = false;
  5942. $this->minwSpacing = 0;
  5943. $save_vis = $this->visibility;
  5944. if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {
  5945. $this->SetVisibility($this->textparam['visibility']);
  5946. }
  5947. // *********** SPAN BACKGROUND COLOR ***************** //
  5948. if (isset($this->spanbgcolor) && $this->spanbgcolor) {
  5949. $cor = $this->spanbgcolorarray;
  5950. $this->SetFColor($cor);
  5951. $save_fill = $fill;
  5952. $spanfill = 1;
  5953. $fill = 1;
  5954. }
  5955. if (!empty($this->spanborddet)) {
  5956. if (strpos($contentB[$k], 'L') !== false && isset($this->spanborddet['L'])) {
  5957. $this->x += $this->spanborddet['L']['w'];
  5958. }
  5959. if (strpos($contentB[$k], 'L') === false) {
  5960. $this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;
  5961. }
  5962. if (strpos($contentB[$k], 'R') === false) {
  5963. $this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;
  5964. }
  5965. }
  5966. // WORD SPACING
  5967. // mPDF 5.7.1
  5968. $stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar);
  5969. $nch = mb_strlen($chunk, $this->mb_enc);
  5970. // Use GPOS OTL
  5971. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  5972. if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
  5973. $nch -= substr_count($cOTLdata[$aord]['group'], 'M');
  5974. }
  5975. }
  5976. $stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE );
  5977. $stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE );
  5978. if (isset($this->objectbuffer[$k])) {
  5979. if ($this->objectbuffer[$k]['type'] == 'dottab') {
  5980. $this->objectbuffer[$k]['OUTER-WIDTH'] +=$empty;
  5981. $this->objectbuffer[$k]['OUTER-WIDTH'] +=$this->objectbuffer[$k]['outdent'];
  5982. }
  5983. // LIST MARKERS // mPDF 6 Lists
  5984. if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
  5985. // do nothing
  5986. } else {
  5987. $stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];
  5988. }
  5989. }
  5990. if ($stringWidth == 0) {
  5991. $stringWidth = 0.000001;
  5992. }
  5993. if ($aord == $arraysize - 1) { // mPDF 5.7
  5994. // mPDF 5.7.1
  5995. if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {
  5996. // force-end overhang
  5997. $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
  5998. $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
  5999. } else {
  6000. $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
  6001. }
  6002. } else {
  6003. $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
  6004. }
  6005. if (!empty($this->spanborddet)) {
  6006. if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) {
  6007. $this->x += $this->spanborddet['R']['w'];
  6008. }
  6009. }
  6010. // *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //
  6011. if (isset($spanfill) && $spanfill) {
  6012. $fill = $save_fill;
  6013. $spanfill = 0;
  6014. if ($fill) {
  6015. $this->SetFColor($bcor);
  6016. }
  6017. }
  6018. if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {
  6019. $this->SetVisibility($save_vis);
  6020. }
  6021. }
  6022. $this->printobjectbuffer($is_table, $blockdir);
  6023. $this->objectbuffer = [];
  6024. $this->ResetSpacing();
  6025. } // END IF CONTENT
  6026. /* -- CSS-IMAGE-FLOAT -- */
  6027. // Update values if set to skipline
  6028. if ($this->floatmargins) {
  6029. $this->_advanceFloatMargins();
  6030. }
  6031. if ($endofblock && $blockstate > 1) {
  6032. // If float exists at this level
  6033. if (isset($this->floatmargins['R']['y1'])) {
  6034. $fry1 = $this->floatmargins['R']['y1'];
  6035. } else {
  6036. $fry1 = 0;
  6037. }
  6038. if (isset($this->floatmargins['L']['y1'])) {
  6039. $fly1 = $this->floatmargins['L']['y1'];
  6040. } else {
  6041. $fly1 = 0;
  6042. }
  6043. if ($this->y < $fry1 || $this->y < $fly1) {
  6044. $drop = max($fry1, $fly1) - $this->y;
  6045. $this->DivLn($drop);
  6046. $this->x = $currentx;
  6047. }
  6048. }
  6049. /* -- END CSS-IMAGE-FLOAT -- */
  6050. // PADDING and BORDER spacing/fill
  6051. 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)) {
  6052. // If CSS height set, extend bottom - if on same page as block started, and CSS HEIGHT > actual height,
  6053. // and does not force pagebreak
  6054. $extra = 0;
  6055. if (isset($this->blk[$this->blklvl]['css_set_height']) && $this->blk[$this->blklvl]['css_set_height'] && $this->blk[$this->blklvl]['startpage'] == $this->page) {
  6056. // predicted height
  6057. $h1 = ($this->y - $this->blk[$this->blklvl]['y0']) + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];
  6058. if ($h1 < ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top'])) {
  6059. $extra = ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top']) - $h1;
  6060. }
  6061. if ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra > $this->PageBreakTrigger) {
  6062. $extra = $this->PageBreakTrigger - ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);
  6063. }
  6064. }
  6065. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  6066. $this->DivLn($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra, -3, true, false, 2);
  6067. $this->x = $currentx;
  6068. if ($this->ColActive) {
  6069. $this->breakpoints[$this->CurrCol][] = $this->y;
  6070. } // *COLUMNS*
  6071. }
  6072. // SET Bottom y1 of block (used for painting borders)
  6073. if (($endofblock) && ($blockstate > 1) && (!$is_table)) {
  6074. $this->blk[$this->blklvl]['y1'] = $this->y;
  6075. }
  6076. // BOTTOM MARGIN
  6077. if (($endofblock) && ($blockstate > 1) && ($this->blk[$this->blklvl]['margin_bottom']) && (!$is_table)) {
  6078. if ($this->y + $this->blk[$this->blklvl]['margin_bottom'] < $this->PageBreakTrigger and ! $this->InFooter) {
  6079. $this->DivLn($this->blk[$this->blklvl]['margin_bottom'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
  6080. if ($this->ColActive) {
  6081. $this->breakpoints[$this->CurrCol][] = $this->y;
  6082. } // *COLUMNS*
  6083. }
  6084. }
  6085. // Reset lineheight
  6086. $stackHeight = $this->divheight;
  6087. }
  6088. function printobjectbuffer($is_table = false, $blockdir = false)
  6089. {
  6090. if (!$blockdir) {
  6091. $blockdir = $this->directionality;
  6092. }
  6093. if ($is_table && $this->shrin_k > 1) {
  6094. $k = $this->shrin_k;
  6095. } else {
  6096. $k = 1;
  6097. }
  6098. $save_y = $this->y;
  6099. $save_x = $this->x;
  6100. $save_currentfontfamily = $this->FontFamily;
  6101. $save_currentfontsize = $this->FontSizePt;
  6102. $save_currentfontstyle = $this->FontStyle;
  6103. if ($blockdir == 'rtl') {
  6104. $rtlalign = 'R';
  6105. } else {
  6106. $rtlalign = 'L';
  6107. }
  6108. foreach ($this->objectbuffer as $ib => $objattr) {
  6109. if ($objattr['type'] == 'bookmark' || $objattr['type'] == 'indexentry' || $objattr['type'] == 'toc') {
  6110. $x = $objattr['OUTER-X'];
  6111. $y = $objattr['OUTER-Y'];
  6112. $this->y = $y - $this->FontSize / 2;
  6113. $this->x = $x;
  6114. if ($objattr['type'] == 'bookmark') {
  6115. $this->Bookmark($objattr['CONTENT'], $objattr['bklevel'], $y - $this->FontSize);
  6116. } // *BOOKMARKS*
  6117. if ($objattr['type'] == 'indexentry') {
  6118. $this->IndexEntry($objattr['CONTENT']);
  6119. } // *INDEX*
  6120. if ($objattr['type'] == 'toc') {
  6121. $this->TOC_Entry($objattr['CONTENT'], $objattr['toclevel'], (isset($objattr['toc_id']) ? $objattr['toc_id'] : ''));
  6122. } // *TOC*
  6123. } /* -- ANNOTATIONS -- */ elseif ($objattr['type'] == 'annot') {
  6124. if ($objattr['POS-X']) {
  6125. $x = $objattr['POS-X'];
  6126. } elseif ($this->annotMargin <> 0) {
  6127. $x = -$objattr['OUTER-X'];
  6128. } else {
  6129. $x = $objattr['OUTER-X'];
  6130. }
  6131. if ($objattr['POS-Y']) {
  6132. $y = $objattr['POS-Y'];
  6133. } else {
  6134. $y = $objattr['OUTER-Y'] - $this->FontSize / 2;
  6135. }
  6136. // Create a dummy entry in the _out/columnBuffer with position sensitive data,
  6137. // linking $y-1 in the Columnbuffer with entry in $this->columnAnnots
  6138. // and when columns are split in length will not break annotation from current line
  6139. $this->y = $y - 1;
  6140. $this->x = $x - 1;
  6141. $this->Line($x - 1, $y - 1, $x - 1, $y - 1);
  6142. $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'] : ''));
  6143. } /* -- END ANNOTATIONS -- */ else {
  6144. $y = $objattr['OUTER-Y'];
  6145. $x = $objattr['OUTER-X'];
  6146. $w = $objattr['OUTER-WIDTH'];
  6147. $h = $objattr['OUTER-HEIGHT'];
  6148. if (isset($objattr['text'])) {
  6149. $texto = $objattr['text'];
  6150. }
  6151. $this->y = $y;
  6152. $this->x = $x;
  6153. if (isset($objattr['fontfamily'])) {
  6154. $this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);
  6155. }
  6156. }
  6157. // HR
  6158. if ($objattr['type'] == 'hr') {
  6159. $this->SetDColor($objattr['color']);
  6160. switch ($objattr['align']) {
  6161. case 'C':
  6162. $empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];
  6163. $empty /= 2;
  6164. $x += $empty;
  6165. break;
  6166. case 'R':
  6167. $empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];
  6168. $x += $empty;
  6169. break;
  6170. }
  6171. $oldlinewidth = $this->LineWidth;
  6172. $this->SetLineWidth($objattr['linewidth'] / $k);
  6173. $this->y += ($objattr['linewidth'] / 2) + $objattr['margin_top'] / $k;
  6174. $this->Line($x, $this->y, $x + $objattr['INNER-WIDTH'], $this->y);
  6175. $this->SetLineWidth($oldlinewidth);
  6176. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  6177. }
  6178. // IMAGE
  6179. if ($objattr['type'] == 'image') {
  6180. // mPDF 5.7.3 TRANSFORMS
  6181. if (isset($objattr['transform'])) {
  6182. $this->writer->write("\n" . '% BTR'); // Begin Transform
  6183. }
  6184. if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {
  6185. $this->BeginLayer($objattr['z-index']);
  6186. }
  6187. if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {
  6188. $this->SetVisibility($objattr['visibility']);
  6189. }
  6190. if (isset($objattr['opacity'])) {
  6191. $this->SetAlpha($objattr['opacity']);
  6192. }
  6193. $obiw = $objattr['INNER-WIDTH'];
  6194. $obih = $objattr['INNER-HEIGHT'];
  6195. $sx = $objattr['orig_w'] ? ($objattr['INNER-WIDTH'] * Mpdf::SCALE / $objattr['orig_w']) : INF;
  6196. $sy = $objattr['orig_h'] ? ($objattr['INNER-HEIGHT'] * Mpdf::SCALE / $objattr['orig_h']) : INF;
  6197. $rotate = 0;
  6198. if (isset($objattr['ROTATE'])) {
  6199. $rotate = $objattr['ROTATE'];
  6200. }
  6201. if ($rotate == 90) {
  6202. // Clockwise
  6203. $obiw = $objattr['INNER-HEIGHT'];
  6204. $obih = $objattr['INNER-WIDTH'];
  6205. $tr = $this->transformTranslate(0, -$objattr['INNER-WIDTH'], true);
  6206. $tr .= ' ' . $this->transformRotate(90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);
  6207. $sx = $obiw * Mpdf::SCALE / $objattr['orig_h'];
  6208. $sy = $obih * Mpdf::SCALE / $objattr['orig_w'];
  6209. } elseif ($rotate == -90 || $rotate == 270) {
  6210. // AntiClockwise
  6211. $obiw = $objattr['INNER-HEIGHT'];
  6212. $obih = $objattr['INNER-WIDTH'];
  6213. $tr = $this->transformTranslate($objattr['INNER-WIDTH'], ($objattr['INNER-HEIGHT'] - $objattr['INNER-WIDTH']), true);
  6214. $tr .= ' ' . $this->transformRotate(-90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);
  6215. $sx = $obiw * Mpdf::SCALE / $objattr['orig_h'];
  6216. $sy = $obih * Mpdf::SCALE / $objattr['orig_w'];
  6217. } elseif ($rotate == 180) {
  6218. // Mirror
  6219. $tr = $this->transformTranslate($objattr['INNER-WIDTH'], -$objattr['INNER-HEIGHT'], true);
  6220. $tr .= ' ' . $this->transformRotate(180, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-HEIGHT']), true);
  6221. } else {
  6222. $tr = '';
  6223. }
  6224. $tr = trim($tr);
  6225. if ($tr) {
  6226. $tr .= ' ';
  6227. }
  6228. $gradmask = '';
  6229. // mPDF 5.7.3 TRANSFORMS
  6230. $tr2 = '';
  6231. if (isset($objattr['transform'])) {
  6232. $maxsize_x = $w;
  6233. $maxsize_y = $h;
  6234. $cx = $x + $w / 2;
  6235. $cy = $y + $h / 2;
  6236. preg_match_all('/(translatex|translatey|translate|scalex|scaley|scale|rotate|skewX|skewY|skew)\((.*?)\)/is', $objattr['transform'], $m);
  6237. if (count($m[0])) {
  6238. for ($i = 0; $i < count($m[0]); $i++) {
  6239. $c = strtolower($m[1][$i]);
  6240. $v = trim($m[2][$i]);
  6241. $vv = preg_split('/[ ,]+/', $v);
  6242. if ($c == 'translate' && count($vv)) {
  6243. $translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false);
  6244. if (count($vv) == 2) {
  6245. $translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false);
  6246. } else {
  6247. $translate_y = 0;
  6248. }
  6249. $tr2 .= $this->transformTranslate($translate_x, $translate_y, true) . ' ';
  6250. } elseif ($c == 'translatex' && count($vv)) {
  6251. $translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false);
  6252. $tr2 .= $this->transformTranslate($translate_x, 0, true) . ' ';
  6253. } elseif ($c == 'translatey' && count($vv)) {
  6254. $translate_y = $this->sizeConverter->convert($vv[0], $maxsize_y, false, false);
  6255. $tr2 .= $this->transformTranslate(0, $translate_y, true) . ' ';
  6256. } elseif ($c == 'scale' && count($vv)) {
  6257. $scale_x = $vv[0] * 100;
  6258. if (count($vv) == 2) {
  6259. $scale_y = $vv[1] * 100;
  6260. } else {
  6261. $scale_y = $scale_x;
  6262. }
  6263. $tr2 .= $this->transformScale($scale_x, $scale_y, $cx, $cy, true) . ' ';
  6264. } elseif ($c == 'scalex' && count($vv)) {
  6265. $scale_x = $vv[0] * 100;
  6266. $tr2 .= $this->transformScale($scale_x, 0, $cx, $cy, true) . ' ';
  6267. } elseif ($c == 'scaley' && count($vv)) {
  6268. $scale_y = $vv[0] * 100;
  6269. $tr2 .= $this->transformScale(0, $scale_y, $cx, $cy, true) . ' ';
  6270. } elseif ($c == 'skew' && count($vv)) {
  6271. $angle_x = $this->ConvertAngle($vv[0], false);
  6272. if (count($vv) == 2) {
  6273. $angle_y = $this->ConvertAngle($vv[1], false);
  6274. } else {
  6275. $angle_y = 0;
  6276. }
  6277. $tr2 .= $this->transformSkew($angle_x, $angle_y, $cx, $cy, true) . ' ';
  6278. } elseif ($c == 'skewx' && count($vv)) {
  6279. $angle = $this->ConvertAngle($vv[0], false);
  6280. $tr2 .= $this->transformSkew($angle, 0, $cx, $cy, true) . ' ';
  6281. } elseif ($c == 'skewy' && count($vv)) {
  6282. $angle = $this->ConvertAngle($vv[0], false);
  6283. $tr2 .= $this->transformSkew(0, $angle, $cx, $cy, true) . ' ';
  6284. } elseif ($c == 'rotate' && count($vv)) {
  6285. $angle = $this->ConvertAngle($vv[0]);
  6286. $tr2 .= $this->transformRotate($angle, $cx, $cy, true) . ' ';
  6287. }
  6288. }
  6289. }
  6290. }
  6291. // LIST MARKERS (Images) // mPDF 6 Lists
  6292. if (isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {
  6293. $mw = $objattr['OUTER-WIDTH'];
  6294. // NB If change marker-offset, also need to alter in function _getListMarkerWidth
  6295. $adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
  6296. if ($objattr['dir'] == 'rtl') {
  6297. $objattr['INNER-X'] += $adjx;
  6298. } else {
  6299. $objattr['INNER-X'] -= $adjx;
  6300. $objattr['INNER-X'] -= $mw;
  6301. }
  6302. }
  6303. // mPDF 5.7.3 TRANSFORMS / BACKGROUND COLOR
  6304. // Transform also affects image background
  6305. if ($tr2) {
  6306. $this->writer->write('q ' . $tr2 . ' ');
  6307. }
  6308. if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
  6309. $bgcol = $objattr['bgcolor'];
  6310. $this->SetFColor($bgcol);
  6311. $this->Rect($x, $y, $w, $h, 'F');
  6312. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  6313. }
  6314. if ($tr2) {
  6315. $this->writer->write('Q');
  6316. }
  6317. /* -- BACKGROUNDS -- */
  6318. if (isset($objattr['GRADIENT-MASK'])) {
  6319. $g = $this->gradient->parseMozGradient($objattr['GRADIENT-MASK']);
  6320. if ($g) {
  6321. $dummy = $this->gradient->Gradient($objattr['INNER-X'], $objattr['INNER-Y'], $obiw, $obih, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true, true);
  6322. $gradmask = '/TGS' . count($this->gradients) . ' gs ';
  6323. }
  6324. }
  6325. /* -- END BACKGROUNDS -- */
  6326. /* -- IMAGES-WMF -- */
  6327. if (isset($objattr['itype']) && $objattr['itype'] == 'wmf') {
  6328. $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
  6329. } else { /* -- END IMAGES-WMF -- */
  6330. if (isset($objattr['itype']) && $objattr['itype'] == 'svg') {
  6331. $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
  6332. } else {
  6333. $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
  6334. }
  6335. }
  6336. $this->writer->write($outstring);
  6337. // LINK
  6338. if (isset($objattr['link'])) {
  6339. $this->Link($objattr['INNER-X'], $objattr['INNER-Y'], $objattr['INNER-WIDTH'], $objattr['INNER-HEIGHT'], $objattr['link']);
  6340. }
  6341. if (isset($objattr['opacity'])) {
  6342. $this->SetAlpha(1);
  6343. }
  6344. // mPDF 5.7.3 TRANSFORMS
  6345. // Transform also affects image borders
  6346. if ($tr2) {
  6347. $this->writer->write('q ' . $tr2 . ' ');
  6348. }
  6349. 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)) {
  6350. $this->PaintImgBorder($objattr, $is_table);
  6351. }
  6352. if ($tr2) {
  6353. $this->writer->write('Q');
  6354. }
  6355. if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {
  6356. $this->SetVisibility('visible');
  6357. }
  6358. if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {
  6359. $this->EndLayer();
  6360. }
  6361. // mPDF 5.7.3 TRANSFORMS
  6362. if (isset($objattr['transform'])) {
  6363. $this->writer->write("\n" . '% ETR'); // End Transform
  6364. }
  6365. }
  6366. if ($objattr['type'] === 'barcode') {
  6367. $bgcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
  6368. if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
  6369. $bgcol = $objattr['bgcolor'];
  6370. }
  6371. $col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
  6372. if (isset($objattr['color']) && $objattr['color']) {
  6373. $col = $objattr['color'];
  6374. }
  6375. $this->SetFColor($bgcol);
  6376. $this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');
  6377. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  6378. if (isset($objattr['BORDER-WIDTH'])) {
  6379. $this->PaintImgBorder($objattr, $is_table);
  6380. }
  6381. $barcodeTypes = ['EAN13', 'ISBN', 'ISSN', 'UPCA', 'UPCE', 'EAN8'];
  6382. if (in_array($objattr['btype'], $barcodeTypes, true)) {
  6383. $this->WriteBarcode(
  6384. $objattr['code'],
  6385. $objattr['showtext'],
  6386. $objattr['INNER-X'],
  6387. $objattr['INNER-Y'],
  6388. $objattr['bsize'],
  6389. 0,
  6390. 0,
  6391. 0,
  6392. 0,
  6393. 0,
  6394. $objattr['bheight'],
  6395. $bgcol,
  6396. $col,
  6397. $objattr['btype'],
  6398. $objattr['bsupp'],
  6399. (isset($objattr['bsupp_code']) ? $objattr['bsupp_code'] : ''),
  6400. $k
  6401. );
  6402. } elseif ($objattr['btype'] === 'QR') {
  6403. if (!class_exists('Mpdf\QrCode\QrCode') || !class_exists('Mpdf\QrCode\Output\Mpdf')) {
  6404. throw new \Mpdf\MpdfException('Mpdf\QrCode package was not found. Install the package from Packagist with "composer require mpdf/qrcode"');
  6405. }
  6406. $barcodeContent = str_replace('\r\n', "\r\n", $objattr['code']);
  6407. $barcodeContent = str_replace('\n', "\n", $barcodeContent);
  6408. $qrcode = new QrCode\QrCode($barcodeContent, $objattr['errorlevel']);
  6409. if ($objattr['disableborder']) {
  6410. $qrcode->disableBorder();
  6411. }
  6412. $bgColor = [255, 255, 255];
  6413. if ($objattr['bgcolor']) {
  6414. $bgColor = array_map(
  6415. function ($col) {
  6416. return intval(255 * floatval($col));
  6417. },
  6418. explode(" ", $this->SetColor($objattr['bgcolor'], 'CodeOnly'))
  6419. );
  6420. }
  6421. $color = [0, 0, 0];
  6422. if ($objattr['color']) {
  6423. $color = array_map(
  6424. function ($col) {
  6425. return intval(255 * floatval($col));
  6426. },
  6427. explode(" ", $this->SetColor($objattr['color'], 'CodeOnly'))
  6428. );
  6429. }
  6430. $out = new QrCode\Output\Mpdf();
  6431. $out->output(
  6432. $qrcode,
  6433. $this,
  6434. $objattr['INNER-X'],
  6435. $objattr['INNER-Y'],
  6436. $objattr['bsize'] * 25,
  6437. $bgColor,
  6438. $color
  6439. );
  6440. unset($qrcode);
  6441. } else {
  6442. $this->WriteBarcode2(
  6443. $objattr['code'],
  6444. $objattr['INNER-X'],
  6445. $objattr['INNER-Y'],
  6446. $objattr['bsize'],
  6447. $objattr['bheight'],
  6448. $bgcol,
  6449. $col,
  6450. $objattr['btype'],
  6451. $objattr['pr_ratio'],
  6452. $k,
  6453. $objattr['quiet_zone_left'],
  6454. $objattr['quiet_zone_right']
  6455. );
  6456. }
  6457. }
  6458. // TEXT CIRCLE
  6459. if ($objattr['type'] == 'textcircle') {
  6460. $bgcol = '';
  6461. if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
  6462. $bgcol = $objattr['bgcolor'];
  6463. }
  6464. $col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
  6465. if (isset($objattr['color']) && $objattr['color']) {
  6466. $col = $objattr['color'];
  6467. }
  6468. $this->SetTColor($col);
  6469. $this->SetFColor($bgcol);
  6470. if ($bgcol) {
  6471. $this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');
  6472. }
  6473. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  6474. if (isset($objattr['BORDER-WIDTH'])) {
  6475. $this->PaintImgBorder($objattr, $is_table);
  6476. }
  6477. if (empty($this->directWrite)) {
  6478. $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
  6479. }
  6480. if (isset($objattr['top-text'])) {
  6481. $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'] : ''));
  6482. }
  6483. if (isset($objattr['bottom-text'])) {
  6484. $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'] : ''));
  6485. }
  6486. }
  6487. $this->ResetSpacing();
  6488. // LIST MARKERS (Text or bullets) // mPDF 6 Lists
  6489. if ($objattr['type'] == 'listmarker') {
  6490. if (isset($objattr['fontfamily'])) {
  6491. $this->SetFont($objattr['fontfamily'], $objattr['fontstyle'], $objattr['fontsizept']);
  6492. }
  6493. $col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
  6494. if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {
  6495. $col = $objattr['colorarray'];
  6496. }
  6497. if (isset($objattr['bullet']) && $objattr['bullet']) { // Used for position "outside" only
  6498. $type = $objattr['bullet'];
  6499. $size = $objattr['size'];
  6500. if ($objattr['listmarkerposition'] == 'inside') {
  6501. $adjx = $size / 2;
  6502. if ($objattr['dir'] == 'rtl') {
  6503. $adjx += $objattr['offset'];
  6504. }
  6505. $this->x += $adjx;
  6506. } else {
  6507. $adjx = $objattr['offset'];
  6508. $adjx += $size / 2;
  6509. if ($objattr['dir'] == 'rtl') {
  6510. $this->x += $adjx;
  6511. } else {
  6512. $this->x -= $adjx;
  6513. }
  6514. }
  6515. $yadj = $objattr['lineBox']['glyphYorigin'];
  6516. if (isset($this->CurrentFont['desc']['XHeight']) && $this->CurrentFont['desc']['XHeight']) {
  6517. $xh = $this->CurrentFont['desc']['XHeight'];
  6518. } else {
  6519. $xh = 500;
  6520. }
  6521. $yadj -= ($this->FontSize * $xh / 1000) * 0.625; // Vertical height of bullet (centre) from baseline= XHeight * 0.625
  6522. $this->y += $yadj;
  6523. $this->_printListBullet($this->x, $this->y, $size, $type, $col);
  6524. } else {
  6525. $this->SetTColor($col);
  6526. $w = $this->GetStringWidth($texto);
  6527. // NB If change marker-offset, also need to alter in function _getListMarkerWidth
  6528. $adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
  6529. if ($objattr['dir'] == 'rtl') {
  6530. $align = 'L';
  6531. $this->x += $adjx;
  6532. } else {
  6533. // Use these lines to set as marker-offset, right-aligned - default
  6534. $align = 'R';
  6535. $this->x -= $adjx;
  6536. $this->x -= $w;
  6537. }
  6538. $this->Cell($w, $this->FontSize, $texto, 0, 0, $align, 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']);
  6539. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  6540. }
  6541. }
  6542. // DOT-TAB
  6543. if ($objattr['type'] == 'dottab') {
  6544. if (isset($objattr['fontfamily'])) {
  6545. $this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);
  6546. }
  6547. $sp = $this->GetStringWidth(' ');
  6548. $nb = floor(($w - 2 * $sp) / $this->GetStringWidth('.'));
  6549. if ($nb > 0) {
  6550. $dots = ' ' . str_repeat('.', $nb) . ' ';
  6551. } else {
  6552. $dots = ' ';
  6553. }
  6554. $col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
  6555. if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {
  6556. $col = $objattr['colorarray'];
  6557. }
  6558. $this->SetTColor($col);
  6559. $save_dh = $this->divheight;
  6560. $save_sbd = $this->spanborddet;
  6561. $save_textvar = $this->textvar; // mPDF 5.7.1
  6562. $this->spanborddet = '';
  6563. $this->divheight = 0;
  6564. $this->textvar = 0x00; // mPDF 5.7.1
  6565. $this->Cell($w, $h, $dots, 0, 0, 'C', 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']); // mPDF 6 DOTTAB
  6566. $this->spanborddet = $save_sbd;
  6567. $this->textvar = $save_textvar; // mPDF 5.7.1
  6568. $this->divheight = $save_dh;
  6569. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  6570. }
  6571. /* -- FORMS -- */
  6572. // TEXT/PASSWORD INPUT
  6573. if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD')) {
  6574. $this->form->print_ob_text($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6575. }
  6576. // TEXTAREA
  6577. if ($objattr['type'] == 'textarea') {
  6578. $this->form->print_ob_textarea($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6579. }
  6580. // SELECT
  6581. if ($objattr['type'] == 'select') {
  6582. $this->form->print_ob_select($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6583. }
  6584. // INPUT/BUTTON as IMAGE
  6585. if ($objattr['type'] == 'input' && $objattr['subtype'] == 'IMAGE') {
  6586. $this->form->print_ob_imageinput($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $is_table);
  6587. }
  6588. // BUTTON
  6589. if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'SUBMIT' || $objattr['subtype'] == 'RESET' || $objattr['subtype'] == 'BUTTON')) {
  6590. $this->form->print_ob_button($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6591. }
  6592. // CHECKBOX
  6593. if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'CHECKBOX')) {
  6594. $this->form->print_ob_checkbox($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);
  6595. }
  6596. // RADIO
  6597. if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'RADIO')) {
  6598. $this->form->print_ob_radio($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);
  6599. }
  6600. /* -- END FORMS -- */
  6601. }
  6602. $this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);
  6603. $this->y = $save_y;
  6604. $this->x = $save_x;
  6605. unset($content);
  6606. }
  6607. function _printListBullet($x, $y, $size, $type, $color)
  6608. {
  6609. // x and y are the centre of the bullet; size is the width and/or height in mm
  6610. $fcol = $this->SetTColor($color, true);
  6611. $lcol = strtoupper($fcol); // change 0 0 0 rg to 0 0 0 RG
  6612. $this->writer->write(sprintf('q %s %s', $lcol, $fcol));
  6613. $this->writer->write('0 j 0 J [] 0 d');
  6614. if ($type == 'square') {
  6615. $size *= 0.85; // Smaller to appear the same size as circle/disc
  6616. $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));
  6617. } elseif ($type == 'disc') {
  6618. $this->Circle($x, $y, $size / 2, 'F'); // Fill
  6619. } elseif ($type == 'circle') {
  6620. $lw = $size / 12; // Line width
  6621. $this->writer->write(sprintf('%.3F w ', $lw * Mpdf::SCALE));
  6622. $this->Circle($x, $y, $size / 2 - $lw / 2, 'S'); // Stroke
  6623. }
  6624. $this->writer->write('Q');
  6625. }
  6626. // mPDF 6
  6627. // Get previous character and move pointers
  6628. function _moveToPrevChar(&$contentctr, &$charctr, $content)
  6629. {
  6630. $lastchar = false;
  6631. $charctr--;
  6632. while ($charctr < 0) { // go back to previous $content[]
  6633. $contentctr--;
  6634. if ($contentctr < 0) {
  6635. return false;
  6636. }
  6637. if ($this->usingCoreFont) {
  6638. $charctr = strlen($content[$contentctr]) - 1;
  6639. } else {
  6640. $charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;
  6641. }
  6642. }
  6643. if ($this->usingCoreFont) {
  6644. $lastchar = $content[$contentctr][$charctr];
  6645. } else {
  6646. $lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);
  6647. }
  6648. return $lastchar;
  6649. }
  6650. // Get previous character
  6651. function _getPrevChar($contentctr, $charctr, $content)
  6652. {
  6653. $lastchar = false;
  6654. $charctr--;
  6655. while ($charctr < 0) { // go back to previous $content[]
  6656. $contentctr--;
  6657. if ($contentctr < 0) {
  6658. return false;
  6659. }
  6660. if ($this->usingCoreFont) {
  6661. $charctr = strlen($content[$contentctr]) - 1;
  6662. } else {
  6663. $charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;
  6664. }
  6665. }
  6666. if ($this->usingCoreFont) {
  6667. $lastchar = $content[$contentctr][$charctr];
  6668. } else {
  6669. $lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);
  6670. }
  6671. return $lastchar;
  6672. }
  6673. function WriteFlowingBlock($s, $sOTLdata)
  6674. {
  6675. // mPDF 5.7.1
  6676. $currentx = $this->x;
  6677. $is_table = $this->flowingBlockAttr['is_table'];
  6678. $table_draft = $this->flowingBlockAttr['table_draft'];
  6679. // width of all the content so far in points
  6680. $contentWidth = & $this->flowingBlockAttr['contentWidth'];
  6681. // cell width in points
  6682. $maxWidth = & $this->flowingBlockAttr['width'];
  6683. $lineCount = & $this->flowingBlockAttr['lineCount'];
  6684. // line height in user units
  6685. $stackHeight = & $this->flowingBlockAttr['height'];
  6686. $align = & $this->flowingBlockAttr['align'];
  6687. $content = & $this->flowingBlockAttr['content'];
  6688. $contentB = & $this->flowingBlockAttr['contentB'];
  6689. $font = & $this->flowingBlockAttr['font'];
  6690. $valign = & $this->flowingBlockAttr['valign'];
  6691. $blockstate = $this->flowingBlockAttr['blockstate'];
  6692. $cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1
  6693. $newblock = $this->flowingBlockAttr['newblock'];
  6694. $blockdir = $this->flowingBlockAttr['blockdir'];
  6695. // *********** BLOCK BACKGROUND COLOR ***************** //
  6696. if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {
  6697. $fill = 0;
  6698. } else {
  6699. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  6700. $fill = 0;
  6701. }
  6702. $font[] = $this->saveFont();
  6703. $content[] = '';
  6704. $contentB[] = '';
  6705. $cOTLdata[] = $sOTLdata; // mPDF 5.7.1
  6706. $currContent = & $content[count($content) - 1];
  6707. $CJKoverflow = false;
  6708. $Oikomi = false; // mPDF 6
  6709. $hanger = '';
  6710. // COLS
  6711. $oldcolumn = $this->CurrCol;
  6712. if ($this->ColActive && !$is_table) {
  6713. $this->breakpoints[$this->CurrCol][] = $this->y;
  6714. } // *COLUMNS*
  6715. /* -- TABLES -- */
  6716. if ($is_table) {
  6717. $ipaddingL = 0;
  6718. $ipaddingR = 0;
  6719. $paddingL = 0;
  6720. $paddingR = 0;
  6721. $cpaddingadjustL = 0;
  6722. $cpaddingadjustR = 0;
  6723. // Added mPDF 3.0
  6724. $fpaddingR = 0;
  6725. $fpaddingL = 0;
  6726. } else {
  6727. /* -- END TABLES -- */
  6728. $ipaddingL = $this->blk[$this->blklvl]['padding_left'];
  6729. $ipaddingR = $this->blk[$this->blklvl]['padding_right'];
  6730. $paddingL = ($ipaddingL * Mpdf::SCALE);
  6731. $paddingR = ($ipaddingR * Mpdf::SCALE);
  6732. $this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];
  6733. $cpaddingadjustL = -$this->cMarginL;
  6734. $this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];
  6735. $cpaddingadjustR = -$this->cMarginR;
  6736. // Added mPDF 3.0 Float DIV
  6737. $fpaddingR = 0;
  6738. $fpaddingL = 0;
  6739. /* -- CSS-FLOAT -- */
  6740. if (count($this->floatDivs)) {
  6741. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  6742. if ($r_exists) {
  6743. $fpaddingR = $r_width;
  6744. }
  6745. if ($l_exists) {
  6746. $fpaddingL = $l_width;
  6747. }
  6748. }
  6749. /* -- END CSS-FLOAT -- */
  6750. $usey = $this->y + 0.002;
  6751. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
  6752. $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  6753. }
  6754. /* -- CSS-IMAGE-FLOAT -- */
  6755. // If float exists at this level
  6756. if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
  6757. $fpaddingR += $this->floatmargins['R']['w'];
  6758. }
  6759. if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
  6760. $fpaddingL += $this->floatmargins['L']['w'];
  6761. }
  6762. /* -- END CSS-IMAGE-FLOAT -- */
  6763. } // *TABLES*
  6764. // OBJECTS - IMAGES & FORM Elements (NB has already skipped line/page if required - in printbuffer)
  6765. if (substr($s, 0, 3) == Mpdf::OBJECT_IDENTIFIER) { // identifier has been identified!
  6766. $objattr = $this->_getObjAttr($s);
  6767. $h_corr = 0;
  6768. if ($is_table) { // *TABLES*
  6769. $maximumW = ($maxWidth / Mpdf::SCALE) - ($this->cellPaddingL + $this->cMarginL + $this->cellPaddingR + $this->cMarginR); // *TABLES*
  6770. } // *TABLES*
  6771. else { // *TABLES*
  6772. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0) && (!$is_table)) {
  6773. $h_corr = $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  6774. }
  6775. $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 );
  6776. } // *TABLES*
  6777. $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);
  6778. // SET LINEHEIGHT for this line ================ RESET AT END
  6779. $stackHeight = max($stackHeight, $objattr['OUTER-HEIGHT']);
  6780. $this->objectbuffer[count($content) - 1] = $objattr;
  6781. // if (isset($objattr['vertical-align'])) { $valign = $objattr['vertical-align']; }
  6782. // else { $valign = ''; }
  6783. // LIST MARKERS // mPDF 6 Lists
  6784. if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {
  6785. // do nothing
  6786. } else {
  6787. $contentWidth += ($objattr['OUTER-WIDTH'] * Mpdf::SCALE);
  6788. }
  6789. return;
  6790. }
  6791. $lbw = $rbw = 0; // Border widths
  6792. if (!empty($this->spanborddet)) {
  6793. if (isset($this->spanborddet['L'])) {
  6794. $lbw = $this->spanborddet['L']['w'];
  6795. }
  6796. if (isset($this->spanborddet['R'])) {
  6797. $rbw = $this->spanborddet['R']['w'];
  6798. }
  6799. }
  6800. if ($this->usingCoreFont) {
  6801. $clen = strlen($s);
  6802. } else {
  6803. $clen = mb_strlen($s, $this->mb_enc);
  6804. }
  6805. // for every character in the string
  6806. for ($i = 0; $i < $clen; $i++) {
  6807. // extract the current character
  6808. // get the width of the character in points
  6809. if ($this->usingCoreFont) {
  6810. $c = $s[$i];
  6811. // Soft Hyphens chr(173)
  6812. $cw = ($this->GetCharWidthCore($c) * Mpdf::SCALE);
  6813. if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
  6814. if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c])) {
  6815. $cw += ($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c] * $this->FontSizePt / 1000 );
  6816. }
  6817. }
  6818. } else {
  6819. $c = mb_substr($s, $i, 1, $this->mb_enc);
  6820. $cw = ($this->GetCharWidthNonCore($c, false) * Mpdf::SCALE);
  6821. // mPDF 5.7.1
  6822. // Use OTL GPOS
  6823. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
  6824. // ...WriteFlowingBlock...
  6825. // Only add XAdvanceL (not sure at present whether RTL or LTR writing direction)
  6826. // At this point, XAdvanceL and XAdvanceR will balance
  6827. if (isset($sOTLdata['GPOSinfo'][$i]['XAdvanceL'])) {
  6828. $cw += $sOTLdata['GPOSinfo'][$i]['XAdvanceL'] * (1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000) * Mpdf::SCALE;
  6829. }
  6830. }
  6831. if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
  6832. $lastc = mb_substr($s, ($i - 1), 1, $this->mb_enc);
  6833. $ulastc = $this->UTF8StringToArray($lastc, false);
  6834. $uc = $this->UTF8StringToArray($c, false);
  6835. if (isset($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]])) {
  6836. $cw += ($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]] * $this->FontSizePt / 1000 );
  6837. }
  6838. }
  6839. }
  6840. if ($i == 0) {
  6841. $cw += $lbw * Mpdf::SCALE;
  6842. $contentB[(count($contentB) - 1)] .= 'L';
  6843. }
  6844. if ($i == ($clen - 1)) {
  6845. $cw += $rbw * Mpdf::SCALE;
  6846. $contentB[(count($contentB) - 1)] .= 'R';
  6847. }
  6848. if ($c == ' ') {
  6849. $currContent .= $c;
  6850. $contentWidth += $cw;
  6851. continue;
  6852. }
  6853. // Paragraph INDENT
  6854. $WidthCorrection = 0;
  6855. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
  6856. $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
  6857. $WidthCorrection = ($ti * Mpdf::SCALE);
  6858. }
  6859. // OUTDENT
  6860. foreach ($this->objectbuffer as $k => $objattr) { // mPDF 6 DOTTAB
  6861. if ($objattr['type'] == 'dottab') {
  6862. $WidthCorrection -= ($objattr['outdent'] * Mpdf::SCALE);
  6863. break;
  6864. }
  6865. }
  6866. // Added mPDF 3.0 Float DIV
  6867. $fpaddingR = 0;
  6868. $fpaddingL = 0;
  6869. /* -- CSS-FLOAT -- */
  6870. if (count($this->floatDivs)) {
  6871. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  6872. if ($r_exists) {
  6873. $fpaddingR = $r_width;
  6874. }
  6875. if ($l_exists) {
  6876. $fpaddingL = $l_width;
  6877. }
  6878. }
  6879. /* -- END CSS-FLOAT -- */
  6880. $usey = $this->y + 0.002;
  6881. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
  6882. $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  6883. }
  6884. /* -- CSS-IMAGE-FLOAT -- */
  6885. // If float exists at this level
  6886. if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
  6887. $fpaddingR += $this->floatmargins['R']['w'];
  6888. }
  6889. if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
  6890. $fpaddingL += $this->floatmargins['L']['w'];
  6891. }
  6892. /* -- END CSS-IMAGE-FLOAT -- */
  6893. // try adding another char
  6894. 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
  6895. // it won't fit, output what we already have
  6896. $lineCount++;
  6897. // contains any content that didn't make it into this print
  6898. $savedContent = '';
  6899. $savedContentB = '';
  6900. $savedOTLdata = []; // mPDF 5.7.1
  6901. $savedFont = [];
  6902. $savedObj = [];
  6903. $savedPreOTLdata = []; // mPDF 5.7.1
  6904. $savedPreContent = [];
  6905. $savedPreContentB = [];
  6906. $savedPreFont = [];
  6907. // mPDF 6
  6908. // New line-breaking algorithm
  6909. /////////////////////
  6910. // LINE BREAKING
  6911. /////////////////////
  6912. $breakfound = false;
  6913. $contentctr = count($content) - 1;
  6914. if ($this->usingCoreFont) {
  6915. $charctr = strlen($currContent);
  6916. } else {
  6917. $charctr = mb_strlen($currContent, $this->mb_enc);
  6918. }
  6919. $checkchar = $c;
  6920. $prevchar = $this->_getPrevChar($contentctr, $charctr, $content);
  6921. /* -- CJK-FONTS -- */
  6922. // 1) CJK Overflowing a) punctuation or b) Oikomi
  6923. // Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi
  6924. if ($CJKoverflow || $Oikomi) { // If flag already set
  6925. $CJKoverflow = false;
  6926. $Oikomi = false;
  6927. $breakfound = true;
  6928. }
  6929. if (!$this->usingCoreFont && !$breakfound && $this->checkCJK) {
  6930. // Get next/following character (in this chunk)
  6931. $followingchar = '';
  6932. if ($i < ($clen - 1)) {
  6933. if ($this->usingCoreFont) {
  6934. $followingchar = $s[$i + 1];
  6935. } else {
  6936. $followingchar = mb_substr($s, $i + 1, 1, $this->mb_enc);
  6937. }
  6938. }
  6939. // 1a) Overflow punctuation
  6940. if (preg_match("/[" . $this->pregCJKchars . "]/u", $prevchar) && preg_match("/[" . $this->CJKoverflow . "]/u", $checkchar) && $this->allowCJKorphans) {
  6941. // add character onto this line
  6942. $currContent .= $c;
  6943. $contentWidth += $cw;
  6944. $CJKoverflow = true; // Set flag
  6945. continue;
  6946. } elseif (preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) && $this->allowCJKorphans &&
  6947. (preg_match("/[" . $this->CJKleading . "]/u", $followingchar) || preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar)) &&
  6948. !preg_match("/[" . $this->CJKleading . "]/u", $checkchar) && !preg_match("/[" . $this->CJKfollowing . "]/u", $followingchar) &&
  6949. !(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $followingchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) {
  6950. // 1b) Try squeezing another character(s) onto this line = Oikomi, if character cannot end line
  6951. // or next character cannot start line (and not splitting CJK numerals)
  6952. // NB otherwise it move lastchar(s) to next line to keep $c company = Oidashi, which is done below in standard way
  6953. // add character onto this line
  6954. $currContent .= $c;
  6955. $contentWidth += $cw;
  6956. $Oikomi = true; // Set flag
  6957. continue;
  6958. }
  6959. }
  6960. /* -- END CJK-FONTS -- */
  6961. /* -- HYPHENATION -- */
  6962. // AUTOMATIC HYPHENATION
  6963. // 2) Automatic hyphen in current word (does not cross tags)
  6964. if (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] == 1) {
  6965. $currWord = '';
  6966. // Look back and ahead to get current word
  6967. for ($ac = $charctr - 1; $ac >= 0; $ac--) {
  6968. if ($this->usingCoreFont) {
  6969. $addc = substr($currContent, $ac, 1);
  6970. } else {
  6971. $addc = mb_substr($currContent, $ac, 1, $this->mb_enc);
  6972. }
  6973. if ($addc == ' ') {
  6974. break;
  6975. }
  6976. $currWord = $addc . $currWord;
  6977. }
  6978. $start = $ac + 1;
  6979. for ($ac = $i; $ac < ($clen - 1); $ac++) {
  6980. if ($this->usingCoreFont) {
  6981. $addc = substr($s, $ac, 1);
  6982. } else {
  6983. $addc = mb_substr($s, $ac, 1, $this->mb_enc);
  6984. }
  6985. if ($addc == ' ') {
  6986. break;
  6987. }
  6988. $currWord .= $addc;
  6989. }
  6990. $ptr = $this->hyphenator->hyphenateWord($currWord, $charctr - $start);
  6991. if ($ptr > -1) {
  6992. $breakfound = [$contentctr, $start + $ptr, $contentctr, $start + $ptr, 'hyphen'];
  6993. }
  6994. }
  6995. /* -- END HYPHENATION -- */
  6996. // Search backwards to find first line-break opportunity
  6997. while ($breakfound == false && $prevchar !== false) {
  6998. $cutcontentctr = $contentctr;
  6999. $cutcharctr = $charctr;
  7000. $prevchar = $this->_moveToPrevChar($contentctr, $charctr, $content);
  7001. /////////////////////
  7002. // 3) Break at SPACE
  7003. /////////////////////
  7004. if ($prevchar == ' ') {
  7005. $breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard'];
  7006. } /////////////////////
  7007. // 4) Break at U+200B in current word (Khmer, Lao & Thai Invisible word boundary, and Tibetan)
  7008. /////////////////////
  7009. elseif ($prevchar == "\xe2\x80\x8b") { // U+200B Zero-width Word Break
  7010. $breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard'];
  7011. } /////////////////////
  7012. // 5) Break at Hard HYPHEN '-' or U+2010
  7013. /////////////////////
  7014. elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && ($prevchar == '-' || $prevchar == "\xe2\x80\x90")) {
  7015. // Don't break a URL
  7016. // Look back to get first part of current word
  7017. $checkw = '';
  7018. for ($ac = $charctr - 1; $ac >= 0; $ac--) {
  7019. if ($this->usingCoreFont) {
  7020. $addc = substr($currContent, $ac, 1);
  7021. } else {
  7022. $addc = mb_substr($currContent, $ac, 1, $this->mb_enc);
  7023. }
  7024. if ($addc == ' ') {
  7025. break;
  7026. }
  7027. $checkw = $addc . $checkw;
  7028. }
  7029. // Don't break if HyphenMinus AND (a URL or before a numeral or before a >)
  7030. if ((!preg_match('/(http:|ftp:|https:|www\.)/', $checkw) && $checkchar != '>' && !preg_match('/[0-9]/', $checkchar)) || $prevchar == "\xe2\x80\x90") {
  7031. $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
  7032. }
  7033. } /////////////////////
  7034. // 6) Break at Soft HYPHEN (replace with hard hyphen)
  7035. /////////////////////
  7036. elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && !$this->usingCoreFont && $prevchar == "\xc2\xad") {
  7037. $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
  7038. $content[$contentctr] = mb_substr($content[$contentctr], 0, $charctr, $this->mb_enc) . '-' . mb_substr($content[$contentctr], $charctr + 1, mb_strlen($content[$contentctr]), $this->mb_enc);
  7039. if (!empty($cOTLdata[$contentctr])) {
  7040. $cOTLdata[$contentctr]['char_data'][$charctr] = ['bidi_class' => 9, 'uni' => 45];
  7041. $cOTLdata[$contentctr]['group'][$charctr] = 'C';
  7042. }
  7043. } elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && $prevchar == chr(173)) {
  7044. $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
  7045. $content[$contentctr] = substr($content[$contentctr], 0, $charctr) . '-' . substr($content[$contentctr], $charctr + 1);
  7046. } /* -- CJK-FONTS -- */
  7047. /////////////////////
  7048. // 7) Break at CJK characters (unless forbidden characters to end or start line)
  7049. // CJK Avoiding line break in the middle of numerals
  7050. /////////////////////
  7051. elseif (!$this->usingCoreFont && $this->checkCJK && preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) &&
  7052. !preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar) && !preg_match("/[" . $this->CJKleading . "]/u", $prevchar) &&
  7053. !(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $prevchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) {
  7054. $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
  7055. }
  7056. /* -- END CJK-FONTS -- */
  7057. /////////////////////
  7058. // 8) Break at OBJECT (Break before all objects here - selected objects are moved forward to next line below e.g. dottab)
  7059. /////////////////////
  7060. if (isset($this->objectbuffer[$contentctr])) {
  7061. $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
  7062. }
  7063. $checkchar = $prevchar;
  7064. }
  7065. // If a line-break opportunity found:
  7066. if (is_array($breakfound)) {
  7067. $contentctr = $breakfound[0];
  7068. $charctr = $breakfound[1];
  7069. $cutcontentctr = $breakfound[2];
  7070. $cutcharctr = $breakfound[3];
  7071. $type = $breakfound[4];
  7072. // Cache chunks which are already processed, but now need to be passed on to the new line
  7073. for ($ix = count($content) - 1; $ix > $cutcontentctr; $ix--) {
  7074. // save and crop off any subsequent chunks
  7075. /* -- OTL -- */
  7076. if (!empty($sOTLdata)) {
  7077. $tmpOTL = array_pop($cOTLdata);
  7078. $savedPreOTLdata[] = $tmpOTL;
  7079. }
  7080. /* -- END OTL -- */
  7081. $savedPreContent[] = array_pop($content);
  7082. $savedPreContentB[] = array_pop($contentB);
  7083. $savedPreFont[] = array_pop($font);
  7084. }
  7085. // Next cache the part which will start the next line
  7086. if ($this->usingCoreFont) {
  7087. $savedPreContent[] = substr($content[$cutcontentctr], $cutcharctr);
  7088. } else {
  7089. $savedPreContent[] = mb_substr($content[$cutcontentctr], $cutcharctr, mb_strlen($content[$cutcontentctr]), $this->mb_enc);
  7090. }
  7091. $savedPreContentB[] = preg_replace('/L/', '', $contentB[$cutcontentctr]);
  7092. $savedPreFont[] = $font[$cutcontentctr];
  7093. /* -- OTL -- */
  7094. if (!empty($sOTLdata)) {
  7095. $savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[$cutcontentctr], $cutcharctr, $cutcharctr);
  7096. }
  7097. /* -- END OTL -- */
  7098. // Finally adjust the Current content which ends this line
  7099. if ($cutcharctr == 0 && $type == 'discard') {
  7100. array_pop($content);
  7101. array_pop($contentB);
  7102. array_pop($font);
  7103. array_pop($cOTLdata);
  7104. }
  7105. $currContent = & $content[count($content) - 1];
  7106. if ($this->usingCoreFont) {
  7107. $currContent = substr($currContent, 0, $charctr);
  7108. } else {
  7109. $currContent = mb_substr($currContent, 0, $charctr, $this->mb_enc);
  7110. }
  7111. if (!empty($sOTLdata)) {
  7112. $savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));
  7113. }
  7114. if (strpos($contentB[(count($contentB) - 1)], 'R') !== false) { // ???
  7115. $contentB[count($content) - 1] = preg_replace('/R/', '', $contentB[count($content) - 1]); // ???
  7116. }
  7117. if ($type === 'hyphen') {
  7118. $hyphen = in_array(mb_substr($currContent, -1), ['-', '–', '—'], true);
  7119. if (!$hyphen) {
  7120. $currContent .= '-';
  7121. } else {
  7122. $savedPreContent[count($savedPreContent) - 1] = '-' . $savedPreContent[count($savedPreContent) - 1];
  7123. }
  7124. if (!empty($cOTLdata[(count($cOTLdata) - 1)])) {
  7125. $cOTLdata[(count($cOTLdata) - 1)]['char_data'][] = ['bidi_class' => 9, 'uni' => 45];
  7126. $cOTLdata[(count($cOTLdata) - 1)]['group'] .= 'C';
  7127. }
  7128. }
  7129. $savedContent = '';
  7130. $savedContentB = '';
  7131. $savedFont = [];
  7132. $savedOTLdata = [];
  7133. }
  7134. // If no line-break opportunity found - split at current position
  7135. // or - Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi, as set above by:
  7136. // 1) CJK Overflowing a) punctuation or b) Oikomi
  7137. // in which case $breakfound==1 and NOT array
  7138. if (!is_array($breakfound)) {
  7139. $savedFont = $this->saveFont();
  7140. if (!empty($sOTLdata)) {
  7141. $savedOTLdata = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));
  7142. }
  7143. }
  7144. if ($content[count($content) - 1] == '' && !isset($this->objectbuffer[count($content) - 1])) {
  7145. array_pop($content);
  7146. array_pop($contentB);
  7147. array_pop($font);
  7148. array_pop($cOTLdata);
  7149. $currContent = & $content[count($content) - 1];
  7150. }
  7151. // Right Trim current content - including CJK space, and for OTLdata
  7152. // incl. CJK - strip CJK space at end of line &#x3000; = \xe3\x80\x80 = CJK space
  7153. $currContent = $currContent ? rtrim($currContent) : '';
  7154. if ($this->checkCJK) {
  7155. $currContent = preg_replace("/\xe3\x80\x80$/", '', $currContent);
  7156. } // *CJK-FONTS*
  7157. /* -- OTL -- */
  7158. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  7159. $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true); // NB also does U+3000
  7160. }
  7161. /* -- END OTL -- */
  7162. // Selected OBJECTS are moved forward to next line, unless they come before a space or U+200B (type='discard')
  7163. if (isset($this->objectbuffer[(count($content) - 1)]) && (!isset($type) || $type != 'discard')) {
  7164. $objtype = $this->objectbuffer[(count($content) - 1)]['type'];
  7165. if ($objtype == 'dottab' || $objtype == 'bookmark' || $objtype == 'indexentry' || $objtype == 'toc' || $objtype == 'annot') {
  7166. $savedObj = array_pop($this->objectbuffer);
  7167. }
  7168. }
  7169. // Decimal alignment (cancel if wraps to > 1 line)
  7170. if ($is_table && substr($align, 0, 1) == 'D') {
  7171. $align = substr($align, 2, 1);
  7172. }
  7173. $lineBox = [];
  7174. $this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);
  7175. // update $contentWidth since it has changed with cropping
  7176. $contentWidth = 0;
  7177. $inclCursive = false;
  7178. foreach ($content as $k => $chunk) {
  7179. if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
  7180. // LIST MARKERS
  7181. if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker']) {
  7182. if ($this->objectbuffer[$k]['listmarkerposition'] != 'outside') {
  7183. $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
  7184. }
  7185. } else {
  7186. $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
  7187. }
  7188. } elseif (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
  7189. $this->restoreFont($font[$k], false);
  7190. if ($this->checkCJK && $k == count($content) - 1 && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $this->CJKforceend) {
  7191. // force-end overhang
  7192. $hanger = mb_substr($chunk, mb_strlen($chunk, $this->mb_enc) - 1, 1, $this->mb_enc);
  7193. // Probably ought to do something with char_data and GPOS in cOTLdata...
  7194. $content[$k] = $chunk = mb_substr($chunk, 0, mb_strlen($chunk, $this->mb_enc) - 1, $this->mb_enc);
  7195. }
  7196. // Soft Hyphens chr(173) + Replace NBSP with SPACE + Set inclcursive if includes CURSIVE TEXT
  7197. if (!$this->usingCoreFont) {
  7198. /* -- OTL -- */
  7199. if ((isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) || !empty($sOTLdata)) {
  7200. $this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad");
  7201. $this->otl->replaceSpace($chunk, $cOTLdata[$k]); // NBSP -> space
  7202. if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) {
  7203. $inclCursive = true;
  7204. }
  7205. $content[$k] = $chunk;
  7206. } /* -- END OTL -- */ else { // *OTL*
  7207. $content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk);
  7208. $content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);
  7209. } // *OTL*
  7210. } elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  7211. $content[$k] = $chunk = str_replace(chr(173), '', $chunk);
  7212. $content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);
  7213. }
  7214. $contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1
  7215. if (!empty($this->spanborddet)) {
  7216. if (isset($this->spanborddet['L']['w']) && strpos($contentB[$k], 'L') !== false) {
  7217. $contentWidth += $this->spanborddet['L']['w'] * Mpdf::SCALE;
  7218. }
  7219. if (isset($this->spanborddet['R']['w']) && strpos($contentB[$k], 'R') !== false) {
  7220. $contentWidth += $this->spanborddet['R']['w'] * Mpdf::SCALE;
  7221. }
  7222. }
  7223. }
  7224. }
  7225. $lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');
  7226. $lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');
  7227. if ($blockdir == 'ltr' && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic
  7228. $lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE;
  7229. } else {
  7230. $lastitalic = 0;
  7231. }
  7232. // NOW FORMAT THE LINE TO OUTPUT
  7233. if (!$table_draft) {
  7234. // DIRECTIONALITY RTL
  7235. $chunkorder = range(0, count($content) - 1); // mPDF 5.7
  7236. /* -- OTL -- */
  7237. // mPDF 6
  7238. if ($blockdir == 'rtl' || $this->biDirectional) {
  7239. $this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);
  7240. // From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to
  7241. // $this->objectbuffer and $font ($chunkorder contains the mapping)
  7242. }
  7243. /* -- END OTL -- */
  7244. // Remove any XAdvance from OTL data at end of line
  7245. foreach ($chunkorder as $aord => $k) {
  7246. if (count($cOTLdata)) {
  7247. $this->restoreFont($font[$k], false);
  7248. // ...WriteFlowingBlock...
  7249. if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line
  7250. $nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character
  7251. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {
  7252. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {
  7253. $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
  7254. } else {
  7255. $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
  7256. }
  7257. $w *= ($this->FontSize / 1000);
  7258. $contentWidth -= $w * Mpdf::SCALE;
  7259. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;
  7260. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;
  7261. }
  7262. // If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it
  7263. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {
  7264. $w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
  7265. $w *= ($this->FontSize / 1000);
  7266. $contentWidth -= $w * Mpdf::SCALE;
  7267. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
  7268. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
  7269. }
  7270. }
  7271. }
  7272. }
  7273. // JUSTIFICATION J
  7274. $jcharspacing = 0;
  7275. $jws = 0;
  7276. $nb_carac = 0;
  7277. $nb_spaces = 0;
  7278. $jkashida = 0;
  7279. // if it's justified, we need to find the char/word spacing (or if hanger $this->CJKforceend)
  7280. 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
  7281. // JUSTIFY J (Use character spacing)
  7282. // WORD SPACING
  7283. // mPDF 5.7
  7284. foreach ($chunkorder as $aord => $k) {
  7285. $chunk = isset($content[$aord]) ? $content[$aord] : '';
  7286. if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
  7287. $nb_carac += mb_strlen($chunk, $this->mb_enc);
  7288. $nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);
  7289. // Use GPOS OTL
  7290. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
  7291. if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
  7292. $nb_carac -= substr_count($cOTLdata[$aord]['group'], 'M');
  7293. }
  7294. }
  7295. } else {
  7296. $nb_carac ++;
  7297. } // mPDF 6 allow spacing for inline object
  7298. }
  7299. // GetJSpacing adds kashida spacing to GPOSinfo if appropriate for Font
  7300. 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);
  7301. }
  7302. // WORD SPACING
  7303. $empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) );
  7304. $empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1
  7305. $empty -= ($jws * $nb_spaces);
  7306. $empty -= ($jkashida);
  7307. $empty /= Mpdf::SCALE;
  7308. $b = ''; // do not use borders
  7309. // Get PAGEBREAK TO TEST for height including the top border/padding
  7310. $check_h = max($this->divheight, $stackHeight);
  7311. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blklvl > 0) && ($lineCount == 1) && (!$is_table)) {
  7312. $check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);
  7313. }
  7314. if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {
  7315. $this->SetCol($this->NbCol - 1);
  7316. }
  7317. // PAGEBREAK
  7318. // 'If' below used in order to fix "first-line of other page with justify on" bug
  7319. if (!$is_table && ($this->y + $check_h) > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {
  7320. $bak_x = $this->x; // Current X position
  7321. // WORD SPACING
  7322. $ws = $this->ws; // Word Spacing
  7323. $charspacing = $this->charspacing; // Character Spacing
  7324. $this->ResetSpacing();
  7325. $this->AddPage($this->CurOrientation);
  7326. $this->x = $bak_x;
  7327. // Added to correct for OddEven Margins
  7328. $currentx += $this->MarginCorrection;
  7329. $this->x += $this->MarginCorrection;
  7330. // WORD SPACING
  7331. $this->SetSpacing($charspacing, $ws);
  7332. }
  7333. if ($this->kwt && !$is_table) { // mPDF 5.7+
  7334. $this->printkwtbuffer();
  7335. $this->kwt = false;
  7336. }
  7337. /* -- COLUMNS -- */
  7338. // COLS
  7339. // COLUMN CHANGE
  7340. if ($this->CurrCol != $oldcolumn) {
  7341. $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  7342. $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  7343. $oldcolumn = $this->CurrCol;
  7344. }
  7345. if ($this->ColActive && !$is_table) {
  7346. $this->breakpoints[$this->CurrCol][] = $this->y;
  7347. } // *COLUMNS*
  7348. /* -- END COLUMNS -- */
  7349. // TOP MARGIN
  7350. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && ($lineCount == 1) && (!$is_table)) {
  7351. $this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
  7352. if ($this->ColActive) {
  7353. $this->breakpoints[$this->CurrCol][] = $this->y;
  7354. } // *COLUMNS*
  7355. }
  7356. // Update y0 for top of block (used to paint border)
  7357. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table)) {
  7358. $this->blk[$this->blklvl]['y0'] = $this->y;
  7359. $this->blk[$this->blklvl]['startpage'] = $this->page;
  7360. if ($this->blk[$this->blklvl]['float']) {
  7361. $this->blk[$this->blklvl]['float_start_y'] = $this->y;
  7362. }
  7363. }
  7364. // TOP PADDING and BORDER spacing/fill
  7365. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 1) && (!$is_table)) {
  7366. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  7367. $this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);
  7368. if ($this->ColActive) {
  7369. $this->breakpoints[$this->CurrCol][] = $this->y;
  7370. } // *COLUMNS*
  7371. }
  7372. $arraysize = count($chunkorder);
  7373. $margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );
  7374. // PAINT BACKGROUND FOR THIS LINE
  7375. if (!$is_table) {
  7376. $this->DivLn($stackHeight, $this->blklvl, false);
  7377. } // false -> don't advance y
  7378. $this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;
  7379. if ($align == 'R') {
  7380. $this->x += $empty;
  7381. } elseif ($align == 'C') {
  7382. $this->x += ($empty / 2);
  7383. }
  7384. // Paragraph INDENT
  7385. if (isset($this->blk[$this->blklvl]['text_indent']) && ($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table) && ($blockdir != 'rtl') && ($align != 'C')) {
  7386. $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
  7387. $this->x += $ti;
  7388. }
  7389. // BIDI magic_reverse moved upwards from here
  7390. foreach ($chunkorder as $aord => $k) { // mPDF 5.7
  7391. $chunk = isset($content[$aord]) ? $content[$aord] : '';
  7392. if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
  7393. $xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];
  7394. $this->objectbuffer[$k]['OUTER-X'] += $xadj;
  7395. $this->objectbuffer[$k]['BORDER-X'] += $xadj;
  7396. $this->objectbuffer[$k]['INNER-X'] += $xadj;
  7397. if ($this->objectbuffer[$k]['type'] == 'listmarker') {
  7398. $this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin
  7399. }
  7400. $yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];
  7401. if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
  7402. $this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin
  7403. }
  7404. if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB
  7405. $yadj += $lineBox[$k]['top'];
  7406. }
  7407. $this->objectbuffer[$k]['OUTER-Y'] += $yadj;
  7408. $this->objectbuffer[$k]['BORDER-Y'] += $yadj;
  7409. $this->objectbuffer[$k]['INNER-Y'] += $yadj;
  7410. }
  7411. $this->restoreFont($font[$k]); // mPDF 5.7
  7412. $this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws);
  7413. // Now unset these values so they don't influence GetStringwidth below or in fn. Cell
  7414. $this->fixedlSpacing = false;
  7415. $this->minwSpacing = 0;
  7416. $save_vis = $this->visibility;
  7417. if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {
  7418. $this->SetVisibility($this->textparam['visibility']);
  7419. }
  7420. // *********** SPAN BACKGROUND COLOR ***************** //
  7421. if ($this->spanbgcolor) {
  7422. $cor = $this->spanbgcolorarray;
  7423. $this->SetFColor($cor);
  7424. $save_fill = $fill;
  7425. $spanfill = 1;
  7426. $fill = 1;
  7427. }
  7428. if (!empty($this->spanborddet)) {
  7429. if (strpos($contentB[$k], 'L') !== false) {
  7430. $this->x += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
  7431. }
  7432. if (strpos($contentB[$k], 'L') === false) {
  7433. $this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;
  7434. }
  7435. if (strpos($contentB[$k], 'R') === false) {
  7436. $this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;
  7437. }
  7438. }
  7439. // WORD SPACING
  7440. // StringWidth this time includes any kashida spacing
  7441. $stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, true);
  7442. $nch = mb_strlen($chunk, $this->mb_enc);
  7443. // Use GPOS OTL
  7444. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
  7445. if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
  7446. $nch -= substr_count($cOTLdata[$aord]['group'], 'M');
  7447. }
  7448. }
  7449. $stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE );
  7450. $stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE );
  7451. if (isset($this->objectbuffer[$k])) {
  7452. // LIST MARKERS // mPDF 6 Lists
  7453. if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
  7454. $stringWidth = 0;
  7455. } else {
  7456. $stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];
  7457. }
  7458. }
  7459. if ($stringWidth == 0) {
  7460. $stringWidth = 0.000001;
  7461. }
  7462. if ($aord == $arraysize - 1) {
  7463. $stringWidth -= ( $this->charspacing / Mpdf::SCALE );
  7464. if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {
  7465. // force-end overhang
  7466. $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));
  7467. $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));
  7468. } else {
  7469. $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)
  7470. }
  7471. } else {
  7472. $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
  7473. }
  7474. if (!empty($this->spanborddet)) {
  7475. if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) {
  7476. $this->x += $this->spanborddet['R']['w'];
  7477. }
  7478. }
  7479. // *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //
  7480. if (isset($spanfill) && $spanfill) {
  7481. $fill = $save_fill;
  7482. $spanfill = 0;
  7483. if ($fill) {
  7484. $this->SetFColor($bcor);
  7485. }
  7486. }
  7487. if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {
  7488. $this->SetVisibility($save_vis);
  7489. }
  7490. }
  7491. } elseif ($table_draft) {
  7492. $this->y += $stackHeight;
  7493. }
  7494. if (!$is_table) {
  7495. $this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin']));
  7496. $this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin']));
  7497. }
  7498. // move on to the next line, reset variables, tack on saved content and current char
  7499. if (!$table_draft) {
  7500. $this->printobjectbuffer($is_table, $blockdir);
  7501. }
  7502. $this->objectbuffer = [];
  7503. /* -- CSS-IMAGE-FLOAT -- */
  7504. // Update values if set to skipline
  7505. if ($this->floatmargins) {
  7506. $this->_advanceFloatMargins();
  7507. }
  7508. /* -- END CSS-IMAGE-FLOAT -- */
  7509. // Reset lineheight
  7510. $stackHeight = $this->divheight;
  7511. $valign = 'M';
  7512. $font = [];
  7513. $content = [];
  7514. $contentB = [];
  7515. $cOTLdata = []; // mPDF 5.7.1
  7516. $contentWidth = 0;
  7517. if (!empty($savedObj)) {
  7518. $this->objectbuffer[] = $savedObj;
  7519. $font[] = $savedFont;
  7520. $content[] = '';
  7521. $contentB[] = '';
  7522. $cOTLdata[] = []; // mPDF 5.7.1
  7523. $contentWidth += $savedObj['OUTER-WIDTH'] * Mpdf::SCALE;
  7524. }
  7525. if (count($savedPreContent) > 0) {
  7526. for ($ix = count($savedPreContent) - 1; $ix >= 0; $ix--) {
  7527. $font[] = $savedPreFont[$ix];
  7528. $content[] = $savedPreContent[$ix];
  7529. $contentB[] = $savedPreContentB[$ix];
  7530. if (!empty($sOTLdata)) {
  7531. $cOTLdata[] = $savedPreOTLdata[$ix];
  7532. }
  7533. $this->restoreFont($savedPreFont[$ix]);
  7534. $lbw = $rbw = 0; // Border widths
  7535. if (!empty($this->spanborddet)) {
  7536. $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
  7537. $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
  7538. }
  7539. if ($ix > 0) {
  7540. $contentWidth += $this->GetStringWidth($savedPreContent[$ix], true, (isset($savedPreOTLdata[$ix]) ? $savedPreOTLdata[$ix] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1
  7541. if (strpos($savedPreContentB[$ix], 'L') !== false) {
  7542. $contentWidth += $lbw;
  7543. }
  7544. if (strpos($savedPreContentB[$ix], 'R') !== false) {
  7545. $contentWidth += $rbw;
  7546. }
  7547. }
  7548. }
  7549. $savedPreContent = [];
  7550. $savedPreContentB = [];
  7551. $savedPreOTLdata = []; // mPDF 5.7.1
  7552. $savedPreFont = [];
  7553. $content[(count($content) - 1)] .= $c;
  7554. } else {
  7555. $font[] = $savedFont;
  7556. $content[] = $savedContent . $c;
  7557. $contentB[] = $savedContentB;
  7558. $cOTLdata[] = $savedOTLdata; // mPDF 5.7.1
  7559. }
  7560. $currContent = & $content[(count($content) - 1)];
  7561. $this->restoreFont($font[(count($font) - 1)]); // mPDF 6.0
  7562. /* -- CJK-FONTS -- */
  7563. // CJK - strip CJK space at start of line
  7564. // &#x3000; = \xe3\x80\x80 = CJK space
  7565. if ($this->checkCJK && $currContent == "\xe3\x80\x80") {
  7566. $currContent = '';
  7567. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  7568. $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], true, false); // left trim U+3000
  7569. }
  7570. }
  7571. /* -- END CJK-FONTS -- */
  7572. $lbw = $rbw = 0; // Border widths
  7573. if (!empty($this->spanborddet)) {
  7574. $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
  7575. $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
  7576. }
  7577. $contentWidth += $this->GetStringWidth($currContent, false, (isset($cOTLdata[(count($cOTLdata) - 1)]) ? $cOTLdata[(count($cOTLdata) - 1)] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1
  7578. if (strpos($savedContentB, 'L') !== false) {
  7579. $contentWidth += $lbw;
  7580. }
  7581. $CJKoverflow = false;
  7582. $hanger = '';
  7583. } // another character will fit, so add it on
  7584. else {
  7585. $contentWidth += $cw;
  7586. $currContent .= $c;
  7587. }
  7588. }
  7589. unset($content);
  7590. unset($contentB);
  7591. }
  7592. // ----------------------END OF FLOWING BLOCK------------------------------------//
  7593. /* -- CSS-IMAGE-FLOAT -- */
  7594. // Update values if set to skipline
  7595. function _advanceFloatMargins()
  7596. {
  7597. // Update floatmargins - L
  7598. if (isset($this->floatmargins['L']) && $this->floatmargins['L']['skipline'] && $this->floatmargins['L']['y0'] != $this->y) {
  7599. $yadj = $this->y - $this->floatmargins['L']['y0'];
  7600. $this->floatmargins['L']['y0'] = $this->y;
  7601. $this->floatmargins['L']['y1'] += $yadj;
  7602. // Update objattr in floatbuffer
  7603. if ($this->floatbuffer[$this->floatmargins['L']['id']]['border_left']['w']) {
  7604. $this->floatbuffer[$this->floatmargins['L']['id']]['BORDER-Y'] += $yadj;
  7605. }
  7606. $this->floatbuffer[$this->floatmargins['L']['id']]['INNER-Y'] += $yadj;
  7607. $this->floatbuffer[$this->floatmargins['L']['id']]['OUTER-Y'] += $yadj;
  7608. // Unset values
  7609. $this->floatbuffer[$this->floatmargins['L']['id']]['skipline'] = false;
  7610. $this->floatmargins['L']['skipline'] = false;
  7611. $this->floatmargins['L']['id'] = '';
  7612. }
  7613. // Update floatmargins - R
  7614. if (isset($this->floatmargins['R']) && $this->floatmargins['R']['skipline'] && $this->floatmargins['R']['y0'] != $this->y) {
  7615. $yadj = $this->y - $this->floatmargins['R']['y0'];
  7616. $this->floatmargins['R']['y0'] = $this->y;
  7617. $this->floatmargins['R']['y1'] += $yadj;
  7618. // Update objattr in floatbuffer
  7619. if ($this->floatbuffer[$this->floatmargins['R']['id']]['border_left']['w']) {
  7620. $this->floatbuffer[$this->floatmargins['R']['id']]['BORDER-Y'] += $yadj;
  7621. }
  7622. $this->floatbuffer[$this->floatmargins['R']['id']]['INNER-Y'] += $yadj;
  7623. $this->floatbuffer[$this->floatmargins['R']['id']]['OUTER-Y'] += $yadj;
  7624. // Unset values
  7625. $this->floatbuffer[$this->floatmargins['R']['id']]['skipline'] = false;
  7626. $this->floatmargins['R']['skipline'] = false;
  7627. $this->floatmargins['R']['id'] = '';
  7628. }
  7629. }
  7630. /* -- END CSS-IMAGE-FLOAT -- */
  7631. /* -- END HTML-CSS -- */
  7632. function _SetTextRendering($mode)
  7633. {
  7634. if (!(($mode == 0) || ($mode == 1) || ($mode == 2))) {
  7635. throw new \Mpdf\MpdfException("Text rendering mode should be 0, 1 or 2 (value : $mode)");
  7636. }
  7637. $tr = ($mode . ' Tr');
  7638. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
  7639. $this->writer->write($tr);
  7640. }
  7641. $this->pageoutput[$this->page]['TextRendering'] = $tr;
  7642. }
  7643. function SetTextOutline($params = [])
  7644. {
  7645. if (isset($params['outline-s']) && $params['outline-s']) {
  7646. $this->SetLineWidth($params['outline-WIDTH']);
  7647. $this->SetDColor($params['outline-COLOR']);
  7648. $tr = ('2 Tr');
  7649. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
  7650. $this->writer->write($tr);
  7651. }
  7652. $this->pageoutput[$this->page]['TextRendering'] = $tr;
  7653. } else { // Now resets all values
  7654. $this->SetLineWidth(0.2);
  7655. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  7656. $this->_SetTextRendering(0);
  7657. $tr = ('0 Tr');
  7658. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
  7659. $this->writer->write($tr);
  7660. }
  7661. $this->pageoutput[$this->page]['TextRendering'] = $tr;
  7662. }
  7663. }
  7664. function Image($file, $x, $y, $w = 0, $h = 0, $type = '', $link = '', $paint = true, $constrain = true, $watermark = false, $shownoimg = true, $allowvector = true)
  7665. {
  7666. $orig_srcpath = $file;
  7667. $this->GetFullPath($file);
  7668. $info = $this->imageProcessor->getImage($file, true, $allowvector, $orig_srcpath);
  7669. if (!$info && $paint) {
  7670. $info = $this->imageProcessor->getImage($this->noImageFile);
  7671. if ($info) {
  7672. $file = $this->noImageFile;
  7673. $w = ($info['w'] * (25.4 / $this->img_dpi)); // 14 x 16px
  7674. $h = ($info['h'] * (25.4 / $this->img_dpi)); // 14 x 16px
  7675. }
  7676. }
  7677. if (!$info) {
  7678. return false;
  7679. }
  7680. // Automatic width and height calculation if needed
  7681. if ($w == 0 and $h == 0) {
  7682. /* -- IMAGES-WMF -- */
  7683. if ($info['type'] === 'wmf') {
  7684. // WMF units are twips (1/20pt)
  7685. // divide by 20 to get points
  7686. // divide by k to get user units
  7687. $w = abs($info['w']) / (20 * Mpdf::SCALE);
  7688. $h = abs($info['h']) / (20 * Mpdf::SCALE);
  7689. } else { /* -- END IMAGES-WMF -- */
  7690. if ($info['type'] === 'svg') {
  7691. // returned SVG units are pts
  7692. // divide by k to get user units (mm)
  7693. $w = abs($info['w']) / Mpdf::SCALE;
  7694. $h = abs($info['h']) / Mpdf::SCALE;
  7695. } else {
  7696. // Put image at default image dpi
  7697. $w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi);
  7698. $h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi);
  7699. }
  7700. }
  7701. }
  7702. if ($w == 0) {
  7703. $w = abs($h * $info['w'] / $info['h']);
  7704. }
  7705. if ($h == 0) {
  7706. $h = abs($w * $info['h'] / $info['w']);
  7707. }
  7708. /* -- WATERMARK -- */
  7709. if ($watermark) {
  7710. $maxw = $this->w;
  7711. $maxh = $this->h;
  7712. // Size = D PF or array
  7713. if (is_array($this->watermark_size)) {
  7714. $w = $this->watermark_size[0];
  7715. $h = $this->watermark_size[1];
  7716. } elseif (!is_string($this->watermark_size)) {
  7717. $maxw -= $this->watermark_size * 2;
  7718. $maxh -= $this->watermark_size * 2;
  7719. $w = $maxw;
  7720. $h = abs($w * $info['h'] / $info['w']);
  7721. if ($h > $maxh) {
  7722. $h = $maxh;
  7723. $w = abs($h * $info['w'] / $info['h']);
  7724. }
  7725. } elseif ($this->watermark_size == 'F') {
  7726. if ($this->ColActive) {
  7727. $maxw = $this->w - ($this->DeflMargin + $this->DefrMargin);
  7728. } else {
  7729. $maxw = $this->pgwidth;
  7730. }
  7731. $maxh = $this->h - ($this->tMargin + $this->bMargin);
  7732. $w = $maxw;
  7733. $h = abs($w * $info['h'] / $info['w']);
  7734. if ($h > $maxh) {
  7735. $h = $maxh;
  7736. $w = abs($h * $info['w'] / $info['h']);
  7737. }
  7738. } elseif ($this->watermark_size == 'P') { // Default P
  7739. $w = $maxw;
  7740. $h = abs($w * $info['h'] / $info['w']);
  7741. if ($h > $maxh) {
  7742. $h = $maxh;
  7743. $w = abs($h * $info['w'] / $info['h']);
  7744. }
  7745. }
  7746. // Automatically resize to maximum dimensions of page if too large
  7747. if ($w > $maxw) {
  7748. $w = $maxw;
  7749. $h = abs($w * $info['h'] / $info['w']);
  7750. }
  7751. if ($h > $maxh) {
  7752. $h = $maxh;
  7753. $w = abs($h * $info['w'] / $info['h']);
  7754. }
  7755. // Position
  7756. if (is_array($this->watermark_pos)) {
  7757. $x = $this->watermark_pos[0];
  7758. $y = $this->watermark_pos[1];
  7759. } elseif ($this->watermark_pos == 'F') { // centred on printable area
  7760. if ($this->ColActive) { // *COLUMNS*
  7761. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) {
  7762. $xadj = $this->DeflMargin - $this->DefrMargin;
  7763. } // *COLUMNS*
  7764. else {
  7765. $xadj = 0;
  7766. } // *COLUMNS*
  7767. $x = ($this->DeflMargin - $xadj + ($this->w - ($this->DeflMargin + $this->DefrMargin)) / 2) - ($w / 2); // *COLUMNS*
  7768. } // *COLUMNS*
  7769. else { // *COLUMNS*
  7770. $x = ($this->lMargin + ($this->pgwidth) / 2) - ($w / 2);
  7771. } // *COLUMNS*
  7772. $y = ($this->tMargin + ($this->h - ($this->tMargin + $this->bMargin)) / 2) - ($h / 2);
  7773. } else { // default P - centred on whole page
  7774. $x = ($this->w / 2) - ($w / 2);
  7775. $y = ($this->h / 2) - ($h / 2);
  7776. }
  7777. /* -- IMAGES-WMF -- */
  7778. if ($info['type'] == 'wmf') {
  7779. $sx = $w * Mpdf::SCALE / $info['w'];
  7780. $sy = -$h * Mpdf::SCALE / $info['h'];
  7781. $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']);
  7782. } else { /* -- END IMAGES-WMF -- */
  7783. if ($info['type'] == 'svg') {
  7784. $sx = $w * Mpdf::SCALE / $info['w'];
  7785. $sy = -$h * Mpdf::SCALE / $info['h'];
  7786. $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']);
  7787. } else {
  7788. $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']);
  7789. }
  7790. }
  7791. if ($this->watermarkImgBehind) {
  7792. $outstring = $this->watermarkImgAlpha . "\n" . $outstring . "\n" . $this->SetAlpha(1, 'Normal', true) . "\n";
  7793. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $outstring . "\n" . '\\1', $this->pages[$this->page]);
  7794. } else {
  7795. $this->writer->write($outstring);
  7796. }
  7797. return 0;
  7798. } // end of IF watermark
  7799. /* -- END WATERMARK -- */
  7800. if ($constrain) {
  7801. // Automatically resize to maximum dimensions of page if too large
  7802. if (isset($this->blk[$this->blklvl]['inner_width']) && $this->blk[$this->blklvl]['inner_width']) {
  7803. $maxw = $this->blk[$this->blklvl]['inner_width'];
  7804. } else {
  7805. $maxw = $this->pgwidth;
  7806. }
  7807. if ($w > $maxw) {
  7808. $w = $maxw;
  7809. $h = abs($w * $info['h'] / $info['w']);
  7810. }
  7811. if ($h > $this->h - ($this->tMargin + $this->bMargin + 1)) { // see below - +10 to avoid drawing too close to border of page
  7812. $h = $this->h - ($this->tMargin + $this->bMargin + 1);
  7813. if ($this->fullImageHeight) {
  7814. $h = $this->fullImageHeight;
  7815. }
  7816. $w = abs($h * $info['w'] / $info['h']);
  7817. }
  7818. // Avoid drawing out of the paper(exceeding width limits).
  7819. // if ( ($x + $w) > $this->fw ) {
  7820. if (($x + $w) > $this->w) {
  7821. $x = $this->lMargin;
  7822. $y += 5;
  7823. }
  7824. $changedpage = false;
  7825. $oldcolumn = $this->CurrCol;
  7826. // Avoid drawing out of the page.
  7827. if ($y + $h > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {
  7828. $this->AddPage($this->CurOrientation);
  7829. // Added to correct for OddEven Margins
  7830. $x = $x + $this->MarginCorrection;
  7831. $y = $this->tMargin; // mPDF 5.7.3
  7832. $changedpage = true;
  7833. }
  7834. /* -- COLUMNS -- */
  7835. // COLS
  7836. // COLUMN CHANGE
  7837. if ($this->CurrCol != $oldcolumn) {
  7838. $y = $this->y0;
  7839. $x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  7840. $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  7841. }
  7842. /* -- END COLUMNS -- */
  7843. } // end of IF constrain
  7844. /* -- IMAGES-WMF -- */
  7845. if ($info['type'] == 'wmf') {
  7846. $sx = $w * Mpdf::SCALE / $info['w'];
  7847. $sy = -$h * Mpdf::SCALE / $info['h'];
  7848. $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']);
  7849. } else { /* -- END IMAGES-WMF -- */
  7850. if ($info['type'] == 'svg') {
  7851. $sx = $w * Mpdf::SCALE / $info['w'];
  7852. $sy = -$h * Mpdf::SCALE / $info['h'];
  7853. $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']);
  7854. } else {
  7855. $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']);
  7856. }
  7857. }
  7858. if ($paint) {
  7859. $this->writer->write($outstring);
  7860. if ($link) {
  7861. $this->Link($x, $y, $w, $h, $link);
  7862. }
  7863. // Avoid writing text on top of the image. // THIS WAS OUTSIDE THE if ($paint) bit!!!!!!!!!!!!!!!!
  7864. $this->y = $y + $h;
  7865. }
  7866. // Return width-height array
  7867. $sizesarray['WIDTH'] = $w;
  7868. $sizesarray['HEIGHT'] = $h;
  7869. $sizesarray['X'] = $x; // Position before painting image
  7870. $sizesarray['Y'] = $y; // Position before painting image
  7871. $sizesarray['OUTPUT'] = $outstring;
  7872. $sizesarray['IMAGE_ID'] = $info['i'];
  7873. $sizesarray['itype'] = $info['type'];
  7874. $sizesarray['set-dpi'] = (isset($info['set-dpi']) ? $info['set-dpi'] : 0);
  7875. return $sizesarray;
  7876. }
  7877. // =============================================================
  7878. // =============================================================
  7879. // =============================================================
  7880. // =============================================================
  7881. // =============================================================
  7882. /* -- HTML-CSS -- */
  7883. function _getObjAttr($t)
  7884. {
  7885. $c = explode(Mpdf::OBJECT_IDENTIFIER, $t, 2);
  7886. $c = explode(',', $c[1], 2);
  7887. foreach ($c as $v) {
  7888. $v = explode('=', $v, 2);
  7889. $sp[$v[0]] = trim($v[1], Mpdf::OBJECT_IDENTIFIER);
  7890. }
  7891. return (unserialize($sp['objattr']));
  7892. }
  7893. function inlineObject($type, $x, $y, $objattr, $Lmargin, $widthUsed, $maxWidth, $lineHeight, $paint = false, $is_table = false)
  7894. {
  7895. if ($is_table) {
  7896. $k = $this->shrin_k;
  7897. } else {
  7898. $k = 1;
  7899. }
  7900. // NB $x is only used when paint=true
  7901. // Lmargin not used
  7902. $w = 0;
  7903. if (isset($objattr['width'])) {
  7904. $w = $objattr['width'] / $k;
  7905. }
  7906. $h = 0;
  7907. if (isset($objattr['height'])) {
  7908. $h = abs($objattr['height'] / $k);
  7909. }
  7910. $widthLeft = $maxWidth - $widthUsed;
  7911. $maxHeight = $this->h - ($this->tMargin + $this->bMargin + 10);
  7912. if ($this->fullImageHeight) {
  7913. $maxHeight = $this->fullImageHeight;
  7914. }
  7915. // For Images
  7916. if (isset($objattr['border_left'])) {
  7917. $extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']) / $k;
  7918. $extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']) / $k;
  7919. if ($type == 'image' || $type == 'barcode' || $type == 'textcircle') {
  7920. $extraWidth += ($objattr['padding_left'] + $objattr['padding_right']) / $k;
  7921. $extraHeight += ($objattr['padding_top'] + $objattr['padding_bottom']) / $k;
  7922. }
  7923. }
  7924. if (!isset($objattr['vertical-align'])) {
  7925. if ($objattr['type'] == 'select') {
  7926. $objattr['vertical-align'] = 'M';
  7927. } else {
  7928. $objattr['vertical-align'] = 'BS';
  7929. }
  7930. } // mPDF 6
  7931. if ($type == 'image' || (isset($objattr['subtype']) && $objattr['subtype'] == 'IMAGE')) {
  7932. if (isset($objattr['itype']) && ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg')) {
  7933. $file = $objattr['file'];
  7934. $info = $this->formobjects[$file];
  7935. } elseif (isset($objattr['file'])) {
  7936. $file = $objattr['file'];
  7937. $info = $this->images[$file];
  7938. }
  7939. }
  7940. if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {
  7941. $w = 0.00001;
  7942. $h = 0.00001;
  7943. }
  7944. // TEST whether need to skipline
  7945. if (!$paint) {
  7946. if ($type == 'hr') { // always force new line
  7947. if (($y + $h + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {
  7948. return [-2, $w, $h];
  7949. } // New page + new line
  7950. else {
  7951. return [1, $w, $h];
  7952. } // new line
  7953. } else {
  7954. // LIST MARKERS // mPDF 6 Lists
  7955. $displayheight = $h;
  7956. $displaywidth = $w;
  7957. if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker']) {
  7958. $displayheight = 0;
  7959. if ($objattr['listmarkerposition'] == 'outside') {
  7960. $displaywidth = 0;
  7961. }
  7962. }
  7963. if ($widthUsed > 0 && $displaywidth > $widthLeft && (!$is_table || $type != 'image')) { // New line needed
  7964. // mPDF 6 Lists
  7965. if (($y + $displayheight + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter) {
  7966. return [-2, $w, $h];
  7967. } // New page + new line
  7968. return [1, $w, $h]; // new line
  7969. } elseif ($widthUsed > 0 && $displaywidth > $widthLeft && $is_table) { // New line needed in TABLE
  7970. return [1, $w, $h]; // new line
  7971. } // Will fit on line but NEW PAGE REQUIRED
  7972. elseif (($y + $displayheight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {
  7973. return [-1, $w, $h];
  7974. } // mPDF 6 Lists
  7975. else {
  7976. return [0, $w, $h];
  7977. }
  7978. }
  7979. }
  7980. if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {
  7981. $w = 0.00001;
  7982. $h = 0.00001;
  7983. $objattr['BORDER-WIDTH'] = 0;
  7984. $objattr['BORDER-HEIGHT'] = 0;
  7985. $objattr['BORDER-X'] = $x;
  7986. $objattr['BORDER-Y'] = $y;
  7987. $objattr['INNER-WIDTH'] = 0;
  7988. $objattr['INNER-HEIGHT'] = 0;
  7989. $objattr['INNER-X'] = $x;
  7990. $objattr['INNER-Y'] = $y;
  7991. }
  7992. if ($type == 'image') {
  7993. // Automatically resize to width remaining
  7994. if ($w > ($widthLeft + 0.0001) && !$is_table) { // mPDF 5.7.4 0.0001 to allow for rounding errors when w==maxWidth
  7995. $w = $widthLeft;
  7996. $h = abs($w * $info['h'] / $info['w']);
  7997. }
  7998. $img_w = $w - $extraWidth;
  7999. $img_h = $h - $extraHeight;
  8000. $objattr['BORDER-WIDTH'] = $img_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
  8001. $objattr['BORDER-HEIGHT'] = $img_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
  8002. $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
  8003. $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
  8004. $objattr['INNER-WIDTH'] = $img_w;
  8005. $objattr['INNER-HEIGHT'] = $img_h;
  8006. $objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
  8007. $objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
  8008. $objattr['ID'] = $info['i'];
  8009. }
  8010. if ($type == 'input' && $objattr['subtype'] == 'IMAGE') {
  8011. $img_w = $w - $extraWidth;
  8012. $img_h = $h - $extraHeight;
  8013. $objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
  8014. $objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
  8015. $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
  8016. $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
  8017. $objattr['INNER-WIDTH'] = $img_w;
  8018. $objattr['INNER-HEIGHT'] = $img_h;
  8019. $objattr['INNER-X'] = $x + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
  8020. $objattr['INNER-Y'] = $y + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
  8021. $objattr['ID'] = $info['i'];
  8022. }
  8023. if ($type == 'barcode' || $type == 'textcircle') {
  8024. $b_w = $w - $extraWidth;
  8025. $b_h = $h - $extraHeight;
  8026. $objattr['BORDER-WIDTH'] = $b_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
  8027. $objattr['BORDER-HEIGHT'] = $b_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
  8028. $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
  8029. $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
  8030. $objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
  8031. $objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
  8032. $objattr['INNER-WIDTH'] = $b_w;
  8033. $objattr['INNER-HEIGHT'] = $b_h;
  8034. }
  8035. if ($type == 'textarea') {
  8036. // Automatically resize to width remaining
  8037. if ($w > $widthLeft && !$is_table) {
  8038. $w = $widthLeft;
  8039. }
  8040. // This used to resize height to maximum remaining on page ? why. Causes problems when in table and causing a new column
  8041. // if (($y + $h > $this->PageBreakTrigger) && !$this->InFooter) {
  8042. // $h=$this->h - $y - $this->bMargin;
  8043. // }
  8044. }
  8045. if ($type == 'hr') {
  8046. if ($is_table) {
  8047. $objattr['INNER-WIDTH'] = $maxWidth * $objattr['W-PERCENT'] / 100;
  8048. $objattr['width'] = $objattr['INNER-WIDTH'];
  8049. $w = $maxWidth;
  8050. } else {
  8051. if ($w > $maxWidth) {
  8052. $w = $maxWidth;
  8053. }
  8054. $objattr['INNER-WIDTH'] = $w;
  8055. $w = $maxWidth;
  8056. }
  8057. }
  8058. if (($type == 'select') || ($type == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD'))) {
  8059. // Automatically resize to width remaining
  8060. if ($w > $widthLeft && !$is_table) {
  8061. $w = $widthLeft;
  8062. }
  8063. }
  8064. if ($type == 'textarea' || $type == 'select' || $type == 'input') {
  8065. if (isset($objattr['fontsize'])) {
  8066. $objattr['fontsize'] /= $k;
  8067. }
  8068. if (isset($objattr['linewidth'])) {
  8069. $objattr['linewidth'] /= $k;
  8070. }
  8071. }
  8072. if (!isset($objattr['BORDER-Y'])) {
  8073. $objattr['BORDER-Y'] = 0;
  8074. }
  8075. if (!isset($objattr['BORDER-X'])) {
  8076. $objattr['BORDER-X'] = 0;
  8077. }
  8078. if (!isset($objattr['INNER-Y'])) {
  8079. $objattr['INNER-Y'] = 0;
  8080. }
  8081. if (!isset($objattr['INNER-X'])) {
  8082. $objattr['INNER-X'] = 0;
  8083. }
  8084. // Return width-height array
  8085. $objattr['OUTER-WIDTH'] = $w;
  8086. $objattr['OUTER-HEIGHT'] = $h;
  8087. $objattr['OUTER-X'] = $x;
  8088. $objattr['OUTER-Y'] = $y;
  8089. return $objattr;
  8090. }
  8091. /* -- END HTML-CSS -- */
  8092. // =============================================================
  8093. // =============================================================
  8094. // =============================================================
  8095. // =============================================================
  8096. // =============================================================
  8097. function SetLineJoin($mode = 0)
  8098. {
  8099. $s = sprintf('%d j', $mode);
  8100. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineJoin']) && $this->pageoutput[$this->page]['LineJoin'] != $s) || !isset($this->pageoutput[$this->page]['LineJoin']))) {
  8101. $this->writer->write($s);
  8102. }
  8103. $this->pageoutput[$this->page]['LineJoin'] = $s;
  8104. }
  8105. function SetLineCap($mode = 2)
  8106. {
  8107. $s = sprintf('%d J', $mode);
  8108. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineCap']) && $this->pageoutput[$this->page]['LineCap'] != $s) || !isset($this->pageoutput[$this->page]['LineCap']))) {
  8109. $this->writer->write($s);
  8110. }
  8111. $this->pageoutput[$this->page]['LineCap'] = $s;
  8112. }
  8113. function SetDash($black = false, $white = false)
  8114. {
  8115. if ($black and $white) {
  8116. $s = sprintf('[%.3F %.3F] 0 d', $black * Mpdf::SCALE, $white * Mpdf::SCALE);
  8117. } else {
  8118. $s = '[] 0 d';
  8119. }
  8120. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Dash']) && $this->pageoutput[$this->page]['Dash'] != $s) || !isset($this->pageoutput[$this->page]['Dash']))) {
  8121. $this->writer->write($s);
  8122. }
  8123. $this->pageoutput[$this->page]['Dash'] = $s;
  8124. }
  8125. function SetDisplayPreferences($preferences)
  8126. {
  8127. // String containing any or none of /HideMenubar/HideToolbar/HideWindowUI/DisplayDocTitle/CenterWindow/FitWindow
  8128. $this->DisplayPreferences .= $preferences;
  8129. }
  8130. function Ln($h = '', $collapsible = 0)
  8131. {
  8132. // Added collapsible to allow collapsible top-margin on new page
  8133. // Line feed; default value is last cell height
  8134. $margin = isset($this->blk[$this->blklvl]['outer_left_margin']) ? $this->blk[$this->blklvl]['outer_left_margin'] : 0;
  8135. $this->x = $this->lMargin + $margin;
  8136. if ($collapsible && ($this->y == $this->tMargin) && (!$this->ColActive)) {
  8137. $h = 0;
  8138. }
  8139. if (is_string($h)) {
  8140. $this->y += $this->lasth;
  8141. } else {
  8142. $this->y += $h;
  8143. }
  8144. }
  8145. /* -- HTML-CSS -- */
  8146. function DivLn($h, $level = -3, $move_y = true, $collapsible = false, $state = 0)
  8147. {
  8148. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  8149. // Used in Columns and keep-with-table i.e. "kwt"
  8150. // writes background block by block so it can be repositioned
  8151. // and also used in writingFlowingBlock at top and bottom of blocks to move y (not to draw/paint anything)
  8152. // adds lines (y) where DIV bgcolors are filled in
  8153. // this->x is returned as it was
  8154. // allows .00001 as nominal height used for bookmarks/annotations etc.
  8155. if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)) && (!$this->ColActive)) {
  8156. return;
  8157. }
  8158. // mPDF 6 Columns
  8159. // if ($collapsible && (sprintf("%0.4f", $this->y)==sprintf("%0.4f", $this->y0)) && ($this->ColActive) && $this->CurrCol == 0) { return; } // *COLUMNS*
  8160. if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->y0)) && ($this->ColActive)) {
  8161. return;
  8162. } // *COLUMNS*
  8163. // Still use this method if columns or keep-with-table, as it allows repositioning later
  8164. // otherwise, now uses PaintDivBB()
  8165. if (!$this->ColActive && !$this->kwt) {
  8166. if ($move_y && !$this->ColActive) {
  8167. $this->y += $h;
  8168. }
  8169. return;
  8170. }
  8171. if ($level == -3) {
  8172. $level = $this->blklvl;
  8173. }
  8174. $firstblockfill = $this->GetFirstBlockFill();
  8175. if ($firstblockfill && $this->blklvl > 0 && $this->blklvl >= $firstblockfill) {
  8176. $last_x = 0;
  8177. $last_w = 0;
  8178. $last_fc = $this->FillColor;
  8179. $bak_x = $this->x;
  8180. $bak_h = $this->divheight;
  8181. $this->divheight = 0; // Temporarily turn off divheight - as Cell() uses it to check for PageBreak
  8182. for ($blvl = $firstblockfill; $blvl <= $level; $blvl++) {
  8183. $this->x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];
  8184. // mPDF 6
  8185. if ($this->blk[$blvl]['bgcolor']) {
  8186. $this->SetFColor($this->blk[$blvl]['bgcolorarray']);
  8187. }
  8188. 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'])) {
  8189. $x = $this->x;
  8190. $this->Cell(($this->blk[$blvl]['width']), $h, '', '', 0, '', 1);
  8191. $this->x = $x;
  8192. if (!$this->keep_block_together && !$this->writingHTMLheader && !$this->writingHTMLfooter) {
  8193. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  8194. if ($blvl == $this->blklvl) {
  8195. $this->PaintDivLnBorder($state, $blvl, $h);
  8196. } else {
  8197. $this->PaintDivLnBorder(0, $blvl, $h);
  8198. }
  8199. }
  8200. }
  8201. $last_x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];
  8202. $last_w = $this->blk[$blvl]['width'];
  8203. $last_fc = $this->FillColor;
  8204. }
  8205. // Reset current block fill
  8206. if (isset($this->blk[$this->blklvl]['bgcolorarray'])) {
  8207. $bcor = $this->blk[$this->blklvl]['bgcolorarray'];
  8208. $this->SetFColor($bcor);
  8209. }
  8210. $this->x = $bak_x;
  8211. $this->divheight = $bak_h;
  8212. }
  8213. if ($move_y) {
  8214. $this->y += $h;
  8215. }
  8216. }
  8217. /* -- END HTML-CSS -- */
  8218. function SetX($x)
  8219. {
  8220. // Set x position
  8221. if ($x >= 0) {
  8222. $this->x = $x;
  8223. } else {
  8224. $this->x = $this->w + $x;
  8225. }
  8226. }
  8227. function SetY($y)
  8228. {
  8229. // Set y position and reset x
  8230. $this->x = $this->lMargin;
  8231. if ($y >= 0) {
  8232. $this->y = $y;
  8233. } else {
  8234. $this->y = $this->h + $y;
  8235. }
  8236. }
  8237. function SetXY($x, $y)
  8238. {
  8239. // Set x and y positions
  8240. $this->SetY($y);
  8241. $this->SetX($x);
  8242. }
  8243. function Output($name = '', $dest = '')
  8244. {
  8245. $this->logger->debug(sprintf('PDF generated in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]);
  8246. // Finish document if necessary
  8247. if ($this->state < 3) {
  8248. $this->Close();
  8249. }
  8250. if ($this->debug && error_get_last()) {
  8251. $e = error_get_last();
  8252. if (($e['type'] < 2048 && $e['type'] != 8) || (intval($e['type']) & intval(ini_get("error_reporting")))) {
  8253. throw new \Mpdf\MpdfException(
  8254. sprintf('Error detected. PDF file generation aborted: %s', $e['message']),
  8255. $e['type'],
  8256. 1,
  8257. $e['file'],
  8258. $e['line']
  8259. );
  8260. }
  8261. }
  8262. if (($this->PDFA || $this->PDFX) && $this->encrypted) {
  8263. throw new \Mpdf\MpdfException('PDF/A1-b or PDF/X1-a does not permit encryption of documents.');
  8264. }
  8265. if (count($this->PDFAXwarnings) && (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto))) {
  8266. if ($this->PDFA) {
  8267. $standard = 'PDFA/1-b';
  8268. $option = '$mpdf->PDFAauto';
  8269. } else {
  8270. $standard = 'PDFX/1-a ';
  8271. $option = '$mpdf->PDFXauto';
  8272. }
  8273. $this->logger->warning(sprintf('PDF could not be generated as it stands as a %s compliant file.', $standard), ['context' => LogContext::PDFA_PDFX]);
  8274. $this->logger->warning(sprintf('These issues can be automatically fixed by mPDF using %s = true;', $option), ['context' => LogContext::PDFA_PDFX]);
  8275. $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]);
  8276. $this->PDFAXwarnings = array_unique($this->PDFAXwarnings);
  8277. foreach ($this->PDFAXwarnings as $w) {
  8278. $this->logger->warning($w, ['context' => LogContext::PDFA_PDFX]);
  8279. }
  8280. throw new \Mpdf\MpdfException('PDFA/PDFX warnings generated. See log for further details');
  8281. }
  8282. $this->logger->debug(sprintf('Compiled in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]);
  8283. $this->logger->debug(sprintf('Peak Memory usage %s MB', number_format(memory_get_peak_usage(true) / (1024 * 1024), 2)), ['context' => LogContext::STATISTICS]);
  8284. $this->logger->debug(sprintf('PDF file size %s kB', number_format(strlen($this->buffer) / 1024)), ['context' => LogContext::STATISTICS]);
  8285. $this->logger->debug(sprintf('%d fonts used', count($this->fonts)), ['context' => LogContext::STATISTICS]);
  8286. if (is_bool($dest)) {
  8287. $dest = $dest ? Destination::DOWNLOAD : Destination::FILE;
  8288. }
  8289. $dest = strtoupper($dest);
  8290. if (empty($dest)) {
  8291. if (empty($name)) {
  8292. $name = 'mpdf.pdf';
  8293. $dest = Destination::INLINE;
  8294. } else {
  8295. $dest = Destination::FILE;
  8296. }
  8297. }
  8298. switch ($dest) {
  8299. case Destination::INLINE:
  8300. if (headers_sent($filename, $line)) {
  8301. throw new \Mpdf\MpdfException(
  8302. sprintf('Data has already been sent to output (%s at line %s), unable to output PDF file', $filename, $line)
  8303. );
  8304. }
  8305. if ($this->debug && !$this->allow_output_buffering && ob_get_contents()) {
  8306. throw new \Mpdf\MpdfException('Output has already been sent from the script - PDF file generation aborted.');
  8307. }
  8308. // We send to a browser
  8309. if (PHP_SAPI !== 'cli') {
  8310. header('Content-Type: application/pdf');
  8311. if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  8312. // don't use length if server using compression
  8313. header('Content-Length: ' . strlen($this->buffer));
  8314. }
  8315. header('Content-disposition: inline; filename="' . $name . '"');
  8316. header('Cache-Control: public, must-revalidate, max-age=0');
  8317. header('Pragma: public');
  8318. header('X-Generator: mPDF' . ($this->exposeVersion ? (' ' . static::VERSION) : ''));
  8319. header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
  8320. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  8321. }
  8322. echo $this->buffer;
  8323. break;
  8324. case Destination::DOWNLOAD:
  8325. if (headers_sent()) {
  8326. throw new \Mpdf\MpdfException('Data has already been sent to output, unable to output PDF file');
  8327. }
  8328. header('Content-Description: File Transfer');
  8329. header('Content-Transfer-Encoding: binary');
  8330. header('Cache-Control: public, must-revalidate, max-age=0');
  8331. header('Pragma: public');
  8332. header('X-Generator: mPDF' . ($this->exposeVersion ? (' ' . static::VERSION) : ''));
  8333. header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
  8334. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  8335. header('Content-Type: application/pdf');
  8336. if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  8337. // don't use length if server using compression
  8338. header('Content-Length: ' . strlen($this->buffer));
  8339. }
  8340. header('Content-Disposition: attachment; filename="' . $name . '"');
  8341. echo $this->buffer;
  8342. break;
  8343. case Destination::FILE:
  8344. $f = fopen($name, 'wb');
  8345. if (!$f) {
  8346. throw new \Mpdf\MpdfException(sprintf('Unable to create output file %s', $name));
  8347. }
  8348. fwrite($f, $this->buffer, strlen($this->buffer));
  8349. fclose($f);
  8350. break;
  8351. case Destination::STRING_RETURN:
  8352. $this->cache->clearOld();
  8353. return $this->buffer;
  8354. default:
  8355. throw new \Mpdf\MpdfException(sprintf('Incorrect output destination %s', $dest));
  8356. }
  8357. $this->cache->clearOld();
  8358. }
  8359. public function OutputBinaryData()
  8360. {
  8361. return $this->Output(null, Destination::STRING_RETURN);
  8362. }
  8363. public function OutputHttpInline()
  8364. {
  8365. return $this->Output(null, Destination::INLINE);
  8366. }
  8367. /**
  8368. * @param string $fileName
  8369. */
  8370. public function OutputHttpDownload($fileName)
  8371. {
  8372. return $this->Output($fileName, Destination::DOWNLOAD);
  8373. }
  8374. /**
  8375. * @param string $fileName
  8376. */
  8377. public function OutputFile($fileName)
  8378. {
  8379. return $this->Output($fileName, Destination::FILE);
  8380. }
  8381. // *****************************************************************************
  8382. // *
  8383. // Protected methods *
  8384. // *
  8385. // *****************************************************************************
  8386. function _dochecks()
  8387. {
  8388. // Check for locale-related bug
  8389. if (1.1 == 1) {
  8390. throw new \Mpdf\MpdfException('Do not alter the locale before including mPDF');
  8391. }
  8392. // Check for decimal separator
  8393. if (sprintf('%.1f', 1.0) != '1.0') {
  8394. setlocale(LC_NUMERIC, 'C');
  8395. }
  8396. if (ini_get('mbstring.func_overload')) {
  8397. throw new \Mpdf\MpdfException('Mpdf cannot function properly with mbstring.func_overload enabled');
  8398. }
  8399. if (!function_exists('mb_substr')) {
  8400. throw new \Mpdf\MpdfException('mbstring extension must be loaded in order to run mPDF');
  8401. }
  8402. if (!function_exists('mb_regex_encoding')) {
  8403. $mamp = '';
  8404. if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
  8405. $mamp = ' If using MAMP, there is a bug in its PHP build causing this.';
  8406. }
  8407. throw new \Mpdf\MpdfException('mbstring extension with mbregex support must be loaded in order to run mPDF.' . $mamp);
  8408. }
  8409. }
  8410. function _puthtmlheaders()
  8411. {
  8412. $this->state = 2;
  8413. $nb = $this->page;
  8414. for ($n = 1; $n <= $nb; $n++) {
  8415. if ($this->mirrorMargins && $n % 2 == 0) {
  8416. $OE = 'E';
  8417. } // EVEN
  8418. else {
  8419. $OE = 'O';
  8420. }
  8421. $this->page = $n;
  8422. $pn = $this->docPageNum($n);
  8423. if ($pn) {
  8424. $pnstr = $this->pagenumPrefix . $pn . $this->pagenumSuffix;
  8425. } else {
  8426. $pnstr = '';
  8427. }
  8428. $pnt = $this->docPageNumTotal($n);
  8429. if ($pnt) {
  8430. $pntstr = $this->nbpgPrefix . $pnt . $this->nbpgSuffix;
  8431. } else {
  8432. $pntstr = '';
  8433. }
  8434. if (isset($this->saveHTMLHeader[$n][$OE])) {
  8435. $html = isset($this->saveHTMLHeader[$n][$OE]['html']) ? $this->saveHTMLHeader[$n][$OE]['html'] : '';
  8436. $this->lMargin = $this->saveHTMLHeader[$n][$OE]['ml'];
  8437. $this->rMargin = $this->saveHTMLHeader[$n][$OE]['mr'];
  8438. $this->tMargin = $this->saveHTMLHeader[$n][$OE]['mh'];
  8439. $this->bMargin = $this->saveHTMLHeader[$n][$OE]['mf'];
  8440. $this->margin_header = $this->saveHTMLHeader[$n][$OE]['mh'];
  8441. $this->margin_footer = $this->saveHTMLHeader[$n][$OE]['mf'];
  8442. $this->w = $this->saveHTMLHeader[$n][$OE]['pw'];
  8443. $this->h = $this->saveHTMLHeader[$n][$OE]['ph'];
  8444. if ($this->w > $this->h) {
  8445. $this->hPt = $this->fwPt;
  8446. $this->wPt = $this->fhPt;
  8447. } else {
  8448. $this->hPt = $this->fhPt;
  8449. $this->wPt = $this->fwPt;
  8450. }
  8451. $rotate = (isset($this->saveHTMLHeader[$n][$OE]['rotate']) ? $this->saveHTMLHeader[$n][$OE]['rotate'] : null);
  8452. $this->Reset();
  8453. $this->pageoutput[$n] = [];
  8454. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  8455. $this->x = $this->lMargin;
  8456. $this->y = $this->margin_header;
  8457. // Replace of page number aliases and date format
  8458. $html = $this->aliasReplace($html, $pnstr, $pntstr, $nb);
  8459. $this->HTMLheaderPageLinks = [];
  8460. $this->HTMLheaderPageAnnots = [];
  8461. $this->HTMLheaderPageForms = [];
  8462. $this->pageBackgrounds = [];
  8463. $this->writingHTMLheader = true;
  8464. $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
  8465. $this->writingHTMLheader = false;
  8466. $this->Reset();
  8467. $this->pageoutput[$n] = [];
  8468. $s = $this->PrintPageBackgrounds();
  8469. $this->headerbuffer = $s . $this->headerbuffer;
  8470. $os = '';
  8471. if ($rotate) {
  8472. $os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE));
  8473. // To rotate the other way i.e. Header to left of page:
  8474. // $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE));
  8475. }
  8476. $os .= $this->headerbuffer;
  8477. if ($rotate) {
  8478. $os .= ' Q' . "\n";
  8479. }
  8480. // Writes over the page background but behind any other output on page
  8481. $os = preg_replace(['/\\\\/', '/\$/'], ['\\\\\\\\', '\\\\$'], $os);
  8482. $this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]);
  8483. $lks = $this->HTMLheaderPageLinks;
  8484. foreach ($lks as $lk) {
  8485. if ($rotate) {
  8486. $lw = $lk[2];
  8487. $lh = $lk[3];
  8488. $lk[2] = $lh;
  8489. $lk[3] = $lw; // swap width and height
  8490. $ax = $lk[0] / Mpdf::SCALE;
  8491. $ay = $lk[1] / Mpdf::SCALE;
  8492. $bx = $ay - ($lh / Mpdf::SCALE);
  8493. $by = $this->w - $ax;
  8494. $lk[0] = $bx * Mpdf::SCALE;
  8495. $lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw;
  8496. }
  8497. $this->PageLinks[$n][] = $lk;
  8498. }
  8499. /* -- FORMS -- */
  8500. foreach ($this->HTMLheaderPageForms as $f) {
  8501. $this->form->forms[$f['n']] = $f;
  8502. }
  8503. /* -- END FORMS -- */
  8504. }
  8505. if (isset($this->saveHTMLFooter[$n][$OE])) {
  8506. $html = $this->saveHTMLFooter[$this->page][$OE]['html'];
  8507. $this->lMargin = $this->saveHTMLFooter[$n][$OE]['ml'];
  8508. $this->rMargin = $this->saveHTMLFooter[$n][$OE]['mr'];
  8509. $this->tMargin = $this->saveHTMLFooter[$n][$OE]['mh'];
  8510. $this->bMargin = $this->saveHTMLFooter[$n][$OE]['mf'];
  8511. $this->margin_header = $this->saveHTMLFooter[$n][$OE]['mh'];
  8512. $this->margin_footer = $this->saveHTMLFooter[$n][$OE]['mf'];
  8513. $this->w = $this->saveHTMLFooter[$n][$OE]['pw'];
  8514. $this->h = $this->saveHTMLFooter[$n][$OE]['ph'];
  8515. if ($this->w > $this->h) {
  8516. $this->hPt = $this->fwPt;
  8517. $this->wPt = $this->fhPt;
  8518. } else {
  8519. $this->hPt = $this->fhPt;
  8520. $this->wPt = $this->fwPt;
  8521. }
  8522. $rotate = (isset($this->saveHTMLFooter[$n][$OE]['rotate']) ? $this->saveHTMLFooter[$n][$OE]['rotate'] : null);
  8523. $this->Reset();
  8524. $this->pageoutput[$n] = [];
  8525. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  8526. $this->x = $this->lMargin;
  8527. $top_y = $this->y = $this->h - $this->margin_footer;
  8528. // if bottom-margin==0, corrects to avoid division by zero
  8529. if ($this->y == $this->h) {
  8530. $top_y = $this->y = ($this->h + 0.01);
  8531. }
  8532. // Replace of page number aliases and date format
  8533. $html = $this->aliasReplace($html, $pnstr, $pntstr, $nb);
  8534. $this->HTMLheaderPageLinks = [];
  8535. $this->HTMLheaderPageAnnots = [];
  8536. $this->HTMLheaderPageForms = [];
  8537. $this->pageBackgrounds = [];
  8538. $this->writingHTMLfooter = true;
  8539. $this->InFooter = true;
  8540. $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
  8541. $this->InFooter = false;
  8542. $this->Reset();
  8543. $this->pageoutput[$n] = [];
  8544. $fheight = $this->y - $top_y;
  8545. $adj = -$fheight;
  8546. $s = $this->PrintPageBackgrounds(-$adj);
  8547. $this->headerbuffer = $s . $this->headerbuffer;
  8548. $this->writingHTMLfooter = false; // mPDF 5.7.3 (moved after PrintPageBackgrounds so can adjust position of images in footer)
  8549. $os = '';
  8550. $os .= $this->StartTransform(true) . "\n";
  8551. if ($rotate) {
  8552. $os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE));
  8553. // To rotate the other way i.e. Header to left of page:
  8554. // $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE));
  8555. }
  8556. $os .= $this->transformTranslate(0, $adj, true) . "\n";
  8557. $os .= $this->headerbuffer;
  8558. if ($rotate) {
  8559. $os .= ' Q' . "\n";
  8560. }
  8561. $os .= $this->StopTransform(true) . "\n";
  8562. // Writes over the page background but behind any other output on page
  8563. $os = preg_replace(['/\\\\/', '/\$/'], ['\\\\\\\\', '\\\\$'], $os);
  8564. $this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]);
  8565. $lks = $this->HTMLheaderPageLinks;
  8566. foreach ($lks as $lk) {
  8567. $lk[1] -= $adj * Mpdf::SCALE;
  8568. if ($rotate) {
  8569. $lw = $lk[2];
  8570. $lh = $lk[3];
  8571. $lk[2] = $lh;
  8572. $lk[3] = $lw; // swap width and height
  8573. $ax = $lk[0] / Mpdf::SCALE;
  8574. $ay = $lk[1] / Mpdf::SCALE;
  8575. $bx = $ay - ($lh / Mpdf::SCALE);
  8576. $by = $this->w - $ax;
  8577. $lk[0] = $bx * Mpdf::SCALE;
  8578. $lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw;
  8579. }
  8580. $this->PageLinks[$n][] = $lk;
  8581. }
  8582. /* -- FORMS -- */
  8583. foreach ($this->HTMLheaderPageForms as $f) {
  8584. $f['y'] += $adj;
  8585. $this->form->forms[$f['n']] = $f;
  8586. }
  8587. /* -- END FORMS -- */
  8588. }
  8589. // Customization for https://github.com/mpdf/mpdf/issues/172
  8590. // Replace of page number aliases and date format
  8591. $this->pages[$n] = $this->aliasReplace($this->pages[$n], $pnstr, $pntstr, $nb);
  8592. }
  8593. $this->page = $nb;
  8594. $this->state = 1;
  8595. }
  8596. /* -- ANNOTATIONS -- */
  8597. function Annotation($text, $x = 0, $y = 0, $icon = 'Note', $author = '', $subject = '', $opacity = 0, $colarray = false, $popup = '', $file = '')
  8598. {
  8599. if (is_array($colarray) && count($colarray) == 3) {
  8600. $colarray = $this->colorConverter->convert('rgb(' . $colarray[0] . ',' . $colarray[1] . ',' . $colarray[2] . ')', $this->PDFAXwarnings);
  8601. }
  8602. if ($colarray === false) {
  8603. $colarray = $this->colorConverter->convert('yellow', $this->PDFAXwarnings);
  8604. }
  8605. if ($x == 0) {
  8606. $x = $this->x;
  8607. }
  8608. if ($y == 0) {
  8609. $y = $this->y;
  8610. }
  8611. $page = $this->page;
  8612. if ($page < 1) { // Document has not been started - assume it's for first page
  8613. $page = 1;
  8614. if ($x == 0) {
  8615. $x = $this->lMargin;
  8616. }
  8617. if ($y == 0) {
  8618. $y = $this->tMargin;
  8619. }
  8620. }
  8621. if ($this->PDFA || $this->PDFX) {
  8622. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  8623. $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)";
  8624. }
  8625. $x = ($this->w) - $this->rMargin * 0.66;
  8626. }
  8627. if (!$this->annotMargin) {
  8628. $y -= $this->FontSize / 2;
  8629. }
  8630. if (!$opacity && $this->annotMargin) {
  8631. $opacity = 1;
  8632. } elseif (!$opacity) {
  8633. $opacity = $this->annotOpacity;
  8634. }
  8635. $an = ['txt' => $text, 'x' => $x, 'y' => $y, 'opt' => ['Icon' => $icon, 'T' => $author, 'Subj' => $subject, 'C' => $colarray, 'CA' => $opacity, 'popup' => $popup, 'file' => $file]];
  8636. if ($this->keep_block_together) { // don't write yet
  8637. return;
  8638. } elseif ($this->table_rotate) {
  8639. $this->tbrot_Annots[$this->page][] = $an;
  8640. return;
  8641. } elseif ($this->kwt) {
  8642. $this->kwt_Annots[$this->page][] = $an;
  8643. return;
  8644. }
  8645. if ($this->writingHTMLheader || $this->writingHTMLfooter) {
  8646. $this->HTMLheaderPageAnnots[] = $an;
  8647. return;
  8648. }
  8649. // Put an Annotation on the page
  8650. $this->PageAnnots[$page][] = $an;
  8651. /* -- COLUMNS -- */
  8652. // Save cross-reference to Column buffer
  8653. $ref = isset($this->PageAnnots[$this->page]) ? (count($this->PageAnnots[$this->page]) - 1) : -1;
  8654. $this->columnAnnots[$this->CurrCol][intval($this->x)][intval($this->y)] = $ref;
  8655. /* -- END COLUMNS -- */
  8656. }
  8657. /* -- END ANNOTATIONS -- */
  8658. function _enddoc()
  8659. {
  8660. // @log Writing Headers & Footers
  8661. $this->_puthtmlheaders();
  8662. // @log Writing Pages
  8663. // Remove references to unused fonts (usually default font)
  8664. foreach ($this->fonts as $fk => $font) {
  8665. if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {
  8666. if ($font['sip'] || $font['smp']) {
  8667. foreach ($font['subsetfontids'] as $k => $fid) {
  8668. foreach ($this->pages as $pn => $page) {
  8669. $this->pages[$pn] = preg_replace('/\s\/F' . $fid . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]);
  8670. }
  8671. }
  8672. } else {
  8673. foreach ($this->pages as $pn => $page) {
  8674. $this->pages[$pn] = preg_replace('/\s\/F' . $font['i'] . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]);
  8675. }
  8676. }
  8677. }
  8678. }
  8679. if (count($this->layers)) {
  8680. foreach ($this->pages as $pn => $page) {
  8681. preg_match_all('/\/OCZ-index \/ZI(\d+) BDC(.*?)(EMCZ)-index/is', $this->pages[$pn], $m1);
  8682. preg_match_all('/\/OCBZ-index \/ZI(\d+) BDC(.*?)(EMCBZ)-index/is', $this->pages[$pn], $m2);
  8683. preg_match_all('/\/OCGZ-index \/ZI(\d+) BDC(.*?)(EMCGZ)-index/is', $this->pages[$pn], $m3);
  8684. $m = [];
  8685. for ($i = 0; $i < 4; $i++) {
  8686. $m[$i] = array_merge($m1[$i], $m2[$i], $m3[$i]);
  8687. }
  8688. if (count($m[0])) {
  8689. $sortarr = [];
  8690. for ($i = 0; $i < count($m[0]); $i++) {
  8691. $key = $m[1][$i] * 2;
  8692. if ($m[3][$i] == 'EMCZ') {
  8693. $key +=2; // background first then gradient then normal
  8694. } elseif ($m[3][$i] == 'EMCGZ') {
  8695. $key +=1;
  8696. }
  8697. $sortarr[$i] = $key;
  8698. }
  8699. asort($sortarr);
  8700. foreach ($sortarr as $i => $k) {
  8701. $this->pages[$pn] = str_replace($m[0][$i], '', $this->pages[$pn]);
  8702. $this->pages[$pn] .= "\n" . $m[0][$i] . "\n";
  8703. }
  8704. $this->pages[$pn] = preg_replace('/\/OC[BG]{0,1}Z-index \/ZI(\d+) BDC/is', '/OC /ZI\\1 BDC ', $this->pages[$pn]);
  8705. $this->pages[$pn] = preg_replace('/EMC[BG]{0,1}Z-index/is', 'EMC', $this->pages[$pn]);
  8706. }
  8707. }
  8708. }
  8709. $this->pageWriter->writePages();
  8710. // @log Writing document resources
  8711. $this->resourceWriter->writeResources();
  8712. // Info
  8713. $this->writer->object();
  8714. $this->InfoRoot = $this->n;
  8715. $this->writer->write('<<');
  8716. // @log Writing document info
  8717. $this->metadataWriter->writeInfo();
  8718. $this->writer->write('>>');
  8719. $this->writer->write('endobj');
  8720. // METADATA
  8721. if ($this->PDFA || $this->PDFX) {
  8722. $this->metadataWriter->writeMetadata();
  8723. }
  8724. // OUTPUTINTENT
  8725. if ($this->PDFA || $this->PDFX || $this->ICCProfile) {
  8726. $this->metadataWriter->writeOutputIntent();
  8727. }
  8728. // Associated files
  8729. if ($this->associatedFiles) {
  8730. $this->metadataWriter->writeAssociatedFiles();
  8731. }
  8732. // Catalog
  8733. $this->writer->object();
  8734. $this->writer->write('<<');
  8735. // @log Writing document catalog
  8736. $this->metadataWriter->writeCatalog();
  8737. $this->writer->write('>>');
  8738. $this->writer->write('endobj');
  8739. // Cross-ref
  8740. $o = strlen($this->buffer);
  8741. $this->writer->write('xref');
  8742. $this->writer->write('0 ' . ($this->n + 1));
  8743. $this->writer->write('0000000000 65535 f ');
  8744. for ($i = 1; $i <= $this->n; $i++) {
  8745. $this->writer->write(sprintf('%010d 00000 n ', $this->offsets[$i]));
  8746. }
  8747. // Trailer
  8748. $this->writer->write('trailer');
  8749. $this->writer->write('<<');
  8750. $this->metadataWriter->writeTrailer();
  8751. $this->writer->write('>>');
  8752. $this->writer->write('startxref');
  8753. $this->writer->write($o);
  8754. $this->buffer .= '%%EOF';
  8755. $this->state = 3;
  8756. }
  8757. function _beginpage(
  8758. $orientation,
  8759. $mgl = '',
  8760. $mgr = '',
  8761. $mgt = '',
  8762. $mgb = '',
  8763. $mgh = '',
  8764. $mgf = '',
  8765. $ohname = '',
  8766. $ehname = '',
  8767. $ofname = '',
  8768. $efname = '',
  8769. $ohvalue = 0,
  8770. $ehvalue = 0,
  8771. $ofvalue = 0,
  8772. $efvalue = 0,
  8773. $pagesel = '',
  8774. $newformat = ''
  8775. ) {
  8776. if (!($pagesel && $this->page == 1 && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)))) {
  8777. $this->page++;
  8778. $this->pages[$this->page] = '';
  8779. }
  8780. $this->state = 2;
  8781. $resetHTMLHeadersrequired = false;
  8782. if ($newformat) {
  8783. $this->_setPageSize($newformat, $orientation);
  8784. }
  8785. /* -- CSS-PAGE -- */
  8786. // Paged media (page-box)
  8787. if ($pagesel || $this->page_box['using']) {
  8788. if ($pagesel || $this->page === 1) {
  8789. $first = true;
  8790. } else {
  8791. $first = false;
  8792. }
  8793. if ($this->mirrorMargins && ($this->page % 2 === 0)) {
  8794. $oddEven = 'E';
  8795. } else {
  8796. $oddEven = 'O';
  8797. }
  8798. if ($pagesel) {
  8799. $psel = $pagesel;
  8800. } elseif ($this->page_box['current']) {
  8801. $psel = $this->page_box['current'];
  8802. } else {
  8803. $psel = '';
  8804. }
  8805. list($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS($psel, $first, $oddEven);
  8806. if ($this->mirrorMargins && ($this->page % 2 === 0)) {
  8807. if ($hname) {
  8808. $ehvalue = 1;
  8809. $ehname = $hname;
  8810. } else {
  8811. $ehvalue = -1;
  8812. }
  8813. if ($fname) {
  8814. $efvalue = 1;
  8815. $efname = $fname;
  8816. } else {
  8817. $efvalue = -1;
  8818. }
  8819. } else {
  8820. if ($hname) {
  8821. $ohvalue = 1;
  8822. $ohname = $hname;
  8823. } else {
  8824. $ohvalue = -1;
  8825. }
  8826. if ($fname) {
  8827. $ofvalue = 1;
  8828. $ofname = $fname;
  8829. } else {
  8830. $ofvalue = -1;
  8831. }
  8832. }
  8833. if ($resetpagenum || $pagenumstyle || $suppress) {
  8834. $this->PageNumSubstitutions[] = ['from' => ($this->page), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
  8835. }
  8836. // PAGED MEDIA - CROP / CROSS MARKS from @PAGE
  8837. $this->show_marks = $marks;
  8838. // Background color
  8839. if (isset($bg['BACKGROUND-COLOR'])) {
  8840. $cor = $this->colorConverter->convert($bg['BACKGROUND-COLOR'], $this->PDFAXwarnings);
  8841. if ($cor) {
  8842. $this->bodyBackgroundColor = $cor;
  8843. }
  8844. } else {
  8845. $this->bodyBackgroundColor = false;
  8846. }
  8847. /* -- BACKGROUNDS -- */
  8848. if (isset($bg['BACKGROUND-GRADIENT'])) {
  8849. $this->bodyBackgroundGradient = $bg['BACKGROUND-GRADIENT'];
  8850. } else {
  8851. $this->bodyBackgroundGradient = false;
  8852. }
  8853. // Tiling Patterns
  8854. if (isset($bg['BACKGROUND-IMAGE']) && $bg['BACKGROUND-IMAGE']) {
  8855. $ret = $this->SetBackground($bg, $this->pgwidth);
  8856. if ($ret) {
  8857. $this->bodyBackgroundImage = $ret;
  8858. }
  8859. } else {
  8860. $this->bodyBackgroundImage = false;
  8861. }
  8862. /* -- END BACKGROUNDS -- */
  8863. $this->page_box['current'] = $psel;
  8864. $this->page_box['using'] = true;
  8865. }
  8866. /* -- END CSS-PAGE -- */
  8867. // Page orientation
  8868. if (!$orientation) {
  8869. $orientation = $this->DefOrientation;
  8870. } else {
  8871. $orientation = strtoupper(substr($orientation, 0, 1));
  8872. if ($orientation != $this->DefOrientation) {
  8873. $this->OrientationChanges[$this->page] = true;
  8874. }
  8875. }
  8876. if ($orientation !== $this->CurOrientation || $newformat) {
  8877. // Change orientation
  8878. if ($orientation === 'P') {
  8879. $this->wPt = $this->fwPt;
  8880. $this->hPt = $this->fhPt;
  8881. $this->w = $this->fw;
  8882. $this->h = $this->fh;
  8883. if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation === 'P') {
  8884. $this->tMargin = $this->orig_tMargin;
  8885. $this->bMargin = $this->orig_bMargin;
  8886. $this->DeflMargin = $this->orig_lMargin;
  8887. $this->DefrMargin = $this->orig_rMargin;
  8888. $this->margin_header = $this->orig_hMargin;
  8889. $this->margin_footer = $this->orig_fMargin;
  8890. } else {
  8891. $resetHTMLHeadersrequired = true;
  8892. }
  8893. } else {
  8894. $this->wPt = $this->fhPt;
  8895. $this->hPt = $this->fwPt;
  8896. $this->w = $this->fh;
  8897. $this->h = $this->fw;
  8898. if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation === 'P') {
  8899. $this->tMargin = $this->orig_lMargin;
  8900. $this->bMargin = $this->orig_rMargin;
  8901. $this->DeflMargin = $this->orig_bMargin;
  8902. $this->DefrMargin = $this->orig_tMargin;
  8903. $this->margin_header = $this->orig_hMargin;
  8904. $this->margin_footer = $this->orig_fMargin;
  8905. } else {
  8906. $resetHTMLHeadersrequired = true;
  8907. }
  8908. }
  8909. $this->CurOrientation = $orientation;
  8910. $this->ResetMargins();
  8911. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  8912. $this->PageBreakTrigger = $this->h - $this->bMargin;
  8913. }
  8914. $this->pageDim[$this->page]['w'] = $this->w;
  8915. $this->pageDim[$this->page]['h'] = $this->h;
  8916. $this->pageDim[$this->page]['outer_width_LR'] = $this->page_box['outer_width_LR'] ?: 0;
  8917. $this->pageDim[$this->page]['outer_width_TB'] = $this->page_box['outer_width_TB'] ?: 0;
  8918. if (!$this->page_box['outer_width_LR'] && !$this->page_box['outer_width_TB']) {
  8919. $this->pageDim[$this->page]['bleedMargin'] = 0;
  8920. } elseif ($this->bleedMargin <= $this->page_box['outer_width_LR'] && $this->bleedMargin <= $this->page_box['outer_width_TB']) {
  8921. $this->pageDim[$this->page]['bleedMargin'] = $this->bleedMargin;
  8922. } else {
  8923. $this->pageDim[$this->page]['bleedMargin'] = min($this->page_box['outer_width_LR'], $this->page_box['outer_width_TB']) - 0.01;
  8924. }
  8925. // If Page Margins are re-defined
  8926. // strlen()>0 is used to pick up (integer) 0, (string) '0', or set value
  8927. 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)) {
  8928. if (strlen($mgl) > 0) {
  8929. $this->DeflMargin = $mgl;
  8930. }
  8931. if (strlen($mgr) > 0) {
  8932. $this->DefrMargin = $mgr;
  8933. }
  8934. if (strlen($mgt) > 0) {
  8935. $this->tMargin = $mgt;
  8936. }
  8937. if (strlen($mgb) > 0) {
  8938. $this->bMargin = $mgb;
  8939. }
  8940. if (strlen($mgh) > 0) {
  8941. $this->margin_header = $mgh;
  8942. }
  8943. if (strlen($mgf) > 0) {
  8944. $this->margin_footer = $mgf;
  8945. }
  8946. $this->ResetMargins();
  8947. $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
  8948. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  8949. $resetHTMLHeadersrequired = true;
  8950. }
  8951. $this->ResetMargins();
  8952. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  8953. $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
  8954. // Reset column top margin
  8955. $this->y0 = $this->tMargin;
  8956. $this->x = $this->lMargin;
  8957. $this->y = $this->tMargin;
  8958. $this->FontFamily = '';
  8959. // HEADERS AND FOOTERS // mPDF 6
  8960. if ($ohvalue < 0 || strtoupper($ohvalue) == 'OFF') {
  8961. $this->HTMLHeader = '';
  8962. $resetHTMLHeadersrequired = true;
  8963. } elseif ($ohname && $ohvalue > 0) {
  8964. if (preg_match('/^html_(.*)$/i', $ohname, $n)) {
  8965. $name = $n[1];
  8966. } else {
  8967. $name = $ohname;
  8968. }
  8969. if (isset($this->pageHTMLheaders[$name])) {
  8970. $this->HTMLHeader = $this->pageHTMLheaders[$name];
  8971. } else {
  8972. $this->HTMLHeader = '';
  8973. }
  8974. $resetHTMLHeadersrequired = true;
  8975. }
  8976. if ($ehvalue < 0 || strtoupper($ehvalue) == 'OFF') {
  8977. $this->HTMLHeaderE = '';
  8978. $resetHTMLHeadersrequired = true;
  8979. } elseif ($ehname && $ehvalue > 0) {
  8980. if (preg_match('/^html_(.*)$/i', $ehname, $n)) {
  8981. $name = $n[1];
  8982. } else {
  8983. $name = $ehname;
  8984. }
  8985. if (isset($this->pageHTMLheaders[$name])) {
  8986. $this->HTMLHeaderE = $this->pageHTMLheaders[$name];
  8987. } else {
  8988. $this->HTMLHeaderE = '';
  8989. }
  8990. $resetHTMLHeadersrequired = true;
  8991. }
  8992. if ($ofvalue < 0 || strtoupper($ofvalue) == 'OFF') {
  8993. $this->HTMLFooter = '';
  8994. $resetHTMLHeadersrequired = true;
  8995. } elseif ($ofname && $ofvalue > 0) {
  8996. if (preg_match('/^html_(.*)$/i', $ofname, $n)) {
  8997. $name = $n[1];
  8998. } else {
  8999. $name = $ofname;
  9000. }
  9001. if (isset($this->pageHTMLfooters[$name])) {
  9002. $this->HTMLFooter = $this->pageHTMLfooters[$name];
  9003. } else {
  9004. $this->HTMLFooter = '';
  9005. }
  9006. $resetHTMLHeadersrequired = true;
  9007. }
  9008. if ($efvalue < 0 || strtoupper($efvalue) == 'OFF') {
  9009. $this->HTMLFooterE = '';
  9010. $resetHTMLHeadersrequired = true;
  9011. } elseif ($efname && $efvalue > 0) {
  9012. if (preg_match('/^html_(.*)$/i', $efname, $n)) {
  9013. $name = $n[1];
  9014. } else {
  9015. $name = $efname;
  9016. }
  9017. if (isset($this->pageHTMLfooters[$name])) {
  9018. $this->HTMLFooterE = $this->pageHTMLfooters[$name];
  9019. } else {
  9020. $this->HTMLFooterE = '';
  9021. }
  9022. $resetHTMLHeadersrequired = true;
  9023. }
  9024. if ($resetHTMLHeadersrequired) {
  9025. $this->SetHTMLHeader($this->HTMLHeader);
  9026. $this->SetHTMLHeader($this->HTMLHeaderE, 'E');
  9027. $this->SetHTMLFooter($this->HTMLFooter);
  9028. $this->SetHTMLFooter($this->HTMLFooterE, 'E');
  9029. }
  9030. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  9031. $this->_setAutoHeaderHeight($this->HTMLHeaderE);
  9032. $this->_setAutoFooterHeight($this->HTMLFooterE);
  9033. } else { // ODD or DEFAULT
  9034. $this->_setAutoHeaderHeight($this->HTMLHeader);
  9035. $this->_setAutoFooterHeight($this->HTMLFooter);
  9036. }
  9037. // Reset column top margin
  9038. $this->y0 = $this->tMargin;
  9039. $this->x = $this->lMargin;
  9040. $this->y = $this->tMargin;
  9041. }
  9042. // mPDF 6
  9043. function _setAutoHeaderHeight(&$htmlh)
  9044. {
  9045. /* When the setAutoTopMargin option is set to pad/stretch, only apply auto header height when a header exists */
  9046. if ($this->HTMLHeader === '' && $this->HTMLHeaderE === '') {
  9047. return;
  9048. }
  9049. if ($this->setAutoTopMargin == 'pad') {
  9050. if (isset($htmlh['h']) && $htmlh['h']) {
  9051. $h = $htmlh['h'];
  9052. } // 5.7.3
  9053. else {
  9054. $h = 0;
  9055. }
  9056. $this->tMargin = $this->margin_header + $h + $this->orig_tMargin;
  9057. } elseif ($this->setAutoTopMargin == 'stretch') {
  9058. if (isset($htmlh['h']) && $htmlh['h']) {
  9059. $h = $htmlh['h'];
  9060. } // 5.7.3
  9061. else {
  9062. $h = 0;
  9063. }
  9064. $this->tMargin = max($this->orig_tMargin, $this->margin_header + $h + $this->autoMarginPadding);
  9065. }
  9066. }
  9067. // mPDF 6
  9068. function _setAutoFooterHeight(&$htmlf)
  9069. {
  9070. /* When the setAutoTopMargin option is set to pad/stretch, only apply auto footer height when a footer exists */
  9071. if ($this->HTMLFooter === '' && $this->HTMLFooterE === '') {
  9072. return;
  9073. }
  9074. if ($this->setAutoBottomMargin == 'pad') {
  9075. if (isset($htmlf['h']) && $htmlf['h']) {
  9076. $h = $htmlf['h'];
  9077. } // 5.7.3
  9078. else {
  9079. $h = 0;
  9080. }
  9081. $this->bMargin = $this->margin_footer + $h + $this->orig_bMargin;
  9082. $this->PageBreakTrigger = $this->h - $this->bMargin;
  9083. } elseif ($this->setAutoBottomMargin == 'stretch') {
  9084. if (isset($htmlf['h']) && $htmlf['h']) {
  9085. $h = $htmlf['h'];
  9086. } // 5.7.3
  9087. else {
  9088. $h = 0;
  9089. }
  9090. $this->bMargin = max($this->orig_bMargin, $this->margin_footer + $h + $this->autoMarginPadding);
  9091. $this->PageBreakTrigger = $this->h - $this->bMargin;
  9092. }
  9093. }
  9094. function _endpage()
  9095. {
  9096. /* -- CSS-IMAGE-FLOAT -- */
  9097. $this->printfloatbuffer();
  9098. /* -- END CSS-IMAGE-FLOAT -- */
  9099. if ($this->visibility != 'visible') {
  9100. $this->SetVisibility('visible');
  9101. }
  9102. $this->EndLayer();
  9103. // End of page contents
  9104. $this->state = 1;
  9105. }
  9106. function _dounderline($x, $y, $txt, $OTLdata = false, $textvar = 0)
  9107. {
  9108. // Now print line exactly where $y secifies - called from Text() and Cell() - adjust position there
  9109. // WORD SPACING
  9110. $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));
  9111. // Draw a line
  9112. 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);
  9113. }
  9114. /* -- WATERMARK -- */
  9115. // add a watermark
  9116. function watermark($texte, $angle = 45, $fontsize = 96, $alpha = 0.2)
  9117. {
  9118. if ($this->PDFA || $this->PDFX) {
  9119. throw new \Mpdf\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');
  9120. }
  9121. if (!$this->watermark_font) {
  9122. $this->watermark_font = $this->default_font;
  9123. }
  9124. $this->SetFont($this->watermark_font, "B", $fontsize, false); // Don't output
  9125. $texte = $this->purify_utf8_text($texte);
  9126. if ($this->text_input_as_HTML) {
  9127. $texte = $this->all_entities_to_utf8($texte);
  9128. }
  9129. if ($this->usingCoreFont) {
  9130. $texte = mb_convert_encoding($texte, $this->mb_enc, 'UTF-8');
  9131. }
  9132. // DIRECTIONALITY
  9133. if (preg_match("/([" . $this->pregRTLchars . "])/u", $texte)) {
  9134. $this->biDirectional = true;
  9135. } // *OTL*
  9136. $textvar = 0;
  9137. $save_OTLtags = $this->OTLtags;
  9138. $this->OTLtags = [];
  9139. if ($this->useKerning) {
  9140. if ($this->CurrentFont['haskernGPOS']) {
  9141. $this->OTLtags['Plus'] .= ' kern';
  9142. } else {
  9143. $textvar = ($textvar | TextVars::FC_KERNING);
  9144. }
  9145. }
  9146. /* -- OTL -- */
  9147. // Use OTL OpenType Table Layout - GSUB & GPOS
  9148. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  9149. $texte = $this->otl->applyOTL($texte, $this->CurrentFont['useOTL']);
  9150. $OTLdata = $this->otl->OTLdata;
  9151. }
  9152. /* -- END OTL -- */
  9153. $this->OTLtags = $save_OTLtags;
  9154. $this->magic_reverse_dir($texte, $this->directionality, $OTLdata);
  9155. $this->SetAlpha($alpha);
  9156. $color = $this->watermarkTextObject ? $this->watermarkTextObject->getColor() : 0;
  9157. $this->SetTColor($this->colorConverter->convert($color, $this->PDFAXwarnings));
  9158. $szfont = $fontsize;
  9159. $loop = 0;
  9160. $maxlen = (min($this->w, $this->h) ); // sets max length of text as 7/8 width/height of page
  9161. while ($loop == 0) {
  9162. $this->SetFont($this->watermark_font, "B", $szfont, false); // Don't output
  9163. $offset = ((sin(deg2rad($angle))) * ($szfont / Mpdf::SCALE));
  9164. $strlen = $this->GetStringWidth($texte, true, $OTLdata, $textvar);
  9165. if ($strlen > $maxlen - $offset) {
  9166. $szfont --;
  9167. } else {
  9168. $loop ++;
  9169. }
  9170. }
  9171. $this->SetFont($this->watermark_font, "B", $szfont - 0.1, true, true); // Output The -0.1 is because SetFont above is not written to PDF
  9172. // Repeating it will not output anything as mPDF thinks it is set
  9173. $adj = ((cos(deg2rad($angle))) * ($strlen / 2));
  9174. $opp = ((sin(deg2rad($angle))) * ($strlen / 2));
  9175. $wx = ($this->w / 2) - $adj + $offset / 3;
  9176. $wy = ($this->h / 2) + $opp;
  9177. $this->Rotate($angle, $wx, $wy);
  9178. $this->Text($wx, $wy, $texte, $OTLdata, $textvar);
  9179. $this->Rotate(0);
  9180. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  9181. $this->SetAlpha(1);
  9182. }
  9183. function watermarkImg($src, $alpha = 0.2)
  9184. {
  9185. if ($this->PDFA || $this->PDFX) {
  9186. throw new \Mpdf\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');
  9187. }
  9188. if ($this->watermarkImgBehind) {
  9189. $this->watermarkImgAlpha = $this->SetAlpha($alpha, 'Normal', true);
  9190. } else {
  9191. $this->SetAlpha($alpha, $this->watermarkImgAlphaBlend);
  9192. }
  9193. $this->Image($src, 0, 0, 0, 0, '', '', true, true, true);
  9194. if (!$this->watermarkImgBehind) {
  9195. $this->SetAlpha(1);
  9196. }
  9197. }
  9198. /* -- END WATERMARK -- */
  9199. function Rotate($angle, $x = -1, $y = -1)
  9200. {
  9201. if ($x == -1) {
  9202. $x = $this->x;
  9203. }
  9204. if ($y == -1) {
  9205. $y = $this->y;
  9206. }
  9207. if ($this->angle != 0) {
  9208. $this->writer->write('Q');
  9209. }
  9210. $this->angle = $angle;
  9211. if ($angle != 0) {
  9212. $angle*=M_PI / 180;
  9213. $c = cos($angle);
  9214. $s = sin($angle);
  9215. $cx = $x * Mpdf::SCALE;
  9216. $cy = ($this->h - $y) * Mpdf::SCALE;
  9217. $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));
  9218. }
  9219. }
  9220. function CircularText($x, $y, $r, $text, $align = 'top', $fontfamily = '', $fontsize = 0, $fontstyle = '', $kerning = 120, $fontwidth = 100, $divider = '')
  9221. {
  9222. if (empty($this->directWrite)) {
  9223. $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
  9224. }
  9225. $this->directWrite->CircularText($x, $y, $r, $text, $align, $fontfamily, $fontsize, $fontstyle, $kerning, $fontwidth, $divider);
  9226. }
  9227. // From Invoice
  9228. function RoundedRect($x, $y, $w, $h, $r, $style = '')
  9229. {
  9230. $hp = $this->h;
  9231. if ($style == 'F') {
  9232. $op = 'f';
  9233. } elseif ($style == 'FD' or $style == 'DF') {
  9234. $op = 'B';
  9235. } else {
  9236. $op = 'S';
  9237. }
  9238. $MyArc = 4 / 3 * (sqrt(2) - 1);
  9239. $this->writer->write(sprintf('%.3F %.3F m', ($x + $r) * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE));
  9240. $xc = $x + $w - $r;
  9241. $yc = $y + $r;
  9242. $this->writer->write(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE));
  9243. $this->_Arc($xc + $r * $MyArc, $yc - $r, $xc + $r, $yc - $r * $MyArc, $xc + $r, $yc);
  9244. $xc = $x + $w - $r;
  9245. $yc = $y + $h - $r;
  9246. $this->writer->write(sprintf('%.3F %.3F l', ($x + $w) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE));
  9247. $this->_Arc($xc + $r, $yc + $r * $MyArc, $xc + $r * $MyArc, $yc + $r, $xc, $yc + $r);
  9248. $xc = $x + $r;
  9249. $yc = $y + $h - $r;
  9250. $this->writer->write(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - ($y + $h)) * Mpdf::SCALE));
  9251. $this->_Arc($xc - $r * $MyArc, $yc + $r, $xc - $r, $yc + $r * $MyArc, $xc - $r, $yc);
  9252. $xc = $x + $r;
  9253. $yc = $y + $r;
  9254. $this->writer->write(sprintf('%.3F %.3F l', ($x) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE));
  9255. $this->_Arc($xc - $r, $yc - $r * $MyArc, $xc - $r * $MyArc, $yc - $r, $xc, $yc - $r);
  9256. $this->writer->write($op);
  9257. }
  9258. function _Arc($x1, $y1, $x2, $y2, $x3, $y3)
  9259. {
  9260. $h = $this->h;
  9261. $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));
  9262. }
  9263. // ====================================================
  9264. /* -- DIRECTW -- */
  9265. function Shaded_box($text, $font = '', $fontstyle = 'B', $szfont = '', $width = '70%', $style = 'DF', $radius = 2.5, $fill = '#FFFFFF', $color = '#000000', $pad = 2)
  9266. {
  9267. // F (shading - no line),S (line, no shading),DF (both)
  9268. if (empty($this->directWrite)) {
  9269. $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
  9270. }
  9271. $this->directWrite->Shaded_box($text, $font, $fontstyle, $szfont, $width, $style, $radius, $fill, $color, $pad);
  9272. }
  9273. /* -- END DIRECTW -- */
  9274. function UTF8StringToArray($str, $addSubset = true)
  9275. {
  9276. $out = [];
  9277. $len = strlen($str);
  9278. for ($i = 0; $i < $len; $i++) {
  9279. $uni = -1;
  9280. $h = ord($str[$i]);
  9281. if ($h <= 0x7F) {
  9282. $uni = $h;
  9283. } elseif ($h >= 0xC2) {
  9284. if (($h <= 0xDF) && ($i < $len - 1)) {
  9285. $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
  9286. } elseif (($h <= 0xEF) && ($i < $len - 2)) {
  9287. $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
  9288. } elseif (($h <= 0xF4) && ($i < $len - 3)) {
  9289. $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
  9290. }
  9291. }
  9292. if ($uni >= 0) {
  9293. $out[] = $uni;
  9294. if ($addSubset && isset($this->CurrentFont['subset'])) {
  9295. $this->CurrentFont['subset'][$uni] = $uni;
  9296. }
  9297. }
  9298. }
  9299. return $out;
  9300. }
  9301. // Convert utf-8 string to <HHHHHH> for Font Subsets
  9302. function UTF8toSubset($str)
  9303. {
  9304. $ret = '<';
  9305. // $str = preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $str ); // mPDF 6 deleted
  9306. // $str = preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $str ); // mPDF 6 deleted
  9307. $unicode = $this->UTF8StringToArray($str);
  9308. $orig_fid = $this->CurrentFont['subsetfontids'][0];
  9309. $last_fid = $this->CurrentFont['subsetfontids'][0];
  9310. foreach ($unicode as $c) {
  9311. /* // mPDF 6 deleted
  9312. if ($c == 7 || $c == 8) {
  9313. if ($orig_fid != $last_fid) {
  9314. $ret .= '> Tj /F'.$orig_fid.' '.$this->FontSizePt.' Tf <';
  9315. $last_fid = $orig_fid;
  9316. }
  9317. if ($c == 7) { $ret .= $this->aliasNbPgHex; }
  9318. else { $ret .= $this->aliasNbPgGpHex; }
  9319. continue;
  9320. }
  9321. */
  9322. if (!$this->_charDefined($this->CurrentFont['cw'], $c)) {
  9323. $c = 0;
  9324. } // mPDF 6
  9325. for ($i = 0; $i < 99; $i++) {
  9326. // return c as decimal char
  9327. $init = array_search($c, $this->CurrentFont['subsets'][$i]);
  9328. if ($init !== false) {
  9329. if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {
  9330. $ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';
  9331. $last_fid = $this->CurrentFont['subsetfontids'][$i];
  9332. }
  9333. $ret .= sprintf("%02s", strtoupper(dechex($init)));
  9334. break;
  9335. } // TrueType embedded SUBSETS
  9336. elseif (count($this->CurrentFont['subsets'][$i]) < 255) {
  9337. $n = count($this->CurrentFont['subsets'][$i]);
  9338. $this->CurrentFont['subsets'][$i][$n] = $c;
  9339. if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {
  9340. $ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';
  9341. $last_fid = $this->CurrentFont['subsetfontids'][$i];
  9342. }
  9343. $ret .= sprintf("%02s", strtoupper(dechex($n)));
  9344. break;
  9345. } elseif (!isset($this->CurrentFont['subsets'][($i + 1)])) {
  9346. // TrueType embedded SUBSETS
  9347. $this->CurrentFont['subsets'][($i + 1)] = [0 => 0];
  9348. $new_fid = count($this->fonts) + $this->extraFontSubsets + 1;
  9349. $this->CurrentFont['subsetfontids'][($i + 1)] = $new_fid;
  9350. $this->extraFontSubsets++;
  9351. }
  9352. }
  9353. }
  9354. $ret .= '>';
  9355. if ($last_fid != $orig_fid) {
  9356. $ret .= ' Tj /F' . $orig_fid . ' ' . $this->FontSizePt . ' Tf <> ';
  9357. }
  9358. return $ret;
  9359. }
  9360. /* -- CJK-FONTS -- */
  9361. // from class PDF_Chinese CJK EXTENSIONS
  9362. function AddCIDFont($family, $style, $name, &$cw, $CMap, $registry, $desc)
  9363. {
  9364. $fontkey = strtolower($family) . strtoupper($style);
  9365. if (isset($this->fonts[$fontkey])) {
  9366. throw new \Mpdf\MpdfException("Font already added: $family $style");
  9367. }
  9368. $i = count($this->fonts) + $this->extraFontSubsets + 1;
  9369. $name = str_replace(' ', '', $name);
  9370. if ($family == 'sjis') {
  9371. $up = -120;
  9372. } else {
  9373. $up = -130;
  9374. }
  9375. // ? 'up' and 'ut' do not seem to be referenced anywhere
  9376. $this->fonts[$fontkey] = ['i' => $i, 'type' => 'Type0', 'name' => $name, 'up' => $up, 'ut' => 40, 'cw' => $cw, 'CMap' => $CMap, 'registry' => $registry, 'MissingWidth' => 1000, 'desc' => $desc];
  9377. }
  9378. function AddCJKFont($family)
  9379. {
  9380. if ($this->PDFA || $this->PDFX) {
  9381. throw new \Mpdf\MpdfException("Adobe CJK fonts cannot be embedded in mPDF (required for PDFA1-b and PDFX/1-a).");
  9382. }
  9383. if ($family == 'big5') {
  9384. $this->AddBig5Font();
  9385. } elseif ($family == 'gb') {
  9386. $this->AddGBFont();
  9387. } elseif ($family == 'sjis') {
  9388. $this->AddSJISFont();
  9389. } elseif ($family == 'uhc') {
  9390. $this->AddUHCFont();
  9391. }
  9392. }
  9393. function AddBig5Font()
  9394. {
  9395. // Add Big5 font with proportional Latin
  9396. $family = 'big5';
  9397. $name = 'MSungStd-Light-Acro';
  9398. $cw = $this->Big5_widths;
  9399. $CMap = 'UniCNS-UTF16-H';
  9400. $registry = ['ordering' => 'CNS1', 'supplement' => 4];
  9401. $desc = [
  9402. 'Ascent' => 880,
  9403. 'Descent' => -120,
  9404. 'CapHeight' => 880,
  9405. 'Flags' => 6,
  9406. 'FontBBox' => '[-160 -249 1015 1071]',
  9407. 'ItalicAngle' => 0,
  9408. 'StemV' => 93,
  9409. ];
  9410. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
  9411. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
  9412. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
  9413. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
  9414. }
  9415. function AddGBFont()
  9416. {
  9417. // Add GB font with proportional Latin
  9418. $family = 'gb';
  9419. $name = 'STSongStd-Light-Acro';
  9420. $cw = $this->GB_widths;
  9421. $CMap = 'UniGB-UTF16-H';
  9422. $registry = ['ordering' => 'GB1', 'supplement' => 4];
  9423. $desc = [
  9424. 'Ascent' => 880,
  9425. 'Descent' => -120,
  9426. 'CapHeight' => 737,
  9427. 'Flags' => 6,
  9428. 'FontBBox' => '[-25 -254 1000 880]',
  9429. 'ItalicAngle' => 0,
  9430. 'StemV' => 58,
  9431. 'Style' => '<< /Panose <000000000400000000000000> >>',
  9432. ];
  9433. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
  9434. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
  9435. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
  9436. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
  9437. }
  9438. function AddSJISFont()
  9439. {
  9440. // Add SJIS font with proportional Latin
  9441. $family = 'sjis';
  9442. $name = 'KozMinPro-Regular-Acro';
  9443. $cw = $this->SJIS_widths;
  9444. $CMap = 'UniJIS-UTF16-H';
  9445. $registry = ['ordering' => 'Japan1', 'supplement' => 5];
  9446. $desc = [
  9447. 'Ascent' => 880,
  9448. 'Descent' => -120,
  9449. 'CapHeight' => 740,
  9450. 'Flags' => 6,
  9451. 'FontBBox' => '[-195 -272 1110 1075]',
  9452. 'ItalicAngle' => 0,
  9453. 'StemV' => 86,
  9454. 'XHeight' => 502,
  9455. ];
  9456. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
  9457. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
  9458. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
  9459. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
  9460. }
  9461. function AddUHCFont()
  9462. {
  9463. // Add UHC font with proportional Latin
  9464. $family = 'uhc';
  9465. $name = 'HYSMyeongJoStd-Medium-Acro';
  9466. $cw = $this->UHC_widths;
  9467. $CMap = 'UniKS-UTF16-H';
  9468. $registry = ['ordering' => 'Korea1', 'supplement' => 2];
  9469. $desc = [
  9470. 'Ascent' => 880,
  9471. 'Descent' => -120,
  9472. 'CapHeight' => 720,
  9473. 'Flags' => 6,
  9474. 'FontBBox' => '[-28 -148 1001 880]',
  9475. 'ItalicAngle' => 0,
  9476. 'StemV' => 60,
  9477. 'Style' => '<< /Panose <000000000600000000000000> >>',
  9478. ];
  9479. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
  9480. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
  9481. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
  9482. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
  9483. }
  9484. /* -- END CJK-FONTS -- */
  9485. //////////////////////////////////////////////////////////////////////////////
  9486. //////////////////////////////////////////////////////////////////////////////
  9487. //////////////////////////////////////////////////////////////////////////////
  9488. //////////////////////////////////////////////////////////////////////////////
  9489. //////////////////////////////////////////////////////////////////////////////
  9490. //////////////////////////////////////////////////////////////////////////////
  9491. //////////////////////////////////////////////////////////////////////////////
  9492. function SetDefaultFont($font)
  9493. {
  9494. // Disallow embedded fonts to be used as defaults in PDFA
  9495. if ($this->PDFA || $this->PDFX) {
  9496. if (strtolower($font) == 'ctimes') {
  9497. $font = 'serif';
  9498. }
  9499. if (strtolower($font) == 'ccourier') {
  9500. $font = 'monospace';
  9501. }
  9502. if (strtolower($font) == 'chelvetica') {
  9503. $font = 'sans-serif';
  9504. }
  9505. }
  9506. $font = $this->SetFont($font); // returns substituted font if necessary
  9507. $this->default_font = $font;
  9508. $this->original_default_font = $font;
  9509. if (!$this->watermark_font) {
  9510. $this->watermark_font = $font;
  9511. } // *WATERMARK*
  9512. $this->defaultCSS['BODY']['FONT-FAMILY'] = $font;
  9513. $this->cssManager->CSS['BODY']['FONT-FAMILY'] = $font;
  9514. }
  9515. function SetDefaultFontSize($fontsize)
  9516. {
  9517. $this->default_font_size = $fontsize;
  9518. $this->original_default_font_size = $fontsize;
  9519. $this->SetFontSize($fontsize);
  9520. $this->defaultCSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';
  9521. $this->cssManager->CSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';
  9522. }
  9523. function SetDefaultBodyCSS($prop, $val)
  9524. {
  9525. if ($prop) {
  9526. $this->defaultCSS['BODY'][strtoupper($prop)] = $val;
  9527. $this->cssManager->CSS['BODY'][strtoupper($prop)] = $val;
  9528. }
  9529. }
  9530. function SetDirectionality($dir = 'ltr')
  9531. {
  9532. /* -- OTL -- */
  9533. if (strtolower($dir) == 'rtl') {
  9534. if ($this->directionality != 'rtl') {
  9535. // Swop L/R Margins so page 1 RTL is an 'even' page
  9536. $tmp = $this->DeflMargin;
  9537. $this->DeflMargin = $this->DefrMargin;
  9538. $this->DefrMargin = $tmp;
  9539. $this->orig_lMargin = $this->DeflMargin;
  9540. $this->orig_rMargin = $this->DefrMargin;
  9541. $this->SetMargins($this->DeflMargin, $this->DefrMargin, $this->tMargin);
  9542. }
  9543. $this->directionality = 'rtl';
  9544. $this->defaultAlign = 'R';
  9545. $this->defaultTableAlign = 'R';
  9546. } else {
  9547. /* -- END OTL -- */
  9548. $this->directionality = 'ltr';
  9549. $this->defaultAlign = 'L';
  9550. $this->defaultTableAlign = 'L';
  9551. } // *OTL*
  9552. $this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality;
  9553. }
  9554. // Return either a number (factor) - based on current set fontsize (if % or em) - or exact lineheight (with 'mm' after it)
  9555. function fixLineheight($v)
  9556. {
  9557. $lh = false;
  9558. if (preg_match('/^[0-9\.,]*$/', $v) && $v >= 0) {
  9559. return ($v + 0);
  9560. } elseif (strtoupper($v) == 'NORMAL' || $v == 'N') {
  9561. return 'N'; // mPDF 6
  9562. } else {
  9563. $tlh = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize, true);
  9564. if ($tlh) {
  9565. return ($tlh . 'mm');
  9566. }
  9567. }
  9568. return $this->normalLineheight;
  9569. }
  9570. function _getNormalLineheight($desc = false)
  9571. {
  9572. if (!$desc) {
  9573. $desc = $this->CurrentFont['desc'];
  9574. }
  9575. if (!isset($desc['Leading'])) {
  9576. $desc['Leading'] = 0;
  9577. }
  9578. if ($this->useFixedNormalLineHeight) {
  9579. $lh = $this->normalLineheight;
  9580. } elseif (isset($desc['Ascent']) && $desc['Ascent']) {
  9581. $lh = ($this->adjustFontDescLineheight * ($desc['Ascent'] - $desc['Descent'] + $desc['Leading']) / 1000);
  9582. } else {
  9583. $lh = $this->normalLineheight;
  9584. }
  9585. return $lh;
  9586. }
  9587. // Set a (fixed) lineheight to an actual value - either to named fontsize(pts) or default
  9588. function SetLineHeight($FontPt = '', $lh = '')
  9589. {
  9590. if (!$FontPt) {
  9591. $FontPt = $this->FontSizePt;
  9592. }
  9593. $fs = $FontPt / Mpdf::SCALE;
  9594. $this->lineheight = $this->_computeLineheight($lh, $fs);
  9595. }
  9596. function _computeLineheight($lh, $fs = '')
  9597. {
  9598. if ($this->shrin_k > 1) {
  9599. $k = $this->shrin_k;
  9600. } else {
  9601. $k = 1;
  9602. }
  9603. if (!$fs) {
  9604. $fs = $this->FontSize;
  9605. }
  9606. if ($lh == 'N') {
  9607. $lh = $this->_getNormalLineheight();
  9608. }
  9609. if (preg_match('/mm/', $lh)) {
  9610. return (((float) $lh) / $k); // convert to number
  9611. } elseif ($lh > 0) {
  9612. return ($fs * $lh);
  9613. }
  9614. return ($fs * $this->normalLineheight);
  9615. }
  9616. function _setLineYpos(&$fontsize, &$fontdesc, &$CSSlineheight, $blockYpos = false)
  9617. {
  9618. $ypos['glyphYorigin'] = 0;
  9619. $ypos['baseline-shift'] = 0;
  9620. $linegap = 0;
  9621. $leading = 0;
  9622. if (isset($fontdesc['Ascent']) && $fontdesc['Ascent'] && !$this->useFixedTextBaseline) {
  9623. // Fontsize uses font metrics - this method seems to produce results compatible with browsers (except IE9)
  9624. $ypos['boxtop'] = $fontdesc['Ascent'] / 1000 * $fontsize;
  9625. $ypos['boxbottom'] = $fontdesc['Descent'] / 1000 * $fontsize;
  9626. if (isset($fontdesc['Leading'])) {
  9627. $linegap = $fontdesc['Leading'] / 1000 * $fontsize;
  9628. }
  9629. } // Default if not set - uses baselineC
  9630. else {
  9631. $ypos['boxtop'] = (0.5 + $this->baselineC) * $fontsize;
  9632. $ypos['boxbottom'] = -(0.5 - $this->baselineC) * $fontsize;
  9633. }
  9634. $fontheight = $ypos['boxtop'] - $ypos['boxbottom'];
  9635. if ($this->shrin_k > 1) {
  9636. $shrin_k = $this->shrin_k;
  9637. } else {
  9638. $shrin_k = 1;
  9639. }
  9640. $leading = 0;
  9641. if ($CSSlineheight == 'N') {
  9642. $lh = $this->_getNormalLineheight($fontdesc);
  9643. $lineheight = ($fontsize * $lh);
  9644. $leading += $linegap; // specified in hhea or sTypo in OpenType tables
  9645. } elseif (preg_match('/mm/', $CSSlineheight)) {
  9646. $lineheight = (((float) $CSSlineheight) / $shrin_k); // convert to number
  9647. } // ??? If lineheight is a factor e.g. 1.3 ?? use factor x 1em or ? use 'normal' lineheight * factor
  9648. // Could depend on value for $text_height - a draft CSS value as set above for now
  9649. elseif ($CSSlineheight > 0) {
  9650. $lineheight = ($fontsize * $CSSlineheight);
  9651. } else {
  9652. $lineheight = ($fontsize * $this->normalLineheight);
  9653. }
  9654. // In general, calculate the "leading" - the difference between the fontheight and the lineheight
  9655. // and add half to the top and half to the bottom. BUT
  9656. // If an inline element has a font-size less than the block element, and the line-height is set as an em or % value
  9657. // it will add too much leading below the font and expand the height of the line - so just use the block element exttop/extbottom:
  9658. if (preg_match('/mm/', $CSSlineheight)
  9659. && ($blockYpos && $ypos['boxtop'] < $blockYpos['boxtop'])
  9660. && ($blockYpos && $ypos['boxbottom'] > $blockYpos['boxbottom'])) {
  9661. $ypos['exttop'] = $blockYpos['exttop'];
  9662. $ypos['extbottom'] = $blockYpos['extbottom'];
  9663. } else {
  9664. $leading += ($lineheight - $fontheight);
  9665. $ypos['exttop'] = $ypos['boxtop'] + $leading / 2;
  9666. $ypos['extbottom'] = $ypos['boxbottom'] - $leading / 2;
  9667. }
  9668. // TEMP ONLY FOR DEBUGGING *********************************
  9669. // $ypos['lineheight'] = $lineheight;
  9670. // $ypos['fontheight'] = $fontheight;
  9671. // $ypos['leading'] = $leading;
  9672. return $ypos;
  9673. }
  9674. /* Called from WriteFlowingBlock() and finishFlowingBlock()
  9675. Determines the line hieght and glyph/writing position
  9676. for each element in the line to be written */
  9677. function _setInlineBlockHeights(&$lineBox, &$stackHeight, &$content, &$font, $is_table)
  9678. {
  9679. if ($this->shrin_k > 1) {
  9680. $shrin_k = $this->shrin_k;
  9681. } else {
  9682. $shrin_k = 1;
  9683. }
  9684. $ypos = [];
  9685. $bordypos = [];
  9686. $bgypos = [];
  9687. if ($is_table) {
  9688. // FOR TABLE
  9689. $fontsize = $this->FontSize;
  9690. $fontkey = $this->FontFamily . $this->FontStyle;
  9691. $fontdesc = $this->fonts[$fontkey]['desc'];
  9692. $CSSlineheight = $this->cellLineHeight;
  9693. $line_stacking_strategy = $this->cellLineStackingStrategy; // inline-line-height [default] | block-line-height | max-height | grid-height
  9694. $line_stacking_shift = $this->cellLineStackingShift; // consider-shifts [default] | disregard-shifts
  9695. } else {
  9696. // FOR BLOCK FONT
  9697. $fontsize = $this->blk[$this->blklvl]['InlineProperties']['size'];
  9698. $fontkey = $this->blk[$this->blklvl]['InlineProperties']['family'] . $this->blk[$this->blklvl]['InlineProperties']['style'];
  9699. $fontdesc = $this->fonts[$fontkey]['desc'];
  9700. $CSSlineheight = $this->blk[$this->blklvl]['line_height'];
  9701. // inline-line-height | block-line-height | max-height | grid-height
  9702. $line_stacking_strategy = (isset($this->blk[$this->blklvl]['line_stacking_strategy']) ? $this->blk[$this->blklvl]['line_stacking_strategy'] : 'inline-line-height');
  9703. // consider-shifts | disregard-shifts
  9704. $line_stacking_shift = (isset($this->blk[$this->blklvl]['line_stacking_shift']) ? $this->blk[$this->blklvl]['line_stacking_shift'] : 'consider-shifts');
  9705. }
  9706. $boxLineHeight = $this->_computeLineheight($CSSlineheight, $fontsize);
  9707. // First, set a "strut" using block font at index $lineBox[-1]
  9708. $ypos[-1] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight);
  9709. // for the block element - always taking the block EXTENDED progression including leading - which may be negative
  9710. if ($line_stacking_strategy == 'block-line-height') {
  9711. $topy = $ypos[-1]['exttop'];
  9712. $bottomy = $ypos[-1]['extbottom'];
  9713. } else {
  9714. $topy = 0;
  9715. $bottomy = 0;
  9716. }
  9717. // Get text-middle for aligning images/objects
  9718. $midpoint = $ypos[-1]['boxtop'] - (($ypos[-1]['boxtop'] - $ypos[-1]['boxbottom']) / 2);
  9719. // for images / inline objects / replaced elements
  9720. $mta = 0; // Maximum top-aligned
  9721. $mba = 0; // Maximum bottom-aligned
  9722. foreach ($content as $k => $chunk) {
  9723. if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'listmarker') {
  9724. $ypos[$k] = $ypos[-1];
  9725. // UPDATE Maximums
  9726. 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
  9727. if ($ypos[$k]['boxtop'] > $topy) {
  9728. $topy = $ypos[$k]['boxtop'];
  9729. }
  9730. if ($ypos[$k]['boxbottom'] < $bottomy) {
  9731. $bottomy = $ypos[$k]['boxbottom'];
  9732. }
  9733. } else {
  9734. if ($ypos[$k]['exttop'] > $topy) {
  9735. $topy = $ypos[$k]['exttop'];
  9736. }
  9737. if ($ypos[$k]['extbottom'] < $bottomy) {
  9738. $bottomy = $ypos[$k]['extbottom'];
  9739. }
  9740. }
  9741. } elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
  9742. $fontsize = $font[$k]['size'];
  9743. $fontdesc = $font[$k]['curr']['desc'];
  9744. $lh = 1;
  9745. $ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $lh, $ypos[-1]); // Lineheight=1 fixed
  9746. } elseif (isset($this->objectbuffer[$k])) {
  9747. $oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];
  9748. $va = $this->objectbuffer[$k]['vertical-align'];
  9749. if ($va == 'BS') { // (BASELINE default)
  9750. if ($oh > $topy) {
  9751. $topy = $oh;
  9752. }
  9753. } elseif ($va == 'M') {
  9754. if (($midpoint + $oh / 2) > $topy) {
  9755. $topy = $midpoint + $oh / 2;
  9756. }
  9757. if (($midpoint - $oh / 2) < $bottomy) {
  9758. $bottomy = $midpoint - $oh / 2;
  9759. }
  9760. } elseif ($va == 'TT') {
  9761. if (($ypos[-1]['boxtop'] - $oh) < $bottomy) {
  9762. $bottomy = $ypos[-1]['boxtop'] - $oh;
  9763. $topy = max($topy, $ypos[-1]['boxtop']);
  9764. }
  9765. } elseif ($va == 'TB') {
  9766. if (($ypos[-1]['boxbottom'] + $oh) > $topy) {
  9767. $topy = $ypos[-1]['boxbottom'] + $oh;
  9768. $bottomy = min($bottomy, $ypos[-1]['boxbottom']);
  9769. }
  9770. } elseif ($va == 'T') {
  9771. if ($oh > $mta) {
  9772. $mta = $oh;
  9773. }
  9774. } elseif ($va == 'B') {
  9775. if ($oh > $mba) {
  9776. $mba = $oh;
  9777. }
  9778. }
  9779. } elseif ($content[$k] || $content[$k] === '0') {
  9780. // FOR FLOWING BLOCK
  9781. $fontsize = $font[$k]['size'];
  9782. $fontdesc = $font[$k]['curr']['desc'];
  9783. // In future could set CSS line-height from inline elements; for now, use block level:
  9784. $ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight, $ypos[-1]);
  9785. if (isset($font[$k]['textparam']['text-baseline']) && $font[$k]['textparam']['text-baseline'] != 0) {
  9786. $ypos[$k]['baseline-shift'] = $font[$k]['textparam']['text-baseline'];
  9787. }
  9788. // DO ALIGNMENT FOR BASELINES *******************
  9789. // Until most fonts have OpenType BASE tables, this won't work
  9790. // $ypos[$k] compared to $ypos[-1] or $ypos[$k-1] using $dominant_baseline and $baseline_table
  9791. // UPDATE Maximums
  9792. 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
  9793. if ($line_stacking_shift == 'disregard-shifts') {
  9794. if ($ypos[$k]['boxtop'] > $topy) {
  9795. $topy = $ypos[$k]['boxtop'];
  9796. }
  9797. if ($ypos[$k]['boxbottom'] < $bottomy) {
  9798. $bottomy = $ypos[$k]['boxbottom'];
  9799. }
  9800. } else {
  9801. if (($ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift']) > $topy) {
  9802. $topy = $ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift'];
  9803. }
  9804. if (($ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) {
  9805. $bottomy = $ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift'];
  9806. }
  9807. }
  9808. } else {
  9809. if ($line_stacking_shift == 'disregard-shifts') {
  9810. if ($ypos[$k]['exttop'] > $topy) {
  9811. $topy = $ypos[$k]['exttop'];
  9812. }
  9813. if ($ypos[$k]['extbottom'] < $bottomy) {
  9814. $bottomy = $ypos[$k]['extbottom'];
  9815. }
  9816. } else {
  9817. if (($ypos[$k]['exttop'] + $ypos[$k]['baseline-shift']) > $topy) {
  9818. $topy = $ypos[$k]['exttop'] + $ypos[$k]['baseline-shift'];
  9819. }
  9820. if (($ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) {
  9821. $bottomy = $ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift'];
  9822. }
  9823. }
  9824. }
  9825. // If BORDER set on inline element
  9826. if (isset($font[$k]['bord']) && $font[$k]['bord']) {
  9827. $bordfontsize = $font[$k]['textparam']['bord-decoration']['fontsize'] / $shrin_k;
  9828. $bordfontkey = $font[$k]['textparam']['bord-decoration']['fontkey'];
  9829. if ($bordfontkey != $fontkey || $bordfontsize != $fontsize || isset($font[$k]['textparam']['bord-decoration']['baseline'])) {
  9830. $bordfontdesc = $this->fonts[$bordfontkey]['desc'];
  9831. $bordypos[$k] = $this->_setLineYpos($bordfontsize, $bordfontdesc, $CSSlineheight, $ypos[-1]);
  9832. if (isset($font[$k]['textparam']['bord-decoration']['baseline']) && $font[$k]['textparam']['bord-decoration']['baseline'] != 0) {
  9833. $bordypos[$k]['baseline-shift'] = $font[$k]['textparam']['bord-decoration']['baseline'] / $shrin_k;
  9834. }
  9835. }
  9836. }
  9837. // If BACKGROUND set on inline element
  9838. if (isset($font[$k]['spanbgcolor']) && $font[$k]['spanbgcolor']) {
  9839. $bgfontsize = $font[$k]['textparam']['bg-decoration']['fontsize'] / $shrin_k;
  9840. $bgfontkey = $font[$k]['textparam']['bg-decoration']['fontkey'];
  9841. if ($bgfontkey != $fontkey || $bgfontsize != $fontsize || isset($font[$k]['textparam']['bg-decoration']['baseline'])) {
  9842. $bgfontdesc = $this->fonts[$bgfontkey]['desc'];
  9843. $bgypos[$k] = $this->_setLineYpos($bgfontsize, $bgfontdesc, $CSSlineheight, $ypos[-1]);
  9844. if (isset($font[$k]['textparam']['bg-decoration']['baseline']) && $font[$k]['textparam']['bg-decoration']['baseline'] != 0) {
  9845. $bgypos[$k]['baseline-shift'] = $font[$k]['textparam']['bg-decoration']['baseline'] / $shrin_k;
  9846. }
  9847. }
  9848. }
  9849. }
  9850. }
  9851. // TOP or BOTTOM aligned images
  9852. if ($mta > ($topy - $bottomy)) {
  9853. if (($topy - $mta) < $bottomy) {
  9854. $bottomy = $topy - $mta;
  9855. }
  9856. }
  9857. if ($mba > ($topy - $bottomy)) {
  9858. if (($bottomy + $mba) > $topy) {
  9859. $topy = $bottomy + $mba;
  9860. }
  9861. }
  9862. if ($line_stacking_strategy == 'block-line-height') { // fixed height set by block element (whether present or not)
  9863. $topy = $ypos[-1]['exttop'];
  9864. $bottomy = $ypos[-1]['extbottom'];
  9865. }
  9866. $inclusiveHeight = $topy - $bottomy;
  9867. // SET $stackHeight taking note of line_stacking_strategy
  9868. // NB inclusive height already takes account of need to consider block progression height (excludes leading set by lineheight)
  9869. // or extended block progression height (includes leading set by lineheight)
  9870. if ($line_stacking_strategy == 'block-line-height') { // fixed = extended block progression height of block element
  9871. $stackHeight = $boxLineHeight;
  9872. } elseif ($line_stacking_strategy == 'max-height') { // smallest height which includes extended block progression height of block element
  9873. // and block progression heights of inline elements (NOT extended)
  9874. $stackHeight = $inclusiveHeight;
  9875. } elseif ($line_stacking_strategy == 'grid-height') { // smallest multiple of block element lineheight to include
  9876. // block progression heights of inline elements (NOT extended)
  9877. $stackHeight = $boxLineHeight;
  9878. while ($stackHeight < $inclusiveHeight) {
  9879. $stackHeight += $boxLineHeight;
  9880. }
  9881. } else { // 'inline-line-height' = default // smallest height which includes extended block progression height of block element
  9882. // AND extended block progression heights of inline elements
  9883. $stackHeight = $inclusiveHeight;
  9884. }
  9885. $diff = $stackHeight - $inclusiveHeight;
  9886. $topy += $diff / 2;
  9887. $bottomy -= $diff / 2;
  9888. // ADJUST $ypos => lineBox using $stackHeight; lineBox are all offsets from the top of stackHeight in mm
  9889. // and SET IMAGE OFFSETS
  9890. $lineBox[-1]['boxtop'] = $topy - $ypos[-1]['boxtop'];
  9891. $lineBox[-1]['boxbottom'] = $topy - $ypos[-1]['boxbottom'];
  9892. // $lineBox[-1]['exttop'] = $topy - $ypos[-1]['exttop'];
  9893. // $lineBox[-1]['extbottom'] = $topy - $ypos[-1]['extbottom'];
  9894. $lineBox[-1]['glyphYorigin'] = $topy - $ypos[-1]['glyphYorigin'];
  9895. $lineBox[-1]['baseline-shift'] = $ypos[-1]['baseline-shift'];
  9896. $midpoint = $lineBox[-1]['boxbottom'] - (($lineBox[-1]['boxbottom'] - $lineBox[-1]['boxtop']) / 2);
  9897. foreach ($content as $k => $chunk) {
  9898. if (isset($this->objectbuffer[$k])) {
  9899. $oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];
  9900. // LIST MARKERS
  9901. if ($this->objectbuffer[$k]['type'] == 'listmarker') {
  9902. $oh = $fontsize;
  9903. } elseif ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
  9904. $oh = $font[$k]['size']; // == $this->objectbuffer[$k]['fontsize']/Mpdf::SCALE;
  9905. $lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];
  9906. $lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];
  9907. $lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];
  9908. $lineBox[$k]['baseline-shift'] = 0;
  9909. // continue;
  9910. }
  9911. $va = $this->objectbuffer[$k]['vertical-align']; // = $objattr['vertical-align'] = set as M,T,B,S
  9912. if ($va == 'BS') { // (BASELINE default)
  9913. $lineBox[$k]['top'] = $lineBox[-1]['glyphYorigin'] - $oh;
  9914. } elseif ($va == 'M') {
  9915. $lineBox[$k]['top'] = $midpoint - $oh / 2;
  9916. } elseif ($va == 'TT') {
  9917. $lineBox[$k]['top'] = $lineBox[-1]['boxtop'];
  9918. } elseif ($va == 'TB') {
  9919. $lineBox[$k]['top'] = $lineBox[-1]['boxbottom'] - $oh;
  9920. } elseif ($va == 'T') {
  9921. $lineBox[$k]['top'] = 0;
  9922. } elseif ($va == 'B') {
  9923. $lineBox[$k]['top'] = $stackHeight - $oh;
  9924. }
  9925. } elseif ($content[$k] || $content[$k] === '0') {
  9926. $lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];
  9927. $lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];
  9928. // $lineBox[$k]['exttop'] = $topy - $ypos[$k]['exttop'];
  9929. // $lineBox[$k]['extbottom'] = $topy - $ypos[$k]['extbottom'];
  9930. $lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];
  9931. $lineBox[$k]['baseline-shift'] = $ypos[$k]['baseline-shift'];
  9932. if (isset($bordypos[$k]['boxtop'])) {
  9933. $lineBox[$k]['border-boxtop'] = $topy - $bordypos[$k]['boxtop'];
  9934. $lineBox[$k]['border-boxbottom'] = $topy - $bordypos[$k]['boxbottom'];
  9935. $lineBox[$k]['border-baseline-shift'] = $bordypos[$k]['baseline-shift'];
  9936. }
  9937. if (isset($bgypos[$k]['boxtop'])) {
  9938. $lineBox[$k]['background-boxtop'] = $topy - $bgypos[$k]['boxtop'];
  9939. $lineBox[$k]['background-boxbottom'] = $topy - $bgypos[$k]['boxbottom'];
  9940. $lineBox[$k]['background-baseline-shift'] = $bgypos[$k]['baseline-shift'];
  9941. }
  9942. }
  9943. }
  9944. }
  9945. function SetBasePath($str = '')
  9946. {
  9947. if (isset($_SERVER['HTTP_HOST'])) {
  9948. $host = $_SERVER['HTTP_HOST'];
  9949. } elseif (isset($_SERVER['SERVER_NAME'])) {
  9950. $host = $_SERVER['SERVER_NAME'];
  9951. } else {
  9952. $host = '';
  9953. }
  9954. if (!$str) {
  9955. if (isset($_SERVER['SCRIPT_NAME'])) {
  9956. $currentPath = dirname($_SERVER['SCRIPT_NAME']);
  9957. } else {
  9958. $currentPath = dirname($_SERVER['PHP_SELF']);
  9959. }
  9960. $currentPath = str_replace("\\", "/", $currentPath);
  9961. if ($currentPath == '/') {
  9962. $currentPath = '';
  9963. }
  9964. if ($host) { // mPDF 6
  9965. if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') {
  9966. $currpath = 'https://' . $host . $currentPath . '/';
  9967. } else {
  9968. $currpath = 'http://' . $host . $currentPath . '/';
  9969. }
  9970. } else {
  9971. $currpath = '';
  9972. }
  9973. $this->basepath = $currpath;
  9974. $this->basepathIsLocal = true;
  9975. return;
  9976. }
  9977. $str = preg_replace('/\?.*/', '', $str);
  9978. if (!preg_match('/(http|https|ftp):\/\/.*\//i', $str)) {
  9979. $str .= '/';
  9980. }
  9981. $str .= 'xxx'; // in case $str ends in / e.g. http://www.bbc.co.uk/
  9982. $this->basepath = dirname($str) . "/"; // returns e.g. e.g. http://www.google.com/dir1/dir2/dir3/
  9983. $this->basepath = str_replace("\\", "/", $this->basepath); // If on Windows
  9984. $tr = parse_url($this->basepath);
  9985. $this->basepathIsLocal = (isset($tr['host']) && ($tr['host'] == $host));
  9986. }
  9987. public function GetFullPath(&$path, $basepath = '')
  9988. {
  9989. // @todo make return, remove reference
  9990. // When parsing CSS need to pass temporary basepath - so links are relative to current stylesheet
  9991. if (!$basepath) {
  9992. $basepath = $this->basepath;
  9993. }
  9994. // Fix path value
  9995. $path = str_replace("\\", '/', $path); // If on Windows
  9996. // mPDF 5.7.2
  9997. if (strpos($path, '//') === 0) {
  9998. $scheme = parse_url($basepath, PHP_URL_SCHEME);
  9999. $scheme = $scheme ?: 'http';
  10000. $path = $scheme . ':' . $path;
  10001. }
  10002. $path = preg_replace('|^./|', '', $path); // Inadvertently corrects "./path/etc" and "//www.domain.com/etc"
  10003. if (strpos($path, '#') === 0) {
  10004. return;
  10005. }
  10006. // Skip schemes not supported by installed stream wrappers
  10007. $wrappers = stream_get_wrappers();
  10008. $pattern = sprintf('@^(?!%s)[a-z0-9\.\-+]+:.*@i', implode('|', $wrappers));
  10009. if (preg_match($pattern, $path)) {
  10010. return;
  10011. }
  10012. if (strpos($path, '../') === 0) { // It is a relative link
  10013. $backtrackamount = substr_count($path, '../');
  10014. $maxbacktrack = substr_count($basepath, '/') - 3;
  10015. $filepath = str_replace('../', '', $path);
  10016. $path = $basepath;
  10017. // If it is an invalid relative link, then make it go to directory root
  10018. if ($backtrackamount > $maxbacktrack) {
  10019. $backtrackamount = $maxbacktrack;
  10020. }
  10021. // Backtrack some directories
  10022. for ($i = 0; $i < $backtrackamount + 1; $i++) {
  10023. $path = substr($path, 0, strrpos($path, "/"));
  10024. }
  10025. $path .= '/' . $filepath; // Make it an absolute path
  10026. return;
  10027. }
  10028. if ((strpos($path, ":/") === false || strpos($path, ":/") > 10) && !@is_file($path)) { // It is a local link. Ignore potential file errors
  10029. if (strpos($path, '/') === 0) {
  10030. $tr = parse_url($basepath);
  10031. // mPDF 5.7.2
  10032. $root = '';
  10033. if (!empty($tr['scheme'])) {
  10034. $root .= $tr['scheme'] . '://';
  10035. }
  10036. $root .= isset($tr['host']) ? $tr['host'] : '';
  10037. $root .= ((isset($tr['port']) && $tr['port']) ? (':' . $tr['port']) : ''); // mPDF 5.7.3
  10038. $path = $root . $path;
  10039. return;
  10040. }
  10041. $path = $basepath . $path;
  10042. }
  10043. // Do nothing if it is an Absolute Link
  10044. }
  10045. function docPageNum($num = 0, $extras = false)
  10046. {
  10047. if ($num < 1) {
  10048. $num = $this->page;
  10049. }
  10050. $type = $this->defaultPageNumStyle; // set default Page Number Style
  10051. $ppgno = $num;
  10052. $suppress = 0;
  10053. $offset = 0;
  10054. $lastreset = 0;
  10055. foreach ($this->PageNumSubstitutions as $psarr) {
  10056. if ($num >= $psarr['from']) {
  10057. if ($psarr['reset']) {
  10058. if ($psarr['reset'] > 1) {
  10059. $offset = $psarr['reset'] - 1;
  10060. }
  10061. $ppgno = $num - $psarr['from'] + 1 + $offset;
  10062. $lastreset = $psarr['from'];
  10063. }
  10064. if ($psarr['type']) {
  10065. $type = $psarr['type'];
  10066. }
  10067. if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
  10068. $suppress = 1;
  10069. } elseif (strtoupper($psarr['suppress']) == 'OFF') {
  10070. $suppress = 0;
  10071. }
  10072. }
  10073. }
  10074. if ($suppress) {
  10075. return '';
  10076. }
  10077. $ppgno = $this->_getStyledNumber($ppgno, $type);
  10078. if ($extras) {
  10079. $ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;
  10080. }
  10081. return $ppgno;
  10082. }
  10083. function docPageNumTotal($num = 0, $extras = false)
  10084. {
  10085. if ($num < 1) {
  10086. $num = $this->page;
  10087. }
  10088. $type = $this->defaultPageNumStyle; // set default Page Number Style
  10089. $ppgstart = 1;
  10090. $ppgend = count($this->pages) + 1;
  10091. $suppress = 0;
  10092. $offset = 0;
  10093. foreach ($this->PageNumSubstitutions as $psarr) {
  10094. if ($num >= $psarr['from']) {
  10095. if ($psarr['reset']) {
  10096. if ($psarr['reset'] > 1) {
  10097. $offset = $psarr['reset'] - 1;
  10098. }
  10099. $ppgstart = $psarr['from'] + $offset;
  10100. $ppgend = count($this->pages) + 1 + $offset;
  10101. }
  10102. if ($psarr['type']) {
  10103. $type = $psarr['type'];
  10104. }
  10105. if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
  10106. $suppress = 1;
  10107. } elseif (strtoupper($psarr['suppress']) == 'OFF') {
  10108. $suppress = 0;
  10109. }
  10110. }
  10111. if ($num < $psarr['from']) {
  10112. if ($psarr['reset']) {
  10113. $ppgend = $psarr['from'] + $offset;
  10114. break;
  10115. }
  10116. }
  10117. }
  10118. if ($suppress) {
  10119. return '';
  10120. }
  10121. $ppgno = $ppgend - $ppgstart + $offset;
  10122. $ppgno = $this->_getStyledNumber($ppgno, $type);
  10123. if ($extras) {
  10124. $ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;
  10125. }
  10126. return $ppgno;
  10127. }
  10128. // mPDF 6
  10129. function _getStyledNumber($ppgno, $type, $listmarker = false)
  10130. {
  10131. if ($listmarker) {
  10132. $reverse = true; // Reverse RTL numerals (Hebrew) when using for list
  10133. $checkfont = true; // Using list - font is set, so check if character is available
  10134. } else {
  10135. $reverse = false; // For pagenumbers, RTL numerals (Hebrew) will get reversed later by bidi
  10136. $checkfont = false; // For pagenumbers - font is not set, so no check
  10137. }
  10138. $decToAlpha = new Conversion\DecToAlpha();
  10139. $decToCjk = new Conversion\DecToCjk();
  10140. $decToHebrew = new Conversion\DecToHebrew();
  10141. $decToRoman = new Conversion\DecToRoman();
  10142. $decToOther = new Conversion\DecToOther($this);
  10143. $lowertype = strtolower($type);
  10144. if ($lowertype == 'upper-latin' || $lowertype == 'upper-alpha' || $type == 'A') {
  10145. $ppgno = $decToAlpha->convert($ppgno, true);
  10146. } elseif ($lowertype == 'lower-latin' || $lowertype == 'lower-alpha' || $type == 'a') {
  10147. $ppgno = $decToAlpha->convert($ppgno, false);
  10148. } elseif ($lowertype == 'upper-roman' || $type == 'I') {
  10149. $ppgno = $decToRoman->convert($ppgno, true);
  10150. } elseif ($lowertype == 'lower-roman' || $type == 'i') {
  10151. $ppgno = $decToRoman->convert($ppgno, false);
  10152. } elseif ($lowertype == 'hebrew') {
  10153. $ppgno = $decToHebrew->convert($ppgno, $reverse);
  10154. } elseif (preg_match('/(arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao|myanmar)/i', $lowertype, $m)) {
  10155. $cp = $decToOther->getCodePage($m[1]);
  10156. $ppgno = $decToOther->convert($ppgno, $cp, $checkfont);
  10157. } elseif ($lowertype == 'cjk-decimal') {
  10158. $ppgno = $decToCjk->convert($ppgno);
  10159. }
  10160. return $ppgno;
  10161. }
  10162. function docPageSettings($num = 0)
  10163. {
  10164. // Returns current type (numberstyle), suppression state for this page number;
  10165. // reset is only returned if set for this page number
  10166. if ($num < 1) {
  10167. $num = $this->page;
  10168. }
  10169. $type = $this->defaultPageNumStyle; // set default Page Number Style
  10170. $ppgno = $num;
  10171. $suppress = 0;
  10172. $offset = 0;
  10173. $reset = '';
  10174. foreach ($this->PageNumSubstitutions as $psarr) {
  10175. if ($num >= $psarr['from']) {
  10176. if ($psarr['reset']) {
  10177. if ($psarr['reset'] > 1) {
  10178. $offset = $psarr['reset'] - 1;
  10179. }
  10180. $ppgno = $num - $psarr['from'] + 1 + $offset;
  10181. }
  10182. if ($psarr['type']) {
  10183. $type = $psarr['type'];
  10184. }
  10185. if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
  10186. $suppress = 1;
  10187. } elseif (strtoupper($psarr['suppress']) == 'OFF') {
  10188. $suppress = 0;
  10189. }
  10190. }
  10191. if ($num == $psarr['from']) {
  10192. $reset = $psarr['reset'];
  10193. }
  10194. }
  10195. if ($suppress) {
  10196. $suppress = 'on';
  10197. } else {
  10198. $suppress = 'off';
  10199. }
  10200. return [$type, $suppress, $reset];
  10201. }
  10202. function RestartDocTemplate()
  10203. {
  10204. $this->docTemplateStart = $this->page;
  10205. }
  10206. // Page header
  10207. function Header($content = '')
  10208. {
  10209. $this->cMarginL = 0;
  10210. $this->cMarginR = 0;
  10211. if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLHeaderE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLHeader) || (!$this->mirrorMargins && $this->HTMLHeader)) {
  10212. $this->writeHTMLHeaders();
  10213. return;
  10214. }
  10215. }
  10216. /* -- TABLES -- */
  10217. function TableHeaderFooter($content = '', $tablestartpage = '', $tablestartcolumn = '', $horf = 'H', $level = 0, $firstSpread = true, $finalSpread = true)
  10218. {
  10219. if (($horf == 'H' || $horf == 'F') && !empty($content)) { // mPDF 5.7.2
  10220. $table = &$this->table[1][1];
  10221. // mPDF 5.7.2
  10222. if ($horf == 'F') { // Table Footer
  10223. $firstrow = count($table['cells']) - $table['footernrows'];
  10224. $lastrow = count($table['cells']) - 1;
  10225. } else { // Table Header
  10226. $firstrow = 0;
  10227. $lastrow = $table['headernrows'] - 1;
  10228. }
  10229. if (empty($content[$firstrow])) {
  10230. if ($this->debug) {
  10231. throw new \Mpdf\MpdfException("<tfoot> must precede <tbody> in a table");
  10232. } else {
  10233. return;
  10234. }
  10235. }
  10236. // Advance down page by half width of top border
  10237. if ($horf == 'H') { // Only if header
  10238. if ($table['borders_separate']) {
  10239. $adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];
  10240. } else {
  10241. $adv = $table['max_cell_border_width']['T'] / 2;
  10242. }
  10243. if ($adv) {
  10244. if ($this->table_rotate) {
  10245. $this->y += ($adv);
  10246. } else {
  10247. $this->DivLn($adv, $this->blklvl, true);
  10248. }
  10249. }
  10250. }
  10251. $topy = $content[$firstrow][0]['y'] - $this->y;
  10252. for ($i = $firstrow; $i <= $lastrow; $i++) {
  10253. $y = $this->y;
  10254. /* -- COLUMNS -- */
  10255. // If outside columns, this is done in PaintDivBB
  10256. if ($this->ColActive) {
  10257. // OUTER FILL BGCOLOR of DIVS
  10258. if ($this->blklvl > 0) {
  10259. $firstblockfill = $this->GetFirstBlockFill();
  10260. if ($firstblockfill && $this->blklvl >= $firstblockfill) {
  10261. $divh = $content[$i][0]['h'];
  10262. $bak_x = $this->x;
  10263. $this->DivLn($divh, -3, false);
  10264. // Reset current block fill
  10265. $bcor = $this->blk[$this->blklvl]['bgcolorarray'];
  10266. $this->SetFColor($bcor);
  10267. $this->x = $bak_x;
  10268. }
  10269. }
  10270. }
  10271. /* -- END COLUMNS -- */
  10272. $colctr = 0;
  10273. foreach ($content[$i] as $tablehf) {
  10274. $colctr++;
  10275. $y = Arrays::get($tablehf, 'y', null) - $topy;
  10276. $this->y = $y;
  10277. // Set some cell values
  10278. $x = Arrays::get($tablehf, 'x', null);
  10279. if (($this->mirrorMargins) && ($tablestartpage == 'ODD') && (($this->page) % 2 == 0)) { // EVEN
  10280. $x = $x + $this->MarginCorrection;
  10281. } elseif (($this->mirrorMargins) && ($tablestartpage == 'EVEN') && (($this->page) % 2 == 1)) { // ODD
  10282. $x = $x + $this->MarginCorrection;
  10283. }
  10284. /* -- COLUMNS -- */
  10285. // Added to correct for Columns
  10286. if ($this->ColActive) {
  10287. if ($this->directionality == 'rtl') { // *OTL*
  10288. $x -= ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  10289. } // *OTL*
  10290. else { // *OTL*
  10291. $x += ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap);
  10292. } // *OTL*
  10293. }
  10294. /* -- END COLUMNS -- */
  10295. if ($colctr == 1) {
  10296. $x0 = $x;
  10297. }
  10298. // mPDF ITERATION
  10299. if ($this->iterationCounter) {
  10300. foreach ($tablehf['textbuffer'] as $k => $t) {
  10301. if (!is_array($t[0]) && preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {
  10302. $vname = '__' . $m[1] . '_';
  10303. if (!isset($this->$vname)) {
  10304. $this->$vname = 1;
  10305. } else {
  10306. $this->$vname++;
  10307. }
  10308. $tablehf['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $tablehf['textbuffer'][$k][0]);
  10309. }
  10310. }
  10311. }
  10312. $w = Arrays::get($tablehf, 'w', null);
  10313. $h = Arrays::get($tablehf, 'h', null);
  10314. $va = Arrays::get($tablehf, 'va', null);
  10315. $R = Arrays::get($tablehf, 'R', null);
  10316. $direction = Arrays::get($tablehf, 'direction', null);
  10317. $mih = Arrays::get($tablehf, 'mih', null);
  10318. $border = Arrays::get($tablehf, 'border', null);
  10319. $border_details = Arrays::get($tablehf, 'border_details', null);
  10320. $padding = Arrays::get($tablehf, 'padding', null);
  10321. $this->tabletheadjustfinished = true;
  10322. $textbuffer = Arrays::get($tablehf, 'textbuffer', null);
  10323. // Align
  10324. $align = Arrays::get($tablehf, 'a', null);
  10325. $this->cellTextAlign = $align;
  10326. $this->cellLineHeight = Arrays::get($tablehf, 'cellLineHeight', null);
  10327. $this->cellLineStackingStrategy = Arrays::get($tablehf, 'cellLineStackingStrategy', null);
  10328. $this->cellLineStackingShift = Arrays::get($tablehf, 'cellLineStackingShift', null);
  10329. $this->x = $x;
  10330. if ($this->ColActive) {
  10331. if ($table['borders_separate']) {
  10332. $tablefill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;
  10333. if ($tablefill) {
  10334. $color = $this->colorConverter->convert($tablefill, $this->PDFAXwarnings);
  10335. if ($color) {
  10336. $xadj = ($table['border_spacing_H'] / 2);
  10337. $yadj = ($table['border_spacing_V'] / 2);
  10338. $wadj = $table['border_spacing_H'];
  10339. $hadj = $table['border_spacing_V'];
  10340. if ($i == $firstrow && $horf == 'H') { // Top
  10341. $yadj += $table['padding']['T'] + $table['border_details']['T']['w'];
  10342. $hadj += $table['padding']['T'] + $table['border_details']['T']['w'];
  10343. }
  10344. if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1)) || (!isset($tablehf['rowspan']) && ($i + 1) == ($lastrow + 1))) && $horf == 'F') { // Bottom
  10345. $hadj += $table['padding']['B'] + $table['border_details']['B']['w'];
  10346. }
  10347. if ($colctr == 1) { // Left
  10348. $xadj += $table['padding']['L'] + $table['border_details']['L']['w'];
  10349. $wadj += $table['padding']['L'] + $table['border_details']['L']['w'];
  10350. }
  10351. if ($colctr == count($content[$i])) { // Right
  10352. $wadj += $table['padding']['R'] + $table['border_details']['R']['w'];
  10353. }
  10354. $this->SetFColor($color);
  10355. $this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');
  10356. }
  10357. }
  10358. }
  10359. }
  10360. if ($table['empty_cells'] != 'hide' || !empty($textbuffer) || !$table['borders_separate']) {
  10361. $paintcell = true;
  10362. } else {
  10363. $paintcell = false;
  10364. }
  10365. // Vertical align
  10366. if ($R && intval($R) > 0 && isset($va) && $va != 'B') {
  10367. $va = 'B';
  10368. }
  10369. if (!isset($va) || empty($va) || $va == 'M') {
  10370. $this->y += ($h - $mih) / 2;
  10371. } elseif (isset($va) && $va == 'B') {
  10372. $this->y += $h - $mih;
  10373. }
  10374. // TABLE ROW OR CELL FILL BGCOLOR
  10375. $fill = 0;
  10376. if (isset($tablehf['bgcolor']) && $tablehf['bgcolor'] && $tablehf['bgcolor'] != 'transparent') {
  10377. $fill = $tablehf['bgcolor'];
  10378. $leveladj = 6;
  10379. } elseif (isset($content[$i][0]['trbgcolor']) && $content[$i][0]['trbgcolor'] && $content[$i][0]['trbgcolor'] != 'transparent') { // Row color
  10380. $fill = $content[$i][0]['trbgcolor'];
  10381. $leveladj = 3;
  10382. }
  10383. if ($fill && $paintcell) {
  10384. $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
  10385. if ($color) {
  10386. if ($table['borders_separate']) {
  10387. if ($this->ColActive) {
  10388. $this->SetFColor($color);
  10389. $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');
  10390. } else {
  10391. $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];
  10392. }
  10393. } else {
  10394. if ($this->ColActive) {
  10395. $this->SetFColor($color);
  10396. $this->Rect($x, $y, $w, $h, 'F');
  10397. } else {
  10398. $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color];
  10399. }
  10400. }
  10401. }
  10402. }
  10403. /* -- BACKGROUNDS -- */
  10404. if (isset($tablehf['gradient']) && $tablehf['gradient'] && $paintcell) {
  10405. $g = $this->gradient->parseBackgroundGradient($tablehf['gradient']);
  10406. if ($g) {
  10407. if ($table['borders_separate']) {
  10408. $px = $x + ($table['border_spacing_H'] / 2);
  10409. $py = $y + ($table['border_spacing_V'] / 2);
  10410. $pw = $w - $table['border_spacing_H'];
  10411. $ph = $h - $table['border_spacing_V'];
  10412. } else {
  10413. $px = $x;
  10414. $py = $y;
  10415. $pw = $w;
  10416. $ph = $h;
  10417. }
  10418. if ($this->ColActive) {
  10419. $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
  10420. } else {
  10421. $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' => ''];
  10422. }
  10423. }
  10424. }
  10425. if (isset($tablehf['background-image']) && $paintcell) {
  10426. if ($tablehf['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $tablehf['background-image']['gradient'])) {
  10427. $g = $this->gradient->parseMozGradient($tablehf['background-image']['gradient']);
  10428. if ($g) {
  10429. if ($table['borders_separate']) {
  10430. $px = $x + ($table['border_spacing_H'] / 2);
  10431. $py = $y + ($table['border_spacing_V'] / 2);
  10432. $pw = $w - $table['border_spacing_H'];
  10433. $ph = $h - $table['border_spacing_V'];
  10434. } else {
  10435. $px = $x;
  10436. $py = $y;
  10437. $pw = $w;
  10438. $ph = $h;
  10439. }
  10440. if ($this->ColActive) {
  10441. $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
  10442. } else {
  10443. $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' => ''];
  10444. }
  10445. }
  10446. } elseif ($tablehf['background-image']['image_id']) { // Background pattern
  10447. $n = count($this->patterns) + 1;
  10448. if ($table['borders_separate']) {
  10449. $px = $x + ($table['border_spacing_H'] / 2);
  10450. $py = $y + ($table['border_spacing_V'] / 2);
  10451. $pw = $w - $table['border_spacing_H'];
  10452. $ph = $h - $table['border_spacing_V'];
  10453. } else {
  10454. $px = $x;
  10455. $py = $y;
  10456. $pw = $w;
  10457. $ph = $h;
  10458. }
  10459. if ($this->ColActive) {
  10460. 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']);
  10461. $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']];
  10462. if ($tablehf['background-image']['opacity'] > 0 && $tablehf['background-image']['opacity'] < 1) {
  10463. $opac = $this->SetAlpha($tablehf['background-image']['opacity'], 'Normal', true);
  10464. } else {
  10465. $opac = '';
  10466. }
  10467. $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));
  10468. } else {
  10469. $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']];
  10470. }
  10471. }
  10472. }
  10473. /* -- END BACKGROUNDS -- */
  10474. // Cell Border
  10475. if ($table['borders_separate'] && $paintcell && $border) {
  10476. $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']);
  10477. } elseif ($paintcell && $border) {
  10478. $this->_tableRect($x, $y, $w, $h, $border, $border_details, true, $table['borders_separate']); // true causes buffer
  10479. }
  10480. // Print cell content
  10481. if (!empty($textbuffer)) {
  10482. if ($horf == 'F' && preg_match('/{colsum([0-9]*)[_]*}/', $textbuffer[0][0], $m)) {
  10483. $rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$colctr - 1]);
  10484. $textbuffer[0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $textbuffer[0][0]);
  10485. }
  10486. if ($R) {
  10487. $cellPtSize = $textbuffer[0][11] / $this->shrin_k;
  10488. if (!$cellPtSize) {
  10489. $cellPtSize = $this->default_font_size;
  10490. }
  10491. $cellFontHeight = ($cellPtSize / Mpdf::SCALE);
  10492. $opx = $this->x;
  10493. $opy = $this->y;
  10494. $angle = intval($R);
  10495. // Only allow 45 - 90 degrees (when bottom-aligned) or -90
  10496. if ($angle > 90) {
  10497. $angle = 90;
  10498. } elseif ($angle > 0 && (isset($va) && $va != 'B')) {
  10499. $angle = 90;
  10500. } elseif ($angle > 0 && $angle < 45) {
  10501. $angle = 45;
  10502. } elseif ($angle < 0) {
  10503. $angle = -90;
  10504. }
  10505. $offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);
  10506. if (isset($align) && $align == 'R') {
  10507. $this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($padding['R'] + $border_details['R']['w']);
  10508. } elseif (!isset($align) || $align == 'C') {
  10509. $this->x += ($w / 2) + ($offset);
  10510. } else {
  10511. $this->x += ($offset) + ($cellFontHeight / 3) + ($padding['L'] + $border_details['L']['w']);
  10512. }
  10513. $str = '';
  10514. foreach ($tablehf['textbuffer'] as $t) {
  10515. $str .= $t[0] . ' ';
  10516. }
  10517. $str = rtrim($str);
  10518. if (!isset($va) || $va == 'M') {
  10519. $this->y -= ($h - $mih) / 2; // Undo what was added earlier VERTICAL ALIGN
  10520. if ($angle > 0) {
  10521. $this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']) + ($mih - ($padding['T'] + $border_details['T']['w'] + $border_details['B']['w'] + $padding['B']));
  10522. } elseif ($angle < 0) {
  10523. $this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']);
  10524. }
  10525. } elseif (isset($va) && $va == 'B') {
  10526. $this->y -= $h - $mih; // Undo what was added earlier VERTICAL ALIGN
  10527. if ($angle > 0) {
  10528. $this->y += $h - ($border_details['B']['w'] + $padding['B']);
  10529. } elseif ($angle < 0) {
  10530. $this->y += $h - $mih + ($padding['T'] + $border_details['T']['w']);
  10531. }
  10532. } elseif (isset($va) && $va == 'T') {
  10533. if ($angle > 0) {
  10534. $this->y += $mih - ($border_details['B']['w'] + $padding['B']);
  10535. } elseif ($angle < 0) {
  10536. $this->y += ($padding['T'] + $border_details['T']['w']);
  10537. }
  10538. }
  10539. $this->Rotate($angle, $this->x, $this->y);
  10540. $s_fs = $this->FontSizePt;
  10541. $s_f = $this->FontFamily;
  10542. $s_st = $this->FontStyle;
  10543. if (!empty($textbuffer[0][3])) { // Font Color
  10544. $cor = $textbuffer[0][3];
  10545. $this->SetTColor($cor);
  10546. }
  10547. $this->SetFont($textbuffer[0][4], $textbuffer[0][2], $cellPtSize, true, true);
  10548. $this->magic_reverse_dir($str, $this->directionality, $textbuffer[0][18]);
  10549. $this->Text($this->x, $this->y, $str, $textbuffer[0][18], $textbuffer[0][8]); // textvar
  10550. $this->Rotate(0);
  10551. $this->SetFont($s_f, $s_st, $s_fs, true, true);
  10552. $this->SetTColor(0);
  10553. $this->x = $opx;
  10554. $this->y = $opy;
  10555. } else {
  10556. if ($table['borders_separate']) { // NB twice border width
  10557. $xadj = $border_details['L']['w'] + $padding['L'] + ($table['border_spacing_H'] / 2);
  10558. $wadj = $border_details['L']['w'] + $border_details['R']['w'] + $padding['L'] + $padding['R'] + $table['border_spacing_H'];
  10559. $yadj = $border_details['T']['w'] + $padding['T'] + ($table['border_spacing_H'] / 2);
  10560. } else {
  10561. $xadj = $border_details['L']['w'] / 2 + $padding['L'];
  10562. $wadj = ($border_details['L']['w'] + $border_details['R']['w']) / 2 + $padding['L'] + $padding['R'];
  10563. $yadj = $border_details['T']['w'] / 2 + $padding['T'];
  10564. }
  10565. $this->divwidth = $w - ($wadj);
  10566. $this->x += $xadj;
  10567. $this->y += $yadj;
  10568. $this->printbuffer($textbuffer, '', true, false, $direction);
  10569. }
  10570. }
  10571. $textbuffer = [];
  10572. /* -- BACKGROUNDS -- */
  10573. if (!$this->ColActive) {
  10574. if (isset($content[$i][0]['trgradients']) && ($colctr == 1 || $table['borders_separate'])) {
  10575. $g = $this->gradient->parseBackgroundGradient($content[$i][0]['trgradients']);
  10576. if ($g) {
  10577. $gx = $x0;
  10578. $gy = $y;
  10579. $gh = $h;
  10580. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  10581. if ($table['borders_separate']) {
  10582. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  10583. $clx = $x + ($table['border_spacing_H'] / 2);
  10584. $cly = $y + ($table['border_spacing_V'] / 2);
  10585. $clw = $w - $table['border_spacing_H'];
  10586. $clh = $h - $table['border_spacing_V'];
  10587. // Set clipping path
  10588. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  10589. $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];
  10590. } else {
  10591. $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' => ''];
  10592. }
  10593. }
  10594. }
  10595. if (isset($content[$i][0]['trbackground-images']) && ($colctr == 1 || $table['borders_separate'])) {
  10596. if ($content[$i][0]['trbackground-images']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $content[$i][0]['trbackground-images']['gradient'])) {
  10597. $g = $this->gradient->parseMozGradient($content[$i][0]['trbackground-images']['gradient']);
  10598. if ($g) {
  10599. $gx = $x0;
  10600. $gy = $y;
  10601. $gh = $h;
  10602. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  10603. if ($table['borders_separate']) {
  10604. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  10605. $clx = $x + ($table['border_spacing_H'] / 2);
  10606. $cly = $y + ($table['border_spacing_V'] / 2);
  10607. $clw = $w - $table['border_spacing_H'];
  10608. $clh = $h - $table['border_spacing_V'];
  10609. // Set clipping path
  10610. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  10611. $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];
  10612. } else {
  10613. $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' => ''];
  10614. }
  10615. }
  10616. } else {
  10617. $image_id = $content[$i][0]['trbackground-images']['image_id'];
  10618. $orig_w = $content[$i][0]['trbackground-images']['orig_w'];
  10619. $orig_h = $content[$i][0]['trbackground-images']['orig_h'];
  10620. $x_pos = $content[$i][0]['trbackground-images']['x_pos'];
  10621. $y_pos = $content[$i][0]['trbackground-images']['y_pos'];
  10622. $x_repeat = $content[$i][0]['trbackground-images']['x_repeat'];
  10623. $y_repeat = $content[$i][0]['trbackground-images']['y_repeat'];
  10624. $resize = $content[$i][0]['trbackground-images']['resize'];
  10625. $opacity = $content[$i][0]['trbackground-images']['opacity'];
  10626. $itype = $content[$i][0]['trbackground-images']['itype'];
  10627. $clippath = '';
  10628. $gx = $x0;
  10629. $gy = $y;
  10630. $gh = $h;
  10631. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  10632. if ($table['borders_separate']) {
  10633. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  10634. $clx = $x + ($table['border_spacing_H'] / 2);
  10635. $cly = $y + ($table['border_spacing_V'] / 2);
  10636. $clw = $w - $table['border_spacing_H'];
  10637. $clh = $h - $table['border_spacing_V'];
  10638. // Set clipping path
  10639. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  10640. $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];
  10641. } else {
  10642. $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];
  10643. }
  10644. }
  10645. }
  10646. }
  10647. /* -- END BACKGROUNDS -- */
  10648. // TABLE BORDER - if separate OR collapsed and only table border
  10649. if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {
  10650. $halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);
  10651. $halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);
  10652. $halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);
  10653. $halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);
  10654. $tbx = $x;
  10655. $tby = $y;
  10656. $tbw = $w;
  10657. $tbh = $h;
  10658. $tab_bord = 0;
  10659. $corner = '';
  10660. if ($i == $firstrow && $horf == 'H') { // Top
  10661. $tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);
  10662. $tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);
  10663. $this->setBorder($tab_bord, Border::TOP);
  10664. $corner .= 'T';
  10665. }
  10666. if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1))) && $horf == 'F') { // Bottom
  10667. $tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);
  10668. $this->setBorder($tab_bord, Border::BOTTOM);
  10669. $corner .= 'B';
  10670. }
  10671. if ($colctr == 1 && $firstSpread) { // Left
  10672. $tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);
  10673. $tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);
  10674. $this->setBorder($tab_bord, Border::LEFT);
  10675. $corner .= 'L';
  10676. }
  10677. if ($colctr == count($content[$i]) && $finalSpread) { // Right
  10678. $tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);
  10679. $this->setBorder($tab_bord, Border::RIGHT);
  10680. $corner .= 'R';
  10681. }
  10682. $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']);
  10683. }
  10684. }// end column $content
  10685. $this->y = $y + $h; // Update y coordinate
  10686. }// end row $i
  10687. unset($table);
  10688. $this->colsums = [];
  10689. }
  10690. }
  10691. /* -- END TABLES -- */
  10692. function SetHTMLHeader($header = '', $OE = '', $write = false)
  10693. {
  10694. $height = 0;
  10695. if (is_array($header) && isset($header['html']) && $header['html']) {
  10696. $Hhtml = $header['html'];
  10697. if ($this->setAutoTopMargin) {
  10698. if (isset($header['h'])) {
  10699. $height = $header['h'];
  10700. } else {
  10701. $height = $this->_getHtmlHeight($Hhtml);
  10702. }
  10703. }
  10704. } elseif (!is_array($header) && $header) {
  10705. $Hhtml = $header;
  10706. if ($this->setAutoTopMargin) {
  10707. $height = $this->_getHtmlHeight($Hhtml);
  10708. }
  10709. } else {
  10710. $Hhtml = '';
  10711. }
  10712. if ($OE !== 'E') {
  10713. $OE = 'O';
  10714. }
  10715. if ($OE === 'E') {
  10716. if ($Hhtml) {
  10717. $this->HTMLHeaderE = [];
  10718. $this->HTMLHeaderE['html'] = $Hhtml;
  10719. $this->HTMLHeaderE['h'] = $height;
  10720. } else {
  10721. $this->HTMLHeaderE = '';
  10722. }
  10723. } else {
  10724. if ($Hhtml) {
  10725. $this->HTMLHeader = [];
  10726. $this->HTMLHeader['html'] = $Hhtml;
  10727. $this->HTMLHeader['h'] = $height;
  10728. } else {
  10729. $this->HTMLHeader = '';
  10730. }
  10731. }
  10732. if (!$this->mirrorMargins && $OE == 'E') {
  10733. return;
  10734. }
  10735. if ($Hhtml == '') {
  10736. return;
  10737. }
  10738. if ($this->setAutoTopMargin == 'pad') {
  10739. $this->tMargin = $this->margin_header + $height + $this->orig_tMargin;
  10740. if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {
  10741. $this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;
  10742. }
  10743. } elseif ($this->setAutoTopMargin == 'stretch') {
  10744. $this->tMargin = max($this->orig_tMargin, $this->margin_header + $height + $this->autoMarginPadding);
  10745. if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {
  10746. $this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;
  10747. }
  10748. }
  10749. if ($write && $this->state != 0 && (($this->mirrorMargins && $OE == 'E' && ($this->page) % 2 == 0) || ($this->mirrorMargins && $OE != 'E' && ($this->page) % 2 == 1) || !$this->mirrorMargins)) {
  10750. $this->writeHTMLHeaders();
  10751. }
  10752. }
  10753. function SetHTMLFooter($footer = '', $OE = '')
  10754. {
  10755. $height = 0;
  10756. if (is_array($footer) && isset($footer['html']) && $footer['html']) {
  10757. $Fhtml = $footer['html'];
  10758. if ($this->setAutoBottomMargin) {
  10759. if (isset($footer['h'])) {
  10760. $height = $footer['h'];
  10761. } else {
  10762. $height = $this->_getHtmlHeight($Fhtml);
  10763. }
  10764. }
  10765. } elseif (!is_array($footer) && $footer) {
  10766. $Fhtml = $footer;
  10767. if ($this->setAutoBottomMargin) {
  10768. $height = $this->_getHtmlHeight($Fhtml);
  10769. }
  10770. } else {
  10771. $Fhtml = '';
  10772. }
  10773. if ($OE !== 'E') {
  10774. $OE = 'O';
  10775. }
  10776. if ($OE === 'E') {
  10777. if ($Fhtml) {
  10778. $this->HTMLFooterE = [];
  10779. $this->HTMLFooterE['html'] = $Fhtml;
  10780. $this->HTMLFooterE['h'] = $height;
  10781. } else {
  10782. $this->HTMLFooterE = '';
  10783. }
  10784. } else {
  10785. if ($Fhtml) {
  10786. $this->HTMLFooter = [];
  10787. $this->HTMLFooter['html'] = $Fhtml;
  10788. $this->HTMLFooter['h'] = $height;
  10789. } else {
  10790. $this->HTMLFooter = '';
  10791. }
  10792. }
  10793. if (!$this->mirrorMargins && $OE == 'E') {
  10794. return;
  10795. }
  10796. if ($Fhtml == '') {
  10797. return false;
  10798. }
  10799. if ($this->setAutoBottomMargin == 'pad') {
  10800. $this->bMargin = $this->margin_footer + $height + $this->orig_bMargin;
  10801. $this->PageBreakTrigger = $this->h - $this->bMargin;
  10802. if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {
  10803. $this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;
  10804. }
  10805. } elseif ($this->setAutoBottomMargin == 'stretch') {
  10806. $this->bMargin = max($this->orig_bMargin, $this->margin_footer + $height + $this->autoMarginPadding);
  10807. $this->PageBreakTrigger = $this->h - $this->bMargin;
  10808. if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {
  10809. $this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;
  10810. }
  10811. }
  10812. }
  10813. function _getHtmlHeight($html)
  10814. {
  10815. $save_state = $this->state;
  10816. if ($this->state == 0) {
  10817. $this->AddPage($this->CurOrientation);
  10818. }
  10819. $this->state = 2;
  10820. $this->Reset();
  10821. $this->pageoutput[$this->page] = [];
  10822. $save_x = $this->x;
  10823. $save_y = $this->y;
  10824. $this->x = $this->lMargin;
  10825. $this->y = $this->margin_header;
  10826. // Replace of page number aliases and date format
  10827. $pnstr = $this->pagenumPrefix . $this->docPageNum($this->page) . $this->pagenumSuffix;
  10828. $pntstr = $this->nbpgPrefix . $this->docPageNumTotal($this->page) . $this->nbpgSuffix;
  10829. $nb = $this->page;
  10830. $html = $this->aliasReplace($html, $pnstr, $pntstr, $nb);
  10831. $this->HTMLheaderPageLinks = [];
  10832. $this->HTMLheaderPageAnnots = [];
  10833. $this->HTMLheaderPageForms = [];
  10834. $savepb = $this->pageBackgrounds;
  10835. $this->writingHTMLheader = true;
  10836. $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
  10837. $this->writingHTMLheader = false;
  10838. $h = ($this->y - $this->margin_header);
  10839. $this->Reset();
  10840. // mPDF 5.7.2 - Clear in case Float used in Header/Footer
  10841. $this->blk[0]['blockContext'] = 0;
  10842. $this->blk[0]['float_endpos'] = 0;
  10843. $this->pageoutput[$this->page] = [];
  10844. $this->headerbuffer = '';
  10845. $this->pageBackgrounds = $savepb;
  10846. $this->x = $save_x;
  10847. $this->y = $save_y;
  10848. $this->state = $save_state;
  10849. if ($save_state == 0) {
  10850. unset($this->pages[1]);
  10851. $this->page = 0;
  10852. }
  10853. return $h;
  10854. }
  10855. // Called internally from Header
  10856. function writeHTMLHeaders()
  10857. {
  10858. if ($this->mirrorMargins && ($this->page) % 2 == 0) {
  10859. $OE = 'E';
  10860. } else {
  10861. $OE = 'O';
  10862. }
  10863. if ($OE === 'E') {
  10864. $this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeaderE['html'];
  10865. } else {
  10866. $this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeader['html'];
  10867. }
  10868. if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {
  10869. $this->saveHTMLHeader[$this->page][$OE]['rotate'] = true;
  10870. $this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->tMargin;
  10871. $this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->bMargin;
  10872. $this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;
  10873. $this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;
  10874. $this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->h;
  10875. $this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->w;
  10876. } else {
  10877. $this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->lMargin;
  10878. $this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->rMargin;
  10879. $this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;
  10880. $this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;
  10881. $this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->w;
  10882. $this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->h;
  10883. }
  10884. }
  10885. function writeHTMLFooters()
  10886. {
  10887. if ($this->mirrorMargins && ($this->page) % 2 == 0) {
  10888. $OE = 'E';
  10889. } else {
  10890. $OE = 'O';
  10891. }
  10892. if ($OE === 'E') {
  10893. $this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooterE['html'];
  10894. } else {
  10895. $this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooter['html'];
  10896. }
  10897. if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {
  10898. $this->saveHTMLFooter[$this->page][$OE]['rotate'] = true;
  10899. $this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->tMargin;
  10900. $this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->bMargin;
  10901. $this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->rMargin;
  10902. $this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->lMargin;
  10903. $this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;
  10904. $this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;
  10905. $this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->h;
  10906. $this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->w;
  10907. } else {
  10908. $this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->lMargin;
  10909. $this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->rMargin;
  10910. $this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->tMargin;
  10911. $this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->bMargin;
  10912. $this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;
  10913. $this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;
  10914. $this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->w;
  10915. $this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->h;
  10916. }
  10917. }
  10918. // mPDF 6
  10919. function _shareHeaderFooterWidth($cl, $cc, $cr)
  10920. {
  10921. // mPDF 6
  10922. $l = mb_strlen($cl, 'UTF-8');
  10923. $c = mb_strlen($cc, 'UTF-8');
  10924. $r = mb_strlen($cr, 'UTF-8');
  10925. $s = max($l, $r);
  10926. $tw = $c + 2 * $s;
  10927. if ($tw > 0) {
  10928. return [intval($s * 100 / $tw), intval($c * 100 / $tw), intval($s * 100 / $tw)];
  10929. } else {
  10930. return [33, 33, 33];
  10931. }
  10932. }
  10933. // mPDF 6
  10934. // Create an HTML header/footer from array (non-HTML header/footer)
  10935. function _createHTMLheaderFooter($arr, $hf)
  10936. {
  10937. $lContent = (isset($arr['L']['content']) ? $arr['L']['content'] : '');
  10938. $cContent = (isset($arr['C']['content']) ? $arr['C']['content'] : '');
  10939. $rContent = (isset($arr['R']['content']) ? $arr['R']['content'] : '');
  10940. list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($lContent, $cContent, $rContent);
  10941. if ($hf == 'H') {
  10942. $valign = 'bottom';
  10943. $vpadding = '0 0 ' . $this->header_line_spacing . 'em 0';
  10944. } else {
  10945. $valign = 'top';
  10946. $vpadding = '' . $this->footer_line_spacing . 'em 0 0 0';
  10947. }
  10948. if ($this->directionality == 'rtl') { // table columns get reversed so need different text-alignment
  10949. $talignL = 'right';
  10950. $talignR = 'left';
  10951. } else {
  10952. $talignL = 'left';
  10953. $talignR = 'right';
  10954. }
  10955. $html = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: ' . $valign . '; color: #000000; ';
  10956. if (isset($arr['line']) && $arr['line']) {
  10957. $html .= ' border-' . $valign . ': 0.1mm solid #000000;';
  10958. }
  10959. $html .= '">';
  10960. $html .= '<tr>';
  10961. $html .= '<td width="' . $lw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignL . '; ';
  10962. if (isset($arr['L']['font-family'])) {
  10963. $html .= ' font-family: ' . $arr['L']['font-family'] . ';';
  10964. }
  10965. if (isset($arr['L']['color'])) {
  10966. $html .= ' color: ' . $arr['L']['color'] . ';';
  10967. }
  10968. if (isset($arr['L']['font-size'])) {
  10969. $html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;';
  10970. }
  10971. if (isset($arr['L']['font-style'])) {
  10972. if ($arr['L']['font-style'] == 'B' || $arr['L']['font-style'] == 'BI') {
  10973. $html .= ' font-weight: bold;';
  10974. }
  10975. if ($arr['L']['font-style'] == 'I' || $arr['L']['font-style'] == 'BI') {
  10976. $html .= ' font-style: italic;';
  10977. }
  10978. }
  10979. $html .= '">' . $lContent . '</td>';
  10980. $html .= '<td width="' . $cw . '%" style="padding: ' . $vpadding . '; text-align: center; ';
  10981. if (isset($arr['C']['font-family'])) {
  10982. $html .= ' font-family: ' . $arr['C']['font-family'] . ';';
  10983. }
  10984. if (isset($arr['C']['color'])) {
  10985. $html .= ' color: ' . $arr['C']['color'] . ';';
  10986. }
  10987. if (isset($arr['C']['font-size'])) {
  10988. $html .= ' font-size: ' . $arr['C']['font-size'] . 'pt;';
  10989. }
  10990. if (isset($arr['C']['font-style'])) {
  10991. if ($arr['C']['font-style'] == 'B' || $arr['C']['font-style'] == 'BI') {
  10992. $html .= ' font-weight: bold;';
  10993. }
  10994. if ($arr['C']['font-style'] == 'I' || $arr['C']['font-style'] == 'BI') {
  10995. $html .= ' font-style: italic;';
  10996. }
  10997. }
  10998. $html .= '">' . $cContent . '</td>';
  10999. $html .= '<td width="' . $rw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignR . '; ';
  11000. if (isset($arr['R']['font-family'])) {
  11001. $html .= ' font-family: ' . $arr['R']['font-family'] . ';';
  11002. }
  11003. if (isset($arr['R']['color'])) {
  11004. $html .= ' color: ' . $arr['R']['color'] . ';';
  11005. }
  11006. if (isset($arr['R']['font-size'])) {
  11007. $html .= ' font-size: ' . $arr['R']['font-size'] . 'pt;';
  11008. }
  11009. if (isset($arr['R']['font-style'])) {
  11010. if ($arr['R']['font-style'] == 'B' || $arr['R']['font-style'] == 'BI') {
  11011. $html .= ' font-weight: bold;';
  11012. }
  11013. if ($arr['R']['font-style'] == 'I' || $arr['R']['font-style'] == 'BI') {
  11014. $html .= ' font-style: italic;';
  11015. }
  11016. }
  11017. $html .= '">' . $rContent . '</td>';
  11018. $html .= '</tr></table>';
  11019. return $html;
  11020. }
  11021. function DefHeaderByName($name, $arr)
  11022. {
  11023. if (!$name) {
  11024. $name = '_nonhtmldefault';
  11025. }
  11026. $html = $this->_createHTMLheaderFooter($arr, 'H');
  11027. $this->pageHTMLheaders[$name]['html'] = $html;
  11028. $this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html);
  11029. }
  11030. function DefFooterByName($name, $arr)
  11031. {
  11032. if (!$name) {
  11033. $name = '_nonhtmldefault';
  11034. }
  11035. $html = $this->_createHTMLheaderFooter($arr, 'F');
  11036. $this->pageHTMLfooters[$name]['html'] = $html;
  11037. $this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html);
  11038. }
  11039. function SetHeaderByName($name, $side = 'O', $write = false)
  11040. {
  11041. if (!$name) {
  11042. $name = '_nonhtmldefault';
  11043. }
  11044. $this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);
  11045. }
  11046. function SetFooterByName($name, $side = 'O')
  11047. {
  11048. if (!$name) {
  11049. $name = '_nonhtmldefault';
  11050. }
  11051. $this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);
  11052. }
  11053. function DefHTMLHeaderByName($name, $html)
  11054. {
  11055. if (!$name) {
  11056. $name = '_default';
  11057. }
  11058. $this->pageHTMLheaders[$name]['html'] = $html;
  11059. $this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html);
  11060. }
  11061. function DefHTMLFooterByName($name, $html)
  11062. {
  11063. if (!$name) {
  11064. $name = '_default';
  11065. }
  11066. $this->pageHTMLfooters[$name]['html'] = $html;
  11067. $this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html);
  11068. }
  11069. function SetHTMLHeaderByName($name, $side = 'O', $write = false)
  11070. {
  11071. if (!$name) {
  11072. $name = '_default';
  11073. }
  11074. $this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);
  11075. }
  11076. function SetHTMLFooterByName($name, $side = 'O')
  11077. {
  11078. if (!$name) {
  11079. $name = '_default';
  11080. }
  11081. $this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);
  11082. }
  11083. function SetHeader($Harray = [], $side = '', $write = false)
  11084. {
  11085. $oddhtml = '';
  11086. $evenhtml = '';
  11087. if (is_string($Harray)) {
  11088. if (strlen($Harray) === 0) {
  11089. $oddhtml = '';
  11090. $evenhtml = '';
  11091. } elseif (strpos($Harray, '|') !== false) {
  11092. $hdet = explode('|', $Harray);
  11093. list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($hdet[0], $hdet[1], $hdet[2]);
  11094. $oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';
  11095. if ($this->defaultheaderfontsize) {
  11096. $oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
  11097. }
  11098. if ($this->defaultheaderfontstyle) {
  11099. if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
  11100. $oddhtml .= ' font-weight: bold;';
  11101. }
  11102. if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
  11103. $oddhtml .= ' font-style: italic;';
  11104. }
  11105. }
  11106. if ($this->defaultheaderline) {
  11107. $oddhtml .= ' border-bottom: 0.1mm solid #000000;';
  11108. }
  11109. $oddhtml .= '">';
  11110. $oddhtml .= '<tr>';
  11111. $oddhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[0] . '</td>';
  11112. $oddhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>';
  11113. $oddhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[2] . '</td>';
  11114. $oddhtml .= '</tr></table>';
  11115. $evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';
  11116. if ($this->defaultheaderfontsize) {
  11117. $evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
  11118. }
  11119. if ($this->defaultheaderfontstyle) {
  11120. if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
  11121. $evenhtml .= ' font-weight: bold;';
  11122. }
  11123. if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
  11124. $evenhtml .= ' font-style: italic;';
  11125. }
  11126. }
  11127. if ($this->defaultheaderline) {
  11128. $evenhtml .= ' border-bottom: 0.1mm solid #000000;';
  11129. }
  11130. $evenhtml .= '">';
  11131. $evenhtml .= '<tr>';
  11132. $evenhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[2] . '</td>';
  11133. $evenhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>';
  11134. $evenhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[0] . '</td>';
  11135. $evenhtml .= '</tr></table>';
  11136. } else {
  11137. $oddhtml = '<div style="margin: 0; color: #000000; ';
  11138. if ($this->defaultheaderfontsize) {
  11139. $oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
  11140. }
  11141. if ($this->defaultheaderfontstyle) {
  11142. if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
  11143. $oddhtml .= ' font-weight: bold;';
  11144. }
  11145. if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
  11146. $oddhtml .= ' font-style: italic;';
  11147. }
  11148. }
  11149. if ($this->defaultheaderline) {
  11150. $oddhtml .= ' border-bottom: 0.1mm solid #000000;';
  11151. }
  11152. $oddhtml .= 'text-align: right; ">' . $Harray . '</div>';
  11153. $evenhtml = '<div style="margin: 0; color: #000000; ';
  11154. if ($this->defaultheaderfontsize) {
  11155. $evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
  11156. }
  11157. if ($this->defaultheaderfontstyle) {
  11158. if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
  11159. $evenhtml .= ' font-weight: bold;';
  11160. }
  11161. if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
  11162. $evenhtml .= ' font-style: italic;';
  11163. }
  11164. }
  11165. if ($this->defaultheaderline) {
  11166. $evenhtml .= ' border-bottom: 0.1mm solid #000000;';
  11167. }
  11168. $evenhtml .= 'text-align: left; ">' . $Harray . '</div>';
  11169. }
  11170. } elseif (is_array($Harray) && !empty($Harray)) {
  11171. $odd = null;
  11172. $even = null;
  11173. if ($side === 'O') {
  11174. $odd = $Harray;
  11175. } elseif ($side === 'E') {
  11176. $even = $Harray;
  11177. } else {
  11178. $odd = Arrays::get($Harray, 'odd', null);
  11179. $even = Arrays::get($Harray, 'even', null);
  11180. }
  11181. $oddhtml = $this->_createHTMLheaderFooter($odd, 'H');
  11182. $evenhtml = $this->_createHTMLheaderFooter($even, 'H');
  11183. }
  11184. if ($side === 'E') {
  11185. $this->SetHTMLHeader($evenhtml, 'E', $write);
  11186. } elseif ($side === 'O') {
  11187. $this->SetHTMLHeader($oddhtml, 'O', $write);
  11188. } else {
  11189. $this->SetHTMLHeader($oddhtml, 'O', $write);
  11190. $this->SetHTMLHeader($evenhtml, 'E', $write);
  11191. }
  11192. }
  11193. function SetFooter($Farray = [], $side = '')
  11194. {
  11195. $oddhtml = '';
  11196. $evenhtml = '';
  11197. if (is_string($Farray)) {
  11198. if (strlen($Farray) == 0) {
  11199. $oddhtml = '';
  11200. $evenhtml = '';
  11201. } elseif (strpos($Farray, '|') !== false) {
  11202. $hdet = explode('|', $Farray);
  11203. $oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';
  11204. if ($this->defaultfooterfontsize) {
  11205. $oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
  11206. }
  11207. if ($this->defaultfooterfontstyle) {
  11208. if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
  11209. $oddhtml .= ' font-weight: bold;';
  11210. }
  11211. if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
  11212. $oddhtml .= ' font-style: italic;';
  11213. }
  11214. }
  11215. if ($this->defaultfooterline) {
  11216. $oddhtml .= ' border-top: 0.1mm solid #000000;';
  11217. }
  11218. $oddhtml .= '">';
  11219. $oddhtml .= '<tr>';
  11220. $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[0] . '</td>';
  11221. $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>';
  11222. $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[2] . '</td>';
  11223. $oddhtml .= '</tr></table>';
  11224. $evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';
  11225. if ($this->defaultfooterfontsize) {
  11226. $evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
  11227. }
  11228. if ($this->defaultfooterfontstyle) {
  11229. if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
  11230. $evenhtml .= ' font-weight: bold;';
  11231. }
  11232. if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
  11233. $evenhtml .= ' font-style: italic;';
  11234. }
  11235. }
  11236. if ($this->defaultfooterline) {
  11237. $evenhtml .= ' border-top: 0.1mm solid #000000;';
  11238. }
  11239. $evenhtml .= '">';
  11240. $evenhtml .= '<tr>';
  11241. $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[2] . '</td>';
  11242. $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>';
  11243. $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[0] . '</td>';
  11244. $evenhtml .= '</tr></table>';
  11245. } else {
  11246. $oddhtml = '<div style="margin: 0; color: #000000; ';
  11247. if ($this->defaultfooterfontsize) {
  11248. $oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
  11249. }
  11250. if ($this->defaultfooterfontstyle) {
  11251. if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
  11252. $oddhtml .= ' font-weight: bold;';
  11253. }
  11254. if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
  11255. $oddhtml .= ' font-style: italic;';
  11256. }
  11257. }
  11258. if ($this->defaultfooterline) {
  11259. $oddhtml .= ' border-top: 0.1mm solid #000000;';
  11260. }
  11261. $oddhtml .= 'text-align: right; ">' . $Farray . '</div>';
  11262. $evenhtml = '<div style="margin: 0; color: #000000; ';
  11263. if ($this->defaultfooterfontsize) {
  11264. $evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
  11265. }
  11266. if ($this->defaultfooterfontstyle) {
  11267. if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
  11268. $evenhtml .= ' font-weight: bold;';
  11269. }
  11270. if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
  11271. $evenhtml .= ' font-style: italic;';
  11272. }
  11273. }
  11274. if ($this->defaultfooterline) {
  11275. $evenhtml .= ' border-top: 0.1mm solid #000000;';
  11276. }
  11277. $evenhtml .= 'text-align: left; ">' . $Farray . '</div>';
  11278. }
  11279. } elseif (is_array($Farray)) {
  11280. $odd = null;
  11281. $even = null;
  11282. if ($side === 'O') {
  11283. $odd = $Farray;
  11284. } elseif ($side == 'E') {
  11285. $even = $Farray;
  11286. } else {
  11287. $odd = Arrays::get($Farray, 'odd', null);
  11288. $even = Arrays::get($Farray, 'even', null);
  11289. }
  11290. $oddhtml = $this->_createHTMLheaderFooter($odd, 'F');
  11291. $evenhtml = $this->_createHTMLheaderFooter($even, 'F');
  11292. }
  11293. if ($side === 'E') {
  11294. $this->SetHTMLFooter($evenhtml, 'E');
  11295. } elseif ($side === 'O') {
  11296. $this->SetHTMLFooter($oddhtml, 'O');
  11297. } else {
  11298. $this->SetHTMLFooter($oddhtml, 'O');
  11299. $this->SetHTMLFooter($evenhtml, 'E');
  11300. }
  11301. }
  11302. /* -- WATERMARK -- */
  11303. function SetWatermarkText($txt = '', $alpha = -1)
  11304. {
  11305. if ($txt instanceof \Mpdf\WatermarkText) {
  11306. $this->watermarkTextObject = $txt;
  11307. $this->watermarkText = $txt->getText();
  11308. $this->watermarkTextAlpha = $txt->getAlpha();
  11309. $this->watermarkAngle = $txt->getAngle();
  11310. $this->watermark_font = $txt->getFont() === null ? $txt->getFont() : $this->watermark_font;
  11311. $this->watermark_size = $txt->getSize();
  11312. return;
  11313. }
  11314. if ($alpha >= 0) {
  11315. $this->watermarkTextAlpha = $alpha;
  11316. }
  11317. $this->watermarkText = $txt;
  11318. }
  11319. function SetWatermarkImage($src, $alpha = -1, $size = 'D', $pos = 'F')
  11320. {
  11321. if ($src instanceof \Mpdf\WatermarkImage) {
  11322. $this->watermarkImage = $src->getPath();
  11323. $this->watermark_size = $src->getSize();
  11324. $this->watermark_pos = $src->getPosition();
  11325. $this->watermarkImageAlpha = $src->getAlpha();
  11326. $this->watermarkImgBehind = $src->isBehindContent();
  11327. $this->watermarkImgAlphaBlend = $src->getAlphaBlend();
  11328. return;
  11329. }
  11330. if ($alpha >= 0) {
  11331. $this->watermarkImageAlpha = $alpha;
  11332. }
  11333. $this->watermarkImage = $src;
  11334. $this->watermark_size = $size;
  11335. $this->watermark_pos = $pos;
  11336. }
  11337. /* -- END WATERMARK -- */
  11338. // Page footer
  11339. function Footer()
  11340. {
  11341. /* -- CSS-PAGE -- */
  11342. // PAGED MEDIA - CROP / CROSS MARKS from @PAGE
  11343. if ($this->show_marks == 'CROP' || $this->show_marks == 'CROPCROSS') {
  11344. // Show TICK MARKS
  11345. $this->SetLineWidth(0.1); // = 0.1 mm
  11346. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  11347. $l = $this->cropMarkLength;
  11348. $m = $this->cropMarkMargin; // Distance of crop mark from margin
  11349. $b = $this->nonPrintMargin; // Non-printable border at edge of paper sheet
  11350. $ax1 = $b;
  11351. $bx = $this->page_box['outer_width_LR'] - $m;
  11352. $ax = max($ax1, $bx - $l);
  11353. $cx1 = $this->w - $b;
  11354. $dx = $this->w - $this->page_box['outer_width_LR'] + $m;
  11355. $cx = min($cx1, $dx + $l);
  11356. $ay1 = $b;
  11357. $by = $this->page_box['outer_width_TB'] - $m;
  11358. $ay = max($ay1, $by - $l);
  11359. $cy1 = $this->h - $b;
  11360. $dy = $this->h - $this->page_box['outer_width_TB'] + $m;
  11361. $cy = min($cy1, $dy + $l);
  11362. $this->Line($ax, $this->page_box['outer_width_TB'], $bx, $this->page_box['outer_width_TB']);
  11363. $this->Line($cx, $this->page_box['outer_width_TB'], $dx, $this->page_box['outer_width_TB']);
  11364. $this->Line($ax, $this->h - $this->page_box['outer_width_TB'], $bx, $this->h - $this->page_box['outer_width_TB']);
  11365. $this->Line($cx, $this->h - $this->page_box['outer_width_TB'], $dx, $this->h - $this->page_box['outer_width_TB']);
  11366. $this->Line($this->page_box['outer_width_LR'], $ay, $this->page_box['outer_width_LR'], $by);
  11367. $this->Line($this->page_box['outer_width_LR'], $cy, $this->page_box['outer_width_LR'], $dy);
  11368. $this->Line($this->w - $this->page_box['outer_width_LR'], $ay, $this->w - $this->page_box['outer_width_LR'], $by);
  11369. $this->Line($this->w - $this->page_box['outer_width_LR'], $cy, $this->w - $this->page_box['outer_width_LR'], $dy);
  11370. if ($this->printers_info) {
  11371. $hd = date('Y-m-d H:i') . ' Page ' . $this->page . ' of {nb}';
  11372. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  11373. $this->SetFont('arial', '', 7.5, true, true);
  11374. $this->x = $this->page_box['outer_width_LR'] + 1.5;
  11375. $this->y = 1;
  11376. $this->Cell(0, $this->FontSize, $hd, 0, 0, 'L', 0, '', 0, 0, 0, 'M');
  11377. $this->SetFont($this->default_font, '', $this->original_default_font_size);
  11378. }
  11379. }
  11380. if ($this->show_marks == 'CROSS' || $this->show_marks == 'CROPCROSS') {
  11381. $this->SetLineWidth(0.1); // = 0.1 mm
  11382. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  11383. $l = 14 / 2; // longer length of the cross line (half)
  11384. $w = 6 / 2; // shorter width of the cross line (half)
  11385. $r = 1.2; // radius of circle
  11386. $m = $this->crossMarkMargin; // Distance of cross mark from margin
  11387. $x1 = $this->page_box['outer_width_LR'] - $m;
  11388. $x2 = $this->w - $this->page_box['outer_width_LR'] + $m;
  11389. $y1 = $this->page_box['outer_width_TB'] - $m;
  11390. $y2 = $this->h - $this->page_box['outer_width_TB'] + $m;
  11391. // Left
  11392. $this->Circle($x1, $this->h / 2, $r, 'S');
  11393. $this->Line($x1 - $w, $this->h / 2, $x1 + $w, $this->h / 2);
  11394. $this->Line($x1, $this->h / 2 - $l, $x1, $this->h / 2 + $l);
  11395. // Right
  11396. $this->Circle($x2, $this->h / 2, $r, 'S');
  11397. $this->Line($x2 - $w, $this->h / 2, $x2 + $w, $this->h / 2);
  11398. $this->Line($x2, $this->h / 2 - $l, $x2, $this->h / 2 + $l);
  11399. // Top
  11400. $this->Circle($this->w / 2, $y1, $r, 'S');
  11401. $this->Line($this->w / 2, $y1 - $w, $this->w / 2, $y1 + $w);
  11402. $this->Line($this->w / 2 - $l, $y1, $this->w / 2 + $l, $y1);
  11403. // Bottom
  11404. $this->Circle($this->w / 2, $y2, $r, 'S');
  11405. $this->Line($this->w / 2, $y2 - $w, $this->w / 2, $y2 + $w);
  11406. $this->Line($this->w / 2 - $l, $y2, $this->w / 2 + $l, $y2);
  11407. }
  11408. /* -- END CSS-PAGE -- */
  11409. // mPDF 6
  11410. // If @page set non-HTML headers/footers named, they were not read until later in the HTML code - so now set them
  11411. if ($this->page == 1) {
  11412. if ($this->firstPageBoxHeader) {
  11413. if (isset($this->pageHTMLheaders[$this->firstPageBoxHeader])) {
  11414. $this->HTMLHeader = $this->pageHTMLheaders[$this->firstPageBoxHeader];
  11415. }
  11416. $this->Header();
  11417. }
  11418. if ($this->firstPageBoxFooter) {
  11419. if (isset($this->pageHTMLfooters[$this->firstPageBoxFooter])) {
  11420. $this->HTMLFooter = $this->pageHTMLfooters[$this->firstPageBoxFooter];
  11421. }
  11422. }
  11423. $this->firstPageBoxHeader = '';
  11424. $this->firstPageBoxFooter = '';
  11425. }
  11426. if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLFooterE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLFooter) || (!$this->mirrorMargins && $this->HTMLFooter)) {
  11427. $this->writeHTMLFooters();
  11428. }
  11429. /* -- WATERMARK -- */
  11430. if (($this->watermarkText) && ($this->showWatermarkText)) {
  11431. $this->watermark($this->watermarkText, $this->watermarkAngle, is_int($this->watermark_size) ? $this->watermark_size : 120, $this->watermarkTextAlpha); // Watermark text
  11432. }
  11433. if (($this->watermarkImage) && ($this->showWatermarkImage)) {
  11434. $this->watermarkImg($this->watermarkImage, $this->watermarkImageAlpha); // Watermark image
  11435. }
  11436. /* -- END WATERMARK -- */
  11437. }
  11438. /* -- HTML-CSS -- */
  11439. /**
  11440. * Write HTML code to the document
  11441. *
  11442. * Also used internally to parse HTML into buffers
  11443. *
  11444. * @param string $html
  11445. * @param int $mode Use HTMLParserMode constants. Controls what parts of the $html code is parsed.
  11446. * @param bool $init Clears and sets buffers to Top level block etc.
  11447. * @param bool $close If false leaves buffers etc. in current state, so that it can continue a block etc.
  11448. */
  11449. function WriteHTML($html, $mode = HTMLParserMode::DEFAULT_MODE, $init = true, $close = true)
  11450. {
  11451. /* Check $html is an integer, float, string, boolean or class with __toString(), otherwise throw exception */
  11452. if (is_scalar($html) === false) {
  11453. if (!is_object($html) || ! method_exists($html, '__toString')) {
  11454. throw new \Mpdf\MpdfException('WriteHTML() requires $html be an integer, float, string, boolean or an object with the __toString() magic method.');
  11455. }
  11456. }
  11457. // Check the mode is valid
  11458. if (in_array($mode, HTMLParserMode::getAllModes(), true) === false) {
  11459. throw new \Mpdf\MpdfException('WriteHTML() requires $mode to be one of the modes defined in HTMLParserMode');
  11460. }
  11461. /* Cast $html as a string */
  11462. $html = (string) $html;
  11463. // @log Parsing CSS & Headers
  11464. if ($init) {
  11465. $this->headerbuffer = '';
  11466. $this->textbuffer = [];
  11467. $this->fixedPosBlockSave = [];
  11468. }
  11469. if ($mode === HTMLParserMode::HEADER_CSS) {
  11470. $html = '<style> ' . $html . ' </style>';
  11471. } // stylesheet only
  11472. if ($this->allow_charset_conversion) {
  11473. if ($mode === HTMLParserMode::DEFAULT_MODE) {
  11474. $this->ReadCharset($html);
  11475. }
  11476. if ($this->charset_in && $mode !== HTMLParserMode::HTML_HEADER_BUFFER) {
  11477. $success = iconv($this->charset_in, 'UTF-8//TRANSLIT', $html);
  11478. if ($success) {
  11479. $html = $success;
  11480. }
  11481. }
  11482. }
  11483. $html = $this->purify_utf8($html, false);
  11484. if ($init) {
  11485. $this->blklvl = 0;
  11486. $this->lastblocklevelchange = 0;
  11487. $this->blk = [];
  11488. $this->initialiseBlock($this->blk[0]);
  11489. $this->blk[0]['width'] = & $this->pgwidth;
  11490. $this->blk[0]['inner_width'] = & $this->pgwidth;
  11491. $this->blk[0]['blockContext'] = $this->blockContext;
  11492. }
  11493. $zproperties = [];
  11494. if ($mode === HTMLParserMode::DEFAULT_MODE || $mode === HTMLParserMode::HEADER_CSS) {
  11495. $this->ReadMetaTags($html);
  11496. if (preg_match('/<base[^>]*href=["\']([^"\'>]*)["\']/i', $html, $m)) {
  11497. $this->SetBasePath($m[1]);
  11498. }
  11499. $html = $this->cssManager->ReadCSS($html);
  11500. if ($this->autoLangToFont && !$this->usingCoreFont && preg_match('/<html [^>]*lang=[\'\"](.*?)[\'\"]/ism', $html, $m)) {
  11501. $html_lang = $m[1];
  11502. }
  11503. if (preg_match('/<html [^>]*dir=[\'\"]\s*rtl\s*[\'\"]/ism', $html)) {
  11504. $zproperties['DIRECTION'] = 'rtl';
  11505. }
  11506. // allow in-line CSS for body tag to be parsed // Get <body> tag inline CSS
  11507. if (preg_match('/<body([^>]*)>(.*?)<\/body>/ism', $html, $m) || preg_match('/<body([^>]*)>(.*)$/ism', $html, $m)) {
  11508. $html = $m[2];
  11509. // Changed to allow style="background: url('bg.jpg')"
  11510. if (preg_match('/style=[\"](.*?)[\"]/ism', $m[1], $mm) || preg_match('/style=[\'](.*?)[\']/ism', $m[1], $mm)) {
  11511. $zproperties = $this->cssManager->readInlineCSS($mm[1]);
  11512. }
  11513. if (preg_match('/dir=[\'\"]\s*rtl\s*[\'\"]/ism', $m[1])) {
  11514. $zproperties['DIRECTION'] = 'rtl';
  11515. }
  11516. if (isset($html_lang) && $html_lang) {
  11517. $zproperties['LANG'] = $html_lang;
  11518. }
  11519. if ($this->autoLangToFont && !$this->onlyCoreFonts && preg_match('/lang=[\'\"](.*?)[\'\"]/ism', $m[1], $mm)) {
  11520. $zproperties['LANG'] = $mm[1];
  11521. }
  11522. }
  11523. }
  11524. $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
  11525. if ($zproperties) {
  11526. $properties = $this->cssManager->array_merge_recursive_unique($properties, $zproperties);
  11527. }
  11528. if (isset($properties['DIRECTION']) && $properties['DIRECTION']) {
  11529. $this->cssManager->CSS['BODY']['DIRECTION'] = $properties['DIRECTION'];
  11530. }
  11531. if (!isset($this->cssManager->CSS['BODY']['DIRECTION'])) {
  11532. $this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality;
  11533. } else {
  11534. $this->SetDirectionality($this->cssManager->CSS['BODY']['DIRECTION']);
  11535. }
  11536. $this->setCSS($properties, '', 'BODY');
  11537. $this->blk[0]['InlineProperties'] = $this->saveInlineProperties();
  11538. if ($mode === HTMLParserMode::HEADER_CSS) {
  11539. return '';
  11540. }
  11541. if (!isset($this->cssManager->CSS['BODY'])) {
  11542. $this->cssManager->CSS['BODY'] = [];
  11543. }
  11544. /* -- BACKGROUNDS -- */
  11545. if (isset($properties['BACKGROUND-GRADIENT'])) {
  11546. $this->bodyBackgroundGradient = $properties['BACKGROUND-GRADIENT'];
  11547. }
  11548. if (isset($properties['BACKGROUND-IMAGE']) && $properties['BACKGROUND-IMAGE']) {
  11549. $ret = $this->SetBackground($properties, $this->pgwidth);
  11550. if ($ret) {
  11551. $this->bodyBackgroundImage = $ret;
  11552. }
  11553. }
  11554. /* -- END BACKGROUNDS -- */
  11555. /* -- CSS-PAGE -- */
  11556. // If page-box is set
  11557. 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
  11558. $this->page_box['current'] = '';
  11559. $this->page_box['using'] = true;
  11560. list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', false, 'O');
  11561. $this->DefOrientation = $this->CurOrientation = $pborientation;
  11562. $this->orig_lMargin = $this->DeflMargin = $pbmgl;
  11563. $this->orig_rMargin = $this->DefrMargin = $pbmgr;
  11564. $this->orig_tMargin = $this->tMargin = $pbmgt;
  11565. $this->orig_bMargin = $this->bMargin = $pbmgb;
  11566. $this->orig_hMargin = $this->margin_header = $pbmgh;
  11567. $this->orig_fMargin = $this->margin_footer = $pbmgf;
  11568. list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', true, 'O'); // first page
  11569. $this->show_marks = $marks;
  11570. if ($hname) {
  11571. $this->firstPageBoxHeader = $hname;
  11572. }
  11573. if ($fname) {
  11574. $this->firstPageBoxFooter = $fname;
  11575. }
  11576. }
  11577. /* -- END CSS-PAGE -- */
  11578. $parseonly = false;
  11579. $this->bufferoutput = false;
  11580. if ($mode == HTMLParserMode::HTML_PARSE_NO_WRITE) {
  11581. $parseonly = true;
  11582. // Close any open block tags
  11583. $arr = [];
  11584. $ai = 0;
  11585. for ($b = $this->blklvl; $b > 0; $b--) {
  11586. $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
  11587. }
  11588. // Output any text left in buffer
  11589. if (count($this->textbuffer)) {
  11590. $this->printbuffer($this->textbuffer);
  11591. }
  11592. $this->textbuffer = [];
  11593. } elseif ($mode === HTMLParserMode::HTML_HEADER_BUFFER) {
  11594. // Close any open block tags
  11595. $arr = [];
  11596. $ai = 0;
  11597. for ($b = $this->blklvl; $b > 0; $b--) {
  11598. $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
  11599. }
  11600. // Output any text left in buffer
  11601. if (count($this->textbuffer)) {
  11602. $this->printbuffer($this->textbuffer);
  11603. }
  11604. $this->bufferoutput = true;
  11605. $this->textbuffer = [];
  11606. $this->headerbuffer = '';
  11607. $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
  11608. $this->setCSS($properties, '', 'BODY');
  11609. }
  11610. mb_internal_encoding('UTF-8');
  11611. $html = $this->AdjustHTML($html, $this->tabSpaces); // Try to make HTML look more like XHTML
  11612. if ($this->autoScriptToLang) {
  11613. $html = $this->markScriptToLang($html);
  11614. }
  11615. preg_match_all('/<htmlpageheader([^>]*)>(.*?)<\/htmlpageheader>/si', $html, $h);
  11616. for ($i = 0; $i < count($h[1]); $i++) {
  11617. if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $h[1][$i], $n)) {
  11618. $this->pageHTMLheaders[$n[1]]['html'] = $h[2][$i];
  11619. $this->pageHTMLheaders[$n[1]]['h'] = $this->_getHtmlHeight($h[2][$i]);
  11620. }
  11621. }
  11622. preg_match_all('/<htmlpagefooter([^>]*)>(.*?)<\/htmlpagefooter>/si', $html, $f);
  11623. for ($i = 0; $i < count($f[1]); $i++) {
  11624. if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $f[1][$i], $n)) {
  11625. $this->pageHTMLfooters[$n[1]]['html'] = $f[2][$i];
  11626. $this->pageHTMLfooters[$n[1]]['h'] = $this->_getHtmlHeight($f[2][$i]);
  11627. }
  11628. }
  11629. $html = preg_replace('/<htmlpageheader.*?<\/htmlpageheader>/si', '', $html);
  11630. $html = preg_replace('/<htmlpagefooter.*?<\/htmlpagefooter>/si', '', $html);
  11631. if ($this->state == 0 && ($mode === HTMLParserMode::DEFAULT_MODE || $mode === HTMLParserMode::HTML_BODY)) {
  11632. $this->AddPage($this->CurOrientation);
  11633. }
  11634. if (isset($hname) && preg_match('/^html_(.*)$/i', $hname, $n)) {
  11635. $this->SetHTMLHeader($this->pageHTMLheaders[$n[1]], 'O', true);
  11636. }
  11637. if (isset($fname) && preg_match('/^html_(.*)$/i', $fname, $n)) {
  11638. $this->SetHTMLFooter($this->pageHTMLfooters[$n[1]], 'O');
  11639. }
  11640. $html = str_replace('<?', '< ', $html); // Fix '<?XML' bug from HTML code generated by MS Word
  11641. $this->checkSIP = false;
  11642. $this->checkSMP = false;
  11643. $this->checkCJK = false;
  11644. if ($this->onlyCoreFonts) {
  11645. $html = $this->SubstituteChars($html);
  11646. } else {
  11647. if (preg_match("/([" . $this->pregRTLchars . "])/u", $html)) {
  11648. $this->biDirectional = true;
  11649. } // *OTL*
  11650. if (preg_match("/([\x{20000}-\x{2FFFF}])/u", $html)) {
  11651. $this->checkSIP = true;
  11652. }
  11653. if (preg_match("/([\x{10000}-\x{1FFFF}])/u", $html)) {
  11654. $this->checkSMP = true;
  11655. }
  11656. /* -- CJK-FONTS -- */
  11657. if (preg_match("/([" . $this->pregCJKchars . "])/u", $html)) {
  11658. $this->checkCJK = true;
  11659. }
  11660. /* -- END CJK-FONTS -- */
  11661. }
  11662. // Don't allow non-breaking spaces that are converted to substituted chars or will break anyway and mess up table width calc.
  11663. $html = str_replace('<tta>160</tta>', chr(32), $html);
  11664. $html = str_replace('</tta><tta>', '|', $html);
  11665. $html = str_replace('</tts><tts>', '|', $html);
  11666. $html = str_replace('</ttz><ttz>', '|', $html);
  11667. // Add new supported tags in the DisableTags function
  11668. $html = strip_tags($html, $this->enabledtags); // remove all unsupported tags, but the ones inside the 'enabledtags' string
  11669. // Explode the string in order to parse the HTML code
  11670. $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
  11671. // ? more accurate regexp that allows e.g. <a name="Silly <name>">
  11672. // if changing - also change in fn.SubstituteChars()
  11673. // $a = preg_split ('/<((?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
  11674. if ($this->mb_enc) {
  11675. mb_internal_encoding($this->mb_enc);
  11676. }
  11677. $pbc = 0;
  11678. $this->subPos = -1;
  11679. $cnt = count($a);
  11680. for ($i = 0; $i < $cnt; $i++) {
  11681. $e = $a[$i];
  11682. if ($i % 2 == 0) {
  11683. // TEXT
  11684. if ($this->blk[$this->blklvl]['hide']) {
  11685. continue;
  11686. }
  11687. if ($this->inlineDisplayOff) {
  11688. continue;
  11689. }
  11690. if ($this->inMeter) {
  11691. continue;
  11692. }
  11693. if ($this->inFixedPosBlock) {
  11694. $this->fixedPosBlock .= $e;
  11695. continue;
  11696. } // *CSS-POSITION*
  11697. if (strlen($e) == 0) {
  11698. continue;
  11699. }
  11700. if ($this->ignorefollowingspaces && !$this->ispre) {
  11701. if (strlen(ltrim($e)) == 0) {
  11702. continue;
  11703. }
  11704. if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && substr($e, 0, 1) == ' ') {
  11705. $this->ignorefollowingspaces = false;
  11706. $e = ltrim($e);
  11707. }
  11708. }
  11709. $this->OTLdata = null; // mPDF 5.7.1
  11710. $e = UtfString::strcode2utf($e);
  11711. $e = $this->lesser_entity_decode($e);
  11712. if ($this->usingCoreFont) {
  11713. // If core font is selected in document which is not onlyCoreFonts - substitute with non-core font
  11714. if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->subPos < $i && !$this->specialcontent) {
  11715. $cnt += $this->SubstituteCharsNonCore($a, $i, $e);
  11716. }
  11717. // CONVERT ENCODING
  11718. $e = mb_convert_encoding($e, $this->mb_enc, 'UTF-8');
  11719. if ($this->textvar & TextVars::FT_UPPERCASE) {
  11720. $e = mb_strtoupper($e, $this->mb_enc);
  11721. } // mPDF 5.7.1
  11722. elseif ($this->textvar & TextVars::FT_LOWERCASE) {
  11723. $e = mb_strtolower($e, $this->mb_enc);
  11724. } // mPDF 5.7.1
  11725. elseif ($this->textvar & TextVars::FT_CAPITALIZE) {
  11726. $e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8");
  11727. } // mPDF 5.7.1
  11728. } else {
  11729. if ($this->checkSIP && $this->CurrentFont['sipext'] && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {
  11730. $cnt += $this->SubstituteCharsSIP($a, $i, $e);
  11731. }
  11732. if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->CurrentFont['type'] != 'Type0' && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {
  11733. $cnt += $this->SubstituteCharsMB($a, $i, $e);
  11734. }
  11735. if ($this->textvar & TextVars::FT_UPPERCASE) {
  11736. $e = mb_strtoupper($e, $this->mb_enc);
  11737. } elseif ($this->textvar & TextVars::FT_LOWERCASE) {
  11738. $e = mb_strtolower($e, $this->mb_enc);
  11739. } elseif ($this->textvar & TextVars::FT_CAPITALIZE) {
  11740. $e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8");
  11741. }
  11742. /* -- OTL -- */
  11743. // Use OTL OpenType Table Layout - GSUB & GPOS
  11744. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL'] && (!$this->specialcontent || !$this->useActiveForms)) {
  11745. if (!$this->otl) {
  11746. $this->otl = new Otl($this, $this->fontCache);
  11747. }
  11748. $e = $this->otl->applyOTL($e, $this->CurrentFont['useOTL']);
  11749. $this->OTLdata = $this->otl->OTLdata;
  11750. $this->otl->removeChar($e, $this->OTLdata, "\xef\xbb\xbf"); // Remove ZWNBSP (also Byte order mark FEFF)
  11751. } /* -- END OTL -- */
  11752. else {
  11753. // removes U+200E/U+200F LTR and RTL mark and U+200C/U+200D Zero-width Joiner and Non-joiner
  11754. $e = preg_replace("/[\xe2\x80\x8c\xe2\x80\x8d\xe2\x80\x8e\xe2\x80\x8f]/u", '', $e);
  11755. $e = preg_replace("/[\xef\xbb\xbf]/u", '', $e); // Remove ZWNBSP (also Byte order mark FEFF)
  11756. }
  11757. }
  11758. if (($this->tts) || ($this->ttz) || ($this->tta)) {
  11759. $es = explode('|', $e);
  11760. $e = '';
  11761. foreach ($es as $val) {
  11762. $e .= chr($val);
  11763. }
  11764. }
  11765. // FORM ELEMENTS
  11766. if ($this->specialcontent) {
  11767. /* -- FORMS -- */
  11768. // SELECT tag (form element)
  11769. if ($this->specialcontent == "type=select") {
  11770. $e = ltrim($e);
  11771. if (!empty($this->OTLdata)) {
  11772. $this->otl->trimOTLdata($this->OTLdata, true, false);
  11773. } // *OTL*
  11774. $stringwidth = $this->GetStringWidth($e);
  11775. if (!isset($this->selectoption['MAXWIDTH']) || $stringwidth > $this->selectoption['MAXWIDTH']) {
  11776. $this->selectoption['MAXWIDTH'] = $stringwidth;
  11777. }
  11778. if (!isset($this->selectoption['SELECTED']) || $this->selectoption['SELECTED'] == '') {
  11779. $this->selectoption['SELECTED'] = $e;
  11780. if (!empty($this->OTLdata)) {
  11781. $this->selectoption['SELECTED-OTLDATA'] = $this->OTLdata;
  11782. } // *OTL*
  11783. }
  11784. // Active Forms
  11785. if (isset($this->selectoption['ACTIVE']) && $this->selectoption['ACTIVE']) {
  11786. $this->selectoption['ITEMS'][] = ['exportValue' => $this->selectoption['currentVAL'], 'content' => $e, 'selected' => $this->selectoption['currentSEL']];
  11787. }
  11788. $this->OTLdata = [];
  11789. } // TEXTAREA
  11790. else {
  11791. $objattr = unserialize($this->specialcontent);
  11792. $objattr['text'] = $e;
  11793. $objattr['OTLdata'] = $this->OTLdata;
  11794. $this->OTLdata = [];
  11795. $te = Mpdf::OBJECT_IDENTIFIER . "type=textarea,objattr=" . serialize($objattr) . Mpdf::OBJECT_IDENTIFIER;
  11796. if ($this->tdbegin) {
  11797. $this->_saveCellTextBuffer($te, $this->HREF);
  11798. } else {
  11799. $this->_saveTextBuffer($te, $this->HREF);
  11800. }
  11801. }
  11802. /* -- END FORMS -- */
  11803. } // TABLE
  11804. elseif ($this->tableLevel) {
  11805. /* -- TABLES -- */
  11806. if ($this->tdbegin) {
  11807. if (($this->ignorefollowingspaces) && !$this->ispre) {
  11808. $e = ltrim($e);
  11809. if (!empty($this->OTLdata)) {
  11810. $this->otl->trimOTLdata($this->OTLdata, true, false);
  11811. } // *OTL*
  11812. }
  11813. if ($e || $e === '0') {
  11814. if ($this->blockjustfinished && $this->cell[$this->row][$this->col]['s'] > 0) {
  11815. $this->_saveCellTextBuffer("\n");
  11816. if (!isset($this->cell[$this->row][$this->col]['maxs'])) {
  11817. $this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];
  11818. } elseif ($this->cell[$this->row][$this->col]['maxs'] < $this->cell[$this->row][$this->col]['s']) {
  11819. $this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];
  11820. }
  11821. $this->cell[$this->row][$this->col]['s'] = 0; // reset
  11822. }
  11823. $this->blockjustfinished = false;
  11824. if (!isset($this->cell[$this->row][$this->col]['R']) || !$this->cell[$this->row][$this->col]['R']) {
  11825. if (isset($this->cell[$this->row][$this->col]['s'])) {
  11826. $this->cell[$this->row][$this->col]['s'] += $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);
  11827. } else {
  11828. $this->cell[$this->row][$this->col]['s'] = $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);
  11829. }
  11830. if (!empty($this->spanborddet)) {
  11831. $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);
  11832. }
  11833. }
  11834. $this->_saveCellTextBuffer($e, $this->HREF);
  11835. if (substr($this->cell[$this->row][$this->col]['a'], 0, 1) == 'D') {
  11836. $dp = $this->decimal_align[substr($this->cell[$this->row][$this->col]['a'], 0, 2)];
  11837. $s = preg_split('/' . preg_quote($dp, '/') . '/', $e, 2); // ? needs to be /u if not core
  11838. $s0 = $this->GetStringWidth($s[0], false);
  11839. if (isset($s[1]) && $s[1]) {
  11840. $s1 = $this->GetStringWidth(($s[1] . $dp), false);
  11841. } else {
  11842. $s1 = 0;
  11843. }
  11844. if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'])) {
  11845. if ($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'] === false) {
  11846. $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'] = [];
  11847. }
  11848. $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = $s0;
  11849. } else {
  11850. $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']);
  11851. }
  11852. if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'])) {
  11853. $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = $s1;
  11854. } else {
  11855. $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']);
  11856. }
  11857. }
  11858. $this->nestedtablejustfinished = false;
  11859. $this->linebreakjustfinished = false;
  11860. }
  11861. }
  11862. /* -- END TABLES -- */
  11863. } // ALL ELSE
  11864. else {
  11865. if ($this->ignorefollowingspaces && !$this->ispre) {
  11866. $e = ltrim($e);
  11867. if (!empty($this->OTLdata)) {
  11868. $this->otl->trimOTLdata($this->OTLdata, true, false);
  11869. } // *OTL*
  11870. }
  11871. if ($e || $e === '0') {
  11872. $this->_saveTextBuffer($e, $this->HREF);
  11873. }
  11874. }
  11875. if ($e || $e === '0') {
  11876. $this->ignorefollowingspaces = false; // mPDF 6
  11877. }
  11878. if (substr($e, -1, 1) == ' ' && !$this->ispre && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  11879. $this->ignorefollowingspaces = true;
  11880. }
  11881. } else { // TAG **
  11882. if (isset($e[0]) && $e[0] == '/') {
  11883. $endtag = trim(strtoupper(substr($e, 1)));
  11884. /* -- CSS-POSITION -- */
  11885. // mPDF 6
  11886. if ($this->inFixedPosBlock) {
  11887. if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {
  11888. $this->fixedPosBlockDepth--;
  11889. }
  11890. if ($this->fixedPosBlockDepth == 0) {
  11891. $this->fixedPosBlockSave[] = [$this->fixedPosBlock, $this->fixedPosBlockBBox, $this->page];
  11892. $this->fixedPosBlock = '';
  11893. $this->inFixedPosBlock = false;
  11894. continue;
  11895. }
  11896. $this->fixedPosBlock .= '<' . $e . '>';
  11897. continue;
  11898. }
  11899. /* -- END CSS-POSITION -- */
  11900. // mPDF 6
  11901. // Correct for tags where HTML5 specifies optional end tags (see also OpenTag() )
  11902. if ($this->allow_html_optional_endtags && !$parseonly) {
  11903. if (isset($this->blk[$this->blklvl]['tag'])) {
  11904. $closed = false;
  11905. // li end tag may be omitted if there is no more content in the parent element
  11906. if (!$closed && $this->blk[$this->blklvl]['tag'] == 'LI' && $endtag != 'LI' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
  11907. $this->tag->CloseTag('LI', $a, $i);
  11908. $closed = true;
  11909. }
  11910. // dd end tag may be omitted if there is no more content in the parent element
  11911. if (!$closed && $this->blk[$this->blklvl]['tag'] == 'DD' && $endtag != 'DD' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
  11912. $this->tag->CloseTag('DD', $a, $i);
  11913. $closed = true;
  11914. }
  11915. // 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 [??????]
  11916. if (!$closed && $this->blk[$this->blklvl]['tag'] == 'P' && $endtag != 'P' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
  11917. $this->tag->CloseTag('P', $a, $i);
  11918. $closed = true;
  11919. }
  11920. // option end tag may be omitted if there is no more content in the parent element
  11921. if (!$closed && $this->blk[$this->blklvl]['tag'] == 'OPTION' && $endtag != 'OPTION' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
  11922. $this->tag->CloseTag('OPTION', $a, $i);
  11923. $closed = true;
  11924. }
  11925. }
  11926. /* -- TABLES -- */
  11927. // Check for Table tags where HTML specifies optional end tags,
  11928. if ($endtag == 'TABLE') {
  11929. if ($this->lastoptionaltag == 'THEAD' || $this->lastoptionaltag == 'TBODY' || $this->lastoptionaltag == 'TFOOT') {
  11930. $this->tag->CloseTag($this->lastoptionaltag, $a, $i);
  11931. }
  11932. if ($this->lastoptionaltag == 'TR') {
  11933. $this->tag->CloseTag('TR', $a, $i);
  11934. }
  11935. if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
  11936. $this->tag->CloseTag($this->lastoptionaltag, $a, $i);
  11937. $this->tag->CloseTag('TR', $a, $i);
  11938. }
  11939. }
  11940. if ($endtag == 'THEAD' || $endtag == 'TBODY' || $endtag == 'TFOOT') {
  11941. if ($this->lastoptionaltag == 'TR') {
  11942. $this->tag->CloseTag('TR', $a, $i);
  11943. }
  11944. if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
  11945. $this->tag->CloseTag($this->lastoptionaltag, $a, $i);
  11946. $this->tag->CloseTag('TR', $a, $i);
  11947. }
  11948. }
  11949. if ($endtag == 'TR') {
  11950. if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
  11951. $this->tag->CloseTag($this->lastoptionaltag, $a, $i);
  11952. }
  11953. }
  11954. /* -- END TABLES -- */
  11955. }
  11956. // mPDF 6
  11957. if ($this->blk[$this->blklvl]['hide']) {
  11958. if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {
  11959. unset($this->blk[$this->blklvl]);
  11960. $this->blklvl--;
  11961. }
  11962. continue;
  11963. }
  11964. // mPDF 6
  11965. $this->tag->CloseTag($endtag, $a, $i); // mPDF 6
  11966. } else { // OPENING TAG
  11967. if ($this->blk[$this->blklvl]['hide']) {
  11968. if (strpos($e, ' ')) {
  11969. $te = strtoupper(substr($e, 0, strpos($e, ' ')));
  11970. } else {
  11971. $te = strtoupper($e);
  11972. }
  11973. // mPDF 6
  11974. if ($te == 'THEAD' || $te == 'TBODY' || $te == 'TFOOT' || $te == 'TR' || $te == 'TD' || $te == 'TH') {
  11975. $this->lastoptionaltag = $te;
  11976. }
  11977. if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {
  11978. $this->blklvl++;
  11979. $this->blk[$this->blklvl]['hide'] = true;
  11980. $this->blk[$this->blklvl]['tag'] = $te; // mPDF 6
  11981. }
  11982. continue;
  11983. }
  11984. /* -- CSS-POSITION -- */
  11985. if ($this->inFixedPosBlock) {
  11986. if (strpos($e, ' ')) {
  11987. $te = strtoupper(substr($e, 0, strpos($e, ' ')));
  11988. } else {
  11989. $te = strtoupper($e);
  11990. }
  11991. $this->fixedPosBlock .= '<' . $e . '>';
  11992. if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {
  11993. $this->fixedPosBlockDepth++;
  11994. }
  11995. continue;
  11996. }
  11997. /* -- END CSS-POSITION -- */
  11998. $regexp = '|=\'(.*?)\'|s'; // eliminate single quotes, if any
  11999. $e = preg_replace($regexp, "=\"\$1\"", $e);
  12000. // changes anykey=anyvalue to anykey="anyvalue" (only do this inside [some] tags)
  12001. 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)
  12002. $regexp = '| (\\w+?)=([^\\s>"]+)|si';
  12003. $e = preg_replace($regexp, " \$1=\"\$2\"", $e);
  12004. }
  12005. $e = preg_replace('/ (\\S+?)\s*=\s*"/i', " \\1=\"", $e);
  12006. // Fix path values, if needed
  12007. $orig_srcpath = '';
  12008. if ((stristr($e, "href=") !== false) or ( stristr($e, "src=") !== false)) {
  12009. $regexp = '/ (href|src)\s*=\s*"(.*?)"/i';
  12010. preg_match($regexp, $e, $auxiliararray);
  12011. if (isset($auxiliararray[2])) {
  12012. $path = $auxiliararray[2];
  12013. } else {
  12014. $path = '';
  12015. }
  12016. if (trim($path) != '' && !(stristr($e, "src=") !== false && substr($path, 0, 4) == 'var:') && substr($path, 0, 1) != '@') {
  12017. $path = htmlspecialchars_decode($path); // mPDF 5.7.4 URLs
  12018. $orig_srcpath = $path;
  12019. $this->GetFullPath($path);
  12020. $regexp = '/ (href|src)="(.*?)"/i';
  12021. $e = preg_replace($regexp, ' \\1="' . $path . '"', $e);
  12022. }
  12023. }//END of Fix path values
  12024. // Extract attributes
  12025. $contents = [];
  12026. $contents1 = [];
  12027. $contents2 = [];
  12028. // Changed to allow style="background: url('bg.jpg')"
  12029. // Changed to improve performance; maximum length of \S (attribute) = 16
  12030. // Increase allowed attribute name to 32 - cutting off "toc-even-header-name" etc.
  12031. preg_match_all('/\\S{1,32}=["][^"]*["]/', $e, $contents1);
  12032. preg_match_all('/\\S{1,32}=[\'][^\']*[\']/i', $e, $contents2);
  12033. $contents = array_merge($contents1, $contents2);
  12034. preg_match('/\\S+/', $e, $a2);
  12035. $tag = (isset($a2[0]) ? strtoupper($a2[0]) : '');
  12036. $attr = [];
  12037. if ($orig_srcpath) {
  12038. $attr['ORIG_SRC'] = $orig_srcpath;
  12039. }
  12040. if (!empty($contents)) {
  12041. foreach ($contents[0] as $v) {
  12042. // Changed to allow style="background: url('bg.jpg')"
  12043. if (preg_match('/^([^=]*)=["]?([^"]*)["]?$/', $v, $a3) || preg_match('/^([^=]*)=[\']?([^\']*)[\']?$/', $v, $a3)) {
  12044. if (strtoupper($a3[1]) == 'ID' || strtoupper($a3[1]) == 'CLASS') { // 4.2.013 Omits STYLE
  12045. $attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));
  12046. } // includes header-style-right etc. used for <pageheader>
  12047. elseif (preg_match('/^(HEADER|FOOTER)-STYLE/i', $a3[1])) {
  12048. $attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));
  12049. } else {
  12050. $attr[strtoupper($a3[1])] = trim($a3[2]);
  12051. }
  12052. }
  12053. }
  12054. }
  12055. $this->tag->OpenTag($tag, $attr, $a, $i); // mPDF 6
  12056. /* -- CSS-POSITION -- */
  12057. if ($this->inFixedPosBlock) {
  12058. $this->fixedPosBlockBBox = [$tag, $attr, $this->x, $this->y];
  12059. $this->fixedPosBlock = '';
  12060. $this->fixedPosBlockDepth = 1;
  12061. }
  12062. /* -- END CSS-POSITION -- */
  12063. if (preg_match('/\/$/', $e)) {
  12064. $this->tag->CloseTag($tag, $a, $i);
  12065. }
  12066. }
  12067. } // end TAG
  12068. } // end of foreach($a as $i=>$e)
  12069. if ($close) {
  12070. // Close any open block tags
  12071. for ($b = $this->blklvl; $b > 0; $b--) {
  12072. $this->tag->CloseTag($this->blk[$b]['tag'], $a, $i);
  12073. }
  12074. // Output any text left in buffer
  12075. if (count($this->textbuffer) && !$parseonly) {
  12076. $this->printbuffer($this->textbuffer);
  12077. }
  12078. if (!$parseonly) {
  12079. $this->textbuffer = [];
  12080. }
  12081. /* -- CSS-FLOAT -- */
  12082. // If ended with a float, need to move to end page
  12083. $currpos = $this->page * 1000 + $this->y;
  12084. if (isset($this->blk[$this->blklvl]['float_endpos']) && $this->blk[$this->blklvl]['float_endpos'] > $currpos) {
  12085. $old_page = $this->page;
  12086. $new_page = intval($this->blk[$this->blklvl]['float_endpos'] / 1000);
  12087. if ($old_page != $new_page) {
  12088. $s = $this->PrintPageBackgrounds();
  12089. // Writes after the marker so not overwritten later by page background etc.
  12090. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  12091. $this->pageBackgrounds = [];
  12092. $this->page = $new_page;
  12093. $this->ResetMargins();
  12094. $this->Reset();
  12095. $this->pageoutput[$this->page] = [];
  12096. }
  12097. $this->y = (round($this->blk[$this->blklvl]['float_endpos'] * 1000) % 1000000) / 1000; // mod changes operands to integers before processing
  12098. }
  12099. /* -- END CSS-FLOAT -- */
  12100. /* -- CSS-IMAGE-FLOAT -- */
  12101. $this->printfloatbuffer();
  12102. /* -- END CSS-IMAGE-FLOAT -- */
  12103. // Create Internal Links, if needed
  12104. if (!empty($this->internallink)) {
  12105. foreach ($this->internallink as $k => $v) {
  12106. if (strpos($k, "#") !== false) {
  12107. continue;
  12108. }
  12109. if (!is_array($v)) {
  12110. continue;
  12111. }
  12112. $ypos = $v['Y'];
  12113. $pagenum = $v['PAGE'];
  12114. $sharp = "#";
  12115. while (array_key_exists($sharp . $k, $this->internallink)) {
  12116. $internallink = $this->internallink[$sharp . $k];
  12117. $this->SetLink($internallink, $ypos, $pagenum);
  12118. $sharp .= "#";
  12119. }
  12120. }
  12121. }
  12122. $this->bufferoutput = false;
  12123. /* -- CSS-POSITION -- */
  12124. if (count($this->fixedPosBlockSave)) {
  12125. foreach ($this->fixedPosBlockSave as $fpbs) {
  12126. $old_page = $this->page;
  12127. $this->page = $fpbs[2];
  12128. $this->WriteFixedPosHTML($fpbs[0], 0, 0, 100, 100, 'auto', $fpbs[1]); // 0,0,10,10 are overwritten by bbox
  12129. $this->page = $old_page;
  12130. }
  12131. $this->fixedPosBlockSave = [];
  12132. }
  12133. /* -- END CSS-POSITION -- */
  12134. }
  12135. }
  12136. /* -- CSS-POSITION -- */
  12137. function WriteFixedPosHTML($html, $x, $y, $w, $h, $overflow = 'visible', $bounding = [])
  12138. {
  12139. // $overflow can be 'hidden', 'visible' or 'auto' - 'auto' causes autofit to size
  12140. // Annotations disabled - enabled in mPDF 5.0
  12141. // Links do work
  12142. // Will always go on current page (or start Page 1 if required)
  12143. // Probably INCOMPATIBLE WITH keep with table, columns etc.
  12144. // Called externally or interally via <div style="position: [fixed|absolute]">
  12145. // When used internally, $x $y $w $h and $overflow are all overridden by $bounding
  12146. $overflow = strtolower($overflow);
  12147. if ($this->state == 0) {
  12148. $this->AddPage($this->CurOrientation);
  12149. }
  12150. $save_y = $this->y;
  12151. $save_x = $this->x;
  12152. $this->fullImageHeight = $this->h;
  12153. $save_cols = false;
  12154. /* -- COLUMNS -- */
  12155. if ($this->ColActive) {
  12156. $save_cols = true;
  12157. $save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off
  12158. $this->SetColumns(0);
  12159. }
  12160. /* -- END COLUMNS -- */
  12161. $save_annots = $this->title2annots; // *ANNOTATIONS*
  12162. $this->writingHTMLheader = true; // a FIX to stop pagebreaks etc.
  12163. $this->writingHTMLfooter = true;
  12164. $this->InFooter = true; // suppresses autopagebreaks
  12165. $save_bgs = $this->pageBackgrounds;
  12166. $checkinnerhtml = preg_replace('/\s/', '', $html);
  12167. $rotate = 0;
  12168. if ($w > $this->w) {
  12169. $x = 0;
  12170. $w = $this->w;
  12171. }
  12172. if ($h > $this->h) {
  12173. $y = 0;
  12174. $h = $this->h;
  12175. }
  12176. if ($x > $this->w) {
  12177. $x = $this->w - $w;
  12178. }
  12179. if ($y > $this->h) {
  12180. $y = $this->h - $h;
  12181. }
  12182. if (!empty($bounding)) {
  12183. // $cont_ containing block = full physical page (position: absolute) or page inside margins (position: fixed)
  12184. // $bbox_ Bounding box is the <div> which is positioned absolutely/fixed
  12185. // top/left/right/bottom/width/height/background*/border*/padding*/margin* are taken from bounding
  12186. // font*[family/size/style/weight]/line-height/text*[align/decoration/transform/indent]/color are transferred to $inner
  12187. // as an enclosing <div> (after having checked ID/CLASS)
  12188. // $x, $y, $w, $h are inside of $bbox_ = containing box for $inner_
  12189. // $inner_ InnerHTML is the contents of that block to be output
  12190. $tag = $bounding[0];
  12191. $attr = $bounding[1];
  12192. $orig_x0 = $bounding[2];
  12193. $orig_y0 = $bounding[3];
  12194. // As in WriteHTML() initialising
  12195. $this->blklvl = 0;
  12196. $this->lastblocklevelchange = 0;
  12197. $this->blk = [];
  12198. $this->initialiseBlock($this->blk[0]);
  12199. $this->blk[0]['width'] = & $this->pgwidth;
  12200. $this->blk[0]['inner_width'] = & $this->pgwidth;
  12201. $this->blk[0]['blockContext'] = $this->blockContext;
  12202. $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
  12203. $this->setCSS($properties, '', 'BODY');
  12204. $this->blklvl = 1;
  12205. $this->initialiseBlock($this->blk[1]);
  12206. $this->blk[1]['tag'] = $tag;
  12207. $this->blk[1]['attr'] = $attr;
  12208. $this->Reset();
  12209. $p = $this->cssManager->MergeCSS('BLOCK', $tag, $attr);
  12210. if (isset($p['ROTATE']) && ($p['ROTATE'] == 90 || $p['ROTATE'] == -90 || $p['ROTATE'] == 180)) {
  12211. $rotate = $p['ROTATE'];
  12212. } // mPDF 6
  12213. if (isset($p['OVERFLOW'])) {
  12214. $overflow = strtolower($p['OVERFLOW']);
  12215. }
  12216. if (strtolower($p['POSITION']) == 'fixed') {
  12217. $cont_w = $this->pgwidth; // $this->blk[0]['inner_width'];
  12218. $cont_h = $this->h - $this->tMargin - $this->bMargin;
  12219. $cont_x = $this->lMargin;
  12220. $cont_y = $this->tMargin;
  12221. } else {
  12222. $cont_w = $this->w; // ABSOLUTE;
  12223. $cont_h = $this->h;
  12224. $cont_x = 0;
  12225. $cont_y = 0;
  12226. }
  12227. // Pass on in-line properties to the innerhtml
  12228. $css = '';
  12229. if (isset($p['TEXT-ALIGN'])) {
  12230. $css .= 'text-align: ' . strtolower($p['TEXT-ALIGN']) . '; ';
  12231. }
  12232. if (isset($p['TEXT-TRANSFORM'])) {
  12233. $css .= 'text-transform: ' . strtolower($p['TEXT-TRANSFORM']) . '; ';
  12234. }
  12235. if (isset($p['TEXT-INDENT'])) {
  12236. $css .= 'text-indent: ' . strtolower($p['TEXT-INDENT']) . '; ';
  12237. }
  12238. if (isset($p['TEXT-DECORATION'])) {
  12239. $css .= 'text-decoration: ' . strtolower($p['TEXT-DECORATION']) . '; ';
  12240. }
  12241. if (isset($p['FONT-FAMILY'])) {
  12242. $css .= 'font-family: ' . strtolower($p['FONT-FAMILY']) . '; ';
  12243. }
  12244. if (isset($p['FONT-STYLE'])) {
  12245. $css .= 'font-style: ' . strtolower($p['FONT-STYLE']) . '; ';
  12246. }
  12247. if (isset($p['FONT-WEIGHT'])) {
  12248. $css .= 'font-weight: ' . strtolower($p['FONT-WEIGHT']) . '; ';
  12249. }
  12250. if (isset($p['FONT-SIZE'])) {
  12251. $css .= 'font-size: ' . strtolower($p['FONT-SIZE']) . '; ';
  12252. }
  12253. if (isset($p['LINE-HEIGHT'])) {
  12254. $css .= 'line-height: ' . strtolower($p['LINE-HEIGHT']) . '; ';
  12255. }
  12256. if (isset($p['TEXT-SHADOW'])) {
  12257. $css .= 'text-shadow: ' . strtolower($p['TEXT-SHADOW']) . '; ';
  12258. }
  12259. if (isset($p['LETTER-SPACING'])) {
  12260. $css .= 'letter-spacing: ' . strtolower($p['LETTER-SPACING']) . '; ';
  12261. }
  12262. // mPDF 6
  12263. if (isset($p['FONT-VARIANT-POSITION'])) {
  12264. $css .= 'font-variant-position: ' . strtolower($p['FONT-VARIANT-POSITION']) . '; ';
  12265. }
  12266. if (isset($p['FONT-VARIANT-CAPS'])) {
  12267. $css .= 'font-variant-caps: ' . strtolower($p['FONT-VARIANT-CAPS']) . '; ';
  12268. }
  12269. if (isset($p['FONT-VARIANT-LIGATURES'])) {
  12270. $css .= 'font-variant-ligatures: ' . strtolower($p['FONT-VARIANT-LIGATURES']) . '; ';
  12271. }
  12272. if (isset($p['FONT-VARIANT-NUMERIC'])) {
  12273. $css .= 'font-variant-numeric: ' . strtolower($p['FONT-VARIANT-NUMERIC']) . '; ';
  12274. }
  12275. if (isset($p['FONT-VARIANT-ALTERNATES'])) {
  12276. $css .= 'font-variant-alternates: ' . strtolower($p['FONT-VARIANT-ALTERNATES']) . '; ';
  12277. }
  12278. if (isset($p['FONT-FEATURE-SETTINGS'])) {
  12279. $css .= 'font-feature-settings: ' . strtolower($p['FONT-FEATURE-SETTINGS']) . '; ';
  12280. }
  12281. if (isset($p['FONT-LANGUAGE-OVERRIDE'])) {
  12282. $css .= 'font-language-override: ' . strtolower($p['FONT-LANGUAGE-OVERRIDE']) . '; ';
  12283. }
  12284. if (isset($p['FONT-KERNING'])) {
  12285. $css .= 'font-kerning: ' . strtolower($p['FONT-KERNING']) . '; ';
  12286. }
  12287. if (isset($p['COLOR'])) {
  12288. $css .= 'color: ' . strtolower($p['COLOR']) . '; ';
  12289. }
  12290. if (isset($p['Z-INDEX'])) {
  12291. $css .= 'z-index: ' . $p['Z-INDEX'] . '; ';
  12292. }
  12293. if ($css) {
  12294. $html = '<div style="' . $css . '">' . $html . '</div>';
  12295. }
  12296. // Copy over (only) the properties to set for border and background
  12297. $pb = [];
  12298. $pb['MARGIN-TOP'] = (isset($p['MARGIN-TOP']) ? $p['MARGIN-TOP'] : '');
  12299. $pb['MARGIN-RIGHT'] = (isset($p['MARGIN-RIGHT']) ? $p['MARGIN-RIGHT'] : '');
  12300. $pb['MARGIN-BOTTOM'] = (isset($p['MARGIN-BOTTOM']) ? $p['MARGIN-BOTTOM'] : '');
  12301. $pb['MARGIN-LEFT'] = (isset($p['MARGIN-LEFT']) ? $p['MARGIN-LEFT'] : '');
  12302. $pb['PADDING-TOP'] = (isset($p['PADDING-TOP']) ? $p['PADDING-TOP'] : '');
  12303. $pb['PADDING-RIGHT'] = (isset($p['PADDING-RIGHT']) ? $p['PADDING-RIGHT'] : '');
  12304. $pb['PADDING-BOTTOM'] = (isset($p['PADDING-BOTTOM']) ? $p['PADDING-BOTTOM'] : '');
  12305. $pb['PADDING-LEFT'] = (isset($p['PADDING-LEFT']) ? $p['PADDING-LEFT'] : '');
  12306. $pb['BORDER-TOP'] = (isset($p['BORDER-TOP']) ? $p['BORDER-TOP'] : '');
  12307. $pb['BORDER-RIGHT'] = (isset($p['BORDER-RIGHT']) ? $p['BORDER-RIGHT'] : '');
  12308. $pb['BORDER-BOTTOM'] = (isset($p['BORDER-BOTTOM']) ? $p['BORDER-BOTTOM'] : '');
  12309. $pb['BORDER-LEFT'] = (isset($p['BORDER-LEFT']) ? $p['BORDER-LEFT'] : '');
  12310. if (isset($p['BORDER-TOP-LEFT-RADIUS-H'])) {
  12311. $pb['BORDER-TOP-LEFT-RADIUS-H'] = $p['BORDER-TOP-LEFT-RADIUS-H'];
  12312. }
  12313. if (isset($p['BORDER-TOP-LEFT-RADIUS-V'])) {
  12314. $pb['BORDER-TOP-LEFT-RADIUS-V'] = $p['BORDER-TOP-LEFT-RADIUS-V'];
  12315. }
  12316. if (isset($p['BORDER-TOP-RIGHT-RADIUS-H'])) {
  12317. $pb['BORDER-TOP-RIGHT-RADIUS-H'] = $p['BORDER-TOP-RIGHT-RADIUS-H'];
  12318. }
  12319. if (isset($p['BORDER-TOP-RIGHT-RADIUS-V'])) {
  12320. $pb['BORDER-TOP-RIGHT-RADIUS-V'] = $p['BORDER-TOP-RIGHT-RADIUS-V'];
  12321. }
  12322. if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-H'])) {
  12323. $pb['BORDER-BOTTOM-LEFT-RADIUS-H'] = $p['BORDER-BOTTOM-LEFT-RADIUS-H'];
  12324. }
  12325. if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-V'])) {
  12326. $pb['BORDER-BOTTOM-LEFT-RADIUS-V'] = $p['BORDER-BOTTOM-LEFT-RADIUS-V'];
  12327. }
  12328. if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-H'])) {
  12329. $pb['BORDER-BOTTOM-RIGHT-RADIUS-H'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-H'];
  12330. }
  12331. if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-V'])) {
  12332. $pb['BORDER-BOTTOM-RIGHT-RADIUS-V'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-V'];
  12333. }
  12334. if (isset($p['BACKGROUND-COLOR'])) {
  12335. $pb['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];
  12336. }
  12337. if (isset($p['BOX-SHADOW'])) {
  12338. $pb['BOX-SHADOW'] = $p['BOX-SHADOW'];
  12339. }
  12340. /* -- BACKGROUNDS -- */
  12341. if (isset($p['BACKGROUND-IMAGE'])) {
  12342. $pb['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];
  12343. }
  12344. if (isset($p['BACKGROUND-IMAGE-RESIZE'])) {
  12345. $pb['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];
  12346. }
  12347. if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {
  12348. $pb['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];
  12349. }
  12350. if (isset($p['BACKGROUND-REPEAT'])) {
  12351. $pb['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];
  12352. }
  12353. if (isset($p['BACKGROUND-POSITION'])) {
  12354. $pb['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];
  12355. }
  12356. if (isset($p['BACKGROUND-GRADIENT'])) {
  12357. $pb['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];
  12358. }
  12359. if (isset($p['BACKGROUND-SIZE'])) {
  12360. $pb['BACKGROUND-SIZE'] = $p['BACKGROUND-SIZE'];
  12361. }
  12362. if (isset($p['BACKGROUND-ORIGIN'])) {
  12363. $pb['BACKGROUND-ORIGIN'] = $p['BACKGROUND-ORIGIN'];
  12364. }
  12365. if (isset($p['BACKGROUND-CLIP'])) {
  12366. $pb['BACKGROUND-CLIP'] = $p['BACKGROUND-CLIP'];
  12367. }
  12368. /* -- END BACKGROUNDS -- */
  12369. $this->setCSS($pb, 'BLOCK', $tag);
  12370. // ================================================================
  12371. $bbox_br = $this->blk[1]['border_right']['w'];
  12372. $bbox_bl = $this->blk[1]['border_left']['w'];
  12373. $bbox_bt = $this->blk[1]['border_top']['w'];
  12374. $bbox_bb = $this->blk[1]['border_bottom']['w'];
  12375. $bbox_pr = $this->blk[1]['padding_right'];
  12376. $bbox_pl = $this->blk[1]['padding_left'];
  12377. $bbox_pt = $this->blk[1]['padding_top'];
  12378. $bbox_pb = $this->blk[1]['padding_bottom'];
  12379. $bbox_mr = $this->blk[1]['margin_right'];
  12380. if (isset($p['MARGIN-RIGHT']) && strtolower($p['MARGIN-RIGHT']) == 'auto') {
  12381. $bbox_mr = 'auto';
  12382. }
  12383. $bbox_ml = $this->blk[1]['margin_left'];
  12384. if (isset($p['MARGIN-LEFT']) && strtolower($p['MARGIN-LEFT']) == 'auto') {
  12385. $bbox_ml = 'auto';
  12386. }
  12387. $bbox_mt = $this->blk[1]['margin_top'];
  12388. if (isset($p['MARGIN-TOP']) && strtolower($p['MARGIN-TOP']) == 'auto') {
  12389. $bbox_mt = 'auto';
  12390. }
  12391. $bbox_mb = $this->blk[1]['margin_bottom'];
  12392. if (isset($p['MARGIN-BOTTOM']) && strtolower($p['MARGIN-BOTTOM']) == 'auto') {
  12393. $bbox_mb = 'auto';
  12394. }
  12395. if (isset($p['LEFT']) && strtolower($p['LEFT']) != 'auto') {
  12396. $bbox_left = $this->sizeConverter->convert($p['LEFT'], $cont_w, $this->FontSize, false);
  12397. } else {
  12398. $bbox_left = 'auto';
  12399. }
  12400. if (isset($p['TOP']) && strtolower($p['TOP']) != 'auto') {
  12401. $bbox_top = $this->sizeConverter->convert($p['TOP'], $cont_h, $this->FontSize, false);
  12402. } else {
  12403. $bbox_top = 'auto';
  12404. }
  12405. if (isset($p['RIGHT']) && strtolower($p['RIGHT']) != 'auto') {
  12406. $bbox_right = $this->sizeConverter->convert($p['RIGHT'], $cont_w, $this->FontSize, false);
  12407. } else {
  12408. $bbox_right = 'auto';
  12409. }
  12410. if (isset($p['BOTTOM']) && strtolower($p['BOTTOM']) != 'auto') {
  12411. $bbox_bottom = $this->sizeConverter->convert($p['BOTTOM'], $cont_h, $this->FontSize, false);
  12412. } else {
  12413. $bbox_bottom = 'auto';
  12414. }
  12415. if (isset($p['WIDTH']) && strtolower($p['WIDTH']) != 'auto') {
  12416. $inner_w = $this->sizeConverter->convert($p['WIDTH'], $cont_w, $this->FontSize, false);
  12417. } else {
  12418. $inner_w = 'auto';
  12419. }
  12420. if (isset($p['HEIGHT']) && strtolower($p['HEIGHT']) != 'auto') {
  12421. $inner_h = $this->sizeConverter->convert($p['HEIGHT'], $cont_h, $this->FontSize, false);
  12422. } else {
  12423. $inner_h = 'auto';
  12424. }
  12425. // If bottom or right pos are set and not left / top - save this to adjust rotated block later
  12426. if ($rotate == 90 || $rotate == -90) { // mPDF 6
  12427. if ($bbox_left === 'auto' && $bbox_right !== 'auto') {
  12428. $rot_rpos = $bbox_right;
  12429. } else {
  12430. $rot_rpos = false;
  12431. }
  12432. if ($bbox_top === 'auto' && $bbox_bottom !== 'auto') {
  12433. $rot_bpos = $bbox_bottom;
  12434. } else {
  12435. $rot_bpos = false;
  12436. }
  12437. }
  12438. // ================================================================
  12439. if ($checkinnerhtml == '' && $inner_h === 'auto') {
  12440. $inner_h = 0.0001;
  12441. }
  12442. if ($checkinnerhtml == '' && $inner_w === 'auto') {
  12443. $inner_w = 2 * $this->GetCharWidth('W', false);
  12444. }
  12445. // ================================================================
  12446. // Algorithm from CSS2.1 See http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
  12447. // mPD 5.3.14
  12448. // Special case (not CSS) if all not specified, centre vertically on page
  12449. $bbox_top_orig = '';
  12450. if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_mt === 'auto' && $bbox_mb === 'auto') {
  12451. $bbox_top_orig = $bbox_top;
  12452. if ($bbox_mt === 'auto') {
  12453. $bbox_mt = 0;
  12454. }
  12455. if ($bbox_mb === 'auto') {
  12456. $bbox_mb = 0;
  12457. }
  12458. $bbox_top = $orig_y0 - $bbox_mt - $cont_y;
  12459. // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
  12460. } // mPD 5.3.14
  12461. elseif ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto') {
  12462. $bbox_top_orig = $bbox_top = $orig_y0 - $cont_y;
  12463. if ($bbox_mt === 'auto') {
  12464. $bbox_mt = 0;
  12465. }
  12466. if ($bbox_mb === 'auto') {
  12467. $bbox_mb = 0;
  12468. }
  12469. // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
  12470. } elseif ($bbox_top !== 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {
  12471. if ($bbox_mt === 'auto' && $bbox_mb === 'auto') {
  12472. $x = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;
  12473. $bbox_mt = $bbox_mb = ($x / 2);
  12474. } elseif ($bbox_mt === 'auto') {
  12475. $bbox_mt = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  12476. } elseif ($bbox_mb === 'auto') {
  12477. $bbox_mb = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;
  12478. } else {
  12479. $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
  12480. }
  12481. } else {
  12482. if ($bbox_mt === 'auto') {
  12483. $bbox_mt = 0;
  12484. }
  12485. if ($bbox_mb === 'auto') {
  12486. $bbox_mb = 0;
  12487. }
  12488. if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom !== 'auto') {
  12489. // solve for $bbox_top when content_h known - $inner_h=='auto' && $bbox_top =='auto'
  12490. } elseif ($bbox_top === 'auto' && $bbox_bottom === 'auto' && $inner_h !== 'auto') {
  12491. $bbox_top = $orig_y0 - $bbox_mt - $cont_y;
  12492. $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
  12493. } elseif ($inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_top !== 'auto') {
  12494. // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
  12495. } elseif ($bbox_top === 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {
  12496. $bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;
  12497. } elseif ($inner_h === 'auto' && $bbox_top !== 'auto' && $bbox_bottom !== 'auto') {
  12498. $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;
  12499. } elseif ($bbox_bottom === 'auto' && $bbox_top !== 'auto' && $inner_h !== 'auto') {
  12500. $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
  12501. }
  12502. }
  12503. // THEN DO SAME FOR WIDTH
  12504. // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
  12505. if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right === 'auto') {
  12506. if ($bbox_ml === 'auto') {
  12507. $bbox_ml = 0;
  12508. }
  12509. if ($bbox_mr === 'auto') {
  12510. $bbox_mr = 0;
  12511. }
  12512. // IF containing element RTL, should set $bbox_right
  12513. $bbox_left = $orig_x0 - $bbox_ml - $cont_x;
  12514. // solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'
  12515. } elseif ($bbox_left !== 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {
  12516. if ($bbox_ml === 'auto' && $bbox_mr === 'auto') {
  12517. $x = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;
  12518. $bbox_ml = $bbox_mr = ($x / 2);
  12519. } elseif ($bbox_ml === 'auto') {
  12520. $bbox_ml = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;
  12521. } elseif ($bbox_mr === 'auto') {
  12522. $bbox_mr = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;
  12523. } else {
  12524. $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
  12525. }
  12526. } else {
  12527. if ($bbox_ml === 'auto') {
  12528. $bbox_ml = 0;
  12529. }
  12530. if ($bbox_mr === 'auto') {
  12531. $bbox_mr = 0;
  12532. }
  12533. if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right !== 'auto') {
  12534. // solve for $bbox_left when content_w known - $inner_w=='auto' && $bbox_left =='auto'
  12535. } elseif ($bbox_left === 'auto' && $bbox_right === 'auto' && $inner_w !== 'auto') {
  12536. // IF containing element RTL, should set $bbox_right
  12537. $bbox_left = $orig_x0 - $bbox_ml - $cont_x;
  12538. $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
  12539. } elseif ($inner_w === 'auto' && $bbox_right === 'auto' && $bbox_left !== 'auto') {
  12540. // solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'
  12541. } elseif ($bbox_left === 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {
  12542. $bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
  12543. } elseif ($inner_w === 'auto' && $bbox_left !== 'auto' && $bbox_right !== 'auto') {
  12544. $inner_w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
  12545. } elseif ($bbox_right === 'auto' && $bbox_left !== 'auto' && $inner_w !== 'auto') {
  12546. $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
  12547. }
  12548. }
  12549. // ================================================================
  12550. // ================================================================
  12551. /* -- BACKGROUNDS -- */
  12552. if (isset($pb['BACKGROUND-IMAGE']) && $pb['BACKGROUND-IMAGE']) {
  12553. $ret = $this->SetBackground($pb, $this->blk[1]['inner_width']);
  12554. if ($ret) {
  12555. $this->blk[1]['background-image'] = $ret;
  12556. }
  12557. }
  12558. /* -- END BACKGROUNDS -- */
  12559. $bbox_top_auto = $bbox_top === 'auto';
  12560. $bbox_left_auto = $bbox_left === 'auto';
  12561. $bbox_right_auto = $bbox_right === 'auto';
  12562. $bbox_bottom_auto = $bbox_bottom === 'auto';
  12563. $bbox_top = is_numeric($bbox_top) ? $bbox_top : 0;
  12564. $bbox_left = is_numeric($bbox_left) ? $bbox_left : 0;
  12565. $bbox_right = is_numeric($bbox_right) ? $bbox_right : 0;
  12566. $bbox_bottom = is_numeric($bbox_bottom) ? $bbox_bottom : 0;
  12567. $y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;
  12568. $h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  12569. $x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;
  12570. $w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;
  12571. // Set (temporary) values for x y w h to do first paint, if values are auto
  12572. if ($inner_h === 'auto' && $bbox_top_auto) {
  12573. $y = $cont_y + $bbox_mt + $bbox_bt + $bbox_pt;
  12574. $h = $cont_h - ($bbox_bottom + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);
  12575. } elseif ($inner_h === 'auto' && $bbox_bottom_auto) {
  12576. $y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;
  12577. $h = $cont_h - ($bbox_top + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);
  12578. }
  12579. if ($inner_w === 'auto' && $bbox_left_auto) {
  12580. $x = $cont_x + $bbox_ml + $bbox_bl + $bbox_pl;
  12581. $w = $cont_w - ($bbox_right + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);
  12582. } elseif ($inner_w === 'auto' && $bbox_right_auto) {
  12583. $x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;
  12584. $w = $cont_w - ($bbox_left + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);
  12585. }
  12586. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  12587. $bbox_x = $cont_x + $bbox_left + $bbox_ml;
  12588. $saved_block1 = $this->blk[1];
  12589. unset($p);
  12590. unset($pb);
  12591. // ================================================================
  12592. if ($inner_w === 'auto') { // do a first write
  12593. $this->lMargin = $x;
  12594. $this->rMargin = $this->w - $w - $x;
  12595. // SET POSITION & FONT VALUES
  12596. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  12597. $this->pageoutput[$this->page] = [];
  12598. $this->x = $x;
  12599. $this->y = $y;
  12600. $this->HTMLheaderPageLinks = [];
  12601. $this->HTMLheaderPageAnnots = [];
  12602. $this->HTMLheaderPageForms = [];
  12603. $this->pageBackgrounds = [];
  12604. $this->maxPosR = 0;
  12605. $this->maxPosL = $this->w; // For RTL
  12606. $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
  12607. $inner_w = $this->maxPosR - $this->lMargin;
  12608. if ($bbox_right_auto) {
  12609. $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
  12610. } elseif ($bbox_left_auto) {
  12611. $bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
  12612. $bbox_x = $cont_x + $bbox_left + $bbox_ml;
  12613. $inner_x = $bbox_x + $bbox_bl + $bbox_pl;
  12614. $x = $inner_x;
  12615. }
  12616. $w = $inner_w;
  12617. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  12618. $bbox_x = $cont_x + $bbox_left + $bbox_ml;
  12619. }
  12620. if ($inner_h === 'auto') { // do a first write
  12621. $this->lMargin = $x;
  12622. $this->rMargin = $this->w - $w - $x;
  12623. // SET POSITION & FONT VALUES
  12624. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  12625. $this->pageoutput[$this->page] = [];
  12626. $this->x = $x;
  12627. $this->y = $y;
  12628. $this->HTMLheaderPageLinks = [];
  12629. $this->HTMLheaderPageAnnots = [];
  12630. $this->HTMLheaderPageForms = [];
  12631. $this->pageBackgrounds = [];
  12632. $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
  12633. $inner_h = $this->y - $y;
  12634. if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
  12635. if (($this->y + $bbox_pb + $bbox_bb) > ($cont_y + $cont_h)) {
  12636. $adj = ($this->y + $bbox_pb + $bbox_bb) - ($cont_y + $cont_h);
  12637. $inner_h -= $adj;
  12638. }
  12639. }
  12640. if ($bbox_bottom_auto && $bbox_top_orig === 'auto') {
  12641. $bbox_bottom = $bbox_top = ($cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb) / 2;
  12642. if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
  12643. if ($bbox_top < 0) {
  12644. $bbox_top = 0;
  12645. $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  12646. }
  12647. }
  12648. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  12649. $inner_y = $bbox_y + $bbox_bt + $bbox_pt;
  12650. $y = $inner_y;
  12651. } elseif ($bbox_bottom_auto) {
  12652. $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb;
  12653. } elseif ($bbox_top_auto) {
  12654. $bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  12655. if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
  12656. if ($bbox_top < 0) {
  12657. $bbox_top = 0;
  12658. $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  12659. }
  12660. }
  12661. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  12662. $inner_y = $bbox_y + $bbox_bt + $bbox_pt;
  12663. $y = $inner_y;
  12664. }
  12665. $h = $inner_h;
  12666. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  12667. $bbox_x = $cont_x + $bbox_left + $bbox_ml;
  12668. }
  12669. $inner_w = $w;
  12670. $inner_h = $h;
  12671. }
  12672. $this->lMargin = $x;
  12673. $this->rMargin = $this->w - $w - $x;
  12674. // SET POSITION & FONT VALUES
  12675. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  12676. $this->pageoutput[$this->page] = [];
  12677. $this->x = $x;
  12678. $this->y = $y;
  12679. $this->HTMLheaderPageLinks = [];
  12680. $this->HTMLheaderPageAnnots = [];
  12681. $this->HTMLheaderPageForms = [];
  12682. $this->pageBackgrounds = [];
  12683. $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
  12684. $actual_h = $this->y - $y;
  12685. $use_w = $w;
  12686. $use_h = $h;
  12687. $ratio = $actual_h / $use_w;
  12688. if ($overflow != 'hidden' && $overflow != 'visible') {
  12689. $target = $h / $w;
  12690. if ($target > 0) {
  12691. if (($ratio / $target) > 1) {
  12692. $nl = ceil($actual_h / $this->lineheight);
  12693. $l = $use_w * $nl;
  12694. $est_w = sqrt(($l * $this->lineheight) / $target) * 0.8;
  12695. $use_w += ($est_w - $use_w) - ($w / 100);
  12696. }
  12697. $bpcstart = ($ratio / $target);
  12698. $bpcctr = 1;
  12699. while (($ratio / $target) > 1) {
  12700. // @log 'Auto-sizing fixed-position block $bpcctr++
  12701. $this->x = $x;
  12702. $this->y = $y;
  12703. if (($ratio / $target) > 1.5 || ($ratio / $target) < 0.6) {
  12704. $use_w += ($w / $this->incrementFPR1);
  12705. } elseif (($ratio / $target) > 1.2 || ($ratio / $target) < 0.85) {
  12706. $use_w += ($w / $this->incrementFPR2);
  12707. } elseif (($ratio / $target) > 1.1 || ($ratio / $target) < 0.91) {
  12708. $use_w += ($w / $this->incrementFPR3);
  12709. } else {
  12710. $use_w += ($w / $this->incrementFPR4);
  12711. }
  12712. $use_h = $use_w * $target;
  12713. $this->rMargin = $this->w - $use_w - $x;
  12714. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  12715. $this->HTMLheaderPageLinks = [];
  12716. $this->HTMLheaderPageAnnots = [];
  12717. $this->HTMLheaderPageForms = [];
  12718. $this->pageBackgrounds = [];
  12719. $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
  12720. $actual_h = $this->y - $y;
  12721. $ratio = $actual_h / $use_w;
  12722. }
  12723. }
  12724. }
  12725. $shrink_f = $w / $use_w;
  12726. // ================================================================
  12727. $this->pages[$this->page] .= '___BEFORE_BORDERS___';
  12728. $block_s = $this->PrintPageBackgrounds(); // Save to print later inside clipping path
  12729. $this->pageBackgrounds = [];
  12730. // ================================================================
  12731. if ($rotate == 90 || $rotate == -90) { // mPDF 6
  12732. $prerotw = $bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br;
  12733. $preroth = $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;
  12734. $rot_start = " q\n";
  12735. if ($rotate == 90) {
  12736. if ($rot_rpos !== false) {
  12737. $adjw = $prerotw;
  12738. } // width before rotation
  12739. else {
  12740. $adjw = $preroth;
  12741. } // height before rotation
  12742. if ($rot_bpos !== false) {
  12743. $adjh = -$prerotw + $preroth;
  12744. } else {
  12745. $adjh = 0;
  12746. }
  12747. } else {
  12748. if ($rot_rpos !== false) {
  12749. $adjw = $prerotw - $preroth;
  12750. } else {
  12751. $adjw = 0;
  12752. }
  12753. if ($rot_bpos !== false) {
  12754. $adjh = $preroth;
  12755. } // height before rotation
  12756. else {
  12757. $adjh = $prerotw;
  12758. } // width before rotation
  12759. }
  12760. $rot_start .= $this->transformTranslate($adjw, $adjh, true) . "\n";
  12761. $rot_start .= $this->transformRotate($rotate, $bbox_x, $bbox_y, true) . "\n";
  12762. $rot_end = " Q\n";
  12763. } elseif ($rotate == 180) { // mPDF 6
  12764. $rot_start = " q\n";
  12765. $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";
  12766. $rot_start .= $this->transformRotate(180, $bbox_x, $bbox_y, true) . "\n";
  12767. $rot_end = " Q\n";
  12768. } else {
  12769. $rot_start = '';
  12770. $rot_end = '';
  12771. }
  12772. // ================================================================
  12773. if (!empty($bounding)) {
  12774. // WHEN HEIGHT // BOTTOM EDGE IS KNOWN and $this->y is set to the bottom
  12775. // Re-instate saved $this->blk[1]
  12776. $this->blk[1] = $saved_block1;
  12777. // These are only needed when painting border/background
  12778. $this->blk[1]['width'] = $bbox_w = $cont_w - $bbox_left - $bbox_ml - $bbox_mr - $bbox_right;
  12779. $this->blk[1]['x0'] = $bbox_x;
  12780. $this->blk[1]['y0'] = $bbox_y;
  12781. $this->blk[1]['startpage'] = $this->page;
  12782. $this->blk[1]['y1'] = $bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;
  12783. $this->writer->write($rot_start);
  12784. $this->PaintDivBB('', 0, 1); // Prints borders and sets backgrounds in $this->pageBackgrounds
  12785. $this->writer->write($rot_end);
  12786. }
  12787. $s = $this->PrintPageBackgrounds();
  12788. $s = $rot_start . $s . $rot_end;
  12789. $this->pages[$this->page] = preg_replace('/___BEFORE_BORDERS___/', "\n" . $s . "\n", $this->pages[$this->page]);
  12790. $this->pageBackgrounds = [];
  12791. $this->writer->write($rot_start);
  12792. // Clipping Output
  12793. if ($overflow == 'hidden') {
  12794. // Bounding rectangle to clip
  12795. $clip_y1 = $this->y;
  12796. if (!empty($bounding) && ($this->y + $bbox_pb + $bbox_bb) > ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb )) {
  12797. $clip_y1 = ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb ) - ($bbox_pb + $bbox_bb);
  12798. }
  12799. // $op = 'W* n'; // Clipping
  12800. $op = 'W n'; // Clipping alternative mode
  12801. $this->writer->write("q");
  12802. $ch = $clip_y1 - $y;
  12803. $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));
  12804. if (!empty($block_s)) {
  12805. $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);
  12806. $tmp .= "\n" . $block_s . "\nQ";
  12807. $block_s = $tmp;
  12808. }
  12809. }
  12810. if (!empty($block_s)) {
  12811. if ($shrink_f != 1) { // i.e. autofit has resized the box
  12812. $tmp = "q\n" . $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y, true);
  12813. $tmp .= "\n" . $block_s . "\nQ";
  12814. $block_s = $tmp;
  12815. }
  12816. $this->writer->write($block_s);
  12817. }
  12818. if ($shrink_f != 1) { // i.e. autofit has resized the box
  12819. $this->StartTransform();
  12820. $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y);
  12821. }
  12822. $this->writer->write($this->headerbuffer);
  12823. if ($shrink_f != 1) { // i.e. autofit has resized the box
  12824. $this->StopTransform();
  12825. }
  12826. if ($overflow == 'hidden') {
  12827. // End clipping
  12828. $this->writer->write("Q");
  12829. }
  12830. $this->writer->write($rot_end);
  12831. // Page Links
  12832. foreach ($this->HTMLheaderPageLinks as $lk) {
  12833. if ($rotate) {
  12834. $tmp = $lk[2]; // Switch h - w
  12835. $lk[2] = $lk[3];
  12836. $lk[3] = $tmp;
  12837. $lx1 = (($lk[0] / Mpdf::SCALE));
  12838. $ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)));
  12839. if ($rotate == 90) {
  12840. $adjx = -($lx1 - $bbox_x) + ($preroth - ($ly1 - $bbox_y));
  12841. $adjy = -($ly1 - $bbox_y) + ($lx1 - $bbox_x);
  12842. $lk[2] = -$lk[2];
  12843. } elseif ($rotate == -90) {
  12844. $adjx = -($lx1 - $bbox_x) + ($ly1 - $bbox_y);
  12845. $adjy = -($ly1 - $bbox_y) - ($lx1 - $bbox_x) + $prerotw;
  12846. $lk[3] = -$lk[3];
  12847. }
  12848. if ($rot_rpos !== false) {
  12849. $adjx += $prerotw - $preroth;
  12850. }
  12851. if ($rot_bpos !== false) {
  12852. $adjy += $preroth - $prerotw;
  12853. }
  12854. $lx1 += $adjx;
  12855. $ly1 += $adjy;
  12856. $lk[0] = $lx1 * Mpdf::SCALE;
  12857. $lk[1] = ($this->h - $ly1) * Mpdf::SCALE;
  12858. }
  12859. if ($shrink_f != 1) { // i.e. autofit has resized the box
  12860. $lx1 = (($lk[0] / Mpdf::SCALE) - $x);
  12861. $lx2 = $x + ($lx1 * $shrink_f);
  12862. $lk[0] = $lx2 * Mpdf::SCALE;
  12863. $ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)) - $y);
  12864. $ly2 = $y + ($ly1 * $shrink_f);
  12865. $lk[1] = ($this->h - $ly2) * Mpdf::SCALE;
  12866. $lk[2] *= $shrink_f; // width
  12867. $lk[3] *= $shrink_f; // height
  12868. }
  12869. $this->PageLinks[$this->page][] = $lk;
  12870. }
  12871. foreach ($this->HTMLheaderPageForms as $n => $f) {
  12872. if ($shrink_f != 1) { // i.e. autofit has resized the box
  12873. $f['x'] = $x + (($f['x'] - $x) * $shrink_f);
  12874. $f['y'] = $y + (($f['y'] - $y) * $shrink_f);
  12875. $f['w'] *= $shrink_f;
  12876. $f['h'] *= $shrink_f;
  12877. $f['style']['fontsize'] *= $shrink_f;
  12878. }
  12879. $this->form->forms[$f['n']] = $f;
  12880. }
  12881. // Page Annotations
  12882. foreach ($this->HTMLheaderPageAnnots as $lk) {
  12883. if ($rotate) {
  12884. if ($rotate == 90) {
  12885. $adjx = -($lk['x'] - $bbox_x) + ($preroth - ($lk['y'] - $bbox_y));
  12886. $adjy = -($lk['y'] - $bbox_y) + ($lk['x'] - $bbox_x);
  12887. } elseif ($rotate == -90) {
  12888. $adjx = -($lk['x'] - $bbox_x) + ($lk['y'] - $bbox_y);
  12889. $adjy = -($lk['y'] - $bbox_y) - ($lk['x'] - $bbox_x) + $prerotw;
  12890. }
  12891. if ($rot_rpos !== false) {
  12892. $adjx += $prerotw - $preroth;
  12893. }
  12894. if ($rot_bpos !== false) {
  12895. $adjy += $preroth - $prerotw;
  12896. }
  12897. $lk['x'] += $adjx;
  12898. $lk['y'] += $adjy;
  12899. }
  12900. if ($shrink_f != 1) { // i.e. autofit has resized the box
  12901. $lk['x'] = $x + (($lk['x'] - $x) * $shrink_f);
  12902. $lk['y'] = $y + (($lk['y'] - $y) * $shrink_f);
  12903. }
  12904. $this->PageAnnots[$this->page][] = $lk;
  12905. }
  12906. // Restore
  12907. $this->headerbuffer = '';
  12908. $this->HTMLheaderPageLinks = [];
  12909. $this->HTMLheaderPageAnnots = [];
  12910. $this->HTMLheaderPageForms = [];
  12911. $this->pageBackgrounds = $save_bgs;
  12912. $this->writingHTMLheader = false;
  12913. $this->writingHTMLfooter = false;
  12914. $this->fullImageHeight = false;
  12915. $this->ResetMargins();
  12916. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  12917. $this->SetXY($save_x, $save_y);
  12918. $this->title2annots = $save_annots; // *ANNOTATIONS*
  12919. $this->InFooter = false; // turns back on autopagebreaks
  12920. $this->pageoutput[$this->page] = [];
  12921. $this->pageoutput[$this->page]['Font'] = '';
  12922. /* -- COLUMNS -- */
  12923. if ($save_cols) {
  12924. $this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);
  12925. }
  12926. /* -- END COLUMNS -- */
  12927. }
  12928. /* -- END CSS-POSITION -- */
  12929. function initialiseBlock(&$blk)
  12930. {
  12931. $blk['margin_top'] = 0;
  12932. $blk['margin_left'] = 0;
  12933. $blk['margin_bottom'] = 0;
  12934. $blk['margin_right'] = 0;
  12935. $blk['padding_top'] = 0;
  12936. $blk['padding_left'] = 0;
  12937. $blk['padding_bottom'] = 0;
  12938. $blk['padding_right'] = 0;
  12939. $blk['border_top']['w'] = 0;
  12940. $blk['border_left']['w'] = 0;
  12941. $blk['border_bottom']['w'] = 0;
  12942. $blk['border_right']['w'] = 0;
  12943. $blk['direction'] = 'ltr';
  12944. $blk['hide'] = false;
  12945. $blk['outer_left_margin'] = 0;
  12946. $blk['outer_right_margin'] = 0;
  12947. $blk['cascadeCSS'] = [];
  12948. $blk['block-align'] = false;
  12949. $blk['bgcolor'] = false;
  12950. $blk['page_break_after_avoid'] = false;
  12951. $blk['keep_block_together'] = false;
  12952. $blk['float'] = false;
  12953. $blk['line_height'] = '';
  12954. $blk['margin_collapse'] = false;
  12955. }
  12956. function border_details($bd)
  12957. {
  12958. $prop = preg_split('/\s+/', trim($bd));
  12959. if (isset($this->blk[$this->blklvl]['inner_width'])) {
  12960. $refw = $this->blk[$this->blklvl]['inner_width'];
  12961. } elseif (isset($this->blk[$this->blklvl - 1]['inner_width'])) {
  12962. $refw = $this->blk[$this->blklvl - 1]['inner_width'];
  12963. } else {
  12964. $refw = $this->w;
  12965. }
  12966. if (count($prop) == 1) {
  12967. $bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false);
  12968. if ($bsize > 0) {
  12969. return ['s' => 1, 'w' => $bsize, 'c' => $this->colorConverter->convert(0, $this->PDFAXwarnings), 'style' => 'solid'];
  12970. } else {
  12971. return ['w' => 0, 's' => 0];
  12972. }
  12973. } elseif (count($prop) == 2) {
  12974. // 1px solid
  12975. if (in_array($prop[1], $this->borderstyles) || $prop[1] == 'none' || $prop[1] == 'hidden') {
  12976. $prop[2] = '';
  12977. } // solid #000000
  12978. elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {
  12979. $prop[0] = '';
  12980. $prop[1] = $prop[0];
  12981. $prop[2] = $prop[1];
  12982. } // 1px #000000
  12983. else {
  12984. $prop[1] = '';
  12985. $prop[2] = $prop[1];
  12986. }
  12987. } elseif (count($prop) == 3) {
  12988. // Change #000000 1px solid to 1px solid #000000 (proper)
  12989. if (substr($prop[0], 0, 1) == '#') {
  12990. $tmp = $prop[0];
  12991. $prop[0] = $prop[1];
  12992. $prop[1] = $prop[2];
  12993. $prop[2] = $tmp;
  12994. } // Change solid #000000 1px to 1px solid #000000 (proper)
  12995. elseif (substr($prop[0], 1, 1) == '#') {
  12996. $tmp = $prop[1];
  12997. $prop[0] = $prop[2];
  12998. $prop[1] = $prop[0];
  12999. $prop[2] = $tmp;
  13000. } // Change solid 1px #000000 to 1px solid #000000 (proper)
  13001. elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {
  13002. $tmp = $prop[0];
  13003. $prop[0] = $prop[1];
  13004. $prop[1] = $tmp;
  13005. }
  13006. } else {
  13007. return ['w' => 0, 's' => 0];
  13008. }
  13009. // Size
  13010. $bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false);
  13011. // color
  13012. $coul = $this->colorConverter->convert($prop[2], $this->PDFAXwarnings); // returns array
  13013. // Style
  13014. $prop[1] = strtolower($prop[1]);
  13015. if (in_array($prop[1], $this->borderstyles) && $bsize > 0) {
  13016. $on = 1;
  13017. } elseif ($prop[1] == 'hidden') {
  13018. $on = 1;
  13019. $bsize = 0;
  13020. $coul = '';
  13021. } elseif ($prop[1] == 'none') {
  13022. $on = 0;
  13023. $bsize = 0;
  13024. $coul = '';
  13025. } else {
  13026. $on = 0;
  13027. $bsize = 0;
  13028. $coul = '';
  13029. $prop[1] = '';
  13030. }
  13031. return ['s' => $on, 'w' => $bsize, 'c' => $coul, 'style' => $prop[1], 'dom' => 0];
  13032. }
  13033. /* -- END HTML-CSS -- */
  13034. /* -- BORDER-RADIUS -- */
  13035. function _borderPadding($a, $b, &$px, &$py)
  13036. {
  13037. // $px and py are padding long axis (x) and short axis (y)
  13038. $added = 0; // extra padding
  13039. $x = $a - $px;
  13040. $y = $b - $py;
  13041. // Check if Falls within ellipse of border radius
  13042. if (( (($x + $added) * ($x + $added)) / ($a * $a) + (($y + $added) * ($y + $added)) / ($b * $b) ) <= 1) {
  13043. return false;
  13044. }
  13045. $t = atan2($y, $x);
  13046. $newx = $b / sqrt((($b * $b) / ($a * $a)) + ( tan($t) * tan($t) ));
  13047. $newy = $a / sqrt((($a * $a) / ($b * $b)) + ( (1 / tan($t)) * (1 / tan($t)) ));
  13048. $px = max($px, $a - $newx + $added);
  13049. $py = max($py, $b - $newy + $added);
  13050. }
  13051. /* -- END BORDER-RADIUS -- */
  13052. /* -- HTML-CSS -- */
  13053. /* -- CSS-PAGE -- */
  13054. function SetPagedMediaCSS($name, $first, $oddEven)
  13055. {
  13056. if ($oddEven == 'E') {
  13057. if ($this->directionality == 'rtl') {
  13058. $side = 'R';
  13059. } else {
  13060. $side = 'L';
  13061. }
  13062. } else {
  13063. if ($this->directionality == 'rtl') {
  13064. $side = 'L';
  13065. } else {
  13066. $side = 'R';
  13067. }
  13068. }
  13069. $name = strtoupper($name);
  13070. $p = [];
  13071. $p['SIZE'] = 'AUTO';
  13072. // Uses mPDF original margins as default
  13073. $p['MARGIN-RIGHT'] = strval($this->orig_rMargin) . 'mm';
  13074. $p['MARGIN-LEFT'] = strval($this->orig_lMargin) . 'mm';
  13075. $p['MARGIN-TOP'] = strval($this->orig_tMargin) . 'mm';
  13076. $p['MARGIN-BOTTOM'] = strval($this->orig_bMargin) . 'mm';
  13077. $p['MARGIN-HEADER'] = strval($this->orig_hMargin) . 'mm';
  13078. $p['MARGIN-FOOTER'] = strval($this->orig_fMargin) . 'mm';
  13079. // Basic page + selector
  13080. if (isset($this->cssManager->CSS['@PAGE'])) {
  13081. $zp = $this->cssManager->CSS['@PAGE'];
  13082. } else {
  13083. $zp = [];
  13084. }
  13085. if (is_array($zp) && !empty($zp)) {
  13086. $p = array_merge($p, $zp);
  13087. }
  13088. if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {
  13089. $p['HEADER'] = $p['EVEN-HEADER-NAME'];
  13090. unset($p['EVEN-HEADER-NAME']);
  13091. }
  13092. if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {
  13093. $p['HEADER'] = $p['ODD-HEADER-NAME'];
  13094. unset($p['ODD-HEADER-NAME']);
  13095. }
  13096. if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {
  13097. $p['FOOTER'] = $p['EVEN-FOOTER-NAME'];
  13098. unset($p['EVEN-FOOTER-NAME']);
  13099. }
  13100. if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {
  13101. $p['FOOTER'] = $p['ODD-FOOTER-NAME'];
  13102. unset($p['ODD-FOOTER-NAME']);
  13103. }
  13104. // If right/Odd page
  13105. if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT']) && $side == 'R') {
  13106. $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT'];
  13107. } else {
  13108. $zp = [];
  13109. }
  13110. if (isset($zp['SIZE'])) {
  13111. unset($zp['SIZE']);
  13112. }
  13113. if (isset($zp['SHEET-SIZE'])) {
  13114. unset($zp['SHEET-SIZE']);
  13115. }
  13116. // Disallow margin-left or -right on :LEFT or :RIGHT
  13117. if (isset($zp['MARGIN-LEFT'])) {
  13118. unset($zp['MARGIN-LEFT']);
  13119. }
  13120. if (isset($zp['MARGIN-RIGHT'])) {
  13121. unset($zp['MARGIN-RIGHT']);
  13122. }
  13123. if (is_array($zp) && !empty($zp)) {
  13124. $p = array_merge($p, $zp);
  13125. }
  13126. // If left/Even page
  13127. if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT']) && $side == 'L') {
  13128. $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT'];
  13129. } else {
  13130. $zp = [];
  13131. }
  13132. if (isset($zp['SIZE'])) {
  13133. unset($zp['SIZE']);
  13134. }
  13135. if (isset($zp['SHEET-SIZE'])) {
  13136. unset($zp['SHEET-SIZE']);
  13137. }
  13138. // Disallow margin-left or -right on :LEFT or :RIGHT
  13139. if (isset($zp['MARGIN-LEFT'])) {
  13140. unset($zp['MARGIN-LEFT']);
  13141. }
  13142. if (isset($zp['MARGIN-RIGHT'])) {
  13143. unset($zp['MARGIN-RIGHT']);
  13144. }
  13145. if (is_array($zp) && !empty($zp)) {
  13146. $p = array_merge($p, $zp);
  13147. }
  13148. // If first page
  13149. if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']) && $first) {
  13150. $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST'];
  13151. } else {
  13152. $zp = [];
  13153. }
  13154. if (isset($zp['SIZE'])) {
  13155. unset($zp['SIZE']);
  13156. }
  13157. if (isset($zp['SHEET-SIZE'])) {
  13158. unset($zp['SHEET-SIZE']);
  13159. }
  13160. // Disallow margin-left or -right on :FIRST // mPDF 5.7.3
  13161. if (isset($zp['MARGIN-LEFT'])) {
  13162. unset($zp['MARGIN-LEFT']);
  13163. }
  13164. if (isset($zp['MARGIN-RIGHT'])) {
  13165. unset($zp['MARGIN-RIGHT']);
  13166. }
  13167. if (is_array($zp) && !empty($zp)) {
  13168. $p = array_merge($p, $zp);
  13169. }
  13170. // If named page
  13171. if ($name) {
  13172. if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name])) {
  13173. $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name];
  13174. } else {
  13175. $zp = [];
  13176. }
  13177. if (is_array($zp) && !empty($zp)) {
  13178. $p = array_merge($p, $zp);
  13179. }
  13180. if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {
  13181. $p['HEADER'] = $p['EVEN-HEADER-NAME'];
  13182. unset($p['EVEN-HEADER-NAME']);
  13183. }
  13184. if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {
  13185. $p['HEADER'] = $p['ODD-HEADER-NAME'];
  13186. unset($p['ODD-HEADER-NAME']);
  13187. }
  13188. if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {
  13189. $p['FOOTER'] = $p['EVEN-FOOTER-NAME'];
  13190. unset($p['EVEN-FOOTER-NAME']);
  13191. }
  13192. if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {
  13193. $p['FOOTER'] = $p['ODD-FOOTER-NAME'];
  13194. unset($p['ODD-FOOTER-NAME']);
  13195. }
  13196. // If named right/Odd page
  13197. if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT']) && $side == 'R') {
  13198. $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT'];
  13199. } else {
  13200. $zp = [];
  13201. }
  13202. if (isset($zp['SIZE'])) {
  13203. unset($zp['SIZE']);
  13204. }
  13205. if (isset($zp['SHEET-SIZE'])) {
  13206. unset($zp['SHEET-SIZE']);
  13207. }
  13208. // Disallow margin-left or -right on :LEFT or :RIGHT
  13209. if (isset($zp['MARGIN-LEFT'])) {
  13210. unset($zp['MARGIN-LEFT']);
  13211. }
  13212. if (isset($zp['MARGIN-RIGHT'])) {
  13213. unset($zp['MARGIN-RIGHT']);
  13214. }
  13215. if (is_array($zp) && !empty($zp)) {
  13216. $p = array_merge($p, $zp);
  13217. }
  13218. // If named left/Even page
  13219. if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT']) && $side == 'L') {
  13220. $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT'];
  13221. } else {
  13222. $zp = [];
  13223. }
  13224. if (isset($zp['SIZE'])) {
  13225. unset($zp['SIZE']);
  13226. }
  13227. if (isset($zp['SHEET-SIZE'])) {
  13228. unset($zp['SHEET-SIZE']);
  13229. }
  13230. // Disallow margin-left or -right on :LEFT or :RIGHT
  13231. if (isset($zp['MARGIN-LEFT'])) {
  13232. unset($zp['MARGIN-LEFT']);
  13233. }
  13234. if (isset($zp['MARGIN-RIGHT'])) {
  13235. unset($zp['MARGIN-RIGHT']);
  13236. }
  13237. if (is_array($zp) && !empty($zp)) {
  13238. $p = array_merge($p, $zp);
  13239. }
  13240. // If named first page
  13241. if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST']) && $first) {
  13242. $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST'];
  13243. } else {
  13244. $zp = [];
  13245. }
  13246. if (isset($zp['SIZE'])) {
  13247. unset($zp['SIZE']);
  13248. }
  13249. if (isset($zp['SHEET-SIZE'])) {
  13250. unset($zp['SHEET-SIZE']);
  13251. }
  13252. // Disallow margin-left or -right on :FIRST // mPDF 5.7.3
  13253. if (isset($zp['MARGIN-LEFT'])) {
  13254. unset($zp['MARGIN-LEFT']);
  13255. }
  13256. if (isset($zp['MARGIN-RIGHT'])) {
  13257. unset($zp['MARGIN-RIGHT']);
  13258. }
  13259. if (is_array($zp) && !empty($zp)) {
  13260. $p = array_merge($p, $zp);
  13261. }
  13262. }
  13263. $orientation = $mgl = $mgr = $mgt = $mgb = $mgh = $mgf = '';
  13264. $header = $footer = '';
  13265. $resetpagenum = $pagenumstyle = $suppress = '';
  13266. $marks = '';
  13267. $bg = [];
  13268. $newformat = '';
  13269. if (isset($p['SHEET-SIZE']) && is_array($p['SHEET-SIZE'])) {
  13270. $newformat = $p['SHEET-SIZE'];
  13271. if ($newformat[0] > $newformat[1]) { // landscape
  13272. $newformat = array_reverse($newformat);
  13273. $p['ORIENTATION'] = 'L';
  13274. } else {
  13275. $p['ORIENTATION'] = 'P';
  13276. }
  13277. $this->_setPageSize($newformat, $p['ORIENTATION']);
  13278. }
  13279. if (isset($p['SIZE']) && is_array($p['SIZE']) && !$newformat) {
  13280. if ($p['SIZE']['W'] > $p['SIZE']['H']) {
  13281. $p['ORIENTATION'] = 'L';
  13282. } else {
  13283. $p['ORIENTATION'] = 'P';
  13284. }
  13285. }
  13286. if (is_array($p['SIZE'])) {
  13287. if ($p['SIZE']['W'] > $this->fw) {
  13288. $p['SIZE']['W'] = $this->fw;
  13289. } // mPD 4.2 use fw not fPt
  13290. if ($p['SIZE']['H'] > $this->fh) {
  13291. $p['SIZE']['H'] = $this->fh;
  13292. }
  13293. if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {
  13294. $outer_width_LR = ($this->fw - $p['SIZE']['W']) / 2;
  13295. $outer_width_TB = ($this->fh - $p['SIZE']['H']) / 2;
  13296. } else {
  13297. $outer_width_LR = ($this->fh - $p['SIZE']['W']) / 2;
  13298. $outer_width_TB = ($this->fw - $p['SIZE']['H']) / 2;
  13299. }
  13300. $pgw = $p['SIZE']['W'];
  13301. $pgh = $p['SIZE']['H'];
  13302. } else { // AUTO LANDSCAPE PORTRAIT
  13303. $outer_width_LR = 0;
  13304. $outer_width_TB = 0;
  13305. if (!$newformat) {
  13306. if (strtoupper($p['SIZE']) == 'AUTO') {
  13307. $p['ORIENTATION'] = $this->DefOrientation;
  13308. } elseif (strtoupper($p['SIZE']) == 'LANDSCAPE') {
  13309. $p['ORIENTATION'] = 'L';
  13310. } else {
  13311. $p['ORIENTATION'] = 'P';
  13312. }
  13313. }
  13314. if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {
  13315. $pgw = $this->fw;
  13316. $pgh = $this->fh;
  13317. } else {
  13318. $pgw = $this->fh;
  13319. $pgh = $this->fw;
  13320. }
  13321. }
  13322. if (isset($p['HEADER']) && $p['HEADER']) {
  13323. $header = $p['HEADER'];
  13324. }
  13325. if (isset($p['FOOTER']) && $p['FOOTER']) {
  13326. $footer = $p['FOOTER'];
  13327. }
  13328. if (isset($p['RESETPAGENUM']) && $p['RESETPAGENUM']) {
  13329. $resetpagenum = $p['RESETPAGENUM'];
  13330. }
  13331. if (isset($p['PAGENUMSTYLE']) && $p['PAGENUMSTYLE']) {
  13332. $pagenumstyle = $p['PAGENUMSTYLE'];
  13333. }
  13334. if (isset($p['SUPPRESS']) && $p['SUPPRESS']) {
  13335. $suppress = $p['SUPPRESS'];
  13336. }
  13337. if (isset($p['MARKS'])) {
  13338. if (preg_match('/cross/i', $p['MARKS']) && preg_match('/crop/i', $p['MARKS'])) {
  13339. $marks = 'CROPCROSS';
  13340. } elseif (strtoupper($p['MARKS']) == 'CROP') {
  13341. $marks = 'CROP';
  13342. } elseif (strtoupper($p['MARKS']) == 'CROSS') {
  13343. $marks = 'CROSS';
  13344. }
  13345. }
  13346. if (isset($p['BACKGROUND-COLOR']) && $p['BACKGROUND-COLOR']) {
  13347. $bg['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];
  13348. }
  13349. /* -- BACKGROUNDS -- */
  13350. if (isset($p['BACKGROUND-GRADIENT']) && $p['BACKGROUND-GRADIENT']) {
  13351. $bg['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];
  13352. }
  13353. if (isset($p['BACKGROUND-IMAGE']) && $p['BACKGROUND-IMAGE']) {
  13354. $bg['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];
  13355. }
  13356. if (isset($p['BACKGROUND-REPEAT']) && $p['BACKGROUND-REPEAT']) {
  13357. $bg['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];
  13358. }
  13359. if (isset($p['BACKGROUND-POSITION']) && $p['BACKGROUND-POSITION']) {
  13360. $bg['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];
  13361. }
  13362. if (isset($p['BACKGROUND-IMAGE-RESIZE']) && $p['BACKGROUND-IMAGE-RESIZE']) {
  13363. $bg['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];
  13364. }
  13365. if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {
  13366. $bg['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];
  13367. }
  13368. /* -- END BACKGROUNDS -- */
  13369. if (isset($p['MARGIN-LEFT'])) {
  13370. $mgl = $this->sizeConverter->convert($p['MARGIN-LEFT'], $pgw) + $outer_width_LR;
  13371. }
  13372. if (isset($p['MARGIN-RIGHT'])) {
  13373. $mgr = $this->sizeConverter->convert($p['MARGIN-RIGHT'], $pgw) + $outer_width_LR;
  13374. }
  13375. if (isset($p['MARGIN-BOTTOM'])) {
  13376. $mgb = $this->sizeConverter->convert($p['MARGIN-BOTTOM'], $pgh) + $outer_width_TB;
  13377. }
  13378. if (isset($p['MARGIN-TOP'])) {
  13379. $mgt = $this->sizeConverter->convert($p['MARGIN-TOP'], $pgh) + $outer_width_TB;
  13380. }
  13381. if (isset($p['MARGIN-HEADER'])) {
  13382. $mgh = $this->sizeConverter->convert($p['MARGIN-HEADER'], $pgh) + $outer_width_TB;
  13383. }
  13384. if (isset($p['MARGIN-FOOTER'])) {
  13385. $mgf = $this->sizeConverter->convert($p['MARGIN-FOOTER'], $pgh) + $outer_width_TB;
  13386. }
  13387. if (isset($p['ORIENTATION']) && $p['ORIENTATION']) {
  13388. $orientation = $p['ORIENTATION'];
  13389. }
  13390. $this->page_box['outer_width_LR'] = $outer_width_LR; // Used in MARKS:crop etc.
  13391. $this->page_box['outer_width_TB'] = $outer_width_TB;
  13392. return [$orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $header, $footer, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat];
  13393. }
  13394. /* -- END CSS-PAGE -- */
  13395. /* -- CSS-FLOAT -- */
  13396. // Added mPDF 3.0 Float DIV - CLEAR
  13397. function ClearFloats($clear, $blklvl = 0)
  13398. {
  13399. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($blklvl, true);
  13400. $end = $currpos = ($this->page * 1000 + $this->y);
  13401. if ($clear == 'BOTH' && ($l_exists || $r_exists)) {
  13402. $this->pageoutput[$this->page] = [];
  13403. $end = max($l_max, $r_max, $currpos);
  13404. } elseif ($clear == 'RIGHT' && $r_exists) {
  13405. $this->pageoutput[$this->page] = [];
  13406. $end = max($r_max, $currpos);
  13407. } elseif ($clear == 'LEFT' && $l_exists) {
  13408. $this->pageoutput[$this->page] = [];
  13409. $end = max($l_max, $currpos);
  13410. } else {
  13411. return;
  13412. }
  13413. $old_page = $this->page;
  13414. $new_page = intval($end / 1000);
  13415. if ($old_page != $new_page) {
  13416. $s = $this->PrintPageBackgrounds();
  13417. // Writes after the marker so not overwritten later by page background etc.
  13418. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  13419. $this->pageBackgrounds = [];
  13420. $this->page = $new_page;
  13421. }
  13422. $this->ResetMargins();
  13423. $this->pageoutput[$this->page] = [];
  13424. $this->y = (round($end * 1000) % 1000000) / 1000; // mod changes operands to integers before processing
  13425. }
  13426. // Added mPDF 3.0 Float DIV
  13427. function GetFloatDivInfo($blklvl = 0, $clear = false)
  13428. {
  13429. // If blklvl specified, only returns floats at that level - for ClearFloats
  13430. $l_exists = false;
  13431. $r_exists = false;
  13432. $l_max = 0;
  13433. $r_max = 0;
  13434. $l_width = 0;
  13435. $r_width = 0;
  13436. if (count($this->floatDivs)) {
  13437. $currpos = ($this->page * 1000 + $this->y);
  13438. foreach ($this->floatDivs as $f) {
  13439. 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'])) {
  13440. if ($f['side'] == 'L') {
  13441. $l_exists = true;
  13442. $l_max = max($l_max, $f['endpos']);
  13443. $l_width = max($l_width, $f['w']);
  13444. }
  13445. if ($f['side'] == 'R') {
  13446. $r_exists = true;
  13447. $r_max = max($r_max, $f['endpos']);
  13448. $r_width = max($r_width, $f['w']);
  13449. }
  13450. }
  13451. }
  13452. }
  13453. return [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width];
  13454. }
  13455. /* -- END CSS-FLOAT -- */
  13456. // LIST MARKERS // mPDF 6 Lists
  13457. function _setListMarker($listitemtype, $listitemimage, $listitemposition)
  13458. {
  13459. // if position:inside (and NOT table) - output now as a textbuffer; (so if next is block, will move to new line)
  13460. // elseif position:outside (and NOT table) - output in front of first textbuffer output by setting listitem (cf. _saveTextBuffer)
  13461. $e = '';
  13462. $this->listitem = '';
  13463. $spacer = ' ';
  13464. // IMAGE
  13465. if ($listitemimage && $listitemimage != 'none') {
  13466. $listitemimage = trim(preg_replace('/url\(["\']*(.*?)["\']*\)/', '\\1', $listitemimage));
  13467. // ? Restrict maximum height/width of list marker??
  13468. $maxWidth = 100;
  13469. $maxHeight = 100;
  13470. $objattr = [];
  13471. $objattr['margin_top'] = 0;
  13472. $objattr['margin_bottom'] = 0;
  13473. $objattr['margin_left'] = 0;
  13474. $objattr['margin_right'] = 0;
  13475. $objattr['padding_top'] = 0;
  13476. $objattr['padding_bottom'] = 0;
  13477. $objattr['padding_left'] = 0;
  13478. $objattr['padding_right'] = 0;
  13479. $objattr['width'] = 0;
  13480. $objattr['height'] = 0;
  13481. $objattr['border_top']['w'] = 0;
  13482. $objattr['border_bottom']['w'] = 0;
  13483. $objattr['border_left']['w'] = 0;
  13484. $objattr['border_right']['w'] = 0;
  13485. $objattr['visibility'] = 'visible';
  13486. $srcpath = $listitemimage;
  13487. $orig_srcpath = $listitemimage;
  13488. $objattr['vertical-align'] = 'BS'; // vertical alignment of marker (baseline)
  13489. $w = 0;
  13490. $h = 0;
  13491. // Image file
  13492. $info = $this->imageProcessor->getImage($srcpath, true, true, $orig_srcpath);
  13493. if (!$info) {
  13494. return;
  13495. }
  13496. if ($info['w'] == 0 && $info['h'] == 0) {
  13497. $info['h'] = $this->sizeConverter->convert('1em', $this->blk[$this->blklvl]['inner_width'], $this->FontSize, false);
  13498. }
  13499. $objattr['file'] = $srcpath;
  13500. // Default width and height calculation if needed
  13501. if ($w == 0 and $h == 0) {
  13502. /* -- IMAGES-WMF -- */
  13503. if ($info['type'] == 'wmf') {
  13504. // WMF units are twips (1/20pt)
  13505. // divide by 20 to get points
  13506. // divide by k to get user units
  13507. $w = abs($info['w']) / (20 * Mpdf::SCALE);
  13508. $h = abs($info['h']) / (20 * Mpdf::SCALE);
  13509. } else { /* -- END IMAGES-WMF -- */
  13510. if ($info['type'] == 'svg') {
  13511. // SVG units are pixels
  13512. $w = abs($info['w']) / Mpdf::SCALE;
  13513. $h = abs($info['h']) / Mpdf::SCALE;
  13514. } else {
  13515. // Put image at default image dpi
  13516. $w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi);
  13517. $h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi);
  13518. }
  13519. }
  13520. }
  13521. // IF WIDTH OR HEIGHT SPECIFIED
  13522. if ($w == 0) {
  13523. $w = abs($h * $info['w'] / $info['h']);
  13524. }
  13525. if ($h == 0) {
  13526. $h = abs($w * $info['h'] / $info['w']);
  13527. }
  13528. if ($w > $maxWidth) {
  13529. $w = $maxWidth;
  13530. $h = abs($w * $info['h'] / $info['w']);
  13531. }
  13532. if ($h > $maxHeight) {
  13533. $h = $maxHeight;
  13534. $w = abs($h * $info['w'] / $info['h']);
  13535. }
  13536. $objattr['type'] = 'image';
  13537. $objattr['itype'] = $info['type'];
  13538. $objattr['orig_h'] = $info['h'];
  13539. $objattr['orig_w'] = $info['w'];
  13540. /* -- IMAGES-WMF -- */
  13541. if ($info['type'] == 'wmf') {
  13542. $objattr['wmf_x'] = $info['x'];
  13543. $objattr['wmf_y'] = $info['y'];
  13544. } else { /* -- END IMAGES-WMF -- */
  13545. if ($info['type'] == 'svg') {
  13546. $objattr['wmf_x'] = $info['x'];
  13547. $objattr['wmf_y'] = $info['y'];
  13548. }
  13549. }
  13550. $objattr['height'] = $h;
  13551. $objattr['width'] = $w;
  13552. $objattr['image_height'] = $h;
  13553. $objattr['image_width'] = $w;
  13554. $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
  13555. $objattr['listmarker'] = true;
  13556. $objattr['listmarkerposition'] = $listitemposition;
  13557. $e = Mpdf::OBJECT_IDENTIFIER . "type=image,objattr=" . serialize($objattr) . Mpdf::OBJECT_IDENTIFIER;
  13558. $this->_saveTextBuffer($e);
  13559. if ($listitemposition == 'inside') {
  13560. $e = $spacer;
  13561. $this->_saveTextBuffer($e);
  13562. }
  13563. } elseif ($listitemtype == 'disc' || $listitemtype == 'circle' || $listitemtype == 'square') { // SYMBOL (needs new font)
  13564. $objattr = [];
  13565. $objattr['type'] = 'listmarker';
  13566. $objattr['listmarkerposition'] = $listitemposition;
  13567. $objattr['width'] = 0;
  13568. $size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize);
  13569. $objattr['size'] = $size;
  13570. $objattr['offset'] = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
  13571. if ($listitemposition == 'inside') {
  13572. $objattr['width'] = $size + $objattr['offset'];
  13573. }
  13574. $objattr['height'] = $this->FontSize;
  13575. $objattr['vertical-align'] = 'T';
  13576. $objattr['text'] = '';
  13577. $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
  13578. $objattr['bullet'] = $listitemtype;
  13579. $objattr['colorarray'] = $this->colorarray;
  13580. $objattr['fontfamily'] = $this->FontFamily;
  13581. $objattr['fontsize'] = $this->FontSize;
  13582. $objattr['fontsizept'] = $this->FontSizePt;
  13583. $objattr['fontstyle'] = $this->FontStyle;
  13584. $e = Mpdf::OBJECT_IDENTIFIER . "type=listmarker,objattr=" . serialize($objattr) . Mpdf::OBJECT_IDENTIFIER;
  13585. $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
  13586. } elseif (preg_match('/U\+([a-fA-F0-9]+)/i', $listitemtype, $m)) { // SYMBOL 2 (needs new font)
  13587. if ($this->_charDefined($this->CurrentFont['cw'], hexdec($m[1]))) {
  13588. $list_item_marker = UtfString::codeHex2utf($m[1]);
  13589. } else {
  13590. $list_item_marker = '-';
  13591. }
  13592. if (preg_match('/rgb\(.*?\)/', $listitemtype, $m)) {
  13593. $list_item_color = $this->colorConverter->convert($m[0], $this->PDFAXwarnings);
  13594. } else {
  13595. $list_item_color = '';
  13596. }
  13597. // SAVE then SET COLR
  13598. $save_colorarray = $this->colorarray;
  13599. if ($list_item_color) {
  13600. $this->colorarray = $list_item_color;
  13601. }
  13602. if ($listitemposition == 'inside') {
  13603. $e = $list_item_marker . $spacer;
  13604. $this->_saveTextBuffer($e);
  13605. } else {
  13606. $objattr = [];
  13607. $objattr['type'] = 'listmarker';
  13608. $objattr['width'] = 0;
  13609. $objattr['height'] = $this->FontSize;
  13610. $objattr['vertical-align'] = 'T';
  13611. $objattr['text'] = $list_item_marker;
  13612. $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
  13613. $objattr['colorarray'] = $this->colorarray;
  13614. $objattr['fontfamily'] = $this->FontFamily;
  13615. $objattr['fontsize'] = $this->FontSize;
  13616. $objattr['fontsizept'] = $this->FontSizePt;
  13617. $objattr['fontstyle'] = $this->FontStyle;
  13618. $e = Mpdf::OBJECT_IDENTIFIER . "type=listmarker,objattr=" . serialize($objattr) . Mpdf::OBJECT_IDENTIFIER;
  13619. $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
  13620. }
  13621. // RESET COLOR
  13622. $this->colorarray = $save_colorarray;
  13623. } else { // TEXT
  13624. $counter = $this->listcounter[$this->listlvl];
  13625. if ($listitemtype == 'none') {
  13626. return;
  13627. }
  13628. $num = $this->_getStyledNumber($counter, $listitemtype, true);
  13629. if ($listitemposition == 'inside') {
  13630. $e = $num . $this->list_number_suffix . $spacer;
  13631. $this->_saveTextBuffer($e);
  13632. } else {
  13633. if (isset($this->blk[$this->blklvl]['direction']) && $this->blk[$this->blklvl]['direction'] == 'rtl') {
  13634. // REPLACE MIRRORED RTL $this->list_number_suffix e.g. ) -> ( (NB could use Ucdn::$mirror_pairs)
  13635. $m = strtr($this->list_number_suffix, ")]}", "([{") . $num;
  13636. } else {
  13637. $m = $num . $this->list_number_suffix;
  13638. }
  13639. $objattr = [];
  13640. $objattr['type'] = 'listmarker';
  13641. $objattr['width'] = 0;
  13642. $objattr['height'] = $this->FontSize;
  13643. $objattr['vertical-align'] = 'T';
  13644. $objattr['text'] = $m;
  13645. $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
  13646. $objattr['colorarray'] = $this->colorarray;
  13647. $objattr['fontfamily'] = $this->FontFamily;
  13648. $objattr['fontsize'] = $this->FontSize;
  13649. $objattr['fontsizept'] = $this->FontSizePt;
  13650. $objattr['fontstyle'] = $this->FontStyle;
  13651. $e = Mpdf::OBJECT_IDENTIFIER . "type=listmarker,objattr=" . serialize($objattr) . Mpdf::OBJECT_IDENTIFIER;
  13652. $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
  13653. }
  13654. }
  13655. }
  13656. // mPDF Lists
  13657. function _getListMarkerWidth(&$currblk, &$a, &$i)
  13658. {
  13659. $blt_width = 0;
  13660. $markeroffset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
  13661. // Get Maximum number in the list
  13662. $maxnum = $this->listcounter[$this->listlvl];
  13663. if ($currblk['list_style_type'] != 'disc' && $currblk['list_style_type'] != 'circle' && $currblk['list_style_type'] != 'square') {
  13664. $lvl = 1;
  13665. for ($j = $i + 2; $j < count($a); $j+=2) {
  13666. $e = $a[$j];
  13667. if (!$e) {
  13668. continue;
  13669. }
  13670. if ($e[0] == '/') { // end tag
  13671. $e = strtoupper(substr($e, 1));
  13672. if ($e == 'OL' || $e == 'UL') {
  13673. if ($lvl == 1) {
  13674. break;
  13675. }
  13676. $lvl--;
  13677. }
  13678. } else { // opening tag
  13679. if (strpos($e, ' ')) {
  13680. $e = substr($e, 0, strpos($e, ' '));
  13681. }
  13682. $e = strtoupper($e);
  13683. if ($e == 'LI') {
  13684. if ($lvl == 1) {
  13685. $maxnum++;
  13686. }
  13687. } elseif ($e == 'OL' || $e == 'UL') {
  13688. $lvl++;
  13689. }
  13690. }
  13691. }
  13692. }
  13693. $decToAlpha = new Conversion\DecToAlpha();
  13694. $decToRoman = new Conversion\DecToRoman();
  13695. $decToOther = new Conversion\DecToOther($this);
  13696. switch ($currblk['list_style_type']) {
  13697. case 'decimal':
  13698. case '1':
  13699. $blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);
  13700. break;
  13701. case 'none':
  13702. $blt_width = 0;
  13703. break;
  13704. case 'upper-alpha':
  13705. case 'upper-latin':
  13706. case 'A':
  13707. $maxnumA = $decToAlpha->convert($maxnum, true);
  13708. if ($maxnum < 13) {
  13709. $blt_width = $this->GetStringWidth('D' . $this->list_number_suffix);
  13710. } else {
  13711. $blt_width = $this->GetStringWidth(str_repeat('W', strlen($maxnumA)) . $this->list_number_suffix);
  13712. }
  13713. break;
  13714. case 'lower-alpha':
  13715. case 'lower-latin':
  13716. case 'a':
  13717. $maxnuma = $decToAlpha->convert($maxnum, false);
  13718. if ($maxnum < 13) {
  13719. $blt_width = $this->GetStringWidth('b' . $this->list_number_suffix);
  13720. } else {
  13721. $blt_width = $this->GetStringWidth(str_repeat('m', strlen($maxnuma)) . $this->list_number_suffix);
  13722. }
  13723. break;
  13724. case 'upper-roman':
  13725. case 'I':
  13726. if ($maxnum > 87) {
  13727. $bbit = 87;
  13728. } elseif ($maxnum > 86) {
  13729. $bbit = 86;
  13730. } elseif ($maxnum > 37) {
  13731. $bbit = 38;
  13732. } elseif ($maxnum > 36) {
  13733. $bbit = 37;
  13734. } elseif ($maxnum > 27) {
  13735. $bbit = 28;
  13736. } elseif ($maxnum > 26) {
  13737. $bbit = 27;
  13738. } elseif ($maxnum > 17) {
  13739. $bbit = 18;
  13740. } elseif ($maxnum > 16) {
  13741. $bbit = 17;
  13742. } elseif ($maxnum > 7) {
  13743. $bbit = 8;
  13744. } elseif ($maxnum > 6) {
  13745. $bbit = 7;
  13746. } elseif ($maxnum > 3) {
  13747. $bbit = 4;
  13748. } else {
  13749. $bbit = $maxnum;
  13750. }
  13751. $maxlnum = $decToRoman->convert($bbit, true);
  13752. $blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);
  13753. break;
  13754. case 'lower-roman':
  13755. case 'i':
  13756. if ($maxnum > 87) {
  13757. $bbit = 87;
  13758. } elseif ($maxnum > 86) {
  13759. $bbit = 86;
  13760. } elseif ($maxnum > 37) {
  13761. $bbit = 38;
  13762. } elseif ($maxnum > 36) {
  13763. $bbit = 37;
  13764. } elseif ($maxnum > 27) {
  13765. $bbit = 28;
  13766. } elseif ($maxnum > 26) {
  13767. $bbit = 27;
  13768. } elseif ($maxnum > 17) {
  13769. $bbit = 18;
  13770. } elseif ($maxnum > 16) {
  13771. $bbit = 17;
  13772. } elseif ($maxnum > 7) {
  13773. $bbit = 8;
  13774. } elseif ($maxnum > 6) {
  13775. $bbit = 7;
  13776. } elseif ($maxnum > 3) {
  13777. $bbit = 4;
  13778. } else {
  13779. $bbit = $maxnum;
  13780. }
  13781. $maxlnum = $decToRoman->convert($bbit, false);
  13782. $blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);
  13783. break;
  13784. case 'disc':
  13785. case 'circle':
  13786. case 'square':
  13787. $size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize);
  13788. $offset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
  13789. $blt_width = $size + $offset;
  13790. break;
  13791. case 'arabic-indic':
  13792. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0660), strlen($maxnum)) . $this->list_number_suffix);
  13793. break;
  13794. case 'persian':
  13795. case 'urdu':
  13796. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x06F0), strlen($maxnum)) . $this->list_number_suffix);
  13797. break;
  13798. case 'bengali':
  13799. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x09E6), strlen($maxnum)) . $this->list_number_suffix);
  13800. break;
  13801. case 'devanagari':
  13802. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0966), strlen($maxnum)) . $this->list_number_suffix);
  13803. break;
  13804. case 'gujarati':
  13805. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0AE6), strlen($maxnum)) . $this->list_number_suffix);
  13806. break;
  13807. case 'gurmukhi':
  13808. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0A66), strlen($maxnum)) . $this->list_number_suffix);
  13809. break;
  13810. case 'kannada':
  13811. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0CE6), strlen($maxnum)) . $this->list_number_suffix);
  13812. break;
  13813. case 'malayalam':
  13814. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(6, 0x0D66), strlen($maxnum)) . $this->list_number_suffix);
  13815. break;
  13816. case 'oriya':
  13817. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0B66), strlen($maxnum)) . $this->list_number_suffix);
  13818. break;
  13819. case 'telugu':
  13820. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0C66), strlen($maxnum)) . $this->list_number_suffix);
  13821. break;
  13822. case 'tamil':
  13823. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(9, 0x0BE6), strlen($maxnum)) . $this->list_number_suffix);
  13824. break;
  13825. case 'thai':
  13826. $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(5, 0x0E50), strlen($maxnum)) . $this->list_number_suffix);
  13827. break;
  13828. default:
  13829. $blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);
  13830. break;
  13831. }
  13832. return ($blt_width + $markeroffset);
  13833. }
  13834. function _saveTextBuffer($t, $link = '', $intlink = '', $return = false)
  13835. {
  13836. // mPDF 6 Lists
  13837. $arr = [];
  13838. $arr[0] = $t;
  13839. if (isset($link) && $link) {
  13840. $arr[1] = $link;
  13841. }
  13842. $arr[2] = $this->currentfontstyle;
  13843. if (isset($this->colorarray) && $this->colorarray) {
  13844. $arr[3] = $this->colorarray;
  13845. }
  13846. $arr[4] = $this->currentfontfamily;
  13847. $arr[5] = $this->currentLang; // mPDF 6
  13848. if (isset($intlink) && $intlink) {
  13849. $arr[7] = $intlink;
  13850. }
  13851. // mPDF 6
  13852. // If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,
  13853. // set for kerning via kern table
  13854. // e.g. Latin script when useOTL set as 0x80
  13855. if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {
  13856. $this->textvar = ($this->textvar | TextVars::FC_KERNING);
  13857. }
  13858. $arr[8] = $this->textvar; // mPDF 5.7.1
  13859. if (isset($this->textparam) && $this->textparam) {
  13860. $arr[9] = $this->textparam;
  13861. }
  13862. if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) {
  13863. $arr[10] = $this->spanbgcolorarray;
  13864. }
  13865. $arr[11] = $this->currentfontsize;
  13866. if (isset($this->ReqFontStyle) && $this->ReqFontStyle) {
  13867. $arr[12] = $this->ReqFontStyle;
  13868. }
  13869. if (isset($this->lSpacingCSS) && $this->lSpacingCSS) {
  13870. $arr[14] = $this->lSpacingCSS;
  13871. }
  13872. if (isset($this->wSpacingCSS) && $this->wSpacingCSS) {
  13873. $arr[15] = $this->wSpacingCSS;
  13874. }
  13875. if (isset($this->spanborddet) && $this->spanborddet) {
  13876. $arr[16] = $this->spanborddet;
  13877. }
  13878. if (isset($this->textshadow) && $this->textshadow) {
  13879. $arr[17] = $this->textshadow;
  13880. }
  13881. if (isset($this->OTLdata) && $this->OTLdata) {
  13882. $arr[18] = $this->OTLdata;
  13883. $this->OTLdata = [];
  13884. } // mPDF 5.7.1
  13885. else {
  13886. $arr[18] = null;
  13887. }
  13888. // mPDF 6 Lists
  13889. if ($return) {
  13890. return ($arr);
  13891. }
  13892. if ($this->listitem) {
  13893. $this->textbuffer[] = $this->listitem;
  13894. $this->listitem = [];
  13895. }
  13896. $this->textbuffer[] = $arr;
  13897. }
  13898. function _saveCellTextBuffer($t, $link = '', $intlink = '')
  13899. {
  13900. $arr = [];
  13901. $arr[0] = $t;
  13902. if (isset($link) && $link) {
  13903. $arr[1] = $link;
  13904. }
  13905. $arr[2] = $this->currentfontstyle;
  13906. if (isset($this->colorarray) && $this->colorarray) {
  13907. $arr[3] = $this->colorarray;
  13908. }
  13909. $arr[4] = $this->currentfontfamily;
  13910. if (isset($intlink) && $intlink) {
  13911. $arr[7] = $intlink;
  13912. }
  13913. // mPDF 6
  13914. // If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,
  13915. // set for kerning via kern table
  13916. // e.g. Latin script when useOTL set as 0x80
  13917. if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {
  13918. $this->textvar = ($this->textvar | TextVars::FC_KERNING);
  13919. }
  13920. $arr[8] = $this->textvar; // mPDF 5.7.1
  13921. if (isset($this->textparam) && $this->textparam) {
  13922. $arr[9] = $this->textparam;
  13923. }
  13924. if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) {
  13925. $arr[10] = $this->spanbgcolorarray;
  13926. }
  13927. $arr[11] = $this->currentfontsize;
  13928. if (isset($this->ReqFontStyle) && $this->ReqFontStyle) {
  13929. $arr[12] = $this->ReqFontStyle;
  13930. }
  13931. if (isset($this->lSpacingCSS) && $this->lSpacingCSS) {
  13932. $arr[14] = $this->lSpacingCSS;
  13933. }
  13934. if (isset($this->wSpacingCSS) && $this->wSpacingCSS) {
  13935. $arr[15] = $this->wSpacingCSS;
  13936. }
  13937. if (isset($this->spanborddet) && $this->spanborddet) {
  13938. $arr[16] = $this->spanborddet;
  13939. }
  13940. if (isset($this->textshadow) && $this->textshadow) {
  13941. $arr[17] = $this->textshadow;
  13942. }
  13943. if (isset($this->OTLdata) && $this->OTLdata) {
  13944. $arr[18] = $this->OTLdata;
  13945. $this->OTLdata = [];
  13946. } // mPDF 5.7.1
  13947. else {
  13948. $arr[18] = null;
  13949. }
  13950. $this->cell[$this->row][$this->col]['textbuffer'][] = $arr;
  13951. }
  13952. function printbuffer($arrayaux, $blockstate = 0, $is_table = false, $table_draft = false, $cell_dir = '')
  13953. {
  13954. // $blockstate = 0; // NO margins/padding
  13955. // $blockstate = 1; // Top margins/padding only
  13956. // $blockstate = 2; // Bottom margins/padding only
  13957. // $blockstate = 3; // Top & bottom margins/padding
  13958. $this->spanbgcolorarray = '';
  13959. $this->spanbgcolor = false;
  13960. $this->spanborder = false;
  13961. $this->spanborddet = [];
  13962. $paint_ht_corr = 0;
  13963. /* -- CSS-FLOAT -- */
  13964. if (count($this->floatDivs)) {
  13965. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  13966. if (($this->blk[$this->blklvl]['inner_width'] - $l_width - $r_width) < (2 * $this->GetCharWidth('W', false))) {
  13967. // Too narrow to fit - try to move down past L or R float
  13968. if ($l_max < $r_max && ($this->blk[$this->blklvl]['inner_width'] - $r_width) > (2 * $this->GetCharWidth('W', false))) {
  13969. $this->ClearFloats('LEFT', $this->blklvl);
  13970. } elseif ($r_max < $l_max && ($this->blk[$this->blklvl]['inner_width'] - $l_width) > (2 * $this->GetCharWidth('W', false))) {
  13971. $this->ClearFloats('RIGHT', $this->blklvl);
  13972. } else {
  13973. $this->ClearFloats('BOTH', $this->blklvl);
  13974. }
  13975. }
  13976. }
  13977. /* -- END CSS-FLOAT -- */
  13978. $bak_y = $this->y;
  13979. $bak_x = $this->x;
  13980. $align = '';
  13981. if (!$is_table) {
  13982. if (isset($this->blk[$this->blklvl]['align']) && $this->blk[$this->blklvl]['align']) {
  13983. $align = $this->blk[$this->blklvl]['align'];
  13984. }
  13985. // Block-align is set by e.g. <.. align="center"> Takes priority for this block but not inherited
  13986. if (isset($this->blk[$this->blklvl]['block-align']) && $this->blk[$this->blklvl]['block-align']) {
  13987. $align = $this->blk[$this->blklvl]['block-align'];
  13988. }
  13989. if (isset($this->blk[$this->blklvl]['direction'])) {
  13990. $blockdir = $this->blk[$this->blklvl]['direction'];
  13991. } else {
  13992. $blockdir = "";
  13993. }
  13994. $this->divwidth = $this->blk[$this->blklvl]['width'];
  13995. } else {
  13996. $align = $this->cellTextAlign;
  13997. $blockdir = $cell_dir;
  13998. }
  13999. $oldpage = $this->page;
  14000. // ADDED for Out of Block now done as Flowing Block
  14001. if ($this->divwidth == 0) {
  14002. $this->divwidth = $this->pgwidth;
  14003. }
  14004. if (!$is_table) {
  14005. $this->SetLineHeight($this->FontSizePt, $this->blk[$this->blklvl]['line_height']);
  14006. }
  14007. $this->divheight = $this->lineheight;
  14008. $old_height = $this->divheight;
  14009. // As a failsafe - if font has been set but not output to page
  14010. if (!$table_draft) {
  14011. $this->SetFont($this->default_font, '', $this->default_font_size, true, true); // force output to page
  14012. }
  14013. $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, true, $blockdir, $table_draft);
  14014. $array_size = count($arrayaux);
  14015. // Added - Otherwise <div><div><p> did not output top margins/padding for 1st/2nd div
  14016. if ($array_size == 0) {
  14017. $this->finishFlowingBlock(true);
  14018. } // true = END of flowing block
  14019. // mPDF 6
  14020. // ALL the chunks of textbuffer need to have at least basic OTLdata set
  14021. // First make sure each element/chunk has the OTLdata for Bidi set.
  14022. for ($i = 0; $i < $array_size; $i++) {
  14023. if (empty($arrayaux[$i][18])) {
  14024. if (substr($arrayaux[$i][0], 0, 3) == Mpdf::OBJECT_IDENTIFIER) { // object identifier has been identified!
  14025. $unicode = [0xFFFC]; // Object replacement character
  14026. } else {
  14027. $unicode = $this->UTF8StringToArray($arrayaux[$i][0], false);
  14028. }
  14029. $is_strong = false;
  14030. $this->getBasicOTLdata($arrayaux[$i][18], $unicode, $is_strong);
  14031. }
  14032. // Gets messed up if try and use core fonts inside a paragraph of text which needs to be BiDi re-ordered or OTLdata set
  14033. if (($blockdir == 'rtl' || $this->biDirectional) && isset($arrayaux[$i][4]) && in_array($arrayaux[$i][4], ['ccourier', 'ctimes', 'chelvetica', 'csymbol', 'czapfdingbats'])) {
  14034. throw new \Mpdf\MpdfException("You cannot use core fonts in a document which contains RTL text.");
  14035. }
  14036. }
  14037. // mPDF 6
  14038. // Process bidirectional text ready for bidi-re-ordering (which is done after line-breaks are established in WriteFlowingBlock etc.)
  14039. if (($blockdir == 'rtl' || $this->biDirectional) && !$table_draft) {
  14040. if (empty($this->otl)) {
  14041. $this->otl = new Otl($this, $this->fontCache);
  14042. }
  14043. $this->otl->bidiPrepare($arrayaux, $blockdir);
  14044. $array_size = count($arrayaux);
  14045. }
  14046. // Remove empty items // mPDF 6
  14047. for ($i = $array_size - 1; $i > 0; $i--) {
  14048. if ('' === $arrayaux[$i][0] && (isset($arrayaux[$i][16]) && $arrayaux[$i][16] !== '0') && empty($arrayaux[$i][7])) {
  14049. unset($arrayaux[$i]);
  14050. }
  14051. }
  14052. // Correct adjoining borders for inline elements
  14053. if (isset($arrayaux[0][16])) {
  14054. $lastspanborder = $arrayaux[0][16];
  14055. } else {
  14056. $lastspanborder = false;
  14057. }
  14058. for ($i = 1; $i < $array_size; $i++) {
  14059. if (isset($arrayaux[$i][16]) && $arrayaux[$i][16] == $lastspanborder &&
  14060. ((!isset($arrayaux[$i][9]['bord-decoration']) && !isset($arrayaux[$i - 1][9]['bord-decoration'])) ||
  14061. (isset($arrayaux[$i][9]['bord-decoration']) && isset($arrayaux[$i - 1][9]['bord-decoration']) && $arrayaux[$i][9]['bord-decoration'] == $arrayaux[$i - 1][9]['bord-decoration'])
  14062. )
  14063. ) {
  14064. if (isset($arrayaux[$i][16]['R'])) {
  14065. $lastspanborder = $arrayaux[$i][16];
  14066. } else {
  14067. $lastspanborder = false;
  14068. }
  14069. $arrayaux[$i][16]['L']['s'] = 0;
  14070. $arrayaux[$i][16]['L']['w'] = 0;
  14071. $arrayaux[$i - 1][16]['R']['s'] = 0;
  14072. $arrayaux[$i - 1][16]['R']['w'] = 0;
  14073. } else {
  14074. if (isset($arrayaux[$i][16]['R'])) {
  14075. $lastspanborder = $arrayaux[$i][16];
  14076. } else {
  14077. $lastspanborder = false;
  14078. }
  14079. }
  14080. }
  14081. for ($i = 0; $i < $array_size; $i++) {
  14082. // COLS
  14083. $oldcolumn = $this->CurrCol;
  14084. $vetor = isset($arrayaux[$i]) ? $arrayaux[$i] : null;
  14085. if ($i == 0 && $vetor[0] != "\n" && ! $this->ispre) {
  14086. $vetor[0] = ltrim($vetor[0]);
  14087. if (!empty($vetor[18])) {
  14088. $this->otl->trimOTLdata($vetor[18], true, false);
  14089. } // *OTL*
  14090. }
  14091. // FIXED TO ALLOW IT TO SHOW '0'
  14092. if (empty($vetor[0]) && !($vetor[0] === '0') && empty($vetor[7])) {
  14093. // Ignore empty text and not carrying an internal link
  14094. // Check if it is the last element. If so then finish printing the block
  14095. if ($i == ($array_size - 1)) {
  14096. $this->finishFlowingBlock(true);
  14097. } // true = END of flowing block
  14098. continue;
  14099. }
  14100. // Activating buffer properties
  14101. if (isset($vetor[11]) && $vetor[11] != '') { // Font Size
  14102. if ($is_table && $this->shrin_k) {
  14103. $this->SetFontSize($vetor[11] / $this->shrin_k, false);
  14104. } else {
  14105. $this->SetFontSize($vetor[11], false);
  14106. }
  14107. }
  14108. if (isset($vetor[17]) && !empty($vetor[17])) { // TextShadow
  14109. $this->textshadow = $vetor[17];
  14110. }
  14111. if (isset($vetor[16]) && !empty($vetor[16])) { // Border
  14112. $this->spanborddet = $vetor[16];
  14113. $this->spanborder = true;
  14114. }
  14115. if (isset($vetor[15])) { // Word spacing
  14116. $this->wSpacingCSS = $vetor[15];
  14117. if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
  14118. $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
  14119. }
  14120. }
  14121. if (isset($vetor[14])) { // Letter spacing
  14122. $this->lSpacingCSS = $vetor[14];
  14123. if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
  14124. $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
  14125. }
  14126. }
  14127. if (isset($vetor[10]) and ! empty($vetor[10])) { // Background color
  14128. $this->spanbgcolorarray = $vetor[10];
  14129. $this->spanbgcolor = true;
  14130. }
  14131. if (isset($vetor[9]) and ! empty($vetor[9])) { // Text parameters - Outline + hyphens
  14132. $this->textparam = $vetor[9];
  14133. $this->SetTextOutline($this->textparam);
  14134. // mPDF 5.7.3 inline text-decoration parameters
  14135. if ($is_table && $this->shrin_k) {
  14136. if (isset($this->textparam['text-baseline'])) {
  14137. $this->textparam['text-baseline'] /= $this->shrin_k;
  14138. }
  14139. if (isset($this->textparam['decoration-baseline'])) {
  14140. $this->textparam['decoration-baseline'] /= $this->shrin_k;
  14141. }
  14142. if (isset($this->textparam['decoration-fontsize'])) {
  14143. $this->textparam['decoration-fontsize'] /= $this->shrin_k;
  14144. }
  14145. }
  14146. }
  14147. if (isset($vetor[8])) { // mPDF 5.7.1
  14148. $this->textvar = $vetor[8];
  14149. }
  14150. if (isset($vetor[7]) and $vetor[7] != '') { // internal target: <a name="anyvalue">
  14151. $ily = $this->y;
  14152. if ($this->table_rotate) {
  14153. $this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "tbrot" => true];
  14154. } elseif ($this->kwt) {
  14155. $this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "kwt" => true];
  14156. } elseif ($this->ColActive) {
  14157. $this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol];
  14158. } elseif (!$this->keep_block_together) {
  14159. $this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page];
  14160. }
  14161. if (empty($vetor[0])) { // Ignore empty text
  14162. // Check if it is the last element. If so then finish printing the block
  14163. if ($i == ($array_size - 1)) {
  14164. $this->finishFlowingBlock(true);
  14165. } // true = END of flowing block
  14166. continue;
  14167. }
  14168. }
  14169. if (isset($vetor[5]) and $vetor[5] != '') { // Language // mPDF 6
  14170. $this->currentLang = $vetor[5];
  14171. }
  14172. if (isset($vetor[4]) and $vetor[4] != '') { // Font Family
  14173. $font = $this->SetFont($vetor[4], $this->FontStyle, 0, false);
  14174. }
  14175. if (!empty($vetor[3])) { // Font Color
  14176. $cor = $vetor[3];
  14177. $this->SetTColor($cor);
  14178. }
  14179. if (isset($vetor[2]) and $vetor[2] != '') { // Bold,Italic styles
  14180. $this->SetStyles($vetor[2]);
  14181. }
  14182. if (isset($vetor[12]) and $vetor[12] != '') { // Requested Bold,Italic
  14183. $this->ReqFontStyle = $vetor[12];
  14184. }
  14185. if (isset($vetor[1]) and $vetor[1] != '') { // LINK
  14186. 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.)
  14187. // Repeated reference to same anchor?
  14188. while (array_key_exists($vetor[1], $this->internallink)) {
  14189. $vetor[1] = "#" . $vetor[1];
  14190. }
  14191. $this->internallink[$vetor[1]] = $this->AddLink();
  14192. $vetor[1] = $this->internallink[$vetor[1]];
  14193. }
  14194. $this->HREF = $vetor[1]; // HREF link style set here ******
  14195. }
  14196. // SPECIAL CONTENT - IMAGES & FORM OBJECTS
  14197. // Print-out special content
  14198. if (substr($vetor[0], 0, 3) == Mpdf::OBJECT_IDENTIFIER) { // identifier has been identified!
  14199. $objattr = $this->_getObjAttr($vetor[0]);
  14200. /* -- TABLES -- */
  14201. if ($objattr['type'] == 'nestedtable') {
  14202. if ($objattr['nestedcontent']) {
  14203. $level = $objattr['level'];
  14204. $table = &$this->table[$level][$objattr['table']];
  14205. if ($table_draft) {
  14206. $this->y += $this->table[($level + 1)][$objattr['nestedcontent']]['h']; // nested table height
  14207. $this->finishFlowingBlock(false, 'nestedtable');
  14208. } else {
  14209. $cell = &$table['cells'][$objattr['row']][$objattr['col']];
  14210. $this->finishFlowingBlock(false, 'nestedtable');
  14211. $save_dw = $this->divwidth;
  14212. $save_buffer = $this->cellBorderBuffer;
  14213. $this->cellBorderBuffer = [];
  14214. $ncx = $this->x;
  14215. list($dummyx, $w) = $this->_tableGetWidth($table, $objattr['row'], $objattr['col']);
  14216. $ntw = $this->table[($level + 1)][$objattr['nestedcontent']]['w']; // nested table width
  14217. if (!$this->simpleTables) {
  14218. if ($this->packTableData) {
  14219. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cell['borderbin']);
  14220. } else {
  14221. $br = $cell['border_details']['R']['w'];
  14222. $bl = $cell['border_details']['L']['w'];
  14223. }
  14224. if ($table['borders_separate']) {
  14225. $innerw = $w - $bl - $br - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];
  14226. } else {
  14227. $innerw = $w - $bl / 2 - $br / 2 - $cell['padding']['L'] - $cell['padding']['R'];
  14228. }
  14229. } elseif ($this->simpleTables) {
  14230. if ($table['borders_separate']) {
  14231. $innerw = $w - $table['simple']['border_details']['L']['w'] - $table['simple']['border_details']['R']['w'] - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];
  14232. } else {
  14233. $innerw = $w - $table['simple']['border_details']['L']['w'] / 2 - $table['simple']['border_details']['R']['w'] / 2 - $cell['padding']['L'] - $cell['padding']['R'];
  14234. }
  14235. }
  14236. if ($cell['a'] == 'C' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'C') {
  14237. $ncx += ($innerw - $ntw) / 2;
  14238. } elseif ($cell['a'] == 'R' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'R') {
  14239. $ncx += $innerw - $ntw;
  14240. }
  14241. $this->x = $ncx;
  14242. $this->_tableWrite($this->table[($level + 1)][$objattr['nestedcontent']]);
  14243. $this->cellBorderBuffer = $save_buffer;
  14244. $this->x = $bak_x;
  14245. $this->divwidth = $save_dw;
  14246. }
  14247. $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
  14248. }
  14249. } else {
  14250. /* -- END TABLES -- */
  14251. if ($is_table) { // *TABLES*
  14252. $maxWidth = $this->divwidth; // *TABLES*
  14253. } // *TABLES*
  14254. else { // *TABLES*
  14255. $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']);
  14256. } // *TABLES*
  14257. /* -- CSS-IMAGE-FLOAT -- */
  14258. // If float (already) exists at this level
  14259. if (isset($this->floatmargins['R']) && $this->y <= $this->floatmargins['R']['y1'] && $this->y >= $this->floatmargins['R']['y0']) {
  14260. $maxWidth -= $this->floatmargins['R']['w'];
  14261. }
  14262. if (isset($this->floatmargins['L']) && $this->y <= $this->floatmargins['L']['y1'] && $this->y >= $this->floatmargins['L']['y0']) {
  14263. $maxWidth -= $this->floatmargins['L']['w'];
  14264. }
  14265. /* -- END CSS-IMAGE-FLOAT -- */
  14266. list($skipln) = $this->inlineObject($objattr['type'], '', $this->y, $objattr, $this->lMargin, ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE), $maxWidth, $this->flowingBlockAttr['height'], false, $is_table);
  14267. // 1 -> New line needed because of width
  14268. // -1 -> Will fit width on line but NEW PAGE REQUIRED because of height
  14269. // -2 -> Will not fit on line therefore needs new line but thus NEW PAGE REQUIRED
  14270. $iby = $this->y;
  14271. $oldpage = $this->page;
  14272. $oldcol = $this->CurrCol;
  14273. if (($skipln == 1 || $skipln == -2) && !isset($objattr['float'])) {
  14274. $this->finishFlowingBlock(false, $objattr['type']);
  14275. $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
  14276. }
  14277. if (!$table_draft) {
  14278. $thispage = $this->page;
  14279. if ($this->CurrCol != $oldcol) {
  14280. $changedcol = true;
  14281. } else {
  14282. $changedcol = false;
  14283. }
  14284. // the previous lines can already have triggered page break or column change
  14285. if (!$changedcol && $skipln < 0 && $this->AcceptPageBreak() && $thispage == $oldpage) {
  14286. $this->AddPage($this->CurOrientation);
  14287. // Added to correct Images already set on line before page advanced
  14288. // i.e. if second inline image on line is higher than first and forces new page
  14289. if (count($this->objectbuffer)) {
  14290. $yadj = $iby - $this->y;
  14291. foreach ($this->objectbuffer as $ib => $val) {
  14292. if ($this->objectbuffer[$ib]['OUTER-Y']) {
  14293. $this->objectbuffer[$ib]['OUTER-Y'] -= $yadj;
  14294. }
  14295. if ($this->objectbuffer[$ib]['BORDER-Y']) {
  14296. $this->objectbuffer[$ib]['BORDER-Y'] -= $yadj;
  14297. }
  14298. if ($this->objectbuffer[$ib]['INNER-Y']) {
  14299. $this->objectbuffer[$ib]['INNER-Y'] -= $yadj;
  14300. }
  14301. }
  14302. }
  14303. }
  14304. // Added to correct for OddEven Margins
  14305. if ($this->page != $oldpage) {
  14306. if (($this->page - $oldpage) % 2 == 1) {
  14307. $bak_x += $this->MarginCorrection;
  14308. }
  14309. $oldpage = $this->page;
  14310. $y = $this->tMargin - $paint_ht_corr;
  14311. $this->oldy = $this->tMargin - $paint_ht_corr;
  14312. $old_height = 0;
  14313. }
  14314. $this->x = $bak_x;
  14315. /* -- COLUMNS -- */
  14316. // COLS
  14317. // OR COLUMN CHANGE
  14318. if ($this->CurrCol != $oldcolumn) {
  14319. if ($this->directionality == 'rtl') { // *OTL*
  14320. $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  14321. } // *OTL*
  14322. else { // *OTL*
  14323. $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
  14324. } // *OTL*
  14325. $this->x = $bak_x;
  14326. $oldcolumn = $this->CurrCol;
  14327. $y = $this->y0 - $paint_ht_corr;
  14328. $this->oldy = $this->y0 - $paint_ht_corr;
  14329. $old_height = 0;
  14330. }
  14331. /* -- END COLUMNS -- */
  14332. }
  14333. /* -- CSS-IMAGE-FLOAT -- */
  14334. if ($objattr['type'] == 'image' && isset($objattr['float'])) {
  14335. $fy = $this->y;
  14336. // DIV TOP MARGIN/BORDER/PADDING
  14337. if ($this->flowingBlockAttr['newblock'] && ($this->flowingBlockAttr['blockstate'] == 1 || $this->flowingBlockAttr['blockstate'] == 3) && $this->flowingBlockAttr['lineCount'] == 0) {
  14338. $fy += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  14339. }
  14340. if ($objattr['float'] == 'R') {
  14341. $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']);
  14342. } elseif ($objattr['float'] == 'L') {
  14343. $fx = $this->lMargin + ($this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left']);
  14344. }
  14345. $w = $objattr['width'];
  14346. $h = abs($objattr['height']);
  14347. $widthLeft = $maxWidth - ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE);
  14348. $maxHeight = $this->h - ($this->tMargin + $this->margin_header + $this->bMargin + 10);
  14349. // For Images
  14350. $extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']);
  14351. $extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']);
  14352. if ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg') {
  14353. $file = $objattr['file'];
  14354. $info = $this->formobjects[$file];
  14355. } else {
  14356. $file = $objattr['file'];
  14357. $info = $this->images[$file];
  14358. }
  14359. $img_w = $w - $extraWidth;
  14360. $img_h = $h - $extraHeight;
  14361. if ($objattr['border_left']['w']) {
  14362. $objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] + $objattr['border_right']['w']) / 2);
  14363. $objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] + $objattr['border_bottom']['w']) / 2);
  14364. $objattr['BORDER-X'] = $fx + $objattr['margin_left'] + (($objattr['border_left']['w']) / 2);
  14365. $objattr['BORDER-Y'] = $fy + $objattr['margin_top'] + (($objattr['border_top']['w']) / 2);
  14366. }
  14367. $objattr['INNER-WIDTH'] = $img_w;
  14368. $objattr['INNER-HEIGHT'] = $img_h;
  14369. $objattr['INNER-X'] = $fx + $objattr['margin_left'] + ($objattr['border_left']['w']);
  14370. $objattr['INNER-Y'] = $fy + $objattr['margin_top'] + ($objattr['border_top']['w']);
  14371. $objattr['ID'] = $info['i'];
  14372. $objattr['OUTER-WIDTH'] = $w;
  14373. $objattr['OUTER-HEIGHT'] = $h;
  14374. $objattr['OUTER-X'] = $fx;
  14375. $objattr['OUTER-Y'] = $fy;
  14376. if ($objattr['float'] == 'R') {
  14377. // If R float already exists at this level
  14378. $this->floatmargins['R']['skipline'] = false;
  14379. if (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {
  14380. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  14381. } // If L float already exists at this level
  14382. elseif (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {
  14383. // Final check distance between floats is not now too narrow to fit text
  14384. $mw = 2 * $this->GetCharWidth('W', false);
  14385. if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['L']['w']) < $mw) {
  14386. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  14387. } else {
  14388. $this->floatmargins['R']['x'] = $fx;
  14389. $this->floatmargins['R']['w'] = $w;
  14390. $this->floatmargins['R']['y0'] = $fy;
  14391. $this->floatmargins['R']['y1'] = $fy + $h;
  14392. if ($skipln == 1) {
  14393. $this->floatmargins['R']['skipline'] = true;
  14394. $this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;
  14395. $objattr['skipline'] = true;
  14396. }
  14397. $this->floatbuffer[] = $objattr;
  14398. }
  14399. } else {
  14400. $this->floatmargins['R']['x'] = $fx;
  14401. $this->floatmargins['R']['w'] = $w;
  14402. $this->floatmargins['R']['y0'] = $fy;
  14403. $this->floatmargins['R']['y1'] = $fy + $h;
  14404. if ($skipln == 1) {
  14405. $this->floatmargins['R']['skipline'] = true;
  14406. $this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;
  14407. $objattr['skipline'] = true;
  14408. }
  14409. $this->floatbuffer[] = $objattr;
  14410. }
  14411. } elseif ($objattr['float'] == 'L') {
  14412. // If L float already exists at this level
  14413. $this->floatmargins['L']['skipline'] = false;
  14414. if (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {
  14415. $this->floatmargins['L']['skipline'] = false;
  14416. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  14417. } // If R float already exists at this level
  14418. elseif (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {
  14419. // Final check distance between floats is not now too narrow to fit text
  14420. $mw = 2 * $this->GetCharWidth('W', false);
  14421. if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['R']['w']) < $mw) {
  14422. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  14423. } else {
  14424. $this->floatmargins['L']['x'] = $fx + $w;
  14425. $this->floatmargins['L']['w'] = $w;
  14426. $this->floatmargins['L']['y0'] = $fy;
  14427. $this->floatmargins['L']['y1'] = $fy + $h;
  14428. if ($skipln == 1) {
  14429. $this->floatmargins['L']['skipline'] = true;
  14430. $this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;
  14431. $objattr['skipline'] = true;
  14432. }
  14433. $this->floatbuffer[] = $objattr;
  14434. }
  14435. } else {
  14436. $this->floatmargins['L']['x'] = $fx + $w;
  14437. $this->floatmargins['L']['w'] = $w;
  14438. $this->floatmargins['L']['y0'] = $fy;
  14439. $this->floatmargins['L']['y1'] = $fy + $h;
  14440. if ($skipln == 1) {
  14441. $this->floatmargins['L']['skipline'] = true;
  14442. $this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;
  14443. $objattr['skipline'] = true;
  14444. }
  14445. $this->floatbuffer[] = $objattr;
  14446. }
  14447. }
  14448. } else {
  14449. /* -- END CSS-IMAGE-FLOAT -- */
  14450. $this->WriteFlowingBlock($vetor[0], (isset($vetor[18]) ? $vetor[18] : null)); // mPDF 5.7.1
  14451. /* -- CSS-IMAGE-FLOAT -- */
  14452. }
  14453. /* -- END CSS-IMAGE-FLOAT -- */
  14454. } // *TABLES*
  14455. } // END If special content
  14456. else { // THE text
  14457. if ($this->tableLevel) {
  14458. $paint_ht_corr = 0;
  14459. } // To move the y up when new column/page started if div border needed
  14460. else {
  14461. $paint_ht_corr = $this->blk[$this->blklvl]['border_top']['w'];
  14462. }
  14463. if ($vetor[0] == "\n") { // We are reading a <BR> now turned into newline ("\n")
  14464. if ($this->flowingBlockAttr['content']) {
  14465. $this->finishFlowingBlock(false, 'br');
  14466. } elseif ($is_table) {
  14467. $this->y+= $this->_computeLineheight($this->cellLineHeight);
  14468. } elseif (!$is_table) {
  14469. $this->DivLn($this->lineheight);
  14470. if ($this->ColActive) {
  14471. $this->breakpoints[$this->CurrCol][] = $this->y;
  14472. } // *COLUMNS*
  14473. }
  14474. // Added to correct for OddEven Margins
  14475. if ($this->page != $oldpage) {
  14476. if (($this->page - $oldpage) % 2 == 1) {
  14477. $bak_x += $this->MarginCorrection;
  14478. }
  14479. $oldpage = $this->page;
  14480. $y = $this->tMargin - $paint_ht_corr;
  14481. $this->oldy = $this->tMargin - $paint_ht_corr;
  14482. $old_height = 0;
  14483. }
  14484. $this->x = $bak_x;
  14485. /* -- COLUMNS -- */
  14486. // COLS
  14487. // OR COLUMN CHANGE
  14488. if ($this->CurrCol != $oldcolumn) {
  14489. if ($this->directionality == 'rtl') { // *OTL*
  14490. $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  14491. } // *OTL*
  14492. else { // *OTL*
  14493. $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
  14494. } // *OTL*
  14495. $this->x = $bak_x;
  14496. $oldcolumn = $this->CurrCol;
  14497. $y = $this->y0 - $paint_ht_corr;
  14498. $this->oldy = $this->y0 - $paint_ht_corr;
  14499. $old_height = 0;
  14500. }
  14501. /* -- END COLUMNS -- */
  14502. $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
  14503. } else {
  14504. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  14505. // Added to correct for OddEven Margins
  14506. if ($this->page != $oldpage) {
  14507. if (($this->page - $oldpage) % 2 == 1) {
  14508. $bak_x += $this->MarginCorrection;
  14509. $this->x = $bak_x;
  14510. }
  14511. $oldpage = $this->page;
  14512. $y = $this->tMargin - $paint_ht_corr;
  14513. $this->oldy = $this->tMargin - $paint_ht_corr;
  14514. $old_height = 0;
  14515. }
  14516. /* -- COLUMNS -- */
  14517. // COLS
  14518. // OR COLUMN CHANGE
  14519. if ($this->CurrCol != $oldcolumn) {
  14520. if ($this->directionality == 'rtl') { // *OTL*
  14521. $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  14522. } // *OTL*
  14523. else { // *OTL*
  14524. $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
  14525. } // *OTL*
  14526. $this->x = $bak_x;
  14527. $oldcolumn = $this->CurrCol;
  14528. $y = $this->y0 - $paint_ht_corr;
  14529. $this->oldy = $this->y0 - $paint_ht_corr;
  14530. $old_height = 0;
  14531. }
  14532. /* -- END COLUMNS -- */
  14533. }
  14534. }
  14535. // Check if it is the last element. If so then finish printing the block
  14536. if ($i == ($array_size - 1)) {
  14537. $this->finishFlowingBlock(true); // true = END of flowing block
  14538. // Added to correct for OddEven Margins
  14539. if ($this->page != $oldpage) {
  14540. if (($this->page - $oldpage) % 2 == 1) {
  14541. $bak_x += $this->MarginCorrection;
  14542. $this->x = $bak_x;
  14543. }
  14544. $oldpage = $this->page;
  14545. $y = $this->tMargin - $paint_ht_corr;
  14546. $this->oldy = $this->tMargin - $paint_ht_corr;
  14547. $old_height = 0;
  14548. }
  14549. /* -- COLUMNS -- */
  14550. // COLS
  14551. // OR COLUMN CHANGE
  14552. if ($this->CurrCol != $oldcolumn) {
  14553. if ($this->directionality == 'rtl') { // *OTL*
  14554. $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  14555. } // *OTL*
  14556. else { // *OTL*
  14557. $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
  14558. } // *OTL*
  14559. $this->x = $bak_x;
  14560. $oldcolumn = $this->CurrCol;
  14561. $y = $this->y0 - $paint_ht_corr;
  14562. $this->oldy = $this->y0 - $paint_ht_corr;
  14563. $old_height = 0;
  14564. }
  14565. /* -- END COLUMNS -- */
  14566. }
  14567. // RESETTING VALUES
  14568. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  14569. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  14570. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  14571. $this->colorarray = '';
  14572. $this->spanbgcolorarray = '';
  14573. $this->spanbgcolor = false;
  14574. $this->spanborder = false;
  14575. $this->spanborddet = [];
  14576. $this->HREF = '';
  14577. $this->textparam = [];
  14578. $this->SetTextOutline();
  14579. $this->textvar = 0x00; // mPDF 5.7.1
  14580. $this->OTLtags = [];
  14581. $this->textshadow = '';
  14582. $this->currentfontfamily = '';
  14583. $this->currentfontsize = '';
  14584. $this->currentfontstyle = '';
  14585. $this->currentLang = $this->default_lang; // mPDF 6
  14586. $this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6
  14587. /* -- TABLES -- */
  14588. if ($this->tableLevel) {
  14589. $this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES*
  14590. } else { /* -- END TABLES -- */
  14591. if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {
  14592. $this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height
  14593. }
  14594. }
  14595. $this->ResetStyles();
  14596. $this->lSpacingCSS = '';
  14597. $this->wSpacingCSS = '';
  14598. $this->fixedlSpacing = false;
  14599. $this->minwSpacing = 0;
  14600. $this->SetDash();
  14601. $this->dash_on = false;
  14602. $this->dotted_on = false;
  14603. }//end of for(i=0;i<arraysize;i++)
  14604. $this->Reset(); // mPDF 6
  14605. // PAINT DIV BORDER // DISABLED IN COLUMNS AS DOESN'T WORK WHEN BROKEN ACROSS COLS??
  14606. 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)) {
  14607. $bottom_y = $this->y; // Does not include Bottom Margin
  14608. if (isset($this->blk[$this->blklvl]['startpage']) && $this->blk[$this->blklvl]['startpage'] != $this->page && $blockstate != 1) {
  14609. $this->PaintDivBB('pagetop', $blockstate);
  14610. } elseif ($blockstate != 1) {
  14611. $this->PaintDivBB('', $blockstate);
  14612. }
  14613. $this->y = $bottom_y;
  14614. $this->x = $bak_x;
  14615. }
  14616. // Reset Font
  14617. $this->SetFontSize($this->default_font_size, false);
  14618. if ($table_draft) {
  14619. $ch = $this->y - $bak_y;
  14620. $this->y = $bak_y;
  14621. $this->x = $bak_x;
  14622. return $ch;
  14623. }
  14624. }
  14625. function _setDashBorder($style, $div, $cp, $side)
  14626. {
  14627. if ($style == 'dashed' && (($side == 'L' || $side == 'R') || ($side == 'T' && $div != 'pagetop' && !$cp) || ($side == 'B' && $div != 'pagebottom') )) {
  14628. $dashsize = 2; // final dash will be this + 1*linewidth
  14629. $dashsizek = 1.5; // ratio of Dash/Blank
  14630. $this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));
  14631. } elseif ($style == 'dotted' || ($side == 'T' && ($div == 'pagetop' || $cp)) || ($side == 'B' && $div == 'pagebottom')) {
  14632. // Round join and cap
  14633. $this->SetLineJoin(1);
  14634. $this->SetLineCap(1);
  14635. $this->SetDash(0.001, ($this->LineWidth * 3));
  14636. }
  14637. }
  14638. function _setBorderLine($b, $k = 1)
  14639. {
  14640. $this->SetLineWidth($b['w'] / $k);
  14641. $this->SetDColor($b['c']);
  14642. if ($b['c'][0] == 5) { // RGBa
  14643. $this->SetAlpha(ord($b['c'][4]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2
  14644. } elseif ($b['c'][0] == 6) { // CMYKa
  14645. $this->SetAlpha(ord($b['c'][5]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2
  14646. }
  14647. }
  14648. function PaintDivBB($divider = '', $blockstate = 0, $blvl = 0)
  14649. {
  14650. // Borders & backgrounds are done elsewhere for columns - messes up the repositioning in printcolumnbuffer
  14651. if ($this->ColActive) {
  14652. return;
  14653. } // *COLUMNS*
  14654. if ($this->keep_block_together) {
  14655. return;
  14656. } // mPDF 6
  14657. $save_y = $this->y;
  14658. if (!$blvl) {
  14659. $blvl = $this->blklvl;
  14660. }
  14661. $x0 = $x1 = $y0 = $y1 = 0;
  14662. // Added mPDF 3.0 Float DIV
  14663. if (isset($this->blk[$blvl]['bb_painted'][$this->page]) && $this->blk[$blvl]['bb_painted'][$this->page]) {
  14664. return;
  14665. } // *CSS-FLOAT*
  14666. if (isset($this->blk[$blvl]['x0'])) {
  14667. $x0 = $this->blk[$blvl]['x0'];
  14668. } // left
  14669. if (isset($this->blk[$blvl]['y1'])) {
  14670. $y1 = $this->blk[$blvl]['y1'];
  14671. } // bottom
  14672. // Added mPDF 3.0 Float DIV - ensures backgrounds/borders are drawn to bottom of page
  14673. if ($y1 == 0) {
  14674. if ($divider == 'pagebottom') {
  14675. $y1 = $this->h - $this->bMargin;
  14676. } else {
  14677. $y1 = $this->y;
  14678. }
  14679. }
  14680. $continuingpage = (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page);
  14681. if (isset($this->blk[$blvl]['y0'])) {
  14682. $y0 = $this->blk[$blvl]['y0'];
  14683. }
  14684. $h = $y1 - $y0;
  14685. $w = $this->blk[$blvl]['width'];
  14686. $x1 = $x0 + $w;
  14687. // Set border-widths as used here
  14688. $border_top = $this->blk[$blvl]['border_top']['w'];
  14689. $border_bottom = $this->blk[$blvl]['border_bottom']['w'];
  14690. $border_left = $this->blk[$blvl]['border_left']['w'];
  14691. $border_right = $this->blk[$blvl]['border_right']['w'];
  14692. if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {
  14693. $border_top = 0;
  14694. }
  14695. if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {
  14696. $border_bottom = 0;
  14697. }
  14698. $brTL_H = 0;
  14699. $brTL_V = 0;
  14700. $brTR_H = 0;
  14701. $brTR_V = 0;
  14702. $brBL_H = 0;
  14703. $brBL_V = 0;
  14704. $brBR_H = 0;
  14705. $brBR_V = 0;
  14706. $brset = false;
  14707. /* -- BORDER-RADIUS -- */
  14708. if (isset($this->blk[$blvl]['border_radius_TL_H'])) {
  14709. $brTL_H = $this->blk[$blvl]['border_radius_TL_H'];
  14710. $brset = true;
  14711. }
  14712. if (isset($this->blk[$blvl]['border_radius_TL_V'])) {
  14713. $brTL_V = $this->blk[$blvl]['border_radius_TL_V'];
  14714. $brset = true;
  14715. }
  14716. if (isset($this->blk[$blvl]['border_radius_TR_H'])) {
  14717. $brTR_H = $this->blk[$blvl]['border_radius_TR_H'];
  14718. $brset = true;
  14719. }
  14720. if (isset($this->blk[$blvl]['border_radius_TR_V'])) {
  14721. $brTR_V = $this->blk[$blvl]['border_radius_TR_V'];
  14722. $brset = true;
  14723. }
  14724. if (isset($this->blk[$blvl]['border_radius_BR_H'])) {
  14725. $brBR_H = $this->blk[$blvl]['border_radius_BR_H'];
  14726. $brset = true;
  14727. }
  14728. if (isset($this->blk[$blvl]['border_radius_BR_V'])) {
  14729. $brBR_V = $this->blk[$blvl]['border_radius_BR_V'];
  14730. $brset = true;
  14731. }
  14732. if (isset($this->blk[$blvl]['border_radius_BL_H'])) {
  14733. $brBL_H = $this->blk[$blvl]['border_radius_BL_H'];
  14734. $brset = true;
  14735. }
  14736. if (isset($this->blk[$blvl]['border_radius_BL_V'])) {
  14737. $brBL_V = $this->blk[$blvl]['border_radius_BL_V'];
  14738. $brset = true;
  14739. }
  14740. if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {
  14741. $brTL_H = 0;
  14742. $brTL_V = 0;
  14743. $brTR_H = 0;
  14744. $brTR_V = 0;
  14745. }
  14746. if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {
  14747. $brBL_H = 0;
  14748. $brBL_V = 0;
  14749. $brBR_H = 0;
  14750. $brBR_V = 0;
  14751. }
  14752. // Disallow border-radius if it is smaller than the border width.
  14753. if ($brTL_H < min($border_left, $border_top)) {
  14754. $brTL_H = $brTL_V = 0;
  14755. }
  14756. if ($brTL_V < min($border_left, $border_top)) {
  14757. $brTL_V = $brTL_H = 0;
  14758. }
  14759. if ($brTR_H < min($border_right, $border_top)) {
  14760. $brTR_H = $brTR_V = 0;
  14761. }
  14762. if ($brTR_V < min($border_right, $border_top)) {
  14763. $brTR_V = $brTR_H = 0;
  14764. }
  14765. if ($brBL_H < min($border_left, $border_bottom)) {
  14766. $brBL_H = $brBL_V = 0;
  14767. }
  14768. if ($brBL_V < min($border_left, $border_bottom)) {
  14769. $brBL_V = $brBL_H = 0;
  14770. }
  14771. if ($brBR_H < min($border_right, $border_bottom)) {
  14772. $brBR_H = $brBR_V = 0;
  14773. }
  14774. if ($brBR_V < min($border_right, $border_bottom)) {
  14775. $brBR_V = $brBR_H = 0;
  14776. }
  14777. // CHECK FOR radii that sum to > width or height of div ********
  14778. $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));
  14779. if ($f < 1) {
  14780. $brTL_H *= $f;
  14781. $brTL_V *= $f;
  14782. $brTR_H *= $f;
  14783. $brTR_V *= $f;
  14784. $brBL_H *= $f;
  14785. $brBL_V *= $f;
  14786. $brBR_H *= $f;
  14787. $brBR_V *= $f;
  14788. }
  14789. /* -- END BORDER-RADIUS -- */
  14790. $tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
  14791. for ($l = 0; $l <= $blvl; $l++) {
  14792. if ($this->blk[$l]['bgcolor']) {
  14793. $tbcol = $this->blk[$l]['bgcolorarray'];
  14794. }
  14795. }
  14796. // BORDERS
  14797. if (isset($this->blk[$blvl]['y0']) && $this->blk[$blvl]['y0']) {
  14798. $y0 = $this->blk[$blvl]['y0'];
  14799. }
  14800. $h = $y1 - $y0;
  14801. $w = $this->blk[$blvl]['width'];
  14802. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  14803. $tbd = $this->blk[$blvl]['border_top'];
  14804. $legend = '';
  14805. $legbreakL = 0;
  14806. $legbreakR = 0;
  14807. // BORDER LEGEND
  14808. if (isset($this->blk[$blvl]['border_legend']) && $this->blk[$blvl]['border_legend']) {
  14809. $legend = $this->blk[$blvl]['border_legend']; // Same structure array as textbuffer
  14810. $txt = $legend[0] = ltrim($legend[0]);
  14811. if (!empty($legend[18])) {
  14812. $this->otl->trimOTLdata($legend[18], true, false);
  14813. } // *OTL*
  14814. // Set font, size, style, color
  14815. $this->SetFont($legend[4], $legend[2], $legend[11]);
  14816. if (isset($legend[3]) && $legend[3]) {
  14817. $cor = $legend[3];
  14818. $this->SetTColor($cor);
  14819. }
  14820. $stringWidth = $this->GetStringWidth($txt, true, $legend[18], $legend[8]);
  14821. $save_x = $this->x;
  14822. $save_y = $this->y;
  14823. $save_currentfontfamily = $this->FontFamily;
  14824. $save_currentfontsize = $this->FontSizePt;
  14825. $save_currentfontstyle = $this->FontStyle;
  14826. $this->y = $y0 - $this->FontSize / 2 + $this->blk[$blvl]['border_top']['w'] / 2;
  14827. $this->x = $x0 + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_left']['w'];
  14828. // Set the distance from the border line to the text ? make configurable variable
  14829. $gap = 0.2 * $this->FontSize;
  14830. $legbreakL = $this->x - $gap;
  14831. $legbreakR = $this->x + $stringWidth + $gap;
  14832. $this->magic_reverse_dir($txt, $this->blk[$blvl]['direction'], $legend[18]);
  14833. $fill = '';
  14834. $this->Cell($stringWidth, $this->FontSize, $txt, '', 0, 'C', $fill, '', 0, 0, 0, 'M', $fill, false, $legend[18], $legend[8]);
  14835. // Reset
  14836. $this->x = $save_x;
  14837. $this->y = $save_y;
  14838. $this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);
  14839. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  14840. }
  14841. if (isset($tbd['s']) && $tbd['s']) {
  14842. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  14843. $this->writer->write('q');
  14844. $this->SetLineWidth(0);
  14845. $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
  14846. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
  14847. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
  14848. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
  14849. $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path
  14850. }
  14851. $this->_setBorderLine($tbd);
  14852. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  14853. $legbreakL -= $border_top / 2; // because line cap different
  14854. $legbreakR += $border_top / 2;
  14855. $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'T');
  14856. } /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brTR_V && $brTR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  14857. $this->SetLineJoin(0);
  14858. $this->SetLineCap(0);
  14859. }
  14860. $s = '';
  14861. if ($brTR_H && $brTR_V) {
  14862. $s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_top / 2, $brTR_V - $border_top / 2, 1, 2, true)) . "\n";
  14863. } else { /* -- END BORDER-RADIUS -- */
  14864. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  14865. $s .= (sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14866. } else {
  14867. $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14868. }
  14869. }
  14870. /* -- BORDER-RADIUS -- */
  14871. if ($brTL_H && $brTL_V) {
  14872. if ($legend) {
  14873. if ($legbreakR < ($x0 + $w - $brTR_H)) {
  14874. $s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14875. }
  14876. if ($legbreakL > ($x0 + $brTL_H )) {
  14877. $s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14878. $s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE) . "\n");
  14879. } else {
  14880. $s .= (sprintf('%.3F %.3F m ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14881. }
  14882. } else {
  14883. $s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14884. }
  14885. $s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_top / 2, $brTL_V - $border_top / 2, 2, 1)) . "\n";
  14886. } else {
  14887. /* -- END BORDER-RADIUS -- */
  14888. if ($legend) {
  14889. if ($legbreakR < ($x0 + $w)) {
  14890. $s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14891. }
  14892. if ($legbreakL > ($x0)) {
  14893. $s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14894. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  14895. $s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14896. } else {
  14897. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14898. }
  14899. } elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  14900. $s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14901. } else {
  14902. $s .= (sprintf('%.3F %.3F m ', ($x0 + $border_top / 2) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14903. }
  14904. } elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  14905. $s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14906. } else {
  14907. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
  14908. }
  14909. /* -- BORDER-RADIUS -- */
  14910. }
  14911. /* -- END BORDER-RADIUS -- */
  14912. $s .= 'S' . "\n";
  14913. $this->writer->write($s);
  14914. if ($tbd['style'] == 'double') {
  14915. $this->SetLineWidth($tbd['w'] / 3);
  14916. $this->SetDColor($tbcol);
  14917. $this->writer->write($s);
  14918. }
  14919. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  14920. $this->writer->write('Q');
  14921. }
  14922. // Reset Corners and Dash off
  14923. $this->SetLineWidth(0.1);
  14924. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  14925. $this->SetLineJoin(2);
  14926. $this->SetLineCap(2);
  14927. $this->SetDash();
  14928. }
  14929. }
  14930. // Reinstate line above for dotted line divider when block border crosses a page
  14931. // elseif ($divider == 'pagetop' || $continuingpage) {
  14932. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  14933. $tbd = $this->blk[$blvl]['border_bottom'];
  14934. if (isset($tbd['s']) && $tbd['s']) {
  14935. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  14936. $this->writer->write('q');
  14937. $this->SetLineWidth(0);
  14938. $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
  14939. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
  14940. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
  14941. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
  14942. $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path
  14943. }
  14944. $this->_setBorderLine($tbd);
  14945. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  14946. $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'B');
  14947. } /* -- BORDER-RADIUS -- */ elseif (($brBL_V && $brBL_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  14948. $this->SetLineJoin(0);
  14949. $this->SetLineCap(0);
  14950. }
  14951. $s = '';
  14952. if ($brBL_H && $brBL_V) {
  14953. $s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_bottom / 2, $brBL_V - $border_bottom / 2, 3, 2, true)) . "\n";
  14954. } else { /* -- END BORDER-RADIUS -- */
  14955. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  14956. $s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
  14957. } else {
  14958. $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
  14959. }
  14960. }
  14961. /* -- BORDER-RADIUS -- */
  14962. if ($brBR_H && $brBR_V) {
  14963. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2) - $brBR_H ) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
  14964. $s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_bottom / 2, $brBR_V - $border_bottom / 2, 4, 1)) . "\n";
  14965. } else { /* -- END BORDER-RADIUS -- */
  14966. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  14967. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
  14968. } else {
  14969. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
  14970. }
  14971. }
  14972. $s .= 'S' . "\n";
  14973. $this->writer->write($s);
  14974. if ($tbd['style'] == 'double') {
  14975. $this->SetLineWidth($tbd['w'] / 3);
  14976. $this->SetDColor($tbcol);
  14977. $this->writer->write($s);
  14978. }
  14979. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  14980. $this->writer->write('Q');
  14981. }
  14982. // Reset Corners and Dash off
  14983. $this->SetLineWidth(0.1);
  14984. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  14985. $this->SetLineJoin(2);
  14986. $this->SetLineCap(2);
  14987. $this->SetDash();
  14988. }
  14989. }
  14990. // Reinstate line below for dotted line divider when block border crosses a page
  14991. // elseif ($blockstate == 1 || $divider == 'pagebottom') {
  14992. if ($this->blk[$blvl]['border_left']) {
  14993. $tbd = $this->blk[$blvl]['border_left'];
  14994. if (isset($tbd['s']) && $tbd['s']) {
  14995. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  14996. $this->writer->write('q');
  14997. $this->SetLineWidth(0);
  14998. $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
  14999. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
  15000. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
  15001. $this->writer->write(sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
  15002. $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path
  15003. }
  15004. $this->_setBorderLine($tbd);
  15005. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15006. $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'L');
  15007. } /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brBL_V && $brBL_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  15008. $this->SetLineJoin(0);
  15009. $this->SetLineCap(0);
  15010. }
  15011. $s = '';
  15012. if ($brTL_V && $brTL_H) {
  15013. $s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_left / 2, $brTL_V - $border_left / 2, 2, 2, true)) . "\n";
  15014. } else { /* -- END BORDER-RADIUS -- */
  15015. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  15016. $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)) . "\n";
  15017. } else {
  15018. $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_left / 2))) * Mpdf::SCALE)) . "\n";
  15019. }
  15020. }
  15021. /* -- BORDER-RADIUS -- */
  15022. if ($brBL_V && $brBL_H) {
  15023. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2) - $brBL_V) ) * Mpdf::SCALE)) . "\n";
  15024. $s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_left / 2, $brBL_V - $border_left / 2, 3, 1)) . "\n";
  15025. } else { /* -- END BORDER-RADIUS -- */
  15026. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  15027. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h) ) * Mpdf::SCALE)) . "\n";
  15028. } else {
  15029. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2)) ) * Mpdf::SCALE)) . "\n";
  15030. }
  15031. }
  15032. $s .= 'S' . "\n";
  15033. $this->writer->write($s);
  15034. if ($tbd['style'] == 'double') {
  15035. $this->SetLineWidth($tbd['w'] / 3);
  15036. $this->SetDColor($tbcol);
  15037. $this->writer->write($s);
  15038. }
  15039. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  15040. $this->writer->write('Q');
  15041. }
  15042. // Reset Corners and Dash off
  15043. $this->SetLineWidth(0.1);
  15044. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  15045. $this->SetLineJoin(2);
  15046. $this->SetLineCap(2);
  15047. $this->SetDash();
  15048. }
  15049. }
  15050. if ($this->blk[$blvl]['border_right']) {
  15051. $tbd = $this->blk[$blvl]['border_right'];
  15052. if (isset($tbd['s']) && $tbd['s']) {
  15053. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  15054. $this->writer->write('q');
  15055. $this->SetLineWidth(0);
  15056. $this->writer->write(sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
  15057. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
  15058. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
  15059. $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
  15060. $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path
  15061. }
  15062. $this->_setBorderLine($tbd);
  15063. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15064. $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'R');
  15065. } /* -- BORDER-RADIUS -- */ elseif (($brTR_V && $brTR_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  15066. $this->SetLineJoin(0);
  15067. $this->SetLineCap(0);
  15068. }
  15069. $s = '';
  15070. if ($brBR_V && $brBR_H) {
  15071. $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";
  15072. } else { /* -- END BORDER-RADIUS -- */
  15073. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  15074. $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)) . "\n";
  15075. } else {
  15076. $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_right / 2))) * Mpdf::SCALE)) . "\n";
  15077. }
  15078. }
  15079. /* -- BORDER-RADIUS -- */
  15080. if ($brTR_V && $brTR_H) {
  15081. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2) + $brTR_V) ) * Mpdf::SCALE)) . "\n";
  15082. $s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_right / 2, $brTR_V - $border_right / 2, 1, 1)) . "\n";
  15083. } else { /* -- END BORDER-RADIUS -- */
  15084. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  15085. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0) ) * Mpdf::SCALE)) . "\n";
  15086. } else {
  15087. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2)) ) * Mpdf::SCALE)) . "\n";
  15088. }
  15089. }
  15090. $s .= 'S' . "\n";
  15091. $this->writer->write($s);
  15092. if ($tbd['style'] == 'double') {
  15093. $this->SetLineWidth($tbd['w'] / 3);
  15094. $this->SetDColor($tbcol);
  15095. $this->writer->write($s);
  15096. }
  15097. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  15098. $this->writer->write('Q');
  15099. }
  15100. // Reset Corners and Dash off
  15101. $this->SetLineWidth(0.1);
  15102. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  15103. $this->SetLineJoin(2);
  15104. $this->SetLineCap(2);
  15105. $this->SetDash();
  15106. }
  15107. }
  15108. $this->SetDash();
  15109. $this->y = $save_y;
  15110. // BACKGROUNDS are disabled in columns/kbt/headers - messes up the repositioning in printcolumnbuffer
  15111. if ($this->ColActive || $this->kwt || $this->keep_block_together) {
  15112. return;
  15113. }
  15114. $bgx0 = $x0;
  15115. $bgx1 = $x1;
  15116. $bgy0 = $y0;
  15117. $bgy1 = $y1;
  15118. // Defined br values represent the radius of the outer curve - need to take border-width/2 from each radius for drawing the borders
  15119. if (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'padding-box') {
  15120. $brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w']);
  15121. $brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w']);
  15122. $brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w']);
  15123. $brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w']);
  15124. $brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w']);
  15125. $brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w']);
  15126. $brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w']);
  15127. $brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w']);
  15128. $bgx0 += $this->blk[$blvl]['border_left']['w'];
  15129. $bgx1 -= $this->blk[$blvl]['border_right']['w'];
  15130. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  15131. $bgy0 += $this->blk[$blvl]['border_top']['w'];
  15132. }
  15133. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  15134. $bgy1 -= $this->blk[$blvl]['border_bottom']['w'];
  15135. }
  15136. } elseif (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'content-box') {
  15137. $brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);
  15138. $brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);
  15139. $brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);
  15140. $brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);
  15141. $brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);
  15142. $brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);
  15143. $brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);
  15144. $brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);
  15145. $bgx0 += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
  15146. $bgx1 -= $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right'];
  15147. if (($this->blk[$blvl]['border_top']['w'] || $this->blk[$blvl]['padding_top']) && $divider != 'pagetop' && !$continuingpage) {
  15148. $bgy0 += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
  15149. }
  15150. if (($this->blk[$blvl]['border_bottom']['w'] || $this->blk[$blvl]['padding_bottom']) && $blockstate != 1 && $divider != 'pagebottom') {
  15151. $bgy1 -= $this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom'];
  15152. }
  15153. } else {
  15154. $brbgTL_H = $brTL_H;
  15155. $brbgTL_V = $brTL_V;
  15156. $brbgTR_H = $brTR_H;
  15157. $brbgTR_V = $brTR_V;
  15158. $brbgBL_H = $brBL_H;
  15159. $brbgBL_V = $brBL_V;
  15160. $brbgBR_H = $brBR_H;
  15161. $brbgBR_V = $brBR_V;
  15162. }
  15163. // Set clipping path
  15164. $s = ' q 0 w '; // Line width=0
  15165. $s .= sprintf('%.3F %.3F m ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // start point TL before the arc
  15166. /* -- BORDER-RADIUS -- */
  15167. if ($brbgTL_H || $brbgTL_V) {
  15168. $s .= $this->_EllipseArc($bgx0 + $brbgTL_H, $bgy0 + $brbgTL_V, $brbgTL_H, $brbgTL_V, 2); // segment 2 TL
  15169. }
  15170. /* -- END BORDER-RADIUS -- */
  15171. $s .= sprintf('%.3F %.3F l ', ($bgx0) * Mpdf::SCALE, ($this->h - ($bgy1 - $brbgBL_V )) * Mpdf::SCALE); // line to BL
  15172. /* -- BORDER-RADIUS -- */
  15173. if ($brbgBL_H || $brbgBL_V) {
  15174. $s .= $this->_EllipseArc($bgx0 + $brbgBL_H, $bgy1 - $brbgBL_V, $brbgBL_H, $brbgBL_V, 3); // segment 3 BL
  15175. }
  15176. /* -- END BORDER-RADIUS -- */
  15177. $s .= sprintf('%.3F %.3F l ', ($bgx1 - $brbgBR_H ) * Mpdf::SCALE, ($this->h - ($bgy1)) * Mpdf::SCALE); // line to BR
  15178. /* -- BORDER-RADIUS -- */
  15179. if ($brbgBR_H || $brbgBR_V) {
  15180. $s .= $this->_EllipseArc($bgx1 - $brbgBR_H, $bgy1 - $brbgBR_V, $brbgBR_H, $brbgBR_V, 4); // segment 4 BR
  15181. }
  15182. /* -- END BORDER-RADIUS -- */
  15183. $s .= sprintf('%.3F %.3F l ', ($bgx1) * Mpdf::SCALE, ($this->h - ($bgy0 + $brbgTR_V)) * Mpdf::SCALE); // line to TR
  15184. /* -- BORDER-RADIUS -- */
  15185. if ($brbgTR_H || $brbgTR_V) {
  15186. $s .= $this->_EllipseArc($bgx1 - $brbgTR_H, $bgy0 + $brbgTR_V, $brbgTR_H, $brbgTR_V, 1); // segment 1 TR
  15187. }
  15188. /* -- END BORDER-RADIUS -- */
  15189. $s .= sprintf('%.3F %.3F l ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // line to TL
  15190. // Box Shadow
  15191. $shadow = '';
  15192. if (isset($this->blk[$blvl]['box_shadow']) && $this->blk[$blvl]['box_shadow'] && $h > 0) {
  15193. foreach ($this->blk[$blvl]['box_shadow'] as $sh) {
  15194. // Colors
  15195. if ($sh['col'][0] == 1) {
  15196. $colspace = 'Gray';
  15197. if ($sh['col'][2] == 1) {
  15198. $col1 = '1' . $sh['col'][1] . '1' . $sh['col'][3];
  15199. } else {
  15200. $col1 = '1' . $sh['col'][1] . '1' . chr(100);
  15201. }
  15202. $col2 = '1' . $sh['col'][1] . '1' . chr(0);
  15203. } elseif ($sh['col'][0] == 4) { // CMYK
  15204. $colspace = 'CMYK';
  15205. $col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(100);
  15206. $col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);
  15207. } elseif ($sh['col'][0] == 5) { // RGBa
  15208. $colspace = 'RGB';
  15209. $col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4];
  15210. $col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);
  15211. } elseif ($sh['col'][0] == 6) { // CMYKa
  15212. $colspace = 'CMYK';
  15213. $col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . $sh['col'][5];
  15214. $col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);
  15215. } else {
  15216. $colspace = 'RGB';
  15217. $col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(100);
  15218. $col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);
  15219. }
  15220. // Use clipping path as set above (and rectangle around page) to clip area outside box
  15221. $shadow .= $s; // Use the clipping path with W*
  15222. $shadow .= sprintf('0 %.3F m %.3F %.3F l ', $this->h * Mpdf::SCALE, $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE);
  15223. $shadow .= sprintf('%.3F 0 l 0 0 l 0 %.3F l ', $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE);
  15224. $shadow .= 'W n' . "\n";
  15225. $sh['blur'] = abs($sh['blur']); // cannot have negative blur value
  15226. // Ensure spread/blur do not make effective shadow width/height < 0
  15227. // Could do more complex things but this just adjusts spread value
  15228. if (-$sh['spread'] + $sh['blur'] / 2 > min($w / 2, $h / 2)) {
  15229. $sh['spread'] = $sh['blur'] / 2 - min($w / 2, $h / 2) + 0.01;
  15230. }
  15231. // Shadow Offset
  15232. if ($sh['x'] || $sh['y']) {
  15233. $shadow .= sprintf(' q 1 0 0 1 %.4F %.4F cm', $sh['x'] * Mpdf::SCALE, -$sh['y'] * Mpdf::SCALE) . "\n";
  15234. }
  15235. // Set path for INNER shadow
  15236. $shadow .= ' q 0 w ';
  15237. $shadow .= $this->SetFColor($col1, true) . "\n";
  15238. if ($col1[0] == 5 && ord($col1[4]) < 100) { // RGBa
  15239. $shadow .= $this->SetAlpha(ord($col1[4]) / 100, 'Normal', true, 'F') . "\n";
  15240. } elseif ($col1[0] == 6 && ord($col1[5]) < 100) { // CMYKa
  15241. $shadow .= $this->SetAlpha(ord($col1[5]) / 100, 'Normal', true, 'F') . "\n";
  15242. } elseif ($col1[0] == 1 && $col1[2] == 1 && ord($col1[3]) < 100) { // Gray
  15243. $shadow .= $this->SetAlpha(ord($col1[3]) / 100, 'Normal', true, 'F') . "\n";
  15244. }
  15245. // Blur edges
  15246. $mag = 0.551784; // Bezier Control magic number for 4-part spline for circle/ellipse
  15247. $mag2 = 0.551784; // Bezier Control magic number to fill in edge of blurred rectangle
  15248. $d1 = $sh['spread'] + $sh['blur'] / 2;
  15249. $d2 = $sh['spread'] - $sh['blur'] / 2;
  15250. $bl = $sh['blur'];
  15251. $x00 = $x0 - $d1;
  15252. $y00 = $y0 - $d1;
  15253. $w00 = $w + $d1 * 2;
  15254. $h00 = $h + $d1 * 2;
  15255. // If any border-radius is greater width-negative spread(inner edge), ignore radii for shadow or screws up
  15256. $flatten = false;
  15257. if (max($brbgTR_H, $brbgTL_H, $brbgBR_H, $brbgBL_H) >= $w + $d2) {
  15258. $flatten = true;
  15259. }
  15260. if (max($brbgTR_V, $brbgTL_V, $brbgBR_V, $brbgBL_V) >= $h + $d2) {
  15261. $flatten = true;
  15262. }
  15263. // TOP RIGHT corner
  15264. $p1x = $x00 + $w00 - $d1 - $brbgTR_H;
  15265. $p1c2x = $p1x + ($d2 + $brbgTR_H) * $mag;
  15266. $p1y = $y00 + $bl;
  15267. $p2x = $x00 + $w00 - $d1 - $brbgTR_H;
  15268. $p2c2x = $p2x + ($d1 + $brbgTR_H) * $mag;
  15269. $p2y = $y00;
  15270. $p2c1y = $p2y + $bl / 2;
  15271. $p3x = $x00 + $w00;
  15272. $p3c2x = $p3x - $bl / 2;
  15273. $p3y = $y00 + $d1 + $brbgTR_V;
  15274. $p3c1y = $p3y - ($d1 + $brbgTR_V) * $mag;
  15275. $p4x = $x00 + $w00 - $bl;
  15276. $p4y = $y00 + $d1 + $brbgTR_V;
  15277. $p4c2y = $p4y - ($d2 + $brbgTR_V) * $mag;
  15278. if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
  15279. $p1x = $x00 + $w00 - $bl;
  15280. $p1c2x = $p1x;
  15281. $p2x = $x00 + $w00 - $bl;
  15282. $p2c2x = $p2x + $bl * $mag2;
  15283. $p3y = $y00 + $bl;
  15284. $p3c1y = $p3y - $bl * $mag2;
  15285. $p4y = $y00 + $bl;
  15286. $p4c2y = $p4y;
  15287. }
  15288. $shadow .= sprintf('%.3F %.3F m ', ($p1x ) * Mpdf::SCALE, ($this->h - ($p1y )) * Mpdf::SCALE);
  15289. $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);
  15290. $patch_array[0]['f'] = 0;
  15291. $patch_array[0]['points'] = [$p1x, $p1y, $p1x, $p1y,
  15292. $p2x, $p2c1y, $p2x, $p2y, $p2c2x, $p2y,
  15293. $p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,
  15294. $p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,
  15295. $p1c2x, $p1y];
  15296. $patch_array[0]['colors'] = [$col1, $col2, $col2, $col1];
  15297. // RIGHT
  15298. $p1x = $x00 + $w00; // control point only matches p3 preceding
  15299. $p1y = $y00 + $d1 + $brbgTR_V;
  15300. $p2x = $x00 + $w00 - $bl; // control point only matches p4 preceding
  15301. $p2y = $y00 + $d1 + $brbgTR_V;
  15302. $p3x = $x00 + $w00 - $bl;
  15303. $p3y = $y00 + $h00 - $d1 - $brbgBR_V;
  15304. $p4x = $x00 + $w00;
  15305. $p4c1x = $p4x - $bl / 2;
  15306. $p4y = $y00 + $h00 - $d1 - $brbgBR_V;
  15307. if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
  15308. $p1y = $y00 + $bl;
  15309. $p2y = $y00 + $bl;
  15310. }
  15311. if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
  15312. $p3y = $y00 + $h00 - $bl;
  15313. $p4y = $y00 + $h00 - $bl;
  15314. }
  15315. $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
  15316. $patch_array[1]['f'] = 2;
  15317. $patch_array[1]['points'] = [$p2x, $p2y,
  15318. $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
  15319. $p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,
  15320. $p1x, $p1y];
  15321. $patch_array[1]['colors'] = [$col1, $col2];
  15322. // BOTTOM RIGHT corner
  15323. $p1x = $x00 + $w00 - $bl; // control points only matches p3 preceding
  15324. $p1y = $y00 + $h00 - $d1 - $brbgBR_V;
  15325. $p1c2y = $p1y + ($d2 + $brbgBR_V) * $mag;
  15326. $p2x = $x00 + $w00; // control point only matches p4 preceding
  15327. $p2y = $y00 + $h00 - $d1 - $brbgBR_V;
  15328. $p2c2y = $p2y + ($d1 + $brbgBR_V) * $mag;
  15329. $p3x = $x00 + $w00 - $d1 - $brbgBR_H;
  15330. $p3c1x = $p3x + ($d1 + $brbgBR_H) * $mag;
  15331. $p3y = $y00 + $h00;
  15332. $p3c2y = $p3y - $bl / 2;
  15333. $p4x = $x00 + $w00 - $d1 - $brbgBR_H;
  15334. $p4c2x = $p4x + ($d2 + $brbgBR_H) * $mag;
  15335. $p4y = $y00 + $h00 - $bl;
  15336. if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
  15337. $p1y = $y00 + $h00 - $bl;
  15338. $p1c2y = $p1y;
  15339. $p2y = $y00 + $h00 - $bl;
  15340. $p2c2y = $p2y + $bl * $mag2;
  15341. $p3x = $x00 + $w00 - $bl;
  15342. $p3c1x = $p3x + $bl * $mag2;
  15343. $p4x = $x00 + $w00 - $bl;
  15344. $p4c2x = $p4x;
  15345. }
  15346. $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);
  15347. $patch_array[2]['f'] = 2;
  15348. $patch_array[2]['points'] = [$p2x, $p2c2y,
  15349. $p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,
  15350. $p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,
  15351. $p1x, $p1c2y];
  15352. $patch_array[2]['colors'] = [$col2, $col1];
  15353. // BOTTOM
  15354. $p1x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p3 preceding
  15355. $p1y = $y00 + $h00;
  15356. $p2x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p4 preceding
  15357. $p2y = $y00 + $h00 - $bl;
  15358. $p3x = $x00 + $d1 + $brbgBL_H;
  15359. $p3y = $y00 + $h00 - $bl;
  15360. $p4x = $x00 + $d1 + $brbgBL_H;
  15361. $p4y = $y00 + $h00;
  15362. $p4c1y = $p4y - $bl / 2;
  15363. if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
  15364. $p1x = $x00 + $w00 - $bl;
  15365. $p2x = $x00 + $w00 - $bl;
  15366. }
  15367. if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
  15368. $p3x = $x00 + $bl;
  15369. $p4x = $x00 + $bl;
  15370. }
  15371. $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
  15372. $patch_array[3]['f'] = 2;
  15373. $patch_array[3]['points'] = [$p2x, $p2y,
  15374. $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
  15375. $p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,
  15376. $p1x, $p1y];
  15377. $patch_array[3]['colors'] = [$col1, $col2];
  15378. // BOTTOM LEFT corner
  15379. $p1x = $x00 + $d1 + $brbgBL_H;
  15380. $p1c2x = $p1x - ($d2 + $brbgBL_H) * $mag; // control points only matches p3 preceding
  15381. $p1y = $y00 + $h00 - $bl;
  15382. $p2x = $x00 + $d1 + $brbgBL_H;
  15383. $p2c2x = $p2x - ($d1 + $brbgBL_H) * $mag; // control point only matches p4 preceding
  15384. $p2y = $y00 + $h00;
  15385. $p3x = $x00;
  15386. $p3c2x = $p3x + $bl / 2;
  15387. $p3y = $y00 + $h00 - $d1 - $brbgBL_V;
  15388. $p3c1y = $p3y + ($d1 + $brbgBL_V) * $mag;
  15389. $p4x = $x00 + $bl;
  15390. $p4y = $y00 + $h00 - $d1 - $brbgBL_V;
  15391. $p4c2y = $p4y + ($d2 + $brbgBL_V) * $mag;
  15392. if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
  15393. $p1x = $x00 + $bl;
  15394. $p1c2x = $p1x;
  15395. $p2x = $x00 + $bl;
  15396. $p2c2x = $p2x - $bl * $mag2;
  15397. $p3y = $y00 + $h00 - $bl;
  15398. $p3c1y = $p3y + $bl * $mag2;
  15399. $p4y = $y00 + $h00 - $bl;
  15400. $p4c2y = $p4y;
  15401. }
  15402. $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);
  15403. $patch_array[4]['f'] = 2;
  15404. $patch_array[4]['points'] = [$p2c2x, $p2y,
  15405. $p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,
  15406. $p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,
  15407. $p1c2x, $p1y];
  15408. $patch_array[4]['colors'] = [$col2, $col1];
  15409. // LEFT - joins on the right (C3-C4 of previous): f = 2
  15410. $p1x = $x00; // control point only matches p3 preceding
  15411. $p1y = $y00 + $h00 - $d1 - $brbgBL_V;
  15412. $p2x = $x00 + $bl; // control point only matches p4 preceding
  15413. $p2y = $y00 + $h00 - $d1 - $brbgBL_V;
  15414. $p3x = $x00 + $bl;
  15415. $p3y = $y00 + $d1 + $brbgTL_V;
  15416. $p4x = $x00;
  15417. $p4c1x = $p4x + $bl / 2;
  15418. $p4y = $y00 + $d1 + $brbgTL_V;
  15419. if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
  15420. $p1y = $y00 + $h00 - $bl;
  15421. $p2y = $y00 + $h00 - $bl;
  15422. }
  15423. if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
  15424. $p3y = $y00 + $bl;
  15425. $p4y = $y00 + $bl;
  15426. }
  15427. $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
  15428. $patch_array[5]['f'] = 2;
  15429. $patch_array[5]['points'] = [$p2x, $p2y,
  15430. $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
  15431. $p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,
  15432. $p1x, $p1y];
  15433. $patch_array[5]['colors'] = [$col1, $col2];
  15434. // TOP LEFT corner
  15435. $p1x = $x00 + $bl; // control points only matches p3 preceding
  15436. $p1y = $y00 + $d1 + $brbgTL_V;
  15437. $p1c2y = $p1y - ($d2 + $brbgTL_V) * $mag;
  15438. $p2x = $x00; // control point only matches p4 preceding
  15439. $p2y = $y00 + $d1 + $brbgTL_V;
  15440. $p2c2y = $p2y - ($d1 + $brbgTL_V) * $mag;
  15441. $p3x = $x00 + $d1 + $brbgTL_H;
  15442. $p3c1x = $p3x - ($d1 + $brbgTL_H) * $mag;
  15443. $p3y = $y00;
  15444. $p3c2y = $p3y + $bl / 2;
  15445. $p4x = $x00 + $d1 + $brbgTL_H;
  15446. $p4c2x = $p4x - ($d2 + $brbgTL_H) * $mag;
  15447. $p4y = $y00 + $bl;
  15448. if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
  15449. $p1y = $y00 + $bl;
  15450. $p1c2y = $p1y;
  15451. $p2y = $y00 + $bl;
  15452. $p2c2y = $p2y - $bl * $mag2;
  15453. $p3x = $x00 + $bl;
  15454. $p3c1x = $p3x - $bl * $mag2;
  15455. $p4x = $x00 + $bl;
  15456. $p4c2x = $p4x;
  15457. }
  15458. $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);
  15459. $patch_array[6]['f'] = 2;
  15460. $patch_array[6]['points'] = [$p2x, $p2c2y,
  15461. $p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,
  15462. $p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,
  15463. $p1x, $p1c2y];
  15464. $patch_array[6]['colors'] = [$col2, $col1];
  15465. // TOP - joins on the right (C3-C4 of previous): f = 2
  15466. $p1x = $x00 + $d1 + $brbgTL_H; // control point only matches p3 preceding
  15467. $p1y = $y00;
  15468. $p2x = $x00 + $d1 + $brbgTL_H; // control point only matches p4 preceding
  15469. $p2y = $y00 + $bl;
  15470. $p3x = $x00 + $w00 - $d1 - $brbgTR_H;
  15471. $p3y = $y00 + $bl;
  15472. $p4x = $x00 + $w00 - $d1 - $brbgTR_H;
  15473. $p4y = $y00;
  15474. $p4c1y = $p4y + $bl / 2;
  15475. if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
  15476. $p1x = $x00 + $bl;
  15477. $p2x = $x00 + $bl;
  15478. }
  15479. if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
  15480. $p3x = $x00 + $w00 - $bl;
  15481. $p4x = $x00 + $w00 - $bl;
  15482. }
  15483. $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
  15484. $patch_array[7]['f'] = 2;
  15485. $patch_array[7]['points'] = [$p2x, $p2y,
  15486. $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
  15487. $p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,
  15488. $p1x, $p1y];
  15489. $patch_array[7]['colors'] = [$col1, $col2];
  15490. $shadow .= ' h f Q ' . "\n"; // Close path and Fill the inner solid shadow
  15491. if ($bl) {
  15492. $shadow .= $this->gradient->CoonsPatchMesh($x00, $y00, $w00, $h00, $patch_array, $x00, $x00 + $w00, $y00, $y00 + $h00, $colspace, true);
  15493. }
  15494. if ($sh['x'] || $sh['y']) {
  15495. $shadow .= ' Q' . "\n"; // Shadow Offset
  15496. }
  15497. $shadow .= ' Q' . "\n"; // Ends path no-op & Sets the clipping path
  15498. }
  15499. }
  15500. $s .= ' W n '; // Ends path no-op & Sets the clipping path
  15501. if ($this->blk[$blvl]['bgcolor']) {
  15502. $this->pageBackgrounds[$blvl][] = [
  15503. 'x' => $x0,
  15504. 'y' => $y0,
  15505. 'w' => $w,
  15506. 'h' => $h,
  15507. 'col' => $this->blk[$blvl]['bgcolorarray'],
  15508. 'clippath' => $s,
  15509. 'visibility' => $this->visibility,
  15510. 'shadow' => $shadow,
  15511. 'z-index' => $this->current_layer,
  15512. ];
  15513. } elseif ($shadow) {
  15514. $this->pageBackgrounds[$blvl][] = [
  15515. 'x' => 0,
  15516. 'y' => 0,
  15517. 'w' => 0,
  15518. 'h' => 0,
  15519. 'shadowonly' => true,
  15520. 'col' => '',
  15521. 'clippath' => '',
  15522. 'visibility' => $this->visibility,
  15523. 'shadow' => $shadow,
  15524. 'z-index' => $this->current_layer,
  15525. ];
  15526. }
  15527. /* -- BACKGROUNDS -- */
  15528. if (isset($this->blk[$blvl]['gradient'])) {
  15529. $g = $this->gradient->parseBackgroundGradient($this->blk[$blvl]['gradient']);
  15530. if ($g) {
  15531. $gx = $x0;
  15532. $gy = $y0;
  15533. $this->pageBackgrounds[$blvl][] = [
  15534. 'gradient' => true,
  15535. 'x' => $gx,
  15536. 'y' => $gy,
  15537. 'w' => $w,
  15538. 'h' => $h,
  15539. 'gradtype' => $g['type'],
  15540. 'stops' => $g['stops'],
  15541. 'colorspace' => $g['colorspace'],
  15542. 'coords' => $g['coords'],
  15543. 'extend' => $g['extend'],
  15544. 'clippath' => $s,
  15545. 'visibility' => $this->visibility,
  15546. 'z-index' => $this->current_layer
  15547. ];
  15548. }
  15549. }
  15550. if (isset($this->blk[$blvl]['background-image'])) {
  15551. 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'])) {
  15552. $g = $this->gradient->parseMozGradient($this->blk[$blvl]['background-image']['gradient']);
  15553. if ($g) {
  15554. $gx = $x0;
  15555. $gy = $y0;
  15556. // origin specifies the background-positioning-area (bpa)
  15557. if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {
  15558. $gx += $this->blk[$blvl]['border_left']['w'];
  15559. $w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);
  15560. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  15561. $gy += $this->blk[$blvl]['border_top']['w'];
  15562. }
  15563. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  15564. $gy1 = $y1 - $this->blk[$blvl]['border_bottom']['w'];
  15565. } else {
  15566. $gy1 = $y1;
  15567. }
  15568. $h = $gy1 - $gy;
  15569. } elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {
  15570. $gx += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
  15571. $w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']);
  15572. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  15573. $gy += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
  15574. }
  15575. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  15576. $gy1 = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);
  15577. } else {
  15578. $gy1 = $y1 - $this->blk[$blvl]['padding_bottom'];
  15579. }
  15580. $h = $gy1 - $gy;
  15581. }
  15582. if (isset($this->blk[$blvl]['background-image']['size']['w']) && $this->blk[$blvl]['background-image']['size']['w']) {
  15583. $size = $this->blk[$blvl]['background-image']['size'];
  15584. if ($size['w'] != 'contain' && $size['w'] != 'cover') {
  15585. if (stristr($size['w'], '%')) {
  15586. $size['w'] = (float) $size['w'];
  15587. $size['w'] /= 100;
  15588. $w *= $size['w'];
  15589. } elseif ($size['w'] != 'auto') {
  15590. $w = $size['w'];
  15591. }
  15592. if (stristr($size['h'], '%')) {
  15593. $size['h'] = (float) $size['h'];
  15594. $size['h'] /= 100;
  15595. $h *= $size['h'];
  15596. } elseif ($size['h'] != 'auto') {
  15597. $h = $size['h'];
  15598. }
  15599. }
  15600. }
  15601. $this->pageBackgrounds[$blvl][] = [
  15602. 'gradient' => true,
  15603. 'x' => $gx,
  15604. 'y' => $gy,
  15605. 'w' => $w,
  15606. 'h' => $h,
  15607. 'gradtype' => $g['type'],
  15608. 'stops' => $g['stops'],
  15609. 'colorspace' => $g['colorspace'],
  15610. 'coords' => $g['coords'],
  15611. 'extend' => $g['extend'],
  15612. 'clippath' => $s,
  15613. 'visibility' => $this->visibility,
  15614. 'z-index' => $this->current_layer
  15615. ];
  15616. }
  15617. } else {
  15618. $image_id = $this->blk[$blvl]['background-image']['image_id'];
  15619. $orig_w = $this->blk[$blvl]['background-image']['orig_w'];
  15620. $orig_h = $this->blk[$blvl]['background-image']['orig_h'];
  15621. $x_pos = $this->blk[$blvl]['background-image']['x_pos'];
  15622. $y_pos = $this->blk[$blvl]['background-image']['y_pos'];
  15623. $x_repeat = $this->blk[$blvl]['background-image']['x_repeat'];
  15624. $y_repeat = $this->blk[$blvl]['background-image']['y_repeat'];
  15625. $resize = $this->blk[$blvl]['background-image']['resize'];
  15626. $opacity = $this->blk[$blvl]['background-image']['opacity'];
  15627. $itype = $this->blk[$blvl]['background-image']['itype'];
  15628. $size = $this->blk[$blvl]['background-image']['size'];
  15629. // origin specifies the background-positioning-area (bpa)
  15630. $bpa = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h];
  15631. if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {
  15632. $bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'];
  15633. $bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);
  15634. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  15635. $bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'];
  15636. } else {
  15637. $bpa['y'] = $y0;
  15638. }
  15639. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  15640. $bpay = $y1 - $this->blk[$blvl]['border_bottom']['w'];
  15641. } else {
  15642. $bpay = $y1;
  15643. }
  15644. $bpa['h'] = $bpay - $bpa['y'];
  15645. } elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {
  15646. $bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
  15647. $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']);
  15648. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  15649. $bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
  15650. } else {
  15651. $bpa['y'] = $y0 + $this->blk[$blvl]['padding_top'];
  15652. }
  15653. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  15654. $bpay = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);
  15655. } else {
  15656. $bpay = $y1 - $this->blk[$blvl]['padding_bottom'];
  15657. }
  15658. $bpa['h'] = $bpay - $bpa['y'];
  15659. }
  15660. $this->pageBackgrounds[$blvl][] = [
  15661. 'x' => $x0,
  15662. 'y' => $y0,
  15663. 'w' => $w,
  15664. 'h' => $h,
  15665. 'image_id' => $image_id,
  15666. 'orig_w' => $orig_w,
  15667. 'orig_h' => $orig_h,
  15668. 'x_pos' => $x_pos,
  15669. 'y_pos' => $y_pos,
  15670. 'x_repeat' => $x_repeat,
  15671. 'y_repeat' => $y_repeat,
  15672. 'clippath' => $s,
  15673. 'resize' => $resize,
  15674. 'opacity' => $opacity,
  15675. 'itype' => $itype,
  15676. 'visibility' => $this->visibility,
  15677. 'z-index' => $this->current_layer,
  15678. 'size' => $size,
  15679. 'bpa' => $bpa
  15680. ];
  15681. }
  15682. }
  15683. /* -- END BACKGROUNDS -- */
  15684. // Float DIV
  15685. $this->blk[$blvl]['bb_painted'][$this->page] = true;
  15686. }
  15687. /* -- BORDER-RADIUS -- */
  15688. function _EllipseArc($x0, $y0, $rx, $ry, $seg = 1, $part = false, $start = false)
  15689. {
  15690. // Anticlockwise segment 1-4 TR-TL-BL-BR (part=1 or 2)
  15691. $s = '';
  15692. if ($rx < 0) {
  15693. $rx = 0;
  15694. }
  15695. if ($ry < 0) {
  15696. $ry = 0;
  15697. }
  15698. $rx *= Mpdf::SCALE;
  15699. $ry *= Mpdf::SCALE;
  15700. $astart = 0;
  15701. if ($seg == 1) { // Top Right
  15702. $afinish = 90;
  15703. $nSeg = 4;
  15704. } elseif ($seg == 2) { // Top Left
  15705. $afinish = 180;
  15706. $nSeg = 8;
  15707. } elseif ($seg == 3) { // Bottom Left
  15708. $afinish = 270;
  15709. $nSeg = 12;
  15710. } else { // Bottom Right
  15711. $afinish = 360;
  15712. $nSeg = 16;
  15713. }
  15714. $astart = deg2rad((float) $astart);
  15715. $afinish = deg2rad((float) $afinish);
  15716. $totalAngle = $afinish - $astart;
  15717. $dt = $totalAngle / $nSeg; // segment angle
  15718. $dtm = $dt / 3;
  15719. $x0 *= Mpdf::SCALE;
  15720. $y0 = ($this->h - $y0) * Mpdf::SCALE;
  15721. $t1 = $astart;
  15722. $a0 = $x0 + ($rx * cos($t1));
  15723. $b0 = $y0 + ($ry * sin($t1));
  15724. $c0 = -$rx * sin($t1);
  15725. $d0 = $ry * cos($t1);
  15726. $op = false;
  15727. for ($i = 1; $i <= $nSeg; $i++) {
  15728. // Draw this bit of the total curve
  15729. $t1 = ($i * $dt) + $astart;
  15730. $a1 = $x0 + ($rx * cos($t1));
  15731. $b1 = $y0 + ($ry * sin($t1));
  15732. $c1 = -$rx * sin($t1);
  15733. $d1 = $ry * cos($t1);
  15734. if ($i > ($nSeg - 4) && (!$part || ($part == 1 && $i <= $nSeg - 2) || ($part == 2 && $i > $nSeg - 2))) {
  15735. if ($start && !$op) {
  15736. $s .= sprintf('%.3F %.3F m ', $a0, $b0);
  15737. }
  15738. $s .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($a0 + ($c0 * $dtm)), ($b0 + ($d0 * $dtm)), ($a1 - ($c1 * $dtm)), ($b1 - ($d1 * $dtm)), $a1, $b1);
  15739. $op = true;
  15740. }
  15741. $a0 = $a1;
  15742. $b0 = $b1;
  15743. $c0 = $c1;
  15744. $d0 = $d1;
  15745. }
  15746. return $s;
  15747. }
  15748. /* -- END BORDER-RADIUS -- */
  15749. function PaintDivLnBorder($state = 0, $blvl = 0, $h = 0)
  15750. {
  15751. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  15752. $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
  15753. $save_y = $this->y;
  15754. $w = $this->blk[$blvl]['width'];
  15755. $x0 = $this->x; // left
  15756. $y0 = $this->y; // top
  15757. $x1 = $this->x + $w; // bottom
  15758. $y1 = $this->y + $h; // bottom
  15759. $continuingpage = (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page);
  15760. if ($this->blk[$blvl]['border_top'] && ($state == 1 || $state == 3)) {
  15761. $tbd = $this->blk[$blvl]['border_top'];
  15762. if (isset($tbd['s']) && $tbd['s']) {
  15763. $this->_setBorderLine($tbd);
  15764. $this->y = $y0 + ($tbd['w'] / 2);
  15765. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15766. $this->_setDashBorder($tbd['style'], '', $continuingpage, 'T');
  15767. $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);
  15768. } else {
  15769. $this->SetLineJoin(0);
  15770. $this->SetLineCap(0);
  15771. $this->Line($x0, $this->y, $x0 + $w, $this->y);
  15772. }
  15773. $this->y += $tbd['w'];
  15774. // Reset Corners and Dash off
  15775. $this->SetLineJoin(2);
  15776. $this->SetLineCap(2);
  15777. $this->SetDash();
  15778. }
  15779. }
  15780. if ($this->blk[$blvl]['border_left']) {
  15781. $tbd = $this->blk[$blvl]['border_left'];
  15782. if (isset($tbd['s']) && $tbd['s']) {
  15783. $this->_setBorderLine($tbd);
  15784. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15785. $this->y = $y0 + ($tbd['w'] / 2);
  15786. $this->_setDashBorder($tbd['style'], '', $continuingpage, 'L');
  15787. $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));
  15788. } else {
  15789. $this->y = $y0;
  15790. $this->SetLineJoin(0);
  15791. $this->SetLineCap(0);
  15792. $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h);
  15793. }
  15794. $this->y += $tbd['w'];
  15795. // Reset Corners and Dash off
  15796. $this->SetLineJoin(2);
  15797. $this->SetLineCap(2);
  15798. $this->SetDash();
  15799. }
  15800. }
  15801. if ($this->blk[$blvl]['border_right']) {
  15802. $tbd = $this->blk[$blvl]['border_right'];
  15803. if (isset($tbd['s']) && $tbd['s']) {
  15804. $this->_setBorderLine($tbd);
  15805. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15806. $this->y = $y0 + ($tbd['w'] / 2);
  15807. $this->_setDashBorder($tbd['style'], '', $continuingpage, 'R');
  15808. $this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));
  15809. } else {
  15810. $this->y = $y0;
  15811. $this->SetLineJoin(0);
  15812. $this->SetLineCap(0);
  15813. $this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h);
  15814. }
  15815. $this->y += $tbd['w'];
  15816. // Reset Corners and Dash off
  15817. $this->SetLineJoin(2);
  15818. $this->SetLineCap(2);
  15819. $this->SetDash();
  15820. }
  15821. }
  15822. if ($this->blk[$blvl]['border_bottom'] && $state > 1) {
  15823. $tbd = $this->blk[$blvl]['border_bottom'];
  15824. if (isset($tbd['s']) && $tbd['s']) {
  15825. $this->_setBorderLine($tbd);
  15826. $this->y = $y0 + $h - ($tbd['w'] / 2);
  15827. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15828. $this->_setDashBorder($tbd['style'], '', $continuingpage, 'B');
  15829. $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);
  15830. } else {
  15831. $this->SetLineJoin(0);
  15832. $this->SetLineCap(0);
  15833. $this->Line($x0, $this->y, $x0 + $w, $this->y);
  15834. }
  15835. $this->y += $tbd['w'];
  15836. // Reset Corners and Dash off
  15837. $this->SetLineJoin(2);
  15838. $this->SetLineCap(2);
  15839. $this->SetDash();
  15840. }
  15841. }
  15842. $this->SetDash();
  15843. $this->y = $save_y;
  15844. }
  15845. function PaintImgBorder($objattr, $is_table)
  15846. {
  15847. // Borders are disabled in columns - messes up the repositioning in printcolumnbuffer
  15848. if ($this->ColActive) {
  15849. return;
  15850. } // *COLUMNS*
  15851. if ($is_table) {
  15852. $k = $this->shrin_k;
  15853. } else {
  15854. $k = 1;
  15855. }
  15856. $h = (isset($objattr['BORDER-HEIGHT']) ? $objattr['BORDER-HEIGHT'] : 0);
  15857. $w = (isset($objattr['BORDER-WIDTH']) ? $objattr['BORDER-WIDTH'] : 0);
  15858. $x0 = (isset($objattr['BORDER-X']) ? $objattr['BORDER-X'] : 0);
  15859. $y0 = (isset($objattr['BORDER-Y']) ? $objattr['BORDER-Y'] : 0);
  15860. // BORDERS
  15861. if ($objattr['border_top']) {
  15862. $tbd = $objattr['border_top'];
  15863. if (!empty($tbd['s'])) {
  15864. $this->_setBorderLine($tbd, $k);
  15865. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15866. $this->_setDashBorder($tbd['style'], '', '', 'T');
  15867. }
  15868. $this->Line($x0, $y0, $x0 + $w, $y0);
  15869. // Reset Corners and Dash off
  15870. $this->SetLineJoin(2);
  15871. $this->SetLineCap(2);
  15872. $this->SetDash();
  15873. }
  15874. }
  15875. if ($objattr['border_left']) {
  15876. $tbd = $objattr['border_left'];
  15877. if (!empty($tbd['s'])) {
  15878. $this->_setBorderLine($tbd, $k);
  15879. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15880. $this->_setDashBorder($tbd['style'], '', '', 'L');
  15881. }
  15882. $this->Line($x0, $y0, $x0, $y0 + $h);
  15883. // Reset Corners and Dash off
  15884. $this->SetLineJoin(2);
  15885. $this->SetLineCap(2);
  15886. $this->SetDash();
  15887. }
  15888. }
  15889. if ($objattr['border_right']) {
  15890. $tbd = $objattr['border_right'];
  15891. if (!empty($tbd['s'])) {
  15892. $this->_setBorderLine($tbd, $k);
  15893. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15894. $this->_setDashBorder($tbd['style'], '', '', 'R');
  15895. }
  15896. $this->Line($x0 + $w, $y0, $x0 + $w, $y0 + $h);
  15897. // Reset Corners and Dash off
  15898. $this->SetLineJoin(2);
  15899. $this->SetLineCap(2);
  15900. $this->SetDash();
  15901. }
  15902. }
  15903. if ($objattr['border_bottom']) {
  15904. $tbd = $objattr['border_bottom'];
  15905. if (!empty($tbd['s'])) {
  15906. $this->_setBorderLine($tbd, $k);
  15907. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  15908. $this->_setDashBorder($tbd['style'], '', '', 'B');
  15909. }
  15910. $this->Line($x0, $y0 + $h, $x0 + $w, $y0 + $h);
  15911. // Reset Corners and Dash off
  15912. $this->SetLineJoin(2);
  15913. $this->SetLineCap(2);
  15914. $this->SetDash();
  15915. }
  15916. }
  15917. $this->SetDash();
  15918. $this->SetAlpha(1);
  15919. }
  15920. /* -- END HTML-CSS -- */
  15921. function Reset()
  15922. {
  15923. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  15924. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  15925. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  15926. $this->SetAlpha(1);
  15927. $this->colorarray = '';
  15928. $this->spanbgcolorarray = '';
  15929. $this->spanbgcolor = false;
  15930. $this->spanborder = false;
  15931. $this->spanborddet = [];
  15932. $this->ResetStyles();
  15933. $this->HREF = '';
  15934. $this->textparam = [];
  15935. $this->SetTextOutline();
  15936. $this->textvar = 0x00; // mPDF 5.7.1
  15937. $this->OTLtags = [];
  15938. $this->textshadow = '';
  15939. $this->currentLang = $this->default_lang; // mPDF 6
  15940. $this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6
  15941. $this->SetFont($this->default_font, '', 0, false);
  15942. $this->SetFontSize($this->default_font_size, false);
  15943. $this->currentfontfamily = '';
  15944. $this->currentfontsize = '';
  15945. $this->currentfontstyle = '';
  15946. if ($this->tableLevel && isset($this->table[1][1]['cellLineHeight'])) {
  15947. $this->SetLineHeight('', $this->table[1][1]['cellLineHeight']);
  15948. } else {
  15949. if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {
  15950. $this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height
  15951. }
  15952. }
  15953. $this->lSpacingCSS = '';
  15954. $this->wSpacingCSS = '';
  15955. $this->fixedlSpacing = false;
  15956. $this->minwSpacing = 0;
  15957. $this->SetDash(); // restore to no dash
  15958. $this->dash_on = false;
  15959. $this->dotted_on = false;
  15960. $this->divwidth = 0;
  15961. $this->divheight = 0;
  15962. $this->cellTextAlign = '';
  15963. $this->cellLineHeight = '';
  15964. $this->cellLineStackingStrategy = '';
  15965. $this->cellLineStackingShift = '';
  15966. $this->oldy = -1;
  15967. $bodystyle = [];
  15968. if (isset($this->cssManager->CSS['BODY']['FONT-STYLE'])) {
  15969. $bodystyle['FONT-STYLE'] = $this->cssManager->CSS['BODY']['FONT-STYLE'];
  15970. }
  15971. if (isset($this->cssManager->CSS['BODY']['FONT-WEIGHT'])) {
  15972. $bodystyle['FONT-WEIGHT'] = $this->cssManager->CSS['BODY']['FONT-WEIGHT'];
  15973. }
  15974. if (isset($this->cssManager->CSS['BODY']['COLOR'])) {
  15975. $bodystyle['COLOR'] = $this->cssManager->CSS['BODY']['COLOR'];
  15976. }
  15977. if (isset($bodystyle)) {
  15978. $this->setCSS($bodystyle, 'BLOCK', 'BODY');
  15979. }
  15980. }
  15981. /* -- HTML-CSS -- */
  15982. function ReadMetaTags($html)
  15983. {
  15984. // changes anykey=anyvalue to anykey="anyvalue" (only do this when this happens inside tags)
  15985. $regexp = '/ (\\w+?)=([^\\s>"]+)/si';
  15986. $html = preg_replace($regexp, " \$1=\"\$2\"", $html);
  15987. if (preg_match('/<title>(.*?)<\/title>/si', $html, $m)) {
  15988. $this->SetTitle($m[1]);
  15989. }
  15990. preg_match_all('/<meta [^>]*?(name|content)="([^>]*?)" [^>]*?(name|content)="([^>]*?)".*?>/si', $html, $aux);
  15991. $firstattr = $aux[1];
  15992. $secondattr = $aux[3];
  15993. for ($i = 0; $i < count($aux[0]); $i++) {
  15994. $name = ( strtoupper($firstattr[$i]) == "NAME" ) ? strtoupper($aux[2][$i]) : strtoupper($aux[4][$i]);
  15995. $content = ( strtoupper($firstattr[$i]) == "CONTENT" ) ? $aux[2][$i] : $aux[4][$i];
  15996. switch ($name) {
  15997. case "KEYWORDS":
  15998. $this->SetKeywords($content);
  15999. break;
  16000. case "AUTHOR":
  16001. $this->SetAuthor($content);
  16002. break;
  16003. case "DESCRIPTION":
  16004. $this->SetSubject($content);
  16005. break;
  16006. }
  16007. }
  16008. }
  16009. function ReadCharset($html)
  16010. {
  16011. // Charset conversion
  16012. if ($this->allow_charset_conversion) {
  16013. if (preg_match('/<head.*charset=([^\'\"\s]*).*<\/head>/si', $html, $m)) {
  16014. if (strtoupper($m[1]) != 'UTF-8') {
  16015. $this->charset_in = strtoupper($m[1]);
  16016. }
  16017. }
  16018. }
  16019. }
  16020. function setCSS($arrayaux, $type = '', $tag = '')
  16021. {
  16022. // type= INLINE | BLOCK | TABLECELL // tag= BODY
  16023. if (!is_array($arrayaux)) {
  16024. return; // Removes PHP Warning
  16025. }
  16026. // mPDF 5.7.3 inline text-decoration parameters
  16027. $preceeding_fontkey = $this->FontFamily . $this->FontStyle;
  16028. $preceeding_fontsize = $this->FontSize;
  16029. $spanbordset = false;
  16030. $spanbgset = false;
  16031. // mPDF 6
  16032. $prevlevel = (($this->blklvl == 0) ? 0 : $this->blklvl - 1);
  16033. // Set font size first so that e.g. MARGIN 0.83em works on font size for this element
  16034. if (isset($arrayaux['FONT-SIZE'])) {
  16035. $v = $arrayaux['FONT-SIZE'];
  16036. $firstLetter = substr($v, 0, 1);
  16037. if (is_numeric($firstLetter) || ($firstLetter === '.')) {
  16038. if ($type == 'BLOCK' && $this->blklvl > 0 && isset($this->blk[$this->blklvl - 1]['InlineProperties']) && isset($this->blk[$this->blklvl - 1]['InlineProperties']['size'])) {
  16039. $mmsize = $this->sizeConverter->convert($v, $this->blk[$this->blklvl - 1]['InlineProperties']['size']);
  16040. } elseif ($type == 'TABLECELL') {
  16041. $mmsize = $this->sizeConverter->convert($v, $this->default_font_size / Mpdf::SCALE);
  16042. } else {
  16043. $mmsize = $this->sizeConverter->convert($v, $this->FontSize);
  16044. }
  16045. $this->SetFontSize($mmsize * (Mpdf::SCALE), false); // Get size in points (pt)
  16046. } else {
  16047. $v = strtoupper($v);
  16048. if (isset($this->fontsizes[$v])) {
  16049. $this->SetFontSize($this->fontsizes[$v] * $this->default_font_size, false);
  16050. }
  16051. }
  16052. if ($tag == 'BODY') {
  16053. $this->SetDefaultFontSize($this->FontSizePt);
  16054. }
  16055. }
  16056. // mPDF 6
  16057. if (isset($arrayaux['LANG']) && $arrayaux['LANG']) {
  16058. if ($this->autoLangToFont && !$this->usingCoreFont) {
  16059. if ($arrayaux['LANG'] != $this->default_lang && $arrayaux['LANG'] != 'UTF-8') {
  16060. list ($coreSuitable, $mpdf_pdf_unifont) = $this->languageToFont->getLanguageOptions($arrayaux['LANG'], $this->useAdobeCJK);
  16061. if ($mpdf_pdf_unifont) {
  16062. $arrayaux['FONT-FAMILY'] = $mpdf_pdf_unifont;
  16063. }
  16064. if ($tag == 'BODY') {
  16065. $this->default_lang = $arrayaux['LANG'];
  16066. }
  16067. }
  16068. }
  16069. $this->currentLang = $arrayaux['LANG'];
  16070. }
  16071. // FOR INLINE and BLOCK OR 'BODY'
  16072. if (isset($arrayaux['FONT-FAMILY'])) {
  16073. $v = $arrayaux['FONT-FAMILY'];
  16074. // If it is a font list, get all font types
  16075. $aux_fontlist = explode(",", $v);
  16076. $found = 0;
  16077. foreach ($aux_fontlist as $f) {
  16078. $fonttype = trim($f);
  16079. $fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
  16080. $fonttype = preg_replace('/ /', '', $fonttype);
  16081. $v = strtolower(trim($fonttype));
  16082. if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {
  16083. $v = $this->fonttrans[$v];
  16084. }
  16085. if ((!$this->onlyCoreFonts && in_array($v, $this->available_unifonts)) ||
  16086. in_array($v, ['ccourier', 'ctimes', 'chelvetica']) ||
  16087. ($this->onlyCoreFonts && in_array($v, ['courier', 'times', 'helvetica', 'arial'])) ||
  16088. in_array($v, ['sjis', 'uhc', 'big5', 'gb'])) {
  16089. $fonttype = $v;
  16090. $found = 1;
  16091. break;
  16092. }
  16093. }
  16094. if (!$found) {
  16095. foreach ($aux_fontlist as $f) {
  16096. $fonttype = trim($f);
  16097. $fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
  16098. $fonttype = preg_replace('/ /', '', $fonttype);
  16099. $v = strtolower(trim($fonttype));
  16100. if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {
  16101. $v = $this->fonttrans[$v];
  16102. }
  16103. if (in_array($v, $this->sans_fonts) || in_array($v, $this->serif_fonts) || in_array($v, $this->mono_fonts)) {
  16104. $fonttype = $v;
  16105. break;
  16106. }
  16107. }
  16108. }
  16109. if ($tag == 'BODY') {
  16110. $this->SetDefaultFont($fonttype);
  16111. }
  16112. $this->SetFont($fonttype, $this->currentfontstyle, 0, false);
  16113. } else {
  16114. $this->SetFont($this->currentfontfamily, $this->currentfontstyle, 0, false);
  16115. }
  16116. foreach ($arrayaux as $k => $v) {
  16117. if ($type != 'INLINE' && $tag != 'BODY' && $type != 'TABLECELL') {
  16118. switch ($k) {
  16119. // BORDERS
  16120. case 'BORDER-TOP':
  16121. $this->blk[$this->blklvl]['border_top'] = $this->border_details($v);
  16122. if ($this->blk[$this->blklvl]['border_top']['s']) {
  16123. $this->blk[$this->blklvl]['border'] = 1;
  16124. }
  16125. break;
  16126. case 'BORDER-BOTTOM':
  16127. $this->blk[$this->blklvl]['border_bottom'] = $this->border_details($v);
  16128. if ($this->blk[$this->blklvl]['border_bottom']['s']) {
  16129. $this->blk[$this->blklvl]['border'] = 1;
  16130. }
  16131. break;
  16132. case 'BORDER-LEFT':
  16133. $this->blk[$this->blklvl]['border_left'] = $this->border_details($v);
  16134. if ($this->blk[$this->blklvl]['border_left']['s']) {
  16135. $this->blk[$this->blklvl]['border'] = 1;
  16136. }
  16137. break;
  16138. case 'BORDER-RIGHT':
  16139. $this->blk[$this->blklvl]['border_right'] = $this->border_details($v);
  16140. if ($this->blk[$this->blklvl]['border_right']['s']) {
  16141. $this->blk[$this->blklvl]['border'] = 1;
  16142. }
  16143. break;
  16144. // PADDING
  16145. case 'PADDING-TOP':
  16146. $this->blk[$this->blklvl]['padding_top'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16147. break;
  16148. case 'PADDING-BOTTOM':
  16149. $this->blk[$this->blklvl]['padding_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16150. break;
  16151. case 'PADDING-LEFT':
  16152. if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {
  16153. $this->blk[$this->blklvl]['padding_left'] = 'auto';
  16154. break;
  16155. }
  16156. $this->blk[$this->blklvl]['padding_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16157. break;
  16158. case 'PADDING-RIGHT':
  16159. if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {
  16160. $this->blk[$this->blklvl]['padding_right'] = 'auto';
  16161. break;
  16162. }
  16163. $this->blk[$this->blklvl]['padding_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16164. break;
  16165. // MARGINS
  16166. case 'MARGIN-TOP':
  16167. $tmp = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16168. if (isset($this->blk[$this->blklvl]['lastbottommargin'])) {
  16169. if ($tmp > $this->blk[$this->blklvl]['lastbottommargin']) {
  16170. $tmp -= $this->blk[$this->blklvl]['lastbottommargin'];
  16171. } else {
  16172. $tmp = 0;
  16173. }
  16174. }
  16175. $this->blk[$this->blklvl]['margin_top'] = $tmp;
  16176. break;
  16177. case 'MARGIN-BOTTOM':
  16178. $this->blk[$this->blklvl]['margin_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16179. break;
  16180. case 'MARGIN-LEFT':
  16181. $this->blk[$this->blklvl]['margin_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16182. break;
  16183. case 'MARGIN-RIGHT':
  16184. $this->blk[$this->blklvl]['margin_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16185. break;
  16186. /* -- BORDER-RADIUS -- */
  16187. case 'BORDER-TOP-LEFT-RADIUS-H':
  16188. $this->blk[$this->blklvl]['border_radius_TL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16189. break;
  16190. case 'BORDER-TOP-LEFT-RADIUS-V':
  16191. $this->blk[$this->blklvl]['border_radius_TL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16192. break;
  16193. case 'BORDER-TOP-RIGHT-RADIUS-H':
  16194. $this->blk[$this->blklvl]['border_radius_TR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16195. break;
  16196. case 'BORDER-TOP-RIGHT-RADIUS-V':
  16197. $this->blk[$this->blklvl]['border_radius_TR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16198. break;
  16199. case 'BORDER-BOTTOM-LEFT-RADIUS-H':
  16200. $this->blk[$this->blklvl]['border_radius_BL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16201. break;
  16202. case 'BORDER-BOTTOM-LEFT-RADIUS-V':
  16203. $this->blk[$this->blklvl]['border_radius_BL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16204. break;
  16205. case 'BORDER-BOTTOM-RIGHT-RADIUS-H':
  16206. $this->blk[$this->blklvl]['border_radius_BR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16207. break;
  16208. case 'BORDER-BOTTOM-RIGHT-RADIUS-V':
  16209. $this->blk[$this->blklvl]['border_radius_BR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16210. break;
  16211. /* -- END BORDER-RADIUS -- */
  16212. case 'BOX-SHADOW':
  16213. $bs = $this->cssManager->setCSSboxshadow($v);
  16214. if ($bs) {
  16215. $this->blk[$this->blklvl]['box_shadow'] = $bs;
  16216. }
  16217. break;
  16218. case 'BACKGROUND-CLIP':
  16219. if (strtoupper($v) == 'PADDING-BOX') {
  16220. $this->blk[$this->blklvl]['background_clip'] = 'padding-box';
  16221. } elseif (strtoupper($v) == 'CONTENT-BOX') {
  16222. $this->blk[$this->blklvl]['background_clip'] = 'content-box';
  16223. }
  16224. break;
  16225. case 'PAGE-BREAK-AFTER':
  16226. if (strtoupper($v) == 'AVOID') {
  16227. $this->blk[$this->blklvl]['page_break_after_avoid'] = true;
  16228. } elseif (strtoupper($v) == 'ALWAYS' || strtoupper($v) == 'LEFT' || strtoupper($v) == 'RIGHT') {
  16229. $this->blk[$this->blklvl]['page_break_after'] = strtoupper($v);
  16230. }
  16231. break;
  16232. // mPDF 6 pagebreaktype
  16233. case 'BOX-DECORATION-BREAK':
  16234. if (strtoupper($v) == 'CLONE') {
  16235. $this->blk[$this->blklvl]['box_decoration_break'] = 'clone';
  16236. } elseif (strtoupper($v) == 'SLICE') {
  16237. $this->blk[$this->blklvl]['box_decoration_break'] = 'slice';
  16238. }
  16239. break;
  16240. case 'WIDTH':
  16241. if (strtoupper($v) != 'AUTO') {
  16242. $this->blk[$this->blklvl]['css_set_width'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  16243. }
  16244. break;
  16245. // mPDF 6 Lists
  16246. // LISTS
  16247. case 'LIST-STYLE-TYPE':
  16248. $this->blk[$this->blklvl]['list_style_type'] = strtolower($v);
  16249. break;
  16250. case 'LIST-STYLE-IMAGE':
  16251. $this->blk[$this->blklvl]['list_style_image'] = strtolower($v);
  16252. break;
  16253. case 'LIST-STYLE-POSITION':
  16254. $this->blk[$this->blklvl]['list_style_position'] = strtolower($v);
  16255. break;
  16256. }//end of switch($k)
  16257. }
  16258. if ($type != 'INLINE' && $type != 'TABLECELL') { // All block-level, including BODY tag
  16259. switch ($k) {
  16260. case 'TEXT-INDENT':
  16261. // Computed value - to inherit
  16262. $this->blk[$this->blklvl]['text_indent'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false) . 'mm';
  16263. break;
  16264. case 'MARGIN-COLLAPSE': // Custom tag to collapse margins at top and bottom of page
  16265. if (strtoupper($v) == 'COLLAPSE') {
  16266. $this->blk[$this->blklvl]['margin_collapse'] = true;
  16267. }
  16268. break;
  16269. case 'LINE-HEIGHT':
  16270. $this->blk[$this->blklvl]['line_height'] = $this->fixLineheight($v);
  16271. if (!$this->blk[$this->blklvl]['line_height']) {
  16272. $this->blk[$this->blklvl]['line_height'] = 'N';
  16273. } // mPDF 6
  16274. break;
  16275. // mPDF 6
  16276. case 'LINE-STACKING-STRATEGY':
  16277. $this->blk[$this->blklvl]['line_stacking_strategy'] = strtolower($v);
  16278. break;
  16279. case 'LINE-STACKING-SHIFT':
  16280. $this->blk[$this->blklvl]['line_stacking_shift'] = strtolower($v);
  16281. break;
  16282. case 'TEXT-ALIGN': // left right center justify
  16283. switch (strtoupper($v)) {
  16284. case 'LEFT':
  16285. $this->blk[$this->blklvl]['align'] = "L";
  16286. break;
  16287. case 'CENTER':
  16288. $this->blk[$this->blklvl]['align'] = "C";
  16289. break;
  16290. case 'RIGHT':
  16291. $this->blk[$this->blklvl]['align'] = "R";
  16292. break;
  16293. case 'JUSTIFY':
  16294. $this->blk[$this->blklvl]['align'] = "J";
  16295. break;
  16296. }
  16297. break;
  16298. /* -- BACKGROUNDS -- */
  16299. case 'BACKGROUND-GRADIENT':
  16300. if ($type == 'BLOCK') {
  16301. $this->blk[$this->blklvl]['gradient'] = $v;
  16302. }
  16303. break;
  16304. /* -- END BACKGROUNDS -- */
  16305. case 'DIRECTION':
  16306. if ($v) {
  16307. $this->blk[$this->blklvl]['direction'] = strtolower($v);
  16308. }
  16309. break;
  16310. }
  16311. }
  16312. // FOR INLINE ONLY
  16313. if ($type == 'INLINE') {
  16314. switch ($k) {
  16315. case 'DISPLAY':
  16316. if (strtoupper($v) == 'NONE') {
  16317. $this->inlineDisplayOff = true;
  16318. }
  16319. break;
  16320. case 'DIRECTION':
  16321. break;
  16322. }
  16323. }
  16324. // FOR INLINE ONLY
  16325. if ($type == 'INLINE') {
  16326. switch ($k) {
  16327. // BORDERS
  16328. case 'BORDER-TOP':
  16329. $this->spanborddet['T'] = $this->border_details($v);
  16330. $this->spanborder = true;
  16331. $spanbordset = true;
  16332. break;
  16333. case 'BORDER-BOTTOM':
  16334. $this->spanborddet['B'] = $this->border_details($v);
  16335. $this->spanborder = true;
  16336. $spanbordset = true;
  16337. break;
  16338. case 'BORDER-LEFT':
  16339. $this->spanborddet['L'] = $this->border_details($v);
  16340. $this->spanborder = true;
  16341. $spanbordset = true;
  16342. break;
  16343. case 'BORDER-RIGHT':
  16344. $this->spanborddet['R'] = $this->border_details($v);
  16345. $this->spanborder = true;
  16346. $spanbordset = true;
  16347. break;
  16348. case 'VISIBILITY': // block is set in OpenTag
  16349. $v = strtolower($v);
  16350. if ($v == 'visible' || $v == 'hidden' || $v == 'printonly' || $v == 'screenonly') {
  16351. $this->textparam['visibility'] = $v;
  16352. }
  16353. break;
  16354. }//end of switch($k)
  16355. }
  16356. if ($type != 'TABLECELL') {
  16357. // FOR INLINE and BLOCK
  16358. switch ($k) {
  16359. case 'TEXT-ALIGN': // left right center justify
  16360. if (strtoupper($v) == 'NOJUSTIFY' && $this->blk[$this->blklvl]['align'] == "J") {
  16361. $this->blk[$this->blklvl]['align'] = "";
  16362. }
  16363. break;
  16364. // bgcolor only - to stay consistent with original html2fpdf
  16365. case 'BACKGROUND':
  16366. case 'BACKGROUND-COLOR':
  16367. $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
  16368. if ($cor) {
  16369. if ($tag == 'BODY') {
  16370. $this->bodyBackgroundColor = $cor;
  16371. } elseif ($type == 'INLINE') {
  16372. $this->spanbgcolorarray = $cor;
  16373. $this->spanbgcolor = true;
  16374. $spanbgset = true;
  16375. } else {
  16376. $this->blk[$this->blklvl]['bgcolorarray'] = $cor;
  16377. $this->blk[$this->blklvl]['bgcolor'] = true;
  16378. }
  16379. } elseif ($type != 'INLINE') {
  16380. if ($this->ColActive) {
  16381. $this->blk[$this->blklvl]['bgcolorarray'] = $this->blk[$prevlevel]['bgcolorarray'];
  16382. $this->blk[$this->blklvl]['bgcolor'] = $this->blk[$prevlevel]['bgcolor'];
  16383. }
  16384. }
  16385. break;
  16386. case 'VERTICAL-ALIGN': // super and sub only dealt with here e.g. <SUB> and <SUP>
  16387. switch (strtoupper($v)) {
  16388. case 'SUPER':
  16389. $this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT); // mPDF 5.7.1
  16390. $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
  16391. // mPDF 5.7.3 inline text-decoration parameters
  16392. if (isset($this->textparam['text-baseline'])) {
  16393. $this->textparam['text-baseline'] += ($this->baselineSup) * $preceeding_fontsize;
  16394. } else {
  16395. $this->textparam['text-baseline'] = ($this->baselineSup) * $preceeding_fontsize;
  16396. }
  16397. break;
  16398. case 'SUB':
  16399. $this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT);
  16400. $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
  16401. // mPDF 5.7.3 inline text-decoration parameters
  16402. if (isset($this->textparam['text-baseline'])) {
  16403. $this->textparam['text-baseline'] += ($this->baselineSub) * $preceeding_fontsize;
  16404. } else {
  16405. $this->textparam['text-baseline'] = ($this->baselineSub) * $preceeding_fontsize;
  16406. }
  16407. break;
  16408. case 'BASELINE':
  16409. $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
  16410. $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
  16411. // mPDF 5.7.3 inline text-decoration parameters
  16412. if (isset($this->textparam['text-baseline'])) {
  16413. unset($this->textparam['text-baseline']);
  16414. }
  16415. break;
  16416. // mPDF 5.7.3 inline text-decoration parameters
  16417. default:
  16418. $lh = $this->_computeLineheight($this->blk[$this->blklvl]['line_height']);
  16419. $sz = $this->sizeConverter->convert($v, $lh, $this->FontSize, false);
  16420. $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
  16421. $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
  16422. if ($sz) {
  16423. if ($sz > 0) {
  16424. $this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT);
  16425. } else {
  16426. $this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT);
  16427. }
  16428. if (isset($this->textparam['text-baseline'])) {
  16429. $this->textparam['text-baseline'] += $sz;
  16430. } else {
  16431. $this->textparam['text-baseline'] = $sz;
  16432. }
  16433. }
  16434. }
  16435. break;
  16436. }//end of switch($k)
  16437. }
  16438. // FOR ALL
  16439. switch ($k) {
  16440. case 'LETTER-SPACING':
  16441. $this->lSpacingCSS = $v;
  16442. if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
  16443. $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize);
  16444. }
  16445. break;
  16446. case 'WORD-SPACING':
  16447. $this->wSpacingCSS = $v;
  16448. if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
  16449. $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize);
  16450. }
  16451. break;
  16452. case 'FONT-STYLE': // italic normal oblique
  16453. switch (strtoupper($v)) {
  16454. case 'ITALIC':
  16455. case 'OBLIQUE':
  16456. $this->SetStyle('I', true);
  16457. break;
  16458. case 'NORMAL':
  16459. $this->SetStyle('I', false);
  16460. break;
  16461. }
  16462. break;
  16463. case 'FONT-WEIGHT': // normal bold // Does not support: bolder, lighter, 100..900(step value=100)
  16464. switch (strtoupper($v)) {
  16465. case 'BOLD':
  16466. $this->SetStyle('B', true);
  16467. break;
  16468. case 'NORMAL':
  16469. $this->SetStyle('B', false);
  16470. break;
  16471. }
  16472. break;
  16473. case 'FONT-KERNING':
  16474. if (strtoupper($v) == 'NORMAL' || (strtoupper($v) == 'AUTO' && $this->useKerning)) {
  16475. /* -- OTL -- */
  16476. if ($this->CurrentFont['haskernGPOS']) {
  16477. if (isset($this->OTLtags['Plus'])) {
  16478. $this->OTLtags['Plus'] .= ' kern';
  16479. } else {
  16480. $this->OTLtags['Plus'] = ' kern';
  16481. }
  16482. } /* -- END OTL -- */ else { // *OTL*
  16483. $this->textvar = ($this->textvar | TextVars::FC_KERNING);
  16484. } // *OTL*
  16485. } elseif (strtoupper($v) == 'NONE' || (strtoupper($v) == 'AUTO' && !$this->useKerning)) {
  16486. if (isset($this->OTLtags['Plus'])) {
  16487. $this->OTLtags['Plus'] = str_replace('kern', '', $this->OTLtags['Plus']); // *OTL*
  16488. }
  16489. if (isset($this->OTLtags['FFPlus'])) {
  16490. $this->OTLtags['FFPlus'] = preg_replace('/kern[\d]*/', '', $this->OTLtags['FFPlus']);
  16491. }
  16492. $this->textvar = ($this->textvar & ~TextVars::FC_KERNING);
  16493. }
  16494. break;
  16495. /* -- OTL -- */
  16496. case 'FONT-LANGUAGE-OVERRIDE':
  16497. $v = strtoupper($v);
  16498. if (strpos($v, 'NORMAL') !== false) {
  16499. $this->fontLanguageOverride = '';
  16500. } else {
  16501. $this->fontLanguageOverride = trim($v);
  16502. }
  16503. break;
  16504. case 'FONT-VARIANT-POSITION':
  16505. if (isset($this->OTLtags['Plus'])) {
  16506. $this->OTLtags['Plus'] = str_replace(['sups', 'subs'], '', $this->OTLtags['Plus']);
  16507. }
  16508. switch (strtoupper($v)) {
  16509. case 'SUPER':
  16510. $this->OTLtags['Plus'] .= ' sups';
  16511. break;
  16512. case 'SUB':
  16513. $this->OTLtags['Plus'] .= ' subs';
  16514. break;
  16515. case 'NORMAL':
  16516. break;
  16517. }
  16518. break;
  16519. case 'FONT-VARIANT-CAPS':
  16520. $v = strtoupper($v);
  16521. if (!isset($this->OTLtags['Plus'])) {
  16522. $this->OTLtags['Plus'] = '';
  16523. }
  16524. $this->OTLtags['Plus'] = str_replace(['c2sc', 'smcp', 'c2pc', 'pcap', 'unic', 'titl'], '', $this->OTLtags['Plus']);
  16525. $this->textvar = ($this->textvar & ~TextVars::FC_SMALLCAPS); // ?????????????? <small-caps>
  16526. if (strpos($v, 'ALL-SMALL-CAPS') !== false) {
  16527. $this->OTLtags['Plus'] .= ' c2sc smcp';
  16528. } elseif (strpos($v, 'SMALL-CAPS') !== false) {
  16529. if (isset($this->CurrentFont['hassmallcapsGSUB']) && $this->CurrentFont['hassmallcapsGSUB']) {
  16530. $this->OTLtags['Plus'] .= ' smcp';
  16531. } else {
  16532. $this->textvar = ($this->textvar | TextVars::FC_SMALLCAPS);
  16533. }
  16534. } elseif (strpos($v, 'ALL-PETITE-CAPS') !== false) {
  16535. $this->OTLtags['Plus'] .= ' c2pc pcap';
  16536. } elseif (strpos($v, 'PETITE-CAPS') !== false) {
  16537. $this->OTLtags['Plus'] .= ' pcap';
  16538. } elseif (strpos($v, 'UNICASE') !== false) {
  16539. $this->OTLtags['Plus'] .= ' unic';
  16540. } elseif (strpos($v, 'TITLING-CAPS') !== false) {
  16541. $this->OTLtags['Plus'] .= ' titl';
  16542. }
  16543. break;
  16544. case 'FONT-VARIANT-LIGATURES':
  16545. $v = strtoupper($v);
  16546. if (!isset($this->OTLtags['Plus'])) {
  16547. $this->OTLtags['Plus'] = '';
  16548. }
  16549. if (!isset($this->OTLtags['Minus'])) {
  16550. $this->OTLtags['Minus'] = '';
  16551. }
  16552. if (strpos($v, 'NORMAL') !== false) {
  16553. $this->OTLtags['Minus'] = str_replace(['liga', 'clig', 'calt'], '', $this->OTLtags['Minus']);
  16554. $this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']);
  16555. } elseif (strpos($v, 'NONE') !== false) {
  16556. $this->OTLtags['Minus'] .= ' liga clig calt';
  16557. $this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']);
  16558. }
  16559. if (strpos($v, 'NO-COMMON-LIGATURES') !== false) {
  16560. $this->OTLtags['Minus'] .= ' liga clig';
  16561. } elseif (strpos($v, 'COMMON-LIGATURES') !== false) {
  16562. $this->OTLtags['Minus'] = str_replace(['liga', 'clig'], '', $this->OTLtags['Minus']);
  16563. }
  16564. if (strpos($v, 'NO-CONTEXTUAL') !== false) {
  16565. $this->OTLtags['Minus'] .= ' calt';
  16566. } elseif (strpos($v, 'CONTEXTUAL') !== false) {
  16567. $this->OTLtags['Minus'] = str_replace('calt', '', $this->OTLtags['Minus']);
  16568. }
  16569. if (strpos($v, 'NO-DISCRETIONARY-LIGATURES') !== false) {
  16570. $this->OTLtags['Plus'] = str_replace('dlig', '', $this->OTLtags['Plus']);
  16571. } elseif (strpos($v, 'DISCRETIONARY-LIGATURES') !== false) {
  16572. $this->OTLtags['Plus'] .= ' dlig';
  16573. }
  16574. if (strpos($v, 'NO-HISTORICAL-LIGATURES') !== false) {
  16575. $this->OTLtags['Plus'] = str_replace('hlig', '', $this->OTLtags['Plus']);
  16576. } elseif (strpos($v, 'HISTORICAL-LIGATURES') !== false) {
  16577. $this->OTLtags['Plus'] .= ' hlig';
  16578. }
  16579. break;
  16580. case 'FONT-VARIANT-NUMERIC':
  16581. $v = strtoupper($v);
  16582. if (!isset($this->OTLtags['Plus'])) {
  16583. $this->OTLtags['Plus'] = '';
  16584. }
  16585. if (strpos($v, 'NORMAL') !== false) {
  16586. $this->OTLtags['Plus'] = str_replace(['ordn', 'zero', 'lnum', 'onum', 'pnum', 'tnum', 'frac', 'afrc'], '', $this->OTLtags['Plus']);
  16587. }
  16588. if (strpos($v, 'ORDINAL') !== false) {
  16589. $this->OTLtags['Plus'] .= ' ordn';
  16590. }
  16591. if (strpos($v, 'SLASHED-ZERO') !== false) {
  16592. $this->OTLtags['Plus'] .= ' zero';
  16593. }
  16594. if (strpos($v, 'LINING-NUMS') !== false) {
  16595. $this->OTLtags['Plus'] .= ' lnum';
  16596. $this->OTLtags['Plus'] = str_replace('onum', '', $this->OTLtags['Plus']);
  16597. } elseif (strpos($v, 'OLDSTYLE-NUMS') !== false) {
  16598. $this->OTLtags['Plus'] .= ' onum';
  16599. $this->OTLtags['Plus'] = str_replace('lnum', '', $this->OTLtags['Plus']);
  16600. }
  16601. if (strpos($v, 'PROPORTIONAL-NUMS') !== false) {
  16602. $this->OTLtags['Plus'] .= ' pnum';
  16603. $this->OTLtags['Plus'] = str_replace('tnum', '', $this->OTLtags['Plus']);
  16604. } elseif (strpos($v, 'TABULAR-NUMS') !== false) {
  16605. $this->OTLtags['Plus'] .= ' tnum';
  16606. $this->OTLtags['Plus'] = str_replace('pnum', '', $this->OTLtags['Plus']);
  16607. }
  16608. if (strpos($v, 'DIAGONAL-FRACTIONS') !== false) {
  16609. $this->OTLtags['Plus'] .= ' frac';
  16610. $this->OTLtags['Plus'] = str_replace('afrc', '', $this->OTLtags['Plus']);
  16611. } elseif (strpos($v, 'STACKED-FRACTIONS') !== false) {
  16612. $this->OTLtags['Plus'] .= ' afrc';
  16613. $this->OTLtags['Plus'] = str_replace('frac', '', $this->OTLtags['Plus']);
  16614. }
  16615. break;
  16616. case 'FONT-VARIANT-ALTERNATES': // Only supports historical-forms
  16617. $v = strtoupper($v);
  16618. if (!isset($this->OTLtags['Plus'])) {
  16619. $this->OTLtags['Plus'] = '';
  16620. }
  16621. if (strpos($v, 'NORMAL') !== false) {
  16622. $this->OTLtags['Plus'] = str_replace('hist', '', $this->OTLtags['Plus']);
  16623. }
  16624. if (strpos($v, 'HISTORICAL-FORMS') !== false) {
  16625. $this->OTLtags['Plus'] .= ' hist';
  16626. }
  16627. break;
  16628. case 'FONT-FEATURE-SETTINGS':
  16629. $v = strtolower($v);
  16630. if (strpos($v, 'normal') !== false) {
  16631. $this->OTLtags['FFMinus'] = '';
  16632. $this->OTLtags['FFPlus'] = '';
  16633. } else {
  16634. if (!isset($this->OTLtags['FFPlus'])) {
  16635. $this->OTLtags['FFPlus'] = '';
  16636. }
  16637. if (!isset($this->OTLtags['FFMinus'])) {
  16638. $this->OTLtags['FFMinus'] = '';
  16639. }
  16640. $tags = preg_split('/[,]/', $v);
  16641. foreach ($tags as $t) {
  16642. if (preg_match('/[\"\']([a-zA-Z0-9]{4})[\"\']\s*(on|off|\d*){0,1}/', $t, $m)) {
  16643. if ($m[2] == 'off' || $m[2] === '0') {
  16644. if (strpos($this->OTLtags['FFMinus'], $m[1]) === false) {
  16645. $this->OTLtags['FFMinus'] .= ' ' . $m[1];
  16646. }
  16647. $this->OTLtags['FFPlus'] = preg_replace('/' . $m[1] . '[\d]*/', '', $this->OTLtags['FFPlus']);
  16648. } else {
  16649. if ($m[2] == 'on') {
  16650. $m[2] = '1';
  16651. }
  16652. if (strpos($this->OTLtags['FFPlus'], $m[1]) === false) {
  16653. $this->OTLtags['FFPlus'] .= ' ' . $m[1] . $m[2];
  16654. }
  16655. $this->OTLtags['FFMinus'] = str_replace($m[1], '', $this->OTLtags['FFMinus']);
  16656. }
  16657. }
  16658. }
  16659. }
  16660. break;
  16661. /* -- END OTL -- */
  16662. case 'TEXT-TRANSFORM': // none uppercase lowercase // Does support: capitalize
  16663. switch (strtoupper($v)) { // Not working 100%
  16664. case 'CAPITALIZE':
  16665. $this->textvar = ($this->textvar | TextVars::FT_CAPITALIZE); // mPDF 5.7.1
  16666. $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
  16667. $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
  16668. break;
  16669. case 'UPPERCASE':
  16670. $this->textvar = ($this->textvar | TextVars::FT_UPPERCASE); // mPDF 5.7.1
  16671. $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
  16672. $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
  16673. break;
  16674. case 'LOWERCASE':
  16675. $this->textvar = ($this->textvar | TextVars::FT_LOWERCASE); // mPDF 5.7.1
  16676. $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
  16677. $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
  16678. break;
  16679. case 'NONE':
  16680. break;
  16681. $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
  16682. $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
  16683. $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
  16684. }
  16685. break;
  16686. case 'TEXT-SHADOW':
  16687. $ts = $this->cssManager->setCSStextshadow($v);
  16688. if ($ts) {
  16689. $this->textshadow = $ts;
  16690. }
  16691. break;
  16692. case 'HYPHENS':
  16693. if (strtoupper($v) == 'NONE') {
  16694. $this->textparam['hyphens'] = 2;
  16695. } elseif (strtoupper($v) == 'AUTO') {
  16696. $this->textparam['hyphens'] = 1;
  16697. } elseif (strtoupper($v) == 'MANUAL') {
  16698. $this->textparam['hyphens'] = 0;
  16699. }
  16700. break;
  16701. case 'TEXT-OUTLINE':
  16702. if (strtoupper($v) == 'NONE') {
  16703. $this->textparam['outline-s'] = false;
  16704. }
  16705. break;
  16706. case 'TEXT-OUTLINE-WIDTH':
  16707. case 'OUTLINE-WIDTH':
  16708. switch (strtoupper($v)) {
  16709. case 'THIN':
  16710. $v = '0.03em';
  16711. break;
  16712. case 'MEDIUM':
  16713. $v = '0.05em';
  16714. break;
  16715. case 'THICK':
  16716. $v = '0.07em';
  16717. break;
  16718. }
  16719. $w = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize);
  16720. if ($w) {
  16721. $this->textparam['outline-WIDTH'] = $w;
  16722. $this->textparam['outline-s'] = true;
  16723. } else {
  16724. $this->textparam['outline-s'] = false;
  16725. }
  16726. break;
  16727. case 'TEXT-OUTLINE-COLOR':
  16728. case 'OUTLINE-COLOR':
  16729. if (strtoupper($v) == 'INVERT') {
  16730. if ($this->colorarray) {
  16731. $cor = $this->colorarray;
  16732. $this->textparam['outline-COLOR'] = $this->colorConverter->invert($cor);
  16733. } else {
  16734. $this->textparam['outline-COLOR'] = $this->colorConverter->convert(255, $this->PDFAXwarnings);
  16735. }
  16736. } else {
  16737. $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
  16738. if ($cor) {
  16739. $this->textparam['outline-COLOR'] = $cor;
  16740. }
  16741. }
  16742. break;
  16743. case 'COLOR': // font color
  16744. $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
  16745. if ($cor) {
  16746. $this->colorarray = $cor;
  16747. $this->SetTColor($cor);
  16748. }
  16749. break;
  16750. }//end of switch($k)
  16751. }//end of foreach
  16752. // mPDF 5.7.3 inline text-decoration parameters
  16753. // Needs to be set at the end - after vertical-align = super/sub, so that textparam['text-baseline'] is set
  16754. if (isset($arrayaux['TEXT-DECORATION'])) {
  16755. $v = $arrayaux['TEXT-DECORATION']; // none underline line-through (strikeout) // Does not support: blink
  16756. if (stristr($v, 'LINE-THROUGH')) {
  16757. $this->textvar = ($this->textvar | TextVars::FD_LINETHROUGH);
  16758. // mPDF 5.7.3 inline text-decoration parameters
  16759. if (isset($this->textparam['text-baseline'])) {
  16760. $this->textparam['s-decoration']['baseline'] = $this->textparam['text-baseline'];
  16761. } else {
  16762. $this->textparam['s-decoration']['baseline'] = 0;
  16763. }
  16764. $this->textparam['s-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  16765. $this->textparam['s-decoration']['fontsize'] = $this->FontSize;
  16766. $this->textparam['s-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  16767. }
  16768. if (stristr($v, 'UNDERLINE')) {
  16769. $this->textvar = ($this->textvar | TextVars::FD_UNDERLINE);
  16770. // mPDF 5.7.3 inline text-decoration parameters
  16771. if (isset($this->textparam['text-baseline'])) {
  16772. $this->textparam['u-decoration']['baseline'] = $this->textparam['text-baseline'];
  16773. } else {
  16774. $this->textparam['u-decoration']['baseline'] = 0;
  16775. }
  16776. $this->textparam['u-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  16777. $this->textparam['u-decoration']['fontsize'] = $this->FontSize;
  16778. $this->textparam['u-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  16779. }
  16780. if (stristr($v, 'OVERLINE')) {
  16781. $this->textvar = ($this->textvar | TextVars::FD_OVERLINE);
  16782. // mPDF 5.7.3 inline text-decoration parameters
  16783. if (isset($this->textparam['text-baseline'])) {
  16784. $this->textparam['o-decoration']['baseline'] = $this->textparam['text-baseline'];
  16785. } else {
  16786. $this->textparam['o-decoration']['baseline'] = 0;
  16787. }
  16788. $this->textparam['o-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  16789. $this->textparam['o-decoration']['fontsize'] = $this->FontSize;
  16790. $this->textparam['o-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  16791. }
  16792. if (stristr($v, 'NONE')) {
  16793. $this->textvar = ($this->textvar & ~TextVars::FD_UNDERLINE);
  16794. $this->textvar = ($this->textvar & ~TextVars::FD_LINETHROUGH);
  16795. $this->textvar = ($this->textvar & ~TextVars::FD_OVERLINE);
  16796. // mPDF 5.7.3 inline text-decoration parameters
  16797. if (isset($this->textparam['u-decoration'])) {
  16798. unset($this->textparam['u-decoration']);
  16799. }
  16800. if (isset($this->textparam['s-decoration'])) {
  16801. unset($this->textparam['s-decoration']);
  16802. }
  16803. if (isset($this->textparam['o-decoration'])) {
  16804. unset($this->textparam['o-decoration']);
  16805. }
  16806. }
  16807. }
  16808. // mPDF 6
  16809. if ($spanbordset) { // BORDER has been set on this INLINE element
  16810. if (isset($this->textparam['text-baseline'])) {
  16811. $this->textparam['bord-decoration']['baseline'] = $this->textparam['text-baseline'];
  16812. } else {
  16813. $this->textparam['bord-decoration']['baseline'] = 0;
  16814. }
  16815. $this->textparam['bord-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  16816. $this->textparam['bord-decoration']['fontsize'] = $this->FontSize;
  16817. }
  16818. if ($spanbgset) { // BACKGROUND[-COLOR] has been set on this INLINE element
  16819. if (isset($this->textparam['text-baseline'])) {
  16820. $this->textparam['bg-decoration']['baseline'] = $this->textparam['text-baseline'];
  16821. } else {
  16822. $this->textparam['bg-decoration']['baseline'] = 0;
  16823. }
  16824. $this->textparam['bg-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  16825. $this->textparam['bg-decoration']['fontsize'] = $this->FontSize;
  16826. }
  16827. }
  16828. /* -- END HTML-CSS -- */
  16829. function SetStyle($tag, $enable)
  16830. {
  16831. $this->$tag = $enable;
  16832. $style = '';
  16833. foreach (['B', 'I'] as $s) {
  16834. if ($this->$s) {
  16835. $style .= $s;
  16836. }
  16837. }
  16838. $this->currentfontstyle = $style;
  16839. $this->SetFont('', $style, 0, false);
  16840. }
  16841. // Set multiple styles at one time
  16842. function SetStylesArray($arr)
  16843. {
  16844. $style = '';
  16845. foreach (['B', 'I'] as $s) {
  16846. if (isset($arr[$s])) {
  16847. if ($arr[$s]) {
  16848. $this->$s = true;
  16849. $style .= $s;
  16850. } else {
  16851. $this->$s = false;
  16852. }
  16853. } elseif ($this->$s) {
  16854. $style .= $s;
  16855. }
  16856. }
  16857. $this->currentfontstyle = $style;
  16858. $this->SetFont('', $style, 0, false);
  16859. }
  16860. // Set multiple styles at one $str e.g. "BI"
  16861. function SetStyles($str)
  16862. {
  16863. $style = '';
  16864. foreach (['B', 'I'] as $s) {
  16865. if (strpos($str, $s) !== false) {
  16866. $this->$s = true;
  16867. $style .= $s;
  16868. } else {
  16869. $this->$s = false;
  16870. }
  16871. }
  16872. $this->currentfontstyle = $style;
  16873. $this->SetFont('', $style, 0, false);
  16874. }
  16875. function ResetStyles()
  16876. {
  16877. foreach (['B', 'I'] as $s) {
  16878. $this->$s = false;
  16879. }
  16880. $this->currentfontstyle = '';
  16881. $this->SetFont('', '', 0, false);
  16882. }
  16883. function DisableTags($str = '')
  16884. {
  16885. if ($str == '') { // enable all tags
  16886. // Insert new supported tags in the long string below.
  16887. $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>";
  16888. } else {
  16889. $str = explode(",", $str);
  16890. foreach ($str as $v) {
  16891. $this->enabledtags = str_replace(trim($v), '', $this->enabledtags);
  16892. }
  16893. }
  16894. }
  16895. /* -- TABLES -- */
  16896. function TableCheckMinWidth($maxwidth, $forcewrap = 0, $textbuffer = [], $checkletter = false)
  16897. {
  16898. // mPDF 6
  16899. $acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)
  16900. $acclongest = 0; // mPDF 6 (accumulated length max across > 1 chunk)
  16901. $biggestword = 0;
  16902. $toonarrow = false;
  16903. if ((count($textbuffer) == 0) or ( (count($textbuffer) == 1) && ($textbuffer[0][0] == ''))) {
  16904. return 0;
  16905. }
  16906. foreach ($textbuffer as $chunk) {
  16907. $line = $chunk[0];
  16908. $OTLdata = (isset($chunk[18]) ? $chunk[18] : null);
  16909. // mPDF ITERATION
  16910. if ($this->iterationCounter) {
  16911. $line = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $line);
  16912. }
  16913. // IMAGES & FORM ELEMENTS
  16914. if (substr($line, 0, 3) == Mpdf::OBJECT_IDENTIFIER) { // inline object - FORM element or IMAGE!
  16915. $objattr = $this->_getObjAttr($line);
  16916. if ($objattr['type'] != 'hr' && isset($objattr['width']) && ($objattr['width'] / $this->shrin_k) > ($maxwidth + 0.0001)) {
  16917. if (($objattr['width'] / $this->shrin_k) > $biggestword) {
  16918. $biggestword = ($objattr['width'] / $this->shrin_k);
  16919. }
  16920. $toonarrow = true;
  16921. }
  16922. continue;
  16923. }
  16924. if ($line == "\n") {
  16925. $acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)
  16926. continue;
  16927. }
  16928. $line = trim($line);
  16929. if (!empty($OTLdata)) {
  16930. $this->otl->trimOTLdata($OTLdata, true, true);
  16931. } // *OTL*
  16932. // SET FONT SIZE/STYLE from $chunk[n]
  16933. // FONTSIZE
  16934. if (isset($chunk[11]) and $chunk[11] != '') {
  16935. if ($this->shrin_k) {
  16936. $this->SetFontSize($chunk[11] / $this->shrin_k, false);
  16937. } else {
  16938. $this->SetFontSize($chunk[11], false);
  16939. }
  16940. }
  16941. // FONTFAMILY
  16942. if (isset($chunk[4]) and $chunk[4] != '') {
  16943. $font = $this->SetFont($chunk[4], $this->FontStyle, 0, false);
  16944. }
  16945. // B I
  16946. if (isset($chunk[2]) and $chunk[2] != '') {
  16947. $this->SetStyles($chunk[2]);
  16948. }
  16949. $lbw = $rbw = 0; // Border widths
  16950. if (isset($chunk[16]) && !empty($chunk[16])) { // Border
  16951. $this->spanborddet = $chunk[16];
  16952. $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
  16953. $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
  16954. }
  16955. if (isset($chunk[15])) { // Word spacing
  16956. $this->wSpacingCSS = $chunk[15];
  16957. if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
  16958. $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
  16959. }
  16960. }
  16961. if (isset($chunk[14])) { // Letter spacing
  16962. $this->lSpacingCSS = $chunk[14];
  16963. if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
  16964. $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
  16965. }
  16966. }
  16967. if (isset($chunk[8])) { // mPDF 5.7.1
  16968. $this->textvar = $chunk[8];
  16969. }
  16970. // mPDF 6
  16971. // If overflow==wrap ($checkletter) OR (No word breaks and contains CJK)
  16972. if ($checkletter || (!preg_match('/(\xe2\x80\x8b| )/', trim($line)) && preg_match("/([" . $this->pregCJKchars . "])/u", $line) )) {
  16973. if (preg_match("/([" . $this->pregCJKchars . "])/u", $line)) {
  16974. $checkCJK = true;
  16975. } else {
  16976. $checkCJK = false;
  16977. }
  16978. $letters = preg_split('//u', $line);
  16979. foreach ($letters as $k => $letter) {
  16980. // mPDF 6
  16981. if ($checkCJK) {
  16982. if (preg_match("/[" . $this->CJKleading . "]/u", $letter) && $k > 0) {
  16983. $letter = $letters[$k - 1] . $letter;
  16984. }
  16985. if (preg_match("/[" . $this->CJKfollowing . "]/u", $letter) && $k < (count($letters) - 1)) {
  16986. $letter = $letter . $letters[$k + 1];
  16987. }
  16988. }
  16989. $letterwidth = $this->GetStringWidth($letter, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here
  16990. // so don't have to split OTLdata for each word
  16991. if ($k == 0) {
  16992. $letterwidth += $lbw;
  16993. }
  16994. if ($k == (count($letters) - 1)) {
  16995. $letterwidth += $rbw;
  16996. }
  16997. // Warn user that maxwidth is insufficient
  16998. if ($letterwidth > $maxwidth + 0.0001) {
  16999. if ($letterwidth > $biggestword) {
  17000. $biggestword = $letterwidth;
  17001. }
  17002. $toonarrow = true;
  17003. }
  17004. }
  17005. } else {
  17006. // mPDF 6
  17007. // Need to account for any XAdvance in GPOSinfo (OTLdata = $chunk[18])
  17008. $wordXAdvance = [];
  17009. if (isset($chunk[18]) && $chunk[18]) {
  17010. preg_match_all('/(\xe2\x80\x8b| )/', $line, $spaces, PREG_OFFSET_CAPTURE); // U+200B Zero Width word boundary, or space
  17011. $lastoffset = 0;
  17012. $k = -1; // Added so that if no spaces found, "last word" later is calculated for the one and only word
  17013. foreach ($spaces[0] as $k => $m) {
  17014. $offset = $m[1];
  17015. // ...TableCheckMinWidth...
  17016. // At this point, BIDI not applied, Writing direction is not set, and XAdvanceL balances XAdvanceR
  17017. for ($n = $lastoffset; $n < $offset; $n++) {
  17018. if (isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {
  17019. if (isset($wordXAdvance[$k])) {
  17020. $wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
  17021. } else {
  17022. $wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
  17023. }
  17024. }
  17025. }
  17026. $lastoffset = $offset + 1;
  17027. }
  17028. $k++; // last word
  17029. foreach ($chunk[18]['GPOSinfo'] as $n => $gpos) {
  17030. if ($n >= $lastoffset && isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {
  17031. if (isset($wordXAdvance[$k])) {
  17032. $wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
  17033. } else {
  17034. $wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
  17035. }
  17036. }
  17037. }
  17038. }
  17039. $words = preg_split('/(\xe2\x80\x8b| )/', $line); // U+200B Zero Width word boundary, or space
  17040. foreach ($words as $k => $word) {
  17041. $word = trim($word);
  17042. $wordwidth = $this->GetStringWidth($word, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here
  17043. // so don't have to split OTLdata for each word
  17044. if (isset($wordXAdvance[$k])) {
  17045. $wordwidth += ($wordXAdvance[$k] * 1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000);
  17046. }
  17047. if ($k == 0) {
  17048. $wordwidth += $lbw;
  17049. }
  17050. if ($k == (count($words) - 1)) {
  17051. $wordwidth += $rbw;
  17052. }
  17053. // mPDF 6
  17054. if (count($words) == 1 && substr($chunk[0], 0, 1) != ' ') {
  17055. $acclength += $wordwidth;
  17056. } elseif (count($words) > 1 && $k == 0 && substr($chunk[0], 0, 1) != ' ') {
  17057. $acclength += $wordwidth;
  17058. } else {
  17059. $acclength = $wordwidth;
  17060. }
  17061. $acclongest = max($acclongest, $acclength);
  17062. if (count($words) == 1 && substr($chunk[0], -1, 1) == ' ') {
  17063. $acclength = 0;
  17064. } elseif (count($words) > 1 && ($k != (count($words) - 1) || substr($chunk[0], -1, 1) == ' ')) {
  17065. $acclength = 0;
  17066. }
  17067. // Warn user that maxwidth is insufficient
  17068. if ($wordwidth > $maxwidth + 0.0001) {
  17069. if ($wordwidth > $biggestword) {
  17070. $biggestword = $wordwidth;
  17071. }
  17072. $toonarrow = true;
  17073. }
  17074. }
  17075. }
  17076. // mPDF 6 Accumulated length of biggest word - across multiple chunks
  17077. if ($acclongest > $maxwidth + 0.0001) {
  17078. if ($acclongest > $biggestword) {
  17079. $biggestword = $acclongest;
  17080. }
  17081. $toonarrow = true;
  17082. }
  17083. // RESET FONT SIZE/STYLE
  17084. // RESETTING VALUES
  17085. // Now we must deactivate what we have used
  17086. if (isset($chunk[2]) and $chunk[2] != '') {
  17087. $this->ResetStyles();
  17088. }
  17089. if (isset($chunk[4]) and $chunk[4] != '') {
  17090. $this->SetFont($this->default_font, $this->FontStyle, 0, false);
  17091. }
  17092. if (isset($chunk[11]) and $chunk[11] != '') {
  17093. $this->SetFontSize($this->default_font_size, false);
  17094. }
  17095. $this->spanborddet = [];
  17096. $this->textvar = 0x00; // mPDF 5.7.1
  17097. $this->OTLtags = [];
  17098. $this->lSpacingCSS = '';
  17099. $this->wSpacingCSS = '';
  17100. $this->fixedlSpacing = false;
  17101. $this->minwSpacing = 0;
  17102. }
  17103. // Return -(wordsize) if word is bigger than maxwidth
  17104. // ADDED
  17105. if (($toonarrow) && ($this->table_error_report)) {
  17106. throw new \Mpdf\MpdfException("Word is too long to fit in table - " . $this->table_error_report_param);
  17107. }
  17108. if ($toonarrow) {
  17109. return -$biggestword;
  17110. } else {
  17111. return 1;
  17112. }
  17113. }
  17114. function shrinkTable(&$table, $k)
  17115. {
  17116. $table['border_spacing_H'] /= $k;
  17117. $table['border_spacing_V'] /= $k;
  17118. $table['padding']['T'] /= $k;
  17119. $table['padding']['R'] /= $k;
  17120. $table['padding']['B'] /= $k;
  17121. $table['padding']['L'] /= $k;
  17122. $table['margin']['T'] /= $k;
  17123. $table['margin']['R'] /= $k;
  17124. $table['margin']['B'] /= $k;
  17125. $table['margin']['L'] /= $k;
  17126. $table['border_details']['T']['w'] /= $k;
  17127. $table['border_details']['R']['w'] /= $k;
  17128. $table['border_details']['B']['w'] /= $k;
  17129. $table['border_details']['L']['w'] /= $k;
  17130. if (isset($table['max_cell_border_width']['T'])) {
  17131. $table['max_cell_border_width']['T'] /= $k;
  17132. }
  17133. if (isset($table['max_cell_border_width']['R'])) {
  17134. $table['max_cell_border_width']['R'] /= $k;
  17135. }
  17136. if (isset($table['max_cell_border_width']['B'])) {
  17137. $table['max_cell_border_width']['B'] /= $k;
  17138. }
  17139. if (isset($table['max_cell_border_width']['L'])) {
  17140. $table['max_cell_border_width']['L'] /= $k;
  17141. }
  17142. if ($this->simpleTables) {
  17143. $table['simple']['border_details']['T']['w'] /= $k;
  17144. $table['simple']['border_details']['R']['w'] /= $k;
  17145. $table['simple']['border_details']['B']['w'] /= $k;
  17146. $table['simple']['border_details']['L']['w'] /= $k;
  17147. }
  17148. $table['miw'] /= $k;
  17149. $table['maw'] /= $k;
  17150. for ($j = 0; $j < $table['nc']; $j++) { // columns
  17151. $table['wc'][$j]['miw'] = isset($table['wc'][$j]['miw']) ? $table['wc'][$j]['miw'] : 0;
  17152. $table['wc'][$j]['maw'] = isset($table['wc'][$j]['maw']) ? $table['wc'][$j]['maw'] : 0;
  17153. $table['wc'][$j]['miw'] /= $k;
  17154. $table['wc'][$j]['maw'] /= $k;
  17155. if (isset($table['decimal_align'][$j]['maxs0']) && $table['decimal_align'][$j]['maxs0']) {
  17156. $table['decimal_align'][$j]['maxs0'] /= $k;
  17157. }
  17158. if (isset($table['decimal_align'][$j]['maxs1']) && $table['decimal_align'][$j]['maxs1']) {
  17159. $table['decimal_align'][$j]['maxs1'] /= $k;
  17160. }
  17161. if (isset($table['wc'][$j]['absmiw']) && $table['wc'][$j]['absmiw']) {
  17162. $table['wc'][$j]['absmiw'] /= $k;
  17163. }
  17164. for ($i = 0; $i < $table['nr']; $i++) { // rows
  17165. $c = &$table['cells'][$i][$j];
  17166. if (isset($c) && $c) {
  17167. if (!$this->simpleTables) {
  17168. if ($this->packTableData) {
  17169. $cell = $this->_unpackCellBorder($c['borderbin']);
  17170. $cell['border_details']['T']['w'] /= $k;
  17171. $cell['border_details']['R']['w'] /= $k;
  17172. $cell['border_details']['B']['w'] /= $k;
  17173. $cell['border_details']['L']['w'] /= $k;
  17174. $cell['border_details']['mbw']['TL'] /= $k;
  17175. $cell['border_details']['mbw']['TR'] /= $k;
  17176. $cell['border_details']['mbw']['BL'] /= $k;
  17177. $cell['border_details']['mbw']['BR'] /= $k;
  17178. $cell['border_details']['mbw']['LT'] /= $k;
  17179. $cell['border_details']['mbw']['LB'] /= $k;
  17180. $cell['border_details']['mbw']['RT'] /= $k;
  17181. $cell['border_details']['mbw']['RB'] /= $k;
  17182. $c['borderbin'] = $this->_packCellBorder($cell);
  17183. } else {
  17184. $c['border_details']['T']['w'] /= $k;
  17185. $c['border_details']['R']['w'] /= $k;
  17186. $c['border_details']['B']['w'] /= $k;
  17187. $c['border_details']['L']['w'] /= $k;
  17188. $c['border_details']['mbw']['TL'] /= $k;
  17189. $c['border_details']['mbw']['TR'] /= $k;
  17190. $c['border_details']['mbw']['BL'] /= $k;
  17191. $c['border_details']['mbw']['BR'] /= $k;
  17192. $c['border_details']['mbw']['LT'] /= $k;
  17193. $c['border_details']['mbw']['LB'] /= $k;
  17194. $c['border_details']['mbw']['RT'] /= $k;
  17195. $c['border_details']['mbw']['RB'] /= $k;
  17196. }
  17197. }
  17198. $c['padding']['T'] /= $k;
  17199. $c['padding']['R'] /= $k;
  17200. $c['padding']['B'] /= $k;
  17201. $c['padding']['L'] /= $k;
  17202. $c['maxs'] = isset($c['maxs']) ? $c['maxs'] /= $k : null;
  17203. $c['w'] = isset($c['w']) ? $c['w'] /= $k : null;
  17204. $c['s'] = isset($c['s']) ? $c['s'] /= $k : 0;
  17205. $c['h'] = isset($c['h']) ? $c['h'] /= $k : null;
  17206. $c['miw'] = isset($c['miw']) ? $c['miw'] /= $k : 0;
  17207. $c['maw'] = isset($c['maw']) ? $c['maw'] /= $k : 0;
  17208. $c['absmiw'] = isset($c['absmiw']) ? $c['absmiw'] /= $k : null;
  17209. $c['nestedmaw'] = isset($c['nestedmaw']) ? $c['nestedmaw'] /= $k : null;
  17210. $c['nestedmiw'] = isset($c['nestedmiw']) ? $c['nestedmiw'] /= $k : null;
  17211. if (isset($c['textbuffer'])) {
  17212. foreach ($c['textbuffer'] as $n => $tb) {
  17213. if (!empty($tb[16])) {
  17214. !isset($c['textbuffer'][$n][16]['T']) || $c['textbuffer'][$n][16]['T']['w'] /= $k;
  17215. !isset($c['textbuffer'][$n][16]['B']) || $c['textbuffer'][$n][16]['B']['w'] /= $k;
  17216. !isset($c['textbuffer'][$n][16]['L']) || $c['textbuffer'][$n][16]['L']['w'] /= $k;
  17217. !isset($c['textbuffer'][$n][16]['R']) || $c['textbuffer'][$n][16]['R']['w'] /= $k;
  17218. }
  17219. }
  17220. }
  17221. unset($c);
  17222. }
  17223. } // rows
  17224. } // columns
  17225. }
  17226. function read_short(&$fh)
  17227. {
  17228. $s = fread($fh, 2);
  17229. $a = (ord($s[0]) << 8) + ord($s[1]);
  17230. if ($a & (1 << 15)) {
  17231. $a = ($a - (1 << 16));
  17232. }
  17233. return $a;
  17234. }
  17235. function _packCellBorder($cell)
  17236. {
  17237. if (!is_array($cell) || !isset($cell)) {
  17238. return '';
  17239. }
  17240. if (!$this->packTableData) {
  17241. return $cell;
  17242. }
  17243. // = 186 bytes
  17244. $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));
  17245. return $bindata;
  17246. }
  17247. function _getBorderWidths($bindata)
  17248. {
  17249. if (!$bindata) {
  17250. return [0, 0, 0, 0];
  17251. }
  17252. if (!$this->packTableData) {
  17253. return [$bindata['border_details']['T']['w'], $bindata['border_details']['R']['w'], $bindata['border_details']['B']['w'], $bindata['border_details']['L']['w']];
  17254. }
  17255. $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);
  17256. $cell['border_details']['R']['w'] = $bd['rw'];
  17257. $cell['border_details']['L']['w'] = $bd['lw'];
  17258. $cell['border_details']['T']['w'] = $bd['tw'];
  17259. $cell['border_details']['B']['w'] = $bd['bw'];
  17260. return [$bd['tw'], $bd['rw'], $bd['bw'], $bd['lw']];
  17261. }
  17262. function _unpackCellBorder($bindata)
  17263. {
  17264. if (!$bindata) {
  17265. return [];
  17266. }
  17267. if (!$this->packTableData) {
  17268. return $bindata;
  17269. }
  17270. $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);
  17271. $cell['border'] = $bd['bord'];
  17272. $cell['border_details']['R']['s'] = $bd['rs'];
  17273. $cell['border_details']['R']['w'] = $bd['rw'];
  17274. $cell['border_details']['R']['c'] = str_pad($bd['rca'], 6, "\x00");
  17275. $cell['border_details']['R']['style'] = trim($bd['rst']);
  17276. $cell['border_details']['R']['dom'] = $bd['rd'];
  17277. $cell['border_details']['L']['s'] = $bd['ls'];
  17278. $cell['border_details']['L']['w'] = $bd['lw'];
  17279. $cell['border_details']['L']['c'] = str_pad($bd['lca'], 6, "\x00");
  17280. $cell['border_details']['L']['style'] = trim($bd['lst']);
  17281. $cell['border_details']['L']['dom'] = $bd['ld'];
  17282. $cell['border_details']['T']['s'] = $bd['ts'];
  17283. $cell['border_details']['T']['w'] = $bd['tw'];
  17284. $cell['border_details']['T']['c'] = str_pad($bd['tca'], 6, "\x00");
  17285. $cell['border_details']['T']['style'] = trim($bd['tst']);
  17286. $cell['border_details']['T']['dom'] = $bd['td'];
  17287. $cell['border_details']['B']['s'] = $bd['bs'];
  17288. $cell['border_details']['B']['w'] = $bd['bw'];
  17289. $cell['border_details']['B']['c'] = str_pad($bd['bca'], 6, "\x00");
  17290. $cell['border_details']['B']['style'] = trim($bd['bst']);
  17291. $cell['border_details']['B']['dom'] = $bd['bd'];
  17292. $cell['border_details']['mbw']['BL'] = $bd['mbl'];
  17293. $cell['border_details']['mbw']['BR'] = $bd['mbr'];
  17294. $cell['border_details']['mbw']['RT'] = $bd['mrt'];
  17295. $cell['border_details']['mbw']['RB'] = $bd['mrb'];
  17296. $cell['border_details']['mbw']['TL'] = $bd['mtl'];
  17297. $cell['border_details']['mbw']['TR'] = $bd['mtr'];
  17298. $cell['border_details']['mbw']['LT'] = $bd['mlt'];
  17299. $cell['border_details']['mbw']['LB'] = $bd['mlb'];
  17300. $cell['border_details']['cellposdom'] = $bd['cpd'];
  17301. return($cell);
  17302. }
  17303. ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
  17304. ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
  17305. ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
  17306. // table Array of (w, h, bc, nr, wc, hr, cells)
  17307. // w Width of table
  17308. // h Height of table
  17309. // nc Number column
  17310. // nr Number row
  17311. // hr List of height of each row
  17312. // wc List of width of each column
  17313. // cells List of cells of each rows, cells[i][j] is a cell in the table
  17314. function _tableColumnWidth(&$table, $firstpass = false)
  17315. {
  17316. $cs = &$table['cells'];
  17317. $nc = $table['nc'];
  17318. $nr = $table['nr'];
  17319. $listspan = [];
  17320. if ($table['borders_separate']) {
  17321. $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'];
  17322. } else {
  17323. $tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];
  17324. }
  17325. // ADDED table['l'][colno]
  17326. // = total length of text approx (using $c['s']) in that column - used to approximately distribute col widths in _tableWidth
  17327. //
  17328. for ($j = 0; $j < $nc; $j++) { // columns
  17329. $wc = &$table['wc'][$j];
  17330. for ($i = 0; $i < $nr; $i++) { // rows
  17331. if (isset($cs[$i][$j]) && $cs[$i][$j]) {
  17332. $c = &$cs[$i][$j];
  17333. if ($this->simpleTables) {
  17334. if ($table['borders_separate']) { // NB twice border width
  17335. $extrcw = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
  17336. } else {
  17337. $extrcw = $table['simple']['border_details']['L']['w'] / 2 + $table['simple']['border_details']['R']['w'] / 2 + $c['padding']['L'] + $c['padding']['R'];
  17338. }
  17339. } else {
  17340. if ($this->packTableData) {
  17341. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
  17342. } else {
  17343. $br = $c['border_details']['R']['w'];
  17344. $bl = $c['border_details']['L']['w'];
  17345. }
  17346. if ($table['borders_separate']) { // NB twice border width
  17347. $extrcw = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
  17348. } else {
  17349. $extrcw = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];
  17350. }
  17351. }
  17352. // $mw = $this->GetStringWidth('W') + $extrcw ;
  17353. $mw = $extrcw; // mPDF 6
  17354. if (substr($c['a'], 0, 1) == 'D') {
  17355. $mw = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'] + $extrcw;
  17356. }
  17357. $c['absmiw'] = $mw;
  17358. if (isset($c['R']) && $c['R']) {
  17359. $c['maw'] = $c['miw'] = $this->FontSize + $extrcw;
  17360. if (isset($c['w'])) { // If cell width is specified
  17361. if ($c['miw'] < $c['w']) {
  17362. $c['miw'] = $c['w'];
  17363. }
  17364. }
  17365. if (!isset($c['colspan'])) {
  17366. if ($wc['miw'] < $c['miw']) {
  17367. $wc['miw'] = $c['miw'];
  17368. }
  17369. if ($wc['maw'] < $c['maw']) {
  17370. $wc['maw'] = $c['maw'];
  17371. }
  17372. if ($firstpass) {
  17373. if (isset($table['l'][$j])) {
  17374. $table['l'][$j] += $c['miw'];
  17375. } else {
  17376. $table['l'][$j] = $c['miw'];
  17377. }
  17378. }
  17379. }
  17380. if ($c['miw'] > $wc['miw']) {
  17381. $wc['miw'] = $c['miw'];
  17382. }
  17383. if ($wc['miw'] > $wc['maw']) {
  17384. $wc['maw'] = $wc['miw'];
  17385. }
  17386. continue;
  17387. }
  17388. if ($firstpass) {
  17389. if (isset($c['s'])) {
  17390. $c['s'] += $extrcw;
  17391. }
  17392. if (isset($c['maxs'])) {
  17393. $c['maxs'] += $extrcw;
  17394. }
  17395. if (isset($c['nestedmiw'])) {
  17396. $c['nestedmiw'] += $extrcw;
  17397. }
  17398. if (isset($c['nestedmaw'])) {
  17399. $c['nestedmaw'] += $extrcw;
  17400. }
  17401. }
  17402. // If minimum width has already been set by a nested table or inline object (image/form), use it
  17403. if (isset($c['nestedmiw']) && (!isset($this->table[1][1]['overflow']) || $this->table[1][1]['overflow'] != 'visible')) {
  17404. $miw = $c['nestedmiw'];
  17405. } else {
  17406. $miw = $mw;
  17407. }
  17408. if (isset($c['maxs']) && $c['maxs'] != '') {
  17409. $c['s'] = $c['maxs'];
  17410. }
  17411. // If maximum width has already been set by a nested table, use it
  17412. if (isset($c['nestedmaw'])) {
  17413. $c['maw'] = $c['nestedmaw'];
  17414. } else {
  17415. $c['maw'] = $c['s'];
  17416. }
  17417. if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {
  17418. if (($c['maw'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
  17419. $c['maw'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;
  17420. }
  17421. }
  17422. if (isset($c['nowrap']) && $c['nowrap']) {
  17423. $miw = $c['maw'];
  17424. }
  17425. if (isset($c['wpercent']) && $firstpass) {
  17426. if (isset($c['colspan'])) { // Not perfect - but % set on colspan is shared equally on cols.
  17427. for ($k = 0; $k < $c['colspan']; $k++) {
  17428. $table['wc'][($j + $k)]['wpercent'] = $c['wpercent'] / $c['colspan'];
  17429. }
  17430. } else {
  17431. if (isset($table['w']) && $table['w']) {
  17432. $c['w'] = $c['wpercent'] / 100 * ($table['w'] - $tblbw );
  17433. }
  17434. $wc['wpercent'] = $c['wpercent'];
  17435. }
  17436. }
  17437. if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {
  17438. if (isset($c['w']) && ($c['w'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
  17439. $c['w'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;
  17440. }
  17441. }
  17442. if (isset($c['w'])) { // If cell width is specified
  17443. if ($miw < $c['w']) {
  17444. $c['miw'] = $c['w'];
  17445. } // Cell min width = that specified
  17446. if ($miw > $c['w']) {
  17447. $c['miw'] = $c['w'] = $miw;
  17448. } // If width specified is less than minimum allowed (W) increase it
  17449. // mPDF 5.7.4 Do not set column width in colspan
  17450. // cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug
  17451. if (!isset($c['colspan'])) {
  17452. if (!isset($wc['w'])) {
  17453. $wc['w'] = 1;
  17454. } // If the Col width is not specified = set it to 1
  17455. }
  17456. // mPDF 5.7.3 cf. http://www.mpdf1.com/forum/discussion/1648/nested-table-bug-
  17457. $c['maw'] = $c['w'];
  17458. } else {
  17459. $c['miw'] = $miw;
  17460. } // If cell width not specified -> set Cell min width it to minimum allowed (W)
  17461. if (isset($c['miw']) && $c['maw'] < $c['miw']) {
  17462. $c['maw'] = $c['miw'];
  17463. } // If Cell max width < Minwidth - increase it to =
  17464. if (!isset($c['colspan'])) {
  17465. if (isset($c['miw']) && $wc['miw'] < $c['miw']) {
  17466. $wc['miw'] = $c['miw'];
  17467. } // Update Col Minimum and maximum widths
  17468. if ($wc['maw'] < $c['maw']) {
  17469. $wc['maw'] = $c['maw'];
  17470. }
  17471. if ((isset($wc['absmiw']) && $wc['absmiw'] < $c['absmiw']) || !isset($wc['absmiw'])) {
  17472. $wc['absmiw'] = $c['absmiw'];
  17473. } // Update Col Minimum and maximum widths
  17474. if (isset($table['l'][$j])) {
  17475. $table['l'][$j] += $c['s'];
  17476. } else {
  17477. $table['l'][$j] = $c['s'];
  17478. }
  17479. } else {
  17480. $listspan[] = [$i, $j];
  17481. }
  17482. // Check if minimum width of the whole column is big enough for largest word to fit
  17483. // mPDF 6
  17484. if (isset($c['textbuffer'])) {
  17485. if (isset($table['overflow']) && $table['overflow'] == 'wrap') {
  17486. $letter = true;
  17487. } // check for maximum width of letters
  17488. else {
  17489. $letter = false;
  17490. }
  17491. $minwidth = $this->TableCheckMinWidth($wc['miw'] - $extrcw, 0, $c['textbuffer'], $letter);
  17492. } else {
  17493. $minwidth = 0;
  17494. }
  17495. if ($minwidth < 0) {
  17496. // increase minimum width
  17497. if (!isset($c['colspan'])) {
  17498. $wc['miw'] = max((isset($wc['miw']) ? $wc['miw'] : 0), ((-$minwidth) + $extrcw));
  17499. } else {
  17500. $c['miw'] = max((isset($c['miw']) ? $c['miw'] : 0), ((-$minwidth) + $extrcw));
  17501. }
  17502. }
  17503. if (!isset($c['colspan'])) {
  17504. if ($wc['miw'] > $wc['maw']) {
  17505. $wc['maw'] = $wc['miw'];
  17506. } // update maximum width, if needed
  17507. }
  17508. }
  17509. unset($c);
  17510. }//rows
  17511. }//columns
  17512. // COLUMN SPANS
  17513. $wc = &$table['wc'];
  17514. foreach ($listspan as $span) {
  17515. list($i, $j) = $span;
  17516. $c = &$cs[$i][$j];
  17517. $lc = $j + $c['colspan'];
  17518. if ($lc > $nc) {
  17519. $lc = $nc;
  17520. }
  17521. $wis = $wisa = 0;
  17522. $was = $wasa = 0;
  17523. $list = [];
  17524. for ($k = $j; $k < $lc; $k++) {
  17525. if (isset($table['l'][$k])) {
  17526. if ($c['R']) {
  17527. $table['l'][$k] += $c['miw'] / $c['colspan'];
  17528. } else {
  17529. $table['l'][$k] += $c['s'] / $c['colspan'];
  17530. }
  17531. } else {
  17532. if ($c['R']) {
  17533. $table['l'][$k] = $c['miw'] / $c['colspan'];
  17534. } else {
  17535. $table['l'][$k] = $c['s'] / $c['colspan'];
  17536. }
  17537. }
  17538. $wis += $wc[$k]['miw']; // $wis is the sum of the column miw in the colspan
  17539. $was += $wc[$k]['maw']; // $was is the sum of the column maw in the colspan
  17540. if (!isset($c['w'])) {
  17541. $list[] = $k;
  17542. $wisa += $wc[$k]['miw']; // $wisa is the sum of the column miw in cells with no width specified in the colspan
  17543. $wasa += $wc[$k]['maw']; // $wasa is the sum of the column maw in cells with no width specified in the colspan
  17544. }
  17545. }
  17546. if ($c['miw'] > $wis) {
  17547. if (!$wis) {
  17548. for ($k = $j; $k < $lc; $k++) {
  17549. $wc[$k]['miw'] = $c['miw'] / $c['colspan'];
  17550. }
  17551. } elseif (!count($list) && $wis != 0) {
  17552. $wi = $c['miw'] - $wis;
  17553. for ($k = $j; $k < $lc; $k++) {
  17554. $wc[$k]['miw'] += ($wc[$k]['miw'] / $wis) * $wi;
  17555. }
  17556. } else {
  17557. $wi = $c['miw'] - $wis;
  17558. // mPDF 5.7.2 Extra min width distributed proportionately to all cells in colspan without a specified width
  17559. // cf. http://www.mpdf1.com/forum/discussion/1607#Item_4
  17560. foreach ($list as $k) {
  17561. if (!isset($wc[$k]['w']) || !$wc[$k]['w']) {
  17562. $wc[$k]['miw'] += ($wc[$k]['miw'] / $wisa) * $wi;
  17563. }
  17564. } // mPDF 5.7.2
  17565. }
  17566. }
  17567. if ($c['maw'] > $was) {
  17568. if (!$wis) {
  17569. for ($k = $j; $k < $lc; $k++) {
  17570. $wc[$k]['maw'] = $c['maw'] / $c['colspan'];
  17571. }
  17572. } elseif (!count($list) && $was != 0) {
  17573. $wi = $c['maw'] - $was;
  17574. for ($k = $j; $k < $lc; $k++) {
  17575. $wc[$k]['maw'] += ($wc[$k]['maw'] / $was) * $wi;
  17576. }
  17577. } else {
  17578. $wi = $c['maw'] - $was;
  17579. // mPDF 5.7.4 Extra max width distributed evenly to all cells in colspan without a specified width
  17580. // cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug
  17581. foreach ($list as $k) {
  17582. $wc[$k]['maw'] += $wi / count($list);
  17583. }
  17584. }
  17585. }
  17586. unset($c);
  17587. }
  17588. $checkminwidth = 0;
  17589. $checkmaxwidth = 0;
  17590. $totallength = 0;
  17591. for ($i = 0; $i < $nc; $i++) {
  17592. $checkminwidth += $table['wc'][$i]['miw'];
  17593. $checkmaxwidth += $table['wc'][$i]['maw'];
  17594. $totallength += isset($table['l']) ? $table['l'][$i] : 0;
  17595. }
  17596. if (!isset($table['w']) && $firstpass) {
  17597. $sumpc = 0;
  17598. $notset = 0;
  17599. for ($i = 0; $i < $nc; $i++) {
  17600. if (isset($table['wc'][$i]['wpercent']) && $table['wc'][$i]['wpercent']) {
  17601. $sumpc += $table['wc'][$i]['wpercent'];
  17602. } else {
  17603. $notset++;
  17604. }
  17605. }
  17606. // If sum of widths as % >= 100% and not all columns are set
  17607. // Set a nominal width of 1% for unset columns
  17608. if ($sumpc >= 100 && $notset) {
  17609. for ($i = 0; $i < $nc; $i++) {
  17610. if ((!isset($table['wc'][$i]['wpercent']) || !$table['wc'][$i]['wpercent']) &&
  17611. (!isset($table['wc'][$i]['w']) || !$table['wc'][$i]['w'])) {
  17612. $table['wc'][$i]['wpercent'] = 1;
  17613. }
  17614. }
  17615. }
  17616. if ($sumpc) { // if any percents are set
  17617. $sumnonpc = (100 - $sumpc);
  17618. $sumpc = max($sumpc, 100);
  17619. $miwleft = 0;
  17620. $miwleftcount = 0;
  17621. $miwsurplusnonpc = 0;
  17622. $maxcalcmiw = 0;
  17623. $mawleft = 0;
  17624. $mawleftcount = 0;
  17625. $mawsurplusnonpc = 0;
  17626. $maxcalcmaw = 0;
  17627. $mawnon = 0;
  17628. $miwnon = 0;
  17629. for ($i = 0; $i < $nc; $i++) {
  17630. if (isset($table['wc'][$i]['wpercent'])) {
  17631. $maxcalcmiw = max($maxcalcmiw, ($table['wc'][$i]['miw'] * $sumpc / $table['wc'][$i]['wpercent']));
  17632. $maxcalcmaw = max($maxcalcmaw, ($table['wc'][$i]['maw'] * $sumpc / $table['wc'][$i]['wpercent']));
  17633. } else {
  17634. $miwleft += $table['wc'][$i]['miw'];
  17635. $mawleft += $table['wc'][$i]['maw'];
  17636. if (!isset($table['wc'][$i]['w'])) {
  17637. $miwleftcount++;
  17638. $mawleftcount++;
  17639. }
  17640. }
  17641. }
  17642. if ($miwleft && $sumnonpc > 0) {
  17643. $miwnon = $miwleft * 100 / $sumnonpc;
  17644. }
  17645. if ($mawleft && $sumnonpc > 0) {
  17646. $mawnon = $mawleft * 100 / $sumnonpc;
  17647. }
  17648. if (($miwnon > $checkminwidth || $maxcalcmiw > $checkminwidth) && $this->keep_table_proportions) {
  17649. if ($miwnon > $maxcalcmiw) {
  17650. $miwsurplusnonpc = round((($miwnon * $sumnonpc / 100) - $miwleft), 3);
  17651. $checkminwidth = $miwnon;
  17652. } else {
  17653. $checkminwidth = $maxcalcmiw;
  17654. }
  17655. for ($i = 0; $i < $nc; $i++) {
  17656. if (isset($table['wc'][$i]['wpercent'])) {
  17657. $newmiw = $checkminwidth * $table['wc'][$i]['wpercent'] / 100;
  17658. if ($table['wc'][$i]['miw'] < $newmiw) {
  17659. $table['wc'][$i]['miw'] = $newmiw;
  17660. }
  17661. $table['wc'][$i]['w'] = 1;
  17662. } elseif ($miwsurplusnonpc && !$table['wc'][$i]['w']) {
  17663. $table['wc'][$i]['miw'] += $miwsurplusnonpc / $miwleftcount;
  17664. }
  17665. }
  17666. }
  17667. if (($mawnon > $checkmaxwidth || $maxcalcmaw > $checkmaxwidth)) {
  17668. if ($mawnon > $maxcalcmaw) {
  17669. $mawsurplusnonpc = round((($mawnon * $sumnonpc / 100) - $mawleft), 3);
  17670. $checkmaxwidth = $mawnon;
  17671. } else {
  17672. $checkmaxwidth = $maxcalcmaw;
  17673. }
  17674. for ($i = 0; $i < $nc; $i++) {
  17675. if (isset($table['wc'][$i]['wpercent'])) {
  17676. $newmaw = $checkmaxwidth * $table['wc'][$i]['wpercent'] / 100;
  17677. if ($table['wc'][$i]['maw'] < $newmaw) {
  17678. $table['wc'][$i]['maw'] = $newmaw;
  17679. }
  17680. $table['wc'][$i]['w'] = 1;
  17681. } elseif ($mawsurplusnonpc && !$table['wc'][$i]['w']) {
  17682. $table['wc'][$i]['maw'] += $mawsurplusnonpc / $mawleftcount;
  17683. }
  17684. if ($table['wc'][$i]['maw'] < $table['wc'][$i]['miw']) {
  17685. $table['wc'][$i]['maw'] = $table['wc'][$i]['miw'];
  17686. }
  17687. }
  17688. }
  17689. if ($checkminwidth > $checkmaxwidth) {
  17690. $checkmaxwidth = $checkminwidth;
  17691. }
  17692. }
  17693. }
  17694. if (isset($table['wpercent']) && $table['wpercent']) {
  17695. $checkminwidth *= (100 / $table['wpercent']);
  17696. $checkmaxwidth *= (100 / $table['wpercent']);
  17697. }
  17698. $checkminwidth += $tblbw;
  17699. $checkmaxwidth += $tblbw;
  17700. // Table['miw'] set by percent in first pass may be larger than sum of column miw
  17701. if ((isset($table['miw']) && $checkminwidth > $table['miw']) || !isset($table['miw'])) {
  17702. $table['miw'] = $checkminwidth;
  17703. }
  17704. if ((isset($table['maw']) && $checkmaxwidth > $table['maw']) || !isset($table['maw'])) {
  17705. $table['maw'] = $checkmaxwidth;
  17706. }
  17707. $table['tl'] = $totallength;
  17708. // mPDF 6
  17709. if ($this->table_rotate) {
  17710. $mxw = $this->tbrot_maxw;
  17711. } else {
  17712. $mxw = $this->blk[$this->blklvl]['inner_width'];
  17713. }
  17714. if (!isset($table['overflow'])) {
  17715. $table['overflow'] = null;
  17716. }
  17717. if ($table['overflow'] == 'visible') {
  17718. return [0, 0];
  17719. } elseif ($table['overflow'] == 'hidden' && !$this->table_rotate && !$this->ColActive && $checkminwidth > $mxw) {
  17720. $table['w'] = $table['miw'];
  17721. return [0, 0];
  17722. }
  17723. // elseif ($table['overflow']=='wrap') { return array(0,0); } // mPDF 6
  17724. if (isset($table['w']) && $table['w']) {
  17725. if ($table['w'] >= $checkminwidth && $table['w'] <= $mxw) {
  17726. $table['maw'] = $mxw = $table['w'];
  17727. } elseif ($table['w'] >= $checkminwidth && $table['w'] > $mxw && $this->keep_table_proportions) {
  17728. $checkminwidth = $table['w'];
  17729. } elseif ($table['w'] < $checkminwidth && $checkminwidth < $mxw && $this->keep_table_proportions) {
  17730. $table['maw'] = $table['w'] = $checkminwidth;
  17731. } else {
  17732. unset($table['w']);
  17733. }
  17734. }
  17735. $ratio = $checkminwidth / $mxw;
  17736. if ($checkminwidth > $mxw) {
  17737. return [($ratio + 0.001), $checkminwidth]; // 0.001 to allow for rounded numbers when resizing
  17738. }
  17739. unset($cs);
  17740. return [0, 0];
  17741. }
  17742. function _tableWidth(&$table)
  17743. {
  17744. $widthcols = &$table['wc'];
  17745. $numcols = $table['nc'];
  17746. $tablewidth = 0;
  17747. if ($table['borders_separate']) {
  17748. $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'];
  17749. } else {
  17750. $tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];
  17751. }
  17752. if ($table['level'] > 1 && isset($table['w'])) {
  17753. if (isset($table['wpercent']) && $table['wpercent']) {
  17754. $table['w'] = $temppgwidth = (($table['w'] - $tblbw) * $table['wpercent'] / 100) + $tblbw;
  17755. } else {
  17756. $temppgwidth = $table['w'];
  17757. }
  17758. } elseif ($this->table_rotate) {
  17759. $temppgwidth = $this->tbrot_maxw;
  17760. // 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
  17761. $enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];
  17762. if ($enddiv / $temppgwidth < 0.05) {
  17763. $temppgwidth -= $enddiv;
  17764. }
  17765. } else {
  17766. if (isset($table['w']) && $table['w'] < $this->blk[$this->blklvl]['inner_width']) {
  17767. $notfullwidth = 1;
  17768. $temppgwidth = $table['w'];
  17769. } elseif ($table['overflow'] == 'visible' && $table['level'] == 1) {
  17770. $temppgwidth = null;
  17771. } elseif ($table['overflow'] == 'hidden' && !$this->ColActive && isset($table['w']) && $table['w'] > $this->blk[$this->blklvl]['inner_width'] && $table['w'] == $table) {
  17772. // $temppgwidth = $this->blk[$this->blklvl]['inner_width'];
  17773. $temppgwidth = $table['w'];
  17774. } else {
  17775. $temppgwidth = $this->blk[$this->blklvl]['inner_width'];
  17776. }
  17777. }
  17778. $totaltextlength = 0; // Added - to sum $table['l'][colno]
  17779. $totalatextlength = 0; // Added - to sum $table['l'][colno] for those columns where width not set
  17780. $percentages_set = 0;
  17781. for ($i = 0; $i < $numcols; $i++) {
  17782. if (isset($widthcols[$i]['wpercent'])) {
  17783. $tablewidth += $widthcols[$i]['maw'];
  17784. $percentages_set = 1;
  17785. } elseif (isset($widthcols[$i]['w'])) {
  17786. $tablewidth += $widthcols[$i]['miw'];
  17787. } else {
  17788. $tablewidth += $widthcols[$i]['maw'];
  17789. }
  17790. $totaltextlength += isset($table['l']) ? $table['l'][$i] : 0;
  17791. }
  17792. if (!$totaltextlength) {
  17793. $totaltextlength = 1;
  17794. }
  17795. $tablewidth += $tblbw; // Outer half of table borders
  17796. if ($tablewidth > $temppgwidth) {
  17797. $table['w'] = $temppgwidth;
  17798. } elseif ($tablewidth < $temppgwidth && !isset($table['w']) && $percentages_set) { // if any widths set as percentages and max width fits < page width
  17799. $table['w'] = $table['maw'];
  17800. }
  17801. // if table width is set and is > allowed width
  17802. if (isset($table['w']) && $table['w'] > $temppgwidth) {
  17803. $table['w'] = $temppgwidth;
  17804. }
  17805. // IF the table width is now set - Need to distribute columns widths
  17806. // mPDF 5.7.3
  17807. // If the table width is already set to the maximum width (e.g. nested table), then use maximum column widths exactly
  17808. if (isset($table['w']) && ($table['w'] == $tablewidth) && !$percentages_set) {
  17809. // This sets the columns all to maximum width
  17810. for ($i = 0; $i < $numcols; $i++) {
  17811. $widthcols[$i] = $widthcols[$i]['maw'];
  17812. }
  17813. } elseif (isset($table['w'])) { // elseif the table width is set distribute width using algorithm
  17814. $wis = $wisa = 0;
  17815. $list = [];
  17816. $notsetlist = [];
  17817. for ($i = 0; $i < $numcols; $i++) {
  17818. $wis += $widthcols[$i]['miw'];
  17819. if (!isset($widthcols[$i]['w']) || ($widthcols[$i]['w'] && $table['w'] > $temppgwidth && !$this->keep_table_proportions && !$notfullwidth )) {
  17820. $list[] = $i;
  17821. $wisa += $widthcols[$i]['miw'];
  17822. $totalatextlength += $table['l'][$i];
  17823. }
  17824. }
  17825. if (!$totalatextlength) {
  17826. $totalatextlength = 1;
  17827. }
  17828. // Allocate spare (more than col's minimum width) across the cols according to their approx total text length
  17829. // Do it by setting minimum width here
  17830. if ($table['w'] > $wis + $tblbw) {
  17831. // First set any cell widths set as percentages
  17832. if ($table['w'] < $temppgwidth || $this->keep_table_proportions) {
  17833. for ($k = 0; $k < $numcols; $k++) {
  17834. if (isset($widthcols[$k]['wpercent'])) {
  17835. $curr = $widthcols[$k]['miw'];
  17836. $widthcols[$k]['miw'] = ($table['w'] - $tblbw) * $widthcols[$k]['wpercent'] / 100;
  17837. $wis += $widthcols[$k]['miw'] - $curr;
  17838. $wisa += $widthcols[$k]['miw'] - $curr;
  17839. }
  17840. }
  17841. }
  17842. // Now allocate surplus up to maximum width of each column
  17843. $surplus = 0;
  17844. $ttl = 0; // number of surplus columns
  17845. if (!count($list)) {
  17846. $wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute
  17847. for ($k = 0; $k < $numcols; $k++) {
  17848. $spareratio = ($table['l'][$k] / $totaltextlength); // gives ratio to divide up free space
  17849. // Don't allocate more than Maximum required width - save rest in surplus
  17850. if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3
  17851. $surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);
  17852. $widthcols[$k]['miw'] = $widthcols[$k]['maw'];
  17853. } else {
  17854. $notsetlist[] = $k;
  17855. $ttl += $table['l'][$k];
  17856. $widthcols[$k]['miw'] += ($wi * $spareratio);
  17857. }
  17858. }
  17859. } else {
  17860. $wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute
  17861. foreach ($list as $k) {
  17862. $spareratio = ($table['l'][$k] / $totalatextlength); // gives ratio to divide up free space
  17863. // Don't allocate more than Maximum required width - save rest in surplus
  17864. if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3
  17865. $surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);
  17866. $widthcols[$k]['miw'] = $widthcols[$k]['maw'];
  17867. } else {
  17868. $notsetlist[] = $k;
  17869. $ttl += $table['l'][$k];
  17870. $widthcols[$k]['miw'] += ($wi * $spareratio);
  17871. }
  17872. }
  17873. }
  17874. // If surplus still left over apportion it across columns
  17875. if ($surplus) {
  17876. if (count($notsetlist) && count($notsetlist) < $numcols) { // if some are set only add to remaining - otherwise add to all of them
  17877. foreach ($notsetlist as $i) {
  17878. if ($ttl) {
  17879. $widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;
  17880. }
  17881. }
  17882. } elseif (count($list) && count($list) < $numcols) { // If some widths are defined, and others have been added up to their maxmum
  17883. foreach ($list as $i) {
  17884. $widthcols[$i]['miw'] += $surplus / count($list);
  17885. }
  17886. } elseif ($numcols) { // If all columns
  17887. $ttl = array_sum($table['l']);
  17888. if ($ttl) {
  17889. for ($i = 0; $i < $numcols; $i++) {
  17890. $widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;
  17891. }
  17892. }
  17893. }
  17894. }
  17895. }
  17896. // This sets the columns all to minimum width (which has been increased above if appropriate)
  17897. for ($i = 0; $i < $numcols; $i++) {
  17898. $widthcols[$i] = $widthcols[$i]['miw'];
  17899. }
  17900. // TABLE NOT WIDE ENOUGH EVEN FOR MINIMUM CONTENT WIDTH
  17901. // If sum of column widths set are too wide for table
  17902. $checktablewidth = 0;
  17903. for ($i = 0; $i < $numcols; $i++) {
  17904. $checktablewidth += $widthcols[$i];
  17905. }
  17906. if ($checktablewidth > ($temppgwidth + 0.001 - $tblbw)) {
  17907. $usedup = 0;
  17908. $numleft = 0;
  17909. for ($i = 0; $i < $numcols; $i++) {
  17910. if ((isset($widthcols[$i]) && $widthcols[$i] > (($temppgwidth - $tblbw) / $numcols)) && (!isset($widthcols[$i]['w']))) {
  17911. $numleft++;
  17912. unset($widthcols[$i]);
  17913. } else {
  17914. $usedup += $widthcols[$i];
  17915. }
  17916. }
  17917. for ($i = 0; $i < $numcols; $i++) {
  17918. if (!isset($widthcols[$i]) || !$widthcols[$i]) {
  17919. $widthcols[$i] = ((($temppgwidth - $tblbw) - $usedup) / ($numleft));
  17920. }
  17921. }
  17922. }
  17923. } else { // table has no width defined
  17924. $table['w'] = $tablewidth;
  17925. for ($i = 0; $i < $numcols; $i++) {
  17926. if (isset($widthcols[$i]['wpercent']) && $this->keep_table_proportions) {
  17927. $colwidth = $widthcols[$i]['maw'];
  17928. } elseif (isset($widthcols[$i]['w'])) {
  17929. $colwidth = $widthcols[$i]['miw'];
  17930. } else {
  17931. $colwidth = $widthcols[$i]['maw'];
  17932. }
  17933. unset($widthcols[$i]);
  17934. $widthcols[$i] = $colwidth;
  17935. }
  17936. }
  17937. if ($table['overflow'] === 'visible' && $table['level'] == 1) {
  17938. if ($tablewidth > $this->blk[$this->blklvl]['inner_width']) {
  17939. for ($j = 0; $j < $numcols; $j++) { // columns
  17940. for ($i = 0; $i < $table['nr']; $i++) { // rows
  17941. if (isset($table['cells'][$i][$j]) && $table['cells'][$i][$j]) {
  17942. $colspan = (isset($table['cells'][$i][$j]['colspan']) ? $table['cells'][$i][$j]['colspan'] : 1);
  17943. if ($colspan > 1) {
  17944. $w = 0;
  17945. for ($c = $j; $c < ($j + $colspan); $c++) {
  17946. $w += $widthcols[$c];
  17947. }
  17948. if ($w > $this->blk[$this->blklvl]['inner_width']) {
  17949. $diff = $w - ($this->blk[$this->blklvl]['inner_width'] - $tblbw);
  17950. for ($c = $j; $c < ($j + $colspan); $c++) {
  17951. $widthcols[$c] -= $diff * ($widthcols[$c] / $w);
  17952. }
  17953. $table['w'] -= $diff;
  17954. $table['csp'][$j] = $w - $diff;
  17955. }
  17956. }
  17957. }
  17958. }
  17959. }
  17960. }
  17961. $pgNo = 0;
  17962. $currWc = 0;
  17963. for ($i = 0; $i < $numcols; $i++) { // columns
  17964. if (isset($table['csp'][$i])) {
  17965. $w = $table['csp'][$i];
  17966. unset($table['csp'][$i]);
  17967. } else {
  17968. $w = $widthcols[$i];
  17969. }
  17970. if (($currWc + $w + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
  17971. $pgNo++;
  17972. $currWc = $widthcols[$i];
  17973. } else {
  17974. $currWc += $widthcols[$i];
  17975. }
  17976. $table['colPg'][$i] = $pgNo;
  17977. }
  17978. }
  17979. }
  17980. function _tableHeight(&$table)
  17981. {
  17982. $level = $table['level'];
  17983. $levelid = $table['levelid'];
  17984. $cells = &$table['cells'];
  17985. $numcols = $table['nc'];
  17986. $numrows = $table['nr'];
  17987. $listspan = [];
  17988. $checkmaxheight = 0;
  17989. $headerrowheight = 0;
  17990. $checkmaxheightplus = 0;
  17991. $headerrowheightplus = 0;
  17992. $firstrowheight = 0;
  17993. $footerrowheight = 0;
  17994. $footerrowheightplus = 0;
  17995. if ($this->table_rotate) {
  17996. $temppgheight = $this->tbrot_maxh;
  17997. $remainingpage = $this->tbrot_maxh;
  17998. } else {
  17999. $temppgheight = ($this->h - $this->bMargin - $this->tMargin) - $this->kwt_height;
  18000. $remainingpage = ($this->h - $this->bMargin - $this->y) - $this->kwt_height;
  18001. // If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin)
  18002. // then allow for this
  18003. $enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $table['margin']['B'];
  18004. if ($remainingpage > $enddiv && $enddiv / $remainingpage < 0.05) {
  18005. $remainingpage -= $enddiv;
  18006. } elseif ($remainingpage == 0) {
  18007. $remainingpage = 0.001;
  18008. }
  18009. if ($temppgheight > $enddiv && $enddiv / $temppgheight < 0.05) {
  18010. $temppgheight -= $enddiv;
  18011. } elseif ($temppgheight == 0) {
  18012. $temppgheight = 0.001;
  18013. }
  18014. }
  18015. if ($remainingpage < 0) {
  18016. $remainingpage = 0.001;
  18017. }
  18018. if ($temppgheight < 0) {
  18019. $temppgheight = 0.001;
  18020. }
  18021. for ($i = 0; $i < $numrows; $i++) { // rows
  18022. $heightrow = &$table['hr'][$i];
  18023. for ($j = 0; $j < $numcols; $j++) { // columns
  18024. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  18025. $c = &$cells[$i][$j];
  18026. if ($this->simpleTables) {
  18027. if ($table['borders_separate']) { // NB twice border width
  18028. $extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) + ($c['padding']['L'] + $c['padding']['R']) + $table['border_spacing_H'];
  18029. $extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) + ($c['padding']['T'] + $c['padding']['B']) + $table['border_spacing_V'];
  18030. } else {
  18031. $extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + ($c['padding']['L'] + $c['padding']['R']);
  18032. $extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) / 2 + ($c['padding']['T'] + $c['padding']['B']);
  18033. }
  18034. } else {
  18035. if ($this->packTableData) {
  18036. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
  18037. } else {
  18038. $bt = $c['border_details']['T']['w'];
  18039. $bb = $c['border_details']['B']['w'];
  18040. $br = $c['border_details']['R']['w'];
  18041. $bl = $c['border_details']['L']['w'];
  18042. }
  18043. if ($table['borders_separate']) { // NB twice border width
  18044. $extraWLR = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
  18045. $extrh = $bt + $bb + $c['padding']['T'] + $c['padding']['B'] + $table['border_spacing_V'];
  18046. } else {
  18047. $extraWLR = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];
  18048. $extrh = $bt / 2 + $bb / 2 + $c['padding']['T'] + $c['padding']['B'];
  18049. }
  18050. }
  18051. if ($table['overflow'] == 'visible' && $level == 1) {
  18052. list($x, $cw) = $this->_splitTableGetWidth($table, $i, $j);
  18053. } else {
  18054. list($x, $cw) = $this->_tableGetWidth($table, $i, $j);
  18055. }
  18056. // Get CELL HEIGHT
  18057. // ++ extra parameter forces wrap to break word
  18058. if ($c['R'] && isset($c['textbuffer'])) {
  18059. $str = '';
  18060. foreach ($c['textbuffer'] as $t) {
  18061. $str .= $t[0] . ' ';
  18062. }
  18063. $str = rtrim($str);
  18064. $s_fs = $this->FontSizePt;
  18065. $s_f = $this->FontFamily;
  18066. $s_st = $this->FontStyle;
  18067. $this->SetFont($c['textbuffer'][0][4], $c['textbuffer'][0][2], $c['textbuffer'][0][11] / $this->shrin_k, true, true);
  18068. $tempch = $this->GetStringWidth($str, true, $c['textbuffer'][0][18], $c['textbuffer'][0][8]);
  18069. if ($c['R'] >= 45 && $c['R'] < 90) {
  18070. $tempch = ((sin(deg2rad($c['R']))) * $tempch ) + ((sin(deg2rad($c['R']))) * (($c['textbuffer'][0][11] / Mpdf::SCALE) / $this->shrin_k));
  18071. }
  18072. $this->SetFont($s_f, $s_st, $s_fs, true, true);
  18073. $ch = ($tempch ) + $extrh;
  18074. } else {
  18075. if (isset($c['textbuffer']) && !empty($c['textbuffer'])) {
  18076. $this->cellLineHeight = $c['cellLineHeight'];
  18077. $this->cellLineStackingStrategy = $c['cellLineStackingStrategy'];
  18078. $this->cellLineStackingShift = $c['cellLineStackingShift'];
  18079. $this->divwidth = $cw - $extraWLR;
  18080. $tempch = $this->printbuffer($c['textbuffer'], '', true, true);
  18081. } else {
  18082. $tempch = 0;
  18083. }
  18084. // Added cellpadding top and bottom. (Lineheight already adjusted)
  18085. $ch = $tempch + $extrh;
  18086. }
  18087. // If height is defined and it is bigger than calculated $ch then update values
  18088. if (isset($c['h']) && $c['h'] > $ch) {
  18089. $c['mih'] = $ch; // in order to keep valign working
  18090. $ch = $c['h'];
  18091. } else {
  18092. $c['mih'] = $ch;
  18093. }
  18094. if (isset($c['rowspan'])) {
  18095. $listspan[] = [$i, $j];
  18096. } elseif ($heightrow < $ch) {
  18097. $heightrow = $ch;
  18098. }
  18099. // this is the extra used in _tableWrite to determine whether to trigger a page change
  18100. if ($table['borders_separate']) {
  18101. if ($i == ($numrows - 1) || (isset($c['rowspan']) && ($i + $c['rowspan']) == ($numrows))) {
  18102. $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  18103. } else {
  18104. $extra = $table['border_spacing_V'] / 2;
  18105. }
  18106. } else {
  18107. if (!$this->simpleTables) {
  18108. $extra = $bb / 2;
  18109. } elseif ($this->simpleTables) {
  18110. $extra = $table['simple']['border_details']['B']['w'] / 2;
  18111. }
  18112. }
  18113. if (isset($table['is_thead'][$i]) && $table['is_thead'][$i]) {
  18114. if ($j == 0) {
  18115. $headerrowheight += $ch;
  18116. $headerrowheightplus += $ch + $extra;
  18117. }
  18118. } elseif (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {
  18119. if ($j == 0) {
  18120. $footerrowheight += $ch;
  18121. $footerrowheightplus += $ch + $extra;
  18122. }
  18123. } else {
  18124. $checkmaxheight = max($checkmaxheight, $ch);
  18125. $checkmaxheightplus = max($checkmaxheightplus, $ch + $extra);
  18126. }
  18127. if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {
  18128. $firstrowheight = max($ch, $firstrowheight);
  18129. }
  18130. unset($c);
  18131. }
  18132. }//end of columns
  18133. }//end of rows
  18134. $heightrow = &$table['hr'];
  18135. foreach ($listspan as $span) {
  18136. list($i, $j) = $span;
  18137. $c = &$cells[$i][$j];
  18138. $lr = $i + $c['rowspan'];
  18139. if ($lr > $numrows) {
  18140. $lr = $numrows;
  18141. }
  18142. $hs = $hsa = 0;
  18143. $list = [];
  18144. for ($k = $i; $k < $lr; $k++) {
  18145. $hs += $heightrow[$k];
  18146. // mPDF 6
  18147. $sh = false; // specified height
  18148. for ($m = 0; $m < $numcols; $m++) { // columns
  18149. $tc = &$cells[$k][$m];
  18150. if (isset($tc['rowspan'])) {
  18151. continue;
  18152. }
  18153. if (isset($tc['h'])) {
  18154. $sh = true;
  18155. break;
  18156. }
  18157. }
  18158. if (!$sh) {
  18159. $list[] = $k;
  18160. }
  18161. }
  18162. if ($table['borders_separate']) {
  18163. if ($i == ($numrows - 1) || ($i + $c['rowspan']) == ($numrows)) {
  18164. $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  18165. } else {
  18166. $extra = $table['border_spacing_V'] / 2;
  18167. }
  18168. } else {
  18169. if (!$this->simpleTables) {
  18170. if ($this->packTableData) {
  18171. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
  18172. } else {
  18173. $bb = $c['border_details']['B']['w'];
  18174. }
  18175. $extra = $bb / 2;
  18176. } elseif ($this->simpleTables) {
  18177. $extra = $table['simple']['border_details']['B']['w'] / 2;
  18178. }
  18179. }
  18180. if (!empty($table['is_thead'][$i])) {
  18181. $headerrowheight = max($headerrowheight, $hs);
  18182. $headerrowheightplus = max($headerrowheightplus, $hs + $extra);
  18183. } elseif (!empty($table['is_tfoot'][$i])) {
  18184. $footerrowheight = max($footerrowheight, $hs);
  18185. $footerrowheightplus = max($footerrowheightplus, $hs + $extra);
  18186. } else {
  18187. $checkmaxheight = max($checkmaxheight, $hs);
  18188. $checkmaxheightplus = max($checkmaxheightplus, $hs + $extra);
  18189. }
  18190. if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {
  18191. $firstrowheight = max($hs, $firstrowheight);
  18192. }
  18193. if ($c['mih'] > $hs) {
  18194. if (!$hs) {
  18195. for ($k = $i; $k < $lr; $k++) {
  18196. $heightrow[$k] = $c['mih'] / $c['rowspan'];
  18197. }
  18198. } elseif (!count($list)) { // no rows in the rowspan have a height specified, so share amongst all rows equally
  18199. $hi = $c['mih'] - $hs;
  18200. for ($k = $i; $k < $lr; $k++) {
  18201. $heightrow[$k] += ($heightrow[$k] / $hs) * $hi;
  18202. }
  18203. } else {
  18204. $hi = $c['mih'] - $hs; // mPDF 6
  18205. foreach ($list as $k) {
  18206. $heightrow[$k] += $hi / (count($list)); // mPDF 6
  18207. }
  18208. }
  18209. }
  18210. unset($c);
  18211. // If rowspans overlap so that one or more rows do not have a height set...
  18212. // i.e. for one or more rows, the only cells (explicit) in that row have rowspan>1
  18213. // so heightrow is still == 0
  18214. if ($heightrow[$i] == 0) {
  18215. // Get row extent to analyse above and below
  18216. $top = $i;
  18217. foreach ($listspan as $checkspan) {
  18218. list($cki, $ckj) = $checkspan;
  18219. $c = &$cells[$cki][$ckj];
  18220. if (isset($c['rowspan']) && $c['rowspan'] > 1) {
  18221. if (($cki + $c['rowspan'] - 1) >= $i) {
  18222. $top = min($top, $cki);
  18223. }
  18224. }
  18225. }
  18226. $bottom = $i + $c['rowspan'] - 1;
  18227. // Check for overconstrained conditions
  18228. for ($k = $top; $k <= $bottom; $k++) {
  18229. // if ['hr'] for any of the others is also 0, then abort (too complicated)
  18230. if ($k != $i && $heightrow[$k] == 0) {
  18231. break(1);
  18232. }
  18233. // check again that top and bottom are not crossed by rowspans - or abort (too complicated)
  18234. if ($k == $top) {
  18235. // ???? take account of colspan as well???
  18236. for ($m = 0; $m < $numcols; $m++) { // columns
  18237. if (!isset($cells[$k][$m]) || $cells[$k][$m] == 0) {
  18238. break(2);
  18239. }
  18240. }
  18241. } elseif ($k == $bottom) {
  18242. // ???? take account of colspan as well???
  18243. for ($m = 0; $m < $numcols; $m++) { // columns
  18244. $c = &$cells[$k][$m];
  18245. if (isset($c['rowspan']) && $c['rowspan'] > 1) {
  18246. break(2);
  18247. }
  18248. }
  18249. }
  18250. }
  18251. // By columns add up col height using ['h'] if set or ['mih'] if not
  18252. // Intentionally do not substract border-spacing
  18253. $colH = [];
  18254. $extH = 0;
  18255. $newhr = [];
  18256. for ($m = 0; $m < $numcols; $m++) { // columns
  18257. for ($k = $top; $k <= $bottom; $k++) {
  18258. if (isset($cells[$k][$m]) && $cells[$k][$m] != 0) {
  18259. $c = &$cells[$k][$m];
  18260. if (isset($c['h']) && $c['h']) {
  18261. $useh = $c['h'];
  18262. } // ???? take account of colspan as well???
  18263. else {
  18264. $useh = $c['mih'];
  18265. }
  18266. if (isset($colH[$m])) {
  18267. $colH[$m] += $useh;
  18268. } else {
  18269. $colH[$m] = $useh;
  18270. }
  18271. if (!isset($c['rowspan']) || $c['rowspan'] < 2) {
  18272. $newhr[$k] = max((isset($newhr[$k]) ? $newhr[$k] : 0), $useh);
  18273. }
  18274. }
  18275. }
  18276. $extH = max($extH, $colH[$m]); // mPDF 6
  18277. }
  18278. $newhr[$i] = $extH - array_sum($newhr);
  18279. for ($k = $top; $k <= $bottom; $k++) {
  18280. $heightrow[$k] = $newhr[$k];
  18281. }
  18282. }
  18283. unset($c);
  18284. }
  18285. $table['h'] = array_sum($heightrow);
  18286. unset($heightrow);
  18287. if ($table['borders_separate']) {
  18288. $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'];
  18289. } else {
  18290. $table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['max_cell_border_width']['T'] / 2 + $table['max_cell_border_width']['B'] / 2;
  18291. }
  18292. $maxrowheight = $checkmaxheightplus + $headerrowheightplus + $footerrowheightplus;
  18293. $maxfirstrowheight = $firstrowheight + $headerrowheightplus + $footerrowheightplus; // includes thead, 1st row and tfoot
  18294. return [$table['h'], $maxrowheight, $temppgheight, $remainingpage, $maxfirstrowheight];
  18295. }
  18296. function _tableGetWidth(&$table, $i, $j)
  18297. {
  18298. $cell = &$table['cells'][$i][$j];
  18299. if ($cell) {
  18300. if (isset($cell['x0'])) {
  18301. return [$cell['x0'], $cell['w0']];
  18302. }
  18303. $x = 0;
  18304. $widthcols = &$table['wc'];
  18305. for ($k = 0; $k < $j; $k++) {
  18306. $x += $widthcols[$k];
  18307. }
  18308. $w = $widthcols[$j];
  18309. if (isset($cell['colspan'])) {
  18310. for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) {
  18311. $w += $widthcols[$k];
  18312. }
  18313. }
  18314. $cell['x0'] = $x;
  18315. $cell['w0'] = $w;
  18316. return [$x, $w];
  18317. }
  18318. return [0, 0];
  18319. }
  18320. function _splitTableGetWidth(&$table, $i, $j)
  18321. {
  18322. $cell = &$table['cells'][$i][$j];
  18323. if ($cell) {
  18324. if (isset($cell['x0'])) {
  18325. return [$cell['x0'], $cell['w0']];
  18326. }
  18327. $x = 0;
  18328. $widthcols = &$table['wc'];
  18329. $pg = $table['colPg'][$j];
  18330. for ($k = 0; $k < $j; $k++) {
  18331. if ($table['colPg'][$k] == $pg) {
  18332. $x += $widthcols[$k];
  18333. }
  18334. }
  18335. $w = $widthcols[$j];
  18336. if (isset($cell['colspan'])) {
  18337. for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) {
  18338. if ($table['colPg'][$k] == $pg) {
  18339. $w += $widthcols[$k];
  18340. }
  18341. }
  18342. }
  18343. $cell['x0'] = $x;
  18344. $cell['w0'] = $w;
  18345. return [$x, $w];
  18346. }
  18347. return [0, 0];
  18348. }
  18349. function _tableGetHeight(&$table, $i, $j)
  18350. {
  18351. $cell = &$table['cells'][$i][$j];
  18352. if ($cell) {
  18353. if (isset($cell['y0'])) {
  18354. return [$cell['y0'], $cell['h0']];
  18355. }
  18356. $y = 0;
  18357. $heightrow = &$table['hr'];
  18358. for ($k = 0; $k < $i; $k++) {
  18359. $y += $heightrow[$k];
  18360. }
  18361. $h = $heightrow[$i];
  18362. if (isset($cell['rowspan'])) {
  18363. for ($k = $i + $cell['rowspan'] - 1; $k > $i; $k--) {
  18364. if (array_key_exists($k, $heightrow)) {
  18365. $h += $heightrow[$k];
  18366. } else {
  18367. $this->logger->debug('Possible non-wellformed HTML markup in a table', ['context' => LogContext::HTML_MARKUP]);
  18368. }
  18369. }
  18370. }
  18371. $cell['y0'] = $y;
  18372. $cell['h0'] = $h;
  18373. return [$y, $h];
  18374. }
  18375. return [0, 0];
  18376. }
  18377. function _tableGetMaxRowHeight($table, $row)
  18378. {
  18379. if ($row == $table['nc'] - 1) {
  18380. return $table['hr'][$row];
  18381. }
  18382. $maxrowheight = $table['hr'][$row];
  18383. for ($i = $row + 1; $i < $table['nr']; $i++) {
  18384. $cellsset = 0;
  18385. for ($j = 0; $j < $table['nc']; $j++) {
  18386. if (!empty($table['cells'][$i][$j])) {
  18387. if (isset($table['cells'][$i][$j]['colspan'])) {
  18388. $cellsset += $table['cells'][$i][$j]['colspan'];
  18389. } else {
  18390. $cellsset += 1;
  18391. }
  18392. }
  18393. }
  18394. if ($cellsset == $table['nc']) {
  18395. return $maxrowheight;
  18396. } else {
  18397. $maxrowheight += $table['hr'][$i];
  18398. }
  18399. }
  18400. return $maxrowheight;
  18401. }
  18402. // CHANGED TO ALLOW TABLE BORDER TO BE SPECIFIED CORRECTLY - added border_details
  18403. function _tableRect($x, $y, $w, $h, $bord = -1, $details = [], $buffer = false, $bSeparate = false, $cort = 'cell', $tablecorner = '', $bsv = 0, $bsh = 0)
  18404. {
  18405. $cellBorderOverlay = [];
  18406. if ($bord == -1) {
  18407. $this->Rect($x, $y, $w, $h);
  18408. } elseif ($this->simpleTables && ($cort == 'cell')) {
  18409. $this->SetLineWidth($details['L']['w']);
  18410. if ($details['L']['c']) {
  18411. $this->SetDColor($details['L']['c']);
  18412. } else {
  18413. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  18414. }
  18415. $this->SetLineJoin(0);
  18416. $this->Rect($x, $y, $w, $h);
  18417. } elseif ($bord) {
  18418. if (!$bSeparate && $buffer) {
  18419. $priority = 'LRTB';
  18420. for ($p = 0; $p < strlen($priority); $p++) {
  18421. $side = $priority[$p];
  18422. $details['p'] = $side;
  18423. $dom = 0;
  18424. if (isset($details[$side]['w'])) {
  18425. $dom += ($details[$side]['w'] * 100000);
  18426. }
  18427. if (isset($details[$side]['style'])) {
  18428. $dom += (array_search($details[$side]['style'], $this->borderstyles) * 100);
  18429. }
  18430. if (isset($details[$side]['dom'])) {
  18431. $dom += ($details[$side]['dom'] * 10);
  18432. }
  18433. // Precedence to darker colours at joins
  18434. $coldom = 0;
  18435. if (isset($details[$side]['c']) && is_array($details[$side]['c'])) {
  18436. if ($details[$side]['c'][0] == 3) { // RGB
  18437. $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);
  18438. }
  18439. } // 10 black - 0 white
  18440. if ($coldom) {
  18441. $dom += $coldom;
  18442. }
  18443. // Lastly precedence to RIGHT and BOTTOM cells at joins
  18444. if (isset($details['cellposdom'])) {
  18445. $dom += $details['cellposdom'];
  18446. }
  18447. $save = false;
  18448. if ($side == 'T' && $this->issetBorder($bord, Border::TOP)) {
  18449. $cbord = Border::TOP;
  18450. $save = true;
  18451. } elseif ($side == 'L' && $this->issetBorder($bord, Border::LEFT)) {
  18452. $cbord = Border::LEFT;
  18453. $save = true;
  18454. } elseif ($side == 'R' && $this->issetBorder($bord, Border::RIGHT)) {
  18455. $cbord = Border::RIGHT;
  18456. $save = true;
  18457. } elseif ($side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) {
  18458. $cbord = Border::BOTTOM;
  18459. $save = true;
  18460. }
  18461. if ($save) {
  18462. $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);
  18463. if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset' || $details[$side]['style'] == 'double') {
  18464. $details[$side]['overlay'] = true;
  18465. $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);
  18466. }
  18467. }
  18468. }
  18469. return;
  18470. }
  18471. if (isset($details['p']) && strlen($details['p']) > 1) {
  18472. $priority = $details['p'];
  18473. } else {
  18474. $priority = 'LTRB';
  18475. }
  18476. $Tw = 0;
  18477. $Rw = 0;
  18478. $Bw = 0;
  18479. $Lw = 0;
  18480. if (isset($details['T']['w'])) {
  18481. $Tw = $details['T']['w'];
  18482. }
  18483. if (isset($details['R']['w'])) {
  18484. $Rw = $details['R']['w'];
  18485. }
  18486. if (isset($details['B']['w'])) {
  18487. $Bw = $details['B']['w'];
  18488. }
  18489. if (isset($details['L']['w'])) {
  18490. $Lw = $details['L']['w'];
  18491. }
  18492. $x2 = $x + $w;
  18493. $y2 = $y + $h;
  18494. $oldlinewidth = $this->LineWidth;
  18495. for ($p = 0; $p < strlen($priority); $p++) {
  18496. $side = $priority[$p];
  18497. $xadj = 0;
  18498. $xadj2 = 0;
  18499. $yadj = 0;
  18500. $yadj2 = 0;
  18501. $print = false;
  18502. if ($Tw && $side == 'T' && $this->issetBorder($bord, Border::TOP)) { // TOP
  18503. $ly1 = $y;
  18504. $ly2 = $y;
  18505. $lx1 = $x;
  18506. $lx2 = $x2;
  18507. $this->SetLineWidth($Tw);
  18508. if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {
  18509. if ($Tw > $Lw) {
  18510. $xadj = ($Tw - $Lw) / 2;
  18511. }
  18512. if ($Tw < $Lw) {
  18513. $xadj = ($Tw + $Lw) / 2;
  18514. }
  18515. } else {
  18516. $xadj = $Tw / 2 - $bsh / 2;
  18517. }
  18518. if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {
  18519. if ($Tw > $Rw) {
  18520. $xadj2 = ($Tw - $Rw) / 2;
  18521. }
  18522. if ($Tw < $Rw) {
  18523. $xadj2 = ($Tw + $Rw) / 2;
  18524. }
  18525. } else {
  18526. $xadj2 = $Tw / 2 - $bsh / 2;
  18527. }
  18528. if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TL'])) {
  18529. $xadj = ($Tw - $details['mbw']['TL']) / 2;
  18530. }
  18531. if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TR'])) {
  18532. $xadj2 = ($Tw - $details['mbw']['TR']) / 2;
  18533. }
  18534. $print = true;
  18535. }
  18536. if ($Lw && $side == 'L' && $this->issetBorder($bord, Border::LEFT)) { // LEFT
  18537. $ly1 = $y;
  18538. $ly2 = $y2;
  18539. $lx1 = $x;
  18540. $lx2 = $x;
  18541. $this->SetLineWidth($Lw);
  18542. if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {
  18543. if ($Lw > $Tw) {
  18544. $yadj = ($Lw - $Tw) / 2;
  18545. }
  18546. if ($Lw < $Tw) {
  18547. $yadj = ($Lw + $Tw) / 2;
  18548. }
  18549. } else {
  18550. $yadj = $Lw / 2 - $bsv / 2;
  18551. }
  18552. if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {
  18553. if ($Lw > $Bw) {
  18554. $yadj2 = ($Lw - $Bw) / 2;
  18555. }
  18556. if ($Lw < $Bw) {
  18557. $yadj2 = ($Lw + $Bw) / 2;
  18558. }
  18559. } else {
  18560. $yadj2 = $Lw / 2 - $bsv / 2;
  18561. }
  18562. if (!$bSeparate && $details['mbw']['LT']) {
  18563. $yadj = ($Lw - $details['mbw']['LT']) / 2;
  18564. }
  18565. if (!$bSeparate && $details['mbw']['LB']) {
  18566. $yadj2 = ($Lw - $details['mbw']['LB']) / 2;
  18567. }
  18568. $print = true;
  18569. }
  18570. if ($Rw && $side == 'R' && $this->issetBorder($bord, Border::RIGHT)) { // RIGHT
  18571. $ly1 = $y;
  18572. $ly2 = $y2;
  18573. $lx1 = $x2;
  18574. $lx2 = $x2;
  18575. $this->SetLineWidth($Rw);
  18576. if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {
  18577. if ($Rw < $Tw) {
  18578. $yadj = ($Rw + $Tw) / 2;
  18579. }
  18580. if ($Rw > $Tw) {
  18581. $yadj = ($Rw - $Tw) / 2;
  18582. }
  18583. } else {
  18584. $yadj = $Rw / 2 - $bsv / 2;
  18585. }
  18586. if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {
  18587. if ($Rw > $Bw) {
  18588. $yadj2 = ($Rw - $Bw) / 2;
  18589. }
  18590. if ($Rw < $Bw) {
  18591. $yadj2 = ($Rw + $Bw) / 2;
  18592. }
  18593. } else {
  18594. $yadj2 = $Rw / 2 - $bsv / 2;
  18595. }
  18596. if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RT'])) {
  18597. $yadj = ($Rw - $details['mbw']['RT']) / 2;
  18598. }
  18599. if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RB'])) {
  18600. $yadj2 = ($Rw - $details['mbw']['RB']) / 2;
  18601. }
  18602. $print = true;
  18603. }
  18604. if ($Bw && $side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) { // BOTTOM
  18605. $ly1 = $y2;
  18606. $ly2 = $y2;
  18607. $lx1 = $x;
  18608. $lx2 = $x2;
  18609. $this->SetLineWidth($Bw);
  18610. if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {
  18611. if ($Bw > $Lw) {
  18612. $xadj = ($Bw - $Lw) / 2;
  18613. }
  18614. if ($Bw < $Lw) {
  18615. $xadj = ($Bw + $Lw) / 2;
  18616. }
  18617. } else {
  18618. $xadj = $Bw / 2 - $bsh / 2;
  18619. }
  18620. if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {
  18621. if ($Bw > $Rw) {
  18622. $xadj2 = ($Bw - $Rw) / 2;
  18623. }
  18624. if ($Bw < $Rw) {
  18625. $xadj2 = ($Bw + $Rw) / 2;
  18626. }
  18627. } else {
  18628. $xadj2 = $Bw / 2 - $bsh / 2;
  18629. }
  18630. if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BL'])) {
  18631. $xadj = ($Bw - $details['mbw']['BL']) / 2;
  18632. }
  18633. if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BR'])) {
  18634. $xadj2 = ($Bw - $details['mbw']['BR']) / 2;
  18635. }
  18636. $print = true;
  18637. }
  18638. // Now draw line
  18639. if ($print) {
  18640. /* -- TABLES-ADVANCED-BORDERS -- */
  18641. if ($details[$side]['style'] == 'double') {
  18642. if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {
  18643. if ($details[$side]['c']) {
  18644. $this->SetDColor($details[$side]['c']);
  18645. } else {
  18646. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  18647. }
  18648. $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
  18649. }
  18650. if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {
  18651. if ($bSeparate && $cort == 'table') {
  18652. if ($side == 'T') {
  18653. $xadj -= $this->LineWidth / 2;
  18654. $xadj2 -= $this->LineWidth;
  18655. if ($this->issetBorder($bord, Border::LEFT)) {
  18656. $xadj += $this->LineWidth / 2;
  18657. }
  18658. if ($this->issetBorder($bord, Border::RIGHT)) {
  18659. $xadj2 += $this->LineWidth;
  18660. }
  18661. }
  18662. if ($side == 'L') {
  18663. $yadj -= $this->LineWidth / 2;
  18664. $yadj2 -= $this->LineWidth;
  18665. if ($this->issetBorder($bord, Border::TOP)) {
  18666. $yadj += $this->LineWidth / 2;
  18667. }
  18668. if ($this->issetBorder($bord, Border::BOTTOM)) {
  18669. $yadj2 += $this->LineWidth;
  18670. }
  18671. }
  18672. if ($side == 'B') {
  18673. $xadj -= $this->LineWidth / 2;
  18674. $xadj2 -= $this->LineWidth;
  18675. if ($this->issetBorder($bord, Border::LEFT)) {
  18676. $xadj += $this->LineWidth / 2;
  18677. }
  18678. if ($this->issetBorder($bord, Border::RIGHT)) {
  18679. $xadj2 += $this->LineWidth;
  18680. }
  18681. }
  18682. if ($side == 'R') {
  18683. $yadj -= $this->LineWidth / 2;
  18684. $yadj2 -= $this->LineWidth;
  18685. if ($this->issetBorder($bord, Border::TOP)) {
  18686. $yadj += $this->LineWidth / 2;
  18687. }
  18688. if ($this->issetBorder($bord, Border::BOTTOM)) {
  18689. $yadj2 += $this->LineWidth;
  18690. }
  18691. }
  18692. }
  18693. $this->SetLineWidth($this->LineWidth / 3);
  18694. $tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
  18695. for ($l = 0; $l <= $this->blklvl; $l++) {
  18696. if ($this->blk[$l]['bgcolor']) {
  18697. $tbcol = ($this->blk[$l]['bgcolorarray']);
  18698. }
  18699. }
  18700. if ($bSeparate) {
  18701. $cellBorderOverlay[] = [
  18702. 'x' => $lx1 + $xadj,
  18703. 'y' => $ly1 + $yadj,
  18704. 'x2' => $lx2 - $xadj2,
  18705. 'y2' => $ly2 - $yadj2,
  18706. 'col' => $tbcol,
  18707. 'lw' => $this->LineWidth,
  18708. ];
  18709. } else {
  18710. $this->SetDColor($tbcol);
  18711. $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
  18712. }
  18713. }
  18714. } elseif (isset($details[$side]['style']) && ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset')) {
  18715. if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {
  18716. if ($details[$side]['c']) {
  18717. $this->SetDColor($details[$side]['c']);
  18718. } else {
  18719. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  18720. }
  18721. if ($details[$side]['style'] == 'outset' || $details[$side]['style'] == 'groove') {
  18722. $nc = $this->colorConverter->darken($details[$side]['c']);
  18723. $this->SetDColor($nc);
  18724. } elseif ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {
  18725. $nc = $this->colorConverter->lighten($details[$side]['c']);
  18726. $this->SetDColor($nc);
  18727. }
  18728. $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
  18729. }
  18730. if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {
  18731. if ($details[$side]['c']) {
  18732. $this->SetDColor($details[$side]['c']);
  18733. } else {
  18734. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  18735. }
  18736. $doubleadj = ($this->LineWidth) / 3;
  18737. $this->SetLineWidth($this->LineWidth / 2);
  18738. $xadj3 = $yadj3 = $wadj3 = $hadj3 = 0;
  18739. if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {
  18740. $nc = $this->colorConverter->darken($details[$side]['c']);
  18741. if ($bSeparate && $cort == 'table') {
  18742. if ($side == 'T') {
  18743. $yadj3 = $this->LineWidth / 2;
  18744. $xadj3 = -$this->LineWidth / 2;
  18745. $wadj3 = $this->LineWidth;
  18746. if ($this->issetBorder($bord, Border::LEFT)) {
  18747. $xadj3 += $this->LineWidth;
  18748. $wadj3 -= $this->LineWidth;
  18749. }
  18750. if ($this->issetBorder($bord, Border::RIGHT)) {
  18751. $wadj3 -= $this->LineWidth * 2;
  18752. }
  18753. }
  18754. if ($side == 'L') {
  18755. $xadj3 = $this->LineWidth / 2;
  18756. $yadj3 = -$this->LineWidth / 2;
  18757. $hadj3 = $this->LineWidth;
  18758. if ($this->issetBorder($bord, Border::TOP)) {
  18759. $yadj3 += $this->LineWidth;
  18760. $hadj3 -= $this->LineWidth;
  18761. }
  18762. if ($this->issetBorder($bord, Border::BOTTOM)) {
  18763. $hadj3 -= $this->LineWidth * 2;
  18764. }
  18765. }
  18766. if ($side == 'B') {
  18767. $yadj3 = $this->LineWidth / 2;
  18768. $xadj3 = -$this->LineWidth / 2;
  18769. $wadj3 = $this->LineWidth;
  18770. }
  18771. if ($side == 'R') {
  18772. $xadj3 = $this->LineWidth / 2;
  18773. $yadj3 = -$this->LineWidth / 2;
  18774. $hadj3 = $this->LineWidth;
  18775. }
  18776. } elseif ($side == 'T') {
  18777. $yadj3 = $this->LineWidth / 2;
  18778. $xadj3 = $this->LineWidth / 2;
  18779. $wadj3 = -$this->LineWidth * 2;
  18780. } elseif ($side == 'L') {
  18781. $xadj3 = $this->LineWidth / 2;
  18782. $yadj3 = $this->LineWidth / 2;
  18783. $hadj3 = -$this->LineWidth * 2;
  18784. } elseif ($side == 'B' && $bSeparate) {
  18785. $yadj3 = $this->LineWidth / 2;
  18786. $wadj3 = $this->LineWidth / 2;
  18787. } elseif ($side == 'R' && $bSeparate) {
  18788. $xadj3 = $this->LineWidth / 2;
  18789. $hadj3 = $this->LineWidth / 2;
  18790. } elseif ($side == 'B') {
  18791. $yadj3 = $this->LineWidth / 2;
  18792. $xadj3 = $this->LineWidth / 2;
  18793. } elseif ($side == 'R') {
  18794. $xadj3 = $this->LineWidth / 2;
  18795. $yadj3 = $this->LineWidth / 2;
  18796. }
  18797. } else {
  18798. $nc = $this->colorConverter->lighten($details[$side]['c']);
  18799. if ($bSeparate && $cort == 'table') {
  18800. if ($side == 'T') {
  18801. $yadj3 = $this->LineWidth / 2;
  18802. $xadj3 = -$this->LineWidth / 2;
  18803. $wadj3 = $this->LineWidth;
  18804. if ($this->issetBorder($bord, Border::LEFT)) {
  18805. $xadj3 += $this->LineWidth;
  18806. $wadj3 -= $this->LineWidth;
  18807. }
  18808. }
  18809. if ($side == 'L') {
  18810. $xadj3 = $this->LineWidth / 2;
  18811. $yadj3 = -$this->LineWidth / 2;
  18812. $hadj3 = $this->LineWidth;
  18813. if ($this->issetBorder($bord, Border::TOP)) {
  18814. $yadj3 += $this->LineWidth;
  18815. $hadj3 -= $this->LineWidth;
  18816. }
  18817. }
  18818. if ($side == 'B') {
  18819. $yadj3 = $this->LineWidth / 2;
  18820. $xadj3 = -$this->LineWidth / 2;
  18821. $wadj3 = $this->LineWidth;
  18822. if ($this->issetBorder($bord, Border::LEFT)) {
  18823. $xadj3 += $this->LineWidth;
  18824. $wadj3 -= $this->LineWidth;
  18825. }
  18826. }
  18827. if ($side == 'R') {
  18828. $xadj3 = $this->LineWidth / 2;
  18829. $yadj3 = -$this->LineWidth / 2;
  18830. $hadj3 = $this->LineWidth;
  18831. if ($this->issetBorder($bord, Border::TOP)) {
  18832. $yadj3 += $this->LineWidth;
  18833. $hadj3 -= $this->LineWidth;
  18834. }
  18835. }
  18836. } elseif ($side == 'T') {
  18837. $yadj3 = $this->LineWidth / 2;
  18838. $xadj3 = $this->LineWidth / 2;
  18839. } elseif ($side == 'L') {
  18840. $xadj3 = $this->LineWidth / 2;
  18841. $yadj3 = $this->LineWidth / 2;
  18842. } elseif ($side == 'B' && $bSeparate) {
  18843. $yadj3 = $this->LineWidth / 2;
  18844. $xadj3 = $this->LineWidth / 2;
  18845. } elseif ($side == 'R' && $bSeparate) {
  18846. $xadj3 = $this->LineWidth / 2;
  18847. $yadj3 = $this->LineWidth / 2;
  18848. } elseif ($side == 'B') {
  18849. $yadj3 = $this->LineWidth / 2;
  18850. $xadj3 = -$this->LineWidth / 2;
  18851. $wadj3 = $this->LineWidth;
  18852. } elseif ($side == 'R') {
  18853. $xadj3 = $this->LineWidth / 2;
  18854. $yadj3 = -$this->LineWidth / 2;
  18855. $hadj3 = $this->LineWidth;
  18856. }
  18857. }
  18858. if ($bSeparate) {
  18859. $cellBorderOverlay[] = [
  18860. 'x' => $lx1 + $xadj + $xadj3,
  18861. 'y' => $ly1 + $yadj + $yadj3,
  18862. 'x2' => $lx2 - $xadj2 + $xadj3 + $wadj3,
  18863. 'y2' => $ly2 - $yadj2 + $yadj3 + $hadj3,
  18864. 'col' => $nc,
  18865. 'lw' => $this->LineWidth,
  18866. ];
  18867. } else {
  18868. $this->SetDColor($nc);
  18869. $this->Line($lx1 + $xadj + $xadj3, $ly1 + $yadj + $yadj3, $lx2 - $xadj2 + $xadj3 + $wadj3, $ly2 - $yadj2 + $yadj3 + $hadj3);
  18870. }
  18871. }
  18872. } else {
  18873. /* -- END TABLES-ADVANCED-BORDERS -- */
  18874. if ($details[$side]['style'] == 'dashed') {
  18875. $dashsize = 2; // final dash will be this + 1*linewidth
  18876. $dashsizek = 1.5; // ratio of Dash/Blank
  18877. $this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));
  18878. } elseif ($details[$side]['style'] == 'dotted') {
  18879. $this->SetLineJoin(1);
  18880. $this->SetLineCap(1);
  18881. $this->SetDash(0.001, ($this->LineWidth * 2));
  18882. }
  18883. if ($details[$side]['c']) {
  18884. $this->SetDColor($details[$side]['c']);
  18885. } else {
  18886. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  18887. }
  18888. $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
  18889. /* -- TABLES-ADVANCED-BORDERS -- */
  18890. }
  18891. /* -- END TABLES-ADVANCED-BORDERS -- */
  18892. // Reset Corners
  18893. $this->SetDash();
  18894. // BUTT style line cap
  18895. $this->SetLineCap(2);
  18896. }
  18897. }
  18898. if ($bSeparate && count($cellBorderOverlay)) {
  18899. foreach ($cellBorderOverlay as $cbo) {
  18900. $this->SetLineWidth($cbo['lw']);
  18901. $this->SetDColor($cbo['col']);
  18902. $this->Line($cbo['x'], $cbo['y'], $cbo['x2'], $cbo['y2']);
  18903. }
  18904. }
  18905. // $this->SetLineWidth($oldlinewidth);
  18906. // $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  18907. }
  18908. }
  18909. /* -- TABLES -- */
  18910. /* -- TABLES-ADVANCED-BORDERS -- */
  18911. /* -- END TABLES-ADVANCED-BORDERS -- */
  18912. function setBorder(&$var, $flag, $set = true)
  18913. {
  18914. $flag = intval($flag);
  18915. if ($set) {
  18916. $set = true;
  18917. }
  18918. $var = intval($var);
  18919. $var = $set ? ($var | $flag) : ($var & ~$flag);
  18920. }
  18921. function issetBorder($var, $flag)
  18922. {
  18923. $flag = intval($flag);
  18924. $var = intval($var);
  18925. return (($var & $flag) == $flag);
  18926. }
  18927. function _table2cellBorder(&$tableb, &$cbdb, &$cellb, $bval)
  18928. {
  18929. if ($tableb && $tableb['w'] > $cbdb['w']) {
  18930. $cbdb = $tableb;
  18931. $this->setBorder($cellb, $bval);
  18932. } elseif ($tableb && $tableb['w'] == $cbdb['w'] && array_search($tableb['style'], $this->borderstyles) > array_search($cbdb['style'], $this->borderstyles)) {
  18933. $cbdb = $tableb;
  18934. $this->setBorder($cellb, $bval);
  18935. }
  18936. }
  18937. // FIX BORDERS ********************************************
  18938. function _fixTableBorders(&$table)
  18939. {
  18940. if (!$table['borders_separate'] && $table['border_details']['L']['w']) {
  18941. $table['max_cell_border_width']['L'] = $table['border_details']['L']['w'];
  18942. }
  18943. if (!$table['borders_separate'] && $table['border_details']['R']['w']) {
  18944. $table['max_cell_border_width']['R'] = $table['border_details']['R']['w'];
  18945. }
  18946. if (!$table['borders_separate'] && $table['border_details']['T']['w']) {
  18947. $table['max_cell_border_width']['T'] = $table['border_details']['T']['w'];
  18948. }
  18949. if (!$table['borders_separate'] && $table['border_details']['B']['w']) {
  18950. $table['max_cell_border_width']['B'] = $table['border_details']['B']['w'];
  18951. }
  18952. if ($this->simpleTables) {
  18953. return;
  18954. }
  18955. $cells = &$table['cells'];
  18956. $numcols = $table['nc'];
  18957. $numrows = $table['nr'];
  18958. /* -- TABLES-ADVANCED-BORDERS -- */
  18959. if (isset($table['topntail']) && $table['topntail']) {
  18960. $tntborddet = $this->border_details($table['topntail']);
  18961. }
  18962. if (isset($table['thead-underline']) && $table['thead-underline']) {
  18963. $thuborddet = $this->border_details($table['thead-underline']);
  18964. }
  18965. /* -- END TABLES-ADVANCED-BORDERS -- */
  18966. for ($i = 0; $i < $numrows; $i++) { // Rows
  18967. for ($j = 0; $j < $numcols; $j++) { // Columns
  18968. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  18969. $cell = &$cells[$i][$j];
  18970. if ($this->packTableData) {
  18971. $cbord = $this->_unpackCellBorder($cell['borderbin']);
  18972. } else {
  18973. $cbord = &$cells[$i][$j];
  18974. }
  18975. // mPDF 5.7.3
  18976. if (!$cbord['border'] && $cbord['border'] !== 0 && isset($table['border']) && $table['border'] && $this->table_border_attr_set) {
  18977. $cbord['border'] = $table['border'];
  18978. $cbord['border_details'] = $table['border_details'];
  18979. }
  18980. if (isset($cell['colspan']) && $cell['colspan'] > 1) {
  18981. $ccolsp = $cell['colspan'];
  18982. } else {
  18983. $ccolsp = 1;
  18984. }
  18985. if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {
  18986. $crowsp = $cell['rowspan'];
  18987. } else {
  18988. $crowsp = 1;
  18989. }
  18990. $cbord['border_details']['cellposdom'] = ((($i + 1) / $numrows) / 10000 ) + ((($j + 1) / $numcols) / 10 );
  18991. // Inherit Cell border from Table border
  18992. if ($this->table_border_css_set && !$table['borders_separate']) {
  18993. if ($i == 0) {
  18994. $this->_table2cellBorder($table['border_details']['T'], $cbord['border_details']['T'], $cbord['border'], Border::TOP);
  18995. }
  18996. if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {
  18997. $this->_table2cellBorder($table['border_details']['B'], $cbord['border_details']['B'], $cbord['border'], Border::BOTTOM);
  18998. }
  18999. if ($j == 0) {
  19000. $this->_table2cellBorder($table['border_details']['L'], $cbord['border_details']['L'], $cbord['border'], Border::LEFT);
  19001. }
  19002. if ($j == ($numcols - 1) || ($j + $ccolsp) == ($numcols)) {
  19003. $this->_table2cellBorder($table['border_details']['R'], $cbord['border_details']['R'], $cbord['border'], Border::RIGHT);
  19004. }
  19005. }
  19006. /* -- TABLES-ADVANCED-BORDERS -- */
  19007. $fixbottom = true;
  19008. if (isset($table['topntail']) && $table['topntail']) {
  19009. if ($i == 0) {
  19010. $cbord['border_details']['T'] = $tntborddet;
  19011. $this->setBorder($cbord['border'], Border::TOP);
  19012. }
  19013. if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {
  19014. $cbord['border_details']['B'] = $tntborddet;
  19015. $this->setBorder($cbord['border'], Border::BOTTOM);
  19016. $fixbottom = false;
  19017. } elseif ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows']) {
  19018. if (!$table['borders_separate']) {
  19019. $cbord['border_details']['T'] = $tntborddet;
  19020. $this->setBorder($cbord['border'], Border::TOP);
  19021. }
  19022. }
  19023. if ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'] - 1)) {
  19024. if (!$table['borders_separate']) {
  19025. $cbord['border_details']['B'] = $tntborddet;
  19026. $this->setBorder($cbord['border'], Border::BOTTOM);
  19027. $fixbottom = false;
  19028. }
  19029. } elseif ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'])) {
  19030. $cbord['border_details']['T'] = $tntborddet;
  19031. $this->setBorder($cbord['border'], Border::TOP);
  19032. }
  19033. if ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader
  19034. if (!$table['borders_separate']) {
  19035. $cbord['border_details']['T'] = $tntborddet;
  19036. $this->setBorder($cbord['border'], Border::TOP);
  19037. }
  19038. }
  19039. if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {
  19040. $cbord['border_details']['B'] = $tntborddet;
  19041. $this->setBorder($cbord['border'], Border::BOTTOM);
  19042. }
  19043. }
  19044. if (isset($table['thead-underline']) && $table['thead-underline']) {
  19045. if ($table['borders_separate']) {
  19046. if ($i == 0) {
  19047. $cbord['border_details']['B'] = $thuborddet;
  19048. $this->setBorder($cbord['border'], Border::BOTTOM);
  19049. $fixbottom = false;
  19050. }
  19051. } else {
  19052. if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {
  19053. $cbord['border_details']['T'] = $thuborddet;
  19054. $this->setBorder($cbord['border'], Border::TOP);
  19055. } elseif ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader
  19056. $cbord['border_details']['T'] = $thuborddet;
  19057. $this->setBorder($cbord['border'], Border::TOP);
  19058. }
  19059. }
  19060. }
  19061. // Collapse Border - Algorithm for conflicting borders
  19062. // Hidden >> Width >> double>solid>dashed>dotted... >> style set on cell>table >> top/left>bottom/right
  19063. // Do not turn off border which is overridden
  19064. // Needed for page break for TOP/BOTTOM both to be defined in Collapsed borders
  19065. // Means it is painted twice. (Left/Right can still disable overridden border)
  19066. if (!$table['borders_separate']) {
  19067. if (($i < ($numrows - 1) || ($i + $crowsp) < $numrows ) && $fixbottom) { // Bottom
  19068. for ($cspi = 0; $cspi < $ccolsp; $cspi++) {
  19069. // already defined Top for adjacent cell below
  19070. if (isset($cells[($i + $crowsp)][$j + $cspi])) {
  19071. if ($this->packTableData) {
  19072. $adjc = $cells[($i + $crowsp)][$j + $cspi];
  19073. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  19074. } else {
  19075. $celladj = & $cells[($i + $crowsp)][$j + $cspi];
  19076. }
  19077. } else {
  19078. $celladj = false;
  19079. }
  19080. if (isset($celladj['border_details']['T']['s']) && $celladj['border_details']['T']['s'] == 1) {
  19081. $csadj = $celladj['border_details']['T']['w'];
  19082. $csthis = $cbord['border_details']['B']['w'];
  19083. // Hidden
  19084. if ($cbord['border_details']['B']['style'] == 'hidden') {
  19085. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  19086. $this->setBorder($celladj['border'], Border::TOP, false);
  19087. $this->setBorder($cbord['border'], Border::BOTTOM, false);
  19088. } elseif ($celladj['border_details']['T']['style'] == 'hidden') {
  19089. $cbord['border_details']['B'] = $celladj['border_details']['T'];
  19090. $this->setBorder($cbord['border'], Border::BOTTOM, false);
  19091. $this->setBorder($celladj['border'], Border::TOP, false);
  19092. } elseif ($csthis > $csadj) { // Width
  19093. 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
  19094. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  19095. $this->setBorder($cbord['border'], Border::BOTTOM);
  19096. }
  19097. } elseif ($csadj > $csthis) {
  19098. if ($ccolsp < 2) { // don't overwrite this cell if it spans
  19099. $cbord['border_details']['B'] = $celladj['border_details']['T'];
  19100. $this->setBorder($celladj['border'], Border::TOP);
  19101. }
  19102. } elseif (array_search($cbord['border_details']['B']['style'], $this->borderstyles) > array_search($celladj['border_details']['T']['style'], $this->borderstyles)) { // double>solid>dashed>dotted...
  19103. 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
  19104. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  19105. $this->setBorder($cbord['border'], Border::BOTTOM);
  19106. }
  19107. } elseif (array_search($celladj['border_details']['T']['style'], $this->borderstyles) > array_search($cbord['border_details']['B']['style'], $this->borderstyles)) {
  19108. if ($ccolsp < 2) { // don't overwrite this cell if it spans
  19109. $cbord['border_details']['B'] = $celladj['border_details']['T'];
  19110. $this->setBorder($celladj['border'], Border::TOP);
  19111. }
  19112. } elseif ($celladj['border_details']['T']['dom'] > $celladj['border_details']['B']['dom']) { // Style set on cell vs. table
  19113. if ($ccolsp < 2) { // don't overwrite this cell if it spans
  19114. $cbord['border_details']['B'] = $celladj['border_details']['T'];
  19115. $this->setBorder($celladj['border'], Border::TOP);
  19116. }
  19117. } else { // Style set on cell vs. table - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT
  19118. 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
  19119. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  19120. $this->setBorder($cbord['border'], Border::BOTTOM);
  19121. }
  19122. }
  19123. } elseif ($celladj) {
  19124. 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
  19125. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  19126. }
  19127. }
  19128. // mPDF 5.7.4
  19129. if ($celladj && $this->packTableData) {
  19130. $cells[$i + $crowsp][$j + $cspi]['borderbin'] = $this->_packCellBorder($celladj);
  19131. }
  19132. unset($celladj);
  19133. }
  19134. }
  19135. if ($j < ($numcols - 1) || ($j + $ccolsp) < $numcols) { // Right-Left
  19136. for ($cspi = 0; $cspi < $crowsp; $cspi++) {
  19137. // already defined Left for adjacent cell to R
  19138. if (isset($cells[($i + $cspi)][$j + $ccolsp])) {
  19139. if ($this->packTableData) {
  19140. $adjc = $cells[($i + $cspi)][$j + $ccolsp];
  19141. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  19142. } else {
  19143. $celladj = & $cells[$i + $cspi][$j + $ccolsp];
  19144. }
  19145. } else {
  19146. $celladj = false;
  19147. }
  19148. if ($celladj && $celladj['border_details']['L']['s'] == 1) {
  19149. $csadj = $celladj['border_details']['L']['w'];
  19150. $csthis = $cbord['border_details']['R']['w'];
  19151. // Hidden
  19152. if ($cbord['border_details']['R']['style'] == 'hidden') {
  19153. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  19154. $this->setBorder($celladj['border'], Border::LEFT, false);
  19155. $this->setBorder($cbord['border'], Border::RIGHT, false);
  19156. } elseif ($celladj['border_details']['L']['style'] == 'hidden') {
  19157. $cbord['border_details']['R'] = $celladj['border_details']['L'];
  19158. $this->setBorder($cbord['border'], Border::RIGHT, false);
  19159. $this->setBorder($celladj['border'], Border::LEFT, false);
  19160. } // Width
  19161. elseif ($csthis > $csadj) {
  19162. 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
  19163. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  19164. $this->setBorder($cbord['border'], Border::RIGHT);
  19165. $this->setBorder($celladj['border'], Border::LEFT, false);
  19166. }
  19167. } elseif ($csadj > $csthis) {
  19168. if ($crowsp < 2) { // don't overwrite this cell if it spans
  19169. $cbord['border_details']['R'] = $celladj['border_details']['L'];
  19170. $this->setBorder($cbord['border'], Border::RIGHT, false);
  19171. $this->setBorder($celladj['border'], Border::LEFT);
  19172. }
  19173. } // double>solid>dashed>dotted...
  19174. elseif (array_search($cbord['border_details']['R']['style'], $this->borderstyles) > array_search($celladj['border_details']['L']['style'], $this->borderstyles)) {
  19175. 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
  19176. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  19177. $this->setBorder($celladj['border'], Border::LEFT, false);
  19178. $this->setBorder($cbord['border'], Border::RIGHT);
  19179. }
  19180. } elseif (array_search($celladj['border_details']['L']['style'], $this->borderstyles) > array_search($cbord['border_details']['R']['style'], $this->borderstyles)) {
  19181. if ($crowsp < 2) { // don't overwrite this cell if it spans
  19182. $cbord['border_details']['R'] = $celladj['border_details']['L'];
  19183. $this->setBorder($cbord['border'], Border::RIGHT, false);
  19184. $this->setBorder($celladj['border'], Border::LEFT);
  19185. }
  19186. } // Style set on cell vs. table
  19187. elseif ($celladj['border_details']['L']['dom'] > $cbord['border_details']['R']['dom']) {
  19188. if ($crowsp < 2) { // don't overwrite this cell if it spans
  19189. $cbord['border_details']['R'] = $celladj['border_details']['L'];
  19190. $this->setBorder($celladj['border'], Border::LEFT);
  19191. }
  19192. } // Style set on cell vs. table - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT
  19193. else {
  19194. 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
  19195. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  19196. $this->setBorder($cbord['border'], Border::RIGHT);
  19197. }
  19198. }
  19199. } elseif ($celladj) {
  19200. // if right-cell border is not set
  19201. 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
  19202. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  19203. }
  19204. }
  19205. // mPDF 5.7.4
  19206. if ($celladj && $this->packTableData) {
  19207. $cells[$i + $cspi][$j + $ccolsp]['borderbin'] = $this->_packCellBorder($celladj);
  19208. }
  19209. unset($celladj);
  19210. }
  19211. }
  19212. }
  19213. // Set maximum cell border width meeting at LRTB edges of cell - used for extended cell border
  19214. // ['border_details']['mbw']['LT'] = meeting border width - Left border - Top end
  19215. if (!$table['borders_separate']) {
  19216. $cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $cbord['border_details']['L']['w']);
  19217. $cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $cbord['border_details']['R']['w']);
  19218. $cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $cbord['border_details']['T']['w']);
  19219. $cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $cbord['border_details']['B']['w']);
  19220. $cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $cbord['border_details']['L']['w']);
  19221. $cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $cbord['border_details']['R']['w']);
  19222. $cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $cbord['border_details']['T']['w']);
  19223. $cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $cbord['border_details']['B']['w']);
  19224. if (($i + $crowsp) < $numrows && isset($cells[$i + $crowsp][$j])) { // Has Bottom adjoining cell
  19225. if ($this->packTableData) {
  19226. $adjc = $cells[$i + $crowsp][$j];
  19227. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  19228. } else {
  19229. $celladj = & $cells[$i + $crowsp][$j];
  19230. }
  19231. $cbord['border_details']['mbw']['BL'] = max(
  19232. $cbord['border_details']['mbw']['BL'],
  19233. $celladj ? $celladj['border_details']['L']['w'] : 0,
  19234. $celladj ? $celladj['border_details']['mbw']['TL']: 0
  19235. );
  19236. $cbord['border_details']['mbw']['BR'] = max(
  19237. $cbord['border_details']['mbw']['BR'],
  19238. $celladj ? $celladj['border_details']['R']['w'] : 0,
  19239. $celladj ? $celladj['border_details']['mbw']['TR']: 0
  19240. );
  19241. $cbord['border_details']['mbw']['LB'] = max(
  19242. $cbord['border_details']['mbw']['LB'],
  19243. $celladj ? $celladj['border_details']['mbw']['LT'] : 0
  19244. );
  19245. $cbord['border_details']['mbw']['RB'] = max(
  19246. $cbord['border_details']['mbw']['RB'],
  19247. $celladj ? $celladj['border_details']['mbw']['RT'] : 0
  19248. );
  19249. unset($celladj);
  19250. }
  19251. if (($j + $ccolsp) < $numcols && isset($cells[$i][$j + $ccolsp])) { // Has Right adjoining cell
  19252. if ($this->packTableData) {
  19253. $adjc = $cells[$i][$j + $ccolsp];
  19254. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  19255. } else {
  19256. $celladj = & $cells[$i][$j + $ccolsp];
  19257. }
  19258. $cbord['border_details']['mbw']['RT'] = max(
  19259. $cbord['border_details']['mbw']['RT'],
  19260. $celladj ? $celladj['border_details']['T']['w'] : 0,
  19261. $celladj ? $celladj['border_details']['mbw']['LT'] : 0
  19262. );
  19263. $cbord['border_details']['mbw']['RB'] = max(
  19264. $cbord['border_details']['mbw']['RB'],
  19265. $celladj ? $celladj['border_details']['B']['w'] : 0,
  19266. $celladj ? $celladj['border_details']['mbw']['LB'] : 0
  19267. );
  19268. $cbord['border_details']['mbw']['TR'] = max(
  19269. $cbord['border_details']['mbw']['TR'],
  19270. $celladj ? $celladj['border_details']['mbw']['TL'] : 0
  19271. );
  19272. $cbord['border_details']['mbw']['BR'] = max(
  19273. $cbord['border_details']['mbw']['BR'],
  19274. $celladj ? $celladj['border_details']['mbw']['BL'] : 0
  19275. );
  19276. unset($celladj);
  19277. }
  19278. 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
  19279. if ($this->packTableData) {
  19280. $adjc = $cells[$i - 1][$j];
  19281. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  19282. } else {
  19283. $celladj = & $cells[$i - 1][$j];
  19284. }
  19285. $cbord['border_details']['mbw']['TL'] = max(
  19286. $cbord['border_details']['mbw']['TL'],
  19287. $celladj ? $celladj['border_details']['L']['w'] : 0,
  19288. $celladj ? $celladj['border_details']['mbw']['BL'] : 0
  19289. );
  19290. $cbord['border_details']['mbw']['TR'] = max(
  19291. $cbord['border_details']['mbw']['TR'],
  19292. $celladj ? $celladj['border_details']['R']['w'] : 0,
  19293. $celladj ? $celladj['border_details']['mbw']['BR'] : 0
  19294. );
  19295. $cbord['border_details']['mbw']['LT'] = max(
  19296. $cbord['border_details']['mbw']['LT'],
  19297. $celladj ? $celladj['border_details']['mbw']['LB'] : 0
  19298. );
  19299. $cbord['border_details']['mbw']['RT'] = max(
  19300. $cbord['border_details']['mbw']['RT'],
  19301. $celladj ? $celladj['border_details']['mbw']['RB'] : 0
  19302. );
  19303. if ($celladj['border_details']['mbw']['BL']) {
  19304. $celladj['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['mbw']['BL']);
  19305. }
  19306. if ($celladj['border_details']['mbw']['BR']) {
  19307. $celladj['border_details']['mbw']['BR'] = max($celladj['border_details']['mbw']['BR'], $cbord['border_details']['mbw']['TR']);
  19308. }
  19309. if ($this->packTableData) {
  19310. $cells[$i - 1][$j]['borderbin'] = $this->_packCellBorder($celladj);
  19311. }
  19312. unset($celladj);
  19313. }
  19314. 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
  19315. if ($this->packTableData) {
  19316. $adjc = $cells[$i][$j - 1];
  19317. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  19318. } else {
  19319. $celladj = & $cells[$i][$j - 1];
  19320. }
  19321. $cbord['border_details']['mbw']['LT'] = max(
  19322. $cbord['border_details']['mbw']['LT'],
  19323. $celladj ? $celladj['border_details']['T']['w'] : 0,
  19324. $celladj ? $celladj['border_details']['mbw']['RT'] : 0
  19325. );
  19326. $cbord['border_details']['mbw']['LB'] = max(
  19327. $cbord['border_details']['mbw']['LB'],
  19328. $celladj ? $celladj['border_details']['B']['w'] : 0,
  19329. $celladj ? $celladj['border_details']['mbw']['RB'] : 0
  19330. );
  19331. $cbord['border_details']['mbw']['BL'] = max(
  19332. $cbord['border_details']['mbw']['BL'],
  19333. $celladj ? $celladj['border_details']['mbw']['BR'] : 0
  19334. );
  19335. $cbord['border_details']['mbw']['TL'] = max(
  19336. $cbord['border_details']['mbw']['TL'],
  19337. $celladj ? $celladj['border_details']['mbw']['TR'] : 0
  19338. );
  19339. if ($celladj['border_details']['mbw']['RT']) {
  19340. $celladj['border_details']['mbw']['RT'] = max($celladj['border_details']['mbw']['RT'], $cbord['border_details']['mbw']['LT']);
  19341. }
  19342. if ($celladj['border_details']['mbw']['RB']) {
  19343. $celladj['border_details']['mbw']['RB'] = max($celladj['border_details']['mbw']['RB'], $cbord['border_details']['mbw']['LB']);
  19344. }
  19345. if ($this->packTableData) {
  19346. $cells[$i][$j - 1]['borderbin'] = $this->_packCellBorder($celladj);
  19347. }
  19348. unset($celladj);
  19349. }
  19350. // Update maximum cell border width at LRTB edges of table - used for overall table width
  19351. if ($j == 0 && $cbord['border_details']['L']['w']) {
  19352. $table['max_cell_border_width']['L'] = max($table['max_cell_border_width']['L'], $cbord['border_details']['L']['w']);
  19353. }
  19354. if (($j == ($numcols - 1) || ($j + $ccolsp) == $numcols ) && $cbord['border_details']['R']['w']) {
  19355. $table['max_cell_border_width']['R'] = max($table['max_cell_border_width']['R'], $cbord['border_details']['R']['w']);
  19356. }
  19357. if ($i == 0 && $cbord['border_details']['T']['w']) {
  19358. $table['max_cell_border_width']['T'] = max($table['max_cell_border_width']['T'], $cbord['border_details']['T']['w']);
  19359. }
  19360. if (($i == ($numrows - 1) || ($i + $crowsp) == $numrows ) && $cbord['border_details']['B']['w']) {
  19361. $table['max_cell_border_width']['B'] = max($table['max_cell_border_width']['B'], $cbord['border_details']['B']['w']);
  19362. }
  19363. }
  19364. /* -- END TABLES-ADVANCED-BORDERS -- */
  19365. if ($this->packTableData) {
  19366. $cell['borderbin'] = $this->_packCellBorder($cbord);
  19367. }
  19368. unset($cbord);
  19369. unset($cell);
  19370. }
  19371. }
  19372. }
  19373. unset($cell);
  19374. }
  19375. // END FIX BORDERS ************************************************************************************
  19376. function _reverseTableDir(&$table)
  19377. {
  19378. $cells = &$table['cells'];
  19379. $numcols = $table['nc'];
  19380. $numrows = $table['nr'];
  19381. for ($i = 0; $i < $numrows; $i++) { // Rows
  19382. $row = [];
  19383. for ($j = ($numcols - 1); $j >= 0; $j--) { // Columns
  19384. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  19385. $cell = &$cells[$i][$j];
  19386. $col = $numcols - $j - 1;
  19387. if (isset($cell['colspan']) && $cell['colspan'] > 1) {
  19388. $col -= ($cell['colspan'] - 1);
  19389. }
  19390. // Nested content
  19391. if (isset($cell['textbuffer'])) {
  19392. for ($n = 0; $n < count($cell['textbuffer']); $n++) {
  19393. $t = $cell['textbuffer'][$n][0];
  19394. if (substr($t, 0, 19) == Mpdf::OBJECT_IDENTIFIER . "type=nestedtable") {
  19395. $objattr = $this->_getObjAttr($t);
  19396. $objattr['col'] = $col;
  19397. $cell['textbuffer'][$n][0] = Mpdf::OBJECT_IDENTIFIER . "type=nestedtable,objattr=" . serialize($objattr) . Mpdf::OBJECT_IDENTIFIER;
  19398. $this->table[($this->tableLevel + 1)][$objattr['nestedcontent']]['nestedpos'][1] = $col;
  19399. }
  19400. }
  19401. }
  19402. $row[$col] = $cells[$i][$j];
  19403. unset($cell);
  19404. }
  19405. }
  19406. for ($f = 0; $f < $numcols; $f++) {
  19407. if (!isset($row[$f])) {
  19408. $row[$f] = 0;
  19409. }
  19410. }
  19411. $table['cells'][$i] = $row;
  19412. }
  19413. }
  19414. function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $splitpg = 0, $rety = 0)
  19415. {
  19416. $level = $table['level'];
  19417. $levelid = $table['levelid'];
  19418. $cells = &$table['cells'];
  19419. $numcols = $table['nc'];
  19420. $numrows = $table['nr'];
  19421. $maxbwtop = 0;
  19422. if ($this->ColActive && $level == 1) {
  19423. $this->breakpoints[$this->CurrCol][] = $this->y;
  19424. } // *COLUMNS*
  19425. if (!$split || ($startrow == 0 && $splitpg == 0) || $startrow > 0) {
  19426. // TABLE TOP MARGIN
  19427. if ($table['margin']['T']) {
  19428. if (!$this->table_rotate && $level == 1) {
  19429. $this->DivLn($table['margin']['T'], $this->blklvl, true, 1); // collapsible
  19430. } else {
  19431. $this->y += ($table['margin']['T']);
  19432. }
  19433. }
  19434. // Advance down page by half width of top border
  19435. if ($table['borders_separate']) {
  19436. if ($startrow > 0 && (!isset($table['is_thead']) || count($table['is_thead']) == 0)) {
  19437. $adv = $table['border_spacing_V'] / 2;
  19438. } else {
  19439. $adv = $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
  19440. }
  19441. } else {
  19442. $adv = $table['max_cell_border_width']['T'] / 2;
  19443. }
  19444. if (!$this->table_rotate && $level == 1) {
  19445. $this->DivLn($adv);
  19446. } else {
  19447. $this->y += $adv;
  19448. }
  19449. }
  19450. if ($level == 1) {
  19451. $this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'];
  19452. $x0 = $this->x;
  19453. $y0 = $this->y;
  19454. $right = $x0 + $this->blk[$this->blklvl]['inner_width'];
  19455. $outerfilled = $this->y; // Keep track of how far down the outer DIV bgcolor is painted (NB rowspans)
  19456. $this->outerfilled = $this->y;
  19457. $this->colsums = [];
  19458. } else {
  19459. $x0 = $this->x;
  19460. $y0 = $this->y;
  19461. $right = $x0 + $table['w'];
  19462. }
  19463. if ($this->table_rotate) {
  19464. $temppgwidth = $this->tbrot_maxw;
  19465. $this->PageBreakTrigger = $pagetrigger = $y0 + ($this->blk[$this->blklvl]['inner_width']);
  19466. if ($level == 1) {
  19467. $this->tbrot_y0 = $this->y - $adv - $table['margin']['T'];
  19468. $this->tbrot_x0 = $this->x;
  19469. $this->tbrot_w = $table['w'];
  19470. if ($table['borders_separate']) {
  19471. $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
  19472. } else {
  19473. $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['max_cell_border_width']['T'];
  19474. }
  19475. }
  19476. } else {
  19477. $this->PageBreakTrigger = $pagetrigger = ($this->h - $this->bMargin);
  19478. if ($level == 1) {
  19479. $temppgwidth = $this->blk[$this->blklvl]['inner_width'];
  19480. if (isset($table['a']) and ( $table['w'] < $this->blk[$this->blklvl]['inner_width'])) {
  19481. if ($table['a'] == 'C') {
  19482. $x0 += ((($right - $x0) - $table['w']) / 2);
  19483. } elseif ($table['a'] == 'R') {
  19484. $x0 = $right - $table['w'];
  19485. }
  19486. }
  19487. } else {
  19488. $temppgwidth = $table['w'];
  19489. }
  19490. }
  19491. if (!isset($table['overflow'])) {
  19492. $table['overflow'] = null;
  19493. }
  19494. if ($table['overflow'] == 'hidden' && $level == 1 && !$this->table_rotate && !$this->ColActive) {
  19495. // Bounding rectangle to clip
  19496. $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);
  19497. $this->writer->write($this->tableClipPath);
  19498. } else {
  19499. $this->tableClipPath = '';
  19500. }
  19501. if ($table['borders_separate']) {
  19502. $indent = $table['margin']['L'] + $table['border_details']['L']['w'] + $table['padding']['L'] + $table['border_spacing_H'] / 2;
  19503. } else {
  19504. $indent = $table['margin']['L'] + $table['max_cell_border_width']['L'] / 2;
  19505. }
  19506. $x0 += $indent;
  19507. $returny = 0;
  19508. $lastCol = 0;
  19509. $tableheader = [];
  19510. $tablefooter = [];
  19511. $tableheaderrowheight = 0;
  19512. $tablefooterrowheight = 0;
  19513. $footery = 0;
  19514. // mPD 3.0 Set the Page & Column where table starts
  19515. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  19516. $tablestartpage = 'EVEN';
  19517. } elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD
  19518. $tablestartpage = 'ODD';
  19519. } else {
  19520. $tablestartpage = '';
  19521. }
  19522. if ($this->ColActive) {
  19523. $tablestartcolumn = $this->CurrCol;
  19524. } else {
  19525. $tablestartcolumn = '';
  19526. }
  19527. $y = $h = 0;
  19528. for ($i = 0; $i < $numrows; $i++) { // Rows
  19529. if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i] && $level == 1) {
  19530. $tablefooterrowheight += $table['hr'][$i];
  19531. $tablefooter[$i][0]['trbackground-images'] = $table['trbackground-images'][$i];
  19532. $tablefooter[$i][0]['trgradients'] = $table['trgradients'][$i];
  19533. $tablefooter[$i][0]['trbgcolor'] = $table['bgcolor'][$i];
  19534. for ($j = $startcol; $j < $numcols; $j++) { // Columns
  19535. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  19536. $cell = &$cells[$i][$j];
  19537. if ($split) {
  19538. if ($table['colPg'][$j] != $splitpg) {
  19539. continue;
  19540. }
  19541. list($x, $w) = $this->_splitTableGetWidth($table, $i, $j);
  19542. $js = $j - $startcol;
  19543. } else {
  19544. list($x, $w) = $this->_tableGetWidth($table, $i, $j);
  19545. $js = $j;
  19546. }
  19547. list($y, $h) = $this->_tableGetHeight($table, $i, $j);
  19548. $x += $x0;
  19549. $y += $y0;
  19550. // Get info of tfoot ==>> table footer
  19551. $tablefooter[$i][$js]['x'] = $x;
  19552. $tablefooter[$i][$js]['y'] = $y;
  19553. $tablefooter[$i][$js]['h'] = $h;
  19554. $tablefooter[$i][$js]['w'] = $w;
  19555. if (isset($cell['textbuffer'])) {
  19556. $tablefooter[$i][$js]['textbuffer'] = $cell['textbuffer'];
  19557. } else {
  19558. $tablefooter[$i][$js]['textbuffer'] = '';
  19559. }
  19560. $tablefooter[$i][$js]['a'] = $cell['a'];
  19561. $tablefooter[$i][$js]['R'] = $cell['R'];
  19562. $tablefooter[$i][$js]['va'] = $cell['va'];
  19563. $tablefooter[$i][$js]['mih'] = $cell['mih'];
  19564. if (isset($cell['gradient'])) {
  19565. $tablefooter[$i][$js]['gradient'] = $cell['gradient']; // *BACKGROUNDS*
  19566. }
  19567. if (isset($cell['background-image'])) {
  19568. $tablefooter[$i][$js]['background-image'] = $cell['background-image']; // *BACKGROUNDS*
  19569. }
  19570. // CELL FILL BGCOLOR
  19571. if (!$this->simpleTables) {
  19572. if ($this->packTableData) {
  19573. $c = $this->_unpackCellBorder($cell['borderbin']);
  19574. $tablefooter[$i][$js]['border'] = $c['border'];
  19575. $tablefooter[$i][$js]['border_details'] = $c['border_details'];
  19576. } else {
  19577. $tablefooter[$i][$js]['border'] = $cell['border'];
  19578. $tablefooter[$i][$js]['border_details'] = $cell['border_details'];
  19579. }
  19580. } elseif ($this->simpleTables) {
  19581. $tablefooter[$i][$js]['border'] = $table['simple']['border'];
  19582. $tablefooter[$i][$js]['border_details'] = $table['simple']['border_details'];
  19583. }
  19584. $tablefooter[$i][$js]['bgcolor'] = $cell['bgcolor'];
  19585. $tablefooter[$i][$js]['padding'] = $cell['padding'];
  19586. if (isset($cell['rowspan'])) {
  19587. $tablefooter[$i][$js]['rowspan'] = $cell['rowspan'];
  19588. }
  19589. if (isset($cell['colspan'])) {
  19590. $tablefooter[$i][$js]['colspan'] = $cell['colspan'];
  19591. }
  19592. if (isset($cell['direction'])) {
  19593. $tablefooter[$i][$js]['direction'] = $cell['direction'];
  19594. }
  19595. if (isset($cell['cellLineHeight'])) {
  19596. $tablefooter[$i][$js]['cellLineHeight'] = $cell['cellLineHeight'];
  19597. }
  19598. if (isset($cell['cellLineStackingStrategy'])) {
  19599. $tablefooter[$i][$js]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];
  19600. }
  19601. if (isset($cell['cellLineStackingShift'])) {
  19602. $tablefooter[$i][$js]['cellLineStackingShift'] = $cell['cellLineStackingShift'];
  19603. }
  19604. }
  19605. }
  19606. }
  19607. }
  19608. if ($level == 1) {
  19609. $this->writer->write('___TABLE___BACKGROUNDS' . $this->uniqstr);
  19610. }
  19611. $tableheaderadj = 0;
  19612. $tablefooteradj = 0;
  19613. $tablestartpageno = $this->page;
  19614. // Draw Table Contents and Borders
  19615. for ($i = 0; $i < $numrows; $i++) { // Rows
  19616. if ($split && $startrow > 0) {
  19617. $thnr = (isset($table['is_thead']) ? count($table['is_thead']) : 0);
  19618. if ($i >= $thnr && $i < $startrow) {
  19619. continue;
  19620. }
  19621. if ($i == $startrow) {
  19622. $returny = $rety - $tableheaderrowheight;
  19623. }
  19624. }
  19625. // Get Maximum row/cell height in row - including rowspan>1 + 1 overlapping
  19626. $maxrowheight = $this->_tableGetMaxRowHeight($table, $i);
  19627. $skippage = false;
  19628. $newpagestarted = false;
  19629. for ($j = $startcol; $j < $numcols; $j++) { // Columns
  19630. if ($split) {
  19631. if ($table['colPg'][$j] > $splitpg) {
  19632. break;
  19633. }
  19634. $lastCol = $j;
  19635. }
  19636. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  19637. $cell = &$cells[$i][$j];
  19638. if ($split) {
  19639. $lastCol = $j + (isset($cell['colspan']) ? ($cell['colspan'] - 1) : 0);
  19640. list($x, $w) = $this->_splitTableGetWidth($table, $i, $j);
  19641. } else {
  19642. list($x, $w) = $this->_tableGetWidth($table, $i, $j);
  19643. }
  19644. list($y, $h) = $this->_tableGetHeight($table, $i, $j);
  19645. $x += $x0;
  19646. $y += $y0;
  19647. $y -= $returny;
  19648. if ($table['borders_separate']) {
  19649. if (!empty($tablefooter) || $i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) {
  19650. $extra = $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  19651. // $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V']/2;
  19652. } else {
  19653. $extra = $table['border_spacing_V'] / 2;
  19654. }
  19655. } else {
  19656. $extra = $table['max_cell_border_width']['B'] / 2;
  19657. }
  19658. // lookahead for pagebreak
  19659. $pagebreaklookahead = 1;
  19660. while (isset($table['pagebreak-before']) && isset($table['pagebreak-before'][$i + $pagebreaklookahead]) && $table['pagebreak-before'][$i + $pagebreaklookahead] == 'avoid') {
  19661. // pagebreak-after is mapped to pagebreak-before on i+1 in Tags/Tr.php
  19662. $pagebreaklookahead++;
  19663. }
  19664. if ($j == $startcol && ((($y + $pagebreaklookahead * $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) {
  19665. if (!$skippage) {
  19666. $finalSpread = true;
  19667. $firstSpread = true;
  19668. if ($split) {
  19669. for ($t = $startcol; $t < $numcols; $t++) {
  19670. // Are there more columns to print on a next page?
  19671. if ($table['colPg'][$t] > $splitpg) {
  19672. $finalSpread = false;
  19673. break;
  19674. }
  19675. }
  19676. if ($startcol > 0) {
  19677. $firstSpread = false;
  19678. }
  19679. }
  19680. if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {
  19681. $this->y = $y;
  19682. $ya = $this->y;
  19683. $this->TableHeaderFooter($tablefooter, $tablestartpage, $tablestartcolumn, 'F', $level, $firstSpread, $finalSpread);
  19684. if ($this->table_rotate) {
  19685. $this->tbrot_h += $this->y - $ya;
  19686. }
  19687. $tablefooteradj = $this->y - $ya;
  19688. }
  19689. $y -= $y0;
  19690. $returny += $y;
  19691. $oldcolumn = $this->CurrCol;
  19692. if ($this->AcceptPageBreak()) {
  19693. $newpagestarted = true;
  19694. $this->y = $y + $y0;
  19695. // Move down to account for border-spacing or
  19696. // extra half border width in case page breaks in middle
  19697. if ($i > 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {
  19698. if ($table['borders_separate']) {
  19699. $adv = $table['border_spacing_V'] / 2;
  19700. // If table footer
  19701. if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {
  19702. $adv += ($table['padding']['B'] + $table['border_details']['B']['w']);
  19703. }
  19704. } else {
  19705. $maxbwtop = 0;
  19706. $maxbwbottom = 0;
  19707. if (!$this->simpleTables) {
  19708. if (!empty($tablefooter)) {
  19709. $maxbwbottom = $table['max_cell_border_width']['B'];
  19710. } else {
  19711. $brow = $i - 1;
  19712. for ($ctj = 0; $ctj < $numcols; $ctj++) {
  19713. if (isset($cells[$brow][$ctj]) && $cells[$brow][$ctj]) {
  19714. if ($this->packTableData) {
  19715. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$brow][$ctj]['borderbin']);
  19716. } else {
  19717. $bb = $cells[$brow][$ctj]['border_details']['B']['w'];
  19718. }
  19719. $maxbwbottom = max($maxbwbottom, $bb);
  19720. }
  19721. }
  19722. }
  19723. if (!empty($tableheader)) {
  19724. $maxbwtop = $table['max_cell_border_width']['T'];
  19725. } else {
  19726. $trow = $i - 1;
  19727. for ($ctj = 0; $ctj < $numcols; $ctj++) {
  19728. if (isset($cells[$trow][$ctj]) && $cells[$trow][$ctj]) {
  19729. if ($this->packTableData) {
  19730. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$trow][$ctj]['borderbin']);
  19731. } else {
  19732. $bt = $cells[$trow][$ctj]['border_details']['T']['w'];
  19733. }
  19734. $maxbwtop = max($maxbwtop, $bt);
  19735. }
  19736. }
  19737. }
  19738. } elseif ($this->simpleTables) {
  19739. $maxbwtop = $table['simple']['border_details']['T']['w'];
  19740. $maxbwbottom = $table['simple']['border_details']['B']['w'];
  19741. }
  19742. $adv = $maxbwbottom / 2;
  19743. }
  19744. $this->y += $adv;
  19745. }
  19746. // Rotated table split over pages - needs this->y for borders/backgrounds
  19747. if ($i > 0 && $this->table_rotate && $level == 1) {
  19748. // $this->y = $y0 + $this->tbrot_w;
  19749. }
  19750. if ($this->tableClipPath) {
  19751. $this->writer->write("Q");
  19752. }
  19753. $bx = $x0;
  19754. $by = $y0;
  19755. if ($table['borders_separate']) {
  19756. $bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);
  19757. if ($tablestartpageno != $this->page) { // IF already broken across a previous pagebreak
  19758. $by += $table['max_cell_border_width']['T'] / 2;
  19759. if (empty($tableheader)) {
  19760. $by -= ($table['border_spacing_V'] / 2);
  19761. }
  19762. } else {
  19763. $by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);
  19764. }
  19765. } elseif ($tablestartpageno != $this->page && !empty($tableheader)) {
  19766. $by += $maxbwtop / 2;
  19767. }
  19768. $by -= $tableheaderadj;
  19769. $bh = $this->y - $by + $tablefooteradj;
  19770. if (!$table['borders_separate']) {
  19771. $bh -= $adv;
  19772. }
  19773. if ($split) {
  19774. $bw = 0;
  19775. for ($t = $startcol; $t < $numcols; $t++) {
  19776. if ($table['colPg'][$t] == $splitpg) {
  19777. $bw += $table['wc'][$t];
  19778. }
  19779. if ($table['colPg'][$t] > $splitpg) {
  19780. break;
  19781. }
  19782. }
  19783. if ($table['borders_separate']) {
  19784. if ($firstSpread) {
  19785. $bw += $table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'];
  19786. } else {
  19787. $bx += ($table['padding']['L'] + $table['border_details']['L']['w']);
  19788. $bw += $table['border_spacing_H'];
  19789. }
  19790. if ($finalSpread) {
  19791. $bw += $table['padding']['R'] + $table['border_details']['R']['w'] / 2 + $table['border_spacing_H'];
  19792. }
  19793. }
  19794. } else {
  19795. $bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  19796. }
  19797. if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tablefooter) && $i > 0 && $table['border_details']['B']['w']) {
  19798. $prevDrawColor = $this->DrawColor;
  19799. $lw = $this->LineWidth;
  19800. $this->SetLineWidth($this->splitTableBorderWidth);
  19801. $this->SetDColor($table['border_details']['B']['c']);
  19802. $this->SetLineJoin(0);
  19803. $this->SetLineCap(0);
  19804. $blx = $bx;
  19805. $blw = $bw;
  19806. if (!$table['borders_separate']) {
  19807. $blx -= ($table['max_cell_border_width']['L'] / 2);
  19808. $blw += ($table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2);
  19809. }
  19810. $this->Line($blx, $this->y + ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y + ($this->splitTableBorderWidth / 2));
  19811. $this->DrawColor = $prevDrawColor;
  19812. $this->writer->write($this->DrawColor);
  19813. $this->SetLineWidth($lw);
  19814. $this->SetLineJoin(2);
  19815. $this->SetLineCap(2);
  19816. }
  19817. if (!$this->ColActive && ($i > 0 || $j > 0)) {
  19818. if (isset($table['bgcolor'][-1])) {
  19819. $color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings);
  19820. if ($color) {
  19821. if (!$table['borders_separate']) {
  19822. $bh -= $table['max_cell_border_width']['B'] / 2;
  19823. }
  19824. $this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color];
  19825. }
  19826. }
  19827. /* -- BACKGROUNDS -- */
  19828. if (isset($table['gradient'])) {
  19829. $g = $this->gradient->parseBackgroundGradient($table['gradient']);
  19830. if ($g) {
  19831. $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' => ''];
  19832. }
  19833. }
  19834. if (isset($table['background-image'])) {
  19835. if ($table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {
  19836. $g = $this->gradient->parseMozGradient($table['background-image']['gradient']);
  19837. if ($g) {
  19838. $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' => ''];
  19839. }
  19840. } else {
  19841. $image_id = $table['background-image']['image_id'];
  19842. $orig_w = $table['background-image']['orig_w'];
  19843. $orig_h = $table['background-image']['orig_h'];
  19844. $x_pos = $table['background-image']['x_pos'];
  19845. $y_pos = $table['background-image']['y_pos'];
  19846. $x_repeat = $table['background-image']['x_repeat'];
  19847. $y_repeat = $table['background-image']['y_repeat'];
  19848. $resize = $table['background-image']['resize'];
  19849. $opacity = $table['background-image']['opacity'];
  19850. $itype = $table['background-image']['itype'];
  19851. $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];
  19852. }
  19853. }
  19854. /* -- END BACKGROUNDS -- */
  19855. }
  19856. // $this->AcceptPageBreak() has moved tablebuffer to $this->pages content
  19857. if ($this->tableBackgrounds) {
  19858. $s = $this->PrintTableBackgrounds();
  19859. if ($this->bufferoutput) {
  19860. $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer);
  19861. $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer);
  19862. } else {
  19863. $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  19864. $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]);
  19865. }
  19866. $this->tableBackgrounds = [];
  19867. }
  19868. if ($split) {
  19869. if ($i == 0 && $j == 0) {
  19870. $y0 = -1;
  19871. } elseif ($finalSpread) {
  19872. $splitpg = 0;
  19873. $startcol = 0;
  19874. $startrow = $i;
  19875. } else {
  19876. $splitpg++;
  19877. $startcol = $t;
  19878. $returny -= $y;
  19879. }
  19880. return [false, $startrow, $startcol, $splitpg, $returny, $y0];
  19881. }
  19882. $this->AddPage($this->CurOrientation);
  19883. $this->writer->write('___TABLE___BACKGROUNDS' . $this->uniqstr);
  19884. if ($this->tableClipPath) {
  19885. $this->writer->write($this->tableClipPath);
  19886. }
  19887. // Added to correct for OddEven Margins
  19888. $x = $x + $this->MarginCorrection;
  19889. $x0 = $x0 + $this->MarginCorrection;
  19890. if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tableheader) && $i > 0 && $table['border_details']['T']['w']) {
  19891. $prevDrawColor = $this->DrawColor;
  19892. $lw = $this->LineWidth;
  19893. $this->SetLineWidth($this->splitTableBorderWidth);
  19894. $this->SetDColor($table['border_details']['T']['c']);
  19895. $this->SetLineJoin(0);
  19896. $this->SetLineCap(0);
  19897. $blx += $this->MarginCorrection;
  19898. $this->Line($blx, $this->y - ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y - ($this->splitTableBorderWidth / 2));
  19899. $this->DrawColor = $prevDrawColor;
  19900. $this->writer->write($this->DrawColor);
  19901. $this->SetLineWidth($lw);
  19902. $this->SetLineJoin(2);
  19903. $this->SetLineCap(2);
  19904. }
  19905. // Move down to account for half of top border-spacing or
  19906. // extra half border width in case page was broken in middle
  19907. if ($i > 0 && !$this->table_rotate && $level == 1 && $table['headernrows'] == 0) {
  19908. if ($table['borders_separate']) {
  19909. $adv = $table['border_spacing_V'] / 2;
  19910. } else {
  19911. $maxbwtop = 0;
  19912. for ($ctj = 0; $ctj < $numcols; $ctj++) {
  19913. if (isset($cells[$i][$ctj]) && $cells[$i][$ctj]) {
  19914. if (!$this->simpleTables) {
  19915. if ($this->packTableData) {
  19916. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$i][$ctj]['borderbin']);
  19917. } else {
  19918. $bt = $cells[$i][$ctj]['border_details']['T']['w'];
  19919. }
  19920. $maxbwtop = max($maxbwtop, $bt);
  19921. } elseif ($this->simpleTables) {
  19922. $maxbwtop = max($maxbwtop, $table['simple']['border_details']['T']['w']);
  19923. }
  19924. }
  19925. }
  19926. $adv = $maxbwtop / 2;
  19927. }
  19928. $this->y += $adv;
  19929. }
  19930. if ($this->table_rotate) {
  19931. $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'];
  19932. if ($table['borders_separate']) {
  19933. $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
  19934. } else {
  19935. $this->tbrot_h = $table['margin']['T'] + $table['max_cell_border_width']['T'];
  19936. }
  19937. $this->tbrot_y0 = $this->y;
  19938. $pagetrigger = $y0 - $tableheaderadj + ($this->blk[$this->blklvl]['inner_width']);
  19939. } else {
  19940. $pagetrigger = $this->PageBreakTrigger;
  19941. }
  19942. if ($this->kwt_saved && $level == 1) {
  19943. $this->kwt_moved = true;
  19944. }
  19945. if (!empty($tableheader)) {
  19946. $ya = $this->y;
  19947. $this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);
  19948. if ($this->table_rotate) {
  19949. $this->tbrot_h = $this->y - $ya;
  19950. }
  19951. $tableheaderadj = $this->y - $ya;
  19952. } elseif ($i == 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {
  19953. // Advance down page
  19954. if ($table['borders_separate']) {
  19955. $adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];
  19956. } else {
  19957. $adv = $table['max_cell_border_width']['T'] / 2;
  19958. }
  19959. if ($adv) {
  19960. if ($this->table_rotate) {
  19961. $this->y += ($adv);
  19962. } else {
  19963. $this->DivLn($adv, $this->blklvl, true);
  19964. }
  19965. }
  19966. }
  19967. $outerfilled = 0;
  19968. $y = $y0 = $this->y;
  19969. }
  19970. /* -- COLUMNS -- */
  19971. // COLS
  19972. // COLUMN CHANGE
  19973. if ($this->CurrCol != $oldcolumn) {
  19974. // Added to correct for Columns
  19975. $x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  19976. $x0 += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  19977. if ($this->CurrCol == 0) { // just added a page - possibly with tableheader
  19978. $y0 = $this->y; // this->y0 is global used by Columns - $y0 is internal to tablewrite
  19979. } else {
  19980. $y0 = $this->y0; // this->y0 is global used by Columns - $y0 is internal to tablewrite
  19981. }
  19982. $y = $y0;
  19983. $outerfilled = 0;
  19984. if ($this->CurrCol != 0 && ($this->keepColumns && $this->ColActive) && !empty($tableheader) && $i > 0) {
  19985. $this->x = $x;
  19986. $this->y = $y;
  19987. $this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);
  19988. $y0 = $y = $this->y;
  19989. }
  19990. }
  19991. /* -- END COLUMNS -- */
  19992. }
  19993. $skippage = true;
  19994. }
  19995. $this->x = $x;
  19996. $this->y = $y;
  19997. if ($this->kwt_saved && $level == 1) {
  19998. $this->printkwtbuffer();
  19999. $x0 = $x = $this->x;
  20000. $y0 = $y = $this->y;
  20001. $this->kwt_moved = false;
  20002. $this->kwt_saved = false;
  20003. }
  20004. // Set the Page & Column where table actually starts
  20005. if ($i == 0 && $j == 0 && $level == 1) {
  20006. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  20007. $tablestartpage = 'EVEN';
  20008. } elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD
  20009. $tablestartpage = 'ODD';
  20010. } else {
  20011. $tablestartpage = '';
  20012. }
  20013. $tablestartpageno = $this->page;
  20014. if ($this->ColActive) {
  20015. $tablestartcolumn = $this->CurrCol;
  20016. } // *COLUMNS*
  20017. }
  20018. // ALIGN
  20019. $align = $cell['a'];
  20020. /* -- COLUMNS -- */
  20021. // If outside columns, this is done in PaintDivBB
  20022. if ($this->ColActive) {
  20023. // OUTER FILL BGCOLOR of DIVS
  20024. if ($this->blklvl > 0 && ($j == 0) && !$this->table_rotate && $level == 1) {
  20025. $firstblockfill = $this->GetFirstBlockFill();
  20026. if ($firstblockfill && $this->blklvl >= $firstblockfill) {
  20027. $divh = $maxrowheight;
  20028. // Last row
  20029. 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)))) {
  20030. if ($table['borders_separate']) {
  20031. $adv = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  20032. } else {
  20033. $adv = $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;
  20034. }
  20035. $divh += $adv; // last row: fill bottom half of bottom border (y advanced at end)
  20036. }
  20037. if (($this->y + $divh) > $outerfilled) { // if not already painted by previous rowspan
  20038. $bak_x = $this->x;
  20039. $bak_y = $this->y;
  20040. if ($outerfilled > $this->y) {
  20041. $divh = ($this->y + $divh) - $outerfilled;
  20042. $this->y = $outerfilled;
  20043. }
  20044. $this->DivLn($divh, -3, false);
  20045. $outerfilled = $this->y + $divh;
  20046. // Reset current block fill
  20047. $bcor = $this->blk[$this->blklvl]['bgcolorarray'];
  20048. if ($bcor) {
  20049. $this->SetFColor($bcor);
  20050. }
  20051. $this->x = $bak_x;
  20052. $this->y = $bak_y;
  20053. }
  20054. }
  20055. }
  20056. }
  20057. // TABLE BACKGROUND FILL BGCOLOR - for cellSpacing
  20058. if ($this->ColActive) {
  20059. if ($table['borders_separate']) {
  20060. $fill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;
  20061. if ($fill) {
  20062. $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
  20063. if ($color) {
  20064. $xadj = ($table['border_spacing_H'] / 2);
  20065. $yadj = ($table['border_spacing_V'] / 2);
  20066. $wadj = $table['border_spacing_H'];
  20067. $hadj = $table['border_spacing_V'];
  20068. if ($i == 0) { // Top
  20069. $yadj += $table['padding']['T'] + $table['border_details']['T']['w'];
  20070. $hadj += $table['padding']['T'] + $table['border_details']['T']['w'];
  20071. }
  20072. if ($j == 0) { // Left
  20073. $xadj += $table['padding']['L'] + $table['border_details']['L']['w'];
  20074. $wadj += $table['padding']['L'] + $table['border_details']['L']['w'];
  20075. }
  20076. if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) { // Bottom
  20077. $hadj += $table['padding']['B'] + $table['border_details']['B']['w'];
  20078. }
  20079. if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols) || (!isset($cell['colspan']) && ($j + 1) == $numcols)) { // Right
  20080. $wadj += $table['padding']['R'] + $table['border_details']['R']['w'];
  20081. }
  20082. $this->SetFColor($color);
  20083. $this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');
  20084. }
  20085. }
  20086. }
  20087. }
  20088. /* -- END COLUMNS -- */
  20089. if ($table['empty_cells'] != 'hide' || !empty($cell['textbuffer']) || (isset($cell['nestedcontent']) && $cell['nestedcontent']) || !$table['borders_separate']) {
  20090. $paintcell = true;
  20091. } else {
  20092. $paintcell = false;
  20093. }
  20094. // Set Borders
  20095. $bord = 0;
  20096. $bord_det = [];
  20097. if (!$this->simpleTables) {
  20098. if ($this->packTableData) {
  20099. $c = $this->_unpackCellBorder($cell['borderbin']);
  20100. $bord = $c['border'];
  20101. $bord_det = $c['border_details'];
  20102. } else {
  20103. $bord = $cell['border'];
  20104. $bord_det = $cell['border_details'];
  20105. }
  20106. } elseif ($this->simpleTables) {
  20107. $bord = $table['simple']['border'];
  20108. $bord_det = $table['simple']['border_details'];
  20109. }
  20110. // TABLE ROW OR CELL FILL BGCOLOR
  20111. $fill = 0;
  20112. if (isset($cell['bgcolor']) && $cell['bgcolor'] && $cell['bgcolor'] != 'transparent') {
  20113. $fill = $cell['bgcolor'];
  20114. $leveladj = 6;
  20115. } elseif (isset($table['bgcolor'][$i]) && $table['bgcolor'][$i] && $table['bgcolor'][$i] != 'transparent') { // Row color
  20116. $fill = $table['bgcolor'][$i];
  20117. $leveladj = 3;
  20118. }
  20119. if ($fill && $paintcell) {
  20120. $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
  20121. if ($color) {
  20122. if ($table['borders_separate']) {
  20123. if ($this->ColActive) {
  20124. $this->SetFColor($color);
  20125. $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');
  20126. } else {
  20127. $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];
  20128. }
  20129. } else {
  20130. if ($this->ColActive) {
  20131. $this->SetFColor($color);
  20132. $this->Rect($x, $y, $w, $h, 'F');
  20133. } else {
  20134. $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color];
  20135. }
  20136. }
  20137. }
  20138. }
  20139. /* -- BACKGROUNDS -- */
  20140. if (isset($cell['gradient']) && $cell['gradient'] && $paintcell) {
  20141. $g = $this->gradient->parseBackgroundGradient($cell['gradient']);
  20142. if ($g) {
  20143. if ($table['borders_separate']) {
  20144. $px = $x + ($table['border_spacing_H'] / 2);
  20145. $py = $y + ($table['border_spacing_V'] / 2);
  20146. $pw = $w - $table['border_spacing_H'];
  20147. $ph = $h - $table['border_spacing_V'];
  20148. } else {
  20149. $px = $x;
  20150. $py = $y;
  20151. $pw = $w;
  20152. $ph = $h;
  20153. }
  20154. if ($this->ColActive) {
  20155. $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
  20156. } else {
  20157. $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' => ''];
  20158. }
  20159. }
  20160. }
  20161. if (isset($cell['background-image']) && $paintcell) {
  20162. if (isset($cell['background-image']['gradient']) && $cell['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $cell['background-image']['gradient'])) {
  20163. $g = $this->gradient->parseMozGradient($cell['background-image']['gradient']);
  20164. if ($g) {
  20165. if ($table['borders_separate']) {
  20166. $px = $x + ($table['border_spacing_H'] / 2);
  20167. $py = $y + ($table['border_spacing_V'] / 2);
  20168. $pw = $w - $table['border_spacing_H'];
  20169. $ph = $h - $table['border_spacing_V'];
  20170. } else {
  20171. $px = $x;
  20172. $py = $y;
  20173. $pw = $w;
  20174. $ph = $h;
  20175. }
  20176. if ($this->ColActive) {
  20177. $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
  20178. } else {
  20179. $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' => ''];
  20180. }
  20181. }
  20182. } elseif (isset($cell['background-image']['image_id']) && $cell['background-image']['image_id']) { // Background pattern
  20183. $n = count($this->patterns) + 1;
  20184. if ($table['borders_separate']) {
  20185. $px = $x + ($table['border_spacing_H'] / 2);
  20186. $py = $y + ($table['border_spacing_V'] / 2);
  20187. $pw = $w - $table['border_spacing_H'];
  20188. $ph = $h - $table['border_spacing_V'];
  20189. } else {
  20190. $px = $x;
  20191. $py = $y;
  20192. $pw = $w;
  20193. $ph = $h;
  20194. }
  20195. if ($this->ColActive) {
  20196. 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']);
  20197. $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];
  20198. if ($cell['background-image']['opacity'] > 0 && $cell['background-image']['opacity'] < 1) {
  20199. $opac = $this->SetAlpha($cell['background-image']['opacity'], 'Normal', true);
  20200. } else {
  20201. $opac = '';
  20202. }
  20203. $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));
  20204. } else {
  20205. $image_id = $cell['background-image']['image_id'];
  20206. $orig_w = $cell['background-image']['orig_w'];
  20207. $orig_h = $cell['background-image']['orig_h'];
  20208. $x_pos = $cell['background-image']['x_pos'];
  20209. $y_pos = $cell['background-image']['y_pos'];
  20210. $x_repeat = $cell['background-image']['x_repeat'];
  20211. $y_repeat = $cell['background-image']['y_repeat'];
  20212. $resize = $cell['background-image']['resize'];
  20213. $opacity = $cell['background-image']['opacity'];
  20214. $itype = $cell['background-image']['itype'];
  20215. $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];
  20216. }
  20217. }
  20218. }
  20219. /* -- END BACKGROUNDS -- */
  20220. if (isset($cell['colspan']) && $cell['colspan'] > 1) {
  20221. $ccolsp = $cell['colspan'];
  20222. } else {
  20223. $ccolsp = 1;
  20224. }
  20225. if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {
  20226. $crowsp = $cell['rowspan'];
  20227. } else {
  20228. $crowsp = 1;
  20229. }
  20230. // but still need to do this for repeated headers...
  20231. if (!$table['borders_separate'] && $this->tabletheadjustfinished && !$this->simpleTables) {
  20232. if (isset($table['topntail']) && $table['topntail']) {
  20233. $bord_det['T'] = $this->border_details($table['topntail']);
  20234. $bord_det['T']['w'] /= $this->shrin_k;
  20235. $this->setBorder($bord, Border::TOP);
  20236. }
  20237. if (isset($table['thead-underline']) && $table['thead-underline']) {
  20238. $bord_det['T'] = $this->border_details($table['thead-underline']);
  20239. $bord_det['T']['w'] /= $this->shrin_k;
  20240. $this->setBorder($bord, Border::TOP);
  20241. }
  20242. }
  20243. // Get info of first row ==>> table header
  20244. // Use > 1 row if THEAD
  20245. if (isset($table['is_thead'][$i]) && $table['is_thead'][$i] && $level == 1) {
  20246. if ($j == 0) {
  20247. $tableheaderrowheight += $table['hr'][$i];
  20248. }
  20249. $tableheader[$i][0]['trbackground-images'] = (isset($table['trbackground-images'][$i]) ? $table['trbackground-images'][$i] : null);
  20250. $tableheader[$i][0]['trgradients'] = (isset($table['trgradients'][$i]) ? $table['trgradients'][$i] : null);
  20251. $tableheader[$i][0]['trbgcolor'] = (isset($table['bgcolor'][$i]) ? $table['bgcolor'][$i] : null);
  20252. $tableheader[$i][$j]['x'] = $x;
  20253. $tableheader[$i][$j]['y'] = $y;
  20254. $tableheader[$i][$j]['h'] = $h;
  20255. $tableheader[$i][$j]['w'] = $w;
  20256. if (isset($cell['textbuffer'])) {
  20257. $tableheader[$i][$j]['textbuffer'] = $cell['textbuffer'];
  20258. } else {
  20259. $tableheader[$i][$j]['textbuffer'] = '';
  20260. }
  20261. $tableheader[$i][$j]['a'] = $cell['a'];
  20262. $tableheader[$i][$j]['R'] = $cell['R'];
  20263. $tableheader[$i][$j]['va'] = $cell['va'];
  20264. $tableheader[$i][$j]['mih'] = $cell['mih'];
  20265. $tableheader[$i][$j]['gradient'] = (isset($cell['gradient']) ? $cell['gradient'] : null); // *BACKGROUNDS*
  20266. $tableheader[$i][$j]['background-image'] = (isset($cell['background-image']) ? $cell['background-image'] : null); // *BACKGROUNDS*
  20267. $tableheader[$i][$j]['rowspan'] = (isset($cell['rowspan']) ? $cell['rowspan'] : null);
  20268. $tableheader[$i][$j]['colspan'] = (isset($cell['colspan']) ? $cell['colspan'] : null);
  20269. $tableheader[$i][$j]['bgcolor'] = $cell['bgcolor'];
  20270. if (!$this->simpleTables) {
  20271. $tableheader[$i][$j]['border'] = $bord;
  20272. $tableheader[$i][$j]['border_details'] = $bord_det;
  20273. } elseif ($this->simpleTables) {
  20274. $tableheader[$i][$j]['border'] = $table['simple']['border'];
  20275. $tableheader[$i][$j]['border_details'] = $table['simple']['border_details'];
  20276. }
  20277. $tableheader[$i][$j]['padding'] = $cell['padding'];
  20278. if (isset($cell['direction'])) {
  20279. $tableheader[$i][$j]['direction'] = $cell['direction'];
  20280. }
  20281. if (isset($cell['cellLineHeight'])) {
  20282. $tableheader[$i][$j]['cellLineHeight'] = $cell['cellLineHeight'];
  20283. }
  20284. if (isset($cell['cellLineStackingStrategy'])) {
  20285. $tableheader[$i][$j]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];
  20286. }
  20287. if (isset($cell['cellLineStackingShift'])) {
  20288. $tableheader[$i][$j]['cellLineStackingShift'] = $cell['cellLineStackingShift'];
  20289. }
  20290. }
  20291. // CELL BORDER
  20292. if ($bord) {
  20293. if ($table['borders_separate'] && $paintcell) {
  20294. $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']);
  20295. } elseif (!$table['borders_separate']) {
  20296. $this->_tableRect($x, $y, $w, $h, $bord, $bord_det, true, $table['borders_separate']); // true causes buffer
  20297. }
  20298. }
  20299. // VERTICAL ALIGN
  20300. if ($cell['R'] && intval($cell['R']) > 0 && intval($cell['R']) < 90 && isset($cell['va']) && $cell['va'] != 'B') {
  20301. $cell['va'] = 'B';
  20302. }
  20303. if (!isset($cell['va']) || $cell['va'] == 'M') {
  20304. $this->y += ($h - $cell['mih']) / 2;
  20305. } elseif (isset($cell['va']) && $cell['va'] == 'B') {
  20306. $this->y += $h - $cell['mih'];
  20307. }
  20308. // NESTED CONTENT
  20309. // TEXT (and nested tables)
  20310. $this->divwidth = $w;
  20311. if (!empty($cell['textbuffer'])) {
  20312. $this->cellTextAlign = $align;
  20313. $this->cellLineHeight = $cell['cellLineHeight'];
  20314. $this->cellLineStackingStrategy = $cell['cellLineStackingStrategy'];
  20315. $this->cellLineStackingShift = $cell['cellLineStackingShift'];
  20316. if ($level == 1) {
  20317. if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {
  20318. if (preg_match('/{colsum([0-9]*)[_]*}/', $cell['textbuffer'][0][0], $m)) {
  20319. $rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$j]);
  20320. $cell['textbuffer'][0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $cell['textbuffer'][0][0]);
  20321. }
  20322. } elseif (!isset($table['is_thead'][$i])) {
  20323. if (isset($this->colsums[$j])) {
  20324. $this->colsums[$j] += $this->toFloat($cell['textbuffer'][0][0]);
  20325. } else {
  20326. $this->colsums[$j] = $this->toFloat($cell['textbuffer'][0][0]);
  20327. }
  20328. }
  20329. }
  20330. $opy = $this->y;
  20331. // mPDF ITERATION
  20332. if ($this->iterationCounter) {
  20333. foreach ($cell['textbuffer'] as $k => $t) {
  20334. if (preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {
  20335. $vname = '__' . $m[1] . '_';
  20336. if (!isset($this->$vname)) {
  20337. $this->$vname = 1;
  20338. } else {
  20339. $this->$vname++;
  20340. }
  20341. $cell['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $cell['textbuffer'][$k][0]);
  20342. }
  20343. }
  20344. }
  20345. if ($cell['R']) {
  20346. $cellPtSize = $cell['textbuffer'][0][11] / $this->shrin_k;
  20347. if (!$cellPtSize) {
  20348. $cellPtSize = $this->default_font_size;
  20349. }
  20350. $cellFontHeight = ($cellPtSize / Mpdf::SCALE);
  20351. $opx = $this->x;
  20352. $angle = intval($cell['R']);
  20353. // Only allow 45 to 89 degrees (when bottom-aligned) or exactly 90 or -90
  20354. if ($angle > 90) {
  20355. $angle = 90;
  20356. } elseif ($angle > 0 && $angle < 45) {
  20357. $angle = 45;
  20358. } elseif ($angle < 0) {
  20359. $angle = -90;
  20360. }
  20361. $offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);
  20362. if (isset($cell['a']) && $cell['a'] == 'R') {
  20363. $this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($cell['padding']['R'] + ($table['border_spacing_H'] / 2));
  20364. } elseif (!isset($cell['a']) || $cell['a'] == 'C') {
  20365. $this->x += ($w / 2) + ($offset);
  20366. } else {
  20367. $this->x += ($offset) + ($cellFontHeight / 3) + ($cell['padding']['L'] + ($table['border_spacing_H'] / 2));
  20368. }
  20369. $str = '';
  20370. foreach ($cell['textbuffer'] as $t) {
  20371. $str .= $t[0] . ' ';
  20372. }
  20373. $str = rtrim($str);
  20374. if (!isset($cell['va']) || $cell['va'] == 'M') {
  20375. $this->y -= ($h - $cell['mih']) / 2; // Undo what was added earlier VERTICAL ALIGN
  20376. if ($angle > 0) {
  20377. $this->y += (($h - $cell['mih']) / 2) + $cell['padding']['T'] + ($cell['mih'] - ($cell['padding']['T'] + $cell['padding']['B']));
  20378. } elseif ($angle < 0) {
  20379. $this->y += (($h - $cell['mih']) / 2) + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
  20380. }
  20381. } elseif (isset($cell['va']) && $cell['va'] == 'B') {
  20382. $this->y -= $h - $cell['mih']; // Undo what was added earlier VERTICAL ALIGN
  20383. if ($angle > 0) {
  20384. $this->y += $h - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));
  20385. } elseif ($angle < 0) {
  20386. $this->y += $h - $cell['mih'] + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
  20387. }
  20388. } elseif (isset($cell['va']) && $cell['va'] == 'T') {
  20389. if ($angle > 0) {
  20390. $this->y += $cell['mih'] - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));
  20391. } elseif ($angle < 0) {
  20392. $this->y += ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
  20393. }
  20394. }
  20395. $this->Rotate($angle, $this->x, $this->y);
  20396. $s_fs = $this->FontSizePt;
  20397. $s_f = $this->FontFamily;
  20398. $s_st = $this->FontStyle;
  20399. if (!empty($cell['textbuffer'][0][3])) { // Font Color
  20400. $cor = $cell['textbuffer'][0][3];
  20401. $this->SetTColor($cor);
  20402. }
  20403. $this->SetFont($cell['textbuffer'][0][4], $cell['textbuffer'][0][2], $cellPtSize, true, true);
  20404. $this->magic_reverse_dir($str, $this->directionality, $cell['textbuffer'][0][18]);
  20405. $this->Text($this->x, $this->y, $str, $cell['textbuffer'][0][18], $cell['textbuffer'][0][8]); // textvar
  20406. $this->Rotate(0);
  20407. $this->SetFont($s_f, $s_st, $s_fs, true, true);
  20408. $this->SetTColor(0);
  20409. $this->x = $opx;
  20410. } else {
  20411. if (!$this->simpleTables) {
  20412. if ($bord_det) {
  20413. $btlw = $bord_det['L']['w'];
  20414. $btrw = $bord_det['R']['w'];
  20415. $bttw = $bord_det['T']['w'];
  20416. } else {
  20417. $btlw = 0;
  20418. $btrw = 0;
  20419. $bttw = 0;
  20420. }
  20421. if ($table['borders_separate']) {
  20422. $xadj = $btlw + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);
  20423. $wadj = $btlw + $btrw + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];
  20424. $yadj = $bttw + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);
  20425. } else {
  20426. $xadj = $btlw / 2 + $cell['padding']['L'];
  20427. $wadj = ($btlw + $btrw) / 2 + $cell['padding']['L'] + $cell['padding']['R'];
  20428. $yadj = $bttw / 2 + $cell['padding']['T'];
  20429. }
  20430. } elseif ($this->simpleTables) {
  20431. if ($table['borders_separate']) { // NB twice border width
  20432. $xadj = $table['simple']['border_details']['L']['w'] + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);
  20433. $wadj = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];
  20434. $yadj = $table['simple']['border_details']['T']['w'] + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);
  20435. } else {
  20436. $xadj = $table['simple']['border_details']['L']['w'] / 2 + $cell['padding']['L'];
  20437. $wadj = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + $cell['padding']['L'] + $cell['padding']['R'];
  20438. $yadj = $table['simple']['border_details']['T']['w'] / 2 + $cell['padding']['T'];
  20439. }
  20440. }
  20441. $this->decimal_offset = 0;
  20442. if (substr($cell['a'], 0, 1) == 'D') {
  20443. if (isset($cell['colspan']) && $cell['colspan'] > 1) {
  20444. $this->cellTextAlign = $c['a'] = substr($cell['a'], 2, 1);
  20445. } else {
  20446. $smax = $table['decimal_align'][$j]['maxs0'];
  20447. $d_content = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'];
  20448. $this->decimal_offset = $smax;
  20449. $extra = ($w - $d_content - $wadj);
  20450. if ($extra > 0) {
  20451. if (substr($cell['a'], 2, 1) == 'R') {
  20452. $this->decimal_offset += $extra;
  20453. } elseif (substr($cell['a'], 2, 1) == 'C') {
  20454. $this->decimal_offset += ($extra) / 2;
  20455. }
  20456. }
  20457. }
  20458. }
  20459. $this->divwidth = $w - $wadj;
  20460. if ($this->divwidth == 0) {
  20461. $this->divwidth = 0.0001;
  20462. }
  20463. $this->x += $xadj;
  20464. $this->y += $yadj;
  20465. $this->printbuffer($cell['textbuffer'], '', true, false, $cell['direction']);
  20466. }
  20467. $this->y = $opy;
  20468. }
  20469. /* -- BACKGROUNDS -- */
  20470. if (!$this->ColActive) {
  20471. if (isset($table['trgradients'][$i]) && ($j == 0 || $table['borders_separate'])) {
  20472. $g = $this->gradient->parseBackgroundGradient($table['trgradients'][$i]);
  20473. if ($g) {
  20474. $gx = $x0;
  20475. $gy = $y;
  20476. $gh = $h;
  20477. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  20478. if ($table['borders_separate']) {
  20479. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  20480. $clx = $x + ($table['border_spacing_H'] / 2);
  20481. $cly = $y + ($table['border_spacing_V'] / 2);
  20482. $clw = $w - $table['border_spacing_H'];
  20483. $clh = $h - $table['border_spacing_V'];
  20484. // Set clipping path
  20485. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  20486. $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];
  20487. } else {
  20488. $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' => ''];
  20489. }
  20490. }
  20491. }
  20492. if (isset($table['trbackground-images'][$i]) && ($j == 0 || $table['borders_separate'])) {
  20493. if (isset($table['trbackground-images'][$i]['gradient']) && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['trbackground-images'][$i]['gradient'])) {
  20494. $g = $this->gradient->parseMozGradient($table['trbackground-images'][$i]['gradient']);
  20495. if ($g) {
  20496. $gx = $x0;
  20497. $gy = $y;
  20498. $gh = $h;
  20499. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  20500. if ($table['borders_separate']) {
  20501. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  20502. $clx = $x + ($table['border_spacing_H'] / 2);
  20503. $cly = $y + ($table['border_spacing_V'] / 2);
  20504. $clw = $w - $table['border_spacing_H'];
  20505. $clh = $h - $table['border_spacing_V'];
  20506. // Set clipping path
  20507. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  20508. $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];
  20509. } else {
  20510. $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' => ''];
  20511. }
  20512. }
  20513. } else {
  20514. $image_id = $table['trbackground-images'][$i]['image_id'];
  20515. $orig_w = $table['trbackground-images'][$i]['orig_w'];
  20516. $orig_h = $table['trbackground-images'][$i]['orig_h'];
  20517. $x_pos = $table['trbackground-images'][$i]['x_pos'];
  20518. $y_pos = $table['trbackground-images'][$i]['y_pos'];
  20519. $x_repeat = $table['trbackground-images'][$i]['x_repeat'];
  20520. $y_repeat = $table['trbackground-images'][$i]['y_repeat'];
  20521. $resize = $table['trbackground-images'][$i]['resize'];
  20522. $opacity = $table['trbackground-images'][$i]['opacity'];
  20523. $itype = $table['trbackground-images'][$i]['itype'];
  20524. $clippath = '';
  20525. $gx = $x0;
  20526. $gy = $y;
  20527. $gh = $h;
  20528. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  20529. if ($table['borders_separate']) {
  20530. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  20531. $clx = $x + ($table['border_spacing_H'] / 2);
  20532. $cly = $y + ($table['border_spacing_V'] / 2);
  20533. $clw = $w - $table['border_spacing_H'];
  20534. $clh = $h - $table['border_spacing_V'];
  20535. // Set clipping path
  20536. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  20537. $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];
  20538. } else {
  20539. $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];
  20540. }
  20541. }
  20542. }
  20543. }
  20544. /* -- END BACKGROUNDS -- */
  20545. // TABLE BORDER - if separate
  20546. if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {
  20547. $halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);
  20548. $halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);
  20549. $halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);
  20550. $halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);
  20551. $tbx = $x;
  20552. $tby = $y;
  20553. $tbw = $w;
  20554. $tbh = $h;
  20555. $tab_bord = 0;
  20556. $corner = '';
  20557. if ($i == 0) { // Top
  20558. $tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);
  20559. $tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);
  20560. $this->setBorder($tab_bord, Border::TOP);
  20561. $corner .= 'T';
  20562. }
  20563. if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows)) { // Bottom
  20564. $tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);
  20565. $this->setBorder($tab_bord, Border::BOTTOM);
  20566. $corner .= 'B';
  20567. }
  20568. if ($j == 0) { // Left
  20569. $tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);
  20570. $tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);
  20571. $this->setBorder($tab_bord, Border::LEFT);
  20572. $corner .= 'L';
  20573. }
  20574. if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols)) { // Right
  20575. $tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);
  20576. $this->setBorder($tab_bord, Border::RIGHT);
  20577. $corner .= 'R';
  20578. }
  20579. $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']);
  20580. }
  20581. unset($cell);
  20582. // Reset values
  20583. $this->Reset();
  20584. }//end of (if isset(cells)...)
  20585. }// end of columns
  20586. $newpagestarted = false;
  20587. $this->tabletheadjustfinished = false;
  20588. /* -- COLUMNS -- */
  20589. if ($this->ColActive) {
  20590. if (!$this->table_keep_together && $i < $numrows - 1 && $level == 1) {
  20591. $this->breakpoints[$this->CurrCol][] = $y + $h;
  20592. } // mPDF 6
  20593. if (count($this->cellBorderBuffer)) {
  20594. $this->printcellbuffer();
  20595. }
  20596. }
  20597. /* -- END COLUMNS -- */
  20598. if ($i == $numrows - 1) {
  20599. $this->y = $y + $h;
  20600. } // last row jump (update this->y position)
  20601. if ($this->table_rotate && $level == 1) {
  20602. $this->tbrot_h += $h;
  20603. }
  20604. } // end of rows
  20605. if (count($this->cellBorderBuffer)) {
  20606. $this->printcellbuffer();
  20607. }
  20608. if ($this->tableClipPath) {
  20609. $this->writer->write("Q");
  20610. }
  20611. $this->tableClipPath = '';
  20612. // Advance down page by half width of bottom border
  20613. if ($table['borders_separate']) {
  20614. $this->y += $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  20615. } else {
  20616. $this->y += $table['max_cell_border_width']['B'] / 2;
  20617. }
  20618. if ($table['borders_separate'] && $level == 1) {
  20619. $this->tbrot_h += $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  20620. } elseif ($level == 1) {
  20621. $this->tbrot_h += $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;
  20622. }
  20623. $bx = $x0;
  20624. $by = $y0;
  20625. if ($table['borders_separate']) {
  20626. $bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);
  20627. if ($tablestartpageno != $this->page) { // IF broken across page
  20628. $by += $table['max_cell_border_width']['T'] / 2;
  20629. if (empty($tableheader)) {
  20630. $by -= ($table['border_spacing_V'] / 2);
  20631. }
  20632. } elseif ($split && $startrow > 0 && empty($tableheader)) {
  20633. $by -= ($table['border_spacing_V'] / 2);
  20634. } else {
  20635. $by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);
  20636. }
  20637. } elseif ($tablestartpageno != $this->page && !empty($tableheader)) {
  20638. $by += $maxbwtop / 2;
  20639. }
  20640. $by -= $tableheaderadj;
  20641. $bh = $this->y - $by;
  20642. if (!$table['borders_separate']) {
  20643. $bh -= $table['max_cell_border_width']['B'] / 2;
  20644. }
  20645. if ($split) {
  20646. $bw = 0;
  20647. $finalSpread = true;
  20648. for ($t = $startcol; $t < $numcols; $t++) {
  20649. if ($table['colPg'][$t] == $splitpg) {
  20650. $bw += $table['wc'][$t];
  20651. }
  20652. if ($table['colPg'][$t] > $splitpg) {
  20653. $finalSpread = false;
  20654. break;
  20655. }
  20656. }
  20657. if ($startcol == 0) {
  20658. $firstSpread = true;
  20659. } else {
  20660. $firstSpread = false;
  20661. }
  20662. if ($table['borders_separate']) {
  20663. $bw += $table['border_spacing_H'];
  20664. if ($firstSpread) {
  20665. $bw += $table['padding']['L'] + $table['border_details']['L']['w'];
  20666. } else {
  20667. $bx += ($table['padding']['L'] + $table['border_details']['L']['w']);
  20668. }
  20669. if ($finalSpread) {
  20670. $bw += $table['padding']['R'] + $table['border_details']['R']['w'];
  20671. }
  20672. }
  20673. } else {
  20674. $bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  20675. }
  20676. if (!$this->ColActive) {
  20677. if (isset($table['bgcolor'][-1])) {
  20678. $color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings);
  20679. if ($color) {
  20680. $this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color];
  20681. }
  20682. }
  20683. /* -- BACKGROUNDS -- */
  20684. if (isset($table['gradient'])) {
  20685. $g = $this->gradient->parseBackgroundGradient($table['gradient']);
  20686. if ($g) {
  20687. $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' => ''];
  20688. }
  20689. }
  20690. if (isset($table['background-image'])) {
  20691. if (isset($table['background-image']['gradient']) && $table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {
  20692. $g = $this->gradient->parseMozGradient($table['background-image']['gradient']);
  20693. if ($g) {
  20694. $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' => ''];
  20695. }
  20696. } else {
  20697. $image_id = $table['background-image']['image_id'];
  20698. $orig_w = $table['background-image']['orig_w'];
  20699. $orig_h = $table['background-image']['orig_h'];
  20700. $x_pos = $table['background-image']['x_pos'];
  20701. $y_pos = $table['background-image']['y_pos'];
  20702. $x_repeat = $table['background-image']['x_repeat'];
  20703. $y_repeat = $table['background-image']['y_repeat'];
  20704. $resize = $table['background-image']['resize'];
  20705. $opacity = $table['background-image']['opacity'];
  20706. $itype = $table['background-image']['itype'];
  20707. $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];
  20708. }
  20709. }
  20710. /* -- END BACKGROUNDS -- */
  20711. }
  20712. if ($this->tableBackgrounds && $level == 1) {
  20713. $s = $this->PrintTableBackgrounds();
  20714. if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) {
  20715. $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->tablebuffer);
  20716. if ($level == 1) {
  20717. $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->tablebuffer);
  20718. }
  20719. } elseif ($this->bufferoutput) {
  20720. $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer);
  20721. if ($level == 1) {
  20722. $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer);
  20723. }
  20724. } else {
  20725. $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  20726. if ($level == 1) {
  20727. $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]);
  20728. }
  20729. }
  20730. $this->tableBackgrounds = [];
  20731. }
  20732. // TABLE BOTTOM MARGIN
  20733. if ($table['margin']['B']) {
  20734. if (!$this->table_rotate && $level == 1) {
  20735. $this->DivLn($table['margin']['B'], $this->blklvl, true); // collapsible
  20736. } else {
  20737. $this->y += ($table['margin']['B']);
  20738. }
  20739. }
  20740. if ($this->ColActive && $level == 1) {
  20741. $this->breakpoints[$this->CurrCol][] = $this->y;
  20742. } // *COLUMNS*
  20743. if ($split) {
  20744. // Are there more columns to print on a next page?
  20745. if ($lastCol < $numcols - 1) {
  20746. $splitpg++;
  20747. $startcol = $lastCol + 1;
  20748. return [false, $startrow, $startcol, $splitpg, $returny, $y0];
  20749. } else {
  20750. return [true, 0, 0, 0, false, false];
  20751. }
  20752. }
  20753. }
  20754. // END OF FUNCTION _tableWrite()
  20755. /////////////////////////END OF TABLE CODE//////////////////////////////////
  20756. /* -- END TABLES -- */
  20757. function _putextgstates()
  20758. {
  20759. for ($i = 1; $i <= count($this->extgstates); $i++) {
  20760. $this->writer->object();
  20761. $this->extgstates[$i]['n'] = $this->n;
  20762. $this->writer->write('<</Type /ExtGState');
  20763. foreach ($this->extgstates[$i]['parms'] as $k => $v) {
  20764. $this->writer->write('/' . $k . ' ' . $v);
  20765. }
  20766. $this->writer->write('>>');
  20767. $this->writer->write('endobj');
  20768. }
  20769. }
  20770. function SetProtection($permissions = [], $user_pass = '', $owner_pass = null, $length = 40)
  20771. {
  20772. $this->encrypted = $this->protection->setProtection($permissions, $user_pass, $owner_pass, $length);
  20773. }
  20774. // =========================================
  20775. // FROM class PDF_Bookmark
  20776. function Bookmark($txt, $level = 0, $y = 0)
  20777. {
  20778. $txt = $this->purify_utf8_text($txt);
  20779. if ($this->text_input_as_HTML) {
  20780. $txt = $this->all_entities_to_utf8($txt);
  20781. }
  20782. if ($y == -1) {
  20783. if (!$this->ColActive) {
  20784. $y = $this->y;
  20785. } else {
  20786. $y = $this->y0;
  20787. } // If columns are on - mark top of columns
  20788. }
  20789. // else y is used as set, or =0 i.e. top of page
  20790. // DIRECTIONALITY RTL
  20791. $bmo = ['t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->page];
  20792. if ($this->keep_block_together) {
  20793. // do nothing
  20794. } elseif ($this->table_rotate) {
  20795. $this->tbrot_BMoutlines[] = $bmo;
  20796. } elseif ($this->kwt) {
  20797. $this->kwt_BMoutlines[] = $bmo;
  20798. } elseif ($this->ColActive) {
  20799. $this->col_BMoutlines[] = $bmo;
  20800. } else {
  20801. $this->BMoutlines[] = $bmo;
  20802. }
  20803. }
  20804. /**
  20805. * Initiate, and Mark a place for the Table of Contents to be inserted
  20806. */
  20807. function TOC(
  20808. $tocfont = '',
  20809. $tocfontsize = 0,
  20810. $tocindent = 0,
  20811. $resetpagenum = '',
  20812. $pagenumstyle = '',
  20813. $suppress = '',
  20814. $toc_orientation = '',
  20815. $TOCusePaging = true,
  20816. $TOCuseLinking = false,
  20817. $toc_id = 0,
  20818. $tocoutdent = ''
  20819. ) {
  20820. $this->tableOfContents->TOC(
  20821. $tocfont,
  20822. $tocfontsize,
  20823. $tocindent,
  20824. $resetpagenum,
  20825. $pagenumstyle,
  20826. $suppress,
  20827. $toc_orientation,
  20828. $TOCusePaging,
  20829. $TOCuseLinking,
  20830. $toc_id,
  20831. $tocoutdent
  20832. );
  20833. }
  20834. function TOCpagebreakByArray($a)
  20835. {
  20836. if (!is_array($a)) {
  20837. $a = [];
  20838. }
  20839. $tocoutdent = (isset($a['tocoutdent']) ? $a['tocoutdent'] : (isset($a['outdent']) ? $a['outdent'] : ''));
  20840. $TOCusePaging = (isset($a['TOCusePaging']) ? $a['TOCusePaging'] : (isset($a['paging']) ? $a['paging'] : true));
  20841. $TOCuseLinking = (isset($a['TOCuseLinking']) ? $a['TOCuseLinking'] : (isset($a['links']) ? $a['links'] : ''));
  20842. $toc_orientation = (isset($a['toc_orientation']) ? $a['toc_orientation'] : (isset($a['toc-orientation']) ? $a['toc-orientation'] : ''));
  20843. $toc_mgl = (isset($a['toc_mgl']) ? $a['toc_mgl'] : (isset($a['toc-margin-left']) ? $a['toc-margin-left'] : ''));
  20844. $toc_mgr = (isset($a['toc_mgr']) ? $a['toc_mgr'] : (isset($a['toc-margin-right']) ? $a['toc-margin-right'] : ''));
  20845. $toc_mgt = (isset($a['toc_mgt']) ? $a['toc_mgt'] : (isset($a['toc-margin-top']) ? $a['toc-margin-top'] : ''));
  20846. $toc_mgb = (isset($a['toc_mgb']) ? $a['toc_mgb'] : (isset($a['toc-margin-bottom']) ? $a['toc-margin-bottom'] : ''));
  20847. $toc_mgh = (isset($a['toc_mgh']) ? $a['toc_mgh'] : (isset($a['toc-margin-header']) ? $a['toc-margin-header'] : ''));
  20848. $toc_mgf = (isset($a['toc_mgf']) ? $a['toc_mgf'] : (isset($a['toc-margin-footer']) ? $a['toc-margin-footer'] : ''));
  20849. $toc_ohname = (isset($a['toc_ohname']) ? $a['toc_ohname'] : (isset($a['toc-odd-header-name']) ? $a['toc-odd-header-name'] : ''));
  20850. $toc_ehname = (isset($a['toc_ehname']) ? $a['toc_ehname'] : (isset($a['toc-even-header-name']) ? $a['toc-even-header-name'] : ''));
  20851. $toc_ofname = (isset($a['toc_ofname']) ? $a['toc_ofname'] : (isset($a['toc-odd-footer-name']) ? $a['toc-odd-footer-name'] : ''));
  20852. $toc_efname = (isset($a['toc_efname']) ? $a['toc_efname'] : (isset($a['toc-even-footer-name']) ? $a['toc-even-footer-name'] : ''));
  20853. $toc_ohvalue = (isset($a['toc_ohvalue']) ? $a['toc_ohvalue'] : (isset($a['toc-odd-header-value']) ? $a['toc-odd-header-value'] : 0));
  20854. $toc_ehvalue = (isset($a['toc_ehvalue']) ? $a['toc_ehvalue'] : (isset($a['toc-even-header-value']) ? $a['toc-even-header-value'] : 0));
  20855. $toc_ofvalue = (isset($a['toc_ofvalue']) ? $a['toc_ofvalue'] : (isset($a['toc-odd-footer-value']) ? $a['toc-odd-footer-value'] : 0));
  20856. $toc_efvalue = (isset($a['toc_efvalue']) ? $a['toc_efvalue'] : (isset($a['toc-even-footer-value']) ? $a['toc-even-footer-value'] : 0));
  20857. $toc_preHTML = (isset($a['toc_preHTML']) ? $a['toc_preHTML'] : (isset($a['toc-preHTML']) ? $a['toc-preHTML'] : ''));
  20858. $toc_postHTML = (isset($a['toc_postHTML']) ? $a['toc_postHTML'] : (isset($a['toc-postHTML']) ? $a['toc-postHTML'] : ''));
  20859. $toc_bookmarkText = (isset($a['toc_bookmarkText']) ? $a['toc_bookmarkText'] : (isset($a['toc-bookmarkText']) ? $a['toc-bookmarkText'] : ''));
  20860. $resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');
  20861. $pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');
  20862. $suppress = (isset($a['suppress']) ? $a['suppress'] : '');
  20863. $orientation = (isset($a['orientation']) ? $a['orientation'] : '');
  20864. $mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));
  20865. $mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));
  20866. $mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));
  20867. $mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));
  20868. $mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));
  20869. $mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));
  20870. $ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));
  20871. $ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));
  20872. $ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));
  20873. $efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));
  20874. $ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));
  20875. $ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));
  20876. $ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));
  20877. $efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));
  20878. $toc_id = (isset($a['toc_id']) ? $a['toc_id'] : (isset($a['name']) ? $a['name'] : 0));
  20879. $pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));
  20880. $toc_pagesel = (isset($a['toc_pagesel']) ? $a['toc_pagesel'] : (isset($a['toc-pageselector']) ? $a['toc-pageselector'] : ''));
  20881. $sheetsize = (isset($a['sheetsize']) ? $a['sheetsize'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));
  20882. $toc_sheetsize = (isset($a['toc_sheetsize']) ? $a['toc_sheetsize'] : (isset($a['toc-sheet-size']) ? $a['toc-sheet-size'] : ''));
  20883. $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);
  20884. }
  20885. 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 = '')
  20886. {
  20887. // Start a new page
  20888. if ($this->state == 0) {
  20889. $this->AddPage();
  20890. }
  20891. if ($this->y == $this->tMargin && (!$this->mirrorMargins || ($this->mirrorMargins && $this->page % 2 == 1))) {
  20892. // Don't add a page
  20893. if ($this->page == 1 && count($this->PageNumSubstitutions) == 0) {
  20894. if (!$suppress) {
  20895. $suppress = 'off';
  20896. }
  20897. // $this->PageNumSubstitutions[] = array('from'=>1, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=> $suppress);
  20898. }
  20899. $this->PageNumSubstitutions[] = ['from' => $this->page, 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
  20900. } else {
  20901. $this->AddPage($orientation, 'NEXT-ODD', $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $sheetsize);
  20902. }
  20903. $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);
  20904. }
  20905. function TOC_Entry($txt, $level = 0, $toc_id = 0)
  20906. {
  20907. if ($this->ColActive) {
  20908. $ily = $this->y0;
  20909. } else {
  20910. $ily = $this->y;
  20911. } // use top of columns
  20912. $linkn = $this->AddLink();
  20913. $uid = '__mpdfinternallink_' . $linkn;
  20914. if ($this->table_rotate) {
  20915. $this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "tbrot" => true];
  20916. } elseif ($this->kwt) {
  20917. $this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "kwt" => true];
  20918. } elseif ($this->ColActive) {
  20919. $this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol];
  20920. } elseif (!$this->keep_block_together) {
  20921. $this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page];
  20922. }
  20923. $this->internallink['#' . $uid] = $linkn;
  20924. $this->SetLink($linkn, $ily, $this->page);
  20925. if (strtoupper($toc_id) == 'ALL') {
  20926. $toc_id = '_mpdf_all';
  20927. } elseif (!$toc_id) {
  20928. $toc_id = 0;
  20929. } else {
  20930. $toc_id = strtolower($toc_id);
  20931. }
  20932. $btoc = ['t' => $txt, 'l' => $level, 'p' => $this->page, 'link' => $linkn, 'toc_id' => $toc_id];
  20933. if ($this->keep_block_together) {
  20934. // do nothing
  20935. } /* -- TABLES -- */ elseif ($this->table_rotate) {
  20936. $this->tbrot_toc[] = $btoc;
  20937. } elseif ($this->kwt) {
  20938. $this->kwt_toc[] = $btoc;
  20939. } /* -- END TABLES -- */ elseif ($this->ColActive) { // *COLUMNS*
  20940. $this->col_toc[] = $btoc; // *COLUMNS*
  20941. } // *COLUMNS*
  20942. else {
  20943. $this->tableOfContents->_toc[] = $btoc;
  20944. }
  20945. }
  20946. /* -- END TOC -- */
  20947. // ======================================================
  20948. function MovePages($target_page, $start_page, $end_page = -1)
  20949. {
  20950. // move a page/pages EARLIER in the document
  20951. if ($end_page < 1) {
  20952. $end_page = $start_page;
  20953. }
  20954. $n_toc = $end_page - $start_page + 1;
  20955. // Set/Update PageNumSubstitutions changes before moving anything
  20956. if (count($this->PageNumSubstitutions)) {
  20957. $tp_present = false;
  20958. $sp_present = false;
  20959. $ep_present = false;
  20960. foreach ($this->PageNumSubstitutions as $k => $v) {
  20961. if ($this->PageNumSubstitutions[$k]['from'] == $target_page) {
  20962. $tp_present = true;
  20963. if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
  20964. $this->PageNumSubstitutions[$k]['suppress'] = 'off';
  20965. }
  20966. }
  20967. if ($this->PageNumSubstitutions[$k]['from'] == $start_page) {
  20968. $sp_present = true;
  20969. if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
  20970. $this->PageNumSubstitutions[$k]['suppress'] = 'off';
  20971. }
  20972. }
  20973. if ($this->PageNumSubstitutions[$k]['from'] == ($end_page + 1)) {
  20974. $ep_present = true;
  20975. if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
  20976. $this->PageNumSubstitutions[$k]['suppress'] = 'off';
  20977. }
  20978. }
  20979. }
  20980. if (!$tp_present) {
  20981. list($tp_type, $tp_suppress, $tp_reset) = $this->docPageSettings($target_page);
  20982. }
  20983. if (!$sp_present) {
  20984. list($sp_type, $sp_suppress, $sp_reset) = $this->docPageSettings($start_page);
  20985. }
  20986. if (!$ep_present) {
  20987. list($ep_type, $ep_suppress, $ep_reset) = $this->docPageSettings($start_page - 1);
  20988. }
  20989. }
  20990. $last = [];
  20991. // store pages
  20992. for ($i = $start_page; $i <= $end_page; $i++) {
  20993. $last[] = $this->pages[$i];
  20994. }
  20995. // move pages
  20996. for ($i = $start_page - 1; $i >= ($target_page); $i--) {
  20997. $this->pages[$i + $n_toc] = $this->pages[$i];
  20998. }
  20999. // Put toc pages at insert point
  21000. for ($i = 0; $i < $n_toc; $i++) {
  21001. $this->pages[$target_page + $i] = $last[$i];
  21002. }
  21003. /* -- BOOKMARKS -- */
  21004. // Update Bookmarks
  21005. foreach ($this->BMoutlines as $i => $o) {
  21006. if ($o['p'] >= $target_page) {
  21007. $this->BMoutlines[$i]['p'] += $n_toc;
  21008. }
  21009. }
  21010. /* -- END BOOKMARKS -- */
  21011. // Update Page Links
  21012. if (count($this->PageLinks)) {
  21013. $newarr = [];
  21014. foreach ($this->PageLinks as $i => $o) {
  21015. foreach ($this->PageLinks[$i] as $key => $pl) {
  21016. if (strpos($pl[4], '@') === 0) {
  21017. $p = substr($pl[4], 1);
  21018. if ($p >= $start_page && $p <= $end_page) {
  21019. $this->PageLinks[$i][$key][4] = '@' . ($p + ($target_page - $start_page));
  21020. } elseif ($p >= $target_page && $p < $start_page) {
  21021. $this->PageLinks[$i][$key][4] = '@' . ($p + $n_toc);
  21022. }
  21023. }
  21024. }
  21025. if ($i >= $start_page && $i <= $end_page) {
  21026. $newarr[($i + ($target_page - $start_page))] = $this->PageLinks[$i];
  21027. } elseif ($i >= $target_page && $i < $start_page) {
  21028. $newarr[($i + $n_toc)] = $this->PageLinks[$i];
  21029. } else {
  21030. $newarr[$i] = $this->PageLinks[$i];
  21031. }
  21032. }
  21033. $this->PageLinks = $newarr;
  21034. }
  21035. // OrientationChanges
  21036. if (count($this->OrientationChanges)) {
  21037. $newarr = [];
  21038. foreach ($this->OrientationChanges as $p => $v) {
  21039. if ($p >= $start_page && $p <= $end_page) {
  21040. $newarr[($p + ($target_page - $start_page))] = $this->OrientationChanges[$p];
  21041. } elseif ($p >= $target_page && $p < $start_page) {
  21042. $newarr[$p + $n_toc] = $this->OrientationChanges[$p];
  21043. } else {
  21044. $newarr[$p] = $this->OrientationChanges[$p];
  21045. }
  21046. }
  21047. ksort($newarr);
  21048. $this->OrientationChanges = $newarr;
  21049. }
  21050. // Page Dimensions
  21051. if (count($this->pageDim)) {
  21052. $newarr = [];
  21053. foreach ($this->pageDim as $p => $v) {
  21054. if ($p >= $start_page && $p <= $end_page) {
  21055. $newarr[($p + ($target_page - $start_page))] = $this->pageDim[$p];
  21056. } elseif ($p >= $target_page && $p < $start_page) {
  21057. $newarr[$p + $n_toc] = $this->pageDim[$p];
  21058. } else {
  21059. $newarr[$p] = $this->pageDim[$p];
  21060. }
  21061. }
  21062. ksort($newarr);
  21063. $this->pageDim = $newarr;
  21064. }
  21065. // HTML Headers & Footers
  21066. if (count($this->saveHTMLHeader)) {
  21067. $newarr = [];
  21068. foreach ($this->saveHTMLHeader as $p => $v) {
  21069. if ($p >= $start_page && $p <= $end_page) {
  21070. $newarr[($p + ($target_page - $start_page))] = $this->saveHTMLHeader[$p];
  21071. } elseif ($p >= $target_page && $p < $start_page) {
  21072. $newarr[$p + $n_toc] = $this->saveHTMLHeader[$p];
  21073. } else {
  21074. $newarr[$p] = $this->saveHTMLHeader[$p];
  21075. }
  21076. }
  21077. ksort($newarr);
  21078. $this->saveHTMLHeader = $newarr;
  21079. }
  21080. if (count($this->saveHTMLFooter)) {
  21081. $newarr = [];
  21082. foreach ($this->saveHTMLFooter as $p => $v) {
  21083. if ($p >= $start_page && $p <= $end_page) {
  21084. $newarr[($p + ($target_page - $start_page))] = $this->saveHTMLFooter[$p];
  21085. } elseif ($p >= $target_page && $p < $start_page) {
  21086. $newarr[$p + $n_toc] = $this->saveHTMLFooter[$p];
  21087. } else {
  21088. $newarr[$p] = $this->saveHTMLFooter[$p];
  21089. }
  21090. }
  21091. ksort($newarr);
  21092. $this->saveHTMLFooter = $newarr;
  21093. }
  21094. // Update Internal Links
  21095. if (count($this->internallink)) {
  21096. foreach ($this->internallink as $key => $o) {
  21097. if (is_array($o) && $o['PAGE'] >= $start_page && $o['PAGE'] <= $end_page) {
  21098. $this->internallink[$key]['PAGE'] += ($target_page - $start_page);
  21099. } elseif (is_array($o) && $o['PAGE'] >= $target_page && $o['PAGE'] < $start_page) {
  21100. $this->internallink[$key]['PAGE'] += $n_toc;
  21101. }
  21102. }
  21103. }
  21104. // Update Links
  21105. if (count($this->links)) {
  21106. foreach ($this->links as $key => $o) {
  21107. if ($o[0] >= $start_page && $o[0] <= $end_page) {
  21108. $this->links[$key][0] += ($target_page - $start_page);
  21109. }
  21110. if ($o[0] >= $target_page && $o[0] < $start_page) {
  21111. $this->links[$key][0] += $n_toc;
  21112. }
  21113. }
  21114. }
  21115. // Update Form fields
  21116. if (count($this->form->forms)) {
  21117. foreach ($this->form->forms as $key => $f) {
  21118. if ($f['page'] >= $start_page && $f['page'] <= $end_page) {
  21119. $this->form->forms[$key]['page'] += ($target_page - $start_page);
  21120. }
  21121. if ($f['page'] >= $target_page && $f['page'] < $start_page) {
  21122. $this->form->forms[$key]['page'] += $n_toc;
  21123. }
  21124. }
  21125. }
  21126. /* -- ANNOTATIONS -- */
  21127. // Update Annotations
  21128. if (count($this->PageAnnots)) {
  21129. $newarr = [];
  21130. foreach ($this->PageAnnots as $p => $anno) {
  21131. if ($p >= $start_page && $p <= $end_page) {
  21132. $np = $p + ($target_page - $start_page);
  21133. foreach ($anno as $o) {
  21134. $newarr[$np][] = $o;
  21135. }
  21136. } elseif ($p >= $target_page && $p < $start_page) {
  21137. $np = $p + $n_toc;
  21138. foreach ($anno as $o) {
  21139. $newarr[$np][] = $o;
  21140. }
  21141. } else {
  21142. $newarr[$p] = $this->PageAnnots[$p];
  21143. }
  21144. }
  21145. $this->PageAnnots = $newarr;
  21146. unset($newarr);
  21147. }
  21148. /* -- END ANNOTATIONS -- */
  21149. // Update TOC pages
  21150. if (count($this->tableOfContents->_toc)) {
  21151. foreach ($this->tableOfContents->_toc as $key => $t) {
  21152. if ($t['p'] >= $start_page && $t['p'] <= $end_page) {
  21153. $this->tableOfContents->_toc[$key]['p'] += ($target_page - $start_page);
  21154. }
  21155. if ($t['p'] >= $target_page && $t['p'] < $start_page) {
  21156. $this->tableOfContents->_toc[$key]['p'] += $n_toc;
  21157. }
  21158. }
  21159. }
  21160. // Update PageNumSubstitutions
  21161. if (count($this->PageNumSubstitutions)) {
  21162. $newarr = [];
  21163. foreach ($this->PageNumSubstitutions as $k => $v) {
  21164. if ($this->PageNumSubstitutions[$k]['from'] >= $start_page && $this->PageNumSubstitutions[$k]['from'] <= $end_page) {
  21165. $this->PageNumSubstitutions[$k]['from'] += ($target_page - $start_page);
  21166. $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
  21167. } elseif ($this->PageNumSubstitutions[$k]['from'] >= $target_page && $this->PageNumSubstitutions[$k]['from'] < $start_page) {
  21168. $this->PageNumSubstitutions[$k]['from'] += $n_toc;
  21169. $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
  21170. } else {
  21171. $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
  21172. }
  21173. }
  21174. if (!$sp_present) {
  21175. $newarr[$target_page] = ['from' => $target_page, 'suppress' => $sp_suppress, 'reset' => $sp_reset, 'type' => $sp_type];
  21176. }
  21177. if (!$tp_present) {
  21178. $newarr[($target_page + $n_toc)] = ['from' => ($target_page + $n_toc), 'suppress' => $tp_suppress, 'reset' => $tp_reset, 'type' => $tp_type];
  21179. }
  21180. if (!$ep_present && $end_page > count($this->pages)) {
  21181. $newarr[($end_page + 1)] = ['from' => ($end_page + 1), 'suppress' => $ep_suppress, 'reset' => $ep_reset, 'type' => $ep_type];
  21182. }
  21183. ksort($newarr);
  21184. $this->PageNumSubstitutions = [];
  21185. foreach ($newarr as $v) {
  21186. $this->PageNumSubstitutions[] = $v;
  21187. }
  21188. }
  21189. }
  21190. function DeletePages($start_page, $end_page = -1)
  21191. {
  21192. // move a page/pages EARLIER in the document
  21193. if ($end_page < 1) {
  21194. $end_page = $start_page;
  21195. }
  21196. $n_tod = $end_page - $start_page + 1;
  21197. $last_page = count($this->pages);
  21198. $n_atend = $last_page - $end_page + 1;
  21199. // move pages
  21200. for ($i = 0; $i < $n_atend; $i++) {
  21201. $this->pages[$start_page + $i] = $this->pages[$end_page + 1 + $i];
  21202. }
  21203. // delete pages
  21204. for ($i = 0; $i < $n_tod; $i++) {
  21205. unset($this->pages[$last_page - $i]);
  21206. }
  21207. /* -- BOOKMARKS -- */
  21208. // Update Bookmarks
  21209. foreach ($this->BMoutlines as $i => $o) {
  21210. if ($o['p'] >= $end_page) {
  21211. $this->BMoutlines[$i]['p'] -= $n_tod;
  21212. } elseif ($p < $start_page) {
  21213. unset($this->BMoutlines[$i]);
  21214. }
  21215. }
  21216. /* -- END BOOKMARKS -- */
  21217. // Update Page Links
  21218. if (count($this->PageLinks)) {
  21219. $newarr = [];
  21220. foreach ($this->PageLinks as $i => $o) {
  21221. foreach ($this->PageLinks[$i] as $key => $pl) {
  21222. if (strpos($pl[4], '@') === 0) {
  21223. $p = substr($pl[4], 1);
  21224. if ($p > $end_page) {
  21225. $this->PageLinks[$i][$key][4] = '@' . ($p - $n_tod);
  21226. } elseif ($p < $start_page) {
  21227. unset($this->PageLinks[$i][$key]);
  21228. }
  21229. }
  21230. }
  21231. if ($i > $end_page) {
  21232. $newarr[($i - $n_tod)] = $this->PageLinks[$i];
  21233. } elseif ($p < $start_page) {
  21234. $newarr[$i] = $this->PageLinks[$i];
  21235. }
  21236. }
  21237. $this->PageLinks = $newarr;
  21238. }
  21239. // OrientationChanges
  21240. if (count($this->OrientationChanges)) {
  21241. $newarr = [];
  21242. foreach ($this->OrientationChanges as $p => $v) {
  21243. if ($p > $end_page) {
  21244. $newarr[($p - $t_tod)] = $this->OrientationChanges[$p];
  21245. } elseif ($p < $start_page) {
  21246. $newarr[$p] = $this->OrientationChanges[$p];
  21247. }
  21248. }
  21249. ksort($newarr);
  21250. $this->OrientationChanges = $newarr;
  21251. }
  21252. // Page Dimensions
  21253. if (count($this->pageDim)) {
  21254. $newarr = [];
  21255. foreach ($this->pageDim as $p => $v) {
  21256. if ($p > $end_page) {
  21257. $newarr[($p - $n_tod)] = $this->pageDim[$p];
  21258. } elseif ($p < $start_page) {
  21259. $newarr[$p] = $this->pageDim[$p];
  21260. }
  21261. }
  21262. ksort($newarr);
  21263. $this->pageDim = $newarr;
  21264. }
  21265. // HTML Headers & Footers
  21266. if (count($this->saveHTMLHeader)) {
  21267. foreach ($this->saveHTMLHeader as $p => $v) {
  21268. if ($p > $end_page) {
  21269. $newarr[($p - $n_tod)] = $this->saveHTMLHeader[$p];
  21270. } // mPDF 5.7.3
  21271. elseif ($p < $start_page) {
  21272. $newarr[$p] = $this->saveHTMLHeader[$p];
  21273. }
  21274. }
  21275. ksort($newarr);
  21276. $this->saveHTMLHeader = $newarr;
  21277. }
  21278. if (count($this->saveHTMLFooter)) {
  21279. $newarr = [];
  21280. foreach ($this->saveHTMLFooter as $p => $v) {
  21281. if ($p > $end_page) {
  21282. $newarr[($p - $n_tod)] = $this->saveHTMLFooter[$p];
  21283. } elseif ($p < $start_page) {
  21284. $newarr[$p] = $this->saveHTMLFooter[$p];
  21285. }
  21286. }
  21287. ksort($newarr);
  21288. $this->saveHTMLFooter = $newarr;
  21289. }
  21290. // Update Internal Links
  21291. foreach ($this->internallink as $key => $o) {
  21292. if ($o['PAGE'] > $end_page) {
  21293. $this->internallink[$key]['PAGE'] -= $n_tod;
  21294. } elseif ($o['PAGE'] < $start_page) {
  21295. unset($this->internallink[$key]);
  21296. }
  21297. }
  21298. // Update Links
  21299. foreach ($this->links as $key => $o) {
  21300. if ($o[0] > $end_page) {
  21301. $this->links[$key][0] -= $n_tod;
  21302. } elseif ($o[0] < $start_page) {
  21303. unset($this->links[$key]);
  21304. }
  21305. }
  21306. // Update Form fields
  21307. foreach ($this->form->forms as $key => $f) {
  21308. if ($f['page'] > $end_page) {
  21309. $this->form->forms[$key]['page'] -= $n_tod;
  21310. } elseif ($f['page'] < $start_page) {
  21311. unset($this->form->forms[$key]);
  21312. }
  21313. }
  21314. /* -- ANNOTATIONS -- */
  21315. // Update Annotations
  21316. if (count($this->PageAnnots)) {
  21317. $newarr = [];
  21318. foreach ($this->PageAnnots as $p => $anno) {
  21319. if ($p > $end_page) {
  21320. foreach ($anno as $o) {
  21321. $newarr[($p - $n_tod)][] = $o;
  21322. }
  21323. } elseif ($p < $start_page) {
  21324. $newarr[$p] = $this->PageAnnots[$p];
  21325. }
  21326. }
  21327. ksort($newarr);
  21328. $this->PageAnnots = $newarr;
  21329. }
  21330. /* -- END ANNOTATIONS -- */
  21331. // Update PageNumSubstitutions
  21332. foreach ($this->PageNumSubstitutions as $k => $v) {
  21333. if ($this->PageNumSubstitutions[$k]['from'] > $end_page) {
  21334. $this->PageNumSubstitutions[$k]['from'] -= $n_tod;
  21335. } elseif ($this->PageNumSubstitutions[$k]['from'] < $start_page) {
  21336. unset($this->PageNumSubstitutions[$k]);
  21337. }
  21338. }
  21339. unset($newarr);
  21340. $this->page = count($this->pages);
  21341. }
  21342. // ======================================================
  21343. /* -- INDEX -- */
  21344. // FROM class PDF_Ref == INDEX
  21345. function IndexEntry($txt, $xref = '')
  21346. {
  21347. if ($xref) {
  21348. $this->IndexEntrySee($txt, $xref);
  21349. return;
  21350. }
  21351. // Search the reference (AND Ref/PageNo) in the array
  21352. $Present = false;
  21353. if ($this->keep_block_together) {
  21354. // do nothing
  21355. } /* -- TABLES -- */ elseif ($this->kwt) {
  21356. $size = count($this->kwt_Reference);
  21357. for ($i = 0; $i < $size; $i++) {
  21358. if (isset($this->kwt_Reference[$i]['t']) && $this->kwt_Reference[$i]['t'] == $txt) {
  21359. $Present = true;
  21360. if ($this->page != $this->kwt_Reference[$i]['op']) {
  21361. $this->kwt_Reference[$i]['op'] = $this->page;
  21362. }
  21363. }
  21364. }
  21365. if (!$Present) { // If not found, add it
  21366. $this->kwt_Reference[] = ['t' => $txt, 'op' => $this->page];
  21367. }
  21368. } /* -- END TABLES -- */ else {
  21369. $size = count($this->Reference);
  21370. for ($i = 0; $i < $size; $i++) {
  21371. if (isset($this->Reference[$i]['t']) && $this->Reference[$i]['t'] == $txt) {
  21372. $Present = true;
  21373. if (!in_array($this->page, $this->Reference[$i]['p'])) {
  21374. $this->Reference[$i]['p'][] = $this->page;
  21375. }
  21376. }
  21377. }
  21378. if (!$Present) { // If not found, add it
  21379. $this->Reference[] = ['t' => $txt, 'p' => [$this->page]];
  21380. }
  21381. }
  21382. }
  21383. // Added function to add a reference "Elephants. See Chickens"
  21384. function IndexEntrySee($txta, $txtb)
  21385. {
  21386. if ($this->directionality == 'rtl') { // *OTL*
  21387. // ONLY DO THIS IF NOT IN TAGS
  21388. if ($txta == strip_tags($txta)) {
  21389. $txta = str_replace(':', ' - ', $txta); // *OTL*
  21390. }
  21391. if ($txtb == strip_tags($txtb)) {
  21392. $txtb = str_replace(':', ' - ', $txtb); // *OTL*
  21393. }
  21394. } // *OTL*
  21395. else { // *OTL*
  21396. if ($txta == strip_tags($txta)) {
  21397. $txta = str_replace(':', ', ', $txta);
  21398. }
  21399. if ($txtb == strip_tags($txtb)) {
  21400. $txtb = str_replace(':', ', ', $txtb);
  21401. }
  21402. } // *OTL*
  21403. $this->Reference[] = ['t' => $txta . ' - see ' . $txtb, 'p' => []];
  21404. }
  21405. private function filesInDir($directory)
  21406. {
  21407. $files = [];
  21408. foreach ((new \DirectoryIterator($directory)) as $v) {
  21409. if ($v->isDir() || $v->isDot()) {
  21410. continue;
  21411. }
  21412. $files[] = $v->getPathname();
  21413. }
  21414. return $files;
  21415. }
  21416. function InsertIndex($usedivletters = 1, $useLinking = false, $indexCollationLocale = '', $indexCollationGroup = '')
  21417. {
  21418. $size = count($this->Reference);
  21419. if ($size == 0) {
  21420. return false;
  21421. }
  21422. // $spacer used after named entry
  21423. // $sep separates number [groups], $joiner joins numbers in range
  21424. // e.g. "elephant 73, 97-99" = elephant[$spacer]73[$sep]97[$joiner]99
  21425. // $subEntrySeparator separates main and subentry (if $this->indexUseSubentries == false;) e.g.
  21426. // Mammal:elephant => Mammal[$subEntrySeparator]elephant
  21427. // $subEntryInset specifies what precedes a subentry (if $this->indexUseSubentries == true;) e.g.
  21428. // Mammal:elephant => [$subEntryInset]elephant
  21429. $spacer = "\xc2\xa0 ";
  21430. if ($this->directionality == 'rtl') {
  21431. $sep = '&#x060c; ';
  21432. $joiner = '-';
  21433. $subEntrySeparator = '&#x060c; ';
  21434. $subEntryInset = ' - ';
  21435. } else {
  21436. $sep = ', ';
  21437. $joiner = '-';
  21438. $subEntrySeparator = ', ';
  21439. $subEntryInset = ' - ';
  21440. }
  21441. for ($i = 0; $i < $size; $i++) {
  21442. $txt = $this->Reference[$i]['t'];
  21443. $txt = strip_tags($txt); // mPDF 6
  21444. $txt = $this->purify_utf8($txt);
  21445. $this->Reference[$i]['uf'] = $txt; // Unformatted e.g. pure utf-8 encoded characters, no mark-up/tags
  21446. // Used for ordering and collation
  21447. }
  21448. if ($usedivletters) {
  21449. if ($indexCollationGroup && \in_array(strtolower($indexCollationGroup), array_map(function ($v) {
  21450. return strtolower(basename($v, '.php'));
  21451. }, $this->filesInDir(__DIR__ . '/../data/collations/')))) {
  21452. $collation = require __DIR__ . '/../data/collations/' . $indexCollationGroup . '.php';
  21453. } else {
  21454. $collation = [];
  21455. }
  21456. for ($i = 0; $i < $size; $i++) {
  21457. if ($this->Reference[$i]['uf']) {
  21458. $l = mb_substr($this->Reference[$i]['uf'], 0, 1, 'UTF-8');
  21459. if (isset($indexCollationGroup) && $indexCollationGroup) {
  21460. $uni = $this->UTF8StringToArray($l);
  21461. $ucode = $uni[0];
  21462. if (isset($collation[$ucode])) {
  21463. $this->Reference[$i]['d'] = UtfString::code2utf($collation[$ucode]);
  21464. } else {
  21465. $this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');
  21466. }
  21467. } else {
  21468. $this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');
  21469. }
  21470. }
  21471. }
  21472. }
  21473. // Alphabetic sort of the references
  21474. $originalLocale = setlocale(LC_COLLATE, 0);
  21475. if ($indexCollationLocale) {
  21476. setlocale(LC_COLLATE, $indexCollationLocale);
  21477. }
  21478. usort($this->Reference, function ($a, $b) {
  21479. return strcoll(strtolower($a['uf']), strtolower($b['uf']));
  21480. });
  21481. if ($indexCollationLocale) {
  21482. setlocale(LC_COLLATE, $originalLocale);
  21483. }
  21484. $html = '<div class="mpdf_index_main">';
  21485. $lett = '';
  21486. $last_lett = '';
  21487. $mainentry = '';
  21488. for ($i = 0; $i < $size; $i++) {
  21489. if ($this->Reference[$i]['t']) {
  21490. if ($usedivletters) {
  21491. $lett = $this->Reference[$i]['d'];
  21492. if ($lett != $last_lett) {
  21493. $html .= '<div class="mpdf_index_letter">' . $lett . '</div>';
  21494. }
  21495. }
  21496. $txt = $this->Reference[$i]['t'];
  21497. // Sub-entries e.g. Mammals:elephant
  21498. // But allow for tags e.g. <b>Mammal</b>:elephants
  21499. $a = preg_split('/(<.*?>)/', $txt, -1, PREG_SPLIT_DELIM_CAPTURE);
  21500. $txt = '';
  21501. $marker = false;
  21502. foreach ($a as $k => $e) {
  21503. if ($k % 2 == 0 && !$marker) {
  21504. if (strpos($e, ':') !== false) { // == SubEntry
  21505. if ($this->indexUseSubentries) {
  21506. // If the Main entry does not have any page numbers associated with it
  21507. // create and insert an entry
  21508. list($txtmain, $sub) = preg_split('/[:]/', $e, 2);
  21509. if (strip_tags($txt . $txtmain) != $mainentry) {
  21510. $html .= '<div class="mpdf_index_entry">' . $txt . $txtmain . '</div>';
  21511. $mainentry = strip_tags($txt . $txtmain);
  21512. }
  21513. $txt = $subEntryInset;
  21514. $e = $sub; // Only replace first one
  21515. } else {
  21516. $e = preg_replace('/[:]/', $subEntrySeparator, $e, 1); // Only replace first one
  21517. }
  21518. $marker = true; // Don't replace any more once the subentry marker has been found
  21519. }
  21520. }
  21521. $txt .= $e;
  21522. }
  21523. if (!$marker) {
  21524. $mainentry = strip_tags($txt);
  21525. }
  21526. $html .= '<div class="mpdf_index_entry">';
  21527. $html .= $txt;
  21528. $ppp = $this->Reference[$i]['p']; // = array of page numbers to point to
  21529. if (count($ppp)) {
  21530. sort($ppp);
  21531. $newarr = [];
  21532. $range_start = $ppp[0];
  21533. $range_end = 0;
  21534. $html .= $spacer;
  21535. for ($zi = 1; $zi < count($ppp); $zi++) {
  21536. if ($ppp[$zi] == ($ppp[($zi - 1)] + 1)) {
  21537. $range_end = $ppp[$zi];
  21538. } else {
  21539. if ($range_end) {
  21540. if ($range_end == $range_start + 1) {
  21541. if ($useLinking) {
  21542. $html .= '<a class="mpdf_index_link" href="@' . $range_start . '">';
  21543. }
  21544. $html .= $this->docPageNum($range_start);
  21545. if ($useLinking) {
  21546. $html .= '</a>';
  21547. }
  21548. $html .= $sep;
  21549. if ($useLinking) {
  21550. $html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">';
  21551. }
  21552. $html .= $this->docPageNum($ppp[$zi - 1]);
  21553. if ($useLinking) {
  21554. $html .= '</a>';
  21555. }
  21556. $html .= $sep;
  21557. }
  21558. } else {
  21559. if ($useLinking) {
  21560. $html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">';
  21561. }
  21562. $html .= $this->docPageNum($ppp[$zi - 1]);
  21563. if ($useLinking) {
  21564. $html .= '</a>';
  21565. }
  21566. $html .= $sep;
  21567. }
  21568. $range_start = $ppp[$zi];
  21569. $range_end = 0;
  21570. }
  21571. }
  21572. if ($range_end) {
  21573. if ($useLinking) {
  21574. $html .= '<a class="mpdf_index_link" href="@' . $range_start . '">';
  21575. }
  21576. $html .= $this->docPageNum($range_start);
  21577. if ($range_end == $range_start + 1) {
  21578. if ($useLinking) {
  21579. $html .= '</a>';
  21580. }
  21581. $html .= $sep;
  21582. if ($useLinking) {
  21583. $html .= '<a class="mpdf_index_link" href="@' . $range_end . '">';
  21584. }
  21585. $html .= $this->docPageNum($range_end);
  21586. if ($useLinking) {
  21587. $html .= '</a>';
  21588. }
  21589. } else {
  21590. $html .= $joiner;
  21591. $html .= $this->docPageNum($range_end);
  21592. if ($useLinking) {
  21593. $html .= '</a>';
  21594. }
  21595. }
  21596. } else {
  21597. if ($useLinking) {
  21598. $html .= '<a class="mpdf_index_link" href="@' . $ppp[(count($ppp) - 1)] . '">';
  21599. }
  21600. $html .= $this->docPageNum($ppp[(count($ppp) - 1)]);
  21601. if ($useLinking) {
  21602. $html .= '</a>';
  21603. }
  21604. }
  21605. }
  21606. }
  21607. $html .= '</div>';
  21608. $last_lett = $lett;
  21609. }
  21610. $html .= '</div>';
  21611. $save_fpb = $this->fixedPosBlockSave;
  21612. $this->WriteHTML($html);
  21613. $this->fixedPosBlockSave = $save_fpb;
  21614. $this->breakpoints[$this->CurrCol][] = $this->y; // *COLUMNS*
  21615. }
  21616. /* -- END INDEX -- */
  21617. function AcceptPageBreak()
  21618. {
  21619. if (count($this->cellBorderBuffer)) {
  21620. $this->printcellbuffer();
  21621. } // *TABLES*
  21622. /* -- COLUMNS -- */
  21623. if ($this->ColActive == 1) {
  21624. if ($this->CurrCol < $this->NbCol - 1) {
  21625. // Go to the next column
  21626. $this->CurrCol++;
  21627. $this->SetCol($this->CurrCol);
  21628. $this->y = $this->y0;
  21629. $this->ChangeColumn = 1; // Number (and direction) of columns changed +1, +2, -2 etc.
  21630. // DIRECTIONALITY RTL
  21631. if ($this->directionality == 'rtl') {
  21632. $this->ChangeColumn = -($this->ChangeColumn);
  21633. } // *OTL*
  21634. // Stay on the page
  21635. return false;
  21636. } else {
  21637. // Go back to the first column - NEW PAGE
  21638. if (count($this->columnbuffer)) {
  21639. $this->printcolumnbuffer();
  21640. }
  21641. $this->SetCol(0);
  21642. $this->y0 = $this->tMargin;
  21643. $this->ChangeColumn = -($this->NbCol - 1);
  21644. // DIRECTIONALITY RTL
  21645. if ($this->directionality == 'rtl') {
  21646. $this->ChangeColumn = -($this->ChangeColumn);
  21647. } // *OTL*
  21648. // Page break
  21649. return true;
  21650. }
  21651. } /* -- END COLUMNS -- */
  21652. /* -- TABLES -- */ elseif ($this->table_rotate) {
  21653. if ($this->tablebuffer) {
  21654. $this->printtablebuffer();
  21655. }
  21656. return true;
  21657. } /* -- END TABLES -- */ else { // *COLUMNS*
  21658. $this->ChangeColumn = 0;
  21659. return $this->autoPageBreak;
  21660. } // *COLUMNS*
  21661. return $this->autoPageBreak;
  21662. }
  21663. // ----------- COLUMNS ---------------------
  21664. /* -- COLUMNS -- */
  21665. function SetColumns($NbCol, $vAlign = '', $gap = 5)
  21666. {
  21667. // NbCol = number of columns
  21668. // Anything less than 2 turns columns off
  21669. if ($NbCol < 2) { // SET COLUMNS OFF
  21670. if ($this->ColActive) {
  21671. $this->ColActive = 0;
  21672. if (count($this->columnbuffer)) {
  21673. $this->printcolumnbuffer();
  21674. }
  21675. $this->NbCol = 1;
  21676. $this->ResetMargins();
  21677. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  21678. $this->divwidth = 0;
  21679. $this->Ln();
  21680. }
  21681. $this->ColActive = 0;
  21682. $this->columnbuffer = [];
  21683. $this->ColDetails = [];
  21684. $this->columnLinks = [];
  21685. $this->columnAnnots = [];
  21686. $this->columnForms = [];
  21687. $this->col_BMoutlines = [];
  21688. $this->col_toc = [];
  21689. $this->breakpoints = [];
  21690. } else { // SET COLUMNS ON
  21691. if ($this->ColActive) {
  21692. $this->ColActive = 0;
  21693. if (count($this->columnbuffer)) {
  21694. $this->printcolumnbuffer();
  21695. }
  21696. $this->ResetMargins();
  21697. }
  21698. if (isset($this->y) && $this->y > $this->tMargin) {
  21699. $this->Ln();
  21700. }
  21701. $this->NbCol = $NbCol;
  21702. $this->ColGap = $gap;
  21703. $this->divwidth = 0;
  21704. $this->ColActive = 1;
  21705. $this->ColumnAdjust = true; // enables column height adjustment for the page
  21706. $this->columnbuffer = [];
  21707. $this->ColDetails = [];
  21708. $this->columnLinks = [];
  21709. $this->columnAnnots = [];
  21710. $this->columnForms = [];
  21711. $this->col_BMoutlines = [];
  21712. $this->col_toc = [];
  21713. $this->breakpoints = [];
  21714. if ((strtoupper($vAlign) == 'J') || (strtoupper($vAlign) == 'JUSTIFY')) {
  21715. $vAlign = 'J';
  21716. } else {
  21717. $vAlign = '';
  21718. }
  21719. $this->colvAlign = $vAlign;
  21720. // Save the ordinate
  21721. $absL = $this->DeflMargin - ($gap / 2);
  21722. $absR = $this->DefrMargin - ($gap / 2);
  21723. $PageWidth = $this->w - $absL - $absR; // virtual pagewidth for calculation only
  21724. $ColWidth = (($PageWidth - ($gap * ($NbCol))) / $NbCol);
  21725. $this->ColWidth = $ColWidth;
  21726. /* -- OTL -- */
  21727. if ($this->directionality == 'rtl') {
  21728. for ($i = 0; $i < $this->NbCol; $i++) {
  21729. $this->ColL[$i] = $absL + ($gap / 2) + (($NbCol - ($i + 1)) * ($PageWidth / $NbCol));
  21730. $this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos
  21731. }
  21732. } else {
  21733. /* -- END OTL -- */
  21734. for ($i = 0; $i < $this->NbCol; $i++) {
  21735. $this->ColL[$i] = $absL + ($gap / 2) + ($i * ($PageWidth / $NbCol) );
  21736. $this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos
  21737. }
  21738. } // *OTL*
  21739. $this->pgwidth = $ColWidth;
  21740. $this->SetCol(0);
  21741. $this->y0 = $this->y;
  21742. }
  21743. $this->x = $this->lMargin;
  21744. }
  21745. function SetCol($CurrCol)
  21746. {
  21747. // Used internally to set column by number: 0 is 1st column
  21748. // Set position on a column
  21749. $this->CurrCol = $CurrCol;
  21750. $x = $this->ColL[$CurrCol];
  21751. $xR = $this->ColR[$CurrCol]; // NB This is not R margin -> R pos
  21752. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  21753. $x += $this->MarginCorrection;
  21754. $xR += $this->MarginCorrection;
  21755. }
  21756. $this->SetMargins($x, ($this->w - $xR), $this->tMargin);
  21757. }
  21758. function AddColumn()
  21759. {
  21760. $this->NewColumn();
  21761. $this->ColumnAdjust = false; // disables all column height adjustment for the page.
  21762. }
  21763. function NewColumn()
  21764. {
  21765. if ($this->ColActive == 1) {
  21766. if ($this->CurrCol < $this->NbCol - 1) {
  21767. // Go to the next column
  21768. $this->CurrCol++;
  21769. $this->SetCol($this->CurrCol);
  21770. $this->y = $this->y0;
  21771. $this->ChangeColumn = 1;
  21772. // DIRECTIONALITY RTL
  21773. if ($this->directionality == 'rtl') {
  21774. $this->ChangeColumn = -($this->ChangeColumn);
  21775. } // *OTL*
  21776. // Stay on the page
  21777. } else {
  21778. // Go back to the first column
  21779. // Page break
  21780. if (count($this->columnbuffer)) {
  21781. $this->printcolumnbuffer();
  21782. }
  21783. $this->AddPage($this->CurOrientation);
  21784. $this->SetCol(0);
  21785. $this->y0 = $this->tMargin;
  21786. $this->ChangeColumn = -($this->NbCol - 1);
  21787. // DIRECTIONALITY RTL
  21788. if ($this->directionality == 'rtl') {
  21789. $this->ChangeColumn = -($this->ChangeColumn);
  21790. } // *OTL*
  21791. }
  21792. $this->x = $this->lMargin;
  21793. } else {
  21794. $this->AddPage($this->CurOrientation);
  21795. }
  21796. }
  21797. function printcolumnbuffer()
  21798. {
  21799. // Columns ended (but page not ended) -> try to match all columns - unless disabled by using a custom column-break
  21800. if (!$this->ColActive && $this->ColumnAdjust && !$this->keepColumns) {
  21801. // Calculate adjustment to add to each column to calculate rel_y value
  21802. $this->ColDetails[0]['add_y'] = 0;
  21803. $last_col = 0;
  21804. // Recursively add previous column's height
  21805. for ($i = 1; $i < $this->NbCol; $i++) {
  21806. if (isset($this->ColDetails[$i]['bottom_margin']) && $this->ColDetails[$i]['bottom_margin']) { // If any entries in the column
  21807. $this->ColDetails[$i]['add_y'] = ($this->ColDetails[$i - 1]['bottom_margin'] - $this->y0) + $this->ColDetails[$i - 1]['add_y'];
  21808. $last_col = $i; // Last column actually printed
  21809. }
  21810. }
  21811. // Calculate value for each position sensitive entry as though for one column
  21812. foreach ($this->columnbuffer as $key => $s) {
  21813. $t = $s['s'];
  21814. if ($t == 'ACROFORM') {
  21815. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  21816. $this->columnbuffer[$key]['s'] = '';
  21817. } elseif (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) {
  21818. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  21819. } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) {
  21820. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  21821. } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) {
  21822. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  21823. } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) {
  21824. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  21825. } 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)) {
  21826. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  21827. } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t)) {
  21828. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  21829. }
  21830. }
  21831. foreach ($this->internallink as $key => $f) {
  21832. if (is_array($f) && isset($f['col'])) {
  21833. $this->internallink[$key]['rel_y'] = $f['Y'] + $this->ColDetails[$f['col']]['add_y'] - $this->y0;
  21834. }
  21835. }
  21836. $breaks = [];
  21837. foreach ($this->breakpoints as $c => $bpa) {
  21838. foreach ($bpa as $rely) {
  21839. $breaks[] = $rely + $this->ColDetails[$c]['add_y'] - $this->y0;
  21840. }
  21841. }
  21842. if (isset($this->ColDetails[$last_col]['bottom_margin'])) {
  21843. $lcbm = $this->ColDetails[$last_col]['bottom_margin'];
  21844. } else {
  21845. $lcbm = 0;
  21846. }
  21847. $sum_h = $this->ColDetails[$last_col]['add_y'] + $lcbm - $this->y0;
  21848. // $sum_h = max($this->ColDetails[$last_col]['add_y'] + $this->ColDetails[$last_col]['bottom_margin'] - $this->y0, end($breaks));
  21849. $target_h = ($sum_h / $this->NbCol);
  21850. $cbr = [];
  21851. for ($i = 1; $i < $this->NbCol; $i++) {
  21852. $th = ($sum_h * $i / $this->NbCol);
  21853. foreach ($breaks as $bk => $val) {
  21854. if ($val > $th) {
  21855. if (!$bk || ($val - $th) < ($th - $breaks[$bk - 1])) {
  21856. $cbr[$i - 1] = $val;
  21857. } else {
  21858. $cbr[$i - 1] = $breaks[$bk - 1];
  21859. }
  21860. break;
  21861. }
  21862. }
  21863. }
  21864. $cbr[($this->NbCol - 1)] = $sum_h;
  21865. // mPDF 6
  21866. // Avoid outputing with 1st column empty
  21867. if (isset($cbr[0]) && $cbr[0] == 0) {
  21868. for ($i = 0; $i < $this->NbCol - 1; $i++) {
  21869. $cbr[$i] = $cbr[$i + 1];
  21870. }
  21871. }
  21872. // Now update the columns - divide into columns of approximately equal value
  21873. $last_new_col = 0;
  21874. $yadj = 0; // mm
  21875. $xadj = 0;
  21876. $last_col_bottom = 0;
  21877. $lowest_bottom_y = 0;
  21878. $block_bottom = 0;
  21879. $newcolumn = 0;
  21880. foreach ($this->columnbuffer as $key => $s) {
  21881. if (isset($s['rel_y'])) { // only process position sensitive data
  21882. if ($s['rel_y'] >= $cbr[$newcolumn]) {
  21883. $newcolumn++;
  21884. } else {
  21885. $newcolumn = $last_new_col;
  21886. }
  21887. $block_bottom = max($block_bottom, ($s['rel_y'] + $s['h']));
  21888. if ($this->directionality == 'rtl') { // *OTL*
  21889. $xadj = -(($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap)); // *OTL*
  21890. } // *OTL*
  21891. else { // *OTL*
  21892. $xadj = ($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap);
  21893. } // *OTL*
  21894. if ($last_new_col != $newcolumn) { // Added new column
  21895. $last_col_bottom = $this->columnbuffer[$key]['rel_y'];
  21896. $block_bottom = 0;
  21897. }
  21898. $yadj = ($s['rel_y'] - $s['y']) - ($last_col_bottom) + $this->y0;
  21899. // callback function
  21900. $t = $s['s'];
  21901. // mPDF 5.7+
  21902. $t = $this->columnAdjustPregReplace('Td', $xadj, $yadj, '/BT (\d+\.\d\d+) (\d+\.\d\d+) Td/', $t);
  21903. $t = $this->columnAdjustPregReplace('re', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) ([\-]{0,1}\d+\.\d\d+) re/', $t);
  21904. $t = $this->columnAdjustPregReplace('l', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) l/', $t);
  21905. $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);
  21906. $t = $this->columnAdjustPregReplace('draw', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) m/', $t);
  21907. $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);
  21908. $this->columnbuffer[$key]['s'] = $t;
  21909. $this->columnbuffer[$key]['newcol'] = $newcolumn;
  21910. $this->columnbuffer[$key]['newy'] = $s['y'] + $yadj;
  21911. $last_new_col = $newcolumn;
  21912. $clb = $s['y'] + $yadj + $s['h']; // bottom_margin of current
  21913. if ((isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb > $this->ColDetails[$newcolumn]['max_bottom']) || (!isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb)) {
  21914. $this->ColDetails[$newcolumn]['max_bottom'] = $clb;
  21915. }
  21916. if ($clb > $lowest_bottom_y) {
  21917. $lowest_bottom_y = $clb;
  21918. }
  21919. // Adjust LINKS
  21920. if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
  21921. $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
  21922. $this->PageLinks[$this->page][$ref][0] += ($xadj * Mpdf::SCALE);
  21923. $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE);
  21924. unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
  21925. }
  21926. // Adjust FORM FIELDS
  21927. if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
  21928. $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
  21929. $this->form->forms[$ref]['x'] += ($xadj);
  21930. $this->form->forms[$ref]['y'] += ($yadj);
  21931. unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
  21932. }
  21933. /* -- ANNOTATIONS -- */
  21934. if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
  21935. $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
  21936. if ($this->PageAnnots[$this->page][$ref]['x'] < 0) {
  21937. $this->PageAnnots[$this->page][$ref]['x'] -= ($xadj);
  21938. } else {
  21939. $this->PageAnnots[$this->page][$ref]['x'] += ($xadj);
  21940. }
  21941. $this->PageAnnots[$this->page][$ref]['y'] += ($yadj); // unlike PageLinks, Page annots has y values from top in mm
  21942. unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
  21943. }
  21944. /* -- END ANNOTATIONS -- */
  21945. }
  21946. }
  21947. /* -- BOOKMARKS -- */
  21948. // Adjust Bookmarks
  21949. foreach ($this->col_BMoutlines as $v) {
  21950. $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']];
  21951. }
  21952. /* -- END BOOKMARKS -- */
  21953. /* -- TOC -- */
  21954. // Adjust ToC
  21955. foreach ($this->col_toc as $v) {
  21956. $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
  21957. $this->links[$v['link']][1] = $this->y0;
  21958. }
  21959. /* -- END TOC -- */
  21960. // Adjust column length to be equal
  21961. if ($this->colvAlign == 'J') {
  21962. foreach ($this->columnbuffer as $key => $s) {
  21963. if (isset($s['rel_y'])) { // only process position sensitive data
  21964. // Set ratio to expand y values or heights
  21965. if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {
  21966. $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));
  21967. } else {
  21968. $ratio = 1;
  21969. }
  21970. if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  21971. $yadj = ($s['newy'] - $this->y0) * ($ratio - 1);
  21972. // Adjust LINKS
  21973. if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
  21974. $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
  21975. $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value
  21976. $this->PageLinks[$this->page][$ref][3] *= $ratio; // height
  21977. unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
  21978. }
  21979. // Adjust FORM FIELDS
  21980. if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
  21981. $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
  21982. $this->form->forms[$ref]['x'] += ($xadj);
  21983. $this->form->forms[$ref]['y'] += ($yadj);
  21984. unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
  21985. }
  21986. /* -- ANNOTATIONS -- */
  21987. if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
  21988. $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
  21989. $this->PageAnnots[$this->page][$ref]['y'] += ($yadj);
  21990. unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
  21991. }
  21992. /* -- END ANNOTATIONS -- */
  21993. }
  21994. }
  21995. }
  21996. foreach ($this->internallink as $key => $f) {
  21997. if (is_array($f) && isset($f['col'])) {
  21998. $last_col_bottom = 0;
  21999. for ($nbc = 0; $nbc < $this->NbCol; $nbc++) {
  22000. if ($f['rel_y'] >= $cbr[$nbc]) {
  22001. $last_col_bottom = $cbr[$nbc];
  22002. }
  22003. }
  22004. $yadj = ($f['rel_y'] - $f['Y']) - $last_col_bottom + $this->y0;
  22005. $f['Y'] += $yadj;
  22006. unset($f['col']);
  22007. unset($f['rel_y']);
  22008. $this->internallink[$key] = $f;
  22009. }
  22010. }
  22011. $last_col = -1;
  22012. $trans_on = false;
  22013. foreach ($this->columnbuffer as $key => $s) {
  22014. if (isset($s['rel_y'])) { // only process position sensitive data
  22015. // Set ratio to expand y values or heights
  22016. if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {
  22017. $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));
  22018. } else {
  22019. $ratio = 1;
  22020. }
  22021. if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  22022. // Start Transformation
  22023. $this->pages[$this->page] .= $this->StartTransform(true) . "\n";
  22024. $this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n";
  22025. $trans_on = true;
  22026. }
  22027. }
  22028. // Now output the adjusted values
  22029. $this->pages[$this->page] .= $s['s'] . "\n";
  22030. if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) { // only process position sensitive data
  22031. // Stop Transformation
  22032. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  22033. $trans_on = false;
  22034. }
  22035. }
  22036. if ($trans_on) {
  22037. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  22038. }
  22039. } else { // if NOT $this->colvAlign == 'J'
  22040. // Now output the adjusted values
  22041. foreach ($this->columnbuffer as $s) {
  22042. $this->pages[$this->page] .= $s['s'] . "\n";
  22043. }
  22044. }
  22045. if ($lowest_bottom_y > 0) {
  22046. $this->y = $lowest_bottom_y;
  22047. }
  22048. } // Columns not ended but new page -> align columns (can leave the columns alone - just tidy up the height)
  22049. elseif ($this->colvAlign == 'J' && $this->ColumnAdjust && !$this->keepColumns) {
  22050. // calculate the lowest bottom margin
  22051. $lowest_bottom_y = 0;
  22052. foreach ($this->columnbuffer as $key => $s) {
  22053. // Only process output data
  22054. $t = $s['s'];
  22055. 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)) ||
  22056. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) ||
  22057. (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) ||
  22058. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) ||
  22059. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) {
  22060. $clb = $s['y'] + $s['h'];
  22061. if ((isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom']) || !isset($this->ColDetails[$s['col']]['max_bottom'])) {
  22062. $this->ColDetails[$s['col']]['max_bottom'] = $clb;
  22063. }
  22064. if ($clb > $lowest_bottom_y) {
  22065. $lowest_bottom_y = $clb;
  22066. }
  22067. $this->columnbuffer[$key]['rel_y'] = $s['y']; // Marks position sensitive data to process later
  22068. if ($t == 'ACROFORM') {
  22069. $this->columnbuffer[$key]['s'] = '';
  22070. }
  22071. }
  22072. }
  22073. // Adjust column length equal
  22074. foreach ($this->columnbuffer as $key => $s) {
  22075. // Set ratio to expand y values or heights
  22076. if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) {
  22077. $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));
  22078. } else {
  22079. $ratio = 1;
  22080. }
  22081. if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  22082. $yadj = ($s['y'] - $this->y0) * ($ratio - 1);
  22083. // Adjust LINKS
  22084. if (isset($s['rel_y'])) { // only process position sensitive data
  22085. // otherwise triggers for all entries in column buffer (.e.g. formatting) and makes below adjustments more than once
  22086. if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
  22087. $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
  22088. $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value
  22089. $this->PageLinks[$this->page][$ref][3] *= $ratio; // height
  22090. unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
  22091. }
  22092. // Adjust FORM FIELDS
  22093. if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
  22094. $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
  22095. $this->form->forms[$ref]['x'] += ($xadj);
  22096. $this->form->forms[$ref]['y'] += ($yadj);
  22097. unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
  22098. }
  22099. /* -- ANNOTATIONS -- */
  22100. if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
  22101. $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
  22102. $this->PageAnnots[$this->page][$ref]['y'] += ($yadj);
  22103. unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
  22104. }
  22105. /* -- END ANNOTATIONS -- */
  22106. }
  22107. }
  22108. }
  22109. /* -- BOOKMARKS -- */
  22110. // Adjust Bookmarks
  22111. foreach ($this->col_BMoutlines as $v) {
  22112. $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']];
  22113. }
  22114. /* -- END BOOKMARKS -- */
  22115. /* -- TOC -- */
  22116. // Adjust ToC
  22117. foreach ($this->col_toc as $v) {
  22118. $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
  22119. $this->links[$v['link']][1] = $this->y0;
  22120. }
  22121. /* -- END TOC -- */
  22122. $trans_on = false;
  22123. foreach ($this->columnbuffer as $key => $s) {
  22124. if (isset($s['rel_y'])) { // only process position sensitive data
  22125. // Set ratio to expand y values or heights
  22126. if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) {
  22127. $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));
  22128. } else {
  22129. $ratio = 1;
  22130. }
  22131. if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  22132. // Start Transformation
  22133. $this->pages[$this->page] .= $this->StartTransform(true) . "\n";
  22134. $this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n";
  22135. $trans_on = true;
  22136. }
  22137. }
  22138. // Now output the adjusted values
  22139. $this->pages[$this->page] .= $s['s'] . "\n";
  22140. if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  22141. // Stop Transformation
  22142. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  22143. $trans_on = false;
  22144. }
  22145. }
  22146. if ($trans_on) {
  22147. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  22148. }
  22149. if ($lowest_bottom_y > 0) {
  22150. $this->y = $lowest_bottom_y;
  22151. }
  22152. } else { // Just reproduce the page as it was
  22153. // If page has not ended but height adjustment was disabled by custom column-break - adjust y
  22154. $lowest_bottom_y = 0;
  22155. if (!$this->ColActive && (!$this->ColumnAdjust || $this->keepColumns)) {
  22156. // calculate the lowest bottom margin
  22157. foreach ($this->columnbuffer as $key => $s) {
  22158. // Only process output data
  22159. $t = $s['s'];
  22160. if ($t === 'ACROFORM'
  22161. || (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t))
  22162. || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t))
  22163. || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t))
  22164. || (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t))
  22165. || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t))
  22166. || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) {
  22167. $clb = $s['y'] + $s['h'];
  22168. if (isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom'] || (!isset($this->ColDetails[$s['col']]['max_bottom']) && $clb)) {
  22169. $this->ColDetails[$s['col']]['max_bottom'] = $clb;
  22170. }
  22171. if ($clb > $lowest_bottom_y) {
  22172. $lowest_bottom_y = $clb;
  22173. }
  22174. }
  22175. }
  22176. }
  22177. foreach ($this->columnbuffer as $key => $s) {
  22178. if ($s['s'] != 'ACROFORM') {
  22179. $this->pages[$this->page] .= $s['s'] . "\n";
  22180. }
  22181. }
  22182. if ($lowest_bottom_y > 0) {
  22183. $this->y = $lowest_bottom_y;
  22184. }
  22185. /* -- BOOKMARKS -- */
  22186. // Output Bookmarks
  22187. foreach ($this->col_BMoutlines as $v) {
  22188. $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
  22189. }
  22190. /* -- END BOOKMARKS -- */
  22191. /* -- TOC -- */
  22192. // Output ToC
  22193. foreach ($this->col_toc as $v) {
  22194. $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
  22195. }
  22196. /* -- END TOC -- */
  22197. }
  22198. foreach ($this->internallink as $key => $f) {
  22199. if (isset($this->internallink[$key]['col'])) {
  22200. unset($this->internallink[$key]['col']);
  22201. }
  22202. if (isset($this->internallink[$key]['rel_y'])) {
  22203. unset($this->internallink[$key]['rel_y']);
  22204. }
  22205. }
  22206. $this->columnbuffer = [];
  22207. $this->ColDetails = [];
  22208. $this->columnLinks = [];
  22209. $this->columnAnnots = [];
  22210. $this->columnForms = [];
  22211. $this->col_BMoutlines = [];
  22212. $this->col_toc = [];
  22213. $this->breakpoints = [];
  22214. }
  22215. // mPDF 5.7+
  22216. function columnAdjustPregReplace($type, $xadj, $yadj, $pattern, $subject)
  22217. {
  22218. preg_match($pattern, $subject, $matches);
  22219. if (!count($matches)) {
  22220. return $subject;
  22221. }
  22222. if (!isset($matches[3])) {
  22223. $matches[3] = 0;
  22224. }
  22225. if (!isset($matches[4])) {
  22226. $matches[4] = 0;
  22227. }
  22228. if (!isset($matches[5])) {
  22229. $matches[5] = 0;
  22230. }
  22231. if (!isset($matches[6])) {
  22232. $matches[6] = 0;
  22233. }
  22234. 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);
  22235. }
  22236. /* -- END COLUMNS -- */
  22237. // ==================================================================
  22238. /* -- TABLES -- */
  22239. function printcellbuffer()
  22240. {
  22241. if (count($this->cellBorderBuffer)) {
  22242. sort($this->cellBorderBuffer);
  22243. foreach ($this->cellBorderBuffer as $cbb) {
  22244. $cba = unpack("A16dom/nbord/A1side/ns/dbw/a6ca/A10style/dx/dy/dw/dh/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd/dover/", $cbb);
  22245. $side = $cba['side'];
  22246. $color = str_pad($cba['ca'], 6, "\x00");
  22247. $details = [];
  22248. $details[$side]['dom'] = (float) $cba['dom'];
  22249. $details[$side]['s'] = $cba['s'];
  22250. $details[$side]['w'] = $cba['bw'];
  22251. $details[$side]['c'] = $color;
  22252. $details[$side]['style'] = trim($cba['style']);
  22253. $details['mbw']['BL'] = $cba['mbl'];
  22254. $details['mbw']['BR'] = $cba['mbr'];
  22255. $details['mbw']['RT'] = $cba['mrt'];
  22256. $details['mbw']['RB'] = $cba['mrb'];
  22257. $details['mbw']['TL'] = $cba['mtl'];
  22258. $details['mbw']['TR'] = $cba['mtr'];
  22259. $details['mbw']['LT'] = $cba['mlt'];
  22260. $details['mbw']['LB'] = $cba['mlb'];
  22261. $details['cellposdom'] = $cba['cpd'];
  22262. $details['p'] = $side;
  22263. if ($cba['over'] == 1) {
  22264. $details[$side]['overlay'] = true;
  22265. } else {
  22266. $details[$side]['overlay'] = false;
  22267. }
  22268. $this->_tableRect($cba['x'], $cba['y'], $cba['w'], $cba['h'], $cba['bord'], $details, false, false);
  22269. }
  22270. $this->cellBorderBuffer = [];
  22271. }
  22272. }
  22273. // ==================================================================
  22274. function printtablebuffer()
  22275. {
  22276. if (!$this->table_rotate) {
  22277. $this->pages[$this->page] .= $this->tablebuffer;
  22278. foreach ($this->tbrot_Links as $p => $l) {
  22279. foreach ($l as $v) {
  22280. $this->PageLinks[$p][] = $v;
  22281. }
  22282. }
  22283. $this->tbrot_Links = [];
  22284. /* -- ANNOTATIONS -- */
  22285. foreach ($this->tbrot_Annots as $p => $l) {
  22286. foreach ($l as $v) {
  22287. $this->PageAnnots[$p][] = $v;
  22288. }
  22289. }
  22290. $this->tbrot_Annots = [];
  22291. /* -- END ANNOTATIONS -- */
  22292. /* -- BOOKMARKS -- */
  22293. // Output Bookmarks
  22294. foreach ($this->tbrot_BMoutlines as $v) {
  22295. $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
  22296. }
  22297. $this->tbrot_BMoutlines = [];
  22298. /* -- END BOOKMARKS -- */
  22299. /* -- TOC -- */
  22300. // Output ToC
  22301. foreach ($this->tbrot_toc as $v) {
  22302. $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
  22303. }
  22304. $this->tbrot_toc = [];
  22305. /* -- END TOC -- */
  22306. return;
  22307. }
  22308. // elseif rotated
  22309. $lm = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left'];
  22310. $pw = $this->blk[$this->blklvl]['inner_width'];
  22311. // Start Transformation
  22312. $this->pages[$this->page] .= $this->StartTransform(true) . "\n";
  22313. if ($this->table_rotate > 1) { // clockwise
  22314. if ($this->tbrot_align == 'L') {
  22315. $xadj = $this->tbrot_h; // align L (as is)
  22316. } elseif ($this->tbrot_align == 'R') {
  22317. $xadj = $lm - $this->tbrot_x0 + ($pw); // align R
  22318. } else {
  22319. $xadj = $lm - $this->tbrot_x0 + (($pw + $this->tbrot_h) / 2); // align C
  22320. }
  22321. $yadj = 0;
  22322. } else { // anti-clockwise
  22323. if ($this->tbrot_align == 'L') {
  22324. $xadj = 0; // align L (as is)
  22325. } elseif ($this->tbrot_align == 'R') {
  22326. $xadj = $lm - $this->tbrot_x0 + ($pw - $this->tbrot_h); // align R
  22327. } else {
  22328. $xadj = $lm - $this->tbrot_x0 + (($pw - $this->tbrot_h) / 2); // align C
  22329. }
  22330. $yadj = $this->tbrot_w;
  22331. }
  22332. $this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n";
  22333. $this->pages[$this->page] .= $this->transformRotate($this->table_rotate, $this->tbrot_x0, $this->tbrot_y0, true) . "\n";
  22334. // Now output the adjusted values
  22335. $this->pages[$this->page] .= $this->tablebuffer;
  22336. foreach ($this->tbrot_Links as $p => $l) {
  22337. foreach ($l as $v) {
  22338. $w = $v[2] / Mpdf::SCALE;
  22339. $h = $v[3] / Mpdf::SCALE;
  22340. $ax = ($v[0] / Mpdf::SCALE) - $this->tbrot_x0;
  22341. $ay = (($this->hPt - $v[1]) / Mpdf::SCALE) - $this->tbrot_y0;
  22342. if ($this->table_rotate > 1) { // clockwise
  22343. $bx = $this->tbrot_x0 + $xadj - $ay - $h;
  22344. $by = $this->tbrot_y0 + $yadj + $ax;
  22345. } else {
  22346. $bx = $this->tbrot_x0 + $xadj + $ay;
  22347. $by = $this->tbrot_y0 + $yadj - $ax - $w;
  22348. }
  22349. $v[0] = $bx * Mpdf::SCALE;
  22350. $v[1] = ($this->h - $by) * Mpdf::SCALE;
  22351. $v[2] = $h * Mpdf::SCALE; // swap width and height
  22352. $v[3] = $w * Mpdf::SCALE;
  22353. $this->PageLinks[$p][] = $v;
  22354. }
  22355. }
  22356. $this->tbrot_Links = [];
  22357. foreach ($this->internallink as $key => $f) {
  22358. if (is_array($f) && isset($f['tbrot'])) {
  22359. $f['Y'] = $this->tbrot_y0;
  22360. $f['PAGE'] = $this->page;
  22361. unset($f['tbrot']);
  22362. $this->internallink[$key] = $f;
  22363. }
  22364. }
  22365. /* -- ANNOTATIONS -- */
  22366. foreach ($this->tbrot_Annots as $p => $l) {
  22367. foreach ($l as $v) {
  22368. $ax = abs($v['x']) - $this->tbrot_x0; // abs because -ve values are internally set and held for reference if annotMargin set
  22369. $ay = $v['y'] - $this->tbrot_y0;
  22370. if ($this->table_rotate > 1) { // clockwise
  22371. $bx = $this->tbrot_x0 + $xadj - $ay;
  22372. $by = $this->tbrot_y0 + $yadj + $ax;
  22373. } else {
  22374. $bx = $this->tbrot_x0 + $xadj + $ay;
  22375. $by = $this->tbrot_y0 + $yadj - $ax;
  22376. }
  22377. if ($v['x'] < 0) {
  22378. $v['x'] = -$bx;
  22379. } else {
  22380. $v['x'] = $bx;
  22381. }
  22382. $v['y'] = ($by);
  22383. $this->PageAnnots[$p][] = $v;
  22384. }
  22385. }
  22386. $this->tbrot_Annots = [];
  22387. /* -- END ANNOTATIONS -- */
  22388. /* -- BOOKMARKS -- */
  22389. // Adjust Bookmarks
  22390. foreach ($this->tbrot_BMoutlines as $v) {
  22391. $v['y'] = $this->tbrot_y0;
  22392. $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page];
  22393. }
  22394. /* -- END BOOKMARKS -- */
  22395. /* -- TOC -- */
  22396. // Adjust ToC - uses document page number
  22397. foreach ($this->tbrot_toc as $v) {
  22398. $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']];
  22399. $this->links[$v['link']][1] = $this->tbrot_y0;
  22400. }
  22401. /* -- END TOC -- */
  22402. $this->tbrot_BMoutlines = [];
  22403. $this->tbrot_toc = [];
  22404. // Stop Transformation
  22405. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  22406. $this->y = $this->tbrot_y0 + $this->tbrot_w;
  22407. $this->x = $this->lMargin;
  22408. $this->tablebuffer = '';
  22409. }
  22410. /**
  22411. * Keep-with-table This buffers contents of h1-6 to keep on page with table
  22412. */
  22413. function printkwtbuffer()
  22414. {
  22415. if (!$this->kwt_moved) {
  22416. foreach ($this->kwt_buffer as $s) {
  22417. $this->pages[$this->page] .= $s['s'] . "\n";
  22418. }
  22419. foreach ($this->kwt_Links as $p => $l) {
  22420. foreach ($l as $v) {
  22421. $this->PageLinks[$p][] = $v;
  22422. }
  22423. }
  22424. $this->kwt_Links = [];
  22425. /* -- ANNOTATIONS -- */
  22426. foreach ($this->kwt_Annots as $p => $l) {
  22427. foreach ($l as $v) {
  22428. $this->PageAnnots[$p][] = $v;
  22429. }
  22430. }
  22431. $this->kwt_Annots = [];
  22432. /* -- END ANNOTATIONS -- */
  22433. /* -- INDEX -- */
  22434. // Output Reference (index)
  22435. foreach ($this->kwt_Reference as $v) {
  22436. $Present = 0;
  22437. for ($i = 0; $i < count($this->Reference); $i++) {
  22438. if ($this->Reference[$i]['t'] == $v['t']) {
  22439. $Present = 1;
  22440. if (!in_array($v['op'], $this->Reference[$i]['p'])) {
  22441. $this->Reference[$i]['p'][] = $v['op'];
  22442. }
  22443. }
  22444. }
  22445. if ($Present == 0) {
  22446. $this->Reference[] = ['t' => $v['t'], 'p' => [$v['op']]];
  22447. }
  22448. }
  22449. $this->kwt_Reference = [];
  22450. /* -- END INDEX -- */
  22451. /* -- BOOKMARKS -- */
  22452. // Output Bookmarks
  22453. foreach ($this->kwt_BMoutlines as $v) {
  22454. $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
  22455. }
  22456. $this->kwt_BMoutlines = [];
  22457. /* -- END BOOKMARKS -- */
  22458. /* -- TOC -- */
  22459. // Output ToC
  22460. foreach ($this->kwt_toc as $v) {
  22461. $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
  22462. }
  22463. $this->kwt_toc = [];
  22464. /* -- END TOC -- */
  22465. $this->pageoutput[$this->page] = []; // mPDF 6
  22466. return;
  22467. }
  22468. // Start Transformation
  22469. $this->pages[$this->page] .= $this->StartTransform(true) . "\n";
  22470. $xadj = $this->lMargin - $this->kwt_x0;
  22471. // $yadj = $this->y - $this->kwt_y0 ;
  22472. $yadj = $this->tMargin - $this->kwt_y0;
  22473. $this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n";
  22474. // Now output the adjusted values
  22475. foreach ($this->kwt_buffer as $s) {
  22476. $this->pages[$this->page] .= $s['s'] . "\n";
  22477. }
  22478. // Adjust hyperLinks
  22479. foreach ($this->kwt_Links as $p => $l) {
  22480. foreach ($l as $v) {
  22481. $bx = $this->kwt_x0 + $xadj;
  22482. $by = $this->kwt_y0 + $yadj;
  22483. $v[0] = $bx * Mpdf::SCALE;
  22484. $v[1] = ($this->h - $by) * Mpdf::SCALE;
  22485. $this->PageLinks[$p][] = $v;
  22486. }
  22487. }
  22488. foreach ($this->internallink as $key => $f) {
  22489. if (is_array($f) && isset($f['kwt'])) {
  22490. $f['Y'] += $yadj;
  22491. $f['PAGE'] = $this->page;
  22492. unset($f['kwt']);
  22493. $this->internallink[$key] = $f;
  22494. }
  22495. }
  22496. /* -- ANNOTATIONS -- */
  22497. foreach ($this->kwt_Annots as $p => $l) {
  22498. foreach ($l as $v) {
  22499. $bx = $this->kwt_x0 + $xadj;
  22500. $by = $this->kwt_y0 + $yadj;
  22501. if ($v['x'] < 0) {
  22502. $v['x'] = -$bx;
  22503. } else {
  22504. $v['x'] = $bx;
  22505. }
  22506. $v['y'] = $by;
  22507. $this->PageAnnots[$p][] = $v;
  22508. }
  22509. }
  22510. /* -- END ANNOTATIONS -- */
  22511. /* -- BOOKMARKS -- */
  22512. // Adjust Bookmarks
  22513. foreach ($this->kwt_BMoutlines as $v) {
  22514. if ($v['y'] != 0) {
  22515. $v['y'] += $yadj;
  22516. }
  22517. $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page];
  22518. }
  22519. /* -- END BOOKMARKS -- */
  22520. /* -- INDEX -- */
  22521. // Adjust Reference (index)
  22522. foreach ($this->kwt_Reference as $v) {
  22523. $Present = 0;
  22524. // Search the reference (AND Ref/PageNo) in the array
  22525. for ($i = 0; $i < count($this->Reference); $i++) {
  22526. if ($this->Reference[$i]['t'] == $v['t']) {
  22527. $Present = 1;
  22528. if (!in_array($this->page, $this->Reference[$i]['p'])) {
  22529. $this->Reference[$i]['p'][] = $this->page;
  22530. }
  22531. }
  22532. }
  22533. if ($Present == 0) {
  22534. $this->Reference[] = ['t' => $v['t'], 'p' => [$this->page]];
  22535. }
  22536. }
  22537. /* -- END INDEX -- */
  22538. /* -- TOC -- */
  22539. // Adjust ToC
  22540. foreach ($this->kwt_toc as $v) {
  22541. $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']];
  22542. $this->links[$v['link']][0] = $this->page;
  22543. $this->links[$v['link']][1] += $yadj;
  22544. }
  22545. /* -- END TOC -- */
  22546. $this->kwt_Links = [];
  22547. $this->kwt_Annots = [];
  22548. $this->kwt_Reference = [];
  22549. $this->kwt_BMoutlines = [];
  22550. $this->kwt_toc = [];
  22551. // Stop Transformation
  22552. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  22553. $this->kwt_buffer = [];
  22554. $this->y += $this->kwt_height;
  22555. $this->pageoutput[$this->page] = []; // mPDF 6
  22556. }
  22557. /* -- END TABLES -- */
  22558. function printfloatbuffer()
  22559. {
  22560. if (count($this->floatbuffer)) {
  22561. $this->objectbuffer = $this->floatbuffer;
  22562. $this->printobjectbuffer(false);
  22563. $this->objectbuffer = [];
  22564. $this->floatbuffer = [];
  22565. $this->floatmargins = [];
  22566. }
  22567. }
  22568. function Circle($x, $y, $r, $style = 'S')
  22569. {
  22570. $this->Ellipse($x, $y, $r, $r, $style);
  22571. }
  22572. function Ellipse($x, $y, $rx, $ry, $style = 'S')
  22573. {
  22574. if ($style === 'F') {
  22575. $op = 'f';
  22576. } elseif ($style === 'FD' or $style === 'DF') {
  22577. $op = 'B';
  22578. } else {
  22579. $op = 'S';
  22580. }
  22581. $lx = 4 / 3 * (M_SQRT2 - 1) * $rx;
  22582. $ly = 4 / 3 * (M_SQRT2 - 1) * $ry;
  22583. $h = $this->h;
  22584. $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));
  22585. $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));
  22586. $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));
  22587. $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));
  22588. }
  22589. /* -- DIRECTW -- */
  22590. function AutosizeText($text, $w, $font, $style, $szfont = 72)
  22591. {
  22592. $text = ' ' . $text . ' ';
  22593. $this->SetFont($font, $style, $szfont, false);
  22594. $text = $this->purify_utf8_text($text);
  22595. if ($this->text_input_as_HTML) {
  22596. $text = $this->all_entities_to_utf8($text);
  22597. }
  22598. if ($this->usingCoreFont) {
  22599. $text = mb_convert_encoding($text, $this->mb_enc, 'UTF-8');
  22600. }
  22601. // DIRECTIONALITY
  22602. if (preg_match("/([" . $this->pregRTLchars . "])/u", $text)) {
  22603. $this->biDirectional = true;
  22604. }
  22605. $textvar = 0;
  22606. $save_OTLtags = $this->OTLtags;
  22607. $this->OTLtags = [];
  22608. if ($this->useKerning) {
  22609. if ($this->CurrentFont['haskernGPOS']) {
  22610. $this->OTLtags['Plus'] .= ' kern';
  22611. } else {
  22612. $textvar = ($textvar | TextVars::FC_KERNING);
  22613. }
  22614. }
  22615. /* -- OTL -- */
  22616. // Use OTL OpenType Table Layout - GSUB & GPOS
  22617. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  22618. $text = $this->otl->applyOTL($text, $this->CurrentFont['useOTL']);
  22619. $OTLdata = $this->otl->OTLdata;
  22620. }
  22621. /* -- END OTL -- */
  22622. $this->OTLtags = $save_OTLtags;
  22623. $this->magic_reverse_dir($text, $this->directionality, $OTLdata);
  22624. $width = $this->sizeConverter->convert($w);
  22625. $loop = 0;
  22626. while ($loop == 0) {
  22627. $this->SetFont($font, $style, $szfont, false);
  22628. $sz = $this->GetStringWidth($text, true, $OTLdata, $textvar);
  22629. if ($sz > $w) {
  22630. $szfont --;
  22631. } else {
  22632. $loop ++;
  22633. }
  22634. }
  22635. $this->SetFont($font, $style, $szfont, true, true);
  22636. $this->Cell($w, 0, $text, 0, 0, "C", 0, '', 0, 0, 0, 'M', 0, false, $OTLdata, $textvar);
  22637. }
  22638. /* -- END DIRECTW -- */
  22639. // ====================================================
  22640. // ====================================================
  22641. function magic_reverse_dir(&$chunk, $dir, &$chunkOTLdata)
  22642. {
  22643. /* -- OTL -- */
  22644. if ($this->usingCoreFont) {
  22645. return 0;
  22646. }
  22647. if ($chunk == '') {
  22648. return 0;
  22649. }
  22650. if ($this->biDirectional || $dir == 'rtl') {
  22651. // check if string contains RTL text
  22652. // including any added from OTL tables (in PUA)
  22653. $pregRTLchars = $this->pregRTLchars;
  22654. if (isset($this->CurrentFont['rtlPUAstr']) && $this->CurrentFont['rtlPUAstr']) {
  22655. $pregRTLchars .= $this->CurrentFont['rtlPUAstr'];
  22656. }
  22657. if (!preg_match("/[" . $pregRTLchars . "]/u", $chunk) && $dir != 'rtl') {
  22658. return 0;
  22659. } // Chunk doesn't contain RTL characters
  22660. $unicode = $this->UTF8StringToArray($chunk, false);
  22661. $isStrong = false;
  22662. if (empty($chunkOTLdata)) {
  22663. $this->getBasicOTLdata($chunkOTLdata, $unicode, $isStrong);
  22664. }
  22665. $useGPOS = isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0x80);
  22666. // NB Returned $chunk may be a shorter string (with adjusted $cOTLdata) by removal of LRE, RLE etc embedding codes.
  22667. list($chunk, $rtl_content) = $this->otl->bidiSort($unicode, $chunk, $dir, $chunkOTLdata, $useGPOS);
  22668. return $rtl_content;
  22669. }
  22670. /* -- END OTL -- */
  22671. return 0;
  22672. }
  22673. /* -- OTL -- */
  22674. function getBasicOTLdata(&$chunkOTLdata, $unicode, &$is_strong)
  22675. {
  22676. if (empty($this->otl)) {
  22677. $this->otl = new Otl($this, $this->fontCache);
  22678. }
  22679. $chunkOTLdata['group'] = '';
  22680. $chunkOTLdata['GPOSinfo'] = [];
  22681. $chunkOTLdata['char_data'] = [];
  22682. foreach ($unicode as $char) {
  22683. $ucd_record = Ucdn::get_ucd_record($char);
  22684. $chunkOTLdata['char_data'][] = ['bidi_class' => $ucd_record[2], 'uni' => $char];
  22685. if ($ucd_record[2] == 0 || $ucd_record[2] == 3 || $ucd_record[2] == 4) {
  22686. $is_strong = true;
  22687. } // contains strong character
  22688. if ($ucd_record[0] == Ucdn::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
  22689. $chunkOTLdata['group'] .= 'M';
  22690. } elseif ($char == 32 || $char == 12288) {
  22691. $chunkOTLdata['group'] .= 'S';
  22692. } else {
  22693. $chunkOTLdata['group'] .= 'C';
  22694. }
  22695. }
  22696. }
  22697. function _setBidiCodes($mode = 'start', $bdf = '')
  22698. {
  22699. $s = '';
  22700. if ($mode == 'end') {
  22701. // PDF comes before PDI to close isolate-override (e.g. "LRILROPDFPDI")
  22702. if (strpos($bdf, 'PDF') !== false) {
  22703. $s .= UtfString::code2utf(0x202C);
  22704. } // POP DIRECTIONAL FORMATTING
  22705. if (strpos($bdf, 'PDI') !== false) {
  22706. $s .= UtfString::code2utf(0x2069);
  22707. } // POP DIRECTIONAL ISOLATE
  22708. } elseif ($mode == 'start') {
  22709. // LRI comes before LRO to open isolate-override (e.g. "LRILROPDFPDI")
  22710. if (strpos($bdf, 'LRI') !== false) { // U+2066 LRI
  22711. $s .= UtfString::code2utf(0x2066);
  22712. } elseif (strpos($bdf, 'RLI') !== false) { // U+2067 RLI
  22713. $s .= UtfString::code2utf(0x2067);
  22714. } elseif (strpos($bdf, 'FSI') !== false) { // U+2068 FSI
  22715. $s .= UtfString::code2utf(0x2068);
  22716. }
  22717. if (strpos($bdf, 'LRO') !== false) { // U+202D LRO
  22718. $s .= UtfString::code2utf(0x202D);
  22719. } elseif (strpos($bdf, 'RLO') !== false) { // U+202E RLO
  22720. $s .= UtfString::code2utf(0x202E);
  22721. } elseif (strpos($bdf, 'LRE') !== false) { // U+202A LRE
  22722. $s .= UtfString::code2utf(0x202A);
  22723. } elseif (strpos($bdf, 'RLE') !== false) { // U+202B RLE
  22724. $s .= UtfString::code2utf(0x202B);
  22725. }
  22726. }
  22727. return $s;
  22728. }
  22729. /* -- END OTL -- */
  22730. function SetSubstitutions()
  22731. {
  22732. $subsarray = [];
  22733. require __DIR__ . '/../data/subs_win-1252.php';
  22734. $this->substitute = [];
  22735. foreach ($subsarray as $key => $val) {
  22736. $this->substitute[UtfString::code2utf($key)] = $val;
  22737. }
  22738. }
  22739. function SubstituteChars($html)
  22740. {
  22741. // only substitute characters between tags
  22742. if (count($this->substitute)) {
  22743. $a = preg_split('/(<.*?>)/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
  22744. $html = '';
  22745. foreach ($a as $i => $e) {
  22746. if ($i % 2 == 0) {
  22747. $e = strtr($e, $this->substitute);
  22748. }
  22749. $html .= $e;
  22750. }
  22751. }
  22752. return $html;
  22753. }
  22754. function SubstituteCharsSIP(&$writehtml_a, &$writehtml_i, &$writehtml_e)
  22755. {
  22756. if (preg_match("/^(.*?)([\x{20000}-\x{2FFFF}]+)(.*)/u", $writehtml_e, $m)) {
  22757. if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {
  22758. $font = $this->CurrentFont['sipext'];
  22759. if (!in_array($font, $this->available_unifonts)) {
  22760. return 0;
  22761. }
  22762. $writehtml_a[$writehtml_i] = $writehtml_e = $m[1];
  22763. array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
  22764. $this->subPos = $writehtml_i;
  22765. return 4;
  22766. }
  22767. }
  22768. return 0;
  22769. }
  22770. /**
  22771. * If core font is selected in document which is not onlyCoreFonts - substitute with non-core font
  22772. */
  22773. function SubstituteCharsNonCore(&$writehtml_a, &$writehtml_i, &$writehtml_e)
  22774. {
  22775. // Ignore if in Textarea
  22776. if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {
  22777. return 0;
  22778. }
  22779. if (mb_convert_encoding(mb_convert_encoding($writehtml_e, $this->mb_enc, "UTF-8"), "UTF-8", $this->mb_enc) == $writehtml_e) {
  22780. return 0;
  22781. }
  22782. $cw = &$this->CurrentFont['cw'];
  22783. $unicode = $this->UTF8StringToArray($writehtml_e, false);
  22784. $start = -1;
  22785. $end = 0;
  22786. $flag = 0;
  22787. $ftype = '';
  22788. $u = [];
  22789. if (!$this->subArrMB) {
  22790. require __DIR__ . '/../data/subs_core.php';
  22791. $this->subArrMB['a'] = $aarr;
  22792. $this->subArrMB['s'] = $sarr;
  22793. $this->subArrMB['z'] = $zarr;
  22794. }
  22795. foreach ($unicode as $c => $char) {
  22796. 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)) {
  22797. if ($flag == 0) {
  22798. $start = $c;
  22799. }
  22800. $flag = 1;
  22801. $u[] = $char;
  22802. } elseif ($flag > 0) {
  22803. $end = $c - 1;
  22804. break;
  22805. }
  22806. }
  22807. if ($flag > 0 && !$end) {
  22808. $end = count($unicode) - 1;
  22809. }
  22810. if ($start == -1) {
  22811. return 0;
  22812. }
  22813. // Try in backup subs font
  22814. if (!is_array($this->backupSubsFont)) {
  22815. $this->backupSubsFont = ["$this->backupSubsFont"];
  22816. }
  22817. foreach ($this->backupSubsFont as $bsfctr => $bsf) {
  22818. if ($this->fonttrans[$bsf] == 'chelvetica' || $this->fonttrans[$bsf] == 'ctimes' || $this->fonttrans[$bsf] == 'ccourier') {
  22819. continue;
  22820. }
  22821. $font = $bsf;
  22822. unset($cw);
  22823. $cw = '';
  22824. if (isset($this->fonts[$font])) {
  22825. $cw = &$this->fonts[$font]['cw'];
  22826. } elseif ($this->fontCache->has($font . '.cw.dat')) {
  22827. $cw = $this->fontCache->load($font . '.cw.dat');
  22828. } else {
  22829. $prevFontFamily = $this->FontFamily;
  22830. $prevFontStyle = $this->currentfontstyle;
  22831. $prevFontSizePt = $this->FontSizePt;
  22832. $this->SetFont($bsf, '', '', false);
  22833. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
  22834. }
  22835. if (!$cw) {
  22836. continue;
  22837. }
  22838. $l = 0;
  22839. foreach ($u as $char) {
  22840. if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) {
  22841. $l++;
  22842. } else {
  22843. if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font
  22844. $cont = mb_substr($writehtml_e, $start + 1);
  22845. $writehtml_e = mb_substr($writehtml_e, 0, $start + 1, 'UTF-8');
  22846. array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]);
  22847. $this->subPos = $writehtml_i + 1;
  22848. return 2;
  22849. } else {
  22850. break;
  22851. }
  22852. }
  22853. }
  22854. if ($l > 0) {
  22855. $patt = mb_substr($writehtml_e, $start, $l, 'UTF-8');
  22856. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  22857. $writehtml_e = $m[1];
  22858. array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
  22859. $this->subPos = $writehtml_i + 3;
  22860. return 4;
  22861. }
  22862. }
  22863. }
  22864. unset($cw);
  22865. return 0;
  22866. }
  22867. function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e)
  22868. {
  22869. // Ignore if in Textarea
  22870. if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {
  22871. return 0;
  22872. }
  22873. $cw = &$this->CurrentFont['cw'];
  22874. $unicode = $this->UTF8StringToArray($writehtml_e, false);
  22875. $start = -1;
  22876. $end = 0;
  22877. $flag = 0;
  22878. $ftype = '';
  22879. $u = [];
  22880. foreach ($unicode as $c => $char) {
  22881. if (($flag == 0 || $flag == 2) && (!$this->_charDefined($cw, $char) || ($flag == 2 && $char == 32)) && $this->checkSIP && $char > 131071) { // Unicode Plane 2 (SIP)
  22882. if (in_array($this->FontFamily, $this->available_CJK_fonts)) {
  22883. return 0;
  22884. }
  22885. if ($flag == 0) {
  22886. $start = $c;
  22887. }
  22888. $flag = 2;
  22889. $u[] = $char;
  22890. // elseif (($flag == 0 || $flag==1) && $char != 173 && !$this->_charDefined($cw,$char) && ($char<1423 || ($char>3583 && $char < 11263))) {
  22891. } elseif (($flag == 0 || $flag == 1) && $char != 173 && (!$this->_charDefined($cw, $char) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) {
  22892. if ($flag == 0) {
  22893. $start = $c;
  22894. }
  22895. $flag = 1;
  22896. $u[] = $char;
  22897. } elseif ($flag > 0) {
  22898. $end = $c - 1;
  22899. break;
  22900. }
  22901. }
  22902. if ($flag > 0 && !$end) {
  22903. $end = count($unicode) - 1;
  22904. }
  22905. if ($start == -1) {
  22906. return 0;
  22907. }
  22908. if ($flag == 2) { // SIP
  22909. // Check if current CJK font has a ext-B related font
  22910. if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {
  22911. $font = $this->CurrentFont['sipext'];
  22912. unset($cw);
  22913. $cw = '';
  22914. if (isset($this->fonts[$font])) {
  22915. $cw = &$this->fonts[$font]['cw'];
  22916. } elseif ($this->fontCache->has($font . '.cw.dat')) {
  22917. $cw = $this->fontCache->load($font . '.cw.dat');
  22918. } else {
  22919. $prevFontFamily = $this->FontFamily;
  22920. $prevFontStyle = $this->currentfontstyle;
  22921. $prevFontSizePt = $this->FontSizePt;
  22922. $this->SetFont($font, '', '', false);
  22923. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
  22924. }
  22925. if (!$cw) {
  22926. return 0;
  22927. }
  22928. $l = 0;
  22929. foreach ($u as $char) {
  22930. if ($this->_charDefined($cw, $char) || $char > 131071) {
  22931. $l++;
  22932. } else {
  22933. break;
  22934. }
  22935. }
  22936. if ($l > 0) {
  22937. $patt = mb_substr($writehtml_e, $start, $l);
  22938. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  22939. $writehtml_e = $m[1];
  22940. array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
  22941. $this->subPos = $writehtml_i + 3;
  22942. return 4;
  22943. }
  22944. }
  22945. }
  22946. // Check Backup SIP font (defined in Config\FontVariables)
  22947. if (isset($this->backupSIPFont) && $this->backupSIPFont) {
  22948. if ($this->currentfontfamily != $this->backupSIPFont) {
  22949. $font = $this->backupSIPFont;
  22950. } else {
  22951. unset($cw);
  22952. return 0;
  22953. }
  22954. unset($cw);
  22955. $cw = '';
  22956. if (isset($this->fonts[$font])) {
  22957. $cw = &$this->fonts[$font]['cw'];
  22958. } elseif ($this->fontCache->has($font . '.cw.dat')) {
  22959. $cw = $this->fontCache->load($font . '.cw.dat');
  22960. } else {
  22961. $prevFontFamily = $this->FontFamily;
  22962. $prevFontStyle = $this->currentfontstyle;
  22963. $prevFontSizePt = $this->FontSizePt;
  22964. $this->SetFont($this->backupSIPFont, '', '', false);
  22965. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
  22966. }
  22967. if (!$cw) {
  22968. return 0;
  22969. }
  22970. $l = 0;
  22971. foreach ($u as $char) {
  22972. if ($this->_charDefined($cw, $char) || $char > 131071) {
  22973. $l++;
  22974. } else {
  22975. break;
  22976. }
  22977. }
  22978. if ($l > 0) {
  22979. $patt = mb_substr($writehtml_e, $start, $l);
  22980. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  22981. $writehtml_e = $m[1];
  22982. array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
  22983. $this->subPos = $writehtml_i + 3;
  22984. return 4;
  22985. }
  22986. }
  22987. }
  22988. return 0;
  22989. }
  22990. // FIRST TRY CORE FONTS (when appropriate)
  22991. if (!$this->PDFA && !$this->PDFX && !$this->biDirectional) { // mPDF 6
  22992. $repl = [];
  22993. if (!$this->subArrMB) {
  22994. require __DIR__ . '/../data/subs_core.php';
  22995. $this->subArrMB['a'] = $aarr;
  22996. $this->subArrMB['s'] = $sarr;
  22997. $this->subArrMB['z'] = $zarr;
  22998. }
  22999. if (isset($this->subArrMB['a'][$u[0]])) {
  23000. $font = 'tta';
  23001. $ftype = 'C';
  23002. foreach ($u as $char) {
  23003. if (isset($this->subArrMB['a'][$char])) {
  23004. $repl[] = $this->subArrMB['a'][$char];
  23005. } else {
  23006. break;
  23007. }
  23008. }
  23009. } elseif (isset($this->subArrMB['z'][$u[0]])) {
  23010. $font = 'ttz';
  23011. $ftype = 'C';
  23012. foreach ($u as $char) {
  23013. if (isset($this->subArrMB['z'][$char])) {
  23014. $repl[] = $this->subArrMB['z'][$char];
  23015. } else {
  23016. break;
  23017. }
  23018. }
  23019. } elseif (isset($this->subArrMB['s'][$u[0]])) {
  23020. $font = 'tts';
  23021. $ftype = 'C';
  23022. foreach ($u as $char) {
  23023. if (isset($this->subArrMB['s'][$char])) {
  23024. $repl[] = $this->subArrMB['s'][$char];
  23025. } else {
  23026. break;
  23027. }
  23028. }
  23029. }
  23030. if ($ftype == 'C') {
  23031. $patt = mb_substr($writehtml_e, $start, count($repl));
  23032. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  23033. $writehtml_e = $m[1];
  23034. array_splice($writehtml_a, $writehtml_i + 1, 0, [$font, implode('|', $repl), '/' . $font, $m[3]]); // e.g. <tts>
  23035. $this->subPos = $writehtml_i + 3;
  23036. return 4;
  23037. }
  23038. return 0;
  23039. }
  23040. }
  23041. // LASTLY TRY IN BACKUP SUBS FONT
  23042. if (!is_array($this->backupSubsFont)) {
  23043. $this->backupSubsFont = ["$this->backupSubsFont"];
  23044. }
  23045. foreach ($this->backupSubsFont as $bsfctr => $bsf) {
  23046. if ($this->currentfontfamily != $bsf) {
  23047. $font = $bsf;
  23048. } else {
  23049. continue;
  23050. }
  23051. unset($cw);
  23052. $cw = '';
  23053. if (isset($this->fonts[$font])) {
  23054. $cw = &$this->fonts[$font]['cw'];
  23055. } elseif ($this->fontCache->has($font . '.cw.dat')) {
  23056. $cw = $this->fontCache->load($font . '.cw.dat');
  23057. } else {
  23058. $prevFontFamily = $this->FontFamily;
  23059. $prevFontStyle = $this->currentfontstyle;
  23060. $prevFontSizePt = $this->FontSizePt;
  23061. $this->SetFont($bsf, '', '', false);
  23062. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
  23063. if ($this->fontCache->has($font . '.cw.dat')) {
  23064. $cw = $this->fontCache->load($font . '.cw.dat');
  23065. }
  23066. }
  23067. if (!$cw) {
  23068. continue;
  23069. }
  23070. $l = 0;
  23071. foreach ($u as $char) {
  23072. if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) { // Arabic and Indic
  23073. $l++;
  23074. } else {
  23075. if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font
  23076. $cont = mb_substr($writehtml_e, $start + 1);
  23077. $writehtml_e = mb_substr($writehtml_e, 0, $start + 1);
  23078. array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]);
  23079. $this->subPos = $writehtml_i + 1;
  23080. return 2;
  23081. } else {
  23082. break;
  23083. }
  23084. }
  23085. }
  23086. if ($l > 0) {
  23087. $patt = mb_substr($writehtml_e, $start, $l);
  23088. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  23089. $writehtml_e = $m[1];
  23090. array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
  23091. $this->subPos = $writehtml_i + 3;
  23092. return 4;
  23093. }
  23094. }
  23095. }
  23096. unset($cw);
  23097. return 0;
  23098. }
  23099. function setHiEntitySubstitutions()
  23100. {
  23101. $entarr = include __DIR__ . '/../data/entity_substitutions.php';
  23102. foreach ($entarr as $key => $val) {
  23103. $this->entsearch[] = '&' . $key . ';';
  23104. $this->entsubstitute[] = UtfString::code2utf($val);
  23105. }
  23106. }
  23107. function SubstituteHiEntities($html)
  23108. {
  23109. // converts html_entities > ASCII 127 to unicode
  23110. // Leaves in particular &lt; to distinguish from tag marker
  23111. if (count($this->entsearch)) {
  23112. $html = str_replace($this->entsearch, $this->entsubstitute, $html);
  23113. }
  23114. return $html;
  23115. }
  23116. /**
  23117. * Edited v1.2 Pass by reference; option to continue if invalid UTF-8 chars
  23118. */
  23119. function is_utf8(&$string)
  23120. {
  23121. if ($string === mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32")) {
  23122. return true;
  23123. }
  23124. if ($this->ignore_invalid_utf8) {
  23125. $string = mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32");
  23126. return true;
  23127. }
  23128. return false;
  23129. }
  23130. /**
  23131. * For HTML
  23132. *
  23133. * Checks string is valid UTF-8 encoded
  23134. * converts html_entities > ASCII 127 to UTF-8
  23135. * Only exception - leaves low ASCII entities e.g. &lt; &amp; etc.
  23136. * Leaves in particular &lt; to distinguish from tag marker
  23137. */
  23138. function purify_utf8($html, $lo = true)
  23139. {
  23140. if (!$this->is_utf8($html)) {
  23141. while (mb_convert_encoding(mb_convert_encoding($html, "UTF-32", "UTF-8"), "UTF-8", "UTF-32") !== $html) {
  23142. $a = @iconv('UTF-8', 'UTF-8', $html);
  23143. $error = error_get_last();
  23144. if ($error && $error['message'] === 'iconv(): Detected an illegal character in input string') {
  23145. throw new \Mpdf\MpdfException('Invalid input characters. Did you set $mpdf->in_charset properly?');
  23146. }
  23147. $pos = $start = strlen($a);
  23148. $err = '';
  23149. while (ord(substr($html, $pos, 1)) > 128) {
  23150. $err .= '[[#' . ord(substr($html, $pos, 1)) . ']]';
  23151. $pos++;
  23152. }
  23153. $this->logger->error($err, ['context' => LogContext::UTF8]);
  23154. $html = substr($html, $pos);
  23155. }
  23156. throw new \Mpdf\MpdfException("HTML contains invalid UTF-8 character(s). See log for further details");
  23157. }
  23158. $html = preg_replace("/\r/", "", $html);
  23159. // converts html_entities > ASCII 127 to UTF-8
  23160. // Leaves in particular &lt; to distinguish from tag marker
  23161. $html = $this->SubstituteHiEntities($html);
  23162. // converts all &#nnn; or &#xHHH; to UTF-8 multibyte
  23163. // If $lo==true then includes ASCII < 128
  23164. $html = UtfString::strcode2utf($html, $lo);
  23165. return $html;
  23166. }
  23167. /**
  23168. * For TEXT
  23169. */
  23170. function purify_utf8_text($txt)
  23171. {
  23172. // Make sure UTF-8 string of characters
  23173. if (!$this->is_utf8($txt)) {
  23174. throw new \Mpdf\MpdfException("Text contains invalid UTF-8 character(s)");
  23175. }
  23176. $txt = preg_replace("/\r/", "", $txt);
  23177. return ($txt);
  23178. }
  23179. function all_entities_to_utf8($txt)
  23180. {
  23181. // converts txt_entities > ASCII 127 to UTF-8
  23182. // Leaves in particular &lt; to distinguish from tag marker
  23183. $txt = $this->SubstituteHiEntities($txt);
  23184. // converts all &#nnn; or &#xHHH; to UTF-8 multibyte
  23185. $txt = UtfString::strcode2utf($txt);
  23186. $txt = $this->lesser_entity_decode($txt);
  23187. return ($txt);
  23188. }
  23189. /* -- BARCODES -- */
  23190. /**
  23191. * UPC/EAN barcode
  23192. *
  23193. * EAN13, EAN8, UPCA, UPCE, ISBN, ISSN
  23194. * Accepts 12 or 13 digits with or without - hyphens
  23195. */
  23196. 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)
  23197. {
  23198. if (empty($code)) {
  23199. return;
  23200. }
  23201. $codestr = $code;
  23202. $code = preg_replace('/\-/', '', $code);
  23203. $this->barcode = new Barcode();
  23204. if ($btype == 'ISSN' || $btype == 'ISBN') {
  23205. $arrcode = $this->barcode->getBarcodeArray($code, 'EAN13');
  23206. } else {
  23207. $arrcode = $this->barcode->getBarcodeArray($code, $btype);
  23208. }
  23209. if ($arrcode === false) {
  23210. throw new \Mpdf\MpdfException('Error in barcode string: ' . $codestr);
  23211. }
  23212. if ((($btype === 'EAN13' || $btype === 'ISBN' || $btype === 'ISSN') && strlen($code) === 12)
  23213. || ($btype == 'UPCA' && strlen($code) === 11)
  23214. || ($btype == 'UPCE' && strlen($code) === 11)
  23215. || ($btype == 'EAN8' && strlen($code) === 7)) {
  23216. $code .= $arrcode['checkdigit'];
  23217. if (stristr($codestr, '-')) {
  23218. $codestr .= '-' . $arrcode['checkdigit'];
  23219. } else {
  23220. $codestr .= $arrcode['checkdigit'];
  23221. }
  23222. }
  23223. if ($btype === 'ISBN') {
  23224. $codestr = 'ISBN ' . $codestr;
  23225. }
  23226. if ($btype === 'ISSN') {
  23227. $codestr = 'ISSN ' . $codestr;
  23228. }
  23229. if (empty($x)) {
  23230. $x = $this->x;
  23231. }
  23232. if (empty($y)) {
  23233. $y = $this->y;
  23234. }
  23235. // set foreground color
  23236. $prevDrawColor = $this->DrawColor;
  23237. $prevTextColor = $this->TextColor;
  23238. $prevFillColor = $this->FillColor;
  23239. $lw = $this->LineWidth;
  23240. $this->SetLineWidth(0.01);
  23241. $size /= $k; // in case resized in a table
  23242. $xres = $arrcode['nom-X'] * $size;
  23243. $llm = $arrcode['lightmL'] * $arrcode['nom-X'] * $size; // Left Light margin
  23244. $rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin
  23245. $bcw = ($arrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size
  23246. $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
  23247. $ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding
  23248. $fbwi = $fbw - 2; // Full barcode width incl. light margins - 2mm - for isbn string
  23249. // cf. http://www.gs1uk.org/downloads/bar_code/Bar coding getting it right.pdf
  23250. $num_height = 3 * $size; // Height of numerals
  23251. $fbh = $arrcode['nom-H'] * $size * $height; // Full barcode height incl. numerals
  23252. $bch = $fbh - (1.5 * $size); // Barcode height of bars (3mm for numerals)
  23253. if (($btype == 'EAN13' && $showtext) || $btype == 'ISSN' || $btype == 'ISBN') { // Add height for ISBN string + margin from top of bars
  23254. $tisbnm = 1.5 * $size; // Top margin between isbn (if shown) & bars
  23255. $codestr_fontsize = 2.1 * $size;
  23256. $paddingT += $codestr_fontsize + $tisbnm;
  23257. }
  23258. $oh = $fbh + $paddingT + $paddingB; // Full overall height incl. user-defined padding
  23259. // PRINT border background color
  23260. $xpos = $x;
  23261. $ypos = $y;
  23262. if ($col) {
  23263. $this->SetDColor($col);
  23264. $this->SetTColor($col);
  23265. } else {
  23266. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  23267. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  23268. }
  23269. if ($bgcol) {
  23270. $this->SetFColor($bgcol);
  23271. } else {
  23272. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  23273. }
  23274. if (!$bgcol && !$col) { // fn. called directly - not via HTML
  23275. if ($border) {
  23276. $fillb = 'DF';
  23277. } else {
  23278. $fillb = 'F';
  23279. }
  23280. $this->Rect($xpos, $ypos, $ow, $oh, $fillb);
  23281. }
  23282. // PRINT BARS
  23283. $xpos = $x + $paddingL + $llm;
  23284. $ypos = $y + $paddingT;
  23285. if ($col) {
  23286. $this->SetFColor($col);
  23287. } else {
  23288. $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  23289. }
  23290. if ($arrcode !== false) {
  23291. foreach ($arrcode["bcode"] as $v) {
  23292. $bw = ($v["w"] * $xres);
  23293. if ($v["t"]) {
  23294. // draw a vertical bar
  23295. $this->Rect($xpos, $ypos, $bw, $bch, 'F');
  23296. }
  23297. $xpos += $bw;
  23298. }
  23299. }
  23300. // print text
  23301. $prevFontFamily = $this->FontFamily;
  23302. $prevFontStyle = $this->FontStyle;
  23303. $prevFontSizePt = $this->FontSizePt;
  23304. // ISBN string
  23305. if (($btype === 'EAN13' && $showtext) || $btype === 'ISBN' || $btype === 'ISSN') {
  23306. if ($this->onlyCoreFonts) {
  23307. $this->SetFont('chelvetica');
  23308. } else {
  23309. $this->SetFont('sans');
  23310. }
  23311. if ($bgcol) {
  23312. $this->SetFColor($bgcol);
  23313. } else {
  23314. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  23315. }
  23316. $this->x = $x + $paddingL + 1; // 1mm left margin (cf. $fbwi above)
  23317. // max width is $fbwi
  23318. $loop = 0;
  23319. while ($loop == 0) {
  23320. $this->SetFontSize($codestr_fontsize * 1.4 * Mpdf::SCALE, false); // don't write
  23321. $sz = $this->GetStringWidth($codestr);
  23322. if ($sz > $fbwi) {
  23323. $codestr_fontsize -= 0.1;
  23324. } else {
  23325. $loop ++;
  23326. }
  23327. }
  23328. $this->SetFont('', '', $codestr_fontsize * 1.4 * Mpdf::SCALE, true, true); // * 1.4 because font height is only 7/10 of given mm
  23329. // WORD SPACING
  23330. if ($fbwi > $sz) {
  23331. $xtra = $fbwi - $sz;
  23332. $charspacing = $xtra / (strlen($codestr) - 1);
  23333. if ($charspacing) {
  23334. $this->writer->write(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE));
  23335. }
  23336. }
  23337. $this->y = $y + $paddingT - ($codestr_fontsize ) - $tisbnm;
  23338. $this->Cell($fbw, $codestr_fontsize, $codestr);
  23339. if ($charspacing) {
  23340. $this->writer->write('BT 0 Tc ET');
  23341. }
  23342. }
  23343. // Bottom NUMERALS
  23344. // mPDF 5.7.4
  23345. if ($this->onlyCoreFonts) {
  23346. $this->SetFont('ccourier');
  23347. $fh = 1.3;
  23348. } else {
  23349. $this->SetFont('ocrb');
  23350. $fh = 1.06;
  23351. }
  23352. $charRO = '';
  23353. if ($btype === 'EAN13' || $btype === 'ISBN' || $btype === 'ISSN') {
  23354. $outerfontsize = 3; // Inner fontsize = 3
  23355. $outerp = $xres * 4;
  23356. $innerp = $xres * 2.5;
  23357. $textw = ($bcw * 0.5) - $outerp - $innerp;
  23358. $chars = 6; // number of numerals in each half
  23359. $charLO = substr($code, 0, 1); // Left Outer
  23360. $charLI = substr($code, 1, 6); // Left Inner
  23361. $charRI = substr($code, 7, 6); // Right Inner
  23362. if (!$supplement) {
  23363. $charRO = '>'; // Right Outer
  23364. }
  23365. } elseif ($btype === 'UPCA') {
  23366. $outerfontsize = 2.3; // Inner fontsize = 3
  23367. $outerp = $xres * 10;
  23368. $innerp = $xres * 2.5;
  23369. $textw = ($bcw * 0.5) - $outerp - $innerp;
  23370. $chars = 5;
  23371. $charLO = substr($code, 0, 1); // Left Outer
  23372. $charLI = substr($code, 1, 5); // Left Inner
  23373. $charRI = substr($code, 6, 5); // Right Inner
  23374. $charRO = substr($code, 11, 1); // Right Outer
  23375. } elseif ($btype === 'UPCE') {
  23376. $outerfontsize = 2.3; // Inner fontsize = 3
  23377. $outerp = $xres * 4;
  23378. $innerp = 0;
  23379. $textw = ($bcw * 0.5) - $outerp - $innerp;
  23380. $chars = 3;
  23381. $upce_code = $arrcode['code'];
  23382. $charLO = substr($code, 0, 1); // Left Outer
  23383. $charLI = substr($upce_code, 0, 3); // Left Inner
  23384. $charRI = substr($upce_code, 3, 3); // Right Inner
  23385. $charRO = substr($code, 11, 1); // Right Outer
  23386. } elseif ($btype === 'EAN8') {
  23387. $outerfontsize = 3; // Inner fontsize = 3
  23388. $outerp = $xres * 4;
  23389. $innerp = $xres * 2.5;
  23390. $textw = ($bcw * 0.5) - $outerp - $innerp;
  23391. $chars = 4;
  23392. $charLO = '<'; // Left Outer
  23393. $charLI = substr($code, 0, 4); // Left Inner
  23394. $charRI = substr($code, 4, 4); // Right Inner
  23395. if (!$supplement) {
  23396. $charRO = '>'; // Right Outer
  23397. }
  23398. }
  23399. $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
  23400. if (!$this->usingCoreFont) { // character width at 3mm
  23401. $cw = $this->_getCharWidth($this->CurrentFont['cw'], 32) * 3 * $fh * $size / 1000;
  23402. } else {
  23403. $cw = 600 * 3 * $fh * $size / 1000;
  23404. }
  23405. // Outer left character
  23406. $y_text = $y + $paddingT + $bch - ($num_height / 2);
  23407. $y_text_outer = $y + $paddingT + $bch - ($num_height * ($outerfontsize / 3) / 2);
  23408. $this->x = $x + $paddingL - ($cw * ($outerfontsize / 3) * 0.1); // 0.1 is correction as char does not fill full width;
  23409. $this->y = $y_text_outer;
  23410. $this->Cell($cw, $num_height, $charLO);
  23411. // WORD SPACING for inner chars
  23412. $xtra = $textw - ($cw * $chars);
  23413. $charspacing = $xtra / ($chars - 1);
  23414. if ($charspacing) {
  23415. $this->writer->write(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE));
  23416. }
  23417. if ($bgcol) {
  23418. $this->SetFColor($bgcol);
  23419. } else {
  23420. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  23421. }
  23422. $this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
  23423. // Inner left half characters
  23424. $this->x = $x + $paddingL + $llm + $outerp;
  23425. $this->y = $y_text;
  23426. $this->Cell($textw, $num_height, $charLI, 0, 0, '', 1);
  23427. // Inner right half characters
  23428. $this->x = $x + $paddingL + $llm + ($bcw * 0.5) + $innerp;
  23429. $this->y = $y_text;
  23430. $this->Cell($textw, $num_height, $charRI, 0, 0, '', 1);
  23431. if ($charspacing) {
  23432. $this->writer->write('BT 0 Tc ET');
  23433. }
  23434. // Outer Right character
  23435. $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
  23436. $this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * ($outerfontsize / 3) * 0.9); // 0.9 is correction as char does not fill full width
  23437. $this->y = $y_text_outer;
  23438. $this->Cell($cw * ($outerfontsize / 3), $num_height, $charRO, 0, 0, 'R');
  23439. if ($supplement) { // EAN-2 or -5 Supplement
  23440. // PRINT BARS
  23441. $supparrcode = $this->barcode->getBarcodeArray($supplement_code, 'EAN' . $supplement);
  23442. if ($supparrcode === false) {
  23443. throw new \Mpdf\MpdfException('Error in barcode string (supplement): ' . $codestr . ' ' . $supplement_code);
  23444. }
  23445. if (strlen($supplement_code) != $supplement) {
  23446. throw new \Mpdf\MpdfException('Barcode supplement incorrect: ' . $supplement_code);
  23447. }
  23448. $llm = $fbw - (($arrcode['lightmR'] - $supparrcode['sepM']) * $arrcode['nom-X'] * $size); // Left Light margin
  23449. $rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin
  23450. $bcw = ($supparrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size
  23451. $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
  23452. $ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding
  23453. $bch = $fbh - (1.5 * $size) - ($num_height + 0.5); // Barcode height of bars (3mm for numerals)
  23454. $xpos = $x + $paddingL + $llm;
  23455. $ypos = $y + $paddingT + $num_height + 0.5;
  23456. if ($col) {
  23457. $this->SetFColor($col);
  23458. } else {
  23459. $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  23460. }
  23461. if ($supparrcode !== false) {
  23462. foreach ($supparrcode["bcode"] as $v) {
  23463. $bw = ($v["w"] * $xres);
  23464. if ($v["t"]) {
  23465. // draw a vertical bar
  23466. $this->Rect($xpos, $ypos, $bw, $bch, 'F');
  23467. }
  23468. $xpos += $bw;
  23469. }
  23470. }
  23471. // Characters
  23472. if ($bgcol) {
  23473. $this->SetFColor($bgcol);
  23474. } else {
  23475. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  23476. }
  23477. $this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
  23478. $this->x = $x + $paddingL + $llm;
  23479. $this->y = $y + $paddingT;
  23480. $this->Cell($bcw, $num_height, $supplement_code, 0, 0, 'C');
  23481. // Outer Right character (light margin)
  23482. $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
  23483. $this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * 0.9); // 0.9 is correction as char does not fill full width
  23484. $this->y = $y + $paddingT;
  23485. $this->Cell($cw * ($outerfontsize / 3), $num_height, '>', 0, 0, 'R');
  23486. }
  23487. // Restore **************
  23488. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
  23489. $this->DrawColor = $prevDrawColor;
  23490. $this->TextColor = $prevTextColor;
  23491. $this->FillColor = $prevFillColor;
  23492. $this->SetLineWidth($lw);
  23493. $this->SetY($y);
  23494. }
  23495. /**
  23496. * POSTAL and OTHER barcodes
  23497. */
  23498. 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)
  23499. {
  23500. if (empty($code)) {
  23501. return;
  23502. }
  23503. $this->barcode = new Barcode();
  23504. $arrcode = $this->barcode->getBarcodeArray($code, $btype, $print_ratio, $quiet_zone_left, $quiet_zone_right);
  23505. if (empty($x)) {
  23506. $x = $this->x;
  23507. }
  23508. if (empty($y)) {
  23509. $y = $this->y;
  23510. }
  23511. $prevDrawColor = $this->DrawColor;
  23512. $prevTextColor = $this->TextColor;
  23513. $prevFillColor = $this->FillColor;
  23514. $lw = $this->LineWidth;
  23515. $this->SetLineWidth(0.01);
  23516. $size /= $k; // in case resized in a table
  23517. $xres = $arrcode['nom-X'] * $size;
  23518. if ($btype === 'IMB' || $btype === 'RM4SCC' || $btype === 'KIX' || $btype === 'POSTNET' || $btype === 'PLANET') {
  23519. $llm = $arrcode['quietL'] / $k; // Left Quiet margin
  23520. $rlm = $arrcode['quietR'] / $k; // Right Quiet margin
  23521. $tlm = $blm = $arrcode['quietTB'] / $k;
  23522. $height = 1; // Overrides
  23523. } 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'])) {
  23524. $llm = $arrcode['lightmL'] * $xres; // Left Quiet margin
  23525. $rlm = $arrcode['lightmR'] * $xres; // Right Quiet margin
  23526. $tlm = $blm = $arrcode['lightTB'] * $xres * $height;
  23527. }
  23528. $bcw = ($arrcode["maxw"] * $xres);
  23529. $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
  23530. $bch = ($arrcode["nom-H"] * $size * $height);
  23531. $fbh = $bch + $tlm + $blm; // Full barcode height
  23532. // PRINT border background color
  23533. $xpos = $x;
  23534. $ypos = $y;
  23535. if ($col) {
  23536. $this->SetDColor($col);
  23537. $this->SetTColor($col);
  23538. } else {
  23539. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  23540. $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  23541. }
  23542. if ($bgcol) {
  23543. $this->SetFColor($bgcol);
  23544. } else {
  23545. $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
  23546. }
  23547. // PRINT BARS
  23548. if ($col) {
  23549. $this->SetFColor($col);
  23550. } else {
  23551. $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  23552. }
  23553. $xpos = $x + $llm;
  23554. if ($arrcode !== false) {
  23555. foreach ($arrcode["bcode"] as $v) {
  23556. $bw = ($v["w"] * $xres);
  23557. if ($v["t"]) {
  23558. $ypos = $y + $tlm + ($bch * $v['p'] / $arrcode['maxh']);
  23559. $this->Rect($xpos, $ypos, $bw, ($v['h'] * $bch / $arrcode['maxh']), 'F');
  23560. }
  23561. $xpos += $bw;
  23562. }
  23563. }
  23564. // PRINT BEARER BARS
  23565. if ($btype == 'I25B' || $btype == 'I25B+') {
  23566. $this->Rect($x, $y, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');
  23567. $this->Rect($x, $y + $tlm + $bch, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');
  23568. }
  23569. // Restore **************
  23570. $this->DrawColor = $prevDrawColor;
  23571. $this->TextColor = $prevTextColor;
  23572. $this->FillColor = $prevFillColor;
  23573. $this->SetLineWidth($lw);
  23574. $this->SetY($y);
  23575. }
  23576. /* -- END BARCODES -- */
  23577. function StartTransform($returnstring = false)
  23578. {
  23579. if ($returnstring) {
  23580. return('q');
  23581. } else {
  23582. $this->writer->write('q');
  23583. }
  23584. }
  23585. function StopTransform($returnstring = false)
  23586. {
  23587. if ($returnstring) {
  23588. return('Q');
  23589. } else {
  23590. $this->writer->write('Q');
  23591. }
  23592. }
  23593. function transformScale($s_x, $s_y, $x = '', $y = '', $returnstring = false)
  23594. {
  23595. if ($x === '') {
  23596. $x = $this->x;
  23597. }
  23598. if ($y === '') {
  23599. $y = $this->y;
  23600. }
  23601. if (($s_x == 0) or ( $s_y == 0)) {
  23602. throw new \Mpdf\MpdfException('Please do not use values equal to zero for scaling');
  23603. }
  23604. $y = ($this->h - $y) * Mpdf::SCALE;
  23605. $x *= Mpdf::SCALE;
  23606. // calculate elements of transformation matrix
  23607. $s_x /= 100;
  23608. $s_y /= 100;
  23609. $tm = [];
  23610. $tm[0] = $s_x;
  23611. $tm[1] = 0;
  23612. $tm[2] = 0;
  23613. $tm[3] = $s_y;
  23614. $tm[4] = $x * (1 - $s_x);
  23615. $tm[5] = $y * (1 - $s_y);
  23616. // scale the coordinate system
  23617. if ($returnstring) {
  23618. return($this->_transform($tm, true));
  23619. } else {
  23620. $this->_transform($tm);
  23621. }
  23622. }
  23623. function transformTranslate($t_x, $t_y, $returnstring = false)
  23624. {
  23625. // calculate elements of transformation matrix
  23626. $tm = [];
  23627. $tm[0] = 1;
  23628. $tm[1] = 0;
  23629. $tm[2] = 0;
  23630. $tm[3] = 1;
  23631. $tm[4] = $t_x * Mpdf::SCALE;
  23632. $tm[5] = -$t_y * Mpdf::SCALE;
  23633. // translate the coordinate system
  23634. if ($returnstring) {
  23635. return($this->_transform($tm, true));
  23636. } else {
  23637. $this->_transform($tm);
  23638. }
  23639. }
  23640. function transformRotate($angle, $x = '', $y = '', $returnstring = false)
  23641. {
  23642. if ($x === '') {
  23643. $x = $this->x;
  23644. }
  23645. if ($y === '') {
  23646. $y = $this->y;
  23647. }
  23648. $angle = -$angle;
  23649. $y = ($this->h - $y) * Mpdf::SCALE;
  23650. $x *= Mpdf::SCALE;
  23651. // calculate elements of transformation matrix
  23652. $tm = [];
  23653. $tm[0] = cos(deg2rad($angle));
  23654. $tm[1] = sin(deg2rad($angle));
  23655. $tm[2] = -$tm[1];
  23656. $tm[3] = $tm[0];
  23657. $tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
  23658. $tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
  23659. // rotate the coordinate system around ($x,$y)
  23660. if ($returnstring) {
  23661. return $this->_transform($tm, true);
  23662. } else {
  23663. $this->_transform($tm);
  23664. }
  23665. }
  23666. /**
  23667. * mPDF 5.7.3 TRANSFORMS
  23668. */
  23669. function transformSkew($angle_x, $angle_y, $x = '', $y = '', $returnstring = false)
  23670. {
  23671. if ($x === '') {
  23672. $x = $this->x;
  23673. }
  23674. if ($y === '') {
  23675. $y = $this->y;
  23676. }
  23677. $angle_x = -$angle_x;
  23678. $angle_y = -$angle_y;
  23679. $x *= Mpdf::SCALE;
  23680. $y = ($this->h - $y) * Mpdf::SCALE;
  23681. // calculate elements of transformation matrix
  23682. $tm = [];
  23683. $tm[0] = 1;
  23684. $tm[1] = tan(deg2rad($angle_y));
  23685. $tm[2] = tan(deg2rad($angle_x));
  23686. $tm[3] = 1;
  23687. $tm[4] = -$tm[2] * $y;
  23688. $tm[5] = -$tm[1] * $x;
  23689. // skew the coordinate system
  23690. if ($returnstring) {
  23691. return $this->_transform($tm, true);
  23692. } else {
  23693. $this->_transform($tm);
  23694. }
  23695. }
  23696. function _transform($tm, $returnstring = false)
  23697. {
  23698. if ($returnstring) {
  23699. return(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
  23700. } else {
  23701. $this->writer->write(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
  23702. }
  23703. }
  23704. // AUTOFONT =========================
  23705. function markScriptToLang($html)
  23706. {
  23707. if ($this->onlyCoreFonts) {
  23708. return $html;
  23709. }
  23710. $n = '';
  23711. $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
  23712. foreach ($a as $i => $e) {
  23713. if ($i % 2 == 0) {
  23714. // ignore if in Textarea
  23715. if ($i > 0 && strtolower(substr($a[$i - 1], 1, 8)) == 'textarea') {
  23716. $a[$i] = $e;
  23717. continue;
  23718. }
  23719. $e = UtfString::strcode2utf($e);
  23720. $e = $this->lesser_entity_decode($e);
  23721. $earr = $this->UTF8StringToArray($e, false);
  23722. $scriptblock = 0;
  23723. $scriptblocks = [];
  23724. $scriptblocks[0] = 0;
  23725. $chardata = [];
  23726. $subchunk = 0;
  23727. $charctr = 0;
  23728. foreach ($earr as $char) {
  23729. $ucd_record = Ucdn::get_ucd_record($char);
  23730. $sbl = $ucd_record[6];
  23731. if ($sbl && $sbl != 40 && $sbl != 102) {
  23732. if ($scriptblock == 0) {
  23733. $scriptblock = $sbl;
  23734. $scriptblocks[$subchunk] = $scriptblock;
  23735. } elseif ($scriptblock > 0 && $scriptblock != $sbl) {
  23736. // NEW (non-common) Script encountered in this chunk.
  23737. // Start a new subchunk
  23738. $subchunk++;
  23739. $scriptblock = $sbl;
  23740. $charctr = 0;
  23741. $scriptblocks[$subchunk] = $scriptblock;
  23742. }
  23743. }
  23744. $chardata[$subchunk][$charctr]['script'] = $sbl;
  23745. $chardata[$subchunk][$charctr]['uni'] = $char;
  23746. $charctr++;
  23747. }
  23748. // If scriptblock[x] = common & non-baseScript
  23749. // and scriptblock[x+1] = baseScript
  23750. // Move common script from end of x to start of x+1
  23751. for ($sch = 0; $sch < $subchunk; $sch++) {
  23752. if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $scriptblocks[$sch + 1] == $this->baseScript) {
  23753. $end = count($chardata[$sch]) - 1;
  23754. while ($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script
  23755. $tmp = array_pop($chardata[$sch]);
  23756. array_unshift($chardata[$sch + 1], $tmp);
  23757. $end--;
  23758. }
  23759. }
  23760. }
  23761. $o = '';
  23762. for ($sch = 0; $sch <= $subchunk; $sch++) {
  23763. if (isset($chardata[$sch])) {
  23764. $s = '';
  23765. for ($j = 0; $j < count($chardata[$sch]); $j++) {
  23766. $s .= UtfString::code2utf($chardata[$sch][$j]['uni']);
  23767. }
  23768. // ZZZ99 Undo lesser_entity_decode as above - but only for <>&
  23769. $s = str_replace("&", "&amp;", $s);
  23770. $s = str_replace("<", "&lt;", $s);
  23771. $s = str_replace(">", "&gt;", $s);
  23772. // Check Vietnamese if Latin script - even if Basescript
  23773. if ($scriptblocks[$sch] == Ucdn::SCRIPT_LATIN && $this->autoVietnamese && preg_match("/([" . $this->scriptToLanguage->getLanguageDelimiters('viet') . "])/u", $s)) {
  23774. $o .= '<span lang="vi" class="lang_vi">' . $s . '</span>';
  23775. } elseif ($scriptblocks[$sch] == Ucdn::SCRIPT_ARABIC && $this->autoArabic) { // Check Arabic for different languages if Arabic script - even if Basescript
  23776. if (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('sindhi') . "]/u", $s)) {
  23777. $o .= '<span lang="sd" class="lang_sd">' . $s . '</span>';
  23778. } elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('urdu') . "]/u", $s)) {
  23779. $o .= '<span lang="ur" class="lang_ur">' . $s . '</span>';
  23780. } elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('pashto') . "]/u", $s)) {
  23781. $o .= '<span lang="ps" class="lang_ps">' . $s . '</span>';
  23782. } elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('persian') . "]/u", $s)) {
  23783. $o .= '<span lang="fa" class="lang_fa">' . $s . '</span>';
  23784. } elseif ($this->baseScript != Ucdn::SCRIPT_ARABIC && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) {
  23785. $o .= '<span lang="' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '" class="lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '">' . $s . '</span>';
  23786. } else {
  23787. // Just output chars
  23788. $o .= $s;
  23789. }
  23790. } elseif ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) { // Identify Script block if not Basescript, and mark up as language
  23791. // Encase in <span>
  23792. $o .= '<span lang="' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '" class="lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '">';
  23793. $o .= $s;
  23794. $o .= '</span>';
  23795. } else {
  23796. // Just output chars
  23797. $o .= $s;
  23798. }
  23799. }
  23800. }
  23801. $a[$i] = $o;
  23802. } else {
  23803. $a[$i] = '<' . $e . '>';
  23804. }
  23805. }
  23806. $n = implode('', $a);
  23807. return $n;
  23808. }
  23809. /* -- COLUMNS -- */
  23810. /**
  23811. * Callback function from function printcolumnbuffer in mpdf
  23812. */
  23813. function columnAdjustAdd($type, $k, $xadj, $yadj, $a, $b, $c = 0, $d = 0, $e = 0, $f = 0)
  23814. {
  23815. if ($type === 'Td') { // xpos,ypos
  23816. $a += ($xadj * $k);
  23817. $b -= ($yadj * $k);
  23818. return 'BT ' . sprintf('%.3F %.3F', $a, $b) . ' Td';
  23819. } elseif ($type === 're') { // xpos,ypos,width,height
  23820. $a += ($xadj * $k);
  23821. $b -= ($yadj * $k);
  23822. return sprintf('%.3F %.3F %.3F %.3F', $a, $b, $c, $d) . ' re';
  23823. } elseif ($type === 'l') { // xpos,ypos,x2pos,y2pos
  23824. $a += ($xadj * $k);
  23825. $b -= ($yadj * $k);
  23826. return sprintf('%.3F %.3F l', $a, $b);
  23827. } elseif ($type === 'img') { // width,height,xpos,ypos
  23828. $c += ($xadj * $k);
  23829. $d -= ($yadj * $k);
  23830. return sprintf('q %.3F 0 0 %.3F %.3F %.3F', $a, $b, $c, $d) . ' cm /' . $e;
  23831. } elseif ($type === 'draw') { // xpos,ypos
  23832. $a += ($xadj * $k);
  23833. $b -= ($yadj * $k);
  23834. return sprintf('%.3F %.3F m', $a, $b);
  23835. } elseif ($type === 'bezier') { // xpos,ypos,x2pos,y2pos,x3pos,y3pos
  23836. $a += ($xadj * $k);
  23837. $b -= ($yadj * $k);
  23838. $c += ($xadj * $k);
  23839. $d -= ($yadj * $k);
  23840. $e += ($xadj * $k);
  23841. $f -= ($yadj * $k);
  23842. return sprintf('%.3F %.3F %.3F %.3F %.3F %.3F', $a, $b, $c, $d, $e, $f) . ' c';
  23843. }
  23844. }
  23845. /* -- END COLUMNS -- */
  23846. // mPDF 5.7.3 TRANSFORMS
  23847. function ConvertAngle($s, $makepositive = true)
  23848. {
  23849. if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i', $s, $m)) {
  23850. $angle = $m[1] + 0;
  23851. if (strtolower($m[2]) == 'deg') {
  23852. $angle = $angle;
  23853. } elseif (strtolower($m[2]) == 'grad') {
  23854. $angle *= (360 / 400);
  23855. } elseif (strtolower($m[2]) == 'rad') {
  23856. $angle = rad2deg($angle);
  23857. }
  23858. while ($angle >= 360) {
  23859. $angle -= 360;
  23860. }
  23861. while ($angle <= -360) {
  23862. $angle += 360;
  23863. }
  23864. if ($makepositive) { // always returns an angle between 0 and 360deg
  23865. if ($angle < 0) {
  23866. $angle += 360;
  23867. }
  23868. }
  23869. } else {
  23870. $angle = $s + 0;
  23871. }
  23872. return $angle;
  23873. }
  23874. function lesser_entity_decode($html)
  23875. {
  23876. // supports the most used entity codes (only does ascii safe characters)
  23877. $html = str_replace("&lt;", "<", $html);
  23878. $html = str_replace("&gt;", ">", $html);
  23879. $html = str_replace("&apos;", "'", $html);
  23880. $html = str_replace("&quot;", '"', $html);
  23881. $html = str_replace("&amp;", "&", $html);
  23882. return $html;
  23883. }
  23884. function AdjustHTML($html, $tabSpaces = 8)
  23885. {
  23886. $limit = ini_get('pcre.backtrack_limit');
  23887. if (0 >= (int) $limit) {
  23888. throw new \Mpdf\MpdfException(sprintf(
  23889. 'mPDF will not process HTML with disabled pcre.backtrack_limit to prevent unexpected behaviours, please set a positive backtrack limit.',
  23890. $limit
  23891. ));
  23892. }
  23893. if (strlen($html) > (int) $limit) {
  23894. throw new \Mpdf\MpdfException(sprintf(
  23895. 'The HTML code size is larger than pcre.backtrack_limit %d. You should use WriteHTML() with smaller string lengths. Pass your HTML in smaller chunks.',
  23896. $limit
  23897. ));
  23898. }
  23899. preg_match_all("/(<annotation.*?>)/si", $html, $m);
  23900. if (count($m[1])) {
  23901. for ($i = 0; $i < count($m[1]); $i++) {
  23902. $sub = preg_replace("/\n/si", Mpdf::OBJECT_IDENTIFIER, $m[1][$i]);
  23903. $html = preg_replace('/' . preg_quote($m[1][$i], '/') . '/si', $sub, $html);
  23904. }
  23905. }
  23906. preg_match_all("/(<svg.*?<\/svg>)/si", $html, $svgi);
  23907. if (count($svgi[0])) {
  23908. for ($i = 0; $i < count($svgi[0]); $i++) {
  23909. $file = $this->cache->write('/_tempSVG' . uniqid(random_int(1, 100000), true) . '_' . $i . '.svg', $svgi[0][$i]);
  23910. $html = str_replace($svgi[0][$i], '<img src="' . $file . '" />', $html);
  23911. }
  23912. }
  23913. // Remove javascript code from HTML (should not appear in the PDF file)
  23914. $html = preg_replace('/<script.*?<\/script>/is', '', $html);
  23915. // Remove special comments
  23916. $html = preg_replace('/<!--mpdf/i', '', $html);
  23917. $html = preg_replace('/mpdf-->/i', '', $html);
  23918. // Remove comments from HTML (should not appear in the PDF file)
  23919. $html = preg_replace('/<!--.*?-->/s', '', $html);
  23920. $html = preg_replace('/\f/', '', $html); // replace formfeed by nothing
  23921. $html = preg_replace('/\r/', '', $html); // replace carriage return by nothing
  23922. // Well formed XHTML end tags
  23923. $html = preg_replace('/<(br|hr)>/i', "<\\1 />", $html); // mPDF 6
  23924. $html = preg_replace('/<(br|hr)\/>/i', "<\\1 />", $html);
  23925. // Get rid of empty <thead></thead> etc
  23926. $html = preg_replace('/<tr>\s*<\/tr>/i', '', $html);
  23927. $html = preg_replace('/<thead>\s*<\/thead>/i', '', $html);
  23928. $html = preg_replace('/<tfoot>\s*<\/tfoot>/i', '', $html);
  23929. $html = preg_replace('/<table[^>]*>\s*<\/table>/i', '', $html);
  23930. // Remove spaces at end of table cells
  23931. $html = preg_replace("/[ \n\r]+<\/t(d|h)/", '</t\\1', $html);
  23932. $html = preg_replace("/[ ]*<dottab\s*[\/]*>[ ]*/", '<dottab />', $html);
  23933. // Concatenates any Substitute characters from symbols/dingbats
  23934. $html = str_replace('</tts><tts>', '|', $html);
  23935. $html = str_replace('</ttz><ttz>', '|', $html);
  23936. $html = str_replace('</tta><tta>', '|', $html);
  23937. $html = preg_replace('/<br \/>\s*/is', "<br />", $html);
  23938. $html = preg_replace('/<wbr[ \/]*>\s*/is', "&#173;", $html);
  23939. // Preserve '\n's in content between the tags <pre> and </pre>
  23940. if (preg_match('/<pre/', $html)) {
  23941. $html_a = preg_split('/(\<\/?pre[^\>]*\>)/', $html, -1, 2);
  23942. $h = [];
  23943. $c = 0;
  23944. foreach ($html_a as $s) {
  23945. if ($c > 1 && preg_match('/^<\/pre/i', $s)) {
  23946. $c--;
  23947. $s = preg_replace('/<\/pre/i', '</innerpre', $s);
  23948. } elseif ($c > 0 && preg_match('/^<pre/i', $s)) {
  23949. $c++;
  23950. $s = preg_replace('/<pre/i', '<innerpre', $s);
  23951. } elseif (preg_match('/^<pre/i', $s)) {
  23952. $c++;
  23953. } elseif (preg_match('/^<\/pre/i', $s)) {
  23954. $c--;
  23955. }
  23956. array_push($h, $s);
  23957. }
  23958. $html = implode('', $h);
  23959. }
  23960. $thereispre = preg_match_all('#<pre(.*?)>(.*?)</pre>#si', $html, $temp);
  23961. // Preserve '\n's in content between the tags <textarea> and </textarea>
  23962. $thereistextarea = preg_match_all('#<textarea(.*?)>(.*?)</textarea>#si', $html, $temp2);
  23963. $html = preg_replace('/[\n]/', ' ', $html); // replace linefeed by spaces
  23964. $html = preg_replace('/[\t]/', ' ', $html); // replace tabs by spaces
  23965. // Converts < to &lt; when not a tag
  23966. $html = preg_replace('/<([^!\/a-zA-Z_:])/i', '&lt;\\1', $html); // mPDF 5.7.3
  23967. $html = preg_replace("/[ ]+/", ' ', $html);
  23968. $html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html);
  23969. $html = preg_replace('/\/(u|o)l>\s+<\/li/i', '/\\1l></li', $html);
  23970. $html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html);
  23971. $html = preg_replace('/\/li>\s+<li/i', '/li><li', $html);
  23972. $html = preg_replace('/<(u|o)l([^>]*)>[ ]+/i', '<\\1l\\2>', $html);
  23973. $html = preg_replace('/[ ]+<(u|o)l/i', '<\\1l', $html);
  23974. // Make self closing tabs valid XHTML
  23975. // Tags which are self-closing: 1) Replaceable and 2) Non-replaced items
  23976. $selftabs = 'input|hr|img|br|barcode|dottab';
  23977. $selftabs2 = 'indexentry|indexinsert|bookmark|watermarktext|watermarkimage|column_break|columnbreak|newcolumn|newpage|page_break|pagebreak|formfeed|columns|toc|tocpagebreak|setpageheader|setpagefooter|sethtmlpageheader|sethtmlpagefooter|annotation';
  23978. // Fix self-closing tags which don't close themselves
  23979. $html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . ')[^>\/]*)>/i', '\\1 />', $html);
  23980. // Fix self-closing tags that don't include a space between the tag name and the closing slash
  23981. $html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . '))\/>/i', '\\1 />', $html);
  23982. $iterator = 0;
  23983. while ($thereispre) { // Recover <pre attributes>content</pre>
  23984. $temp[2][$iterator] = preg_replace('/<([^!\/a-zA-Z_:])/', '&lt;\\1', $temp[2][$iterator]); // mPDF 5.7.2 // mPDF 5.7.3
  23985. $temp[2][$iterator] = preg_replace_callback("/^([^\n\t]*?)\t/m", [$this, 'tabs2spaces_callback'], $temp[2][$iterator]); // mPDF 5.7+
  23986. $temp[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp[2][$iterator]);
  23987. $temp[2][$iterator] = preg_replace('/\n/', "<br />", $temp[2][$iterator]);
  23988. $temp[2][$iterator] = str_replace('\\', "\\\\", $temp[2][$iterator]);
  23989. // $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.$temp[2][$iterator].'</erp>',$html,1);
  23990. $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si', '<erp' . $temp[1][$iterator] . '>' . str_replace('$', '\$', $temp[2][$iterator]) . '</erp>', $html, 1); // mPDF 5.7+
  23991. $thereispre--;
  23992. $iterator++;
  23993. }
  23994. $iterator = 0;
  23995. while ($thereistextarea) { // Recover <textarea attributes>content</textarea>
  23996. $temp2[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp2[2][$iterator]);
  23997. $temp2[2][$iterator] = str_replace('\\', "\\\\", $temp2[2][$iterator]);
  23998. $html = preg_replace('#<textarea(.*?)>(.*?)</textarea>#si', '<aeratxet' . $temp2[1][$iterator] . '>' . trim($temp2[2][$iterator]) . '</aeratxet>', $html, 1);
  23999. $thereistextarea--;
  24000. $iterator++;
  24001. }
  24002. // Restore original tag names
  24003. $html = str_replace("<erp", "<pre", $html);
  24004. $html = str_replace("</erp>", "</pre>", $html);
  24005. $html = str_replace("<aeratxet", "<textarea", $html);
  24006. $html = str_replace("</aeratxet>", "</textarea>", $html);
  24007. $html = str_replace("</innerpre", "</pre", $html);
  24008. $html = str_replace("<innerpre", "<pre", $html);
  24009. $html = preg_replace('/<textarea([^>]*)><\/textarea>/si', '<textarea\\1> </textarea>', $html);
  24010. $html = preg_replace('/(<table[^>]*>)\s*(<caption)(.*?<\/caption>)(.*?<\/table>)/si', '\\2 position="top"\\3\\1\\4\\2 position="bottom"\\3', $html); // *TABLES*
  24011. if ($this->use_kwt) {
  24012. $returnHtml = preg_replace('/<(h[1-6])([^>]*(?<!\/))(>[^>]*<\/\\1>\s*<table)/si', '<\\1\\2 keep-with-table="1"\\3', $html);
  24013. /* If no errors then save the return value */
  24014. if (preg_last_error() === PREG_NO_ERROR) {
  24015. $html = $returnHtml;
  24016. }
  24017. }
  24018. $html = preg_replace('/' . Mpdf::OBJECT_IDENTIFIER . '/', "\n", $html);
  24019. // Fixes <p>&#8377</p> which browser copes with even though it is wrong!
  24020. $html = preg_replace("/(&#[x]{0,1}[0-9a-f]{1,5})</i", "\\1;<", $html);
  24021. return $html;
  24022. }
  24023. // mPDF 5.7+
  24024. function tabs2spaces_callback($matches)
  24025. {
  24026. return (stripslashes($matches[1]) . str_repeat(' ', $this->tabSpaces - (mb_strlen(stripslashes($matches[1])) % $this->tabSpaces)));
  24027. }
  24028. // mPDF 5.7+
  24029. function date_callback($matches)
  24030. {
  24031. return date($matches[1]);
  24032. }
  24033. // ========== OVERWRITE SEARCH STRING IN A PDF FILE ================
  24034. function OverWrite($file_in, $search, $replacement, $dest = Destination::DOWNLOAD, $file_out = "mpdf")
  24035. {
  24036. $pdf = file_get_contents($file_in);
  24037. if (!is_array($search)) {
  24038. $x = $search;
  24039. $search = [$x];
  24040. }
  24041. if (!is_array($replacement)) {
  24042. $x = $replacement;
  24043. $replacement = [$x]; // mPDF 5.7.4
  24044. }
  24045. if (!$this->onlyCoreFonts && !$this->usingCoreFont) {
  24046. foreach ($search as $k => $val) {
  24047. $search[$k] = $this->writer->utf8ToUtf16BigEndian($search[$k], false);
  24048. $search[$k] = $this->writer->escape($search[$k]);
  24049. $replacement[$k] = $this->writer->utf8ToUtf16BigEndian($replacement[$k], false);
  24050. $replacement[$k] = $this->writer->escape($replacement[$k]);
  24051. }
  24052. } else {
  24053. foreach ($replacement as $k => $val) {
  24054. $replacement[$k] = mb_convert_encoding($replacement[$k], $this->mb_enc, 'utf-8');
  24055. $replacement[$k] = $this->writer->escape($replacement[$k]);
  24056. }
  24057. }
  24058. // Get xref into array
  24059. $xref = [];
  24060. preg_match("/xref\n0 (\d+)\n(.*?)\ntrailer/s", $pdf, $m);
  24061. $xref_objid = $m[1];
  24062. preg_match_all('/(\d{10}) (\d{5}) (f|n)/', $m[2], $x);
  24063. for ($i = 0; $i < count($x[0]); $i++) {
  24064. $xref[] = [(int) $x[1][$i], $x[2][$i], $x[3][$i]];
  24065. }
  24066. $changes = [];
  24067. preg_match("/<<\s*\/Type\s*\/Pages\s*\/Kids\s*\[(.*?)\]\s*\/Count/s", $pdf, $m);
  24068. preg_match_all("/(\d+) 0 R /s", $m[1], $o);
  24069. $objlist = $o[1];
  24070. foreach ($objlist as $obj) {
  24071. if ($this->compress) {
  24072. preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Filter\s*\/FlateDecode\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m);
  24073. } else {
  24074. preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m);
  24075. }
  24076. $s = $m[2];
  24077. if (!$s) {
  24078. continue;
  24079. }
  24080. $oldlen = $m[1];
  24081. if ($this->encrypted) {
  24082. $s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s);
  24083. }
  24084. if ($this->compress) {
  24085. $s = gzuncompress($s);
  24086. }
  24087. foreach ($search as $k => $val) {
  24088. $s = str_replace($search[$k], $replacement[$k], $s);
  24089. }
  24090. if ($this->compress) {
  24091. $s = gzcompress($s);
  24092. }
  24093. if ($this->encrypted) {
  24094. $s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s);
  24095. }
  24096. $newlen = strlen($s);
  24097. $changes[($xref[$obj + 1][0])] = ($newlen - $oldlen) + (strlen($newlen) - strlen($oldlen));
  24098. if ($this->compress) {
  24099. $newstr = ($obj + 1) . " 0 obj\n<</Filter /FlateDecode /Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n";
  24100. } else {
  24101. $newstr = ($obj + 1) . " 0 obj\n<</Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n";
  24102. }
  24103. $pdf = str_replace($m[0], $newstr, $pdf);
  24104. }
  24105. // Update xref in PDF
  24106. krsort($changes);
  24107. $newxref = "xref\n0 " . $xref_objid . "\n";
  24108. foreach ($xref as $v) {
  24109. foreach ($changes as $ck => $cv) {
  24110. if ($v[0] > $ck) {
  24111. $v[0] += $cv;
  24112. }
  24113. }
  24114. $newxref .= sprintf('%010d', $v[0]) . ' ' . $v[1] . ' ' . $v[2] . " \n";
  24115. }
  24116. $newxref .= "trailer";
  24117. $pdf = preg_replace("/xref\n0 \d+\n.*?\ntrailer/s", $newxref, $pdf);
  24118. // Update startxref in PDF
  24119. preg_match("/startxref\n(\d+)\n%%EOF/s", $pdf, $m);
  24120. $startxref = $m[1];
  24121. $startxref += array_sum($changes);
  24122. $pdf = preg_replace("/startxref\n(\d+)\n%%EOF/s", "startxref\n" . $startxref . "\n%%EOF", $pdf);
  24123. // OUTPUT
  24124. switch ($dest) {
  24125. case Destination::INLINE:
  24126. if (isset($_SERVER['SERVER_NAME'])) {
  24127. // We send to a browser
  24128. header('Content-Type: application/pdf');
  24129. header('Content-Length: ' . strlen($pdf));
  24130. header('Content-disposition: inline; filename=' . $file_out);
  24131. }
  24132. echo $pdf;
  24133. break;
  24134. case Destination::FILE:
  24135. if (!$file_out) {
  24136. $file_out = 'mpdf.pdf';
  24137. }
  24138. $f = fopen($file_out, 'wb');
  24139. if (!$f) {
  24140. throw new \Mpdf\MpdfException('Unable to create output file: ' . $file_out);
  24141. }
  24142. fwrite($f, $pdf, strlen($pdf));
  24143. fclose($f);
  24144. break;
  24145. case Destination::STRING_RETURN:
  24146. return $pdf;
  24147. case Destination::DOWNLOAD: // Download file
  24148. default:
  24149. if (isset($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
  24150. header('Content-Type: application/force-download');
  24151. } else {
  24152. header('Content-Type: application/octet-stream');
  24153. }
  24154. header('Content-Length: ' . strlen($pdf));
  24155. header('Content-disposition: attachment; filename=' . $file_out);
  24156. echo $pdf;
  24157. break;
  24158. }
  24159. }
  24160. function Thumbnail($file, $npr = 3, $spacing = 10)
  24161. {
  24162. // $npr = number per row
  24163. $w = (($this->pgwidth + $spacing) / $npr) - $spacing;
  24164. $oldlinewidth = $this->LineWidth;
  24165. $this->SetLineWidth(0.02);
  24166. $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
  24167. $h = 0;
  24168. $maxh = 0;
  24169. $x = $_x = $this->lMargin;
  24170. $_y = $this->tMargin;
  24171. if ($this->y == 0) {
  24172. $y = $_y;
  24173. } else {
  24174. $y = $this->y;
  24175. }
  24176. $pagecount = $this->setSourceFile($file);
  24177. for ($n = 1; $n <= $pagecount; $n++) {
  24178. $tplidx = $this->importPage($n);
  24179. $size = $this->useTemplate($tplidx, $x, $y, $w);
  24180. $this->Rect($x, $y, $size['width'], $size['height']);
  24181. $h = max($h, $size['height']);
  24182. $maxh = max($h, $maxh);
  24183. if ($n % $npr == 0) {
  24184. if (($y + $h + $spacing + $maxh) > $this->PageBreakTrigger && $n != $pagecount) {
  24185. $this->AddPage();
  24186. $x = $_x;
  24187. $y = $_y;
  24188. } else {
  24189. $y += $h + $spacing;
  24190. $x = $_x;
  24191. $h = 0;
  24192. }
  24193. } else {
  24194. $x += $w + $spacing;
  24195. }
  24196. }
  24197. $this->SetLineWidth($oldlinewidth);
  24198. }
  24199. function SetPageTemplate($tplidx = '')
  24200. {
  24201. if (!isset($this->importedPages[$tplidx])) {
  24202. $this->pageTemplate = '';
  24203. return false;
  24204. }
  24205. $this->pageTemplate = $tplidx;
  24206. }
  24207. function SetDocTemplate($file = '', $continue = 0, $continue2pages = 0)
  24208. {
  24209. $this->docTemplate = $file;
  24210. $this->docTemplateContinue = $continue;
  24211. $this->docTemplateContinue2pages = $continue2pages;
  24212. if ($this->docTemplateContinue2pages) { // Enable continue when continue2pages is set
  24213. $this->docTemplateContinue = $this->docTemplateContinue2pages;
  24214. }
  24215. }
  24216. /* -- END IMPORTS -- */
  24217. // JAVASCRIPT
  24218. function _set_object_javascript($string)
  24219. {
  24220. $this->writer->object();
  24221. $this->writer->write('<<');
  24222. $this->writer->write('/S /JavaScript ');
  24223. $this->writer->write('/JS ' . $this->writer->string($string));
  24224. $this->writer->write('>>');
  24225. $this->writer->write('endobj');
  24226. }
  24227. function SetJS($script)
  24228. {
  24229. $this->js = $script;
  24230. }
  24231. /**
  24232. * This function takes the last comma or dot (if any) to make a clean float, ignoring thousand separator, currency or any other letter
  24233. *
  24234. * @param string $num
  24235. * @see http://php.net/manual/de/function.floatval.php#114486
  24236. * @return float
  24237. */
  24238. public function toFloat($num)
  24239. {
  24240. $dotPos = strrpos($num, '.');
  24241. $commaPos = strrpos($num, ',');
  24242. $sep = (($dotPos > $commaPos) && $dotPos) ? $dotPos : ((($commaPos > $dotPos) && $commaPos) ? $commaPos : false);
  24243. if (!$sep) {
  24244. return floatval(preg_replace('/[^0-9]/', '', $num));
  24245. }
  24246. return floatval(
  24247. preg_replace('/[^0-9]/', '', substr($num, 0, $sep)) . '.' .
  24248. preg_replace('/[^0-9]/', '', substr($num, $sep+1, strlen($num)))
  24249. );
  24250. }
  24251. public function getFontDescriptor()
  24252. {
  24253. return $this->fontDescriptor;
  24254. }
  24255. /**
  24256. * Temporarily return the method to preserve example 44 yearbook
  24257. */
  24258. public function _out($s)
  24259. {
  24260. $this->writer->write($s);
  24261. }
  24262. /**
  24263. * @param string $html
  24264. * @param string $PAGENO
  24265. * @param string $NbPgGp
  24266. * @param string $NbPg
  24267. * @return string
  24268. */
  24269. protected function aliasReplace($html, $PAGENO, $NbPgGp, $NbPg)
  24270. {
  24271. // Replaces for header and footer
  24272. $html = str_replace('{PAGENO}', $PAGENO, $html);
  24273. $html = str_replace($this->aliasNbPgGp, $NbPgGp, $html); // {nbpg}
  24274. $html = str_replace($this->aliasNbPg, $NbPg, $html); // {nb}
  24275. // Replaces for the body
  24276. $html = str_replace(mb_convert_encoding('{PAGENO}', 'UTF-16BE', 'UTF-8'), mb_convert_encoding($PAGENO, 'UTF-16BE', 'UTF-8'), $html);
  24277. $html = str_replace(mb_convert_encoding($this->aliasNbPgGp, 'UTF-16BE', 'UTF-8'), mb_convert_encoding($NbPgGp, 'UTF-16BE', 'UTF-8'), $html); // {nbpg}
  24278. $html = str_replace(mb_convert_encoding($this->aliasNbPg, 'UTF-16BE', 'UTF-8'), mb_convert_encoding($NbPg, 'UTF-16BE', 'UTF-8'), $html); // {nb}
  24279. // Date replace
  24280. $html = preg_replace_callback('/\{DATE\s+(.*?)\}/', [$this, 'date_callback'], $html); // mPDF 5.7
  24281. return $html;
  24282. }
  24283. }