1<?php
2
3namespace Mpdf;
4
5use Mpdf\Config\ConfigVariables;
6use Mpdf\Config\FontVariables;
7use Mpdf\Conversion;
8use Mpdf\Css\Border;
9use Mpdf\Css\TextVars;
10use Mpdf\Log\Context as LogContext;
11use Mpdf\Fonts\MetricsGenerator;
12use Mpdf\Output\Destination;
13use Mpdf\QrCode;
14use Mpdf\Utils\Arrays;
15use Mpdf\Utils\NumericString;
16use Mpdf\Utils\UtfString;
17use Psr\Log\LoggerInterface;
18use Psr\Log\NullLogger;
19
20/**
21 * mPDF, PHP library generating PDF files from UTF-8 encoded HTML
22 *
23 * based on FPDF by Olivier Plathey
24 *      and HTML2FPDF by Renato Coelho
25 *
26 * @license GPL-2.0
27 */
28class Mpdf implements \Psr\Log\LoggerAwareInterface
29{
30
31	use Strict;
32	use FpdiTrait;
33
34	const VERSION = '8.0.17';
35
36	const SCALE = 72 / 25.4;
37
38	var $useFixedNormalLineHeight; // mPDF 6
39	var $useFixedTextBaseline; // mPDF 6
40	var $adjustFontDescLineheight; // mPDF 6
41	var $interpolateImages; // mPDF 6
42	var $defaultPagebreakType; // mPDF 6 pagebreaktype
43	var $indexUseSubentries; // mPDF 6
44
45	var $autoScriptToLang; // mPDF 6
46	var $baseScript; // mPDF 6
47	var $autoVietnamese; // mPDF 6
48	var $autoArabic; // mPDF 6
49
50	var $CJKforceend;
51	var $h2bookmarks;
52	var $h2toc;
53	var $decimal_align;
54	var $margBuffer;
55	var $splitTableBorderWidth;
56
57	var $bookmarkStyles;
58	var $useActiveForms;
59
60	var $repackageTTF;
61	var $allowCJKorphans;
62	var $allowCJKoverflow;
63
64	var $useKerning;
65	var $restrictColorSpace;
66	var $bleedMargin;
67	var $crossMarkMargin;
68	var $cropMarkMargin;
69	var $cropMarkLength;
70	var $nonPrintMargin;
71
72	var $PDFX;
73	var $PDFXauto;
74
75	var $PDFA;
76	var $PDFAversion = '1-B';
77	var $PDFAauto;
78	var $ICCProfile;
79
80	var $printers_info;
81	var $iterationCounter;
82	var $smCapsScale;
83	var $smCapsStretch;
84
85	var $backupSubsFont;
86	var $backupSIPFont;
87	var $fonttrans;
88	var $debugfonts;
89	var $useAdobeCJK;
90	var $percentSubset;
91	var $maxTTFFilesize;
92	var $BMPonly;
93
94	var $tableMinSizePriority;
95
96	var $dpi;
97	var $watermarkImgAlphaBlend;
98	var $watermarkImgBehind;
99	var $justifyB4br;
100	var $packTableData;
101	var $pgsIns;
102	var $simpleTables;
103	var $enableImports;
104
105	var $debug;
106
107	var $setAutoTopMargin;
108	var $setAutoBottomMargin;
109	var $autoMarginPadding;
110	var $collapseBlockMargins;
111	var $falseBoldWeight;
112	var $normalLineheight;
113	var $incrementFPR1;
114	var $incrementFPR2;
115	var $incrementFPR3;
116	var $incrementFPR4;
117
118	var $SHYlang;
119	var $SHYleftmin;
120	var $SHYrightmin;
121	var $SHYcharmin;
122	var $SHYcharmax;
123	var $SHYlanguages;
124
125	// PageNumber Conditional Text
126	var $pagenumPrefix;
127	var $pagenumSuffix;
128
129	var $nbpgPrefix;
130	var $nbpgSuffix;
131	var $showImageErrors;
132	var $allow_output_buffering;
133	var $autoPadding;
134	var $tabSpaces;
135	var $autoLangToFont;
136	var $watermarkTextAlpha;
137	var $watermarkImageAlpha;
138	var $watermark_size;
139	var $watermark_pos;
140	var $annotSize;
141	var $annotMargin;
142	var $annotOpacity;
143	var $title2annots;
144	var $keepColumns;
145	var $keep_table_proportions;
146	var $ignore_table_widths;
147	var $ignore_table_percents;
148	var $list_number_suffix;
149
150	var $list_auto_mode; // mPDF 6
151	var $list_indent_first_level; // mPDF 6
152	var $list_indent_default; // mPDF 6
153	var $list_indent_default_mpdf;
154	var $list_marker_offset; // mPDF 6
155	var $list_symbol_size;
156
157	var $useSubstitutions;
158	var $CSSselectMedia;
159
160	var $forcePortraitHeaders;
161	var $forcePortraitMargins;
162	var $displayDefaultOrientation;
163	var $ignore_invalid_utf8;
164	var $allowedCSStags;
165	var $onlyCoreFonts;
166	var $allow_charset_conversion;
167
168	var $jSWord;
169	var $jSmaxChar;
170	var $jSmaxCharLast;
171	var $jSmaxWordLast;
172
173	var $max_colH_correction;
174
175	var $table_error_report;
176	var $table_error_report_param;
177	var $biDirectional;
178	var $text_input_as_HTML;
179	var $anchor2Bookmark;
180	var $shrink_tables_to_fit;
181
182	var $allow_html_optional_endtags;
183
184	var $img_dpi;
185	var $whitelistStreamWrappers;
186
187	var $defaultheaderfontsize;
188	var $defaultheaderfontstyle;
189	var $defaultheaderline;
190	var $defaultfooterfontsize;
191	var $defaultfooterfontstyle;
192	var $defaultfooterline;
193	var $header_line_spacing;
194	var $footer_line_spacing;
195
196	var $pregCJKchars;
197	var $pregRTLchars;
198	var $pregCURSchars; // mPDF 6
199
200	var $mirrorMargins;
201	var $watermarkText;
202	var $watermarkAngle;
203	var $watermarkImage;
204	var $showWatermarkText;
205	var $showWatermarkImage;
206
207	var $svgAutoFont;
208	var $svgClasses;
209
210	var $fontsizes;
211
212	var $defaultPageNumStyle; // mPDF 6
213
214	//////////////////////
215	// INTERNAL VARIABLES
216	//////////////////////
217	var $extrapagebreak; // mPDF 6 pagebreaktype
218
219	var $uniqstr; // mPDF 5.7.2
220	var $hasOC;
221
222	var $textvar; // mPDF 5.7.1
223	var $fontLanguageOverride; // mPDF 5.7.1
224	var $OTLtags; // mPDF 5.7.1
225	var $OTLdata;  // mPDF 5.7.1
226
227	var $useDictionaryLBR;
228	var $useTibetanLBR;
229
230	var $writingToC;
231	var $layers;
232	var $layerDetails;
233	var $current_layer;
234	var $open_layer_pane;
235	var $decimal_offset;
236	var $inMeter;
237
238	var $CJKleading;
239	var $CJKfollowing;
240	var $CJKoverflow;
241
242	var $textshadow;
243
244	var $colsums;
245	var $spanborder;
246	var $spanborddet;
247
248	var $visibility;
249
250	var $kerning;
251	var $fixedlSpacing;
252	var $minwSpacing;
253	var $lSpacingCSS;
254	var $wSpacingCSS;
255
256	var $spotColorIDs;
257	var $SVGcolors;
258	var $spotColors;
259	var $defTextColor;
260	var $defDrawColor;
261	var $defFillColor;
262
263	var $tableBackgrounds;
264	var $inlineDisplayOff;
265	var $kt_y00;
266	var $kt_p00;
267	var $upperCase;
268	var $checkSIP;
269	var $checkSMP;
270	var $checkCJK;
271
272	var $watermarkImgAlpha;
273	var $PDFAXwarnings;
274
275	var $MetadataRoot;
276	var $OutputIntentRoot;
277	var $InfoRoot;
278	var $associatedFilesRoot;
279
280	var $pdf_version;
281
282	private $fontDir;
283
284	var $tempDir;
285
286	var $cacheCleanupInterval;
287
288	var $allowAnnotationFiles;
289
290	var $fontdata;
291
292	var $noImageFile;
293	var $lastblockbottommargin;
294	var $baselineC;
295
296	// mPDF 5.7.3  inline text-decoration parameters
297	var $baselineSup;
298	var $baselineSub;
299	var $baselineS;
300	var $baselineO;
301
302	var $subPos;
303	var $subArrMB;
304	var $ReqFontStyle;
305	var $tableClipPath;
306
307	var $fullImageHeight;
308
309	var $inFixedPosBlock;  // Internal flag for position:fixed block
310	var $fixedPosBlock;  // Buffer string for position:fixed block
311	var $fixedPosBlockDepth;
312	var $fixedPosBlockBBox;
313	var $fixedPosBlockSave;
314	var $maxPosL;
315	var $maxPosR;
316	var $loaded;
317
318	var $extraFontSubsets;
319
320	var $docTemplateStart;  // Internal flag for page (page no. -1) that docTemplate starts on
321
322	var $time0;
323
324	var $hyphenationDictionaryFile;
325
326	var $spanbgcolorarray;
327	var $default_font;
328	var $headerbuffer;
329	var $lastblocklevelchange;
330	var $nestedtablejustfinished;
331	var $linebreakjustfinished;
332	var $cell_border_dominance_L;
333	var $cell_border_dominance_R;
334	var $cell_border_dominance_T;
335	var $cell_border_dominance_B;
336	var $table_keep_together;
337	var $plainCell_properties;
338	var $shrin_k1;
339	var $outerfilled;
340
341	var $blockContext;
342	var $floatDivs;
343
344	var $patterns;
345	var $pageBackgrounds;
346
347	var $bodyBackgroundGradient;
348	var $bodyBackgroundImage;
349	var $bodyBackgroundColor;
350
351	var $writingHTMLheader; // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
352	var $writingHTMLfooter;
353
354	var $angle;
355
356	var $gradients;
357
358	var $kwt_Reference;
359	var $kwt_BMoutlines;
360	var $kwt_toc;
361
362	var $tbrot_BMoutlines;
363	var $tbrot_toc;
364
365	var $col_BMoutlines;
366	var $col_toc;
367
368	var $floatbuffer;
369	var $floatmargins;
370
371	var $bullet;
372	var $bulletarray;
373
374	var $currentLang;
375	var $default_lang;
376
377	var $default_available_fonts;
378
379	var $pageTemplate;
380	var $docTemplate;
381	var $docTemplateContinue;
382	var $docTemplateContinue2pages;
383
384	var $arabGlyphs;
385	var $arabHex;
386	var $persianGlyphs;
387	var $persianHex;
388	var $arabVowels;
389	var $arabPrevLink;
390	var $arabNextLink;
391
392	var $formobjects; // array of Form Objects for WMF
393	var $InlineProperties;
394	var $InlineAnnots;
395	var $InlineBDF; // mPDF 6 Bidirectional formatting
396	var $InlineBDFctr; // mPDF 6
397
398	var $ktAnnots;
399	var $tbrot_Annots;
400	var $kwt_Annots;
401	var $columnAnnots;
402	var $columnForms;
403	var $tbrotForms;
404
405	var $PageAnnots;
406
407	var $pageDim; // Keep track of page wxh for orientation changes - set in _beginpage, used in _putannots
408
409	var $breakpoints;
410
411	var $tableLevel;
412	var $tbctr;
413	var $innermostTableLevel;
414	var $saveTableCounter;
415	var $cellBorderBuffer;
416
417	var $saveHTMLFooter_height;
418	var $saveHTMLFooterE_height;
419
420	var $firstPageBoxHeader;
421	var $firstPageBoxHeaderEven;
422	var $firstPageBoxFooter;
423	var $firstPageBoxFooterEven;
424
425	var $page_box;
426
427	var $show_marks; // crop or cross marks
428	var $basepathIsLocal;
429
430	var $use_kwt;
431	var $kwt;
432	var $kwt_height;
433	var $kwt_y0;
434	var $kwt_x0;
435	var $kwt_buffer;
436	var $kwt_Links;
437	var $kwt_moved;
438	var $kwt_saved;
439
440	var $PageNumSubstitutions;
441
442	var $table_borders_separate;
443	var $base_table_properties;
444	var $borderstyles;
445
446	var $blockjustfinished;
447
448	var $orig_bMargin;
449	var $orig_tMargin;
450	var $orig_lMargin;
451	var $orig_rMargin;
452	var $orig_hMargin;
453	var $orig_fMargin;
454
455	var $pageHTMLheaders;
456	var $pageHTMLfooters;
457
458	var $saveHTMLHeader;
459	var $saveHTMLFooter;
460
461	var $HTMLheaderPageLinks;
462	var $HTMLheaderPageAnnots;
463	var $HTMLheaderPageForms;
464
465	// See Config\FontVariables for these next 5 values
466	var $available_unifonts;
467	var $sans_fonts;
468	var $serif_fonts;
469	var $mono_fonts;
470	var $defaultSubsFont;
471
472	// List of ALL available CJK fonts (incl. styles) (Adobe add-ons)  hw removed
473	var $available_CJK_fonts;
474
475	var $HTMLHeader;
476	var $HTMLFooter;
477	var $HTMLHeaderE;
478	var $HTMLFooterE;
479	var $bufferoutput;
480
481	// CJK fonts
482	var $Big5_widths;
483	var $GB_widths;
484	var $SJIS_widths;
485	var $UHC_widths;
486
487	// SetProtection
488	var $encrypted;
489
490	var $enc_obj_id; // encryption object id
491
492	// Bookmark
493	var $BMoutlines;
494	var $OutlineRoot;
495
496	// INDEX
497	var $ColActive;
498	var $Reference;
499	var $CurrCol;
500	var $NbCol;
501	var $y0;   // Top ordinate of columns
502
503	var $ColL;
504	var $ColWidth;
505	var $ColGap;
506
507	// COLUMNS
508	var $ColR;
509	var $ChangeColumn;
510	var $columnbuffer;
511	var $ColDetails;
512	var $columnLinks;
513	var $colvAlign;
514
515	// Substitutions
516	var $substitute;  // Array of substitution strings e.g. <ttz>112</ttz>
517	var $entsearch;  // Array of HTML entities (>ASCII 127) to substitute
518	var $entsubstitute; // Array of substitution decimal unicode for the Hi entities
519
520	// Default values if no style sheet offered	(cf. http://www.w3.org/TR/CSS21/sample.html)
521	var $defaultCSS;
522	var $defaultCssFile;
523
524	var $lastoptionaltag; // Save current block item which HTML specifies optionsl endtag
525	var $pageoutput;
526	var $charset_in;
527	var $blk;
528	var $blklvl;
529	var $ColumnAdjust;
530
531	var $ws; // Word spacing
532
533	var $HREF;
534	var $pgwidth;
535	var $fontlist;
536	var $oldx;
537	var $oldy;
538	var $B;
539	var $I;
540
541	var $tdbegin;
542	var $table;
543	var $cell;
544	var $col;
545	var $row;
546
547	var $divbegin;
548	var $divwidth;
549	var $divheight;
550	var $spanbgcolor;
551
552	// mPDF 6 Used for table cell (block-type) properties
553	var $cellTextAlign;
554	var $cellLineHeight;
555	var $cellLineStackingStrategy;
556	var $cellLineStackingShift;
557
558	// mPDF 6  Lists
559	var $listcounter;
560	var $listlvl;
561	var $listtype;
562	var $listitem;
563
564	var $pjustfinished;
565	var $ignorefollowingspaces;
566	var $SMALL;
567	var $BIG;
568	var $dash_on;
569	var $dotted_on;
570
571	var $textbuffer;
572	var $currentfontstyle;
573	var $currentfontfamily;
574	var $currentfontsize;
575	var $colorarray;
576	var $bgcolorarray;
577	var $internallink;
578	var $enabledtags;
579
580	var $lineheight;
581	var $default_lineheight_correction;
582	var $basepath;
583	var $textparam;
584
585	var $specialcontent;
586	var $selectoption;
587	var $objectbuffer;
588
589	// Table Rotation
590	var $table_rotate;
591	var $tbrot_maxw;
592	var $tbrot_maxh;
593	var $tablebuffer;
594	var $tbrot_align;
595	var $tbrot_Links;
596
597	var $keep_block_together; // Keep a Block from page-break-inside: avoid
598
599	var $tbrot_y0;
600	var $tbrot_x0;
601	var $tbrot_w;
602	var $tbrot_h;
603
604	var $mb_enc;
605	var $originalMbEnc;
606	var $originalMbRegexEnc;
607
608	var $directionality;
609
610	var $extgstates; // Used for alpha channel - Transparency (Watermark)
611	var $mgl;
612	var $mgt;
613	var $mgr;
614	var $mgb;
615
616	var $tts;
617	var $ttz;
618	var $tta;
619
620	// Best to alter the below variables using default stylesheet above
621	var $page_break_after_avoid;
622	var $margin_bottom_collapse;
623	var $default_font_size; // in pts
624	var $original_default_font_size; // used to save default sizes when using table default
625	var $original_default_font;
626	var $watermark_font;
627	var $defaultAlign;
628
629	// TABLE
630	var $defaultTableAlign;
631	var $tablethead;
632	var $thead_font_weight;
633	var $thead_font_style;
634	var $thead_font_smCaps;
635	var $thead_valign_default;
636	var $thead_textalign_default;
637	var $tabletfoot;
638	var $tfoot_font_weight;
639	var $tfoot_font_style;
640	var $tfoot_font_smCaps;
641	var $tfoot_valign_default;
642	var $tfoot_textalign_default;
643
644	var $trow_text_rotate;
645
646	var $cellPaddingL;
647	var $cellPaddingR;
648	var $cellPaddingT;
649	var $cellPaddingB;
650	var $table_border_attr_set;
651	var $table_border_css_set;
652
653	var $shrin_k; // factor with which to shrink tables - used internally - do not change
654	var $shrink_this_table_to_fit; // 0 or false to disable; value (if set) gives maximum factor to reduce fontsize
655	var $MarginCorrection; // corrects for OddEven Margins
656	var $margin_footer;
657	var $margin_header;
658
659	var $tabletheadjustfinished;
660	var $usingCoreFont;
661	var $charspacing;
662
663	var $js;
664
665	/**
666	 * Set timeout for cURL
667	 *
668	 * @var int
669	 */
670	var $curlTimeout;
671
672	/**
673	 * Set execution timeout for cURL
674	 *
675	 * @var int
676	 */
677	var $curlExecutionTimeout;
678
679	/**
680	 * Set to true to follow redirects with cURL.
681	 *
682	 * @var bool
683	 */
684	var $curlFollowLocation;
685
686	/**
687	 * Set your own CA certificate store for SSL Certificate verification when using cURL
688	 *
689	 * Useful setting to use on hosts with outdated CA certificates.
690	 *
691	 * Download the latest CA certificate from https://curl.haxx.se/docs/caextract.html
692	 *
693	 * @var string The absolute path to the pem file
694	 */
695	var $curlCaCertificate;
696
697	/**
698	 * Set to true to allow unsafe SSL HTTPS requests.
699	 *
700	 * Can be useful when using CDN with HTTPS and if you don't want to configure settings with SSL certificates.
701	 *
702	 * @var bool
703	 */
704	var $curlAllowUnsafeSslRequests;
705
706	/**
707	 * Set the proxy for cURL.
708	 *
709	 * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html
710	 *
711	 * @var string
712	 */
713	var $curlProxy;
714
715	/**
716	 * Set the proxy auth for cURL.
717	 *
718	 * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXYUSERPWD.html
719	 *
720	 * @var string
721	 */
722	var $curlProxyAuth;
723
724	/**
725	 * Set the User-Agent header in the HTTP requests sent by cURL.
726	 *
727	 * @see https://curl.haxx.se/libcurl/c/CURLOPT_USERAGENT.html
728	 *
729	 * @var string User Agent header
730	 */
731	var $curlUserAgent;
732
733	// Private properties FROM FPDF
734	var $DisplayPreferences;
735	var $flowingBlockAttr;
736
737	var $page; // current page number
738
739	var $n; // current object number
740	var $n_js; // current object number
741
742	var $n_ocg_hidden;
743	var $n_ocg_print;
744	var $n_ocg_view;
745
746	var $offsets; // array of object offsets
747	var $buffer; // buffer holding in-memory PDF
748	var $pages; // array containing pages
749	var $state; // current document state
750	var $compress; // compression flag
751
752	var $DefOrientation; // default orientation
753	var $CurOrientation; // current orientation
754	var $OrientationChanges; // array indicating orientation changes
755
756	var $fwPt;
757	var $fhPt; // dimensions of page format in points
758	var $fw;
759	var $fh; // dimensions of page format in user unit
760	var $wPt;
761	var $hPt; // current dimensions of page in points
762
763	var $w;
764	var $h; // current dimensions of page in user unit
765
766	var $lMargin; // left margin
767	var $tMargin; // top margin
768	var $rMargin; // right margin
769	var $bMargin; // page break margin
770	var $cMarginL; // cell margin Left
771	var $cMarginR; // cell margin Right
772	var $cMarginT; // cell margin Left
773	var $cMarginB; // cell margin Right
774
775	var $DeflMargin; // Default left margin
776	var $DefrMargin; // Default right margin
777
778	var $x;
779	var $y; // current position in user unit for cell positioning
780
781	var $lasth; // height of last cell printed
782	var $LineWidth; // line width in user unit
783
784	var $CoreFonts; // array of standard font names
785	var $fonts; // array of used fonts
786	var $FontFiles; // array of font files
787
788	var $images; // array of used images
789	var $imageVars = []; // array of image vars
790
791	var $PageLinks; // array of links in pages
792	var $links; // array of internal links
793	var $FontFamily; // current font family
794	var $FontStyle; // current font style
795	var $CurrentFont; // current font info
796	var $FontSizePt; // current font size in points
797	var $FontSize; // current font size in user unit
798	var $DrawColor; // commands for drawing color
799	var $FillColor; // commands for filling color
800	var $TextColor; // commands for text color
801	var $ColorFlag; // indicates whether fill and text colors are different
802	var $autoPageBreak; // automatic page breaking
803	var $PageBreakTrigger; // threshold used to trigger page breaks
804	var $InFooter; // flag set when processing footer
805
806	var $InHTMLFooter;
807	var $processingFooter; // flag set when processing footer - added for columns
808	var $processingHeader; // flag set when processing header - added for columns
809	var $ZoomMode; // zoom display mode
810	var $LayoutMode; // layout display mode
811	var $title; // title
812	var $subject; // subject
813	var $author; // author
814	var $keywords; // keywords
815	var $creator; // creator
816
817	var $customProperties; // array of custom document properties
818
819	var $associatedFiles; // associated files (see SetAssociatedFiles below)
820	var $additionalXmpRdf; // additional rdf added in xmp
821
822	var $aliasNbPg; // alias for total number of pages
823	var $aliasNbPgGp; // alias for total number of pages in page group
824
825	var $ispre;
826	var $outerblocktags;
827	var $innerblocktags;
828
829	public $exposeVersion;
830
831	private $preambleWritten = false;
832
833	/**
834	 * @var string
835	 */
836	private $fontDescriptor;
837
838	/**
839	 * @var \Mpdf\Otl
840	 */
841	private $otl;
842
843	/**
844	 * @var \Mpdf\CssManager
845	 */
846	private $cssManager;
847
848	/**
849	 * @var \Mpdf\Gradient
850	 */
851	private $gradient;
852
853	/**
854	 * @var \Mpdf\Image\Bmp
855	 */
856	private $bmp;
857
858	/**
859	 * @var \Mpdf\Image\Wmf
860	 */
861	private $wmf;
862
863	/**
864	 * @var \Mpdf\TableOfContents
865	 */
866	private $tableOfContents;
867
868	/**
869	 * @var \Mpdf\Form
870	 */
871	private $form;
872
873	/**
874	 * @var \Mpdf\DirectWrite
875	 */
876	private $directWrite;
877
878	/**
879	 * @var \Mpdf\Cache
880	 */
881	private $cache;
882
883	/**
884	 * @var \Mpdf\Fonts\FontCache
885	 */
886	private $fontCache;
887
888	/**
889	 * @var \Mpdf\Fonts\FontFileFinder
890	 */
891	private $fontFileFinder;
892
893	/**
894	 * @var \Mpdf\Tag
895	 */
896	private $tag;
897
898	/**
899	 * @var \Mpdf\Barcode
900	 * @todo solve Tag dependency and make private
901	 */
902	public $barcode;
903
904	/**
905	 * @var \Mpdf\QrCode\QrCode
906	 */
907	private $qrcode;
908
909	/**
910	 * @var \Mpdf\SizeConverter
911	 */
912	private $sizeConverter;
913
914	/**
915	 * @var \Mpdf\Color\ColorConverter
916	 */
917	private $colorConverter;
918
919	/**
920	 * @var \Mpdf\Color\ColorModeConverter
921	 */
922	private $colorModeConverter;
923
924	/**
925	 * @var \Mpdf\Color\ColorSpaceRestrictor
926	 */
927	private $colorSpaceRestrictor;
928
929	/**
930	 * @var \Mpdf\Hyphenator
931	 */
932	private $hyphenator;
933
934	/**
935	 * @var \Mpdf\Pdf\Protection
936	 */
937	private $protection;
938
939	/**
940	 * @var \Mpdf\RemoteContentFetcher
941	 */
942	private $remoteContentFetcher;
943
944	/**
945	 * @var \Mpdf\Image\ImageProcessor
946	 */
947	private $imageProcessor;
948
949	/**
950	 * @var \Mpdf\Language\LanguageToFontInterface
951	 */
952	private $languageToFont;
953
954	/**
955	 * @var \Mpdf\Language\ScriptToLanguageInterface
956	 */
957	private $scriptToLanguage;
958
959	/**
960	 * @var \Psr\Log\LoggerInterface
961	 */
962	private $logger;
963
964	/**
965	 * @var \Mpdf\Writer\BaseWriter
966	 */
967	private $writer;
968
969	/**
970	 * @var \Mpdf\Writer\FontWriter
971	 */
972	private $fontWriter;
973
974	/**
975	 * @var \Mpdf\Writer\MetadataWriter
976	 */
977	private $metadataWriter;
978
979	/**
980	 * @var \Mpdf\Writer\ImageWriter
981	 */
982	private $imageWriter;
983
984	/**
985	 * @var \Mpdf\Writer\FormWriter
986	 */
987	private $formWriter;
988
989	/**
990	 * @var \Mpdf\Writer\PageWriter
991	 */
992	private $pageWriter;
993
994	/**
995	 * @var \Mpdf\Writer\BookmarkWriter
996	 */
997	private $bookmarkWriter;
998
999	/**
1000	 * @var \Mpdf\Writer\OptionalContentWriter
1001	 */
1002	private $optionalContentWriter;
1003
1004	/**
1005	 * @var \Mpdf\Writer\ColorWriter
1006	 */
1007	private $colorWriter;
1008
1009	/**
1010	 * @var \Mpdf\Writer\BackgroundWriter
1011	 */
1012	private $backgroundWriter;
1013
1014	/**
1015	 * @var \Mpdf\Writer\JavaScriptWriter
1016	 */
1017	private $javaScriptWriter;
1018
1019	/**
1020	 * @var \Mpdf\Writer\ResourceWriter
1021	 */
1022	private $resourceWriter;
1023
1024	/**
1025	 * @var string[]
1026	 */
1027	private $services;
1028
1029	/**
1030	 * @param mixed[] $config
1031	 */
1032	public function __construct(array $config = [])
1033	{
1034		$this->_dochecks();
1035
1036		list(
1037			$mode,
1038			$format,
1039			$default_font_size,
1040			$default_font,
1041			$mgl,
1042			$mgr,
1043			$mgt,
1044			$mgb,
1045			$mgh,
1046			$mgf,
1047			$orientation
1048		) = $this->initConstructorParams($config);
1049
1050		$this->logger = new NullLogger();
1051
1052		$originalConfig = $config;
1053		$config = $this->initConfig($originalConfig);
1054
1055		$serviceFactory = new ServiceFactory();
1056		$services = $serviceFactory->getServices(
1057			$this,
1058			$this->logger,
1059			$config,
1060			$this->restrictColorSpace,
1061			$this->languageToFont,
1062			$this->scriptToLanguage,
1063			$this->fontDescriptor,
1064			$this->bmp,
1065			$this->directWrite,
1066			$this->wmf
1067		);
1068
1069		$this->services = [];
1070
1071		foreach ($services as $key => $service) {
1072			$this->{$key} = $service;
1073			$this->services[] = $key;
1074		}
1075
1076		$this->time0 = microtime(true);
1077
1078		$this->writingToC = false;
1079
1080		$this->layers = [];
1081		$this->current_layer = 0;
1082		$this->open_layer_pane = false;
1083
1084		$this->visibility = 'visible';
1085
1086		$this->tableBackgrounds = [];
1087		$this->uniqstr = '20110230'; // mPDF 5.7.2
1088		$this->kt_y00 = 0;
1089		$this->kt_p00 = 0;
1090		$this->BMPonly = [];
1091		$this->page = 0;
1092		$this->n = 2;
1093		$this->buffer = '';
1094		$this->objectbuffer = [];
1095		$this->pages = [];
1096		$this->OrientationChanges = [];
1097		$this->state = 0;
1098		$this->fonts = [];
1099		$this->FontFiles = [];
1100		$this->images = [];
1101		$this->links = [];
1102		$this->InFooter = false;
1103		$this->processingFooter = false;
1104		$this->processingHeader = false;
1105		$this->lasth = 0;
1106		$this->FontFamily = '';
1107		$this->FontStyle = '';
1108		$this->FontSizePt = 9;
1109
1110		// Small Caps
1111		$this->inMeter = false;
1112		$this->decimal_offset = 0;
1113
1114		$this->PDFAXwarnings = [];
1115
1116		$this->defTextColor = $this->TextColor = $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true);
1117		$this->defDrawColor = $this->DrawColor = $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true);
1118		$this->defFillColor = $this->FillColor = $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings), true);
1119
1120		$this->upperCase = require __DIR__ . '/../data/upperCase.php';
1121
1122		$this->extrapagebreak = true; // mPDF 6 pagebreaktype
1123
1124		$this->ColorFlag = false;
1125		$this->extgstates = [];
1126
1127		$this->mb_enc = 'windows-1252';
1128		$this->originalMbEnc = mb_internal_encoding();
1129		$this->originalMbRegexEnc = mb_regex_encoding();
1130
1131		$this->directionality = 'ltr';
1132		$this->defaultAlign = 'L';
1133		$this->defaultTableAlign = 'L';
1134
1135		$this->fixedPosBlockSave = [];
1136		$this->extraFontSubsets = 0;
1137
1138		$this->blockContext = 1;
1139		$this->floatDivs = [];
1140		$this->DisplayPreferences = '';
1141
1142		// Tiling patterns used for backgrounds
1143		$this->patterns = [];
1144		$this->pageBackgrounds = [];
1145		$this->gradients = [];
1146
1147		// internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
1148		$this->writingHTMLheader = false;
1149		// internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
1150		$this->writingHTMLfooter = false;
1151
1152		$this->kwt_Reference = [];
1153		$this->kwt_BMoutlines = [];
1154		$this->kwt_toc = [];
1155
1156		$this->tbrot_BMoutlines = [];
1157		$this->tbrot_toc = [];
1158
1159		$this->col_BMoutlines = [];
1160		$this->col_toc = [];
1161
1162		$this->pgsIns = [];
1163		$this->PDFAXwarnings = [];
1164		$this->inlineDisplayOff = false;
1165		$this->lSpacingCSS = '';
1166		$this->wSpacingCSS = '';
1167		$this->fixedlSpacing = false;
1168		$this->minwSpacing = 0;
1169
1170		// Baseline for text
1171		$this->baselineC = 0.35;
1172
1173		// mPDF 5.7.3  inline text-decoration parameters
1174		// Sets default change in baseline for <sup> text as factor of preceeding fontsize
1175		// 0.35 has been recommended; 0.5 matches applications like MS Word
1176		$this->baselineSup = 0.5;
1177
1178		// Sets default change in baseline for <sub> text as factor of preceeding fontsize
1179		$this->baselineSub = -0.2;
1180		// Sets default height for <strike> text as factor of fontsize
1181		$this->baselineS = 0.3;
1182		// Sets default height for overline text as factor of fontsize
1183		$this->baselineO = 1.1;
1184
1185		$this->noImageFile = __DIR__ . '/../data/no_image.jpg';
1186		$this->subPos = 0;
1187
1188		$this->fullImageHeight = false;
1189		$this->floatbuffer = [];
1190		$this->floatmargins = [];
1191		$this->formobjects = []; // array of Form Objects for WMF
1192		$this->InlineProperties = [];
1193		$this->InlineAnnots = [];
1194		$this->InlineBDF = []; // mPDF 6
1195		$this->InlineBDFctr = 0; // mPDF 6
1196		$this->tbrot_Annots = [];
1197		$this->kwt_Annots = [];
1198		$this->columnAnnots = [];
1199		$this->PageLinks = [];
1200		$this->OrientationChanges = [];
1201		$this->pageDim = [];
1202		$this->saveHTMLHeader = [];
1203		$this->saveHTMLFooter = [];
1204		$this->PageAnnots = [];
1205		$this->PageNumSubstitutions = [];
1206		$this->breakpoints = []; // used in columnbuffer
1207		$this->tableLevel = 0;
1208		$this->tbctr = []; // counter for nested tables at each level
1209		$this->page_box = [];
1210		$this->show_marks = ''; // crop or cross marks
1211		$this->kwt = false;
1212		$this->kwt_height = 0;
1213		$this->kwt_y0 = 0;
1214		$this->kwt_x0 = 0;
1215		$this->kwt_buffer = [];
1216		$this->kwt_Links = [];
1217		$this->kwt_moved = false;
1218		$this->kwt_saved = false;
1219		$this->PageNumSubstitutions = [];
1220		$this->base_table_properties = [];
1221		$this->borderstyles = ['inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double'];
1222		$this->tbrot_align = 'C';
1223
1224		$this->pageHTMLheaders = [];
1225		$this->pageHTMLfooters = [];
1226		$this->HTMLheaderPageLinks = [];
1227		$this->HTMLheaderPageAnnots = [];
1228
1229		$this->HTMLheaderPageForms = [];
1230		$this->columnForms = [];
1231		$this->tbrotForms = [];
1232
1233		$this->pageoutput = [];
1234
1235		$this->bufferoutput = false;
1236
1237		$this->encrypted = false;
1238
1239		$this->BMoutlines = [];
1240		$this->ColActive = 0;          // Flag indicating that columns are on (the index is being processed)
1241		$this->Reference = [];    // Array containing the references
1242		$this->CurrCol = 0;               // Current column number
1243		$this->ColL = [0];   // Array of Left pos of columns - absolute - needs Margin correction for Odd-Even
1244		$this->ColR = [0];   // Array of Right pos of columns - absolute pos - needs Margin correction for Odd-Even
1245		$this->ChangeColumn = 0;
1246		$this->columnbuffer = [];
1247		$this->ColDetails = [];  // Keeps track of some column details
1248		$this->columnLinks = [];  // Cross references PageLinks
1249		$this->substitute = [];  // Array of substitution strings e.g. <ttz>112</ttz>
1250		$this->entsearch = [];  // Array of HTML entities (>ASCII 127) to substitute
1251		$this->entsubstitute = []; // Array of substitution decimal unicode for the Hi entities
1252		$this->lastoptionaltag = '';
1253		$this->charset_in = '';
1254		$this->blk = [];
1255		$this->blklvl = 0;
1256		$this->tts = false;
1257		$this->ttz = false;
1258		$this->tta = false;
1259		$this->ispre = false;
1260
1261		$this->checkSIP = false;
1262		$this->checkSMP = false;
1263		$this->checkCJK = false;
1264
1265		$this->page_break_after_avoid = false;
1266		$this->margin_bottom_collapse = false;
1267		$this->tablethead = 0;
1268		$this->tabletfoot = 0;
1269		$this->table_border_attr_set = 0;
1270		$this->table_border_css_set = 0;
1271		$this->shrin_k = 1.0;
1272		$this->shrink_this_table_to_fit = 0;
1273		$this->MarginCorrection = 0;
1274
1275		$this->tabletheadjustfinished = false;
1276		$this->usingCoreFont = false;
1277		$this->charspacing = 0;
1278
1279		$this->autoPageBreak = true;
1280
1281		$this->_setPageSize($format, $orientation);
1282		$this->DefOrientation = $orientation;
1283
1284		$this->margin_header = $mgh;
1285		$this->margin_footer = $mgf;
1286
1287		$bmargin = $mgb;
1288
1289		$this->DeflMargin = $mgl;
1290		$this->DefrMargin = $mgr;
1291
1292		$this->orig_tMargin = $mgt;
1293		$this->orig_bMargin = $bmargin;
1294		$this->orig_lMargin = $this->DeflMargin;
1295		$this->orig_rMargin = $this->DefrMargin;
1296		$this->orig_hMargin = $this->margin_header;
1297		$this->orig_fMargin = $this->margin_footer;
1298
1299		if ($this->setAutoTopMargin == 'pad') {
1300			$mgt += $this->margin_header;
1301		}
1302		if ($this->setAutoBottomMargin == 'pad') {
1303			$mgb += $this->margin_footer;
1304		}
1305
1306		// sets l r t margin
1307		$this->SetMargins($this->DeflMargin, $this->DefrMargin, $mgt);
1308
1309		// Automatic page break
1310		// sets $this->bMargin & PageBreakTrigger
1311		$this->SetAutoPageBreak($this->autoPageBreak, $bmargin);
1312
1313		$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
1314
1315		// Interior cell margin (1 mm) ? not used
1316		$this->cMarginL = 1;
1317		$this->cMarginR = 1;
1318
1319		// Line width (0.2 mm)
1320		$this->LineWidth = .567 / Mpdf::SCALE;
1321
1322		// Enable all tags as default
1323		$this->DisableTags();
1324		// Full width display mode
1325		$this->SetDisplayMode(100); // fullwidth? 'fullpage'
1326
1327		// Compression
1328		$this->SetCompression(true);
1329		// Set default display preferences
1330		$this->SetDisplayPreferences('');
1331
1332		$this->initFontConfig($originalConfig);
1333
1334		// Available fonts
1335		$this->available_unifonts = [];
1336		foreach ($this->fontdata as $f => $fs) {
1337			if (isset($fs['R']) && $fs['R']) {
1338				$this->available_unifonts[] = $f;
1339			}
1340			if (isset($fs['B']) && $fs['B']) {
1341				$this->available_unifonts[] = $f . 'B';
1342			}
1343			if (isset($fs['I']) && $fs['I']) {
1344				$this->available_unifonts[] = $f . 'I';
1345			}
1346			if (isset($fs['BI']) && $fs['BI']) {
1347				$this->available_unifonts[] = $f . 'BI';
1348			}
1349		}
1350
1351		$this->default_available_fonts = $this->available_unifonts;
1352
1353		$optcore = false;
1354		$onlyCoreFonts = false;
1355		if (preg_match('/([\-+])aCJK/i', $mode, $m)) {
1356			$mode = preg_replace('/([\-+])aCJK/i', '', $mode); // mPDF 6
1357			if ($m[1] == '+') {
1358				$this->useAdobeCJK = true;
1359			} else {
1360				$this->useAdobeCJK = false;
1361			}
1362		}
1363
1364		if (strlen($mode) == 1) {
1365			if ($mode == 's') {
1366				$this->percentSubset = 100;
1367				$mode = '';
1368			} elseif ($mode == 'c') {
1369				$onlyCoreFonts = true;
1370				$mode = '';
1371			}
1372		} elseif (substr($mode, -2) == '-s') {
1373			$this->percentSubset = 100;
1374			$mode = substr($mode, 0, strlen($mode) - 2);
1375		} elseif (substr($mode, -2) == '-c') {
1376			$onlyCoreFonts = true;
1377			$mode = substr($mode, 0, strlen($mode) - 2);
1378		} elseif (substr($mode, -2) == '-x') {
1379			$optcore = true;
1380			$mode = substr($mode, 0, strlen($mode) - 2);
1381		}
1382
1383		// Autodetect if mode is a language_country string (en-GB or en_GB or en)
1384		if ($mode && $mode != 'UTF-8') { // mPDF 6
1385			list ($coreSuitable, $mpdf_pdf_unifont) = $this->languageToFont->getLanguageOptions($mode, $this->useAdobeCJK);
1386			if ($coreSuitable && $optcore) {
1387				$onlyCoreFonts = true;
1388			}
1389			if ($mpdf_pdf_unifont) {  // mPDF 6
1390				$default_font = $mpdf_pdf_unifont;
1391			}
1392			$this->currentLang = $mode;
1393			$this->default_lang = $mode;
1394		}
1395
1396		$this->onlyCoreFonts = $onlyCoreFonts;
1397
1398		if ($this->onlyCoreFonts) {
1399			$this->setMBencoding('windows-1252'); // sets $this->mb_enc
1400		} else {
1401			$this->setMBencoding('UTF-8'); // sets $this->mb_enc
1402		}
1403		@mb_regex_encoding('UTF-8'); // required only for mb_ereg... and mb_split functions
1404
1405		// Adobe CJK fonts
1406		$this->available_CJK_fonts = [
1407			'gb',
1408			'big5',
1409			'sjis',
1410			'uhc',
1411			'gbB',
1412			'big5B',
1413			'sjisB',
1414			'uhcB',
1415			'gbI',
1416			'big5I',
1417			'sjisI',
1418			'uhcI',
1419			'gbBI',
1420			'big5BI',
1421			'sjisBI',
1422			'uhcBI',
1423		];
1424
1425		// Standard fonts
1426		$this->CoreFonts = [
1427			'ccourier' => 'Courier',
1428			'ccourierB' => 'Courier-Bold',
1429			'ccourierI' => 'Courier-Oblique',
1430			'ccourierBI' => 'Courier-BoldOblique',
1431			'chelvetica' => 'Helvetica',
1432			'chelveticaB' => 'Helvetica-Bold',
1433			'chelveticaI' => 'Helvetica-Oblique',
1434			'chelveticaBI' => 'Helvetica-BoldOblique',
1435			'ctimes' => 'Times-Roman',
1436			'ctimesB' => 'Times-Bold',
1437			'ctimesI' => 'Times-Italic',
1438			'ctimesBI' => 'Times-BoldItalic',
1439			'csymbol' => 'Symbol',
1440			'czapfdingbats' => 'ZapfDingbats'
1441		];
1442
1443		$this->fontlist = [
1444			"ctimes",
1445			"ccourier",
1446			"chelvetica",
1447			"csymbol",
1448			"czapfdingbats"
1449		];
1450
1451		// Substitutions
1452		$this->setHiEntitySubstitutions();
1453
1454		if ($this->onlyCoreFonts) {
1455			$this->useSubstitutions = true;
1456			$this->SetSubstitutions();
1457		} else {
1458			$this->useSubstitutions = $config['useSubstitutions'];
1459		}
1460
1461		if (file_exists($this->defaultCssFile)) {
1462			$css = file_get_contents($this->defaultCssFile);
1463			$this->cssManager->ReadCSS('<style> ' . $css . ' </style>');
1464		} else {
1465			throw new \Mpdf\MpdfException(sprintf('Unable to read default CSS file "%s"', $this->defaultCssFile));
1466		}
1467
1468		if ($default_font == '') {
1469			if ($this->onlyCoreFonts) {
1470				if (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->mono_fonts)) {
1471					$default_font = 'ccourier';
1472				} elseif (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->sans_fonts)) {
1473					$default_font = 'chelvetica';
1474				} else {
1475					$default_font = 'ctimes';
1476				}
1477			} else {
1478				$default_font = $this->defaultCSS['BODY']['FONT-FAMILY'];
1479			}
1480		}
1481		if (!$default_font_size) {
1482			$mmsize = $this->sizeConverter->convert($this->defaultCSS['BODY']['FONT-SIZE']);
1483			$default_font_size = $mmsize * (Mpdf::SCALE);
1484		}
1485
1486		if ($default_font) {
1487			$this->SetDefaultFont($default_font);
1488		}
1489		if ($default_font_size) {
1490			$this->SetDefaultFontSize($default_font_size);
1491		}
1492
1493		$this->SetLineHeight(); // lineheight is in mm
1494
1495		$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
1496		$this->HREF = '';
1497		$this->oldy = -1;
1498		$this->B = 0;
1499		$this->I = 0;
1500
1501		// mPDF 6  Lists
1502		$this->listlvl = 0;
1503		$this->listtype = [];
1504		$this->listitem = [];
1505		$this->listcounter = [];
1506
1507		$this->tdbegin = false;
1508		$this->table = [];
1509		$this->cell = [];
1510		$this->col = -1;
1511		$this->row = -1;
1512		$this->cellBorderBuffer = [];
1513
1514		$this->divbegin = false;
1515		// mPDF 6
1516		$this->cellTextAlign = '';
1517		$this->cellLineHeight = '';
1518		$this->cellLineStackingStrategy = '';
1519		$this->cellLineStackingShift = '';
1520
1521		$this->divwidth = 0;
1522		$this->divheight = 0;
1523		$this->spanbgcolor = false;
1524		$this->spanborder = false;
1525		$this->spanborddet = [];
1526
1527		$this->blockjustfinished = false;
1528		$this->ignorefollowingspaces = true; // in order to eliminate exceeding left-side spaces
1529		$this->dash_on = false;
1530		$this->dotted_on = false;
1531		$this->textshadow = '';
1532
1533		$this->currentfontfamily = '';
1534		$this->currentfontsize = '';
1535		$this->currentfontstyle = '';
1536		$this->colorarray = ''; // mPDF 6
1537		$this->spanbgcolorarray = ''; // mPDF 6
1538		$this->textbuffer = [];
1539		$this->internallink = [];
1540		$this->basepath = "";
1541
1542		$this->SetBasePath('');
1543
1544		$this->textparam = [];
1545
1546		$this->specialcontent = '';
1547		$this->selectoption = [];
1548	}
1549
1550	public function cleanup()
1551	{
1552		mb_internal_encoding($this->originalMbEnc);
1553		@mb_regex_encoding($this->originalMbRegexEnc);
1554
1555		// this will free up the readers, based on code from Setasign's FpdiTrait::cleanUp()
1556		foreach ($this->createdReaders as $id) {
1557			$this->readers[$id]->getParser()->getStreamReader()->cleanUp();
1558			unset($this->readers[$id]);
1559		}
1560
1561		$this->createdReaders = [];
1562	}
1563
1564	/**
1565	 * @param \Psr\Log\LoggerInterface
1566	 *
1567	 * @return \Mpdf\Mpdf
1568	 */
1569	public function setLogger(LoggerInterface $logger)
1570	{
1571		$this->logger = $logger;
1572
1573		foreach ($this->services as $name) {
1574			if ($this->$name && $this->$name instanceof \Psr\Log\LoggerAwareInterface) {
1575				$this->$name->setLogger($logger);
1576			}
1577		}
1578
1579		return $this;
1580	}
1581
1582	private function initConfig(array $config)
1583	{
1584		$configObject = new ConfigVariables();
1585		$defaults = $configObject->getDefaults();
1586		$config = array_intersect_key($config + $defaults, $defaults);
1587
1588		foreach ($config as $var => $val) {
1589			$this->{$var} = $val;
1590		}
1591
1592		return $config;
1593	}
1594
1595	private function initConstructorParams(array $config)
1596	{
1597		$constructor = [
1598			'mode' => '',
1599			'format' => 'A4',
1600			'default_font_size' => 0,
1601			'default_font' => '',
1602			'margin_left' => 15,
1603			'margin_right' => 15,
1604			'margin_top' => 16,
1605			'margin_bottom' => 16,
1606			'margin_header' => 9,
1607			'margin_footer' => 9,
1608			'orientation' => 'P',
1609		];
1610
1611		foreach ($constructor as $key => $val) {
1612			if (isset($config[$key])) {
1613				$constructor[$key] = $config[$key];
1614			}
1615		}
1616
1617		return array_values($constructor);
1618	}
1619
1620	private function initFontConfig(array $config)
1621	{
1622		$configObject = new FontVariables();
1623		$defaults = $configObject->getDefaults();
1624		$config = array_intersect_key($config + $defaults, $defaults);
1625		foreach ($config as $var => $val) {
1626			$this->{$var} = $val;
1627		}
1628
1629		return $config;
1630	}
1631
1632	function _setPageSize($format, &$orientation)
1633	{
1634		if (is_string($format)) {
1635
1636			if (empty($format)) {
1637				$format = 'A4';
1638			}
1639
1640			// e.g. A4-L = A4 landscape, A4-P = A4 portrait
1641			if (preg_match('/([0-9a-zA-Z]*)-([P,L])/i', $format, $m)) {
1642				$format = $m[1];
1643				$orientation = $m[2];
1644			} elseif (empty($orientation)) {
1645				$orientation = 'P';
1646			}
1647
1648			$format = PageFormat::getSizeFromName($format);
1649
1650			$this->fwPt = $format[0];
1651			$this->fhPt = $format[1];
1652
1653		} else {
1654
1655			if (!$format[0] || !$format[1]) {
1656				throw new \Mpdf\MpdfException('Invalid page format: ' . $format[0] . ' ' . $format[1]);
1657			}
1658
1659			$this->fwPt = $format[0] * Mpdf::SCALE;
1660			$this->fhPt = $format[1] * Mpdf::SCALE;
1661		}
1662
1663		$this->fw = $this->fwPt / Mpdf::SCALE;
1664		$this->fh = $this->fhPt / Mpdf::SCALE;
1665
1666		// Page orientation
1667		$orientation = strtolower($orientation);
1668		if ($orientation === 'p' || $orientation == 'portrait') {
1669			$orientation = 'P';
1670			$this->wPt = $this->fwPt;
1671			$this->hPt = $this->fhPt;
1672		} elseif ($orientation === 'l' || $orientation == 'landscape') {
1673			$orientation = 'L';
1674			$this->wPt = $this->fhPt;
1675			$this->hPt = $this->fwPt;
1676		} else {
1677			throw new \Mpdf\MpdfException('Incorrect orientation: ' . $orientation);
1678		}
1679
1680		$this->CurOrientation = $orientation;
1681
1682		$this->w = $this->wPt / Mpdf::SCALE;
1683		$this->h = $this->hPt / Mpdf::SCALE;
1684	}
1685
1686	function RestrictUnicodeFonts($res)
1687	{
1688		// $res = array of (Unicode) fonts to restrict to: e.g. norasi|norasiB - language specific
1689		if (count($res)) { // Leave full list of available fonts if passed blank array
1690			$this->available_unifonts = $res;
1691		} else {
1692			$this->available_unifonts = $this->default_available_fonts;
1693		}
1694		if (count($this->available_unifonts) == 0) {
1695			$this->available_unifonts[] = $this->default_available_fonts[0];
1696		}
1697		$this->available_unifonts = array_values($this->available_unifonts);
1698	}
1699
1700	function setMBencoding($enc)
1701	{
1702		if ($this->mb_enc != $enc) {
1703			$this->mb_enc = $enc;
1704			mb_internal_encoding($this->mb_enc);
1705		}
1706	}
1707
1708	function SetMargins($left, $right, $top)
1709	{
1710		// Set left, top and right margins
1711		$this->lMargin = $left;
1712		$this->rMargin = $right;
1713		$this->tMargin = $top;
1714	}
1715
1716	function ResetMargins()
1717	{
1718		// ReSet left, top margins
1719		if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P' && $this->CurOrientation == 'L') {
1720			if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
1721				$this->tMargin = $this->orig_rMargin;
1722				$this->bMargin = $this->orig_lMargin;
1723			} else { // ODD	// OR NOT MIRRORING MARGINS/FOOTERS
1724				$this->tMargin = $this->orig_lMargin;
1725				$this->bMargin = $this->orig_rMargin;
1726			}
1727			$this->lMargin = $this->DeflMargin;
1728			$this->rMargin = $this->DefrMargin;
1729			$this->MarginCorrection = 0;
1730			$this->PageBreakTrigger = $this->h - $this->bMargin;
1731		} elseif (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
1732			$this->lMargin = $this->DefrMargin;
1733			$this->rMargin = $this->DeflMargin;
1734			$this->MarginCorrection = $this->DefrMargin - $this->DeflMargin;
1735		} else { // ODD	// OR NOT MIRRORING MARGINS/FOOTERS
1736			$this->lMargin = $this->DeflMargin;
1737			$this->rMargin = $this->DefrMargin;
1738			if ($this->mirrorMargins) {
1739				$this->MarginCorrection = $this->DeflMargin - $this->DefrMargin;
1740			}
1741		}
1742		$this->x = $this->lMargin;
1743	}
1744
1745	function SetLeftMargin($margin)
1746	{
1747		// Set left margin
1748		$this->lMargin = $margin;
1749		if ($this->page > 0 and $this->x < $margin) {
1750			$this->x = $margin;
1751		}
1752	}
1753
1754	function SetTopMargin($margin)
1755	{
1756		// Set top margin
1757		$this->tMargin = $margin;
1758	}
1759
1760	function SetRightMargin($margin)
1761	{
1762		// Set right margin
1763		$this->rMargin = $margin;
1764	}
1765
1766	function SetAutoPageBreak($auto, $margin = 0)
1767	{
1768		// Set auto page break mode and triggering margin
1769		$this->autoPageBreak = $auto;
1770		$this->bMargin = $margin;
1771		$this->PageBreakTrigger = $this->h - $margin;
1772	}
1773
1774	function SetDisplayMode($zoom, $layout = 'continuous')
1775	{
1776		$allowedZoomModes = ['fullpage', 'fullwidth', 'real', 'default', 'none'];
1777
1778		if (in_array($zoom, $allowedZoomModes, true) || is_numeric($zoom)) {
1779			$this->ZoomMode = $zoom;
1780		} else {
1781			throw new \Mpdf\MpdfException('Incorrect zoom display mode: ' . $zoom);
1782		}
1783
1784		$allowedLayoutModes = ['single', 'continuous', 'two', 'twoleft', 'tworight', 'default'];
1785
1786		if (in_array($layout, $allowedLayoutModes, true)) {
1787			$this->LayoutMode = $layout;
1788		} else {
1789			throw new \Mpdf\MpdfException('Incorrect layout display mode: ' . $layout);
1790		}
1791	}
1792
1793	function SetCompression($compress)
1794	{
1795		// Set page compression
1796		if (function_exists('gzcompress')) {
1797			$this->compress = $compress;
1798		} else {
1799			$this->compress = false;
1800		}
1801	}
1802
1803	function SetTitle($title)
1804	{
1805		// Title of document // Arrives as UTF-8
1806		$this->title = $title;
1807	}
1808
1809	function SetSubject($subject)
1810	{
1811		// Subject of document
1812		$this->subject = $subject;
1813	}
1814
1815	function SetAuthor($author)
1816	{
1817		// Author of document
1818		$this->author = $author;
1819	}
1820
1821	function SetKeywords($keywords)
1822	{
1823		// Keywords of document
1824		$this->keywords = $keywords;
1825	}
1826
1827	function SetCreator($creator)
1828	{
1829		// Creator of document
1830		$this->creator = $creator;
1831	}
1832
1833	function AddCustomProperty($key, $value)
1834	{
1835		$this->customProperties[$key] = $value;
1836	}
1837
1838	/**
1839	 * Set one or multiple associated file ("/AF" as required by PDF/A-3)
1840	 *
1841	 * param $files is an array of hash containing:
1842	 *   path: file path on FS
1843	 *   content: file content
1844	 *   name: file name (not necessarily the same as the file on FS)
1845	 *   mime (optional): file mime type (will show up as /Subtype in the PDF)
1846	 *   description (optional): file description
1847	 *   AFRelationship (optional): PDF/A-3 AFRelationship (e.g. "Alternative")
1848	 *
1849	 * e.g. to associate 1 file:
1850	 *     [[
1851	 *         'path' => 'tmp/1234.xml',
1852	 *         'content' => 'file content',
1853	 *         'name' => 'public_name.xml',
1854	 *         'mime' => 'text/xml',
1855	 *         'description' => 'foo',
1856	 *         'AFRelationship' => 'Alternative',
1857	 *     ]]
1858	 *
1859	 * @param mixed[] $files Array of arrays of associated files. See above
1860	 */
1861	function SetAssociatedFiles(array $files)
1862	{
1863		$this->associatedFiles = $files;
1864	}
1865
1866	function SetAdditionalXmpRdf($s)
1867	{
1868		$this->additionalXmpRdf = $s;
1869	}
1870
1871	function SetAnchor2Bookmark($x)
1872	{
1873		$this->anchor2Bookmark = $x;
1874	}
1875
1876	public function AliasNbPages($alias = '{nb}')
1877	{
1878		// Define an alias for total number of pages
1879		$this->aliasNbPg = $alias;
1880	}
1881
1882	public function AliasNbPageGroups($alias = '{nbpg}')
1883	{
1884		// Define an alias for total number of pages in a group
1885		$this->aliasNbPgGp = $alias;
1886	}
1887
1888	function SetAlpha($alpha, $bm = 'Normal', $return = false, $mode = 'B')
1889	{
1890		// alpha: real value from 0 (transparent) to 1 (opaque)
1891		// bm:    blend mode, one of the following:
1892		//          Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn,
1893		//          HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
1894		// set alpha for stroking (CA) and non-stroking (ca) operations
1895		// mode determines F (fill) S (stroke) B (both)
1896		if (($this->PDFA || $this->PDFX) && $alpha != 1) {
1897			if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
1898				$this->PDFAXwarnings[] = "Image opacity must be 100% (Opacity changed to 100%)";
1899			}
1900			$alpha = 1;
1901		}
1902		$a = ['BM' => '/' . $bm];
1903		if ($mode == 'F' || $mode == 'B') {
1904			$a['ca'] = $alpha; // mPDF 5.7.2
1905		}
1906		if ($mode == 'S' || $mode == 'B') {
1907			$a['CA'] = $alpha; // mPDF 5.7.2
1908		}
1909		$gs = $this->AddExtGState($a);
1910		if ($return) {
1911			return sprintf('/GS%d gs', $gs);
1912		} else {
1913			$this->writer->write(sprintf('/GS%d gs', $gs));
1914		}
1915	}
1916
1917	function AddExtGState($parms)
1918	{
1919		$n = count($this->extgstates);
1920		// check if graphics state already exists
1921		for ($i = 1; $i <= $n; $i++) {
1922			if (count($this->extgstates[$i]['parms']) == count($parms)) {
1923				$same = true;
1924				foreach ($this->extgstates[$i]['parms'] as $k => $v) {
1925					if (!isset($parms[$k]) || $parms[$k] != $v) {
1926						$same = false;
1927						break;
1928					}
1929				}
1930				if ($same) {
1931					return $i;
1932				}
1933			}
1934		}
1935		$n++;
1936		$this->extgstates[$n]['parms'] = $parms;
1937		return $n;
1938	}
1939
1940	function SetVisibility($v)
1941	{
1942		if (($this->PDFA || $this->PDFX) && $this->visibility != 'visible') {
1943			$this->PDFAXwarnings[] = "Cannot set visibility to anything other than full when using PDFA or PDFX";
1944			return '';
1945		} elseif (!$this->PDFA && !$this->PDFX) {
1946			$this->pdf_version = '1.5';
1947		}
1948		if ($this->visibility != 'visible') {
1949			$this->writer->write('EMC');
1950			$this->hasOC = intval($this->hasOC);
1951		}
1952		if ($v == 'printonly') {
1953			$this->writer->write('/OC /OC1 BDC');
1954			$this->hasOC = ($this->hasOC | 1);
1955		} elseif ($v == 'screenonly') {
1956			$this->writer->write('/OC /OC2 BDC');
1957			$this->hasOC = ($this->hasOC | 2);
1958		} elseif ($v == 'hidden') {
1959			$this->writer->write('/OC /OC3 BDC');
1960			$this->hasOC = ($this->hasOC | 4);
1961		} elseif ($v != 'visible') {
1962			throw new \Mpdf\MpdfException('Incorrect visibility: ' . $v);
1963		}
1964		$this->visibility = $v;
1965	}
1966
1967	function Open()
1968	{
1969		// Begin document
1970		if ($this->state == 0) {
1971			$this->state = 1;
1972			if (false === $this->preambleWritten) {
1973				$this->writer->write('%PDF-' . $this->pdf_version);
1974				$this->writer->write('%' . chr(226) . chr(227) . chr(207) . chr(211)); // 4 chars > 128 to show binary file
1975				$this->preambleWritten = true;
1976			}
1977		}
1978	}
1979
1980	function Close()
1981	{
1982		// @log Closing last page
1983
1984		// Terminate document
1985		if ($this->state == 3) {
1986			return;
1987		}
1988
1989		if ($this->page == 0) {
1990			$this->AddPage($this->CurOrientation);
1991		}
1992
1993		if (count($this->cellBorderBuffer)) {
1994			$this->printcellbuffer();
1995		}
1996
1997		// *TABLES*
1998		if ($this->tablebuffer) {
1999			$this->printtablebuffer();
2000		}
2001
2002		/* -- COLUMNS -- */
2003
2004		if ($this->ColActive) {
2005			$this->SetColumns(0);
2006			$this->ColActive = 0;
2007			if (count($this->columnbuffer)) {
2008				$this->printcolumnbuffer();
2009			}
2010		}
2011
2012		/* -- END COLUMNS -- */
2013
2014		// BODY Backgrounds
2015		$s = '';
2016
2017		$s .= $this->PrintBodyBackgrounds();
2018		$s .= $this->PrintPageBackgrounds();
2019
2020		$this->pages[$this->page] = preg_replace(
2021			'/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/',
2022			"\n" . $s . "\n" . '\\1',
2023			$this->pages[$this->page]
2024		);
2025
2026		$this->pageBackgrounds = [];
2027
2028		if ($this->visibility != 'visible') {
2029			$this->SetVisibility('visible');
2030		}
2031
2032		$this->EndLayer();
2033
2034		if (!$this->tableOfContents->TOCmark) { // Page footer
2035			$this->InFooter = true;
2036			$this->Footer();
2037			$this->InFooter = false;
2038		}
2039
2040		if ($this->tableOfContents->TOCmark || count($this->tableOfContents->m_TOC)) {
2041			$this->tableOfContents->insertTOC();
2042		}
2043
2044		// Close page
2045		$this->_endpage();
2046
2047		// Close document
2048		$this->_enddoc();
2049	}
2050
2051	/* -- BACKGROUNDS -- */
2052
2053	function _resizeBackgroundImage($imw, $imh, $cw, $ch, $resize, $repx, $repy, $pba = [], $size = [])
2054	{
2055		// pba is background positioning area (from CSS background-origin) may not always be set [x,y,w,h]
2056		// size is from CSS3 background-size - takes precendence over old resize
2057		// $w - absolute length or % or auto or cover | contain
2058		// $h - absolute length or % or auto or cover | contain
2059		if (isset($pba['w'])) {
2060			$cw = $pba['w'];
2061		}
2062		if (isset($pba['h'])) {
2063			$ch = $pba['h'];
2064		}
2065
2066		$cw = $cw * Mpdf::SCALE;
2067		$ch = $ch * Mpdf::SCALE;
2068		if (empty($size) && !$resize) {
2069			return [$imw, $imh, $repx, $repy];
2070		}
2071
2072		if (isset($size['w']) && $size['w']) {
2073			if ($size['w'] == 'contain') {
2074				// Scale the image, while preserving its intrinsic aspect ratio (if any),
2075				// to the largest size such that both its width and its height can fit inside the background positioning area.
2076				// Same as resize==3
2077				$h = $imh * $cw / $imw;
2078				$w = $cw;
2079				if ($h > $ch) {
2080					$w = $w * $ch / $h;
2081					$h = $ch;
2082				}
2083			} elseif ($size['w'] == 'cover') {
2084				// Scale the image, while preserving its intrinsic aspect ratio (if any),
2085				// to the smallest size such that both its width and its height can completely cover the background positioning area.
2086				$h = $imh * $cw / $imw;
2087				$w = $cw;
2088				if ($h < $ch) {
2089					$w = $w * $h / $ch;
2090					$h = $ch;
2091				}
2092			} else {
2093				if (stristr($size['w'], '%')) {
2094					$size['w'] = (float) $size['w'];
2095					$size['w'] /= 100;
2096					$size['w'] = ($cw * $size['w']);
2097				}
2098				if (stristr($size['h'], '%')) {
2099					$size['h'] = (float) $size['h'];
2100					$size['h'] /= 100;
2101					$size['h'] = ($ch * $size['h']);
2102				}
2103				if ($size['w'] == 'auto' && $size['h'] == 'auto') {
2104					$w = $imw;
2105					$h = $imh;
2106				} elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
2107					$w = $imw * $size['h'] / $imh;
2108					$h = $size['h'];
2109				} elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
2110					$h = $imh * $size['w'] / $imw;
2111					$w = $size['w'];
2112				} else {
2113					$w = $size['w'];
2114					$h = $size['h'];
2115				}
2116			}
2117			return [$w, $h, $repx, $repy];
2118		} elseif ($resize == 1 && $imw > $cw) {
2119			$h = $imh * $cw / $imw;
2120			return [$cw, $h, $repx, $repy];
2121		} elseif ($resize == 2 && $imh > $ch) {
2122			$w = $imw * $ch / $imh;
2123			return [$w, $ch, $repx, $repy];
2124		} elseif ($resize == 3) {
2125			$w = $imw;
2126			$h = $imh;
2127			if ($w > $cw) {
2128				$h = $h * $cw / $w;
2129				$w = $cw;
2130			}
2131			if ($h > $ch) {
2132				$w = $w * $ch / $h;
2133				$h = $ch;
2134			}
2135			return [$w, $h, $repx, $repy];
2136		} elseif ($resize == 4) {
2137			$h = $imh * $cw / $imw;
2138			return [$cw, $h, $repx, $repy];
2139		} elseif ($resize == 5) {
2140			$w = $imw * $ch / $imh;
2141			return [$w, $ch, $repx, $repy];
2142		} elseif ($resize == 6) {
2143			return [$cw, $ch, $repx, $repy];
2144		}
2145		return [$imw, $imh, $repx, $repy];
2146	}
2147
2148	function SetBackground(&$properties, &$maxwidth)
2149	{
2150		if (isset($properties['BACKGROUND-ORIGIN']) && ($properties['BACKGROUND-ORIGIN'] == 'border-box' || $properties['BACKGROUND-ORIGIN'] == 'content-box')) {
2151			$origin = $properties['BACKGROUND-ORIGIN'];
2152		} else {
2153			$origin = 'padding-box';
2154		}
2155
2156		if (isset($properties['BACKGROUND-SIZE'])) {
2157			if (stristr($properties['BACKGROUND-SIZE'], 'contain')) {
2158				$bsw = $bsh = 'contain';
2159			} elseif (stristr($properties['BACKGROUND-SIZE'], 'cover')) {
2160				$bsw = $bsh = 'cover';
2161			} else {
2162				$bsw = $bsh = 'auto';
2163				$sz = preg_split('/\s+/', trim($properties['BACKGROUND-SIZE']));
2164				if (count($sz) == 2) {
2165					$bsw = $sz[0];
2166					$bsh = $sz[1];
2167				} else {
2168					$bsw = $sz[0];
2169				}
2170				if (!stristr($bsw, '%') && !stristr($bsw, 'auto')) {
2171					$bsw = $this->sizeConverter->convert($bsw, $maxwidth, $this->FontSize);
2172				}
2173				if (!stristr($bsh, '%') && !stristr($bsh, 'auto')) {
2174					$bsh = $this->sizeConverter->convert($bsh, $maxwidth, $this->FontSize);
2175				}
2176			}
2177			$size = ['w' => $bsw, 'h' => $bsh];
2178		} else {
2179			$size = false;
2180		} // mPDF 6
2181		if (preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $properties['BACKGROUND-IMAGE'])) {
2182			return ['gradient' => $properties['BACKGROUND-IMAGE'], 'origin' => $origin, 'size' => $size];
2183		} else {
2184			$file = $properties['BACKGROUND-IMAGE'];
2185			$sizesarray = $this->Image($file, 0, 0, 0, 0, '', '', false, false, false, false, true);
2186			if (isset($sizesarray['IMAGE_ID'])) {
2187				$image_id = $sizesarray['IMAGE_ID'];
2188				$orig_w = $sizesarray['WIDTH'] * Mpdf::SCALE;  // in user units i.e. mm
2189				$orig_h = $sizesarray['HEIGHT'] * Mpdf::SCALE;  // (using $this->img_dpi)
2190				if (isset($properties['BACKGROUND-IMAGE-RESOLUTION'])) {
2191					if (preg_match('/from-image/i', $properties['BACKGROUND-IMAGE-RESOLUTION']) && isset($sizesarray['set-dpi']) && $sizesarray['set-dpi'] > 0) {
2192						$orig_w *= $this->img_dpi / $sizesarray['set-dpi'];
2193						$orig_h *= $this->img_dpi / $sizesarray['set-dpi'];
2194					} elseif (preg_match('/(\d+)dpi/i', $properties['BACKGROUND-IMAGE-RESOLUTION'], $m)) {
2195						$dpi = $m[1];
2196						if ($dpi > 0) {
2197							$orig_w *= $this->img_dpi / $dpi;
2198							$orig_h *= $this->img_dpi / $dpi;
2199						}
2200					}
2201				}
2202				$x_repeat = true;
2203				$y_repeat = true;
2204				if (isset($properties['BACKGROUND-REPEAT'])) {
2205					if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-x') {
2206						$y_repeat = false;
2207					}
2208					if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-y') {
2209						$x_repeat = false;
2210					}
2211				}
2212				$x_pos = 0;
2213				$y_pos = 0;
2214				if (isset($properties['BACKGROUND-POSITION'])) {
2215					$ppos = preg_split('/\s+/', $properties['BACKGROUND-POSITION']);
2216					$x_pos = $ppos[0];
2217					$y_pos = $ppos[1];
2218					if (!stristr($x_pos, '%')) {
2219						$x_pos = $this->sizeConverter->convert($x_pos, $maxwidth, $this->FontSize);
2220					}
2221					if (!stristr($y_pos, '%')) {
2222						$y_pos = $this->sizeConverter->convert($y_pos, $maxwidth, $this->FontSize);
2223					}
2224				}
2225				if (isset($properties['BACKGROUND-IMAGE-RESIZE'])) {
2226					$resize = $properties['BACKGROUND-IMAGE-RESIZE'];
2227				} else {
2228					$resize = 0;
2229				}
2230				if (isset($properties['BACKGROUND-IMAGE-OPACITY'])) {
2231					$opacity = $properties['BACKGROUND-IMAGE-OPACITY'];
2232				} else {
2233					$opacity = 1;
2234				}
2235				return ['image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $sizesarray['itype'], 'origin' => $origin, 'size' => $size];
2236			}
2237		}
2238		return false;
2239	}
2240
2241	/* -- END BACKGROUNDS -- */
2242
2243	function PrintBodyBackgrounds()
2244	{
2245		$s = '';
2246		$clx = 0;
2247		$cly = 0;
2248		$clw = $this->w;
2249		$clh = $this->h;
2250		// If using bleed and trim margins in paged media
2251		if ($this->pageDim[$this->page]['outer_width_LR'] || $this->pageDim[$this->page]['outer_width_TB']) {
2252			$clx = $this->pageDim[$this->page]['outer_width_LR'] - $this->pageDim[$this->page]['bleedMargin'];
2253			$cly = $this->pageDim[$this->page]['outer_width_TB'] - $this->pageDim[$this->page]['bleedMargin'];
2254			$clw = $this->w - 2 * $clx;
2255			$clh = $this->h - 2 * $cly;
2256		}
2257
2258		if ($this->bodyBackgroundColor) {
2259			$s .= 'q ' . $this->SetFColor($this->bodyBackgroundColor, true) . "\n";
2260			if ($this->bodyBackgroundColor[0] == 5) { // RGBa
2261				$s .= $this->SetAlpha(ord($this->bodyBackgroundColor[4]) / 100, 'Normal', true, 'F') . "\n";
2262			} elseif ($this->bodyBackgroundColor[0] == 6) { // CMYKa
2263				$s .= $this->SetAlpha(ord($this->bodyBackgroundColor[5]) / 100, 'Normal', true, 'F') . "\n";
2264			}
2265			$s .= sprintf('%.3F %.3F %.3F %.3F re f Q', ($clx * Mpdf::SCALE), ($cly * Mpdf::SCALE), $clw * Mpdf::SCALE, $clh * Mpdf::SCALE) . "\n";
2266		}
2267
2268		/* -- BACKGROUNDS -- */
2269		if ($this->bodyBackgroundGradient) {
2270			$g = $this->gradient->parseBackgroundGradient($this->bodyBackgroundGradient);
2271			if ($g) {
2272				$s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, (isset($g['gradtype']) ? $g['gradtype'] : null), $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);
2273			}
2274		}
2275		if ($this->bodyBackgroundImage) {
2276			if (isset($this->bodyBackgroundImage['gradient']) && $this->bodyBackgroundImage['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->bodyBackgroundImage['gradient'])) {
2277				$g = $this->gradient->parseMozGradient($this->bodyBackgroundImage['gradient']);
2278				if ($g) {
2279					$s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);
2280				}
2281			} elseif ($this->bodyBackgroundImage['image_id']) { // Background pattern
2282				$n = count($this->patterns) + 1;
2283				// If using resize, uses TrimBox (not including the bleed)
2284				list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($this->bodyBackgroundImage['orig_w'], $this->bodyBackgroundImage['orig_h'], $clw, $clh, $this->bodyBackgroundImage['resize'], $this->bodyBackgroundImage['x_repeat'], $this->bodyBackgroundImage['y_repeat']);
2285
2286				$this->patterns[$n] = ['x' => $clx, 'y' => $cly, 'w' => $clw, 'h' => $clh, 'pgh' => $this->h, 'image_id' => $this->bodyBackgroundImage['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $this->bodyBackgroundImage['x_pos'], 'y_pos' => $this->bodyBackgroundImage['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $this->bodyBackgroundImage['itype']];
2287				if (($this->bodyBackgroundImage['opacity'] > 0 || $this->bodyBackgroundImage['opacity'] === '0') && $this->bodyBackgroundImage['opacity'] < 1) {
2288					$opac = $this->SetAlpha($this->bodyBackgroundImage['opacity'], 'Normal', true);
2289				} else {
2290					$opac = '';
2291				}
2292				$s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, ($clx * Mpdf::SCALE), ($cly * Mpdf::SCALE), $clw * Mpdf::SCALE, $clh * Mpdf::SCALE) . "\n";
2293			}
2294		}
2295		/* -- END BACKGROUNDS -- */
2296		return $s;
2297	}
2298
2299	function _setClippingPath($clx, $cly, $clw, $clh)
2300	{
2301		$s = ' q 0 w '; // Line width=0
2302		$s .= sprintf('%.3F %.3F m ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // start point TL before the arc
2303		$s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BL
2304		$s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BR
2305		$s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TR
2306		$s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TL
2307		$s .= ' W n '; // Ends path no-op & Sets the clipping path
2308		return $s;
2309	}
2310
2311	function PrintPageBackgrounds($adjustmenty = 0)
2312	{
2313		$s = '';
2314
2315		ksort($this->pageBackgrounds);
2316
2317		foreach ($this->pageBackgrounds as $bl => $pbs) {
2318
2319			foreach ($pbs as $pb) {
2320
2321				if ((!isset($pb['image_id']) && !isset($pb['gradient'])) || isset($pb['shadowonly'])) { // Background colour or boxshadow
2322
2323					if ($pb['z-index'] > 0) {
2324						$this->current_layer = $pb['z-index'];
2325						$s .= "\n" . '/OCBZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n";
2326					}
2327
2328					if ($pb['visibility'] != 'visible') {
2329						if ($pb['visibility'] == 'printonly') {
2330							$s .= '/OC /OC1 BDC' . "\n";
2331						} elseif ($pb['visibility'] == 'screenonly') {
2332							$s .= '/OC /OC2 BDC' . "\n";
2333						} elseif ($pb['visibility'] == 'hidden') {
2334							$s .= '/OC /OC3 BDC' . "\n";
2335						}
2336					}
2337
2338					// Box shadow
2339					if (isset($pb['shadow']) && $pb['shadow']) {
2340						$s .= $pb['shadow'] . "\n";
2341					}
2342
2343					if (isset($pb['clippath']) && $pb['clippath']) {
2344						$s .= $pb['clippath'] . "\n";
2345					}
2346
2347					$s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n";
2348
2349					if ($pb['col'] && $pb['col'][0] === '5') { // RGBa
2350						$s .= $this->SetAlpha(ord($pb['col'][4]) / 100, 'Normal', true, 'F') . "\n";
2351					} elseif ($pb['col'] && $pb['col'][0] === '6') { // CMYKa
2352						$s .= $this->SetAlpha(ord($pb['col'][5]) / 100, 'Normal', true, 'F') . "\n";
2353					}
2354
2355					$s .= sprintf('%.3F %.3F %.3F %.3F re f Q', $pb['x'] * Mpdf::SCALE, ($this->h - $pb['y']) * Mpdf::SCALE, $pb['w'] * Mpdf::SCALE, -$pb['h'] * Mpdf::SCALE) . "\n";
2356
2357					if (isset($pb['clippath']) && $pb['clippath']) {
2358						$s .= 'Q' . "\n";
2359					}
2360
2361					if ($pb['visibility'] != 'visible') {
2362						$s .= 'EMC' . "\n";
2363					}
2364
2365					if ($pb['z-index'] > 0) {
2366						$s .= "\n" . 'EMCBZ-index' . "\n";
2367						$this->current_layer = 0;
2368					}
2369				}
2370			}
2371
2372			/* -- BACKGROUNDS -- */
2373			foreach ($pbs as $pb) {
2374
2375				if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {
2376
2377					if ($pb['z-index'] > 0) {
2378						$this->current_layer = $pb['z-index'];
2379						$s .= "\n" . '/OCGZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n";
2380					}
2381
2382					if ($pb['visibility'] != 'visible') {
2383						if ($pb['visibility'] == 'printonly') {
2384							$s .= '/OC /OC1 BDC' . "\n";
2385						} elseif ($pb['visibility'] == 'screenonly') {
2386							$s .= '/OC /OC2 BDC' . "\n";
2387						} elseif ($pb['visibility'] == 'hidden') {
2388							$s .= '/OC /OC3 BDC' . "\n";
2389						}
2390					}
2391
2392				}
2393
2394				if (isset($pb['gradient']) && $pb['gradient']) {
2395
2396					if (isset($pb['clippath']) && $pb['clippath']) {
2397						$s .= $pb['clippath'] . "\n";
2398					}
2399
2400					$s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);
2401
2402					if (isset($pb['clippath']) && $pb['clippath']) {
2403						$s .= 'Q' . "\n";
2404					}
2405
2406				} elseif (isset($pb['image_id']) && $pb['image_id']) { // Background Image
2407
2408					$pb['y'] -= $adjustmenty;
2409					$pb['h'] += $adjustmenty;
2410					$n = count($this->patterns) + 1;
2411
2412					list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($pb['orig_w'], $pb['orig_h'], $pb['w'], $pb['h'], $pb['resize'], $pb['x_repeat'], $pb['y_repeat'], $pb['bpa'], $pb['size']);
2413
2414					$this->patterns[$n] = ['x' => $pb['x'], 'y' => $pb['y'], 'w' => $pb['w'], 'h' => $pb['h'], 'pgh' => $this->h, 'image_id' => $pb['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $pb['x_pos'], 'y_pos' => $pb['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $pb['itype'], 'bpa' => $pb['bpa']];
2415
2416					$x = $pb['x'] * Mpdf::SCALE;
2417					$y = ($this->h - $pb['y']) * Mpdf::SCALE;
2418					$w = $pb['w'] * Mpdf::SCALE;
2419					$h = -$pb['h'] * Mpdf::SCALE;
2420
2421					if (isset($pb['clippath']) && $pb['clippath']) {
2422						$s .= $pb['clippath'] . "\n";
2423					}
2424
2425					if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern
2426
2427						$iw = $pb['orig_w'] / Mpdf::SCALE;
2428						$ih = $pb['orig_h'] / Mpdf::SCALE;
2429
2430						$w = $pb['w'];
2431						$h = $pb['h'];
2432						$x0 = $pb['x'];
2433						$y0 = $pb['y'];
2434
2435						if (isset($pb['bpa']) && $pb['bpa']) {
2436							$w = $pb['bpa']['w'];
2437							$h = $pb['bpa']['h'];
2438							$x0 = $pb['bpa']['x'];
2439							$y0 = $pb['bpa']['y'];
2440						}
2441
2442						if (isset($pb['size']['w']) && $pb['size']['w']) {
2443							$size = $pb['size'];
2444
2445							if ($size['w'] == 'contain') {
2446								// Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest
2447								// size such that both its width and its height can fit inside the background positioning area.
2448								// Same as resize==3
2449								$ih = $ih * $pb['bpa']['w'] / $iw;
2450								$iw = $pb['bpa']['w'];
2451								if ($ih > $pb['bpa']['h']) {
2452									$iw = $iw * $pb['bpa']['h'] / $ih;
2453									$ih = $pb['bpa']['h'];
2454								}
2455							} elseif ($size['w'] == 'cover') {
2456								// Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest
2457								// size such that both its width and its height can completely cover the background positioning area.
2458								$ih = $ih * $pb['bpa']['w'] / $iw;
2459								$iw = $pb['bpa']['w'];
2460								if ($ih < $pb['bpa']['h']) {
2461									$iw = $iw * $ih / $pb['bpa']['h'];
2462									$ih = $pb['bpa']['h'];
2463								}
2464							} else {
2465
2466								if (NumericString::containsPercentChar($size['w'])) {
2467									$size['w'] = NumericString::removePercentChar($size['w']);
2468									$size['w'] /= 100;
2469									$size['w'] = ($pb['bpa']['w'] * $size['w']);
2470								}
2471
2472								if (NumericString::containsPercentChar($size['h'])) {
2473									$size['h'] = NumericString::removePercentChar($size['h']);
2474									$size['h'] /= 100;
2475									$size['h'] = ($pb['bpa']['h'] * $size['h']);
2476								}
2477
2478								if ($size['w'] == 'auto' && $size['h'] == 'auto') {
2479									$iw = $iw;
2480									$ih = $ih;
2481								} elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
2482									$iw = $iw * $size['h'] / $ih;
2483									$ih = $size['h'];
2484								} elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
2485									$ih = $ih * $size['w'] / $iw;
2486									$iw = $size['w'];
2487								} else {
2488									$iw = $size['w'];
2489									$ih = $size['h'];
2490								}
2491							}
2492						}
2493
2494						// Number to repeat
2495						if ($pb['x_repeat']) {
2496							$nx = ceil($pb['w'] / $iw) + 1;
2497						} else {
2498							$nx = 1;
2499						}
2500
2501						if ($pb['y_repeat']) {
2502							$ny = ceil($pb['h'] / $ih) + 1;
2503						} else {
2504							$ny = 1;
2505						}
2506
2507						$x_pos = $pb['x_pos'];
2508						if (stristr($x_pos, '%')) {
2509							$x_pos = (float) $x_pos;
2510							$x_pos /= 100;
2511							$x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
2512						}
2513
2514						$y_pos = $pb['y_pos'];
2515
2516						if (stristr($y_pos, '%')) {
2517							$y_pos = (float) $y_pos;
2518							$y_pos /= 100;
2519							$y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
2520						}
2521
2522						if ($nx > 1) {
2523							while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {
2524								$x_pos -= $iw;
2525							}
2526						}
2527
2528						if ($ny > 1) {
2529							while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {
2530								$y_pos -= $ih;
2531							}
2532						}
2533
2534						for ($xi = 0; $xi < $nx; $xi++) {
2535							for ($yi = 0; $yi < $ny; $yi++) {
2536								$x = $x0 + $x_pos + ($iw * $xi);
2537								$y = $y0 + $y_pos + ($ih * $yi);
2538								if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {
2539									$opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
2540								} else {
2541									$opac = '';
2542								}
2543								$s .= sprintf("q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $opac, $iw * Mpdf::SCALE, $ih * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $ih)) * Mpdf::SCALE, $pb['image_id']) . "\n";
2544							}
2545						}
2546
2547					} else {
2548						if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {
2549							$opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
2550						} else {
2551							$opac = '';
2552						}
2553						$s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n";
2554					}
2555
2556					if (isset($pb['clippath']) && $pb['clippath']) {
2557						$s .= 'Q' . "\n";
2558					}
2559				}
2560
2561				if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {
2562					if ($pb['visibility'] != 'visible') {
2563						$s .= 'EMC' . "\n";
2564					}
2565
2566					if ($pb['z-index'] > 0) {
2567						$s .= "\n" . 'EMCGZ-index' . "\n";
2568						$this->current_layer = 0;
2569					}
2570				}
2571			}
2572			/* -- END BACKGROUNDS -- */
2573		}
2574
2575		return $s;
2576	}
2577
2578	function PrintTableBackgrounds($adjustmenty = 0)
2579	{
2580		$s = '';
2581		/* -- BACKGROUNDS -- */
2582		ksort($this->tableBackgrounds);
2583		foreach ($this->tableBackgrounds as $bl => $pbs) {
2584			foreach ($pbs as $pb) {
2585				if ((!isset($pb['gradient']) || !$pb['gradient']) && (!isset($pb['image_id']) || !$pb['image_id'])) {
2586					$s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n";
2587					if ($pb['col'][0] == 5) { // RGBa
2588						$s .= $this->SetAlpha(ord($pb['col'][4]) / 100, 'Normal', true, 'F') . "\n";
2589					} elseif ($pb['col'][0] == 6) { // CMYKa
2590						$s .= $this->SetAlpha(ord($pb['col'][5]) / 100, 'Normal', true, 'F') . "\n";
2591					}
2592					$s .= sprintf('%.3F %.3F %.3F %.3F re %s Q', $pb['x'] * Mpdf::SCALE, ($this->h - $pb['y']) * Mpdf::SCALE, $pb['w'] * Mpdf::SCALE, -$pb['h'] * Mpdf::SCALE, 'f') . "\n";
2593				}
2594				if (isset($pb['gradient']) && $pb['gradient']) {
2595					if (isset($pb['clippath']) && $pb['clippath']) {
2596						$s .= $pb['clippath'] . "\n";
2597					}
2598					$s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);
2599					if (isset($pb['clippath']) && $pb['clippath']) {
2600						$s .= 'Q' . "\n";
2601					}
2602				}
2603				if (isset($pb['image_id']) && $pb['image_id']) { // Background pattern
2604					$pb['y'] -= $adjustmenty;
2605					$pb['h'] += $adjustmenty;
2606					$n = count($this->patterns) + 1;
2607					list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($pb['orig_w'], $pb['orig_h'], $pb['w'], $pb['h'], $pb['resize'], $pb['x_repeat'], $pb['y_repeat']);
2608					$this->patterns[$n] = ['x' => $pb['x'], 'y' => $pb['y'], 'w' => $pb['w'], 'h' => $pb['h'], 'pgh' => $this->h, 'image_id' => $pb['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $pb['x_pos'], 'y_pos' => $pb['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $pb['itype']];
2609					$x = $pb['x'] * Mpdf::SCALE;
2610					$y = ($this->h - $pb['y']) * Mpdf::SCALE;
2611					$w = $pb['w'] * Mpdf::SCALE;
2612					$h = -$pb['h'] * Mpdf::SCALE;
2613
2614					// mPDF 5.7.3
2615					if (($this->writingHTMLfooter || $this->writingHTMLheader) && (!isset($pb['clippath']) || $pb['clippath'] == '')) {
2616						// Set clipping path
2617						$pb['clippath'] = sprintf(' q 0 w %.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l W n ', $x, $y, $x, $y + $h, $x + $w, $y + $h, $x + $w, $y, $x, $y);
2618					}
2619
2620					if (isset($pb['clippath']) && $pb['clippath']) {
2621						$s .= $pb['clippath'] . "\n";
2622					}
2623
2624					// mPDF 5.7.3
2625					if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern
2626						$iw = $pb['orig_w'] / Mpdf::SCALE;
2627						$ih = $pb['orig_h'] / Mpdf::SCALE;
2628
2629						$w = $pb['w'];
2630						$h = $pb['h'];
2631						$x0 = $pb['x'];
2632						$y0 = $pb['y'];
2633
2634						if (isset($pb['bpa']) && $pb['bpa']) {
2635							$w = $pb['bpa']['w'];
2636							$h = $pb['bpa']['h'];
2637							$x0 = $pb['bpa']['x'];
2638							$y0 = $pb['bpa']['y'];
2639						} // At present 'bpa' (background page area) is not set for tablebackgrounds - only pagebackgrounds
2640						// For now, just set it as:
2641						else {
2642							$pb['bpa'] = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h];
2643						}
2644
2645						if (isset($pb['size']['w']) && $pb['size']['w']) {
2646							$size = $pb['size'];
2647
2648							if ($size['w'] == 'contain') {
2649								// Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area.
2650								// Same as resize==3
2651								$ih = $ih * $pb['bpa']['w'] / $iw;
2652								$iw = $pb['bpa']['w'];
2653								if ($ih > $pb['bpa']['h']) {
2654									$iw = $iw * $pb['bpa']['h'] / $ih;
2655									$ih = $pb['bpa']['h'];
2656								}
2657							} elseif ($size['w'] == 'cover') {
2658								// Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area.
2659								$ih = $ih * $pb['bpa']['w'] / $iw;
2660								$iw = $pb['bpa']['w'];
2661								if ($ih < $pb['bpa']['h']) {
2662									$iw = $iw * $ih / $pb['bpa']['h'];
2663									$ih = $pb['bpa']['h'];
2664								}
2665							} else {
2666								if (NumericString::containsPercentChar($size['w'])) {
2667									$size['w'] = NumericString::removePercentChar($size['w']);
2668									$size['w'] /= 100;
2669									$size['w'] = ($pb['bpa']['w'] * $size['w']);
2670								}
2671								if (NumericString::containsPercentChar($size['h'])) {
2672									$size['h'] = NumericString::removePercentChar($size['h']);
2673									$size['h'] /= 100;
2674									$size['h'] = ($pb['bpa']['h'] * $size['h']);
2675								}
2676								if ($size['w'] == 'auto' && $size['h'] == 'auto') {
2677									$iw = $iw;
2678									$ih = $ih;
2679								} elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
2680									$iw = $iw * $size['h'] / $ih;
2681									$ih = $size['h'];
2682								} elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
2683									$ih = $ih * $size['w'] / $iw;
2684									$iw = $size['w'];
2685								} else {
2686									$iw = $size['w'];
2687									$ih = $size['h'];
2688								}
2689							}
2690						}
2691
2692						// Number to repeat
2693						if (isset($pb['x_repeat']) && $pb['x_repeat']) {
2694							$nx = ceil($pb['w'] / $iw) + 1;
2695						} else {
2696							$nx = 1;
2697						}
2698						if (isset($pb['y_repeat']) && $pb['y_repeat']) {
2699							$ny = ceil($pb['h'] / $ih) + 1;
2700						} else {
2701							$ny = 1;
2702						}
2703
2704						$x_pos = $pb['x_pos'];
2705						if (NumericString::containsPercentChar($x_pos)) {
2706							$x_pos = NumericString::removePercentChar($x_pos);
2707							$x_pos /= 100;
2708							$x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
2709						}
2710						$y_pos = $pb['y_pos'];
2711						if (NumericString::containsPercentChar($y_pos)) {
2712							$y_pos = NumericString::removePercentChar($y_pos);
2713							$y_pos /= 100;
2714							$y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
2715						}
2716						if ($nx > 1) {
2717							while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {
2718								$x_pos -= $iw;
2719							}
2720						}
2721						if ($ny > 1) {
2722							while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {
2723								$y_pos -= $ih;
2724							}
2725						}
2726						for ($xi = 0; $xi < $nx; $xi++) {
2727							for ($yi = 0; $yi < $ny; $yi++) {
2728								$x = $x0 + $x_pos + ($iw * $xi);
2729								$y = $y0 + $y_pos + ($ih * $yi);
2730								if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {
2731									$opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
2732								} else {
2733									$opac = '';
2734								}
2735								$s .= sprintf("q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $opac, $iw * Mpdf::SCALE, $ih * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $ih)) * Mpdf::SCALE, $pb['image_id']) . "\n";
2736							}
2737						}
2738					} else {
2739						if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {
2740							$opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
2741						} else {
2742							$opac = '';
2743						}
2744						$s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n";
2745					}
2746
2747					if (isset($pb['clippath']) && $pb['clippath']) {
2748						$s .= 'Q' . "\n";
2749					}
2750				}
2751			}
2752		}
2753		/* -- END BACKGROUNDS -- */
2754		return $s;
2755	}
2756
2757	function BeginLayer($id)
2758	{
2759		if ($this->current_layer > 0) {
2760			$this->EndLayer();
2761		}
2762		if ($id < 1) {
2763			return false;
2764		}
2765		if (!isset($this->layers[$id])) {
2766			$this->layers[$id] = ['name' => 'Layer ' . ($id)];
2767			if (($this->PDFA || $this->PDFX)) {
2768				$this->PDFAXwarnings[] = "Cannot use layers when using PDFA or PDFX";
2769				return '';
2770			} elseif (!$this->PDFA && !$this->PDFX) {
2771				$this->pdf_version = '1.5';
2772			}
2773		}
2774		$this->current_layer = $id;
2775		$this->writer->write('/OCZ-index /ZI' . $id . ' BDC');
2776
2777		$this->pageoutput[$this->page] = [];
2778	}
2779
2780	function EndLayer()
2781	{
2782		if ($this->current_layer > 0) {
2783			$this->writer->write('EMCZ-index');
2784			$this->current_layer = 0;
2785		}
2786	}
2787
2788	function AddPageByArray($a)
2789	{
2790		if (!is_array($a)) {
2791			$a = [];
2792		}
2793
2794		$orientation = (isset($a['orientation']) ? $a['orientation'] : '');
2795		$condition = (isset($a['condition']) ? $a['condition'] : (isset($a['type']) ? $a['type'] : ''));
2796		$resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');
2797		$pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');
2798		$suppress = (isset($a['suppress']) ? $a['suppress'] : '');
2799		$mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));
2800		$mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));
2801		$mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));
2802		$mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));
2803		$mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));
2804		$mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));
2805		$ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));
2806		$ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));
2807		$ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));
2808		$efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));
2809		$ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));
2810		$ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));
2811		$ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));
2812		$efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));
2813		$pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));
2814		$newformat = (isset($a['newformat']) ? $a['newformat'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));
2815
2816		$this->AddPage($orientation, $condition, $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);
2817	}
2818
2819	// mPDF 6 pagebreaktype
2820	function _preForcedPagebreak($pagebreaktype)
2821	{
2822		if ($pagebreaktype == 'cloneall') {
2823			// Close any open block tags
2824			$arr = [];
2825			$ai = 0;
2826			for ($b = $this->blklvl; $b > 0; $b--) {
2827				$this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
2828			}
2829			if ($this->blklvl == 0 && !empty($this->textbuffer)) { // Output previously buffered content
2830				$this->printbuffer($this->textbuffer, 1);
2831				$this->textbuffer = [];
2832			}
2833		} elseif ($pagebreaktype == 'clonebycss') {
2834			// Close open block tags whilst box-decoration-break==clone
2835			$arr = [];
2836			$ai = 0;
2837			for ($b = $this->blklvl; $b > 0; $b--) {
2838				if (isset($this->blk[$b]['box_decoration_break']) && $this->blk[$b]['box_decoration_break'] == 'clone') {
2839					$this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
2840				} else {
2841					if ($b == $this->blklvl && !empty($this->textbuffer)) { // Output previously buffered content
2842						$this->printbuffer($this->textbuffer, 1);
2843						$this->textbuffer = [];
2844					}
2845					break;
2846				}
2847			}
2848		} elseif (!empty($this->textbuffer)) { // Output previously buffered content
2849			$this->printbuffer($this->textbuffer, 1);
2850			$this->textbuffer = [];
2851		}
2852	}
2853
2854	// mPDF 6 pagebreaktype
2855	function _postForcedPagebreak($pagebreaktype, $startpage, $save_blk, $save_blklvl)
2856	{
2857		if ($pagebreaktype == 'cloneall') {
2858			$this->blk = [];
2859			$this->blk[0] = $save_blk[0];
2860			// Re-open block tags
2861			$this->blklvl = 0;
2862			$arr = [];
2863			$i = 0;
2864			for ($b = 1; $b <= $save_blklvl; $b++) {
2865				$this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);
2866			}
2867		} elseif ($pagebreaktype == 'clonebycss') {
2868			$this->blk = [];
2869			$this->blk[0] = $save_blk[0];
2870			// Don't re-open tags for lowest level elements - so need to do some adjustments
2871			for ($b = 1; $b <= $this->blklvl; $b++) {
2872				$this->blk[$b] = $save_blk[$b];
2873				$this->blk[$b]['startpage'] = 0;
2874				$this->blk[$b]['y0'] = $this->y; // ?? $this->tMargin
2875				if (($this->page - $startpage) % 2) {
2876					if (isset($this->blk[$b]['x0'])) {
2877						$this->blk[$b]['x0'] += $this->MarginCorrection;
2878					} else {
2879						$this->blk[$b]['x0'] = $this->MarginCorrection;
2880					}
2881				}
2882				// for Float DIV
2883				$this->blk[$b]['marginCorrected'][$this->page] = true;
2884			}
2885
2886			// Re-open block tags for any that have box_decoration_break==clone
2887			$arr = [];
2888			$i = 0;
2889			for ($b = $this->blklvl + 1; $b <= $save_blklvl; $b++) {
2890				if ($b < $this->blklvl) {
2891					$this->lastblocklevelchange = -1;
2892				}
2893				$this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);
2894			}
2895			if ($this->blk[$this->blklvl]['box_decoration_break'] != 'clone') {
2896				$this->lastblocklevelchange = -1;
2897			}
2898		} else {
2899			$this->lastblocklevelchange = -1;
2900		}
2901	}
2902
2903	function AddPage(
2904		$orientation = '',
2905		$condition = '',
2906		$resetpagenum = '',
2907		$pagenumstyle = '',
2908		$suppress = '',
2909		$mgl = '',
2910		$mgr = '',
2911		$mgt = '',
2912		$mgb = '',
2913		$mgh = '',
2914		$mgf = '',
2915		$ohname = '',
2916		$ehname = '',
2917		$ofname = '',
2918		$efname = '',
2919		$ohvalue = 0,
2920		$ehvalue = 0,
2921		$ofvalue = 0,
2922		$efvalue = 0,
2923		$pagesel = '',
2924		$newformat = ''
2925	) {
2926		/* -- CSS-FLOAT -- */
2927		// Float DIV
2928		// Cannot do with columns on, or if any change in page orientation/margins etc.
2929		// If next page already exists - i.e background /headers and footers already written
2930		if ($this->state > 0 && $this->page < count($this->pages)) {
2931			$bak_cml = $this->cMarginL;
2932			$bak_cmr = $this->cMarginR;
2933			$bak_dw = $this->divwidth;
2934			// Paint Div Border if necessary
2935			if ($this->blklvl > 0) {
2936				$save_tr = $this->table_rotate; // *TABLES*
2937				$this->table_rotate = 0; // *TABLES*
2938				if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0']) {
2939					$this->blk[$this->blklvl]['startpage'] ++;
2940				}
2941				if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table']) {
2942					$toplvl = $this->blklvl;
2943				} else {
2944					$toplvl = $this->blklvl - 1;
2945				}
2946				$sy = $this->y;
2947				for ($bl = 1; $bl <= $toplvl; $bl++) {
2948					$this->PaintDivBB('pagebottom', 0, $bl);
2949				}
2950				$this->y = $sy;
2951				$this->table_rotate = $save_tr; // *TABLES*
2952			}
2953			$s = $this->PrintPageBackgrounds();
2954
2955			// Writes after the marker so not overwritten later by page background etc.
2956			$this->pages[$this->page] = preg_replace(
2957				'/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/',
2958				'\\1' . "\n" . $s . "\n",
2959				$this->pages[$this->page]
2960			);
2961
2962			$this->pageBackgrounds = [];
2963			$family = $this->FontFamily;
2964			$style = $this->FontStyle;
2965			$size = $this->FontSizePt;
2966			$lw = $this->LineWidth;
2967			$dc = $this->DrawColor;
2968			$fc = $this->FillColor;
2969			$tc = $this->TextColor;
2970			$cf = $this->ColorFlag;
2971
2972			$this->printfloatbuffer();
2973
2974			// Move to next page
2975			$this->page++;
2976
2977			$this->ResetMargins();
2978			$this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
2979			$this->x = $this->lMargin;
2980			$this->y = $this->tMargin;
2981			$this->FontFamily = '';
2982			$this->writer->write('2 J');
2983			$this->LineWidth = $lw;
2984			$this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));
2985
2986			if ($family) {
2987				$this->SetFont($family, $style, $size, true, true);
2988			}
2989
2990			$this->DrawColor = $dc;
2991
2992			if ($dc != $this->defDrawColor) {
2993				$this->writer->write($dc);
2994			}
2995
2996			$this->FillColor = $fc;
2997
2998			if ($fc != $this->defFillColor) {
2999				$this->writer->write($fc);
3000			}
3001
3002			$this->TextColor = $tc;
3003			$this->ColorFlag = $cf;
3004
3005			for ($bl = 1; $bl <= $this->blklvl; $bl++) {
3006				$this->blk[$bl]['y0'] = $this->y;
3007				// Don't correct more than once for background DIV containing a Float
3008				if (!isset($this->blk[$bl]['marginCorrected'][$this->page])) {
3009					if (isset($this->blk[$bl]['x0'])) {
3010						$this->blk[$bl]['x0'] += $this->MarginCorrection;
3011					} else {
3012						$this->blk[$bl]['x0'] = $this->MarginCorrection;
3013					}
3014				}
3015				$this->blk[$bl]['marginCorrected'][$this->page] = true;
3016			}
3017
3018			$this->cMarginL = $bak_cml;
3019			$this->cMarginR = $bak_cmr;
3020			$this->divwidth = $bak_dw;
3021
3022			return '';
3023		}
3024		/* -- END CSS-FLOAT -- */
3025
3026		// Start a new page
3027		if ($this->state == 0) {
3028			$this->Open();
3029		}
3030
3031		$bak_cml = $this->cMarginL;
3032		$bak_cmr = $this->cMarginR;
3033		$bak_dw = $this->divwidth;
3034
3035		$bak_lh = $this->lineheight;
3036
3037		$orientation = substr(strtoupper($orientation), 0, 1);
3038		$condition = strtoupper($condition);
3039
3040
3041		if ($condition == 'E') { // only adds new page if needed to create an Even page
3042			if (!$this->mirrorMargins || ($this->page) % 2 == 0) {
3043				return false;
3044			}
3045		} elseif ($condition == 'O') { // only adds new page if needed to create an Odd page
3046			if (!$this->mirrorMargins || ($this->page) % 2 == 1) {
3047				return false;
3048			}
3049		} elseif ($condition == 'NEXT-EVEN') { // always adds at least one new page to create an Even page
3050			if (!$this->mirrorMargins) {
3051				$condition = '';
3052			} else {
3053				if ($pagesel) {
3054					$pbch = $pagesel;
3055					$pagesel = '';
3056				} // *CSS-PAGE*
3057				else {
3058					$pbch = false;
3059				} // *CSS-PAGE*
3060				$this->AddPage($this->CurOrientation, 'O');
3061				$this->extrapagebreak = true; // mPDF 6 pagebreaktype
3062				if ($pbch) {
3063					$pagesel = $pbch;
3064				} // *CSS-PAGE*
3065				$condition = '';
3066			}
3067		} elseif ($condition == 'NEXT-ODD') { // always adds at least one new page to create an Odd page
3068			if (!$this->mirrorMargins) {
3069				$condition = '';
3070			} else {
3071				if ($pagesel) {
3072					$pbch = $pagesel;
3073					$pagesel = '';
3074				} // *CSS-PAGE*
3075				else {
3076					$pbch = false;
3077				} // *CSS-PAGE*
3078				$this->AddPage($this->CurOrientation, 'E');
3079				$this->extrapagebreak = true; // mPDF 6 pagebreaktype
3080				if ($pbch) {
3081					$pagesel = $pbch;
3082				} // *CSS-PAGE*
3083				$condition = '';
3084			}
3085		}
3086
3087		if ($resetpagenum || $pagenumstyle || $suppress) {
3088			$this->PageNumSubstitutions[] = ['from' => ($this->page + 1), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
3089		}
3090
3091		$save_tr = $this->table_rotate; // *TABLES*
3092		$this->table_rotate = 0; // *TABLES*
3093		$save_kwt = $this->kwt;
3094		$this->kwt = 0;
3095		$save_layer = $this->current_layer;
3096		$save_vis = $this->visibility;
3097
3098		if ($this->visibility != 'visible') {
3099			$this->SetVisibility('visible');
3100		}
3101
3102		$this->EndLayer();
3103
3104		// Paint Div Border if necessary
3105		// PAINTS BACKGROUND COLOUR OR BORDERS for DIV - DISABLED FOR COLUMNS (cf. AcceptPageBreak) AT PRESENT in ->PaintDivBB
3106		if (!$this->ColActive && $this->blklvl > 0) {
3107			if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0'] && !$this->extrapagebreak) { // mPDF 6 pagebreaktype
3108				if (isset($this->blk[$this->blklvl]['startpage'])) {
3109					$this->blk[$this->blklvl]['startpage'] ++;
3110				} else {
3111					$this->blk[$this->blklvl]['startpage'] = 1;
3112				}
3113			}
3114			if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table'] || $this->extrapagebreak) {
3115				$toplvl = $this->blklvl;
3116			} // mPDF 6 pagebreaktype
3117			else {
3118				$toplvl = $this->blklvl - 1;
3119			}
3120			$sy = $this->y;
3121			for ($bl = 1; $bl <= $toplvl; $bl++) {
3122				if (isset($this->blk[$bl]['z-index']) && $this->blk[$bl]['z-index'] > 0) {
3123					$this->BeginLayer($this->blk[$bl]['z-index']);
3124				}
3125				if (isset($this->blk[$bl]['visibility']) && $this->blk[$bl]['visibility'] && $this->blk[$bl]['visibility'] != 'visible') {
3126					$this->SetVisibility($this->blk[$bl]['visibility']);
3127				}
3128				$this->PaintDivBB('pagebottom', 0, $bl);
3129			}
3130			$this->y = $sy;
3131			// RESET block y0 and x0 - see below
3132		}
3133		$this->extrapagebreak = false; // mPDF 6 pagebreaktype
3134
3135		if ($this->visibility != 'visible') {
3136			$this->SetVisibility('visible');
3137		}
3138
3139		$this->EndLayer();
3140
3141		// BODY Backgrounds
3142		if ($this->page > 0) {
3143			$s = '';
3144			$s .= $this->PrintBodyBackgrounds();
3145
3146			$s .= $this->PrintPageBackgrounds();
3147			$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $s . "\n" . '\\1', $this->pages[$this->page]);
3148			$this->pageBackgrounds = [];
3149		}
3150
3151		$save_kt = $this->keep_block_together;
3152		$this->keep_block_together = 0;
3153
3154		$save_cols = false;
3155
3156		/* -- COLUMNS -- */
3157		if ($this->ColActive) {
3158			$save_cols = true;
3159			$save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off
3160			$this->SetColumns(0);
3161		}
3162		/* -- END COLUMNS -- */
3163
3164		$family = $this->FontFamily;
3165		$style = $this->FontStyle;
3166		$size = $this->FontSizePt;
3167		$this->ColumnAdjust = true; // enables column height adjustment for the page
3168		$lw = $this->LineWidth;
3169		$dc = $this->DrawColor;
3170		$fc = $this->FillColor;
3171		$tc = $this->TextColor;
3172		$cf = $this->ColorFlag;
3173		if ($this->page > 0) {
3174			// Page footer
3175			$this->InFooter = true;
3176
3177			$this->Reset();
3178			$this->pageoutput[$this->page] = [];
3179
3180			$this->Footer();
3181			// Close page
3182			$this->_endpage();
3183		}
3184
3185		// Start new page
3186		$pageBeforeNewPage = $this->page;
3187		$this->_beginpage($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);
3188		$isNewPage = $pageBeforeNewPage !== $this->page;
3189
3190		if ($this->docTemplate) {
3191			$currentReaderId = $this->currentReaderId;
3192
3193			$pagecount = $this->setSourceFile($this->docTemplate);
3194			if (($this->page - $this->docTemplateStart) > $pagecount) {
3195				if ($this->docTemplateContinue) {
3196					if ($this->docTemplateContinue2pages && $pagecount >= 2 && (0 === $this->page % 2)) {
3197						$tplIdx = $this->importPage(($pagecount - 1));
3198						$this->useTemplate($tplIdx);
3199					} else {
3200						$tplIdx = $this->importPage($pagecount);
3201						$this->useTemplate($tplIdx);
3202					}
3203				}
3204			} else {
3205				$tplIdx = $this->importPage(($this->page - $this->docTemplateStart));
3206				$this->useTemplate($tplIdx);
3207			}
3208
3209			$this->currentReaderId = $currentReaderId;
3210		}
3211
3212		if ($this->pageTemplate) {
3213			$this->useTemplate($this->pageTemplate);
3214		}
3215
3216		// Only add the headers if it's a new page
3217		if ($isNewPage) {
3218			// Tiling Patterns
3219			$this->writer->write('___PAGE___START' . $this->uniqstr);
3220			$this->writer->write('___BACKGROUND___PATTERNS' . $this->uniqstr);
3221			$this->writer->write('___HEADER___MARKER' . $this->uniqstr);
3222		}
3223
3224		$this->pageBackgrounds = [];
3225
3226		// Set line cap style to square
3227		$this->SetLineCap(2);
3228		// Set line width
3229		$this->LineWidth = $lw;
3230		$this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));
3231		// Set font
3232		if ($family) {
3233			$this->SetFont($family, $style, $size, true, true); // forces write
3234		}
3235
3236		// Set colors
3237		$this->DrawColor = $dc;
3238		if ($dc != $this->defDrawColor) {
3239			$this->writer->write($dc);
3240		}
3241		$this->FillColor = $fc;
3242		if ($fc != $this->defFillColor) {
3243			$this->writer->write($fc);
3244		}
3245		$this->TextColor = $tc;
3246		$this->ColorFlag = $cf;
3247
3248		// Page header
3249		$this->Header();
3250
3251		// Restore line width
3252		if ($this->LineWidth != $lw) {
3253			$this->LineWidth = $lw;
3254			$this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));
3255		}
3256		// Restore font
3257		if ($family) {
3258			$this->SetFont($family, $style, $size, true, true); // forces write
3259		}
3260
3261		// Restore colors
3262		if ($this->DrawColor != $dc) {
3263			$this->DrawColor = $dc;
3264			$this->writer->write($dc);
3265		}
3266		if ($this->FillColor != $fc) {
3267			$this->FillColor = $fc;
3268			$this->writer->write($fc);
3269		}
3270		$this->TextColor = $tc;
3271		$this->ColorFlag = $cf;
3272		$this->InFooter = false;
3273
3274		if ($save_layer > 0) {
3275			$this->BeginLayer($save_layer);
3276		}
3277
3278		if ($save_vis != 'visible') {
3279			$this->SetVisibility($save_vis);
3280		}
3281
3282		/* -- COLUMNS -- */
3283		if ($save_cols) {
3284			// Restore columns
3285			$this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);
3286		}
3287		if ($this->ColActive) {
3288			$this->SetCol(0);
3289		}
3290		/* -- END COLUMNS -- */
3291
3292
3293		// RESET BLOCK BORDER TOP
3294		if (!$this->ColActive) {
3295			for ($bl = 1; $bl <= $this->blklvl; $bl++) {
3296				$this->blk[$bl]['y0'] = $this->y;
3297				if (isset($this->blk[$bl]['x0'])) {
3298					$this->blk[$bl]['x0'] += $this->MarginCorrection;
3299				} else {
3300					$this->blk[$bl]['x0'] = $this->MarginCorrection;
3301				}
3302				// Added mPDF 3.0 Float DIV
3303				$this->blk[$bl]['marginCorrected'][$this->page] = true;
3304			}
3305		}
3306
3307
3308		$this->table_rotate = $save_tr; // *TABLES*
3309		$this->kwt = $save_kwt;
3310
3311		$this->keep_block_together = $save_kt;
3312
3313		$this->cMarginL = $bak_cml;
3314		$this->cMarginR = $bak_cmr;
3315		$this->divwidth = $bak_dw;
3316
3317		$this->lineheight = $bak_lh;
3318	}
3319
3320	/**
3321	 * Get current page number
3322	 *
3323	 * @return int
3324	 */
3325	function PageNo()
3326	{
3327		return $this->page;
3328	}
3329
3330	function AddSpotColorsFromFile($file)
3331	{
3332		$colors = @file($file);
3333		if (!$colors) {
3334			throw new \Mpdf\MpdfException("Cannot load spot colors file - " . $file);
3335		}
3336		foreach ($colors as $sc) {
3337			list($name, $c, $m, $y, $k) = preg_split("/\t/", $sc);
3338			$c = intval($c);
3339			$m = intval($m);
3340			$y = intval($y);
3341			$k = intval($k);
3342			$this->AddSpotColor($name, $c, $m, $y, $k);
3343		}
3344	}
3345
3346	function AddSpotColor($name, $c, $m, $y, $k)
3347	{
3348		$name = strtoupper(trim($name));
3349		if (!isset($this->spotColors[$name])) {
3350			$i = count($this->spotColors) + 1;
3351			$this->spotColors[$name] = ['i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k];
3352			$this->spotColorIDs[$i] = $name;
3353		}
3354	}
3355
3356	function SetColor($col, $type = '')
3357	{
3358		$out = '';
3359		if (!$col) {
3360			return '';
3361		} // mPDF 6
3362		if ($col[0] == 3 || $col[0] == 5) { // RGB / RGBa
3363			$out = sprintf('%.3F %.3F %.3F rg', ord($col[1]) / 255, ord($col[2]) / 255, ord($col[3]) / 255);
3364		} elseif ($col[0] == 1) { // GRAYSCALE
3365			$out = sprintf('%.3F g', ord($col[1]) / 255);
3366		} elseif ($col[0] == 2) { // SPOT COLOR
3367			$out = sprintf('/CS%d cs %.3F scn', ord($col[1]), ord($col[2]) / 100);
3368		} elseif ($col[0] == 4 || $col[0] == 6) { // CMYK / CMYKa
3369			$out = sprintf('%.3F %.3F %.3F %.3F k', ord($col[1]) / 100, ord($col[2]) / 100, ord($col[3]) / 100, ord($col[4]) / 100);
3370		}
3371		if ($type == 'Draw') {
3372			$out = strtoupper($out);
3373		} // e.g. rg => RG
3374		elseif ($type == 'CodeOnly') {
3375			$out = preg_replace('/\s(rg|g|k)/', '', $out);
3376		}
3377		return $out;
3378	}
3379
3380	function SetDColor($col, $return = false)
3381	{
3382		$out = $this->SetColor($col, 'Draw');
3383		if ($return) {
3384			return $out;
3385		}
3386		if ($out == '') {
3387			return '';
3388		}
3389		$this->DrawColor = $out;
3390		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['DrawColor']) && $this->pageoutput[$this->page]['DrawColor'] != $this->DrawColor) || !isset($this->pageoutput[$this->page]['DrawColor']))) {
3391			$this->writer->write($this->DrawColor);
3392		}
3393		$this->pageoutput[$this->page]['DrawColor'] = $this->DrawColor;
3394	}
3395
3396	function SetFColor($col, $return = false)
3397	{
3398		$out = $this->SetColor($col, 'Fill');
3399		if ($return) {
3400			return $out;
3401		}
3402		if ($out == '') {
3403			return '';
3404		}
3405		$this->FillColor = $out;
3406		$this->ColorFlag = ($out != $this->TextColor);
3407		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor']))) {
3408			$this->writer->write($this->FillColor);
3409		}
3410		$this->pageoutput[$this->page]['FillColor'] = $this->FillColor;
3411	}
3412
3413	function SetTColor($col, $return = false)
3414	{
3415		$out = $this->SetColor($col, 'Text');
3416		if ($return) {
3417			return $out;
3418		}
3419		if ($out == '') {
3420			return '';
3421		}
3422		$this->TextColor = $out;
3423		$this->ColorFlag = ($this->FillColor != $out);
3424	}
3425
3426	function SetDrawColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
3427	{
3428		// Set color for all stroking operations
3429		$col = [];
3430		if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
3431			$col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
3432		} elseif ($col4 == -1) {
3433			$col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
3434		} else {
3435			$col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
3436		}
3437		$out = $this->SetDColor($col, $return);
3438		return $out;
3439	}
3440
3441	function SetFillColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
3442	{
3443		// Set color for all filling operations
3444		$col = [];
3445		if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
3446			$col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
3447		} elseif ($col4 == -1) {
3448			$col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
3449		} else {
3450			$col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
3451		}
3452		$out = $this->SetFColor($col, $return);
3453		return $out;
3454	}
3455
3456	function SetTextColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
3457	{
3458		// Set color for text
3459		$col = [];
3460		if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
3461			$col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
3462		} elseif ($col4 == -1) {
3463			$col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
3464		} else {
3465			$col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
3466		}
3467		$out = $this->SetTColor($col, $return);
3468		return $out;
3469	}
3470
3471	function _getCharWidth(&$cw, $u, $isdef = true)
3472	{
3473		$w = 0;
3474
3475		if ($u == 0) {
3476			$w = false;
3477		} elseif (isset($cw[$u * 2 + 1])) {
3478			$w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);
3479		}
3480
3481		if ($w == 65535) {
3482			return 0;
3483		} elseif ($w) {
3484			return $w;
3485		} elseif ($isdef) {
3486			return false;
3487		} else {
3488			return 0;
3489		}
3490	}
3491
3492	function _charDefined(&$cw, $u)
3493	{
3494		$w = 0;
3495		if ($u == 0) {
3496			return false;
3497		}
3498		if (isset($cw[$u * 2 + 1])) {
3499			$w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);
3500		}
3501
3502		return (bool) $w;
3503	}
3504
3505	function GetCharWidthCore($c)
3506	{
3507		// Get width of a single character in the current Core font
3508		$c = (string) $c;
3509		$w = 0;
3510		// Soft Hyphens chr(173)
3511		if ($c == chr(173) && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
3512			return 0;
3513		} elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($c)])) {  // mPDF 5.7.1
3514			$charw = $this->CurrentFont['cw'][chr($this->upperCase[ord($c)])];
3515			if ($charw !== false) {
3516				$charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
3517				$w+=$charw;
3518			}
3519		} elseif (isset($this->CurrentFont['cw'][$c])) {
3520			$w += $this->CurrentFont['cw'][$c];
3521		} elseif (isset($this->CurrentFont['cw'][ord($c)])) {
3522			$w += $this->CurrentFont['cw'][ord($c)];
3523		}
3524		$w *= ($this->FontSize / 1000);
3525		if ($this->minwSpacing || $this->fixedlSpacing) {
3526			if ($c == ' ') {
3527				$nb_spaces = 1;
3528			} else {
3529				$nb_spaces = 0;
3530			}
3531			$w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);
3532		}
3533		return ($w);
3534	}
3535
3536	function GetCharWidthNonCore($c, $addSubset = true)
3537	{
3538		// Get width of a single character in the current Non-Core font
3539		$c = (string) $c;
3540		$w = 0;
3541		$unicode = $this->UTF8StringToArray($c, $addSubset);
3542		$char = $unicode[0];
3543		/* -- CJK-FONTS -- */
3544		if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts
3545			if ($char == 173) {
3546				return 0;
3547			} // Soft Hyphens
3548			elseif (isset($this->CurrentFont['cw'][$char])) {
3549				$w+=$this->CurrentFont['cw'][$char];
3550			} elseif (isset($this->CurrentFont['MissingWidth'])) {
3551				$w += $this->CurrentFont['MissingWidth'];
3552			} else {
3553				$w += 500;
3554			}
3555		} else {
3556			/* -- END CJK-FONTS -- */
3557			if ($char == 173) {
3558				return 0;
3559			} // Soft Hyphens
3560			elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) { // mPDF 5.7.1
3561				$charw = $this->_getCharWidth($this->CurrentFont['cw'], $this->upperCase[$char]);
3562				if ($charw !== false) {
3563					$charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
3564					$w+=$charw;
3565				} elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
3566					$w += $this->CurrentFont['desc']['MissingWidth'];
3567				} elseif (isset($this->CurrentFont['MissingWidth'])) {
3568					$w += $this->CurrentFont['MissingWidth'];
3569				} else {
3570					$w += 500;
3571				}
3572			} else {
3573				$charw = $this->_getCharWidth($this->CurrentFont['cw'], $char);
3574				if ($charw !== false) {
3575					$w+=$charw;
3576				} elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
3577					$w += $this->CurrentFont['desc']['MissingWidth'];
3578				} elseif (isset($this->CurrentFont['MissingWidth'])) {
3579					$w += $this->CurrentFont['MissingWidth'];
3580				} else {
3581					$w += 500;
3582				}
3583			}
3584		} // *CJK-FONTS*
3585		$w *= ($this->FontSize / 1000);
3586		if ($this->minwSpacing || $this->fixedlSpacing) {
3587			if ($c == ' ') {
3588				$nb_spaces = 1;
3589			} else {
3590				$nb_spaces = 0;
3591			}
3592			$w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);
3593		}
3594		return ($w);
3595	}
3596
3597	function GetCharWidth($c, $addSubset = true)
3598	{
3599		if (!$this->usingCoreFont) {
3600			return $this->GetCharWidthNonCore($c, $addSubset);
3601		} else {
3602			return $this->GetCharWidthCore($c);
3603		}
3604	}
3605
3606	function GetStringWidth($s, $addSubset = true, $OTLdata = false, $textvar = 0, $includeKashida = false)
3607	{
3608	// mPDF 5.7.1
3609		// Get width of a string in the current font
3610		$s = (string) $s;
3611		$cw = &$this->CurrentFont['cw'];
3612		$w = 0;
3613		$kerning = 0;
3614		$lastchar = 0;
3615		$nb_carac = 0;
3616		$nb_spaces = 0;
3617		$kashida = 0;
3618		// mPDF ITERATION
3619		if ($this->iterationCounter) {
3620			$s = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $s);
3621		}
3622		if (!$this->usingCoreFont) {
3623			$discards = substr_count($s, "\xc2\xad"); // mPDF 6 soft hyphens [U+00AD]
3624			$unicode = $this->UTF8StringToArray($s, $addSubset);
3625			if ($this->minwSpacing || $this->fixedlSpacing) {
3626				$nb_spaces = mb_substr_count($s, ' ', $this->mb_enc);
3627				$nb_carac = count($unicode) - $discards; // mPDF 6
3628				// mPDF 5.7.1
3629				// Use GPOS OTL
3630				if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
3631					if (isset($OTLdata['group']) && $OTLdata['group']) {
3632						$nb_carac -= substr_count($OTLdata['group'], 'M');
3633					}
3634				}
3635			}
3636			/* -- CJK-FONTS -- */
3637			if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts
3638				foreach ($unicode as $char) {
3639					if ($char == 0x00AD) {
3640						continue;
3641					} // mPDF 6 soft hyphens [U+00AD]
3642					if (isset($cw[$char])) {
3643						$w+=$cw[$char];
3644					} elseif (isset($this->CurrentFont['MissingWidth'])) {
3645						$w += $this->CurrentFont['MissingWidth'];
3646					} else {
3647						$w += 500;
3648					}
3649				}
3650			} else {
3651				/* -- END CJK-FONTS -- */
3652				foreach ($unicode as $i => $char) {
3653					if ($char == 0x00AD) {
3654						continue;
3655					} // mPDF 6 soft hyphens [U+00AD]
3656					if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) {
3657						$charw = $this->_getCharWidth($cw, $this->upperCase[$char]);
3658						if ($charw !== false) {
3659							$charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
3660							$w+=$charw;
3661						} elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
3662							$w += $this->CurrentFont['desc']['MissingWidth'];
3663						} elseif (isset($this->CurrentFont['MissingWidth'])) {
3664							$w += $this->CurrentFont['MissingWidth'];
3665						} else {
3666							$w += 500;
3667						}
3668					} else {
3669						$charw = $this->_getCharWidth($cw, $char);
3670						if ($charw !== false) {
3671							$w+=$charw;
3672						} elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
3673							$w += $this->CurrentFont['desc']['MissingWidth'];
3674						} elseif (isset($this->CurrentFont['MissingWidth'])) {
3675							$w += $this->CurrentFont['MissingWidth'];
3676						} else {
3677							$w += 500;
3678						}
3679						// mPDF 5.7.1
3680						// Use GPOS OTL
3681						// ...GetStringWidth...
3682						if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata)) {
3683							if (isset($OTLdata['GPOSinfo'][$i]['wDir']) && $OTLdata['GPOSinfo'][$i]['wDir'] == 'RTL') {
3684								if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceR']) && $OTLdata['GPOSinfo'][$i]['XAdvanceR']) {
3685									$w += $OTLdata['GPOSinfo'][$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
3686								}
3687							} else {
3688								if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceL']) && $OTLdata['GPOSinfo'][$i]['XAdvanceL']) {
3689									$w += $OTLdata['GPOSinfo'][$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
3690								}
3691							}
3692							// Kashida from GPOS
3693							// Kashida is set as an absolute length value (already set as a proportion based on useKashida %)
3694							if ($includeKashida && isset($OTLdata['GPOSinfo'][$i]['kashida_space']) && $OTLdata['GPOSinfo'][$i]['kashida_space']) {
3695								$kashida += $OTLdata['GPOSinfo'][$i]['kashida_space'];
3696							}
3697						}
3698						if (($textvar & TextVars::FC_KERNING) && $lastchar) {
3699							if (isset($this->CurrentFont['kerninfo'][$lastchar][$char])) {
3700								$kerning += $this->CurrentFont['kerninfo'][$lastchar][$char];
3701							}
3702						}
3703						$lastchar = $char;
3704					}
3705				}
3706			} // *CJK-FONTS*
3707		} else {
3708			if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
3709				$s = str_replace(chr(173), '', $s);
3710			}
3711			$nb_carac = $l = strlen($s);
3712			if ($this->minwSpacing || $this->fixedlSpacing) {
3713				$nb_spaces = substr_count($s, ' ');
3714			}
3715			for ($i = 0; $i < $l; $i++) {
3716				if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($s[$i])])) {  // mPDF 5.7.1
3717					$charw = $cw[chr($this->upperCase[ord($s[$i])])];
3718					if ($charw !== false) {
3719						$charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
3720						$w+=$charw;
3721					}
3722				} elseif (isset($cw[$s[$i]])) {
3723					$w += $cw[$s[$i]];
3724				} elseif (isset($cw[ord($s[$i])])) {
3725					$w += $cw[ord($s[$i])];
3726				}
3727				if (($textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
3728					if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]])) {
3729						$kerning += $this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]];
3730					}
3731				}
3732			}
3733		}
3734		unset($cw);
3735		if ($textvar & TextVars::FC_KERNING) {
3736			$w += $kerning;
3737		} // mPDF 5.7.1
3738		$w *= ($this->FontSize / 1000);
3739		$w += (($nb_carac + $nb_spaces) * $this->fixedlSpacing) + ($nb_spaces * $this->minwSpacing);
3740		$w += $kashida / Mpdf::SCALE;
3741
3742		return ($w);
3743	}
3744
3745	function SetLineWidth($width)
3746	{
3747		// Set line width
3748		$this->LineWidth = $width;
3749		$lwout = (sprintf('%.3F w', $width * Mpdf::SCALE));
3750		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineWidth']) && $this->pageoutput[$this->page]['LineWidth'] != $lwout) || !isset($this->pageoutput[$this->page]['LineWidth']))) {
3751			$this->writer->write($lwout);
3752		}
3753		$this->pageoutput[$this->page]['LineWidth'] = $lwout;
3754	}
3755
3756	function Line($x1, $y1, $x2, $y2)
3757	{
3758		// Draw a line
3759		$this->writer->write(sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($this->h - $y2) * Mpdf::SCALE));
3760	}
3761
3762	function Arrow($x1, $y1, $x2, $y2, $headsize = 3, $fill = 'B', $angle = 25)
3763	{
3764		// F == fill // S == stroke // B == stroke and fill
3765		// angle = splay of arrowhead - 1 - 89 degrees
3766		if ($fill == 'F') {
3767			$fill = 'f';
3768		} elseif ($fill == 'FD' or $fill == 'DF' or $fill == 'B') {
3769			$fill = 'B';
3770		} else {
3771			$fill = 'S';
3772		}
3773		$a = atan2(($y2 - $y1), ($x2 - $x1));
3774		$b = $a + deg2rad($angle);
3775		$c = $a - deg2rad($angle);
3776		$x3 = $x2 - ($headsize * cos($b));
3777		$y3 = $this->h - ($y2 - ($headsize * sin($b)));
3778		$x4 = $x2 - ($headsize * cos($c));
3779		$y4 = $this->h - ($y2 - ($headsize * sin($c)));
3780
3781		$x5 = $x3 - ($x3 - $x4) / 2; // mid point of base of arrowhead - to join arrow line to
3782		$y5 = $y3 - ($y3 - $y4) / 2;
3783
3784		$s = '';
3785		$s .= sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE);
3786		$this->writer->write($s);
3787
3788		$s = '';
3789		$s .= sprintf('%.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l ', $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE, $x3 * Mpdf::SCALE, $y3 * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($this->h - $y2) * Mpdf::SCALE, $x4 * Mpdf::SCALE, $y4 * Mpdf::SCALE, $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE);
3790		$s .= $fill;
3791		$this->writer->write($s);
3792	}
3793
3794	function Rect($x, $y, $w, $h, $style = '')
3795	{
3796		// Draw a rectangle
3797		if ($style == 'F') {
3798			$op = 'f';
3799		} elseif ($style == 'FD' or $style == 'DF') {
3800			$op = 'B';
3801		} else {
3802			$op = 'S';
3803		}
3804		$this->writer->write(sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$h * Mpdf::SCALE, $op));
3805	}
3806
3807	function AddFontDirectory($directory)
3808	{
3809		$this->fontDir[] = $directory;
3810		$this->fontFileFinder->setDirectories($this->fontDir);
3811	}
3812
3813	function AddFont($family, $style = '')
3814	{
3815		if (empty($family)) {
3816			return;
3817		}
3818
3819		$family = strtolower($family);
3820		$style = strtoupper($style);
3821		$style = str_replace('U', '', $style);
3822
3823		if ($style == 'IB') {
3824			$style = 'BI';
3825		}
3826
3827		$fontkey = $family . $style;
3828
3829		// check if the font has been already added
3830		if (isset($this->fonts[$fontkey])) {
3831			return;
3832		}
3833
3834		/* -- CJK-FONTS -- */
3835		if (in_array($family, $this->available_CJK_fonts)) {
3836			if (empty($this->Big5_widths)) {
3837				require __DIR__ . '/../data/CJKdata.php';
3838			}
3839			$this->AddCJKFont($family); // don't need to add style
3840			return;
3841		}
3842		/* -- END CJK-FONTS -- */
3843
3844		if ($this->usingCoreFont) {
3845			throw new \Mpdf\MpdfException("mPDF Error - problem with Font management");
3846		}
3847
3848		$stylekey = $style;
3849		if (!$style) {
3850			$stylekey = 'R';
3851		}
3852
3853		if (!isset($this->fontdata[$family][$stylekey]) || !$this->fontdata[$family][$stylekey]) {
3854			throw new \Mpdf\MpdfException(sprintf('Font "%s%s%s" is not supported', $family, $style ? ' - ' : '', $style));
3855		}
3856
3857		/* Setup defaults */
3858		$font = [
3859			'name' => '',
3860			'type' => '',
3861			'desc' => '',
3862			'panose' => '',
3863			'unitsPerEm' => '',
3864			'up' => '',
3865			'ut' => '',
3866			'strs' => '',
3867			'strp' => '',
3868			'sip' => false,
3869			'smp' => false,
3870			'useOTL' => 0,
3871			'fontmetrics' => '',
3872			'haskerninfo' => false,
3873			'haskernGPOS' => false,
3874			'hassmallcapsGSUB' => false,
3875			'BMPselected' => false,
3876			'GSUBScriptLang' => [],
3877			'GSUBFeatures' => [],
3878			'GSUBLookups' => [],
3879			'GPOSScriptLang' => [],
3880			'GPOSFeatures' => [],
3881			'GPOSLookups' => [],
3882			'rtlPUAstr' => '',
3883		];
3884
3885		$fontCacheFilename = $fontkey . '.mtx.json';
3886		if ($this->fontCache->jsonHas($fontCacheFilename)) {
3887			$font = $this->fontCache->jsonLoad($fontCacheFilename);
3888		}
3889
3890		$ttffile = $this->fontFileFinder->findFontFile($this->fontdata[$family][$stylekey]);
3891		$ttfstat = stat($ttffile);
3892
3893		$TTCfontID = isset($this->fontdata[$family]['TTCfontID'][$stylekey]) ? isset($this->fontdata[$family]['TTCfontID'][$stylekey]) : 0;
3894		$fontUseOTL = isset($this->fontdata[$family]['useOTL']) ? $this->fontdata[$family]['useOTL'] : false;
3895		$BMPonly = in_array($family, $this->BMPonly) ? true : false;
3896
3897		$regenerate = false;
3898		if ($BMPonly && !$font['BMPselected']) {
3899			$regenerate = true;
3900		} elseif (!$BMPonly && $font['BMPselected']) {
3901			$regenerate = true;
3902		}
3903
3904		if ($fontUseOTL && $font['useOTL'] != $fontUseOTL) {
3905			$regenerate = true;
3906			$font['useOTL'] = $fontUseOTL;
3907		} elseif (!$fontUseOTL && $font['useOTL']) {
3908			$regenerate = true;
3909			$font['useOTL'] = 0;
3910		}
3911
3912		if ($this->fontDescriptor != $font['fontmetrics']) {
3913			$regenerate = true;
3914		} // mPDF 6
3915
3916		if (empty($font['name']) || $font['originalsize'] != $ttfstat['size'] || $regenerate) {
3917			$generator = new MetricsGenerator($this->fontCache, $this->fontDescriptor);
3918
3919			$generator->generateMetrics(
3920				$ttffile,
3921				$ttfstat,
3922				$fontkey,
3923				$TTCfontID,
3924				$this->debugfonts,
3925				$BMPonly,
3926				$font['useOTL'],
3927				$fontUseOTL
3928			);
3929
3930			$font = $this->fontCache->jsonLoad($fontCacheFilename);
3931			$cw = $this->fontCache->load($fontkey . '.cw.dat');
3932			$glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat');
3933		} else {
3934			if ($this->fontCache->has($fontkey . '.cw.dat')) {
3935				$cw = $this->fontCache->load($fontkey . '.cw.dat');
3936			}
3937
3938			if ($this->fontCache->has($fontkey . '.gid.dat')) {
3939				$glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat');
3940			}
3941		}
3942
3943		if (isset($this->fontdata[$family]['sip-ext']) && $this->fontdata[$family]['sip-ext']) {
3944			$sipext = $this->fontdata[$family]['sip-ext'];
3945		} else {
3946			$sipext = '';
3947		}
3948
3949		// Override with values from config_font.php
3950		if (isset($this->fontdata[$family]['Ascent']) && $this->fontdata[$family]['Ascent']) {
3951			$desc['Ascent'] = $this->fontdata[$family]['Ascent'];
3952		}
3953		if (isset($this->fontdata[$family]['Descent']) && $this->fontdata[$family]['Descent']) {
3954			$desc['Descent'] = $this->fontdata[$family]['Descent'];
3955		}
3956		if (isset($this->fontdata[$family]['Leading']) && $this->fontdata[$family]['Leading']) {
3957			$desc['Leading'] = $this->fontdata[$family]['Leading'];
3958		}
3959
3960		$i = count($this->fonts) + $this->extraFontSubsets + 1;
3961
3962		$this->fonts[$fontkey] = [
3963			'i' => $i,
3964			'name' => $font['name'],
3965			'type' => $font['type'],
3966			'desc' => $font['desc'],
3967			'panose' => $font['panose'],
3968			'unitsPerEm' => $font['unitsPerEm'],
3969			'up' => $font['up'],
3970			'ut' => $font['ut'],
3971			'strs' => $font['strs'],
3972			'strp' => $font['strp'],
3973			'cw' => $cw,
3974			'ttffile' => $ttffile,
3975			'fontkey' => $fontkey,
3976			'used' => false,
3977			'sip' => $font['sip'],
3978			'sipext' => $sipext,
3979			'smp' => $font['smp'],
3980			'TTCfontID' => $TTCfontID,
3981			'useOTL' => $fontUseOTL,
3982			'useKashida' => (isset($this->fontdata[$family]['useKashida']) ? $this->fontdata[$family]['useKashida'] : false),
3983			'GSUBScriptLang' => $font['GSUBScriptLang'],
3984			'GSUBFeatures' => $font['GSUBFeatures'],
3985			'GSUBLookups' => $font['GSUBLookups'],
3986			'GPOSScriptLang' => $font['GPOSScriptLang'],
3987			'GPOSFeatures' => $font['GPOSFeatures'],
3988			'GPOSLookups' => $font['GPOSLookups'],
3989			'rtlPUAstr' => $font['rtlPUAstr'],
3990			'glyphIDtoUni' => $glyphIDtoUni,
3991			'haskerninfo' => $font['haskerninfo'],
3992			'haskernGPOS' => $font['haskernGPOS'],
3993			'hassmallcapsGSUB' => $font['hassmallcapsGSUB'],
3994		];
3995
3996
3997		if (!$font['sip'] && !$font['smp']) {
3998			$subsetRange = range(32, 127);
3999			$this->fonts[$fontkey]['subset'] = array_combine($subsetRange, $subsetRange);
4000		} else {
4001			$this->fonts[$fontkey]['subsets'] = [0 => range(0, 127)];
4002			$this->fonts[$fontkey]['subsetfontids'] = [$i];
4003		}
4004
4005		if ($font['haskerninfo']) {
4006			$this->fonts[$fontkey]['kerninfo'] = $font['kerninfo'];
4007		}
4008
4009		$this->FontFiles[$fontkey] = [
4010			'length1' => $font['originalsize'],
4011			'type' => 'TTF',
4012			'ttffile' => $ttffile,
4013			'sip' => $font['sip'],
4014			'smp' => $font['smp'],
4015		];
4016
4017		unset($cw);
4018	}
4019
4020	function SetFont($family, $style = '', $size = 0, $write = true, $forcewrite = false)
4021	{
4022		$family = strtolower($family);
4023
4024		if (!$this->onlyCoreFonts) {
4025			if ($family == 'sans' || $family == 'sans-serif') {
4026				$family = $this->sans_fonts[0];
4027			}
4028			if ($family == 'serif') {
4029				$family = $this->serif_fonts[0];
4030			}
4031			if ($family == 'mono' || $family == 'monospace') {
4032				$family = $this->mono_fonts[0];
4033			}
4034		}
4035
4036		if (isset($this->fonttrans[$family]) && $this->fonttrans[$family]) {
4037			$family = $this->fonttrans[$family];
4038		}
4039
4040		if ($family == '') {
4041			if ($this->FontFamily) {
4042				$family = $this->FontFamily;
4043			} elseif ($this->default_font) {
4044				$family = $this->default_font;
4045			} else {
4046				throw new \Mpdf\MpdfException("No font or default font set!");
4047			}
4048		}
4049
4050		$this->ReqFontStyle = $style; // required or requested style - used later for artificial bold/italic
4051
4052		if (($family == 'csymbol') || ($family == 'czapfdingbats') || ($family == 'ctimes') || ($family == 'ccourier') || ($family == 'chelvetica')) {
4053			if ($this->PDFA || $this->PDFX) {
4054				if ($family == 'csymbol' || $family == 'czapfdingbats') {
4055					throw new \Mpdf\MpdfException("Symbol and Zapfdingbats cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a).");
4056				}
4057				if ($family == 'ctimes' || $family == 'ccourier' || $family == 'chelvetica') {
4058					if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
4059						$this->PDFAXwarnings[] = "Core Adobe font " . ucfirst($family) . " cannot be embedded in mPDF, which is required for PDFA1-b or PDFX/1-a. (Embedded font will be substituted.)";
4060					}
4061					if ($family == 'chelvetica') {
4062						$family = 'sans';
4063					}
4064					if ($family == 'ctimes') {
4065						$family = 'serif';
4066					}
4067					if ($family == 'ccourier') {
4068						$family = 'mono';
4069					}
4070				}
4071				$this->usingCoreFont = false;
4072			} else {
4073				$this->usingCoreFont = true;
4074			}
4075			if ($family == 'csymbol' || $family == 'czapfdingbats') {
4076				$style = '';
4077			}
4078		} else {
4079			$this->usingCoreFont = false;
4080		}
4081
4082		// mPDF 5.7.1
4083		if ($style) {
4084			$style = strtoupper($style);
4085			if ($style == 'IB') {
4086				$style = 'BI';
4087			}
4088		}
4089
4090		if (!$size) {
4091			$size = $this->FontSizePt;
4092		}
4093
4094		$fontkey = $family . $style;
4095
4096		$stylekey = $style;
4097
4098		if (!$stylekey) {
4099			$stylekey = "R";
4100		}
4101
4102		if (!$this->onlyCoreFonts && !$this->usingCoreFont) {
4103			if (!isset($this->fonts[$fontkey]) || count($this->default_available_fonts) != count($this->available_unifonts)) { // not already added
4104
4105				/* -- CJK-FONTS -- */
4106				if (in_array($fontkey, $this->available_CJK_fonts)) {
4107					if (!isset($this->fonts[$fontkey])) { // already added
4108						if (empty($this->Big5_widths)) {
4109							require __DIR__ . '/../data/CJKdata.php';
4110						}
4111						$this->AddCJKFont($family); // don't need to add style
4112					}
4113				} else { // Test to see if requested font/style is available - or substitute /* -- END CJK-FONTS -- */
4114					if (!in_array($fontkey, $this->available_unifonts)) {
4115						// If font[nostyle] exists - set it
4116						if (in_array($family, $this->available_unifonts)) {
4117							$style = '';
4118						} // elseif only one font available - set it (assumes if only one font available it will not have a style)
4119						elseif (count($this->available_unifonts) == 1) {
4120							$family = $this->available_unifonts[0];
4121							$style = '';
4122						} else {
4123							$found = 0;
4124							// else substitute font of similar type
4125							if (in_array($family, $this->sans_fonts)) {
4126								$i = array_intersect($this->sans_fonts, $this->available_unifonts);
4127								if (count($i)) {
4128									$i = array_values($i);
4129									// with requested style if possible
4130									if (!in_array(($i[0] . $style), $this->available_unifonts)) {
4131										$style = '';
4132									}
4133									$family = $i[0];
4134									$found = 1;
4135								}
4136							} elseif (in_array($family, $this->serif_fonts)) {
4137								$i = array_intersect($this->serif_fonts, $this->available_unifonts);
4138								if (count($i)) {
4139									$i = array_values($i);
4140									// with requested style if possible
4141									if (!in_array(($i[0] . $style), $this->available_unifonts)) {
4142										$style = '';
4143									}
4144									$family = $i[0];
4145									$found = 1;
4146								}
4147							} elseif (in_array($family, $this->mono_fonts)) {
4148								$i = array_intersect($this->mono_fonts, $this->available_unifonts);
4149								if (count($i)) {
4150									$i = array_values($i);
4151									// with requested style if possible
4152									if (!in_array(($i[0] . $style), $this->available_unifonts)) {
4153										$style = '';
4154									}
4155									$family = $i[0];
4156									$found = 1;
4157								}
4158							}
4159
4160							if (!$found) {
4161								// set first available font
4162								$fs = $this->available_unifonts[0];
4163								preg_match('/^([a-z_0-9\-]+)([BI]{0,2})$/', $fs, $fas); // Allow "-"
4164								// with requested style if possible
4165								$ws = $fas[1] . $style;
4166								if (in_array($ws, $this->available_unifonts)) {
4167									$family = $fas[1]; // leave $style as is
4168								} elseif (in_array($fas[1], $this->available_unifonts)) {
4169									// or without style
4170									$family = $fas[1];
4171									$style = '';
4172								} else {
4173									// or with the style specified
4174									$family = $fas[1];
4175									$style = $fas[2];
4176								}
4177							}
4178						}
4179						$fontkey = $family . $style;
4180					}
4181				}
4182			}
4183
4184			// try to add font (if not already added)
4185			$this->AddFont($family, $style);
4186
4187			// Test if font is already selected
4188			if ($this->FontFamily == $family && $this->FontFamily == $this->currentfontfamily && $this->FontStyle == $style && $this->FontStyle == $this->currentfontstyle && $this->FontSizePt == $size && $this->FontSizePt == $this->currentfontsize && !$forcewrite) {
4189				return $family;
4190			}
4191
4192			$fontkey = $family . $style;
4193
4194			// Select it
4195			$this->FontFamily = $family;
4196			$this->FontStyle = $style;
4197			$this->FontSizePt = $size;
4198			$this->FontSize = $size / Mpdf::SCALE;
4199			$this->CurrentFont = &$this->fonts[$fontkey];
4200			if ($write) {
4201				$fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4202				if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
4203					$this->writer->write($fontout);
4204				}
4205				$this->pageoutput[$this->page]['Font'] = $fontout;
4206			}
4207
4208			// Added - currentfont (lowercase) used in HTML2PDF
4209			$this->currentfontfamily = $family;
4210			$this->currentfontsize = $size;
4211			$this->currentfontstyle = $style;
4212			$this->setMBencoding('UTF-8');
4213		} else {  // if using core fonts
4214			if ($this->PDFA || $this->PDFX) {
4215				throw new \Mpdf\MpdfException('Core Adobe fonts cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a) - cannot use option to use core fonts.');
4216			}
4217			$this->setMBencoding('windows-1252');
4218
4219			// Test if font is already selected
4220			if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) {
4221				return $family;
4222			}
4223
4224			if (!isset($this->CoreFonts[$fontkey])) {
4225				if (in_array($family, $this->serif_fonts)) {
4226					$family = 'ctimes';
4227				} elseif (in_array($family, $this->mono_fonts)) {
4228					$family = 'ccourier';
4229				} else {
4230					$family = 'chelvetica';
4231				}
4232				$this->usingCoreFont = true;
4233				$fontkey = $family . $style;
4234			}
4235
4236			if (!isset($this->fonts[$fontkey])) {
4237				// STANDARD CORE FONTS
4238				if (isset($this->CoreFonts[$fontkey])) {
4239					// Load metric file
4240					$file = $family;
4241					if ($family == 'ctimes' || $family == 'chelvetica' || $family == 'ccourier') {
4242						$file .= strtolower($style);
4243					}
4244					require __DIR__ . '/../data/font/' . $file . '.php';
4245					if (!isset($cw)) {
4246						throw new \Mpdf\MpdfException(sprintf('Could not include font metric file "%s"', $file));
4247					}
4248					$i = count($this->fonts) + $this->extraFontSubsets + 1;
4249					$this->fonts[$fontkey] = ['i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw];
4250					if ($this->useKerning && isset($kerninfo)) {
4251						$this->fonts[$fontkey]['kerninfo'] = $kerninfo;
4252					}
4253				} else {
4254					throw new \Mpdf\MpdfException(sprintf('Font %s not defined', $fontkey));
4255				}
4256			}
4257
4258			// Test if font is already selected
4259			if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) {
4260				return $family;
4261			}
4262			// Select it
4263			$this->FontFamily = $family;
4264			$this->FontStyle = $style;
4265			$this->FontSizePt = $size;
4266			$this->FontSize = $size / Mpdf::SCALE;
4267			$this->CurrentFont = &$this->fonts[$fontkey];
4268			if ($write) {
4269				$fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4270				if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
4271					$this->writer->write($fontout);
4272				}
4273				$this->pageoutput[$this->page]['Font'] = $fontout;
4274			}
4275			// Added - currentfont (lowercase) used in HTML2PDF
4276			$this->currentfontfamily = $family;
4277			$this->currentfontsize = $size;
4278			$this->currentfontstyle = $style;
4279		}
4280
4281		return $family;
4282	}
4283
4284	function SetFontSize($size, $write = true)
4285	{
4286		// Set font size in points
4287		if ($this->FontSizePt == $size) {
4288			return;
4289		}
4290		$this->FontSizePt = $size;
4291		$this->FontSize = $size / Mpdf::SCALE;
4292		$this->currentfontsize = $size;
4293		if ($write) {
4294			$fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4295			// Edited mPDF 3.0
4296			if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
4297				$this->writer->write($fontout);
4298			}
4299			$this->pageoutput[$this->page]['Font'] = $fontout;
4300		}
4301	}
4302
4303	function AddLink()
4304	{
4305		// Create a new internal link
4306		$n = count($this->links) + 1;
4307		$this->links[$n] = [0, 0];
4308		return $n;
4309	}
4310
4311	function SetLink($link, $y = 0, $page = -1)
4312	{
4313		// Set destination of internal link
4314		if ($y == -1) {
4315			$y = $this->y;
4316		}
4317		if ($page == -1) {
4318			$page = $this->page;
4319		}
4320		$this->links[$link] = [$page, $y];
4321	}
4322
4323	function Link($x, $y, $w, $h, $link)
4324	{
4325		$l = [$x * Mpdf::SCALE, $this->hPt - $y * Mpdf::SCALE, $w * Mpdf::SCALE, $h * Mpdf::SCALE, $link];
4326		if ($this->keep_block_together) { // don't write yet
4327			return;
4328		} elseif ($this->table_rotate) { // *TABLES*
4329			$this->tbrot_Links[$this->page][] = $l; // *TABLES*
4330			return; // *TABLES*
4331		} // *TABLES*
4332		elseif ($this->kwt) {
4333			$this->kwt_Links[$this->page][] = $l;
4334			return;
4335		}
4336
4337		if ($this->writingHTMLheader || $this->writingHTMLfooter) {
4338			$this->HTMLheaderPageLinks[] = $l;
4339			return;
4340		}
4341		// Put a link on the page
4342		$this->PageLinks[$this->page][] = $l;
4343		// Save cross-reference to Column buffer
4344		$ref = count($this->PageLinks[$this->page]) - 1; // *COLUMNS*
4345		$this->columnLinks[$this->CurrCol][(int) $this->x][(int) $this->y] = $ref; // *COLUMNS*
4346	}
4347
4348	function Text($x, $y, $txt, $OTLdata = [], $textvar = 0, $aixextra = '', $coordsys = '', $return = false)
4349	{
4350		// Output (or return) a string
4351		// Called (internally) by Watermark() & _tableWrite() [rotated cells] & TableHeaderFooter() & WriteText()
4352		// Called also from classes/svg.php
4353		// Expects Font to be set
4354		// Expects input to be mb_encoded if necessary and RTL reversed & OTL processed
4355		// ARTIFICIAL BOLD AND ITALIC
4356		$s = 'q ';
4357		if ($this->falseBoldWeight && strpos($this->ReqFontStyle, "B") !== false && strpos($this->FontStyle, "B") === false) {
4358			$s .= '2 Tr 1 J 1 j ';
4359			$s .= sprintf('%.3F w ', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight);
4360			$tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
4361			if ($this->FillColor != $tc) {
4362				$s .= $tc . ' ';
4363			}  // stroke (outline) = same colour as text(fill)
4364		}
4365		if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) {
4366			$aix = '1 0 0.261799 1 %.3F %.3F Tm';
4367		} else {
4368			$aix = '%.3F %.3F Td';
4369		}
4370
4371		$aix = $aixextra . $aix;
4372
4373		if ($this->ColorFlag) {
4374			$s .= $this->TextColor . ' ';
4375		}
4376
4377		$this->CurrentFont['used'] = true;
4378
4379		if ($this->usingCoreFont) {
4380			$txt2 = str_replace(chr(160), chr(32), $txt);
4381		} else {
4382			$txt2 = str_replace(chr(194) . chr(160), chr(32), $txt);
4383		}
4384
4385		$px = $x;
4386		$py = $y;
4387		if ($coordsys != 'SVG') {
4388			$px = $x * Mpdf::SCALE;
4389			$py = ($this->h - $y) * Mpdf::SCALE;
4390		}
4391
4392
4393		/** ************** SIMILAR TO Cell() ************************ */
4394
4395		// IF corefonts AND NOT SmCaps AND NOT Kerning
4396		// Just output text
4397		if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) {
4398			$txt2 = $this->writer->escape($txt2);
4399			$s .= sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
4400		} // IF NOT corefonts [AND NO wordspacing] AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL
4401		// Just output text
4402		elseif (!$this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {
4403			// IF SIP/SMP
4404			if ($this->CurrentFont['sip'] || $this->CurrentFont['smp']) {
4405				$txt2 = $this->UTF8toSubset($txt2);
4406				$s .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);
4407			} // NOT SIP/SMP
4408			else {
4409				$txt2 = $this->writer->utf8ToUtf16BigEndian($txt2, false);
4410				$txt2 = $this->writer->escape($txt2);
4411				$s .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
4412			}
4413		} // IF NOT corefonts [AND IS wordspacing] AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL
4414		// Not required here (cf. Cell() )
4415		// ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]
4416		else {
4417			$s .= $this->applyGPOSpdf($txt2, $aix, $px, $py, $OTLdata, $textvar);
4418		}
4419		/*         * ************** END ************************ */
4420
4421		$s .= ' ';
4422
4423		if (($textvar & TextVars::FD_UNDERLINE) && $txt != '') { // mPDF 5.7.1
4424			$c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
4425			if ($this->FillColor != $c) {
4426				$s.= ' ' . $c . ' ';
4427			}
4428			if (isset($this->CurrentFont['up']) && $this->CurrentFont['up']) {
4429				$up = $this->CurrentFont['up'];
4430			} else {
4431				$up = -100;
4432			}
4433			$adjusty = (-$up / 1000 * $this->FontSize);
4434			if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {
4435				$ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;
4436			} else {
4437				$ut = 60 / 1000 * $this->FontSize;
4438			}
4439			$olw = $this->LineWidth;
4440			$s .= ' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE));
4441			$s .= ' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);
4442			$s .= ' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE));
4443			if ($this->FillColor != $c) {
4444				$s.= ' ' . $this->FillColor . ' ';
4445			}
4446		}
4447		// STRIKETHROUGH
4448		if (($textvar & TextVars::FD_LINETHROUGH) && $txt != '') { // mPDF 5.7.1
4449			$c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
4450			if ($this->FillColor != $c) {
4451				$s.= ' ' . $c . ' ';
4452			}
4453			// Superscript and Subscript Y coordinate adjustment (now for striked-through texts)
4454			if (isset($this->CurrentFont['desc']['CapHeight']) && $this->CurrentFont['desc']['CapHeight']) {
4455				$ch = $this->CurrentFont['desc']['CapHeight'];
4456			} else {
4457				$ch = 700;
4458			}
4459			$adjusty = (-$ch / 1000 * $this->FontSize) * 0.35;
4460			if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {
4461				$ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;
4462			} else {
4463				$ut = 60 / 1000 * $this->FontSize;
4464			}
4465			$olw = $this->LineWidth;
4466			$s .= ' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE));
4467			$s .= ' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);
4468			$s .= ' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE));
4469			if ($this->FillColor != $c) {
4470				$s.= ' ' . $this->FillColor . ' ';
4471			}
4472		}
4473		$s .= 'Q';
4474
4475		if ($return) {
4476			return $s . " \n";
4477		}
4478		$this->writer->write($s);
4479	}
4480
4481	/* -- DIRECTW -- */
4482
4483	function WriteText($x, $y, $txt)
4484	{
4485		// Output a string using Text() but does encoding and text reversing of RTL
4486		$txt = $this->purify_utf8_text($txt);
4487		if ($this->text_input_as_HTML) {
4488			$txt = $this->all_entities_to_utf8($txt);
4489		}
4490		if ($this->usingCoreFont) {
4491			$txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
4492		}
4493
4494		// DIRECTIONALITY
4495		if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
4496			$this->biDirectional = true;
4497		} // *OTL*
4498
4499		$textvar = 0;
4500		$save_OTLtags = $this->OTLtags;
4501		$this->OTLtags = [];
4502		if ($this->useKerning) {
4503			if ($this->CurrentFont['haskernGPOS']) {
4504				$this->OTLtags['Plus'] .= ' kern';
4505			} else {
4506				$textvar = ($textvar | TextVars::FC_KERNING);
4507			}
4508		}
4509
4510		/* -- OTL -- */
4511		// Use OTL OpenType Table Layout - GSUB & GPOS
4512		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
4513			$txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
4514			$OTLdata = $this->otl->OTLdata;
4515		}
4516		/* -- END OTL -- */
4517		$this->OTLtags = $save_OTLtags;
4518
4519		$this->magic_reverse_dir($txt, $this->directionality, $OTLdata);
4520
4521		$this->Text($x, $y, $txt, $OTLdata, $textvar);
4522	}
4523
4524	function WriteCell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0)
4525	{
4526		// Output a cell using Cell() but does encoding and text reversing of RTL
4527		$txt = $this->purify_utf8_text($txt);
4528		if ($this->text_input_as_HTML) {
4529			$txt = $this->all_entities_to_utf8($txt);
4530		}
4531		if ($this->usingCoreFont) {
4532			$txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
4533		}
4534		// DIRECTIONALITY
4535		if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
4536			$this->biDirectional = true;
4537		} // *OTL*
4538
4539		$textvar = 0;
4540		$save_OTLtags = $this->OTLtags;
4541		$this->OTLtags = [];
4542		if ($this->useKerning) {
4543			if ($this->CurrentFont['haskernGPOS']) {
4544				$this->OTLtags['Plus'] .= ' kern';
4545			} else {
4546				$textvar = ($textvar | TextVars::FC_KERNING);
4547			}
4548		}
4549
4550		/* -- OTL -- */
4551		// Use OTL OpenType Table Layout - GSUB & GPOS
4552		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
4553			$txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
4554			$OTLdata = $this->otl->OTLdata;
4555		}
4556		/* -- END OTL -- */
4557		$this->OTLtags = $save_OTLtags;
4558
4559		$this->magic_reverse_dir($txt, $this->directionality, $OTLdata);
4560
4561		$this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link, $currentx, 0, 0, 'M', 0, false, $OTLdata, $textvar);
4562	}
4563
4564	/* -- END DIRECTW -- */
4565
4566	function ResetSpacing()
4567	{
4568		if ($this->ws != 0) {
4569			$this->writer->write('BT 0 Tw ET');
4570		}
4571		$this->ws = 0;
4572		if ($this->charspacing != 0) {
4573			$this->writer->write('BT 0 Tc ET');
4574		}
4575		$this->charspacing = 0;
4576	}
4577
4578	function SetSpacing($cs, $ws)
4579	{
4580		if (intval($cs * 1000) == 0) {
4581			$cs = 0;
4582		}
4583		if ($cs) {
4584			$this->writer->write(sprintf('BT %.3F Tc ET', $cs));
4585		} elseif ($this->charspacing != 0) {
4586			$this->writer->write('BT 0 Tc ET');
4587		}
4588		$this->charspacing = $cs;
4589		if (intval($ws * 1000) == 0) {
4590			$ws = 0;
4591		}
4592		if ($ws) {
4593			$this->writer->write(sprintf('BT %.3F Tw ET', $ws));
4594		} elseif ($this->ws != 0) {
4595			$this->writer->write('BT 0 Tw ET');
4596		}
4597		$this->ws = $ws;
4598	}
4599
4600	// WORD SPACING
4601	function GetJspacing($nc, $ns, $w, $inclCursive, &$cOTLdata)
4602	{
4603		$kashida_present = false;
4604		$kashida_space = 0;
4605		if ($w > 0 && $inclCursive && isset($this->CurrentFont['useKashida']) && $this->CurrentFont['useKashida'] && !empty($cOTLdata)) {
4606			for ($c = 0; $c < count($cOTLdata); $c++) {
4607				for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
4608					if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
4609						$kashida_present = true;
4610						break 2;
4611					}
4612				}
4613			}
4614		}
4615
4616		if ($kashida_present) {
4617			$k_ctr = 0;  // Number of kashida points
4618			$k_total = 0;  // Total of kashida values (priority)
4619			// Reset word
4620			$max_kashida_in_word = 0;
4621			$last_kashida_in_word = -1;
4622
4623			for ($c = 0; $c < count($cOTLdata); $c++) {
4624				for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
4625					if ($cOTLdata[$c]['group'][$i] == 'S') {
4626						// Save from last word
4627						if ($max_kashida_in_word) {
4628							$k_ctr++;
4629							$k_total = $max_kashida_in_word;
4630						}
4631						// Reset word
4632						$max_kashida_in_word = 0;
4633						$last_kashida_in_word = -1;
4634					}
4635
4636					if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
4637						if ($max_kashida_in_word) {
4638							if ($cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > $max_kashida_in_word) {
4639								$max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];
4640								$cOTLdata[$c]['GPOSinfo'][$last_kashida_in_word]['kashida'] = 0;
4641								$last_kashida_in_word = $i;
4642							} else {
4643								$cOTLdata[$c]['GPOSinfo'][$i]['kashida'] = 0;
4644							}
4645						} else {
4646							$max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];
4647							$last_kashida_in_word = $i;
4648						}
4649					}
4650				}
4651			}
4652			// Save from last word
4653			if ($max_kashida_in_word) {
4654				$k_ctr++;
4655				$k_total = $max_kashida_in_word;
4656			}
4657
4658			// Number of kashida points = $k_ctr
4659			// $useKashida is a % value from CurrentFont/config_fonts.php
4660			// % ratio divided between word-spacing and kashida-spacing
4661			$kashida_space_ratio = intval($this->CurrentFont['useKashida']) / 100;
4662
4663
4664			$kashida_space = $w * $kashida_space_ratio;
4665
4666			$tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);
4667			// Only use kashida if each allocated kashida width is > 0.01 x width of a tatweel
4668			// Otherwise fontstretch is too small and errors
4669			// If not just leave to adjust word-spacing
4670			if ($tatw && (($kashida_space / $k_ctr) / $tatw) > 0.01) {
4671				for ($c = 0; $c < count($cOTLdata); $c++) {
4672					for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
4673						if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
4674							// At this point kashida is a number representing priority (higher number - higher priority)
4675							// We are now going to set it as an actual length
4676							// This shares it equally amongst words:
4677							$cOTLdata[$c]['GPOSinfo'][$i]['kashida_space'] = (1 / $k_ctr) * $kashida_space;
4678						}
4679					}
4680				}
4681				$w -= $kashida_space;
4682			}
4683		}
4684
4685		$ws = 0;
4686		$charspacing = 0;
4687		$ww = $this->jSWord;
4688		$ncx = $nc - 1;
4689		if ($nc == 0) {
4690			return [0, 0, 0];
4691		} // Only word spacing allowed / possible
4692		elseif ($this->fixedlSpacing !== false || $inclCursive) {
4693			if ($ns) {
4694				$ws = $w / $ns;
4695			}
4696		} elseif ($nc == 1) {
4697			$charspacing = $w;
4698		} elseif (!$ns) {
4699			$charspacing = $w / ($ncx );
4700			if (($this->jSmaxChar > 0) && ($charspacing > $this->jSmaxChar)) {
4701				$charspacing = $this->jSmaxChar;
4702			}
4703		} elseif ($ns == ($ncx )) {
4704			$charspacing = $w / $ns;
4705		} else {
4706			if ($this->usingCoreFont) {
4707				$cs = ($w * (1 - $this->jSWord)) / ($ncx );
4708				if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {
4709					$cs = $this->jSmaxChar;
4710					$ww = 1 - (($cs * ($ncx )) / $w);
4711				}
4712				$charspacing = $cs;
4713				$ws = ($w * ($ww) ) / $ns;
4714			} else {
4715				$cs = ($w * (1 - $this->jSWord)) / ($ncx - $ns);
4716				if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {
4717					$cs = $this->jSmaxChar;
4718					$ww = 1 - (($cs * ($ncx - $ns)) / $w);
4719				}
4720				$charspacing = $cs;
4721				$ws = (($w * ($ww) ) / $ns) - $charspacing;
4722			}
4723		}
4724		return [$charspacing, $ws, $kashida_space];
4725	}
4726
4727	/**
4728	 * Output a cell
4729	 *
4730	 * Expects input to be mb_encoded if necessary and RTL reversed
4731	 *
4732	 * @since mPDF 5.7.1
4733	 */
4734	function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0, $lcpaddingL = 0, $lcpaddingR = 0, $valign = 'M', $spanfill = 0, $exactWidth = false, $OTLdata = false, $textvar = 0, $lineBox = false)
4735	{
4736		// NON_BREAKING SPACE
4737		if ($this->usingCoreFont) {
4738			$txt = str_replace(chr(160), chr(32), $txt);
4739		} else {
4740			$txt = str_replace(chr(194) . chr(160), chr(32), $txt);
4741		}
4742
4743		$oldcolumn = $this->CurrCol;
4744
4745		// Automatic page break
4746		// Allows PAGE-BREAK-AFTER = avoid to work
4747		if (isset($this->blk[$this->blklvl])) {
4748			$bottom = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['margin_bottom'];
4749		} else {
4750			$bottom = 0;
4751		}
4752
4753		if (!$this->tableLevel
4754			&& (
4755				($this->y + $this->divheight > $this->PageBreakTrigger)
4756				|| ($this->y + $h > $this->PageBreakTrigger)
4757				|| (
4758					$this->y + ($h * 2) + $bottom > $this->PageBreakTrigger
4759						&& (isset($this->blk[$this->blklvl]['page_break_after_avoid']) && $this->blk[$this->blklvl]['page_break_after_avoid'])
4760				)
4761			)
4762			&& !$this->InFooter
4763			&& $this->AcceptPageBreak()
4764		) { // mPDF 5.7.2
4765
4766			$x = $this->x; // Current X position
4767
4768			// WORD SPACING
4769			$ws = $this->ws; // Word Spacing
4770			$charspacing = $this->charspacing; // Character Spacing
4771			$this->ResetSpacing();
4772
4773			$this->AddPage($this->CurOrientation);
4774
4775			// Added to correct for OddEven Margins
4776			$x += $this->MarginCorrection;
4777			if ($currentx) {
4778				$currentx += $this->MarginCorrection;
4779			}
4780			$this->x = $x;
4781			// WORD SPACING
4782			$this->SetSpacing($charspacing, $ws);
4783		}
4784
4785		// Test: to put line through centre of cell: $this->Line($this->x,$this->y+($h/2),$this->x+50,$this->y+($h/2));
4786		// Test: to put border around cell as it is specified: $border='LRTB';
4787
4788		/* -- COLUMNS -- */
4789		// COLS
4790		// COLUMN CHANGE
4791		if ($this->CurrCol != $oldcolumn) {
4792			if ($currentx) {
4793				$currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
4794			}
4795			$this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
4796		}
4797
4798		// COLUMNS Update/overwrite the lowest bottom of printing y value for a column
4799		if ($this->ColActive) {
4800			if ($h) {
4801				$this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
4802			} else {
4803				$this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $this->divheight;
4804			}
4805		}
4806		/* -- END COLUMNS -- */
4807
4808
4809		if ($w == 0) {
4810			$w = $this->w - $this->rMargin - $this->x;
4811		}
4812
4813		$s = '';
4814		if ($fill == 1 && $this->FillColor) {
4815			if ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor'])) {
4816				$s .= $this->FillColor . ' ';
4817			}
4818			$this->pageoutput[$this->page]['FillColor'] = $this->FillColor;
4819		}
4820
4821		if ($lineBox && isset($lineBox['boxtop']) && $txt) { // i.e. always from WriteFlowingBlock/finishFlowingBlock (but not objects -
4822			// which only have $lineBox['top'] set)
4823			$boxtop = $this->y + $lineBox['boxtop'];
4824			$boxbottom = $this->y + $lineBox['boxbottom'];
4825			$glyphYorigin = $lineBox['glyphYorigin'];
4826			$baseline_shift = $lineBox['baseline-shift'];
4827			$bord_boxtop = $bg_boxtop = $boxtop = $boxtop - $baseline_shift;
4828			$bord_boxbottom = $bg_boxbottom = $boxbottom = $boxbottom - $baseline_shift;
4829			$bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;
4830
4831			// If inline element BACKGROUND has bounding box set by parent element:
4832			if (isset($lineBox['background-boxtop'])) {
4833				$bg_boxtop = $this->y + $lineBox['background-boxtop'] - $lineBox['background-baseline-shift'];
4834				$bg_boxbottom = $this->y + $lineBox['background-boxbottom'] - $lineBox['background-baseline-shift'];
4835				$bg_boxheight = $bg_boxbottom - $bg_boxtop;
4836			}
4837			// If inline element BORDER has bounding box set by parent element:
4838			if (isset($lineBox['border-boxtop'])) {
4839				$bord_boxtop = $this->y + $lineBox['border-boxtop'] - $lineBox['border-baseline-shift'];
4840				$bord_boxbottom = $this->y + $lineBox['border-boxbottom'] - $lineBox['border-baseline-shift'];
4841				$bord_boxheight = $bord_boxbottom - $bord_boxtop;
4842			}
4843
4844		} else {
4845
4846			$boxtop = $this->y;
4847			$boxheight = $h;
4848			$boxbottom = $this->y + $h;
4849			$baseline_shift = 0;
4850
4851			if ($txt != '') {
4852
4853				// FONT SIZE - this determines the baseline caculation
4854				$bfs = $this->FontSize;
4855				// Calculate baseline Superscript and Subscript Y coordinate adjustment
4856				$bfx = $this->baselineC;
4857				$baseline = $bfx * $bfs;
4858
4859				if ($textvar & TextVars::FA_SUPERSCRIPT) {
4860					$baseline_shift = $this->textparam['text-baseline'];
4861				} elseif ($textvar & TextVars::FA_SUBSCRIPT) {
4862					$baseline_shift = $this->textparam['text-baseline'];
4863				} elseif ($this->bullet) {
4864					$baseline += ($bfx - 0.7) * $this->FontSize;
4865				}
4866
4867				// Vertical align (for Images)
4868				if ($valign == 'T') {
4869					$va = (0.5 * $bfs * $this->normalLineheight);
4870				} elseif ($valign == 'B') {
4871					$va = $h - (0.5 * $bfs * $this->normalLineheight);
4872				} else {
4873					$va = 0.5 * $h;
4874				} // Middle
4875
4876				// ONLY SET THESE IF WANT TO CONFINE BORDER +/- FILL TO FIT FONTSIZE - NOT FULL CELL AS IS ORIGINAL FUNCTION
4877				// spanfill or spanborder are set in FlowingBlock functions
4878				if ($spanfill || !empty($this->spanborddet) || $link != '') {
4879					$exth = 0.2; // Add to fontsize to increase height of background / link / border
4880					$boxtop = $this->y + $baseline + $va - ($this->FontSize * (1 + $exth / 2) * (0.5 + $bfx));
4881					$boxheight = $this->FontSize * (1 + $exth);
4882					$boxbottom = $boxtop + $boxheight;
4883				}
4884
4885				$glyphYorigin = $baseline + $va;
4886			}
4887
4888			$boxtop -= $baseline_shift;
4889			$boxbottom -= $baseline_shift;
4890			$bord_boxtop = $bg_boxtop = $boxtop;
4891			$bord_boxbottom = $bg_boxbottom = $boxbottom;
4892			$bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;
4893		}
4894
4895		$bbw = $tbw = $lbw = $rbw = 0; // Border widths
4896		if (!empty($this->spanborddet)) {
4897
4898			if (!isset($this->spanborddet['B'])) {
4899				$this->spanborddet['B'] = ['s' => 0, 'style' => '', 'w' => 0];
4900			}
4901
4902			if (!isset($this->spanborddet['T'])) {
4903				$this->spanborddet['T'] = ['s' => 0, 'style' => '', 'w' => 0];
4904			}
4905
4906			if (!isset($this->spanborddet['L'])) {
4907				$this->spanborddet['L'] = ['s' => 0, 'style' => '', 'w' => 0];
4908			}
4909
4910			if (!isset($this->spanborddet['R'])) {
4911				$this->spanborddet['R'] = ['s' => 0, 'style' => '', 'w' => 0];
4912			}
4913
4914			$bbw = $this->spanborddet['B']['w'];
4915			$tbw = $this->spanborddet['T']['w'];
4916			$lbw = $this->spanborddet['L']['w'];
4917			$rbw = $this->spanborddet['R']['w'];
4918		}
4919
4920		if ($fill == 1 || $border == 1 || !empty($this->spanborddet)) {
4921
4922			if (!empty($this->spanborddet)) {
4923
4924				if ($fill == 1) {
4925					$s .= sprintf('%.3F %.3F %.3F %.3F re f ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bg_boxtop + $tbw) * Mpdf::SCALE, ($w + $lbw + $rbw) * Mpdf::SCALE, (-$bg_boxheight - $tbw - $bbw) * Mpdf::SCALE);
4926				}
4927
4928				$s.= ' q ';
4929				$dashon = 3;
4930				$dashoff = 3.5;
4931				$dot = 2.5;
4932
4933				if ($tbw) {
4934					$short = 0;
4935
4936					if ($this->spanborddet['T']['style'] == 'dashed') {
4937						$s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $tbw * $dashon * Mpdf::SCALE, $tbw * $dashoff * Mpdf::SCALE);
4938					} elseif ($this->spanborddet['T']['style'] == 'dotted') {
4939						$s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $tbw * $dot * Mpdf::SCALE, -$tbw / 2 * Mpdf::SCALE);
4940						$short = $tbw / 2;
4941					} else {
4942						$s .= ' 0 j 0 J [] 0 d ';
4943					}
4944
4945					if ($this->spanborddet['T']['style'] != 'dotted') {
4946						$s .= 'q ';
4947						$s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
4948						$s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
4949						$s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
4950						$s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
4951						$s .= ' h W n '; // Ends path no-op & Sets the clipping path
4952					}
4953
4954					$c = $this->SetDColor($this->spanborddet['T']['c'], true);
4955
4956					if ($this->spanborddet['T']['style'] == 'double') {
4957						$s .= sprintf(' %s %.3F w ', $c, $tbw / 3 * Mpdf::SCALE);
4958						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw * 5 / 6) * Mpdf::SCALE, ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw * 5 / 6) * Mpdf::SCALE);
4959						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 6) * Mpdf::SCALE, ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 6) * Mpdf::SCALE);
4960					} elseif ($this->spanborddet['T']['style'] == 'dotted') {
4961						$s .= sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE);
4962						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE);
4963					} else {
4964						$s .= sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE);
4965						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE);
4966					}
4967
4968					if ($this->spanborddet['T']['style'] != 'dotted') {
4969						$s .= ' Q ';
4970					}
4971				}
4972				if ($bbw) {
4973
4974					$short = 0;
4975					if ($this->spanborddet['B']['style'] == 'dashed') {
4976						$s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $bbw * $dashon * Mpdf::SCALE, $bbw * $dashoff * Mpdf::SCALE);
4977					} elseif ($this->spanborddet['B']['style'] == 'dotted') {
4978						$s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $bbw * $dot * Mpdf::SCALE, -$bbw / 2 * Mpdf::SCALE);
4979						$short = $bbw / 2;
4980					} else {
4981						$s .= ' 0 j 0 J [] 0 d ';
4982					}
4983
4984					if ($this->spanborddet['B']['style'] != 'dotted') {
4985						$s .= 'q ';
4986						$s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
4987						$s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
4988						$s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
4989						$s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
4990						$s .= ' h W n '; // Ends path no-op & Sets the clipping path
4991					}
4992
4993					$c = $this->SetDColor($this->spanborddet['B']['c'], true);
4994
4995					if ($this->spanborddet['B']['style'] == 'double') {
4996						$s .= sprintf(' %s %.3F w ', $c, $bbw / 3 * Mpdf::SCALE);
4997						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 6) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 6) * Mpdf::SCALE);
4998						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw * 5 / 6) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw * 5 / 6) * Mpdf::SCALE);
4999					} elseif ($this->spanborddet['B']['style'] == 'dotted') {
5000						$s .= sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE);
5001						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE);
5002					} else {
5003						$s .= sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE);
5004						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE);
5005					}
5006
5007					if ($this->spanborddet['B']['style'] != 'dotted') {
5008						$s .= ' Q ';
5009					}
5010				}
5011
5012				if ($lbw) {
5013					$short = 0;
5014					if ($this->spanborddet['L']['style'] == 'dashed') {
5015						$s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $lbw * $dashon * Mpdf::SCALE, $lbw * $dashoff * Mpdf::SCALE);
5016					} elseif ($this->spanborddet['L']['style'] == 'dotted') {
5017						$s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $lbw * $dot * Mpdf::SCALE, -$lbw / 2 * Mpdf::SCALE);
5018						$short = $lbw / 2;
5019					} else {
5020						$s .= ' 0 j 0 J [] 0 d ';
5021					}
5022
5023					if ($this->spanborddet['L']['style'] != 'dotted') {
5024						$s .= 'q ';
5025						$s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
5026						$s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
5027						$s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
5028						$s .= sprintf('%.3F %.3F l ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
5029						$s .= ' h W n '; // Ends path no-op & Sets the clipping path
5030					}
5031
5032					$c = $this->SetDColor($this->spanborddet['L']['c'], true);
5033					if ($this->spanborddet['L']['style'] == 'double') {
5034						$s .= sprintf(' %s %.3F w ', $c, $lbw / 3 * Mpdf::SCALE);
5035						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);
5036						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);
5037					} elseif ($this->spanborddet['L']['style'] == 'dotted') {
5038						$s .= sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE);
5039						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);
5040					} else {
5041						$s .= sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE);
5042						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);
5043					}
5044
5045					if ($this->spanborddet['L']['style'] != 'dotted') {
5046						$s .= ' Q ';
5047					}
5048				}
5049
5050				if ($rbw) {
5051
5052					$short = 0;
5053					if ($this->spanborddet['R']['style'] == 'dashed') {
5054						$s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $rbw * $dashon * Mpdf::SCALE, $rbw * $dashoff * Mpdf::SCALE);
5055					} elseif ($this->spanborddet['R']['style'] == 'dotted') {
5056						$s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $rbw * $dot * Mpdf::SCALE, -$rbw / 2 * Mpdf::SCALE);
5057						$short = $rbw / 2;
5058					} else {
5059						$s .= ' 0 j 0 J [] 0 d ';
5060					}
5061
5062					if ($this->spanborddet['R']['style'] != 'dotted') {
5063						$s .= 'q ';
5064						$s .= sprintf('%.3F %.3F m ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
5065						$s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
5066						$s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
5067						$s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
5068						$s .= ' h W n '; // Ends path no-op & Sets the clipping path
5069					}
5070
5071					$c = $this->SetDColor($this->spanborddet['R']['c'], true);
5072					if ($this->spanborddet['R']['style'] == 'double') {
5073						$s .= sprintf(' %s %.3F w ', $c, $rbw / 3 * Mpdf::SCALE);
5074						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);
5075						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);
5076					} elseif ($this->spanborddet['R']['style'] == 'dotted') {
5077						$s .= sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE);
5078						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);
5079					} else {
5080						$s .= sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE);
5081						$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);
5082					}
5083
5084					if ($this->spanborddet['R']['style'] != 'dotted') {
5085						$s .= ' Q ';
5086					}
5087				}
5088
5089				$s.= ' Q ';
5090
5091			} else { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock
5092
5093				if ($fill == 1) {
5094					$op = ($border == 1) ? 'B' : 'f';
5095				} else {
5096					$op = 'S';
5097				}
5098
5099				$s .= sprintf('%.3F %.3F %.3F %.3F re %s ', $this->x * Mpdf::SCALE, ($this->h - $bg_boxtop) * Mpdf::SCALE, $w * Mpdf::SCALE, -$bg_boxheight * Mpdf::SCALE, $op);
5100			}
5101		}
5102
5103		if (is_string($border)) { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock
5104
5105			$x = $this->x;
5106			$y = $this->y;
5107
5108			if (is_int(strpos($border, 'L'))) {
5109				$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE);
5110			}
5111
5112			if (is_int(strpos($border, 'T'))) {
5113				$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
5114			}
5115
5116			if (is_int(strpos($border, 'R'))) {
5117				$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE);
5118			}
5119
5120			if (is_int(strpos($border, 'B'))) {
5121				$s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE);
5122			}
5123		}
5124
5125		if ($txt != '') {
5126
5127			if ($exactWidth) {
5128				$stringWidth = $w;
5129			} else {
5130				$stringWidth = $this->GetStringWidth($txt, true, $OTLdata, $textvar) + ( $this->charspacing * mb_strlen($txt, $this->mb_enc) / Mpdf::SCALE ) + ( $this->ws * mb_substr_count($txt, ' ', $this->mb_enc) / Mpdf::SCALE );
5131			}
5132
5133			// Set x OFFSET FOR PRINTING
5134			if ($align == 'R') {
5135				$dx = $w - $this->cMarginR - $stringWidth - $lcpaddingR;
5136			} elseif ($align == 'C') {
5137				$dx = (($w - $stringWidth ) / 2);
5138			} elseif ($align == 'L' or $align == 'J') {
5139				$dx = $this->cMarginL + $lcpaddingL;
5140			} else {
5141				$dx = 0;
5142			}
5143
5144			if ($this->ColorFlag) {
5145				$s .='q ' . $this->TextColor . ' ';
5146			}
5147
5148			// OUTLINE
5149			if (isset($this->textparam['outline-s']) && $this->textparam['outline-s'] && !($textvar & TextVars::FC_SMALLCAPS)) { // mPDF 5.7.1
5150				$s .=' ' . sprintf('%.3F w', $this->LineWidth * Mpdf::SCALE) . ' ';
5151				$s .=" $this->DrawColor ";
5152				$s .=" 2 Tr ";
5153			} elseif ($this->falseBoldWeight && strpos($this->ReqFontStyle, "B") !== false && strpos($this->FontStyle, "B") === false && !($textvar & TextVars::FC_SMALLCAPS)) { // can't use together with OUTLINE or Small Caps	// mPDF 5.7.1	??? why not with SmallCaps ???
5154				$s .= ' 2 Tr 1 J 1 j ';
5155				$s .= ' ' . sprintf('%.3F w', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight) . ' ';
5156				$tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
5157				if ($this->FillColor != $tc) {
5158					$s .= ' ' . $tc . ' ';
5159				}  // stroke (outline) = same colour as text(fill)
5160			} else {
5161				$s .=" 0 Tr ";
5162			}
5163
5164			if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) { // Artificial italic
5165				$aix = '1 0 0.261799 1 %.3F %.3F Tm ';
5166			} else {
5167				$aix = '%.3F %.3F Td ';
5168			}
5169
5170			$px = ($this->x + $dx) * Mpdf::SCALE;
5171			$py = ($this->h - ($this->y + $glyphYorigin - $baseline_shift)) * Mpdf::SCALE;
5172
5173			// THE TEXT
5174			$txt2 = $txt;
5175			$sub = '';
5176			$this->CurrentFont['used'] = true;
5177
5178			/*             * ************** SIMILAR TO Text() ************************ */
5179
5180			// IF corefonts AND NOT SmCaps AND NOT Kerning
5181			// Just output text; charspacing and wordspacing already set by charspacing (Tc) and ws (Tw)
5182			if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) {
5183				$txt2 = $this->writer->escape($txt2);
5184				$sub .= sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
5185			} // IF NOT corefonts AND NO wordspacing AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL
5186			// Just output text
5187			elseif (!$this->usingCoreFont && !$this->ws && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {
5188				// IF SIP/SMP
5189				if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {
5190					$txt2 = $this->UTF8toSubset($txt2);
5191					$sub .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);
5192				} // NOT SIP/SMP
5193				else {
5194					$txt2 = $this->writer->utf8ToUtf16BigEndian($txt2, false);
5195					$txt2 = $this->writer->escape($txt2);
5196					$sub .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
5197				}
5198			} // IF NOT corefonts AND IS wordspacing AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL
5199			// Output text word by word with an adjustment to the intercharacter spacing for SPACEs to form word spacing
5200			// IF multibyte - Tw has no effect - need to do word spacing using an adjustment before each space
5201			elseif (!$this->usingCoreFont && $this->ws && !((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && (!empty($OTLdata['GPOSinfo']) || (strpos($OTLdata['group'], 'M') !== false && $this->charspacing)) )) {
5202				$space = " ";
5203				$space = $this->writer->utf8ToUtf16BigEndian($space, false);
5204				$space = $this->writer->escape($space);
5205				$sub .=sprintf('BT ' . $aix . ' %.3F Tc [', $px, $py, $this->charspacing);
5206				$t = explode(' ', $txt2);
5207				$numt = count($t);
5208				for ($i = 0; $i < $numt; $i++) {
5209					$tx = $t[$i];
5210					$tx = $this->writer->utf8ToUtf16BigEndian($tx, false);
5211					$tx = $this->writer->escape($tx);
5212					$sub .=sprintf('(%s) ', $tx);
5213					if (($i + 1) < $numt) {
5214						$adj = -($this->ws) * 1000 / $this->FontSizePt;
5215						$sub .=sprintf('%d(%s) ', $adj, $space);
5216					}
5217				}
5218				$sub .='] TJ ';
5219				$sub .=' ET';
5220			} // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]
5221			else {
5222				$sub = $this->applyGPOSpdf($txt, $aix, $px, $py, $OTLdata, $textvar);
5223			}
5224
5225			/** ************** END SIMILAR TO Text() ************************ */
5226
5227			if ($this->shrin_k > 1) {
5228				$shrin_k = $this->shrin_k;
5229			} else {
5230				$shrin_k = 1;
5231			}
5232
5233			// UNDERLINE
5234			if ($textvar & TextVars::FD_UNDERLINE) { // mPDF 5.7.1	// mPDF 6
5235
5236				// mPDF 5.7.3  inline text-decoration parameters
5237
5238				$c = isset($this->textparam['u-decoration']['color']) ? $this->textparam['u-decoration']['color'] : '';
5239				if ($this->FillColor != $c) {
5240					$sub .= ' ' . $c . ' ';
5241				}
5242
5243				// mPDF 5.7.3  inline text-decoration parameters
5244				$decorationfontkey = isset($this->textparam['u-decoration']['fontkey']) ? $this->textparam['u-decoration']['fontkey'] : '';
5245				$decorationfontsize = isset($this->textparam['u-decoration']['fontsize']) ? $this->textparam['u-decoration']['fontsize'] / $shrin_k : 0;
5246
5247				if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
5248					$ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
5249				} else {
5250					$ut = 60 / 1000 * $decorationfontsize;
5251				}
5252
5253				if (isset($this->fonts[$decorationfontkey]['up']) && $this->fonts[$decorationfontkey]['up']) {
5254					$up = $this->fonts[$decorationfontkey]['up'];
5255				} else {
5256					$up = -100;
5257				}
5258
5259				$adjusty = (-$up / 1000 * $decorationfontsize) + $ut / 2;
5260				$ubaseline = isset($this->textparam['u-decoration']['baseline'])
5261					? $glyphYorigin - $this->textparam['u-decoration']['baseline'] / $shrin_k
5262					: $glyphYorigin;
5263
5264				$olw = $this->LineWidth;
5265
5266				$sub .= ' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
5267				$sub .= ' ' . $this->_dounderline($this->x + $dx, $this->y + $ubaseline + $adjusty, $txt, $OTLdata, $textvar);
5268				$sub .= ' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
5269
5270				if ($this->FillColor != $c) {
5271					$sub .= ' ' . $this->FillColor . ' ';
5272				}
5273			}
5274
5275			// STRIKETHROUGH
5276			if ($textvar & TextVars::FD_LINETHROUGH) { // mPDF 5.7.1	// mPDF 6
5277
5278				// mPDF 5.7.3  inline text-decoration parameters
5279				$c = $this->textparam['s-decoration']['color'];
5280
5281				if ($this->FillColor != $c) {
5282					$sub .= ' ' . $c . ' ';
5283				}
5284
5285				// mPDF 5.7.3  inline text-decoration parameters
5286				$decorationfontkey = $this->textparam['s-decoration']['fontkey'];
5287				$decorationfontsize = $this->textparam['s-decoration']['fontsize'] / $shrin_k;
5288
5289				// Use yStrikeoutSize from OS/2 if available
5290				if (isset($this->fonts[$decorationfontkey]['strs']) && $this->fonts[$decorationfontkey]['strs']) {
5291					$ut = $this->fonts[$decorationfontkey]['strs'] / 1000 * $decorationfontsize;
5292				} // else use underlineThickness from post if available
5293				elseif (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
5294					$ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
5295				} else {
5296					$ut = 50 / 1000 * $decorationfontsize;
5297				}
5298
5299				// Use yStrikeoutPosition from OS/2 if available
5300				if (isset($this->fonts[$decorationfontkey]['strp']) && $this->fonts[$decorationfontkey]['strp']) {
5301					$up = $this->fonts[$decorationfontkey]['strp'];
5302					$adjusty = (-$up / 1000 * $decorationfontsize);
5303				} // else use a fraction ($this->baselineS) of CapHeight
5304				else {
5305					if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {
5306						$ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];
5307					} else {
5308						$ch = 700;
5309					}
5310					$adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineS;
5311				}
5312
5313				$sbaseline = $glyphYorigin - $this->textparam['s-decoration']['baseline'] / $shrin_k;
5314
5315				$olw = $this->LineWidth;
5316
5317				$sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
5318				$sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $sbaseline + $adjusty, $txt, $OTLdata, $textvar);
5319				$sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
5320
5321				if ($this->FillColor != $c) {
5322					$sub .= ' ' . $this->FillColor . ' ';
5323				}
5324			}
5325
5326			// mPDF 5.7.3  inline text-decoration parameters
5327			// OVERLINE
5328			if ($textvar & TextVars::FD_OVERLINE) { // mPDF 5.7.1	// mPDF 6
5329				// mPDF 5.7.3  inline text-decoration parameters
5330				$c = $this->textparam['o-decoration']['color'];
5331				if ($this->FillColor != $c) {
5332					$sub .= ' ' . $c . ' ';
5333				}
5334
5335				// mPDF 5.7.3  inline text-decoration parameters
5336				$decorationfontkey = (int) (((float) $this->textparam['o-decoration']['fontkey']) / $shrin_k);
5337				$decorationfontsize = $this->textparam['o-decoration']['fontsize'];
5338
5339				if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
5340					$ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
5341				} else {
5342					$ut = 60 / 1000 * $decorationfontsize;
5343				}
5344				if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {
5345					$ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];
5346				} else {
5347					$ch = 700;
5348				}
5349				$adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineO;
5350				$obaseline = $glyphYorigin - $this->textparam['o-decoration']['baseline'] / $shrin_k;
5351				$olw = $this->LineWidth;
5352				$sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
5353				$sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $obaseline + $adjusty, $txt, $OTLdata, $textvar);
5354				$sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
5355				if ($this->FillColor != $c) {
5356					$sub .= ' ' . $this->FillColor . ' ';
5357				}
5358			}
5359
5360			// TEXT SHADOW
5361			if ($this->textshadow) {  // First to process is last in CSS comma separated shadows
5362				foreach ($this->textshadow as $ts) {
5363					$s .= ' q ';
5364					$s .= $this->SetTColor($ts['col'], true) . "\n";
5365					if ($ts['col'][0] == 5 && ord($ts['col'][4]) < 100) { // RGBa
5366						$s .= $this->SetAlpha(ord($ts['col'][4]) / 100, 'Normal', true, 'F') . "\n";
5367					} elseif ($ts['col'][0] == 6 && ord($ts['col'][5]) < 100) { // CMYKa
5368						$s .= $this->SetAlpha(ord($ts['col'][5]) / 100, 'Normal', true, 'F') . "\n";
5369					} elseif ($ts['col'][0] == 1 && $ts['col'][2] == 1 && ord($ts['col'][3]) < 100) { // Gray
5370						$s .= $this->SetAlpha(ord($ts['col'][3]) / 100, 'Normal', true, 'F') . "\n";
5371					}
5372					$s .= sprintf(' 1 0 0 1 %.4F %.4F cm', $ts['x'] * Mpdf::SCALE, -$ts['y'] * Mpdf::SCALE) . "\n";
5373					$s .= $sub;
5374					$s .= ' Q ';
5375				}
5376			}
5377
5378			$s .= $sub;
5379
5380			// COLOR
5381			if ($this->ColorFlag) {
5382				$s .=' Q';
5383			}
5384
5385			// LINK
5386			if ($link != '') {
5387				$this->Link($this->x, $boxtop, $w, $boxheight, $link);
5388			}
5389		}
5390		if ($s) {
5391			$this->writer->write($s);
5392		}
5393
5394		// WORD SPACING
5395		if ($this->ws && !$this->usingCoreFont) {
5396			$this->writer->write(sprintf('BT %.3F Tc ET', $this->charspacing));
5397		}
5398		$this->lasth = $h;
5399		if (strpos($txt, "\n") !== false) {
5400			$ln = 1; // cell recognizes \n from <BR> tag
5401		}
5402		if ($ln > 0) {
5403			// Go to next line
5404			$this->y += $h;
5405			if ($ln == 1) {
5406				// Move to next line
5407				if ($currentx != 0) {
5408					$this->x = $currentx;
5409				} else {
5410					$this->x = $this->lMargin;
5411				}
5412			}
5413		} else {
5414			$this->x+=$w;
5415		}
5416	}
5417
5418	function applyGPOSpdf($txt, $aix, $x, $y, $OTLdata, $textvar = 0)
5419	{
5420		// Generate PDF string
5421		// ==============================
5422		if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {
5423			$sipset = true;
5424		} else {
5425			$sipset = false;
5426		}
5427
5428		if ($textvar & TextVars::FC_SMALLCAPS) {
5429			$smcaps = true;
5430		} // IF SmallCaps using transformation, NOT OTL
5431		else {
5432			$smcaps = false;
5433		}
5434
5435		if ($sipset) {
5436			$fontid = $last_fontid = $original_fontid = $this->CurrentFont['subsetfontids'][0];
5437		} else {
5438			$fontid = $last_fontid = $original_fontid = $this->CurrentFont['i'];
5439		}
5440		$SmallCapsON = false;  // state: uppercase/not
5441		$lastSmallCapsON = false; // state: uppercase/not
5442		$last_fontsize = $fontsize = $this->FontSizePt;
5443		$last_fontstretch = $fontstretch = 100;
5444		$groupBreak = false;
5445
5446		$unicode = $this->UTF8StringToArray($txt);
5447
5448		$GPOSinfo = (isset($OTLdata['GPOSinfo']) ? $OTLdata['GPOSinfo'] : []);
5449		$charspacing = ($this->charspacing * 1000 / $this->FontSizePt);
5450		$wordspacing = ($this->ws * 1000 / $this->FontSizePt);
5451
5452		$XshiftBefore = 0;
5453		$XshiftAfter = 0;
5454		$lastYPlacement = 0;
5455
5456		if ($sipset) {
5457			// mPDF 6  DELETED ********
5458			// 	$txt= preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $txt);	// ? Need to adjust OTL info
5459			// 	$txt= preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $txt);	// ? Need to adjust OTL info
5460			$tj = '<';
5461		} else {
5462			$tj = '(';
5463		}
5464
5465		for ($i = 0; $i < count($unicode); $i++) {
5466			$c = $unicode[$i];
5467			$tx = '';
5468			$XshiftBefore = $XshiftAfter;
5469			$XshiftAfter = 0;
5470			$YPlacement = 0;
5471			$groupBreak = false;
5472			$kashida = 0;
5473			if (!empty($OTLdata)) {
5474				// YPlacement from GPOS
5475				if (isset($GPOSinfo[$i]['YPlacement']) && $GPOSinfo[$i]['YPlacement']) {
5476					$YPlacement = $GPOSinfo[$i]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];
5477					$groupBreak = true;
5478				}
5479				// XPlacement from GPOS
5480				if (isset($GPOSinfo[$i]['XPlacement']) && $GPOSinfo[$i]['XPlacement']) {
5481					if (!isset($GPOSinfo[$i]['wDir']) || $GPOSinfo[$i]['wDir'] != 'RTL') {
5482						if (isset($GPOSinfo[$i]['BaseWidth'])) {
5483							$GPOSinfo[$i]['XPlacement'] -= $GPOSinfo[$i]['BaseWidth'];
5484						}
5485					}
5486
5487					// Convert to PDF Text space (thousandths of a unit );
5488					$XshiftBefore += $GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
5489					$XshiftAfter += -$GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
5490				}
5491
5492				// Kashida from GPOS
5493				// Kashida is set as an absolute length value, but to adjust text needs to be converted to
5494				// font-related size
5495				if (isset($GPOSinfo[$i]['kashida_space']) && $GPOSinfo[$i]['kashida_space']) {
5496					$kashida = $GPOSinfo[$i]['kashida_space'];
5497				}
5498
5499				if ($c == 32) { // word spacing
5500					$XshiftAfter += $wordspacing;
5501				}
5502
5503				if (substr($OTLdata['group'], ($i + 1), 1) != 'M') { // Don't add inter-character spacing before Marks
5504					$XshiftAfter += $charspacing;
5505				}
5506
5507				// ...applyGPOSpdf...
5508				// XAdvance from GPOS - Convert to PDF Text space (thousandths of a unit );
5509				if (((isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] != 'RTL') || !isset($GPOSinfo[$i]['wDir'])) && isset($GPOSinfo[$i]['XAdvanceL']) && $GPOSinfo[$i]['XAdvanceL']) {
5510					$XshiftAfter += $GPOSinfo[$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
5511				} elseif (isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] == 'RTL' && isset($GPOSinfo[$i]['XAdvanceR']) && $GPOSinfo[$i]['XAdvanceR']) {
5512					$XshiftAfter += $GPOSinfo[$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
5513				}
5514			} // Character & Word spacing - if NOT OTL
5515			else {
5516				$XshiftAfter += $charspacing;
5517				if ($c == 32) {
5518					$XshiftAfter += $wordspacing;
5519				}
5520			}
5521
5522			// IF Kerning done using pairs rather than OTL
5523			if ($textvar & TextVars::FC_KERNING) {
5524				if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {
5525					$XshiftBefore += $this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];
5526				}
5527			}
5528
5529			if ($YPlacement != $lastYPlacement) {
5530				$groupBreak = true;
5531			}
5532
5533			if ($XshiftBefore) {  // +ve value in PDF moves to the left
5534				// If Fontstretch is ongoing, need to adjust X adjustments because these will be stretched out.
5535				$XshiftBefore *= 100 / $last_fontstretch;
5536				if ($sipset) {
5537					$tj .= sprintf('>%d<', (-$XshiftBefore));
5538				} else {
5539					$tj .= sprintf(')%d(', (-$XshiftBefore));
5540				}
5541			}
5542
5543			// Small-Caps
5544			if ($smcaps) {
5545				if (isset($this->upperCase[$c])) {
5546					$c = $this->upperCase[$c];
5547					// $this->CurrentFont['subset'][$this->upperCase[$c]] = $this->upperCase[$c];	// add the CAP to subset
5548					$SmallCapsON = true;
5549					// For $sipset
5550					if (!$lastSmallCapsON) {   // Turn ON SmallCaps
5551						$groupBreak = true;
5552						$fontstretch = $this->smCapsStretch;
5553						$fontsize = $this->FontSizePt * $this->smCapsScale;
5554					}
5555				} else {
5556					$SmallCapsON = false;
5557					if ($lastSmallCapsON) {  // Turn OFF SmallCaps
5558						$groupBreak = true;
5559						$fontstretch = 100;
5560						$fontsize = $this->FontSizePt;
5561					}
5562				}
5563			}
5564
5565			// Prepare Text and Select Font ID
5566			if ($sipset) {
5567				// mPDF 6  DELETED ********
5568				// if ($c == 7 || $c == 8) {
5569				// if ($original_fontid != $last_fontid) {
5570				// 	$groupBreak = true;
5571				// 	$fontid = $original_fontid;
5572				// }
5573				// if ($c == 7) { $tj .= $this->aliasNbPgHex; }
5574				// else { $tj .= $this->aliasNbPgGpHex; }
5575				// continue;
5576				// }
5577				for ($j = 0; $j < 99; $j++) {
5578					$init = array_search($c, $this->CurrentFont['subsets'][$j]);
5579					if ($init !== false) {
5580						if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
5581							$groupBreak = true;
5582							$fontid = $this->CurrentFont['subsetfontids'][$j];
5583						}
5584						$tx = sprintf("%02s", strtoupper(dechex($init)));
5585						break;
5586					} elseif (count($this->CurrentFont['subsets'][$j]) < 255) {
5587						$n = count($this->CurrentFont['subsets'][$j]);
5588						$this->CurrentFont['subsets'][$j][$n] = $c;
5589						if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
5590							$groupBreak = true;
5591							$fontid = $this->CurrentFont['subsetfontids'][$j];
5592						}
5593						$tx = sprintf("%02s", strtoupper(dechex($n)));
5594						break;
5595					} elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) {
5596						$this->CurrentFont['subsets'][($j + 1)] = [0 => 0];
5597						$this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;
5598						$this->extraFontSubsets++;
5599					}
5600				}
5601			} else {
5602				$tx = UtfString::code2utf($c);
5603				if ($this->usingCoreFont) {
5604					$tx = utf8_decode($tx);
5605				} else {
5606					$tx = $this->writer->utf8ToUtf16BigEndian($tx, false);
5607				}
5608				$tx = $this->writer->escape($tx);
5609			}
5610
5611			// If any settings require a new Text Group
5612			if ($groupBreak || $fontstretch != $last_fontstretch) {
5613				if ($sipset) {
5614					$tj .= '>] TJ ';
5615				} else {
5616					$tj .= ')] TJ ';
5617				}
5618				if ($fontid != $last_fontid || $fontsize != $last_fontsize) {
5619					$tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);
5620				}
5621				if ($fontstretch != $last_fontstretch) {
5622					$tj .= sprintf('%d Tz ', $fontstretch);
5623				}
5624				if ($YPlacement != $lastYPlacement) {
5625					$tj .= sprintf('%.3F Ts ', $YPlacement);
5626				}
5627				if ($sipset) {
5628					$tj .= '[<';
5629				} else {
5630					$tj .= '[(';
5631				}
5632			}
5633
5634			// Output the code for the txt character
5635			$tj .= $tx;
5636			$lastSmallCapsON = $SmallCapsON;
5637			$last_fontid = $fontid;
5638			$last_fontsize = $fontsize;
5639			$last_fontstretch = $fontstretch;
5640
5641			// Kashida
5642			if ($kashida) {
5643				$c = 0x0640; // add the Tatweel U+0640
5644				if (isset($this->CurrentFont['subset'])) {
5645					$this->CurrentFont['subset'][$c] = $c;
5646				}
5647				$kashida *= 1000 / $this->FontSizePt;
5648				$tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);
5649
5650				// Get YPlacement from next Base character
5651				$nextbase = $i + 1;
5652				while ($OTLdata['group'][$nextbase] != 'C') {
5653					$nextbase++;
5654				}
5655				if (isset($GPOSinfo[$nextbase]) && isset($GPOSinfo[$nextbase]['YPlacement']) && $GPOSinfo[$nextbase]['YPlacement']) {
5656					$YPlacement = $GPOSinfo[$nextbase]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];
5657				}
5658
5659				// Prepare Text and Select Font ID
5660				if ($sipset) {
5661					for ($j = 0; $j < 99; $j++) {
5662						$init = array_search($c, $this->CurrentFont['subsets'][$j]);
5663						if ($init !== false) {
5664							if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
5665								$fontid = $this->CurrentFont['subsetfontids'][$j];
5666							}
5667							$tx = sprintf("%02s", strtoupper(dechex($init)));
5668							break;
5669						} elseif (count($this->CurrentFont['subsets'][$j]) < 255) {
5670							$n = count($this->CurrentFont['subsets'][$j]);
5671							$this->CurrentFont['subsets'][$j][$n] = $c;
5672							if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
5673								$fontid = $this->CurrentFont['subsetfontids'][$j];
5674							}
5675							$tx = sprintf("%02s", strtoupper(dechex($n)));
5676							break;
5677						} elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) {
5678							$this->CurrentFont['subsets'][($j + 1)] = [0 => 0];
5679							$this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;
5680							$this->extraFontSubsets++;
5681						}
5682					}
5683				} else {
5684					$tx = UtfString::code2utf($c);
5685					$tx = $this->writer->utf8ToUtf16BigEndian($tx, false);
5686					$tx = $this->writer->escape($tx);
5687				}
5688
5689				if ($kashida > $tatw) {
5690					// Insert multiple tatweel characters, repositioning the last one to give correct total length
5691					$fontstretch = 100;
5692					$nt = intval($kashida / $tatw);
5693					$nudgeback = (($nt + 1) * $tatw) - $kashida;
5694					$optx = str_repeat($tx, $nt);
5695					if ($sipset) {
5696						$optx .= sprintf('>%d<', ($nudgeback));
5697					} else {
5698						$optx .= sprintf(')%d(', ($nudgeback));
5699					}
5700					$optx .= $tx; // #last
5701				} else {
5702					// Insert single tatweel character and use fontstretch to get correct length
5703					$fontstretch = ($kashida / $tatw) * 100;
5704					$optx = $tx;
5705				}
5706
5707				if ($sipset) {
5708					$tj .= '>] TJ ';
5709				} else {
5710					$tj .= ')] TJ ';
5711				}
5712				if ($fontid != $last_fontid || $fontsize != $last_fontsize) {
5713					$tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);
5714				}
5715				if ($fontstretch != $last_fontstretch) {
5716					$tj .= sprintf('%d Tz ', $fontstretch);
5717				}
5718				$tj .= sprintf('%.3F Ts ', $YPlacement);
5719				if ($sipset) {
5720					$tj .= '[<';
5721				} else {
5722					$tj .= '[(';
5723				}
5724
5725				// Output the code for the txt character(s)
5726				$tj .= $optx;
5727				$last_fontid = $fontid;
5728				$last_fontstretch = $fontstretch;
5729				$fontstretch = 100;
5730			}
5731
5732			$lastYPlacement = $YPlacement;
5733		}
5734
5735
5736		// Finish up
5737		if ($sipset) {
5738			$tj .= '>';
5739			if ($XshiftAfter) {
5740				$tj .= sprintf('%d', (-$XshiftAfter));
5741			}
5742			if ($last_fontid != $original_fontid) {
5743				$tj .= '] TJ ';
5744				$tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);
5745				$tj .= '[';
5746			}
5747			$tj = preg_replace('/([^\\\])<>/', '\\1 ', $tj);
5748		} else {
5749			$tj .= ')';
5750			if ($XshiftAfter) {
5751				$tj .= sprintf('%d', (-$XshiftAfter));
5752			}
5753			if ($last_fontid != $original_fontid) {
5754				$tj .= '] TJ ';
5755				$tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);
5756				$tj .= '[';
5757			}
5758			$tj = preg_replace('/([^\\\])\(\)/', '\\1 ', $tj);
5759		}
5760
5761		$s = sprintf(' BT ' . $aix . ' 0 Tc 0 Tw [%s] TJ ET ', $x, $y, $tj);
5762
5763		// echo $s."\n\n"; // exit;
5764
5765		return $s;
5766	}
5767
5768	function _kern($txt, $mode, $aix, $x, $y)
5769	{
5770		if ($mode == 'MBTw') { // Multibyte requiring word spacing
5771			$space = ' ';
5772			// Convert string to UTF-16BE without BOM
5773			$space = $this->writer->utf8ToUtf16BigEndian($space, false);
5774			$space = $this->writer->escape($space);
5775			$s = sprintf(' BT ' . $aix, $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE);
5776			$t = explode(' ', $txt);
5777			for ($i = 0; $i < count($t); $i++) {
5778				$tx = $t[$i];
5779
5780				$tj = '(';
5781				$unicode = $this->UTF8StringToArray($tx);
5782				for ($ti = 0; $ti < count($unicode); $ti++) {
5783					if ($ti > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]])) {
5784						$kern = -$this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]];
5785						$tj .= sprintf(')%d(', $kern);
5786					}
5787					$tc = UtfString::code2utf($unicode[$ti]);
5788					$tc = $this->writer->utf8ToUtf16BigEndian($tc, false);
5789					$tj .= $this->writer->escape($tc);
5790				}
5791				$tj .= ')';
5792				$s .= sprintf(' %.3F Tc [%s] TJ', $this->charspacing, $tj);
5793
5794
5795				if (($i + 1) < count($t)) {
5796					$s .= sprintf(' %.3F Tc (%s) Tj', $this->ws + $this->charspacing, $space);
5797				}
5798			}
5799			$s .= ' ET ';
5800		} elseif (!$this->usingCoreFont) {
5801			$s = '';
5802			$tj = '(';
5803			$unicode = $this->UTF8StringToArray($txt);
5804			for ($i = 0; $i < count($unicode); $i++) {
5805				if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {
5806					$kern = -$this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];
5807					$tj .= sprintf(')%d(', $kern);
5808				}
5809				$tx = UtfString::code2utf($unicode[$i]);
5810				$tx = $this->writer->utf8ToUtf16BigEndian($tx, false);
5811				$tj .= $this->writer->escape($tx);
5812			}
5813			$tj .= ')';
5814			$s .= sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj);
5815		} else { // CORE Font
5816			$s = '';
5817			$tj = '(';
5818			$l = strlen($txt);
5819			for ($i = 0; $i < $l; $i++) {
5820				if ($i > 0 && isset($this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]])) {
5821					$kern = -$this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]];
5822					$tj .= sprintf(')%d(', $kern);
5823				}
5824				$tj .= $this->writer->escape($txt[$i]);
5825			}
5826			$tj .= ')';
5827			$s .= sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj);
5828		}
5829
5830		return $s;
5831	}
5832
5833	function MultiCell(
5834		$w,
5835		$h,
5836		$txt,
5837		$border = 0,
5838		$align = '',
5839		$fill = 0,
5840		$link = '',
5841		$directionality = 'ltr',
5842		$encoded = false,
5843		$OTLdata = false,
5844		$maxrows = false
5845	) {
5846		// maxrows is called from mpdfform->TEXTAREA
5847		// Parameter (pre-)encoded - When called internally from form::textarea -
5848		// mb_encoding already done and OTL - but not reverse RTL
5849		if (!$encoded) {
5850
5851			$txt = $this->purify_utf8_text($txt);
5852
5853			if ($this->text_input_as_HTML) {
5854				$txt = $this->all_entities_to_utf8($txt);
5855			}
5856
5857			if ($this->usingCoreFont) {
5858				$txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
5859			}
5860
5861			if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
5862				$this->biDirectional = true;
5863			}
5864
5865			/* -- OTL -- */
5866			if (!is_array($OTLdata)) {
5867				unset($OTLdata);
5868			}
5869
5870			// Use OTL OpenType Table Layout - GSUB & GPOS
5871			if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
5872				$txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
5873				$OTLdata = $this->otl->OTLdata;
5874			}
5875
5876			if ($directionality == 'rtl' || $this->biDirectional) {
5877				if (!isset($OTLdata)) {
5878					$unicode = $this->UTF8StringToArray($txt, false);
5879					$is_strong = false;
5880					$this->getBasicOTLdata($OTLdata, $unicode, $is_strong);
5881				}
5882			}
5883			/* -- END OTL -- */
5884		}
5885
5886		if (!$align) {
5887			$align = $this->defaultAlign;
5888		}
5889
5890		// Output text with automatic or explicit line breaks
5891		$cw = &$this->CurrentFont['cw'];
5892
5893		if ($w == 0) {
5894			$w = $this->w - $this->rMargin - $this->x;
5895		}
5896
5897		$wmax = ($w - ($this->cMarginL + $this->cMarginR));
5898
5899		if ($this->usingCoreFont) {
5900			$s = str_replace("\r", '', $txt);
5901			$nb = strlen($s);
5902			while ($nb > 0 and $s[$nb - 1] == "\n") {
5903				$nb--;
5904			}
5905		} else {
5906			$s = str_replace("\r", '', $txt);
5907			$nb = mb_strlen($s, $this->mb_enc);
5908			while ($nb > 0 and mb_substr($s, $nb - 1, 1, $this->mb_enc) == "\n") {
5909				$nb--;
5910			}
5911		}
5912
5913		$b = 0;
5914
5915		if ($border) {
5916
5917			if ($border == 1) {
5918				$border = 'LTRB';
5919				$b = 'LRT';
5920				$b2 = 'LR';
5921			} else {
5922				$b2 = '';
5923				if (is_int(strpos($border, 'L'))) {
5924					$b2 .= 'L';
5925				}
5926				if (is_int(strpos($border, 'R'))) {
5927					$b2 .= 'R';
5928				}
5929				$b = is_int(strpos($border, 'T')) ? $b2 . 'T' : $b2;
5930			}
5931		}
5932
5933		$sep = -1;
5934		$i = 0;
5935		$j = 0;
5936		$l = 0;
5937		$ns = 0;
5938		$nl = 1;
5939
5940		$rows = 0;
5941		$start_y = $this->y;
5942
5943		if (!$this->usingCoreFont) {
5944
5945			$inclCursive = false;
5946
5947			if (preg_match("/([" . $this->pregCURSchars . "])/u", $s)) {
5948				$inclCursive = true;
5949			}
5950
5951			while ($i < $nb) {
5952
5953				// Get next character
5954				$c = mb_substr($s, $i, 1, $this->mb_enc);
5955
5956				if ($c === "\n") { // Explicit line break
5957
5958					// WORD SPACING
5959					$this->ResetSpacing();
5960					$tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
5961					$tmpOTLdata = false;
5962
5963					/* -- OTL -- */
5964					if (isset($OTLdata)) {
5965						$tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
5966						$this->otl->trimOTLdata($tmpOTLdata, false, true);
5967						$this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
5968					}
5969					/* -- END OTL -- */
5970
5971					$this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
5972
5973					if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
5974						return false;
5975					}
5976
5977					$i++;
5978					$sep = -1;
5979					$j = $i;
5980					$l = 0;
5981					$ns = 0;
5982					$nl++;
5983
5984					if ($border and $nl == 2) {
5985						$b = $b2;
5986					}
5987
5988					continue;
5989				}
5990
5991				if ($c == " ") {
5992					$sep = $i;
5993					$ls = $l;
5994					$ns++;
5995				}
5996
5997				$l += $this->GetCharWidthNonCore($c);
5998
5999				if ($l > $wmax) {
6000
6001					// Automatic line break
6002					if ($sep == -1) { // Only one word
6003
6004						if ($i == $j) {
6005							$i++;
6006						}
6007
6008						// WORD SPACING
6009						$this->ResetSpacing();
6010						$tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
6011						$tmpOTLdata = false;
6012
6013						/* -- OTL -- */
6014						if (isset($OTLdata)) {
6015							$tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
6016							$this->otl->trimOTLdata($tmpOTLdata, false, true);
6017							$this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
6018						}
6019						/* -- END OTL -- */
6020
6021						$this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
6022
6023					} else {
6024
6025						$tmp = rtrim(mb_substr($s, $j, $sep - $j, $this->mb_enc));
6026						$tmpOTLdata = false;
6027
6028						/* -- OTL -- */
6029						if (isset($OTLdata)) {
6030							$tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $sep - $j);
6031							$this->otl->trimOTLdata($tmpOTLdata, false, true);
6032						}
6033						/* -- END OTL -- */
6034
6035						if ($align === 'J') {
6036
6037							// JUSTIFY J using Unicode fonts (Word spacing doesn't work)
6038							// WORD SPACING UNICODE
6039							// Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
6040
6041							$tmp = str_replace(chr(194) . chr(160), chr(32), $tmp);
6042							$len_ligne = $this->GetStringWidth($tmp, false, $tmpOTLdata);
6043							$nb_carac = mb_strlen($tmp, $this->mb_enc);
6044							$nb_spaces = mb_substr_count($tmp, ' ', $this->mb_enc);
6045
6046							// Take off number of Marks
6047							// Use GPOS OTL
6048
6049							if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'])) {
6050								if (isset($tmpOTLdata['group']) && $tmpOTLdata['group']) {
6051									$nb_carac -= substr_count($tmpOTLdata['group'], 'M');
6052								}
6053							}
6054
6055							list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), $inclCursive, $tmpOTLdata);
6056							$this->SetSpacing($charspacing, $ws);
6057						}
6058
6059						if (isset($OTLdata)) {
6060							$this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
6061						}
6062
6063						$this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
6064
6065						$i = $sep + 1;
6066					}
6067
6068					if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
6069						return false;
6070					}
6071
6072					$sep = -1;
6073					$j = $i;
6074					$l = 0;
6075					$ns = 0;
6076					$nl++;
6077
6078					if ($border and $nl == 2) {
6079						$b = $b2;
6080					}
6081
6082				} else {
6083					$i++;
6084				}
6085			}
6086
6087			// Last chunk
6088			// WORD SPACING
6089
6090			$this->ResetSpacing();
6091
6092		} else {
6093
6094			while ($i < $nb) {
6095
6096				// Get next character
6097				$c = $s[$i];
6098				if ($c === "\n") {
6099
6100					// Explicit line break
6101					// WORD SPACING
6102
6103					$this->ResetSpacing();
6104					$this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
6105
6106					if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
6107						return false;
6108					}
6109
6110					$i++;
6111					$sep = -1;
6112					$j = $i;
6113					$l = 0;
6114					$ns = 0;
6115					$nl++;
6116
6117					if ($border and $nl == 2) {
6118						$b = $b2;
6119					}
6120
6121					continue;
6122				}
6123
6124				if ($c === ' ') {
6125					$sep = $i;
6126					$ls = $l;
6127					$ns++;
6128				}
6129
6130				$l += $this->GetCharWidthCore($c);
6131
6132				if ($l > $wmax) {
6133
6134					// Automatic line break
6135					if ($sep == -1) {
6136
6137						if ($i == $j) {
6138							$i++;
6139						}
6140
6141						// WORD SPACING
6142						$this->ResetSpacing();
6143						$this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
6144
6145					} else {
6146
6147						if ($align === 'J') {
6148
6149							$tmp = rtrim(substr($s, $j, $sep - $j));
6150
6151							// JUSTIFY J using Unicode fonts (Word spacing doesn't work)
6152							// WORD SPACING NON_UNICODE/CJK
6153							// Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
6154
6155							$tmp = str_replace(chr(160), chr(32), $tmp);
6156							$len_ligne = $this->GetStringWidth($tmp);
6157							$nb_carac = strlen($tmp);
6158							$nb_spaces = substr_count($tmp, ' ');
6159							$tmpOTLdata = [];
6160
6161							list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), false, $tmpOTLdata);
6162							$this->SetSpacing($charspacing, $ws);
6163						}
6164
6165						$this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill, $link);
6166						$i = $sep + 1;
6167					}
6168
6169					if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
6170						return false;
6171					}
6172
6173					$sep = -1;
6174					$j = $i;
6175					$l = 0;
6176					$ns = 0;
6177					$nl++;
6178
6179					if ($border and $nl == 2) {
6180						$b = $b2;
6181					}
6182
6183				} else {
6184					$i++;
6185				}
6186			}
6187
6188			// Last chunk
6189			// WORD SPACING
6190
6191			$this->ResetSpacing();
6192		}
6193
6194		// Last chunk
6195		if ($border and is_int(strpos($border, 'B'))) {
6196			$b .= 'B';
6197		}
6198
6199		if (!$this->usingCoreFont) {
6200
6201			$tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
6202			$tmpOTLdata = false;
6203
6204			/* -- OTL -- */
6205			if (isset($OTLdata)) {
6206				$tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
6207				$this->otl->trimOTLdata($tmpOTLdata, false, true);
6208				$this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
6209			}
6210			/* -- END OTL -- */
6211
6212			$this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
6213		} else {
6214			$this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
6215		}
6216
6217		$this->x = $this->lMargin;
6218	}
6219
6220	/* -- DIRECTW -- */
6221
6222	function Write($h, $txt, $currentx = 0, $link = '', $directionality = 'ltr', $align = '', $fill = 0)
6223	{
6224		if (empty($this->directWrite)) {
6225			$this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
6226		}
6227
6228		$this->directWrite->Write($h, $txt, $currentx, $link, $directionality, $align, $fill);
6229	}
6230
6231	/* -- END DIRECTW -- */
6232
6233
6234	/* -- HTML-CSS -- */
6235
6236	function saveInlineProperties()
6237	{
6238		$saved = [];
6239		$saved['family'] = $this->FontFamily;
6240		$saved['style'] = $this->FontStyle;
6241		$saved['sizePt'] = $this->FontSizePt;
6242		$saved['size'] = $this->FontSize;
6243		$saved['HREF'] = $this->HREF;
6244		$saved['textvar'] = $this->textvar; // mPDF 5.7.1
6245		$saved['OTLtags'] = $this->OTLtags; // mPDF 5.7.1
6246		$saved['textshadow'] = $this->textshadow;
6247		$saved['linewidth'] = $this->LineWidth;
6248		$saved['drawcolor'] = $this->DrawColor;
6249		$saved['textparam'] = $this->textparam;
6250		$saved['lSpacingCSS'] = $this->lSpacingCSS;
6251		$saved['wSpacingCSS'] = $this->wSpacingCSS;
6252		$saved['I'] = $this->I;
6253		$saved['B'] = $this->B;
6254		$saved['colorarray'] = $this->colorarray;
6255		$saved['bgcolorarray'] = $this->spanbgcolorarray;
6256		$saved['border'] = $this->spanborddet;
6257		$saved['color'] = $this->TextColor;
6258		$saved['bgcolor'] = $this->FillColor;
6259		$saved['lang'] = $this->currentLang;
6260		$saved['fontLanguageOverride'] = $this->fontLanguageOverride; // mPDF 5.7.1
6261		$saved['display_off'] = $this->inlineDisplayOff;
6262
6263		return $saved;
6264	}
6265
6266	function restoreInlineProperties(&$saved)
6267	{
6268		$FontFamily = $saved['family'];
6269		$this->FontStyle = $saved['style'];
6270		$this->FontSizePt = $saved['sizePt'];
6271		$this->FontSize = $saved['size'];
6272
6273		$this->currentLang = $saved['lang'];
6274		$this->fontLanguageOverride = $saved['fontLanguageOverride']; // mPDF 5.7.1
6275
6276		$this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well
6277
6278		$this->HREF = $saved['HREF'];
6279		$this->textvar = $saved['textvar']; // mPDF 5.7.1
6280		$this->OTLtags = $saved['OTLtags']; // mPDF 5.7.1
6281		$this->textshadow = $saved['textshadow'];
6282		$this->LineWidth = $saved['linewidth'];
6283		$this->DrawColor = $saved['drawcolor'];
6284		$this->textparam = $saved['textparam'];
6285		$this->inlineDisplayOff = $saved['display_off'];
6286
6287		$this->lSpacingCSS = $saved['lSpacingCSS'];
6288		if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
6289			$this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize);
6290		} else {
6291			$this->fixedlSpacing = false;
6292		}
6293		$this->wSpacingCSS = $saved['wSpacingCSS'];
6294		if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
6295			$this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize);
6296		} else {
6297			$this->minwSpacing = 0;
6298		}
6299
6300		$this->SetFont($FontFamily, $saved['style'], $saved['sizePt'], false);
6301
6302		$this->currentfontstyle = $saved['style'];
6303		$this->currentfontsize = $saved['sizePt'];
6304		$this->SetStylesArray(['B' => $saved['B'], 'I' => $saved['I']]); // mPDF 5.7.1
6305
6306		$this->TextColor = $saved['color'];
6307		$this->FillColor = $saved['bgcolor'];
6308		$this->colorarray = $saved['colorarray'];
6309		$cor = $saved['colorarray'];
6310		if ($cor) {
6311			$this->SetTColor($cor);
6312		}
6313		$this->spanbgcolorarray = $saved['bgcolorarray'];
6314		$cor = $saved['bgcolorarray'];
6315		if ($cor) {
6316			$this->SetFColor($cor);
6317		}
6318		$this->spanborddet = $saved['border'];
6319	}
6320
6321	// Used when ColActive for tables - updated to return first block with background fill OR borders
6322	function GetFirstBlockFill()
6323	{
6324		// Returns the first blocklevel that uses a bgcolor fill
6325		$startfill = 0;
6326		for ($i = 1; $i <= $this->blklvl; $i++) {
6327			if ($this->blk[$i]['bgcolor'] || $this->blk[$i]['border_left']['w'] || $this->blk[$i]['border_right']['w'] || $this->blk[$i]['border_top']['w'] || $this->blk[$i]['border_bottom']['w']) {
6328				$startfill = $i;
6329				break;
6330			}
6331		}
6332		return $startfill;
6333	}
6334
6335	// -------------------------FLOWING BLOCK------------------------------------//
6336	// The following functions were originally written by Damon Kohler           //
6337	// --------------------------------------------------------------------------//
6338
6339	function saveFont()
6340	{
6341		$saved = [];
6342		$saved['family'] = $this->FontFamily;
6343		$saved['style'] = $this->FontStyle;
6344		$saved['sizePt'] = $this->FontSizePt;
6345		$saved['size'] = $this->FontSize;
6346		$saved['curr'] = &$this->CurrentFont;
6347		$saved['lang'] = $this->currentLang; // mPDF 6
6348		$saved['color'] = $this->TextColor;
6349		$saved['spanbgcolor'] = $this->spanbgcolor;
6350		$saved['spanbgcolorarray'] = $this->spanbgcolorarray;
6351		$saved['bord'] = $this->spanborder;
6352		$saved['border'] = $this->spanborddet;
6353		$saved['HREF'] = $this->HREF;
6354		$saved['textvar'] = $this->textvar; // mPDF 5.7.1
6355		$saved['textshadow'] = $this->textshadow;
6356		$saved['linewidth'] = $this->LineWidth;
6357		$saved['drawcolor'] = $this->DrawColor;
6358		$saved['textparam'] = $this->textparam;
6359		$saved['ReqFontStyle'] = $this->ReqFontStyle;
6360		$saved['fixedlSpacing'] = $this->fixedlSpacing;
6361		$saved['minwSpacing'] = $this->minwSpacing;
6362		return $saved;
6363	}
6364
6365	function restoreFont(&$saved, $write = true)
6366	{
6367		if (!isset($saved) || empty($saved)) {
6368			return;
6369		}
6370
6371		$this->FontFamily = $saved['family'];
6372		$this->FontStyle = $saved['style'];
6373		$this->FontSizePt = $saved['sizePt'];
6374		$this->FontSize = $saved['size'];
6375		$this->CurrentFont = &$saved['curr'];
6376		$this->currentLang = $saved['lang']; // mPDF 6
6377		$this->TextColor = $saved['color'];
6378		$this->spanbgcolor = $saved['spanbgcolor'];
6379		$this->spanbgcolorarray = $saved['spanbgcolorarray'];
6380		$this->spanborder = $saved['bord'];
6381		$this->spanborddet = $saved['border'];
6382		$this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well
6383		$this->HREF = $saved['HREF'];
6384		$this->fixedlSpacing = $saved['fixedlSpacing'];
6385		$this->minwSpacing = $saved['minwSpacing'];
6386		$this->textvar = $saved['textvar'];  // mPDF 5.7.1
6387		$this->textshadow = $saved['textshadow'];
6388		$this->LineWidth = $saved['linewidth'];
6389		$this->DrawColor = $saved['drawcolor'];
6390		$this->textparam = $saved['textparam'];
6391		if ($write) {
6392			$this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], true, true); // force output
6393			$fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
6394			if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
6395				$this->writer->write($fontout);
6396			}
6397			$this->pageoutput[$this->page]['Font'] = $fontout;
6398		} else {
6399			$this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], false);
6400		}
6401		$this->ReqFontStyle = $saved['ReqFontStyle'];
6402	}
6403
6404	function newFlowingBlock($w, $h, $a = '', $is_table = false, $blockstate = 0, $newblock = true, $blockdir = 'ltr', $table_draft = false)
6405	{
6406		if (!$a) {
6407			if ($blockdir == 'rtl') {
6408				$a = 'R';
6409			} else {
6410				$a = 'L';
6411			}
6412		}
6413		$this->flowingBlockAttr['width'] = ($w * Mpdf::SCALE);
6414		// line height in user units
6415		$this->flowingBlockAttr['is_table'] = $is_table;
6416		$this->flowingBlockAttr['table_draft'] = $table_draft;
6417		$this->flowingBlockAttr['height'] = $h;
6418		$this->flowingBlockAttr['lineCount'] = 0;
6419		$this->flowingBlockAttr['align'] = $a;
6420		$this->flowingBlockAttr['font'] = [];
6421		$this->flowingBlockAttr['content'] = [];
6422		$this->flowingBlockAttr['contentB'] = [];
6423		$this->flowingBlockAttr['contentWidth'] = 0;
6424		$this->flowingBlockAttr['blockstate'] = $blockstate;
6425
6426		$this->flowingBlockAttr['newblock'] = $newblock;
6427		$this->flowingBlockAttr['valign'] = 'M';
6428		$this->flowingBlockAttr['blockdir'] = $blockdir;
6429		$this->flowingBlockAttr['cOTLdata'] = []; // mPDF 5.7.1
6430		$this->flowingBlockAttr['lastBidiText'] = ''; // mPDF 5.7.1
6431		if (!empty($this->otl)) {
6432			$this->otl->lastBidiStrongType = '';
6433		} // *OTL*
6434	}
6435
6436	function finishFlowingBlock($endofblock = false, $next = '')
6437	{
6438		$currentx = $this->x;
6439		// prints out the last chunk
6440		$is_table = $this->flowingBlockAttr['is_table'];
6441		$table_draft = $this->flowingBlockAttr['table_draft'];
6442		$maxWidth = & $this->flowingBlockAttr['width'];
6443		$stackHeight = & $this->flowingBlockAttr['height'];
6444		$align = & $this->flowingBlockAttr['align'];
6445		$content = & $this->flowingBlockAttr['content'];
6446		$contentB = & $this->flowingBlockAttr['contentB'];
6447		$font = & $this->flowingBlockAttr['font'];
6448		$contentWidth = & $this->flowingBlockAttr['contentWidth'];
6449		$lineCount = & $this->flowingBlockAttr['lineCount'];
6450		$valign = & $this->flowingBlockAttr['valign'];
6451		$blockstate = $this->flowingBlockAttr['blockstate'];
6452
6453		$cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1
6454		$newblock = $this->flowingBlockAttr['newblock'];
6455		$blockdir = $this->flowingBlockAttr['blockdir'];
6456
6457		// *********** BLOCK BACKGROUND COLOR *****************//
6458		if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {
6459			$fill = 0;
6460		} else {
6461			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
6462			$fill = 0;
6463		}
6464
6465		$hanger = '';
6466		// Always right trim!
6467		// Right trim last content and adjust width if needed to justify (later)
6468		if (isset($content[count($content) - 1]) && preg_match('/[ ]+$/', $content[count($content) - 1], $m)) {
6469			$strip = strlen($m[0]);
6470			$content[count($content) - 1] = substr($content[count($content) - 1], 0, (strlen($content[count($content) - 1]) - $strip));
6471			/* -- OTL -- */
6472			if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
6473				$this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true);
6474			}
6475			/* -- END OTL -- */
6476		}
6477
6478		// the amount of space taken up so far in user units
6479		$usedWidth = 0;
6480
6481		// COLS
6482		$oldcolumn = $this->CurrCol;
6483
6484		if ($this->ColActive && !$is_table) {
6485			$this->breakpoints[$this->CurrCol][] = $this->y;
6486		} // *COLUMNS*
6487		// Print out each chunk
6488
6489		/* -- TABLES -- */
6490		if ($is_table) {
6491			$ipaddingL = 0;
6492			$ipaddingR = 0;
6493			$paddingL = 0;
6494			$paddingR = 0;
6495		} else {
6496			/* -- END TABLES -- */
6497			$ipaddingL = $this->blk[$this->blklvl]['padding_left'];
6498			$ipaddingR = $this->blk[$this->blklvl]['padding_right'];
6499			$paddingL = ($ipaddingL * Mpdf::SCALE);
6500			$paddingR = ($ipaddingR * Mpdf::SCALE);
6501			$this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];
6502			$this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];
6503
6504			// Added mPDF 3.0 Float DIV
6505			$fpaddingR = 0;
6506			$fpaddingL = 0;
6507			/* -- CSS-FLOAT -- */
6508			if (count($this->floatDivs)) {
6509				list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
6510				if ($r_exists) {
6511					$fpaddingR = $r_width;
6512				}
6513				if ($l_exists) {
6514					$fpaddingL = $l_width;
6515				}
6516			}
6517			/* -- END CSS-FLOAT -- */
6518
6519			$usey = $this->y + 0.002;
6520			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
6521				$usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
6522			}
6523			/* -- CSS-IMAGE-FLOAT -- */
6524			// If float exists at this level
6525			if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
6526				$fpaddingR += $this->floatmargins['R']['w'];
6527			}
6528			if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
6529				$fpaddingL += $this->floatmargins['L']['w'];
6530			}
6531			/* -- END CSS-IMAGE-FLOAT -- */
6532		} // *TABLES*
6533
6534
6535		$lineBox = [];
6536
6537		$this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);
6538
6539		if ($is_table && count($content) == 0) {
6540			$stackHeight = 0;
6541		}
6542
6543		if ($table_draft) {
6544			$this->y += $stackHeight;
6545			$this->objectbuffer = [];
6546			return 0;
6547		}
6548
6549		// While we're at it, check if contains cursive text
6550		// Change NBSP to SPACE.
6551		// Re-calculate contentWidth
6552		$contentWidth = 0;
6553
6554		foreach ($content as $k => $chunk) {
6555			$this->restoreFont($font[$k], false);
6556			if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
6557				// Soft Hyphens chr(173)
6558				if (!$this->usingCoreFont) {
6559					/* -- OTL -- */
6560					// mPDF 5.7.1
6561					if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
6562						$this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad");
6563						$this->otl->replaceSpace($chunk, $cOTLdata[$k]);
6564						$content[$k] = $chunk;
6565					} /* -- END OTL -- */ else {  // *OTL*
6566						$content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk);
6567						$content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);
6568					} // *OTL*
6569				} elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
6570					$content[$k] = $chunk = str_replace(chr(173), '', $chunk);
6571					$content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);
6572				}
6573				$contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE;
6574			} elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
6575				// LIST MARKERS	// mPDF 6  Lists
6576				if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
6577					// do nothing
6578				} else {
6579					$contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
6580				}
6581			}
6582		}
6583
6584		if (isset($font[count($font) - 1])) {
6585			$lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');
6586			$lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');
6587		} else {
6588			$lastfontreqstyle = null;
6589			$lastfontstyle = null;
6590		}
6591		if ($blockdir == 'ltr' && $lastfontreqstyle && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic
6592			$lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE;
6593		} else {
6594			$lastitalic = 0;
6595		}
6596
6597		// Get PAGEBREAK TO TEST for height including the bottom border/padding
6598		$check_h = max($this->divheight, $stackHeight);
6599
6600		// This fixes a proven bug...
6601		if ($endofblock && $newblock && $blockstate == 0 && !$content) {
6602			$check_h = 0;
6603		}
6604		// but ? needs to fix potentially more widespread...
6605		// if (!$content) {  $check_h = 0; }
6606
6607		if ($this->blklvl > 0 && !$is_table) {
6608			if ($endofblock && $blockstate > 1) {
6609				if ($this->blk[$this->blklvl]['page_break_after_avoid']) {
6610					$check_h += $stackHeight;
6611				}
6612				$check_h += ($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);
6613			}
6614			if (($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0) || ($endofblock && $blockstate == 3 && $lineCount == 0)) {
6615				$check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);
6616			}
6617		}
6618
6619		// Force PAGE break if column height cannot take check-height
6620		if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {
6621			$this->SetCol($this->NbCol - 1);
6622		}
6623
6624		// Avoid just border/background-color moved on to next page
6625		if ($endofblock && $blockstate > 1 && !$content) {
6626			$buff = $this->margBuffer;
6627		} else {
6628			$buff = 0;
6629		}
6630
6631
6632		// PAGEBREAK
6633		if (!$is_table && ($this->y + $check_h) > ($this->PageBreakTrigger + $buff) and ! $this->InFooter and $this->AcceptPageBreak()) {
6634			$bak_x = $this->x; // Current X position
6635			// WORD SPACING
6636			$ws = $this->ws; // Word Spacing
6637			$charspacing = $this->charspacing; // Character Spacing
6638			$this->ResetSpacing();
6639
6640			$this->AddPage($this->CurOrientation);
6641
6642			$this->x = $bak_x;
6643			// Added to correct for OddEven Margins
6644			$currentx += $this->MarginCorrection;
6645			$this->x += $this->MarginCorrection;
6646
6647			// WORD SPACING
6648			$this->SetSpacing($charspacing, $ws);
6649		}
6650
6651
6652		/* -- COLUMNS -- */
6653		// COLS
6654		// COLUMN CHANGE
6655		if ($this->CurrCol != $oldcolumn) {
6656			$currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
6657			$this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
6658			$oldcolumn = $this->CurrCol;
6659		}
6660
6661
6662		if ($this->ColActive && !$is_table) {
6663			$this->breakpoints[$this->CurrCol][] = $this->y;
6664		}
6665		/* -- END COLUMNS -- */
6666
6667		// TOP MARGIN
6668		if ($newblock && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && $lineCount == 0 && !$is_table) {
6669			$this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
6670			if ($this->ColActive) {
6671				$this->breakpoints[$this->CurrCol][] = $this->y;
6672			} // *COLUMNS*
6673		}
6674
6675		if ($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0 && !$is_table) {
6676			$this->blk[$this->blklvl]['y0'] = $this->y;
6677			$this->blk[$this->blklvl]['startpage'] = $this->page;
6678			if ($this->blk[$this->blklvl]['float']) {
6679				$this->blk[$this->blklvl]['float_start_y'] = $this->y;
6680			}
6681			if ($this->ColActive) {
6682				$this->breakpoints[$this->CurrCol][] = $this->y;
6683			} // *COLUMNS*
6684		}
6685
6686		// Paragraph INDENT
6687		$WidthCorrection = 0;
6688		if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
6689			$ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false);  // mPDF 5.7.4
6690			$WidthCorrection = ($ti * Mpdf::SCALE);
6691		}
6692
6693
6694		// PADDING and BORDER spacing/fill
6695		if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 0) && (!$is_table)) {
6696			// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
6697			$this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);
6698			if ($this->ColActive) {
6699				$this->breakpoints[$this->CurrCol][] = $this->y;
6700			} // *COLUMNS*
6701			$this->x = $currentx;
6702		}
6703
6704
6705		// Added mPDF 3.0 Float DIV
6706		$fpaddingR = 0;
6707		$fpaddingL = 0;
6708		/* -- CSS-FLOAT -- */
6709		if (count($this->floatDivs)) {
6710			list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
6711			if ($r_exists) {
6712				$fpaddingR = $r_width;
6713			}
6714			if ($l_exists) {
6715				$fpaddingL = $l_width;
6716			}
6717		}
6718		/* -- END CSS-FLOAT -- */
6719
6720		$usey = $this->y + 0.002;
6721		if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
6722			$usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
6723		}
6724		/* -- CSS-IMAGE-FLOAT -- */
6725		// If float exists at this level
6726		if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
6727			$fpaddingR += $this->floatmargins['R']['w'];
6728		}
6729		if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
6730			$fpaddingL += $this->floatmargins['L']['w'];
6731		}
6732		/* -- END CSS-IMAGE-FLOAT -- */
6733
6734
6735		if ($content) {
6736			// In FinishFlowing Block no lines are justified as it is always last line
6737			// but if CJKorphan has allowed content width to go over max width, use J charspacing to compress line
6738			// JUSTIFICATION J - NOT!
6739			$nb_carac = 0;
6740			$nb_spaces = 0;
6741			$jcharspacing = 0;
6742			$jkashida = 0;
6743			$jws = 0;
6744			$inclCursive = false;
6745			$dottab = false;
6746			foreach ($content as $k => $chunk) {
6747				if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
6748					$nb_carac += mb_strlen($chunk, $this->mb_enc);
6749					$nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);
6750					// mPDF 6
6751					// Use GPOS OTL
6752					$this->restoreFont($font[$k], false);
6753					if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
6754						if (isset($cOTLdata[$k]['group']) && $cOTLdata[$k]['group']) {
6755							$nb_marks = substr_count($cOTLdata[$k]['group'], 'M');
6756							$nb_carac -= $nb_marks;
6757						}
6758						if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) {
6759							$inclCursive = true;
6760						}
6761					}
6762				} else {
6763					$nb_carac ++;  // mPDF 6 allow spacing for inline object
6764					if ($this->objectbuffer[$k]['type'] == 'dottab') {
6765						$dottab = $this->objectbuffer[$k]['outdent'];
6766					}
6767				}
6768			}
6769
6770			// DIRECTIONALITY RTL
6771			$chunkorder = range(0, count($content) - 1); // mPDF 6
6772			/* -- OTL -- */
6773			// mPDF 6
6774			if ($blockdir == 'rtl' || $this->biDirectional) {
6775				$this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);
6776				// From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to
6777				// $this->objectbuffer and $font ($chunkorder contains the mapping)
6778			}
6779			/* -- END OTL -- */
6780
6781			// Remove any XAdvance from OTL data at end of line
6782			// And correct for XPlacement on last character
6783			// BIDI is applied
6784			foreach ($chunkorder as $aord => $k) {
6785				if (count($cOTLdata)) {
6786					$this->restoreFont($font[$k], false);
6787					// ...FinishFlowingBlock...
6788					if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line
6789						$nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character
6790						if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {
6791							if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {
6792								$w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
6793							} else {
6794								$w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
6795							}
6796							$w *= ($this->FontSize / 1000);
6797							$contentWidth -= $w * Mpdf::SCALE;
6798							$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;
6799							$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;
6800						}
6801
6802						// If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it
6803						if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {
6804							$w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
6805							$w *= ($this->FontSize / 1000);
6806							$contentWidth -= $w * Mpdf::SCALE;
6807							$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
6808							$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
6809						}
6810					}
6811				}
6812			}
6813
6814			// if it's justified, we need to find the char/word spacing (or if orphans have allowed length of line to go over the maxwidth)
6815			// If "orphans" in fact is just a final space - ignore this
6816			$lastchar = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);
6817			if (preg_match("/[" . $this->CJKoverflow . "]/u", $lastchar)) {
6818				$CJKoverflow = true;
6819			} else {
6820				$CJKoverflow = false;
6821			}
6822			if ((((($contentWidth + $lastitalic) > $maxWidth) && ($content[(count($chunkorder) - 1)] != ' ') ) ||
6823				(!$endofblock && $align == 'J' && ($next == 'image' || $next == 'select' || $next == 'input' || $next == 'textarea' || ($next == 'br' && $this->justifyB4br)))) && !($CJKoverflow && $this->allowCJKoverflow)) {
6824				// WORD SPACING
6825				list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata);
6826			} /* -- CJK-FONTS -- */ elseif ($this->checkCJK && $align == 'J' && $CJKoverflow && $this->allowCJKoverflow && $this->CJKforceend) {
6827				// force-end overhang
6828				$hanger = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);
6829				if (preg_match("/[" . $this->CJKoverflow . "]/u", $hanger)) {
6830					$content[(count($chunkorder) - 1)] = mb_substr($content[(count($chunkorder) - 1)], 0, mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, $this->mb_enc);
6831					$this->restoreFont($font[$chunkorder[count($chunkorder) - 1]], false);
6832					$contentWidth -= $this->GetStringWidth($hanger) * Mpdf::SCALE;
6833					$nb_carac -= 1;
6834					list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata);
6835				}
6836			} /* -- END CJK-FONTS -- */
6837
6838			// Check if will fit at word/char spacing of previous line - if so continue it
6839			// but only allow a maximum of $this->jSmaxWordLast and $this->jSmaxCharLast
6840			elseif ($contentWidth < ($maxWidth - $lastitalic - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE))) && !$this->fixedlSpacing) {
6841				if ($this->ws > $this->jSmaxWordLast) {
6842					$jws = $this->jSmaxWordLast;
6843				}
6844				if ($this->charspacing > $this->jSmaxCharLast) {
6845					$jcharspacing = $this->jSmaxCharLast;
6846				}
6847				$check = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) - ( $jcharspacing * $nb_carac) - ( $jws * $nb_spaces);
6848				if ($check <= 0) {
6849					$jcharspacing = 0;
6850					$jws = 0;
6851				}
6852			}
6853
6854			$empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) );
6855
6856
6857			$empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1
6858			$empty -= ($jws * $nb_spaces);
6859			$empty -= ($jkashida);
6860
6861			$empty /= Mpdf::SCALE;
6862
6863			if (!$is_table) {
6864				$this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin'] - $empty));
6865				$this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $empty));
6866			}
6867
6868			$arraysize = count($chunkorder);
6869
6870			$margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );
6871
6872			if (!$is_table) {
6873				$this->DivLn($stackHeight, $this->blklvl, false);
6874			} // false -> don't advance y
6875
6876			$this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;
6877			if ($dottab !== false && $blockdir == 'rtl') {
6878				$this->x -= $dottab;
6879			} elseif ($align == 'R') {
6880				$this->x += $empty;
6881			} elseif ($align == 'J' && $blockdir == 'rtl') {
6882				$this->x += $empty;
6883			} elseif ($align == 'C') {
6884				$this->x += ($empty / 2);
6885			}
6886
6887			// Paragraph INDENT
6888			$WidthCorrection = 0;
6889			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
6890				$ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false);  // mPDF 5.7.4
6891				if ($blockdir != 'rtl') {
6892					$this->x += $ti;
6893				} // mPDF 6
6894			}
6895
6896			foreach ($chunkorder as $aord => $k) { // mPDF 5.7
6897				$chunk = $content[$aord];
6898				if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
6899					$xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];
6900					$this->objectbuffer[$k]['OUTER-X'] += $xadj;
6901					$this->objectbuffer[$k]['BORDER-X'] += $xadj;
6902					$this->objectbuffer[$k]['INNER-X'] += $xadj;
6903
6904					if ($this->objectbuffer[$k]['type'] == 'listmarker') {
6905						$this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin
6906					}
6907					$yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];
6908					if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
6909						$this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin
6910					}
6911					if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB
6912						$yadj += $lineBox[$k]['top'];
6913					}
6914					$this->objectbuffer[$k]['OUTER-Y'] += $yadj;
6915					$this->objectbuffer[$k]['BORDER-Y'] += $yadj;
6916					$this->objectbuffer[$k]['INNER-Y'] += $yadj;
6917				}
6918
6919				$this->restoreFont($font[$k]);  // mPDF 5.7
6920
6921				if ($is_table && substr($align, 0, 1) == 'D' && $aord == 0) {
6922					$dp = $this->decimal_align[substr($align, 0, 2)];
6923					$s = preg_split('/' . preg_quote($dp, '/') . '/', $content[0], 2);  // ? needs to be /u if not core
6924					$s0 = $this->GetStringWidth($s[0], false);
6925					$this->x += ($this->decimal_offset - $s0);
6926				}
6927
6928				$this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws);
6929				$this->fixedlSpacing = false;
6930				$this->minwSpacing = 0;
6931
6932				$save_vis = $this->visibility;
6933				if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {
6934					$this->SetVisibility($this->textparam['visibility']);
6935				}
6936
6937				// *********** SPAN BACKGROUND COLOR ***************** //
6938				if (isset($this->spanbgcolor) && $this->spanbgcolor) {
6939					$cor = $this->spanbgcolorarray;
6940					$this->SetFColor($cor);
6941					$save_fill = $fill;
6942					$spanfill = 1;
6943					$fill = 1;
6944				}
6945				if (!empty($this->spanborddet)) {
6946					if (strpos($contentB[$k], 'L') !== false && isset($this->spanborddet['L'])) {
6947						$this->x += $this->spanborddet['L']['w'];
6948					}
6949					if (strpos($contentB[$k], 'L') === false) {
6950						$this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;
6951					}
6952					if (strpos($contentB[$k], 'R') === false) {
6953						$this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;
6954					}
6955				}
6956				// WORD SPACING
6957				// mPDF 5.7.1
6958				$stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar);
6959				$nch = mb_strlen($chunk, $this->mb_enc);
6960				// Use GPOS OTL
6961				if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
6962					if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
6963						$nch -= substr_count($cOTLdata[$aord]['group'], 'M');
6964					}
6965				}
6966				$stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE );
6967
6968				$stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE );
6969
6970				if (isset($this->objectbuffer[$k])) {
6971					if ($this->objectbuffer[$k]['type'] == 'dottab') {
6972						$this->objectbuffer[$k]['OUTER-WIDTH'] +=$empty;
6973						$this->objectbuffer[$k]['OUTER-WIDTH'] +=$this->objectbuffer[$k]['outdent'];
6974					}
6975					// LIST MARKERS	// mPDF 6  Lists
6976					if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
6977						// do nothing
6978					} else {
6979						$stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];
6980					}
6981				}
6982
6983				if ($stringWidth == 0) {
6984					$stringWidth = 0.000001;
6985				}
6986				if ($aord == $arraysize - 1) { // mPDF 5.7
6987					// mPDF 5.7.1
6988					if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {
6989						// force-end overhang
6990						$this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false));  // mPDF 5.7.1
6991						$this->Cell($this->GetStringWidth($hanger), $stackHeight, $hanger, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1
6992					} else {
6993						$this->Cell($stringWidth, $stackHeight, $chunk, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1
6994					}
6995				} else {
6996					$this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, 0, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // first or middle part	// mPDF 5.7.1
6997				}
6998
6999
7000				if (!empty($this->spanborddet)) {
7001					if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) {
7002						$this->x += $this->spanborddet['R']['w'];
7003					}
7004				}
7005				// *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //
7006				if (isset($spanfill) && $spanfill) {
7007					$fill = $save_fill;
7008					$spanfill = 0;
7009					if ($fill) {
7010						$this->SetFColor($bcor);
7011					}
7012				}
7013				if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {
7014					$this->SetVisibility($save_vis);
7015				}
7016			}
7017
7018			$this->printobjectbuffer($is_table, $blockdir);
7019			$this->objectbuffer = [];
7020			$this->ResetSpacing();
7021		} // END IF CONTENT
7022
7023		/* -- CSS-IMAGE-FLOAT -- */
7024		// Update values if set to skipline
7025		if ($this->floatmargins) {
7026			$this->_advanceFloatMargins();
7027		}
7028
7029
7030		if ($endofblock && $blockstate > 1) {
7031			// If float exists at this level
7032			if (isset($this->floatmargins['R']['y1'])) {
7033				$fry1 = $this->floatmargins['R']['y1'];
7034			} else {
7035				$fry1 = 0;
7036			}
7037			if (isset($this->floatmargins['L']['y1'])) {
7038				$fly1 = $this->floatmargins['L']['y1'];
7039			} else {
7040				$fly1 = 0;
7041			}
7042			if ($this->y < $fry1 || $this->y < $fly1) {
7043				$drop = max($fry1, $fly1) - $this->y;
7044				$this->DivLn($drop);
7045				$this->x = $currentx;
7046			}
7047		}
7048		/* -- END CSS-IMAGE-FLOAT -- */
7049
7050
7051		// PADDING and BORDER spacing/fill
7052		if ($endofblock && ($blockstate > 1) && ($this->blk[$this->blklvl]['padding_bottom'] || $this->blk[$this->blklvl]['border_bottom'] || $this->blk[$this->blklvl]['css_set_height']) && (!$is_table)) {
7053			// If CSS height set, extend bottom - if on same page as block started, and CSS HEIGHT > actual height,
7054			// and does not force pagebreak
7055			$extra = 0;
7056			if (isset($this->blk[$this->blklvl]['css_set_height']) && $this->blk[$this->blklvl]['css_set_height'] && $this->blk[$this->blklvl]['startpage'] == $this->page) {
7057				// predicted height
7058				$h1 = ($this->y - $this->blk[$this->blklvl]['y0']) + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];
7059				if ($h1 < ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top'])) {
7060					$extra = ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top']) - $h1;
7061				}
7062				if ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra > $this->PageBreakTrigger) {
7063					$extra = $this->PageBreakTrigger - ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);
7064				}
7065			}
7066
7067			// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
7068			$this->DivLn($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra, -3, true, false, 2);
7069			$this->x = $currentx;
7070
7071			if ($this->ColActive) {
7072				$this->breakpoints[$this->CurrCol][] = $this->y;
7073			} // *COLUMNS*
7074		}
7075
7076		// SET Bottom y1 of block (used for painting borders)
7077		if (($endofblock) && ($blockstate > 1) && (!$is_table)) {
7078			$this->blk[$this->blklvl]['y1'] = $this->y;
7079		}
7080
7081		// BOTTOM MARGIN
7082		if (($endofblock) && ($blockstate > 1) && ($this->blk[$this->blklvl]['margin_bottom']) && (!$is_table)) {
7083			if ($this->y + $this->blk[$this->blklvl]['margin_bottom'] < $this->PageBreakTrigger and ! $this->InFooter) {
7084				$this->DivLn($this->blk[$this->blklvl]['margin_bottom'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
7085				if ($this->ColActive) {
7086					$this->breakpoints[$this->CurrCol][] = $this->y;
7087				} // *COLUMNS*
7088			}
7089		}
7090
7091		// Reset lineheight
7092		$stackHeight = $this->divheight;
7093	}
7094
7095	function printobjectbuffer($is_table = false, $blockdir = false)
7096	{
7097		if (!$blockdir) {
7098			$blockdir = $this->directionality;
7099		}
7100
7101		if ($is_table && $this->shrin_k > 1) {
7102			$k = $this->shrin_k;
7103		} else {
7104			$k = 1;
7105		}
7106
7107		$save_y = $this->y;
7108		$save_x = $this->x;
7109
7110		$save_currentfontfamily = $this->FontFamily;
7111		$save_currentfontsize = $this->FontSizePt;
7112		$save_currentfontstyle = $this->FontStyle;
7113
7114		if ($blockdir == 'rtl') {
7115			$rtlalign = 'R';
7116		} else {
7117			$rtlalign = 'L';
7118		}
7119
7120		foreach ($this->objectbuffer as $ib => $objattr) {
7121
7122			if ($objattr['type'] == 'bookmark' || $objattr['type'] == 'indexentry' || $objattr['type'] == 'toc') {
7123				$x = $objattr['OUTER-X'];
7124				$y = $objattr['OUTER-Y'];
7125				$this->y = $y - $this->FontSize / 2;
7126				$this->x = $x;
7127				if ($objattr['type'] == 'bookmark') {
7128					$this->Bookmark($objattr['CONTENT'], $objattr['bklevel'], $y - $this->FontSize);
7129				} // *BOOKMARKS*
7130				if ($objattr['type'] == 'indexentry') {
7131					$this->IndexEntry($objattr['CONTENT']);
7132				} // *INDEX*
7133				if ($objattr['type'] == 'toc') {
7134					$this->TOC_Entry($objattr['CONTENT'], $objattr['toclevel'], (isset($objattr['toc_id']) ? $objattr['toc_id'] : ''));
7135				} // *TOC*
7136			} /* -- ANNOTATIONS -- */ elseif ($objattr['type'] == 'annot') {
7137				if ($objattr['POS-X']) {
7138					$x = $objattr['POS-X'];
7139				} elseif ($this->annotMargin <> 0) {
7140					$x = -$objattr['OUTER-X'];
7141				} else {
7142					$x = $objattr['OUTER-X'];
7143				}
7144				if ($objattr['POS-Y']) {
7145					$y = $objattr['POS-Y'];
7146				} else {
7147					$y = $objattr['OUTER-Y'] - $this->FontSize / 2;
7148				}
7149				// Create a dummy entry in the _out/columnBuffer with position sensitive data,
7150				// linking $y-1 in the Columnbuffer with entry in $this->columnAnnots
7151				// and when columns are split in length will not break annotation from current line
7152				$this->y = $y - 1;
7153				$this->x = $x - 1;
7154				$this->Line($x - 1, $y - 1, $x - 1, $y - 1);
7155				$this->Annotation($objattr['CONTENT'], $x, $y, $objattr['ICON'], $objattr['AUTHOR'], $objattr['SUBJECT'], $objattr['OPACITY'], $objattr['COLOR'], (isset($objattr['POPUP']) ? $objattr['POPUP'] : ''), (isset($objattr['FILE']) ? $objattr['FILE'] : ''));
7156			} /* -- END ANNOTATIONS -- */ else {
7157				$y = $objattr['OUTER-Y'];
7158				$x = $objattr['OUTER-X'];
7159				$w = $objattr['OUTER-WIDTH'];
7160				$h = $objattr['OUTER-HEIGHT'];
7161				if (isset($objattr['text'])) {
7162					$texto = $objattr['text'];
7163				}
7164				$this->y = $y;
7165				$this->x = $x;
7166				if (isset($objattr['fontfamily'])) {
7167					$this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);
7168				}
7169			}
7170
7171			// HR
7172			if ($objattr['type'] == 'hr') {
7173				$this->SetDColor($objattr['color']);
7174				switch ($objattr['align']) {
7175					case 'C':
7176						$empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];
7177						$empty /= 2;
7178						$x += $empty;
7179						break;
7180					case 'R':
7181						$empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];
7182						$x += $empty;
7183						break;
7184				}
7185				$oldlinewidth = $this->LineWidth;
7186				$this->SetLineWidth($objattr['linewidth'] / $k);
7187				$this->y += ($objattr['linewidth'] / 2) + $objattr['margin_top'] / $k;
7188				$this->Line($x, $this->y, $x + $objattr['INNER-WIDTH'], $this->y);
7189				$this->SetLineWidth($oldlinewidth);
7190				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
7191			}
7192			// IMAGE
7193			if ($objattr['type'] == 'image') {
7194				// mPDF 5.7.3 TRANSFORMS
7195				if (isset($objattr['transform'])) {
7196					$this->writer->write("\n" . '% BTR'); // Begin Transform
7197				}
7198				if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {
7199					$this->BeginLayer($objattr['z-index']);
7200				}
7201				if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {
7202					$this->SetVisibility($objattr['visibility']);
7203				}
7204				if (isset($objattr['opacity'])) {
7205					$this->SetAlpha($objattr['opacity']);
7206				}
7207
7208				$obiw = $objattr['INNER-WIDTH'];
7209				$obih = $objattr['INNER-HEIGHT'];
7210
7211				$sx = $objattr['orig_w'] ? ($objattr['INNER-WIDTH'] * Mpdf::SCALE / $objattr['orig_w']) : INF;
7212				$sy = $objattr['orig_h'] ? ($objattr['INNER-HEIGHT'] * Mpdf::SCALE / $objattr['orig_h']) : INF;
7213
7214				$rotate = 0;
7215				if (isset($objattr['ROTATE'])) {
7216					$rotate = $objattr['ROTATE'];
7217				}
7218
7219				if ($rotate == 90) {
7220					// Clockwise
7221					$obiw = $objattr['INNER-HEIGHT'];
7222					$obih = $objattr['INNER-WIDTH'];
7223					$tr = $this->transformTranslate(0, -$objattr['INNER-WIDTH'], true);
7224					$tr .= ' ' . $this->transformRotate(90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);
7225					$sx = $obiw * Mpdf::SCALE / $objattr['orig_h'];
7226					$sy = $obih * Mpdf::SCALE / $objattr['orig_w'];
7227				} elseif ($rotate == -90 || $rotate == 270) {
7228					// AntiClockwise
7229					$obiw = $objattr['INNER-HEIGHT'];
7230					$obih = $objattr['INNER-WIDTH'];
7231					$tr = $this->transformTranslate($objattr['INNER-WIDTH'], ($objattr['INNER-HEIGHT'] - $objattr['INNER-WIDTH']), true);
7232					$tr .= ' ' . $this->transformRotate(-90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);
7233					$sx = $obiw * Mpdf::SCALE / $objattr['orig_h'];
7234					$sy = $obih * Mpdf::SCALE / $objattr['orig_w'];
7235				} elseif ($rotate == 180) {
7236					// Mirror
7237					$tr = $this->transformTranslate($objattr['INNER-WIDTH'], -$objattr['INNER-HEIGHT'], true);
7238					$tr .= ' ' . $this->transformRotate(180, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-HEIGHT']), true);
7239				} else {
7240					$tr = '';
7241				}
7242				$tr = trim($tr);
7243				if ($tr) {
7244					$tr .= ' ';
7245				}
7246				$gradmask = '';
7247
7248				// mPDF 5.7.3 TRANSFORMS
7249				$tr2 = '';
7250				if (isset($objattr['transform'])) {
7251					$maxsize_x = $w;
7252					$maxsize_y = $h;
7253					$cx = $x + $w / 2;
7254					$cy = $y + $h / 2;
7255					preg_match_all('/(translatex|translatey|translate|scalex|scaley|scale|rotate|skewX|skewY|skew)\((.*?)\)/is', $objattr['transform'], $m);
7256					if (count($m[0])) {
7257						for ($i = 0; $i < count($m[0]); $i++) {
7258							$c = strtolower($m[1][$i]);
7259							$v = trim($m[2][$i]);
7260							$vv = preg_split('/[ ,]+/', $v);
7261							if ($c == 'translate' && count($vv)) {
7262								$translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false);
7263								if (count($vv) == 2) {
7264									$translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false);
7265								} else {
7266									$translate_y = 0;
7267								}
7268								$tr2 .= $this->transformTranslate($translate_x, $translate_y, true) . ' ';
7269							} elseif ($c == 'translatex' && count($vv)) {
7270								$translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false);
7271								$tr2 .= $this->transformTranslate($translate_x, 0, true) . ' ';
7272							} elseif ($c == 'translatey' && count($vv)) {
7273								$translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false);
7274								$tr2 .= $this->transformTranslate(0, $translate_y, true) . ' ';
7275							} elseif ($c == 'scale' && count($vv)) {
7276								$scale_x = $vv[0] * 100;
7277								if (count($vv) == 2) {
7278									$scale_y = $vv[1] * 100;
7279								} else {
7280									$scale_y = $scale_x;
7281								}
7282								$tr2 .= $this->transformScale($scale_x, $scale_y, $cx, $cy, true) . ' ';
7283							} elseif ($c == 'scalex' && count($vv)) {
7284								$scale_x = $vv[0] * 100;
7285								$tr2 .= $this->transformScale($scale_x, 0, $cx, $cy, true) . ' ';
7286							} elseif ($c == 'scaley' && count($vv)) {
7287								$scale_y = $vv[1] * 100;
7288								$tr2 .= $this->transformScale(0, $scale_y, $cx, $cy, true) . ' ';
7289							} elseif ($c == 'skew' && count($vv)) {
7290								$angle_x = $this->ConvertAngle($vv[0], false);
7291								if (count($vv) == 2) {
7292									$angle_y = $this->ConvertAngle($vv[1], false);
7293								} else {
7294									$angle_y = 0;
7295								}
7296								$tr2 .= $this->transformSkew($angle_x, $angle_y, $cx, $cy, true) . ' ';
7297							} elseif ($c == 'skewx' && count($vv)) {
7298								$angle = $this->ConvertAngle($vv[0], false);
7299								$tr2 .= $this->transformSkew($angle, 0, $cx, $cy, true) . ' ';
7300							} elseif ($c == 'skewy' && count($vv)) {
7301								$angle = $this->ConvertAngle($vv[0], false);
7302								$tr2 .= $this->transformSkew(0, $angle, $cx, $cy, true) . ' ';
7303							} elseif ($c == 'rotate' && count($vv)) {
7304								$angle = $this->ConvertAngle($vv[0]);
7305								$tr2 .= $this->transformRotate($angle, $cx, $cy, true) . ' ';
7306							}
7307						}
7308					}
7309				}
7310
7311				// LIST MARKERS (Images)	// mPDF 6  Lists
7312				if (isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {
7313					$mw = $objattr['OUTER-WIDTH'];
7314					// NB If change marker-offset, also need to alter in function _getListMarkerWidth
7315					$adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
7316					if ($objattr['dir'] == 'rtl') {
7317						$objattr['INNER-X'] += $adjx;
7318					} else {
7319						$objattr['INNER-X'] -= $adjx;
7320						$objattr['INNER-X'] -= $mw;
7321					}
7322				}
7323				// mPDF 5.7.3 TRANSFORMS / BACKGROUND COLOR
7324				// Transform also affects image background
7325				if ($tr2) {
7326					$this->writer->write('q ' . $tr2 . ' ');
7327				}
7328				if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
7329					$bgcol = $objattr['bgcolor'];
7330					$this->SetFColor($bgcol);
7331					$this->Rect($x, $y, $w, $h, 'F');
7332					$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
7333				}
7334				if ($tr2) {
7335					$this->writer->write('Q');
7336				}
7337
7338				/* -- BACKGROUNDS -- */
7339				if (isset($objattr['GRADIENT-MASK'])) {
7340					$g = $this->gradient->parseMozGradient($objattr['GRADIENT-MASK']);
7341					if ($g) {
7342						$dummy = $this->gradient->Gradient($objattr['INNER-X'], $objattr['INNER-Y'], $obiw, $obih, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true, true);
7343						$gradmask = '/TGS' . count($this->gradients) . ' gs ';
7344					}
7345				}
7346				/* -- END BACKGROUNDS -- */
7347				/* -- IMAGES-WMF -- */
7348				if (isset($objattr['itype']) && $objattr['itype'] == 'wmf') {
7349					$outstring = sprintf('q ' . $tr . $tr2 . '%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X'] * Mpdf::SCALE - $sx * $objattr['wmf_x'], (($this->h - $objattr['INNER-Y']) * Mpdf::SCALE) + $sy * $objattr['wmf_y'], $objattr['ID']); // mPDF 5.7.3 TRANSFORMS
7350				} else { 				/* -- END IMAGES-WMF -- */
7351					if (isset($objattr['itype']) && $objattr['itype'] == 'svg') {
7352						$outstring = sprintf('q ' . $tr . $tr2 . '%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X'] * Mpdf::SCALE - $sx * $objattr['wmf_x'], (($this->h - $objattr['INNER-Y']) * Mpdf::SCALE) + $sy * $objattr['wmf_y'], $objattr['ID']); // mPDF 5.7.3 TRANSFORMS
7353					} else {
7354						$outstring = sprintf("q " . $tr . $tr2 . "%.3F 0 0 %.3F %.3F %.3F cm " . $gradmask . "/I%d Do Q", $obiw * Mpdf::SCALE, $obih * Mpdf::SCALE, $objattr['INNER-X'] * Mpdf::SCALE, ($this->h - ($objattr['INNER-Y'] + $obih )) * Mpdf::SCALE, $objattr['ID']); // mPDF 5.7.3 TRANSFORMS
7355					}
7356				}
7357				$this->writer->write($outstring);
7358				// LINK
7359				if (isset($objattr['link'])) {
7360					$this->Link($objattr['INNER-X'], $objattr['INNER-Y'], $objattr['INNER-WIDTH'], $objattr['INNER-HEIGHT'], $objattr['link']);
7361				}
7362				if (isset($objattr['opacity'])) {
7363					$this->SetAlpha(1);
7364				}
7365
7366				// mPDF 5.7.3 TRANSFORMS
7367				// Transform also affects image borders
7368				if ($tr2) {
7369					$this->writer->write('q ' . $tr2 . ' ');
7370				}
7371				if ((isset($objattr['border_top']) && $objattr['border_top'] > 0) || (isset($objattr['border_left']) && $objattr['border_left'] > 0) || (isset($objattr['border_right']) && $objattr['border_right'] > 0) || (isset($objattr['border_bottom']) && $objattr['border_bottom'] > 0)) {
7372					$this->PaintImgBorder($objattr, $is_table);
7373				}
7374				if ($tr2) {
7375					$this->writer->write('Q');
7376				}
7377
7378				if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {
7379					$this->SetVisibility('visible');
7380				}
7381				if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {
7382					$this->EndLayer();
7383				}
7384				// mPDF 5.7.3 TRANSFORMS
7385				if (isset($objattr['transform'])) {
7386					$this->writer->write("\n" . '% ETR'); // End Transform
7387				}
7388			}
7389
7390			if ($objattr['type'] === 'barcode') {
7391
7392				$bgcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
7393
7394				if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
7395					$bgcol = $objattr['bgcolor'];
7396				}
7397
7398				$col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
7399
7400				if (isset($objattr['color']) && $objattr['color']) {
7401					$col = $objattr['color'];
7402				}
7403
7404				$this->SetFColor($bgcol);
7405				$this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');
7406				$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
7407
7408				if (isset($objattr['BORDER-WIDTH'])) {
7409					$this->PaintImgBorder($objattr, $is_table);
7410				}
7411
7412				$barcodeTypes = ['EAN13', 'ISBN', 'ISSN', 'UPCA', 'UPCE', 'EAN8'];
7413				if (in_array($objattr['btype'], $barcodeTypes, true)) {
7414
7415					$this->WriteBarcode(
7416						$objattr['code'],
7417						$objattr['showtext'],
7418						$objattr['INNER-X'],
7419						$objattr['INNER-Y'],
7420						$objattr['bsize'],
7421						0,
7422						0,
7423						0,
7424						0,
7425						0,
7426						$objattr['bheight'],
7427						$bgcol,
7428						$col,
7429						$objattr['btype'],
7430						$objattr['bsupp'],
7431						(isset($objattr['bsupp_code']) ? $objattr['bsupp_code'] : ''),
7432						$k
7433					);
7434
7435				} elseif ($objattr['btype'] === 'QR') {
7436
7437					if (!class_exists('Mpdf\QrCode\QrCode') || !class_exists('Mpdf\QrCode\Output\Mpdf')) {
7438						throw new \Mpdf\MpdfException('Mpdf\QrCode package was not found. Install the package from Packagist with "composer require mpdf/qrcode"');
7439					}
7440
7441					$barcodeContent = str_replace('\r\n', "\r\n", $objattr['code']);
7442					$barcodeContent = str_replace('\n', "\n", $barcodeContent);
7443
7444					$qrcode = new QrCode\QrCode($barcodeContent, $objattr['errorlevel']);
7445					if ($objattr['disableborder']) {
7446						$qrcode->disableBorder();
7447					}
7448
7449					$bgColor = [255, 255, 255];
7450					if ($objattr['bgcolor']) {
7451						$bgColor = array_map(
7452							function ($col) {
7453								return intval(255 * floatval($col));
7454							},
7455							explode(" ", $this->SetColor($objattr['bgcolor'], 'CodeOnly'))
7456						);
7457					}
7458					$color = [0, 0, 0];
7459					if ($objattr['color']) {
7460						$color = array_map(
7461							function ($col) {
7462								return intval(255 * floatval($col));
7463							},
7464							explode(" ", $this->SetColor($objattr['color'], 'CodeOnly'))
7465						);
7466					}
7467
7468					$out = new QrCode\Output\Mpdf();
7469					$out->output(
7470						$qrcode,
7471						$this,
7472						$objattr['INNER-X'],
7473						$objattr['INNER-Y'],
7474						$objattr['bsize'] * 25,
7475						$bgColor,
7476						$color
7477					);
7478
7479					unset($qrcode);
7480
7481				} else {
7482					$this->WriteBarcode2(
7483						$objattr['code'],
7484						$objattr['INNER-X'],
7485						$objattr['INNER-Y'],
7486						$objattr['bsize'],
7487						$objattr['bheight'],
7488						$bgcol,
7489						$col,
7490						$objattr['btype'],
7491						$objattr['pr_ratio'],
7492						$k,
7493						$objattr['quiet_zone_left'],
7494						$objattr['quiet_zone_right']
7495					);
7496				}
7497			}
7498
7499			// TEXT CIRCLE
7500			if ($objattr['type'] == 'textcircle') {
7501				$bgcol = '';
7502				if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
7503					$bgcol = $objattr['bgcolor'];
7504				}
7505				$col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
7506				if (isset($objattr['color']) && $objattr['color']) {
7507					$col = $objattr['color'];
7508				}
7509				$this->SetTColor($col);
7510				$this->SetFColor($bgcol);
7511				if ($bgcol) {
7512					$this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');
7513				}
7514				$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
7515				if (isset($objattr['BORDER-WIDTH'])) {
7516					$this->PaintImgBorder($objattr, $is_table);
7517				}
7518				if (empty($this->directWrite)) {
7519					$this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
7520				}
7521				if (isset($objattr['top-text'])) {
7522					$this->directWrite->CircularText($objattr['INNER-X'] + $objattr['INNER-WIDTH'] / 2, $objattr['INNER-Y'] + $objattr['INNER-HEIGHT'] / 2, $objattr['r'] / $k, $objattr['top-text'], 'top', $objattr['fontfamily'], $objattr['fontsize'] / $k, $objattr['fontstyle'], $objattr['space-width'], $objattr['char-width'], (isset($objattr['divider']) ? $objattr['divider'] : ''));
7523				}
7524				if (isset($objattr['bottom-text'])) {
7525					$this->directWrite->CircularText($objattr['INNER-X'] + $objattr['INNER-WIDTH'] / 2, $objattr['INNER-Y'] + $objattr['INNER-HEIGHT'] / 2, $objattr['r'] / $k, $objattr['bottom-text'], 'bottom', $objattr['fontfamily'], $objattr['fontsize'] / $k, $objattr['fontstyle'], $objattr['space-width'], $objattr['char-width'], (isset($objattr['divider']) ? $objattr['divider'] : ''));
7526				}
7527			}
7528
7529			$this->ResetSpacing();
7530
7531			// LIST MARKERS (Text or bullets)	// mPDF 6  Lists
7532			if ($objattr['type'] == 'listmarker') {
7533				if (isset($objattr['fontfamily'])) {
7534					$this->SetFont($objattr['fontfamily'], $objattr['fontstyle'], $objattr['fontsizept']);
7535				}
7536				$col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
7537				if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {
7538					$col = $objattr['colorarray'];
7539				}
7540
7541				if (isset($objattr['bullet']) && $objattr['bullet']) { // Used for position "outside" only
7542					$type = $objattr['bullet'];
7543					$size = $objattr['size'];
7544
7545					if ($objattr['listmarkerposition'] == 'inside') {
7546						$adjx = $size / 2;
7547						if ($objattr['dir'] == 'rtl') {
7548							$adjx += $objattr['offset'];
7549						}
7550						$this->x += $adjx;
7551					} else {
7552						$adjx = $objattr['offset'];
7553						$adjx += $size / 2;
7554						if ($objattr['dir'] == 'rtl') {
7555							$this->x += $adjx;
7556						} else {
7557							$this->x -= $adjx;
7558						}
7559					}
7560
7561					$yadj = $objattr['lineBox']['glyphYorigin'];
7562					if (isset($this->CurrentFont['desc']['XHeight']) && $this->CurrentFont['desc']['XHeight']) {
7563						$xh = $this->CurrentFont['desc']['XHeight'];
7564					} else {
7565						$xh = 500;
7566					}
7567					$yadj -= ($this->FontSize * $xh / 1000) * 0.625; // Vertical height of bullet (centre) from baseline= XHeight * 0.625
7568					$this->y += $yadj;
7569
7570					$this->_printListBullet($this->x, $this->y, $size, $type, $col);
7571				} else {
7572					$this->SetTColor($col);
7573					$w = $this->GetStringWidth($texto);
7574					// NB If change marker-offset, also need to alter in function _getListMarkerWidth
7575					$adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
7576					if ($objattr['dir'] == 'rtl') {
7577						$align = 'L';
7578						$this->x += $adjx;
7579					} else {
7580						// Use these lines to set as marker-offset, right-aligned - default
7581						$align = 'R';
7582						$this->x -= $adjx;
7583						$this->x -= $w;
7584					}
7585					$this->Cell($w, $this->FontSize, $texto, 0, 0, $align, 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']);
7586					$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
7587				}
7588			}
7589
7590			// DOT-TAB
7591			if ($objattr['type'] == 'dottab') {
7592				if (isset($objattr['fontfamily'])) {
7593					$this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);
7594				}
7595				$sp = $this->GetStringWidth(' ');
7596				$nb = floor(($w - 2 * $sp) / $this->GetStringWidth('.'));
7597				if ($nb > 0) {
7598					$dots = ' ' . str_repeat('.', $nb) . ' ';
7599				} else {
7600					$dots = ' ';
7601				}
7602				$col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
7603				if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {
7604					$col = $objattr['colorarray'];
7605				}
7606				$this->SetTColor($col);
7607				$save_dh = $this->divheight;
7608				$save_sbd = $this->spanborddet;
7609				$save_textvar = $this->textvar; // mPDF 5.7.1
7610				$this->spanborddet = '';
7611				$this->divheight = 0;
7612				$this->textvar = 0x00; // mPDF 5.7.1
7613
7614				$this->Cell($w, $h, $dots, 0, 0, 'C', 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']); // mPDF 6 DOTTAB
7615				$this->spanborddet = $save_sbd;
7616				$this->textvar = $save_textvar; // mPDF 5.7.1
7617				$this->divheight = $save_dh;
7618				$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
7619			}
7620
7621			/* -- FORMS -- */
7622			// TEXT/PASSWORD INPUT
7623			if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD')) {
7624				$this->form->print_ob_text($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
7625			}
7626
7627			// TEXTAREA
7628			if ($objattr['type'] == 'textarea') {
7629				$this->form->print_ob_textarea($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
7630			}
7631
7632			// SELECT
7633			if ($objattr['type'] == 'select') {
7634				$this->form->print_ob_select($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
7635			}
7636
7637
7638			// INPUT/BUTTON as IMAGE
7639			if ($objattr['type'] == 'input' && $objattr['subtype'] == 'IMAGE') {
7640				$this->form->print_ob_imageinput($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $is_table);
7641			}
7642
7643			// BUTTON
7644			if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'SUBMIT' || $objattr['subtype'] == 'RESET' || $objattr['subtype'] == 'BUTTON')) {
7645				$this->form->print_ob_button($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
7646			}
7647
7648			// CHECKBOX
7649			if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'CHECKBOX')) {
7650				$this->form->print_ob_checkbox($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);
7651			}
7652			// RADIO
7653			if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'RADIO')) {
7654				$this->form->print_ob_radio($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);
7655			}
7656			/* -- END FORMS -- */
7657		}
7658
7659		$this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);
7660
7661		$this->y = $save_y;
7662		$this->x = $save_x;
7663
7664		unset($content);
7665	}
7666
7667	function _printListBullet($x, $y, $size, $type, $color)
7668	{
7669		// x and y are the centre of the bullet; size is the width and/or height in mm
7670		$fcol = $this->SetTColor($color, true);
7671		$lcol = strtoupper($fcol); // change 0 0 0 rg to 0 0 0 RG
7672		$this->writer->write(sprintf('q %s %s', $lcol, $fcol));
7673		$this->writer->write('0 j 0 J [] 0 d');
7674		if ($type == 'square') {
7675			$size *= 0.85; // Smaller to appear the same size as circle/disc
7676			$this->writer->write(sprintf('%.3F %.3F %.3F %.3F re f', ($x - $size / 2) * Mpdf::SCALE, ($this->h - $y + $size / 2) * Mpdf::SCALE, ($size) * Mpdf::SCALE, (-$size) * Mpdf::SCALE));
7677		} elseif ($type == 'disc') {
7678			$this->Circle($x, $y, $size / 2, 'F'); // Fill
7679		} elseif ($type == 'circle') {
7680			$lw = $size / 12; // Line width
7681			$this->writer->write(sprintf('%.3F w ', $lw * Mpdf::SCALE));
7682			$this->Circle($x, $y, $size / 2 - $lw / 2, 'S'); // Stroke
7683		}
7684		$this->writer->write('Q');
7685	}
7686
7687	// mPDF 6
7688	// Get previous character and move pointers
7689	function _moveToPrevChar(&$contentctr, &$charctr, $content)
7690	{
7691		$lastchar = false;
7692		$charctr--;
7693		while ($charctr < 0) { // go back to previous $content[]
7694			$contentctr--;
7695			if ($contentctr < 0) {
7696				return false;
7697			}
7698			if ($this->usingCoreFont) {
7699				$charctr = strlen($content[$contentctr]) - 1;
7700			} else {
7701				$charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;
7702			}
7703		}
7704		if ($this->usingCoreFont) {
7705			$lastchar = $content[$contentctr][$charctr];
7706		} else {
7707			$lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);
7708		}
7709		return $lastchar;
7710	}
7711
7712	// Get previous character
7713	function _getPrevChar($contentctr, $charctr, $content)
7714	{
7715		$lastchar = false;
7716		$charctr--;
7717		while ($charctr < 0) { // go back to previous $content[]
7718			$contentctr--;
7719			if ($contentctr < 0) {
7720				return false;
7721			}
7722			if ($this->usingCoreFont) {
7723				$charctr = strlen($content[$contentctr]) - 1;
7724			} else {
7725				$charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;
7726			}
7727		}
7728		if ($this->usingCoreFont) {
7729			$lastchar = $content[$contentctr][$charctr];
7730		} else {
7731			$lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);
7732		}
7733		return $lastchar;
7734	}
7735
7736	function WriteFlowingBlock($s, $sOTLdata)
7737	{
7738	// mPDF 5.7.1
7739		$currentx = $this->x;
7740		$is_table = $this->flowingBlockAttr['is_table'];
7741		$table_draft = $this->flowingBlockAttr['table_draft'];
7742		// width of all the content so far in points
7743		$contentWidth = & $this->flowingBlockAttr['contentWidth'];
7744		// cell width in points
7745		$maxWidth = & $this->flowingBlockAttr['width'];
7746		$lineCount = & $this->flowingBlockAttr['lineCount'];
7747		// line height in user units
7748		$stackHeight = & $this->flowingBlockAttr['height'];
7749		$align = & $this->flowingBlockAttr['align'];
7750		$content = & $this->flowingBlockAttr['content'];
7751		$contentB = & $this->flowingBlockAttr['contentB'];
7752		$font = & $this->flowingBlockAttr['font'];
7753		$valign = & $this->flowingBlockAttr['valign'];
7754		$blockstate = $this->flowingBlockAttr['blockstate'];
7755		$cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1
7756
7757		$newblock = $this->flowingBlockAttr['newblock'];
7758		$blockdir = $this->flowingBlockAttr['blockdir'];
7759
7760		// *********** BLOCK BACKGROUND COLOR ***************** //
7761		if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {
7762			$fill = 0;
7763		} else {
7764			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
7765			$fill = 0;
7766		}
7767		$font[] = $this->saveFont();
7768		$content[] = '';
7769		$contentB[] = '';
7770		$cOTLdata[] = $sOTLdata; // mPDF 5.7.1
7771		$currContent = & $content[count($content) - 1];
7772
7773		$CJKoverflow = false;
7774		$Oikomi = false; // mPDF 6
7775		$hanger = '';
7776
7777		// COLS
7778		$oldcolumn = $this->CurrCol;
7779		if ($this->ColActive && !$is_table) {
7780			$this->breakpoints[$this->CurrCol][] = $this->y;
7781		} // *COLUMNS*
7782
7783		/* -- TABLES -- */
7784		if ($is_table) {
7785			$ipaddingL = 0;
7786			$ipaddingR = 0;
7787			$paddingL = 0;
7788			$paddingR = 0;
7789			$cpaddingadjustL = 0;
7790			$cpaddingadjustR = 0;
7791			// Added mPDF 3.0
7792			$fpaddingR = 0;
7793			$fpaddingL = 0;
7794		} else {
7795			/* -- END TABLES -- */
7796			$ipaddingL = $this->blk[$this->blklvl]['padding_left'];
7797			$ipaddingR = $this->blk[$this->blklvl]['padding_right'];
7798			$paddingL = ($ipaddingL * Mpdf::SCALE);
7799			$paddingR = ($ipaddingR * Mpdf::SCALE);
7800			$this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];
7801			$cpaddingadjustL = -$this->cMarginL;
7802			$this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];
7803			$cpaddingadjustR = -$this->cMarginR;
7804			// Added mPDF 3.0 Float DIV
7805			$fpaddingR = 0;
7806			$fpaddingL = 0;
7807			/* -- CSS-FLOAT -- */
7808			if (count($this->floatDivs)) {
7809				list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
7810				if ($r_exists) {
7811					$fpaddingR = $r_width;
7812				}
7813				if ($l_exists) {
7814					$fpaddingL = $l_width;
7815				}
7816			}
7817			/* -- END CSS-FLOAT -- */
7818
7819			$usey = $this->y + 0.002;
7820			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
7821				$usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
7822			}
7823			/* -- CSS-IMAGE-FLOAT -- */
7824			// If float exists at this level
7825			if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
7826				$fpaddingR += $this->floatmargins['R']['w'];
7827			}
7828			if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
7829				$fpaddingL += $this->floatmargins['L']['w'];
7830			}
7831			/* -- END CSS-IMAGE-FLOAT -- */
7832		} // *TABLES*
7833		// OBJECTS - IMAGES & FORM Elements (NB has already skipped line/page if required - in printbuffer)
7834		if (substr($s, 0, 3) == "\xbb\xa4\xac") { // identifier has been identified!
7835			$objattr = $this->_getObjAttr($s);
7836			$h_corr = 0;
7837			if ($is_table) { // *TABLES*
7838				$maximumW = ($maxWidth / Mpdf::SCALE) - ($this->cellPaddingL + $this->cMarginL + $this->cellPaddingR + $this->cMarginR);  // *TABLES*
7839			} // *TABLES*
7840			else { // *TABLES*
7841				if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0) && (!$is_table)) {
7842					$h_corr = $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
7843				}
7844				$maximumW = ($maxWidth / Mpdf::SCALE) - ($this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_right'] + $this->blk[$this->blklvl]['border_right']['w'] + $fpaddingL + $fpaddingR );
7845			} // *TABLES*
7846			$objattr = $this->inlineObject($objattr['type'], $this->lMargin + $fpaddingL + ($contentWidth / Mpdf::SCALE), ($this->y + $h_corr), $objattr, $this->lMargin, ($contentWidth / Mpdf::SCALE), $maximumW, $stackHeight, true, $is_table);
7847
7848			// SET LINEHEIGHT for this line ================ RESET AT END
7849			$stackHeight = max($stackHeight, $objattr['OUTER-HEIGHT']);
7850			$this->objectbuffer[count($content) - 1] = $objattr;
7851			// if (isset($objattr['vertical-align'])) { $valign = $objattr['vertical-align']; }
7852			// else { $valign = ''; }
7853			// LIST MARKERS	// mPDF 6  Lists
7854			if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {
7855				// do nothing
7856			} else {
7857				$contentWidth += ($objattr['OUTER-WIDTH'] * Mpdf::SCALE);
7858			}
7859			return;
7860		}
7861
7862		$lbw = $rbw = 0; // Border widths
7863		if (!empty($this->spanborddet)) {
7864			if (isset($this->spanborddet['L'])) {
7865				$lbw = $this->spanborddet['L']['w'];
7866			}
7867			if (isset($this->spanborddet['R'])) {
7868				$rbw = $this->spanborddet['R']['w'];
7869			}
7870		}
7871
7872		if ($this->usingCoreFont) {
7873			$clen = strlen($s);
7874		} else {
7875			$clen = mb_strlen($s, $this->mb_enc);
7876		}
7877
7878		// for every character in the string
7879		for ($i = 0; $i < $clen; $i++) {
7880			// extract the current character
7881			// get the width of the character in points
7882			if ($this->usingCoreFont) {
7883				$c = $s[$i];
7884				// Soft Hyphens chr(173)
7885				$cw = ($this->GetCharWidthCore($c) * Mpdf::SCALE);
7886				if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
7887					if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c])) {
7888						$cw += ($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c] * $this->FontSizePt / 1000 );
7889					}
7890				}
7891			} else {
7892				$c = mb_substr($s, $i, 1, $this->mb_enc);
7893				$cw = ($this->GetCharWidthNonCore($c, false) * Mpdf::SCALE);
7894				// mPDF 5.7.1
7895				// Use OTL GPOS
7896				if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
7897					// ...WriteFlowingBlock...
7898					// Only  add XAdvanceL (not sure at present whether RTL or LTR writing direction)
7899					// At this point, XAdvanceL and XAdvanceR will balance
7900					if (isset($sOTLdata['GPOSinfo'][$i]['XAdvanceL'])) {
7901						$cw += $sOTLdata['GPOSinfo'][$i]['XAdvanceL'] * (1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000) * Mpdf::SCALE;
7902					}
7903				}
7904				if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
7905					$lastc = mb_substr($s, ($i - 1), 1, $this->mb_enc);
7906					$ulastc = $this->UTF8StringToArray($lastc, false);
7907					$uc = $this->UTF8StringToArray($c, false);
7908					if (isset($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]])) {
7909						$cw += ($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]] * $this->FontSizePt / 1000 );
7910					}
7911				}
7912			}
7913
7914			if ($i == 0) {
7915				$cw += $lbw * Mpdf::SCALE;
7916				$contentB[(count($contentB) - 1)] .= 'L';
7917			}
7918			if ($i == ($clen - 1)) {
7919				$cw += $rbw * Mpdf::SCALE;
7920				$contentB[(count($contentB) - 1)] .= 'R';
7921			}
7922			if ($c == ' ') {
7923				$currContent .= $c;
7924				$contentWidth += $cw;
7925				continue;
7926			}
7927
7928			// Paragraph INDENT
7929			$WidthCorrection = 0;
7930			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
7931				$ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false);  // mPDF 5.7.4
7932				$WidthCorrection = ($ti * Mpdf::SCALE);
7933			}
7934			// OUTDENT
7935			foreach ($this->objectbuffer as $k => $objattr) {   // mPDF 6 DOTTAB
7936				if ($objattr['type'] == 'dottab') {
7937					$WidthCorrection -= ($objattr['outdent'] * Mpdf::SCALE);
7938					break;
7939				}
7940			}
7941
7942
7943			// Added mPDF 3.0 Float DIV
7944			$fpaddingR = 0;
7945			$fpaddingL = 0;
7946			/* -- CSS-FLOAT -- */
7947			if (count($this->floatDivs)) {
7948				list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
7949				if ($r_exists) {
7950					$fpaddingR = $r_width;
7951				}
7952				if ($l_exists) {
7953					$fpaddingL = $l_width;
7954				}
7955			}
7956			/* -- END CSS-FLOAT -- */
7957
7958			$usey = $this->y + 0.002;
7959			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
7960				$usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
7961			}
7962
7963			/* -- CSS-IMAGE-FLOAT -- */
7964			// If float exists at this level
7965			if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
7966				$fpaddingR += $this->floatmargins['R']['w'];
7967			}
7968			if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
7969				$fpaddingL += $this->floatmargins['L']['w'];
7970			}
7971			/* -- END CSS-IMAGE-FLOAT -- */
7972
7973
7974			// try adding another char
7975			if (( $contentWidth + $cw > $maxWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) + 0.001)) {// 0.001 is to correct for deviations converting mm=>pts
7976				// it won't fit, output what we already have
7977				$lineCount++;
7978
7979				// contains any content that didn't make it into this print
7980				$savedContent = '';
7981				$savedContentB = '';
7982				$savedOTLdata = []; // mPDF 5.7.1
7983				$savedFont = [];
7984				$savedObj = [];
7985				$savedPreOTLdata = []; // mPDF 5.7.1
7986				$savedPreContent = [];
7987				$savedPreContentB = [];
7988				$savedPreFont = [];
7989
7990				// mPDF 6
7991				// New line-breaking algorithm
7992				/////////////////////
7993				// LINE BREAKING
7994				/////////////////////
7995				$breakfound = false;
7996				$contentctr = count($content) - 1;
7997				if ($this->usingCoreFont) {
7998					$charctr = strlen($currContent);
7999				} else {
8000					$charctr = mb_strlen($currContent, $this->mb_enc);
8001				}
8002				$checkchar = $c;
8003				$prevchar = $this->_getPrevChar($contentctr, $charctr, $content);
8004
8005				/* -- CJK-FONTS -- */
8006				// 1) CJK Overflowing a) punctuation or b) Oikomi
8007				// Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi
8008				if ($CJKoverflow || $Oikomi) { // If flag already set
8009					$CJKoverflow = false;
8010					$Oikomi = false;
8011					$breakfound = true;
8012				}
8013				if (!$this->usingCoreFont && !$breakfound && $this->checkCJK) {
8014
8015					// Get next/following character (in this chunk)
8016					$followingchar = '';
8017					if ($i < ($clen - 1)) {
8018						if ($this->usingCoreFont) {
8019							$followingchar = $s[$i + 1];
8020						} else {
8021							$followingchar = mb_substr($s, $i + 1, 1, $this->mb_enc);
8022						}
8023					}
8024
8025					// 1a) Overflow punctuation
8026					if (preg_match("/[" . $this->pregCJKchars . "]/u", $prevchar) && preg_match("/[" . $this->CJKoverflow . "]/u", $checkchar) && $this->allowCJKorphans) {
8027						// add character onto this line
8028						$currContent .= $c;
8029						$contentWidth += $cw;
8030						$CJKoverflow = true; // Set flag
8031						continue;
8032					} elseif (preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) && $this->allowCJKorphans &&
8033							(preg_match("/[" . $this->CJKleading . "]/u", $followingchar) || preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar)) &&
8034							!preg_match("/[" . $this->CJKleading . "]/u", $checkchar) && !preg_match("/[" . $this->CJKfollowing . "]/u", $followingchar) &&
8035							!(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $followingchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) {
8036						// 1b) Try squeezing another character(s) onto this line = Oikomi, if character cannot end line
8037						// or next character cannot start line (and not splitting CJK numerals)
8038						// NB otherwise it move lastchar(s) to next line to keep $c company = Oidashi, which is done below in standard way
8039						// add character onto this line
8040						$currContent .= $c;
8041						$contentWidth += $cw;
8042						$Oikomi = true; // Set flag
8043						continue;
8044					}
8045				}
8046				/* -- END CJK-FONTS -- */
8047				/* -- HYPHENATION -- */
8048
8049				// AUTOMATIC HYPHENATION
8050				// 2) Automatic hyphen in current word (does not cross tags)
8051				if (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] == 1) {
8052					$currWord = '';
8053					// Look back and ahead to get current word
8054					for ($ac = $charctr - 1; $ac >= 0; $ac--) {
8055						if ($this->usingCoreFont) {
8056							$addc = substr($currContent, $ac, 1);
8057						} else {
8058							$addc = mb_substr($currContent, $ac, 1, $this->mb_enc);
8059						}
8060						if ($addc == ' ') {
8061							break;
8062						}
8063						$currWord = $addc . $currWord;
8064					}
8065					$start = $ac + 1;
8066					for ($ac = $i; $ac < ($clen - 1); $ac++) {
8067						if ($this->usingCoreFont) {
8068							$addc = substr($s, $ac, 1);
8069						} else {
8070							$addc = mb_substr($s, $ac, 1, $this->mb_enc);
8071						}
8072						if ($addc == ' ') {
8073							break;
8074						}
8075						$currWord .= $addc;
8076					}
8077					$ptr = $this->hyphenator->hyphenateWord($currWord, $charctr - $start);
8078					if ($ptr > -1) {
8079						$breakfound = [$contentctr, $start + $ptr, $contentctr, $start + $ptr, 'hyphen'];
8080					}
8081				}
8082				/* -- END HYPHENATION -- */
8083
8084				// Search backwards to find first line-break opportunity
8085				while ($breakfound == false && $prevchar !== false) {
8086					$cutcontentctr = $contentctr;
8087					$cutcharctr = $charctr;
8088					$prevchar = $this->_moveToPrevChar($contentctr, $charctr, $content);
8089					/////////////////////
8090					// 3) Break at SPACE
8091					/////////////////////
8092					if ($prevchar == ' ') {
8093						$breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard'];
8094					} /////////////////////
8095					// 4) Break at U+200B in current word (Khmer, Lao & Thai Invisible word boundary, and Tibetan)
8096					/////////////////////
8097					elseif ($prevchar == "\xe2\x80\x8b") { // U+200B Zero-width Word Break
8098						$breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard'];
8099					} /////////////////////
8100					// 5) Break at Hard HYPHEN '-' or U+2010
8101					/////////////////////
8102					elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && ($prevchar == '-' || $prevchar == "\xe2\x80\x90")) {
8103						// Don't break a URL
8104						// Look back to get first part of current word
8105						$checkw = '';
8106						for ($ac = $charctr - 1; $ac >= 0; $ac--) {
8107							if ($this->usingCoreFont) {
8108								$addc = substr($currContent, $ac, 1);
8109							} else {
8110								$addc = mb_substr($currContent, $ac, 1, $this->mb_enc);
8111							}
8112							if ($addc == ' ') {
8113								break;
8114							}
8115							$checkw = $addc . $checkw;
8116						}
8117						// Don't break if HyphenMinus AND (a URL or before a numeral or before a >)
8118						if ((!preg_match('/(http:|ftp:|https:|www\.)/', $checkw) && $checkchar != '>' && !preg_match('/[0-9]/', $checkchar)) || $prevchar == "\xe2\x80\x90") {
8119							$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
8120						}
8121					} /////////////////////
8122					// 6) Break at Soft HYPHEN (replace with hard hyphen)
8123					/////////////////////
8124					elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && !$this->usingCoreFont && $prevchar == "\xc2\xad") {
8125						$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
8126						$content[$contentctr] = mb_substr($content[$contentctr], 0, $charctr, $this->mb_enc) . '-' . mb_substr($content[$contentctr], $charctr + 1, mb_strlen($content[$contentctr]), $this->mb_enc);
8127						if (!empty($cOTLdata[$contentctr])) {
8128							$cOTLdata[$contentctr]['char_data'][$charctr] = ['bidi_class' => 9, 'uni' => 45];
8129							$cOTLdata[$contentctr]['group'][$charctr] = 'C';
8130						}
8131					} elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && $prevchar == chr(173)) {
8132						$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
8133						$content[$contentctr] = substr($content[$contentctr], 0, $charctr) . '-' . substr($content[$contentctr], $charctr + 1);
8134					} /* -- CJK-FONTS -- */
8135					/////////////////////
8136					// 7) Break at CJK characters (unless forbidden characters to end or start line)
8137					// CJK Avoiding line break in the middle of numerals
8138					/////////////////////
8139					elseif (!$this->usingCoreFont && $this->checkCJK && preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) &&
8140						!preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar) && !preg_match("/[" . $this->CJKleading . "]/u", $prevchar) &&
8141						!(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $prevchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) {
8142						$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
8143					}
8144					/* -- END CJK-FONTS -- */
8145					/////////////////////
8146					// 8) Break at OBJECT (Break before all objects here - selected objects are moved forward to next line below e.g. dottab)
8147					/////////////////////
8148					if (isset($this->objectbuffer[$contentctr])) {
8149						$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
8150					}
8151
8152
8153					$checkchar = $prevchar;
8154				}
8155
8156				// If a line-break opportunity found:
8157				if (is_array($breakfound)) {
8158					$contentctr = $breakfound[0];
8159					$charctr = $breakfound[1];
8160					$cutcontentctr = $breakfound[2];
8161					$cutcharctr = $breakfound[3];
8162					$type = $breakfound[4];
8163					// Cache chunks which are already processed, but now need to be passed on to the new line
8164					for ($ix = count($content) - 1; $ix > $cutcontentctr; $ix--) {
8165						// save and crop off any subsequent chunks
8166						/* -- OTL -- */
8167						if (!empty($sOTLdata)) {
8168							$tmpOTL = array_pop($cOTLdata);
8169							$savedPreOTLdata[] = $tmpOTL;
8170						}
8171						/* -- END OTL -- */
8172						$savedPreContent[] = array_pop($content);
8173						$savedPreContentB[] = array_pop($contentB);
8174						$savedPreFont[] = array_pop($font);
8175					}
8176
8177					// Next cache the part which will start the next line
8178					if ($this->usingCoreFont) {
8179						$savedPreContent[] = substr($content[$cutcontentctr], $cutcharctr);
8180					} else {
8181						$savedPreContent[] = mb_substr($content[$cutcontentctr], $cutcharctr, mb_strlen($content[$cutcontentctr]), $this->mb_enc);
8182					}
8183					$savedPreContentB[] = preg_replace('/L/', '', $contentB[$cutcontentctr]);
8184					$savedPreFont[] = $font[$cutcontentctr];
8185					/* -- OTL -- */
8186					if (!empty($sOTLdata)) {
8187						$savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[$cutcontentctr], $cutcharctr, $cutcharctr);
8188					}
8189					/* -- END OTL -- */
8190
8191
8192					// Finally adjust the Current content which ends this line
8193					if ($cutcharctr == 0 && $type == 'discard') {
8194						array_pop($content);
8195						array_pop($contentB);
8196						array_pop($font);
8197						array_pop($cOTLdata);
8198					}
8199
8200					$currContent = & $content[count($content) - 1];
8201					if ($this->usingCoreFont) {
8202						$currContent = substr($currContent, 0, $charctr);
8203					} else {
8204						$currContent = mb_substr($currContent, 0, $charctr, $this->mb_enc);
8205					}
8206
8207					if (!empty($sOTLdata)) {
8208						$savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));
8209					}
8210
8211					if (strpos($contentB[(count($contentB) - 1)], 'R') !== false) {   // ???
8212						$contentB[count($content) - 1] = preg_replace('/R/', '', $contentB[count($content) - 1]); // ???
8213					}
8214
8215					if ($type == 'hyphen') {
8216						$currContent .= '-';
8217						if (!empty($cOTLdata[(count($cOTLdata) - 1)])) {
8218							$cOTLdata[(count($cOTLdata) - 1)]['char_data'][] = ['bidi_class' => 9, 'uni' => 45];
8219							$cOTLdata[(count($cOTLdata) - 1)]['group'] .= 'C';
8220						}
8221					}
8222
8223					$savedContent = '';
8224					$savedContentB = '';
8225					$savedFont = [];
8226					$savedOTLdata = [];
8227				}
8228				// If no line-break opportunity found - split at current position
8229				// or - Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi, as set above by:
8230				// 1) CJK Overflowing a) punctuation or b) Oikomi
8231				// in which case $breakfound==1 and NOT array
8232
8233				if (!is_array($breakfound)) {
8234					$savedFont = $this->saveFont();
8235					if (!empty($sOTLdata)) {
8236						$savedOTLdata = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));
8237					}
8238				}
8239
8240				if ($content[count($content) - 1] == '' && !isset($this->objectbuffer[count($content) - 1])) {
8241					array_pop($content);
8242					array_pop($contentB);
8243					array_pop($font);
8244					array_pop($cOTLdata);
8245					$currContent = & $content[count($content) - 1];
8246				}
8247
8248				// Right Trim current content - including CJK space, and for OTLdata
8249				// incl. CJK - strip CJK space at end of line &#x3000; = \xe3\x80\x80 = CJK space
8250				$currContent = $currContent ? rtrim($currContent) : '';
8251				if ($this->checkCJK) {
8252					$currContent = preg_replace("/\xe3\x80\x80$/", '', $currContent);
8253				} // *CJK-FONTS*
8254				/* -- OTL -- */
8255				if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
8256					$this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true); // NB also does U+3000
8257				}
8258				/* -- END OTL -- */
8259
8260
8261				// Selected OBJECTS are moved forward to next line, unless they come before a space or U+200B (type='discard')
8262				if (isset($this->objectbuffer[(count($content) - 1)]) && (!isset($type) || $type != 'discard')) {
8263					$objtype = $this->objectbuffer[(count($content) - 1)]['type'];
8264					if ($objtype == 'dottab' || $objtype == 'bookmark' || $objtype == 'indexentry' || $objtype == 'toc' || $objtype == 'annot') {
8265						$savedObj = array_pop($this->objectbuffer);
8266					}
8267				}
8268
8269
8270				// Decimal alignment (cancel if wraps to > 1 line)
8271				if ($is_table && substr($align, 0, 1) == 'D') {
8272					$align = substr($align, 2, 1);
8273				}
8274
8275				$lineBox = [];
8276
8277				$this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);
8278
8279				// update $contentWidth since it has changed with cropping
8280				$contentWidth = 0;
8281
8282				$inclCursive = false;
8283				foreach ($content as $k => $chunk) {
8284					if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
8285						// LIST MARKERS
8286						if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker']) {
8287							if ($this->objectbuffer[$k]['listmarkerposition'] != 'outside') {
8288								$contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
8289							}
8290						} else {
8291							$contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
8292						}
8293					} elseif (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
8294						$this->restoreFont($font[$k], false);
8295						if ($this->checkCJK && $k == count($content) - 1 && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $this->CJKforceend) {
8296							// force-end overhang
8297							$hanger = mb_substr($chunk, mb_strlen($chunk, $this->mb_enc) - 1, 1, $this->mb_enc);
8298							// Probably ought to do something with char_data and GPOS in cOTLdata...
8299							$content[$k] = $chunk = mb_substr($chunk, 0, mb_strlen($chunk, $this->mb_enc) - 1, $this->mb_enc);
8300						}
8301
8302						// Soft Hyphens chr(173) + Replace NBSP with SPACE + Set inclcursive if includes CURSIVE TEXT
8303						if (!$this->usingCoreFont) {
8304							/* -- OTL -- */
8305							if ((isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) || !empty($sOTLdata)) {
8306								$this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad");
8307								$this->otl->replaceSpace($chunk, $cOTLdata[$k]); // NBSP -> space
8308								if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) {
8309									$inclCursive = true;
8310								}
8311								$content[$k] = $chunk;
8312							} /* -- END OTL -- */ else {  // *OTL*
8313								$content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk);
8314								$content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);
8315							} // *OTL*
8316						} elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
8317							$content[$k] = $chunk = str_replace(chr(173), '', $chunk);
8318							$content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);
8319						}
8320
8321						$contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE;  // mPDF 5.7.1
8322						if (!empty($this->spanborddet)) {
8323							if (isset($this->spanborddet['L']['w']) && strpos($contentB[$k], 'L') !== false) {
8324								$contentWidth += $this->spanborddet['L']['w'] * Mpdf::SCALE;
8325							}
8326							if (isset($this->spanborddet['R']['w']) && strpos($contentB[$k], 'R') !== false) {
8327								$contentWidth += $this->spanborddet['R']['w'] * Mpdf::SCALE;
8328							}
8329						}
8330					}
8331				}
8332
8333				$lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');
8334				$lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');
8335				if ($blockdir == 'ltr' && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic
8336					$lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE;
8337				} else {
8338					$lastitalic = 0;
8339				}
8340
8341
8342
8343
8344				// NOW FORMAT THE LINE TO OUTPUT
8345				if (!$table_draft) {
8346					// DIRECTIONALITY RTL
8347					$chunkorder = range(0, count($content) - 1); // mPDF 5.7
8348					/* -- OTL -- */
8349					// mPDF 6
8350					if ($blockdir == 'rtl' || $this->biDirectional) {
8351						$this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);
8352						// From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to
8353						// $this->objectbuffer and $font ($chunkorder contains the mapping)
8354					}
8355
8356					/* -- END OTL -- */
8357					// Remove any XAdvance from OTL data at end of line
8358					foreach ($chunkorder as $aord => $k) {
8359						if (count($cOTLdata)) {
8360							$this->restoreFont($font[$k], false);
8361							// ...WriteFlowingBlock...
8362							if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line
8363								$nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character
8364								if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {
8365									if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {
8366										$w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
8367									} else {
8368										$w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
8369									}
8370									$w *= ($this->FontSize / 1000);
8371									$contentWidth -= $w * Mpdf::SCALE;
8372									$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;
8373									$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;
8374								}
8375
8376								// If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it
8377								if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {
8378									$w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
8379									$w *= ($this->FontSize / 1000);
8380									$contentWidth -= $w * Mpdf::SCALE;
8381									$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
8382									$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
8383								}
8384							}
8385						}
8386					}
8387
8388					// JUSTIFICATION J
8389					$jcharspacing = 0;
8390					$jws = 0;
8391					$nb_carac = 0;
8392					$nb_spaces = 0;
8393					$jkashida = 0;
8394					// if it's justified, we need to find the char/word spacing (or if hanger $this->CJKforceend)
8395					if (($align == 'J' && !$CJKoverflow) || (($contentWidth + $lastitalic > $maxWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) + 0.001) && (!$CJKoverflow || ($CJKoverflow && !$this->allowCJKoverflow))) || $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {   // 0.001 is to correct for deviations converting mm=>pts
8396						// JUSTIFY J (Use character spacing)
8397						// WORD SPACING
8398						// mPDF 5.7
8399						foreach ($chunkorder as $aord => $k) {
8400							$chunk = isset($content[$aord]) ? $content[$aord] : '';
8401							if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
8402								$nb_carac += mb_strlen($chunk, $this->mb_enc);
8403								$nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);
8404								// Use GPOS OTL
8405								if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
8406									if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
8407										$nb_carac -= substr_count($cOTLdata[$aord]['group'], 'M');
8408									}
8409								}
8410							} else {
8411								$nb_carac ++;
8412							} // mPDF 6 allow spacing for inline object
8413						}
8414						// GetJSpacing adds kashida spacing to GPOSinfo if appropriate for Font
8415						list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata);
8416					}
8417
8418					// WORD SPACING
8419					$empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) );
8420
8421					$empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1
8422					$empty -= ($jws * $nb_spaces);
8423					$empty -= ($jkashida);
8424					$empty /= Mpdf::SCALE;
8425
8426					$b = ''; // do not use borders
8427					// Get PAGEBREAK TO TEST for height including the top border/padding
8428					$check_h = max($this->divheight, $stackHeight);
8429					if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blklvl > 0) && ($lineCount == 1) && (!$is_table)) {
8430						$check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);
8431					}
8432
8433					if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {
8434						$this->SetCol($this->NbCol - 1);
8435					}
8436
8437					// PAGEBREAK
8438					// 'If' below used in order to fix "first-line of other page with justify on" bug
8439					if (!$is_table && ($this->y + $check_h) > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {
8440						$bak_x = $this->x; // Current X position
8441						// WORD SPACING
8442						$ws = $this->ws; // Word Spacing
8443						$charspacing = $this->charspacing; // Character Spacing
8444						$this->ResetSpacing();
8445
8446						$this->AddPage($this->CurOrientation);
8447
8448						$this->x = $bak_x;
8449						// Added to correct for OddEven Margins
8450						$currentx += $this->MarginCorrection;
8451						$this->x += $this->MarginCorrection;
8452
8453						// WORD SPACING
8454						$this->SetSpacing($charspacing, $ws);
8455					}
8456
8457					if ($this->kwt && !$is_table) { // mPDF 5.7+
8458						$this->printkwtbuffer();
8459						$this->kwt = false;
8460					}
8461
8462
8463					/* -- COLUMNS -- */
8464					// COLS
8465					// COLUMN CHANGE
8466					if ($this->CurrCol != $oldcolumn) {
8467						$currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
8468						$this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
8469						$oldcolumn = $this->CurrCol;
8470					}
8471
8472					if ($this->ColActive && !$is_table) {
8473						$this->breakpoints[$this->CurrCol][] = $this->y;
8474					} // *COLUMNS*
8475					/* -- END COLUMNS -- */
8476
8477					// TOP MARGIN
8478					if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && ($lineCount == 1) && (!$is_table)) {
8479						$this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
8480						if ($this->ColActive) {
8481							$this->breakpoints[$this->CurrCol][] = $this->y;
8482						} // *COLUMNS*
8483					}
8484
8485
8486					// Update y0 for top of block (used to paint border)
8487					if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table)) {
8488						$this->blk[$this->blklvl]['y0'] = $this->y;
8489						$this->blk[$this->blklvl]['startpage'] = $this->page;
8490						if ($this->blk[$this->blklvl]['float']) {
8491							$this->blk[$this->blklvl]['float_start_y'] = $this->y;
8492						}
8493					}
8494
8495					// TOP PADDING and BORDER spacing/fill
8496					if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 1) && (!$is_table)) {
8497						// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
8498						$this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);
8499						if ($this->ColActive) {
8500							$this->breakpoints[$this->CurrCol][] = $this->y;
8501						} // *COLUMNS*
8502					}
8503
8504					$arraysize = count($chunkorder);
8505
8506					$margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );
8507
8508					// PAINT BACKGROUND FOR THIS LINE
8509					if (!$is_table) {
8510						$this->DivLn($stackHeight, $this->blklvl, false);
8511					} // false -> don't advance y
8512
8513					$this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;
8514					if ($align == 'R') {
8515						$this->x += $empty;
8516					} elseif ($align == 'C') {
8517						$this->x += ($empty / 2);
8518					}
8519
8520					// Paragraph INDENT
8521					if (isset($this->blk[$this->blklvl]['text_indent']) && ($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table) && ($blockdir != 'rtl') && ($align != 'C')) {
8522						$ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false);  // mPDF 5.7.4
8523						$this->x += $ti;
8524					}
8525
8526					// BIDI magic_reverse moved upwards from here
8527					foreach ($chunkorder as $aord => $k) { // mPDF 5.7
8528
8529						$chunk = isset($content[$aord]) ? $content[$aord] : '';
8530
8531						if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
8532							$xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];
8533							$this->objectbuffer[$k]['OUTER-X'] += $xadj;
8534							$this->objectbuffer[$k]['BORDER-X'] += $xadj;
8535							$this->objectbuffer[$k]['INNER-X'] += $xadj;
8536
8537							if ($this->objectbuffer[$k]['type'] == 'listmarker') {
8538								$this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin
8539							}
8540							$yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];
8541							if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
8542								$this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin
8543							}
8544							if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB
8545								$yadj += $lineBox[$k]['top'];
8546							}
8547							$this->objectbuffer[$k]['OUTER-Y'] += $yadj;
8548							$this->objectbuffer[$k]['BORDER-Y'] += $yadj;
8549							$this->objectbuffer[$k]['INNER-Y'] += $yadj;
8550						}
8551
8552						$this->restoreFont($font[$k]);  // mPDF 5.7
8553
8554						$this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws);
8555						// Now unset these values so they don't influence GetStringwidth below or in fn. Cell
8556						$this->fixedlSpacing = false;
8557						$this->minwSpacing = 0;
8558
8559						$save_vis = $this->visibility;
8560						if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {
8561							$this->SetVisibility($this->textparam['visibility']);
8562						}
8563						// *********** SPAN BACKGROUND COLOR ***************** //
8564						if ($this->spanbgcolor) {
8565							$cor = $this->spanbgcolorarray;
8566							$this->SetFColor($cor);
8567							$save_fill = $fill;
8568							$spanfill = 1;
8569							$fill = 1;
8570						}
8571						if (!empty($this->spanborddet)) {
8572							if (strpos($contentB[$k], 'L') !== false) {
8573								$this->x += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
8574							}
8575							if (strpos($contentB[$k], 'L') === false) {
8576								$this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;
8577							}
8578							if (strpos($contentB[$k], 'R') === false) {
8579								$this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;
8580							}
8581						}
8582
8583						// WORD SPACING
8584						// StringWidth this time includes any kashida spacing
8585						$stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, true);
8586
8587						$nch = mb_strlen($chunk, $this->mb_enc);
8588						// Use GPOS OTL
8589						if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
8590							if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
8591								$nch -= substr_count($cOTLdata[$aord]['group'], 'M');
8592							}
8593						}
8594						$stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE );
8595
8596						$stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE );
8597
8598						if (isset($this->objectbuffer[$k])) {
8599							// LIST MARKERS	// mPDF 6  Lists
8600							if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
8601								$stringWidth = 0;
8602							} else {
8603								$stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];
8604							}
8605						}
8606
8607						if ($stringWidth == 0) {
8608							$stringWidth = 0.000001;
8609						}
8610
8611						if ($aord == $arraysize - 1) {
8612							$stringWidth -= ( $this->charspacing / Mpdf::SCALE );
8613							if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {
8614								// force-end overhang
8615								$this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false));
8616								$this->Cell($this->GetStringWidth($hanger), $stackHeight, $hanger, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false));
8617							} else {
8618								$this->Cell($stringWidth, $stackHeight, $chunk, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mono-style line or last part (skips line)
8619							}
8620						} else {
8621							$this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, 0, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // first or middle part
8622						}
8623
8624						if (!empty($this->spanborddet)) {
8625							if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) {
8626								$this->x += $this->spanborddet['R']['w'];
8627							}
8628						}
8629						// *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //
8630						if (isset($spanfill) && $spanfill) {
8631							$fill = $save_fill;
8632							$spanfill = 0;
8633							if ($fill) {
8634								$this->SetFColor($bcor);
8635							}
8636						}
8637						if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {
8638							$this->SetVisibility($save_vis);
8639						}
8640					}
8641				} elseif ($table_draft) {
8642					$this->y += $stackHeight;
8643				}
8644
8645				if (!$is_table) {
8646					$this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin']));
8647					$this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin']));
8648				}
8649
8650				// move on to the next line, reset variables, tack on saved content and current char
8651
8652				if (!$table_draft) {
8653					$this->printobjectbuffer($is_table, $blockdir);
8654				}
8655				$this->objectbuffer = [];
8656
8657
8658				/* -- CSS-IMAGE-FLOAT -- */
8659				// Update values if set to skipline
8660				if ($this->floatmargins) {
8661					$this->_advanceFloatMargins();
8662				}
8663				/* -- END CSS-IMAGE-FLOAT -- */
8664
8665				// Reset lineheight
8666				$stackHeight = $this->divheight;
8667				$valign = 'M';
8668
8669				$font = [];
8670				$content = [];
8671				$contentB = [];
8672				$cOTLdata = []; // mPDF 5.7.1
8673				$contentWidth = 0;
8674				if (!empty($savedObj)) {
8675					$this->objectbuffer[] = $savedObj;
8676					$font[] = $savedFont;
8677					$content[] = '';
8678					$contentB[] = '';
8679					$cOTLdata[] = []; // mPDF 5.7.1
8680					$contentWidth += $savedObj['OUTER-WIDTH'] * Mpdf::SCALE;
8681				}
8682				if (count($savedPreContent) > 0) {
8683					for ($ix = count($savedPreContent) - 1; $ix >= 0; $ix--) {
8684						$font[] = $savedPreFont[$ix];
8685						$content[] = $savedPreContent[$ix];
8686						$contentB[] = $savedPreContentB[$ix];
8687						if (!empty($sOTLdata)) {
8688							$cOTLdata[] = $savedPreOTLdata[$ix];
8689						}
8690						$this->restoreFont($savedPreFont[$ix]);
8691						$lbw = $rbw = 0; // Border widths
8692						if (!empty($this->spanborddet)) {
8693							$lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
8694							$rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
8695						}
8696						if ($ix > 0) {
8697							$contentWidth += $this->GetStringWidth($savedPreContent[$ix], true, (isset($savedPreOTLdata[$ix]) ? $savedPreOTLdata[$ix] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1
8698							if (strpos($savedPreContentB[$ix], 'L') !== false) {
8699								$contentWidth += $lbw;
8700							}
8701							if (strpos($savedPreContentB[$ix], 'R') !== false) {
8702								$contentWidth += $rbw;
8703							}
8704						}
8705					}
8706					$savedPreContent = [];
8707					$savedPreContentB = [];
8708					$savedPreOTLdata = []; // mPDF 5.7.1
8709					$savedPreFont = [];
8710					$content[(count($content) - 1)] .= $c;
8711				} else {
8712					$font[] = $savedFont;
8713					$content[] = $savedContent . $c;
8714					$contentB[] = $savedContentB;
8715					$cOTLdata[] = $savedOTLdata; // mPDF 5.7.1
8716				}
8717
8718				$currContent = & $content[(count($content) - 1)];
8719				$this->restoreFont($font[(count($font) - 1)]); // mPDF 6.0
8720
8721				/* -- CJK-FONTS -- */
8722				// CJK - strip CJK space at start of line
8723				// &#x3000; = \xe3\x80\x80 = CJK space
8724				if ($this->checkCJK && $currContent == "\xe3\x80\x80") {
8725					$currContent = '';
8726					if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
8727						$this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], true, false); // left trim U+3000
8728					}
8729				}
8730				/* -- END CJK-FONTS -- */
8731
8732				$lbw = $rbw = 0; // Border widths
8733				if (!empty($this->spanborddet)) {
8734					$lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
8735					$rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
8736				}
8737
8738				$contentWidth += $this->GetStringWidth($currContent, false, (isset($cOTLdata[(count($cOTLdata) - 1)]) ? $cOTLdata[(count($cOTLdata) - 1)] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1
8739				if (strpos($savedContentB, 'L') !== false) {
8740					$contentWidth += $lbw;
8741				}
8742				$CJKoverflow = false;
8743				$hanger = '';
8744			} // another character will fit, so add it on
8745			else {
8746				$contentWidth += $cw;
8747				$currContent .= $c;
8748			}
8749		}
8750
8751		unset($content);
8752		unset($contentB);
8753	}
8754
8755	// ----------------------END OF FLOWING BLOCK------------------------------------//
8756
8757
8758	/* -- CSS-IMAGE-FLOAT -- */
8759	// Update values if set to skipline
8760	function _advanceFloatMargins()
8761	{
8762		// Update floatmargins - L
8763		if (isset($this->floatmargins['L']) && $this->floatmargins['L']['skipline'] && $this->floatmargins['L']['y0'] != $this->y) {
8764			$yadj = $this->y - $this->floatmargins['L']['y0'];
8765			$this->floatmargins['L']['y0'] = $this->y;
8766			$this->floatmargins['L']['y1'] += $yadj;
8767
8768			// Update objattr in floatbuffer
8769			if ($this->floatbuffer[$this->floatmargins['L']['id']]['border_left']['w']) {
8770				$this->floatbuffer[$this->floatmargins['L']['id']]['BORDER-Y'] += $yadj;
8771			}
8772			$this->floatbuffer[$this->floatmargins['L']['id']]['INNER-Y'] += $yadj;
8773			$this->floatbuffer[$this->floatmargins['L']['id']]['OUTER-Y'] += $yadj;
8774
8775			// Unset values
8776			$this->floatbuffer[$this->floatmargins['L']['id']]['skipline'] = false;
8777			$this->floatmargins['L']['skipline'] = false;
8778			$this->floatmargins['L']['id'] = '';
8779		}
8780		// Update floatmargins - R
8781		if (isset($this->floatmargins['R']) && $this->floatmargins['R']['skipline'] && $this->floatmargins['R']['y0'] != $this->y) {
8782			$yadj = $this->y - $this->floatmargins['R']['y0'];
8783			$this->floatmargins['R']['y0'] = $this->y;
8784			$this->floatmargins['R']['y1'] += $yadj;
8785
8786			// Update objattr in floatbuffer
8787			if ($this->floatbuffer[$this->floatmargins['R']['id']]['border_left']['w']) {
8788				$this->floatbuffer[$this->floatmargins['R']['id']]['BORDER-Y'] += $yadj;
8789			}
8790			$this->floatbuffer[$this->floatmargins['R']['id']]['INNER-Y'] += $yadj;
8791			$this->floatbuffer[$this->floatmargins['R']['id']]['OUTER-Y'] += $yadj;
8792
8793			// Unset values
8794			$this->floatbuffer[$this->floatmargins['R']['id']]['skipline'] = false;
8795			$this->floatmargins['R']['skipline'] = false;
8796			$this->floatmargins['R']['id'] = '';
8797		}
8798	}
8799
8800	/* -- END CSS-IMAGE-FLOAT -- */
8801
8802
8803
8804	/* -- END HTML-CSS -- */
8805
8806	function _SetTextRendering($mode)
8807	{
8808		if (!(($mode == 0) || ($mode == 1) || ($mode == 2))) {
8809			throw new \Mpdf\MpdfException("Text rendering mode should be 0, 1 or 2 (value : $mode)");
8810		}
8811		$tr = ($mode . ' Tr');
8812		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
8813			$this->writer->write($tr);
8814		}
8815		$this->pageoutput[$this->page]['TextRendering'] = $tr;
8816	}
8817
8818	function SetTextOutline($params = [])
8819	{
8820		if (isset($params['outline-s']) && $params['outline-s']) {
8821			$this->SetLineWidth($params['outline-WIDTH']);
8822			$this->SetDColor($params['outline-COLOR']);
8823			$tr = ('2 Tr');
8824			if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
8825				$this->writer->write($tr);
8826			}
8827			$this->pageoutput[$this->page]['TextRendering'] = $tr;
8828		} else { // Now resets all values
8829			$this->SetLineWidth(0.2);
8830			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
8831			$this->_SetTextRendering(0);
8832			$tr = ('0 Tr');
8833			if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
8834				$this->writer->write($tr);
8835			}
8836			$this->pageoutput[$this->page]['TextRendering'] = $tr;
8837		}
8838	}
8839
8840	function Image($file, $x, $y, $w = 0, $h = 0, $type = '', $link = '', $paint = true, $constrain = true, $watermark = false, $shownoimg = true, $allowvector = true)
8841	{
8842		$orig_srcpath = $file;
8843		$this->GetFullPath($file);
8844
8845		$info = $this->imageProcessor->getImage($file, true, $allowvector, $orig_srcpath);
8846		if (!$info && $paint) {
8847			$info = $this->imageProcessor->getImage($this->noImageFile);
8848			if ($info) {
8849				$file = $this->noImageFile;
8850				$w = ($info['w'] * (25.4 / $this->img_dpi));  // 14 x 16px
8851				$h = ($info['h'] * (25.4 / $this->img_dpi));  // 14 x 16px
8852			}
8853		}
8854		if (!$info) {
8855			return false;
8856		}
8857		// Automatic width and height calculation if needed
8858		if ($w == 0 and $h == 0) {
8859			/* -- IMAGES-WMF -- */
8860			if ($info['type'] == 'wmf') {
8861				// WMF units are twips (1/20pt)
8862				// divide by 20 to get points
8863				// divide by k to get user units
8864				$w = abs($info['w']) / (20 * Mpdf::SCALE);
8865				$h = abs($info['h']) / (20 * Mpdf::SCALE);
8866			} else { 			/* -- END IMAGES-WMF -- */
8867				if ($info['type'] == 'svg') {
8868					// returned SVG units are pts
8869					// divide by k to get user units (mm)
8870					$w = abs($info['w']) / Mpdf::SCALE;
8871					$h = abs($info['h']) / Mpdf::SCALE;
8872				} else {
8873					// Put image at default image dpi
8874					$w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi);
8875					$h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi);
8876				}
8877			}
8878		}
8879		if ($w == 0) {
8880			$w = abs($h * $info['w'] / $info['h']);
8881		}
8882		if ($h == 0) {
8883			$h = abs($w * $info['h'] / $info['w']);
8884		}
8885
8886		/* -- WATERMARK -- */
8887		if ($watermark) {
8888			$maxw = $this->w;
8889			$maxh = $this->h;
8890			// Size = D PF or array
8891			if (is_array($this->watermark_size)) {
8892				$w = $this->watermark_size[0];
8893				$h = $this->watermark_size[1];
8894			} elseif (!is_string($this->watermark_size)) {
8895				$maxw -= $this->watermark_size * 2;
8896				$maxh -= $this->watermark_size * 2;
8897				$w = $maxw;
8898				$h = abs($w * $info['h'] / $info['w']);
8899				if ($h > $maxh) {
8900					$h = $maxh;
8901					$w = abs($h * $info['w'] / $info['h']);
8902				}
8903			} elseif ($this->watermark_size == 'F') {
8904				if ($this->ColActive) {
8905					$maxw = $this->w - ($this->DeflMargin + $this->DefrMargin);
8906				} else {
8907					$maxw = $this->pgwidth;
8908				}
8909				$maxh = $this->h - ($this->tMargin + $this->bMargin);
8910				$w = $maxw;
8911				$h = abs($w * $info['h'] / $info['w']);
8912				if ($h > $maxh) {
8913					$h = $maxh;
8914					$w = abs($h * $info['w'] / $info['h']);
8915				}
8916			} elseif ($this->watermark_size == 'P') { // Default P
8917				$w = $maxw;
8918				$h = abs($w * $info['h'] / $info['w']);
8919				if ($h > $maxh) {
8920					$h = $maxh;
8921					$w = abs($h * $info['w'] / $info['h']);
8922				}
8923			}
8924			// Automatically resize to maximum dimensions of page if too large
8925			if ($w > $maxw) {
8926				$w = $maxw;
8927				$h = abs($w * $info['h'] / $info['w']);
8928			}
8929			if ($h > $maxh) {
8930				$h = $maxh;
8931				$w = abs($h * $info['w'] / $info['h']);
8932			}
8933			// Position
8934			if (is_array($this->watermark_pos)) {
8935				$x = $this->watermark_pos[0];
8936				$y = $this->watermark_pos[1];
8937			} elseif ($this->watermark_pos == 'F') { // centred on printable area
8938				if ($this->ColActive) { // *COLUMNS*
8939					if (($this->mirrorMargins) && (($this->page) % 2 == 0)) {
8940						$xadj = $this->DeflMargin - $this->DefrMargin;
8941					} // *COLUMNS*
8942					else {
8943						$xadj = 0;
8944					} // *COLUMNS*
8945					$x = ($this->DeflMargin - $xadj + ($this->w - ($this->DeflMargin + $this->DefrMargin)) / 2) - ($w / 2); // *COLUMNS*
8946				} // *COLUMNS*
8947				else {  // *COLUMNS*
8948					$x = ($this->lMargin + ($this->pgwidth) / 2) - ($w / 2);
8949				} // *COLUMNS*
8950				$y = ($this->tMargin + ($this->h - ($this->tMargin + $this->bMargin)) / 2) - ($h / 2);
8951			} else { // default P - centred on whole page
8952				$x = ($this->w / 2) - ($w / 2);
8953				$y = ($this->h / 2) - ($h / 2);
8954			}
8955			/* -- IMAGES-WMF -- */
8956			if ($info['type'] == 'wmf') {
8957				$sx = $w * Mpdf::SCALE / $info['w'];
8958				$sy = -$h * Mpdf::SCALE / $info['h'];
8959				$outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']);
8960			} else { 			/* -- END IMAGES-WMF -- */
8961				if ($info['type'] == 'svg') {
8962					$sx = $w * Mpdf::SCALE / $info['w'];
8963					$sy = -$h * Mpdf::SCALE / $info['h'];
8964					$outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']);
8965				} else {
8966					$outstring = sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $w * Mpdf::SCALE, $h * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $h)) * Mpdf::SCALE, $info['i']);
8967				}
8968			}
8969
8970			if ($this->watermarkImgBehind) {
8971				$outstring = $this->watermarkImgAlpha . "\n" . $outstring . "\n" . $this->SetAlpha(1, 'Normal', true) . "\n";
8972				$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $outstring . "\n" . '\\1', $this->pages[$this->page]);
8973			} else {
8974				$this->writer->write($outstring);
8975			}
8976
8977			return 0;
8978		} // end of IF watermark
8979		/* -- END WATERMARK -- */
8980
8981		if ($constrain) {
8982			// Automatically resize to maximum dimensions of page if too large
8983			if (isset($this->blk[$this->blklvl]['inner_width']) && $this->blk[$this->blklvl]['inner_width']) {
8984				$maxw = $this->blk[$this->blklvl]['inner_width'];
8985			} else {
8986				$maxw = $this->pgwidth;
8987			}
8988			if ($w > $maxw) {
8989				$w = $maxw;
8990				$h = abs($w * $info['h'] / $info['w']);
8991			}
8992			if ($h > $this->h - ($this->tMargin + $this->bMargin + 1)) {  // see below - +10 to avoid drawing too close to border of page
8993				$h = $this->h - ($this->tMargin + $this->bMargin + 1);
8994				if ($this->fullImageHeight) {
8995					$h = $this->fullImageHeight;
8996				}
8997				$w = abs($h * $info['w'] / $info['h']);
8998			}
8999
9000
9001			// Avoid drawing out of the paper(exceeding width limits).
9002			// if ( ($x + $w) > $this->fw ) {
9003			if (($x + $w) > $this->w) {
9004				$x = $this->lMargin;
9005				$y += 5;
9006			}
9007
9008			$changedpage = false;
9009			$oldcolumn = $this->CurrCol;
9010			// Avoid drawing out of the page.
9011			if ($y + $h > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {
9012				$this->AddPage($this->CurOrientation);
9013				// Added to correct for OddEven Margins
9014				$x = $x + $this->MarginCorrection;
9015				$y = $this->tMargin; // mPDF 5.7.3
9016				$changedpage = true;
9017			}
9018			/* -- COLUMNS -- */
9019			// COLS
9020			// COLUMN CHANGE
9021			if ($this->CurrCol != $oldcolumn) {
9022				$y = $this->y0;
9023				$x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
9024				$this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
9025			}
9026			/* -- END COLUMNS -- */
9027		} // end of IF constrain
9028
9029		/* -- IMAGES-WMF -- */
9030		if ($info['type'] == 'wmf') {
9031			$sx = $w * Mpdf::SCALE / $info['w'];
9032			$sy = -$h * Mpdf::SCALE / $info['h'];
9033			$outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']);
9034		} else { 		/* -- END IMAGES-WMF -- */
9035			if ($info['type'] == 'svg') {
9036				$sx = $w * Mpdf::SCALE / $info['w'];
9037				$sy = -$h * Mpdf::SCALE / $info['h'];
9038				$outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']);
9039			} else {
9040				$outstring = sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $w * Mpdf::SCALE, $h * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $h)) * Mpdf::SCALE, $info['i']);
9041			}
9042		}
9043
9044		if ($paint) {
9045			$this->writer->write($outstring);
9046			if ($link) {
9047				$this->Link($x, $y, $w, $h, $link);
9048			}
9049
9050			// Avoid writing text on top of the image. // THIS WAS OUTSIDE THE if ($paint) bit!!!!!!!!!!!!!!!!
9051			$this->y = $y + $h;
9052		}
9053
9054		// Return width-height array
9055		$sizesarray['WIDTH'] = $w;
9056		$sizesarray['HEIGHT'] = $h;
9057		$sizesarray['X'] = $x; // Position before painting image
9058		$sizesarray['Y'] = $y; // Position before painting image
9059		$sizesarray['OUTPUT'] = $outstring;
9060
9061		$sizesarray['IMAGE_ID'] = $info['i'];
9062		$sizesarray['itype'] = $info['type'];
9063		$sizesarray['set-dpi'] = (isset($info['set-dpi']) ? $info['set-dpi'] : 0);
9064		return $sizesarray;
9065	}
9066
9067	// =============================================================
9068	// =============================================================
9069	// =============================================================
9070	// =============================================================
9071	// =============================================================
9072	/* -- HTML-CSS -- */
9073
9074	function _getObjAttr($t)
9075	{
9076		$c = explode("\xbb\xa4\xac", $t, 2);
9077		$c = explode(",", $c[1], 2);
9078		foreach ($c as $v) {
9079			$v = explode("=", $v, 2);
9080			$sp[$v[0]] = $v[1];
9081		}
9082		return (unserialize($sp['objattr']));
9083	}
9084
9085	function inlineObject($type, $x, $y, $objattr, $Lmargin, $widthUsed, $maxWidth, $lineHeight, $paint = false, $is_table = false)
9086	{
9087		if ($is_table) {
9088			$k = $this->shrin_k;
9089		} else {
9090			$k = 1;
9091		}
9092
9093		// NB $x is only used when paint=true
9094		// Lmargin not used
9095		$w = 0;
9096		if (isset($objattr['width'])) {
9097			$w = $objattr['width'] / $k;
9098		}
9099		$h = 0;
9100		if (isset($objattr['height'])) {
9101			$h = abs($objattr['height'] / $k);
9102		}
9103		$widthLeft = $maxWidth - $widthUsed;
9104		$maxHeight = $this->h - ($this->tMargin + $this->bMargin + 10);
9105		if ($this->fullImageHeight) {
9106			$maxHeight = $this->fullImageHeight;
9107		}
9108		// For Images
9109		if (isset($objattr['border_left'])) {
9110			$extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']) / $k;
9111			$extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']) / $k;
9112
9113			if ($type == 'image' || $type == 'barcode' || $type == 'textcircle') {
9114				$extraWidth += ($objattr['padding_left'] + $objattr['padding_right']) / $k;
9115				$extraHeight += ($objattr['padding_top'] + $objattr['padding_bottom']) / $k;
9116			}
9117		}
9118
9119		if (!isset($objattr['vertical-align'])) {
9120			if ($objattr['type'] == 'select') {
9121				$objattr['vertical-align'] = 'M';
9122			} else {
9123				$objattr['vertical-align'] = 'BS';
9124			}
9125		} // mPDF 6
9126
9127		if ($type == 'image' || (isset($objattr['subtype']) && $objattr['subtype'] == 'IMAGE')) {
9128			if (isset($objattr['itype']) && ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg')) {
9129				$file = $objattr['file'];
9130				$info = $this->formobjects[$file];
9131			} elseif (isset($objattr['file'])) {
9132				$file = $objattr['file'];
9133				$info = $this->images[$file];
9134			}
9135		}
9136		if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {
9137			$w = 0.00001;
9138			$h = 0.00001;
9139		}
9140
9141		// TEST whether need to skipline
9142		if (!$paint) {
9143			if ($type == 'hr') { // always force new line
9144				if (($y + $h + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {
9145					return [-2, $w, $h];
9146				} // New page + new line
9147				else {
9148					return [1, $w, $h];
9149				} // new line
9150			} else {
9151				// LIST MARKERS	// mPDF 6  Lists
9152				$displayheight = $h;
9153				$displaywidth = $w;
9154				if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker']) {
9155					$displayheight = 0;
9156					if ($objattr['listmarkerposition'] == 'outside') {
9157						$displaywidth = 0;
9158					}
9159				}
9160
9161				if ($widthUsed > 0 && $displaywidth > $widthLeft && (!$is_table || $type != 'image')) {  // New line needed
9162					// mPDF 6  Lists
9163					if (($y + $displayheight + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter) {
9164						return [-2, $w, $h];
9165					} // New page + new line
9166					return [1, $w, $h]; // new line
9167				} elseif ($widthUsed > 0 && $displaywidth > $widthLeft && $is_table) {  // New line needed in TABLE
9168					return [1, $w, $h]; // new line
9169				} // Will fit on line but NEW PAGE REQUIRED
9170				elseif (($y + $displayheight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {
9171					return [-1, $w, $h];
9172				} // mPDF 6  Lists
9173				else {
9174					return [0, $w, $h];
9175				}
9176			}
9177		}
9178
9179		if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {
9180			$w = 0.00001;
9181			$h = 0.00001;
9182			$objattr['BORDER-WIDTH'] = 0;
9183			$objattr['BORDER-HEIGHT'] = 0;
9184			$objattr['BORDER-X'] = $x;
9185			$objattr['BORDER-Y'] = $y;
9186			$objattr['INNER-WIDTH'] = 0;
9187			$objattr['INNER-HEIGHT'] = 0;
9188			$objattr['INNER-X'] = $x;
9189			$objattr['INNER-Y'] = $y;
9190		}
9191
9192		if ($type == 'image') {
9193			// Automatically resize to width remaining
9194			if ($w > ($widthLeft + 0.0001) && !$is_table) { // mPDF 5.7.4  0.0001 to allow for rounding errors when w==maxWidth
9195				$w = $widthLeft;
9196				$h = abs($w * $info['h'] / $info['w']);
9197			}
9198			$img_w = $w - $extraWidth;
9199			$img_h = $h - $extraHeight;
9200
9201			$objattr['BORDER-WIDTH'] = $img_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
9202			$objattr['BORDER-HEIGHT'] = $img_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
9203			$objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
9204			$objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
9205			$objattr['INNER-WIDTH'] = $img_w;
9206			$objattr['INNER-HEIGHT'] = $img_h;
9207			$objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
9208			$objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
9209			$objattr['ID'] = $info['i'];
9210		}
9211
9212		if ($type == 'input' && $objattr['subtype'] == 'IMAGE') {
9213			$img_w = $w - $extraWidth;
9214			$img_h = $h - $extraHeight;
9215			$objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
9216			$objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
9217			$objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
9218			$objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
9219			$objattr['INNER-WIDTH'] = $img_w;
9220			$objattr['INNER-HEIGHT'] = $img_h;
9221			$objattr['INNER-X'] = $x + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
9222			$objattr['INNER-Y'] = $y + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
9223			$objattr['ID'] = $info['i'];
9224		}
9225
9226		if ($type == 'barcode' || $type == 'textcircle') {
9227			$b_w = $w - $extraWidth;
9228			$b_h = $h - $extraHeight;
9229			$objattr['BORDER-WIDTH'] = $b_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
9230			$objattr['BORDER-HEIGHT'] = $b_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
9231			$objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
9232			$objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
9233			$objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
9234			$objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
9235			$objattr['INNER-WIDTH'] = $b_w;
9236			$objattr['INNER-HEIGHT'] = $b_h;
9237		}
9238
9239
9240		if ($type == 'textarea') {
9241			// Automatically resize to width remaining
9242			if ($w > $widthLeft && !$is_table) {
9243				$w = $widthLeft;
9244			}
9245			// This used to resize height to maximum remaining on page ? why. Causes problems when in table and causing a new column
9246			// if (($y + $h > $this->PageBreakTrigger) && !$this->InFooter) {
9247			// 	$h=$this->h - $y - $this->bMargin;
9248			// }
9249		}
9250
9251		if ($type == 'hr') {
9252			if ($is_table) {
9253				$objattr['INNER-WIDTH'] = $maxWidth * $objattr['W-PERCENT'] / 100;
9254				$objattr['width'] = $objattr['INNER-WIDTH'];
9255				$w = $maxWidth;
9256			} else {
9257				if ($w > $maxWidth) {
9258					$w = $maxWidth;
9259				}
9260				$objattr['INNER-WIDTH'] = $w;
9261				$w = $maxWidth;
9262			}
9263		}
9264
9265
9266
9267		if (($type == 'select') || ($type == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD'))) {
9268			// Automatically resize to width remaining
9269			if ($w > $widthLeft && !$is_table) {
9270				$w = $widthLeft;
9271			}
9272		}
9273
9274		if ($type == 'textarea' || $type == 'select' || $type == 'input') {
9275			if (isset($objattr['fontsize'])) {
9276				$objattr['fontsize'] /= $k;
9277			}
9278			if (isset($objattr['linewidth'])) {
9279				$objattr['linewidth'] /= $k;
9280			}
9281		}
9282
9283		if (!isset($objattr['BORDER-Y'])) {
9284			$objattr['BORDER-Y'] = 0;
9285		}
9286		if (!isset($objattr['BORDER-X'])) {
9287			$objattr['BORDER-X'] = 0;
9288		}
9289		if (!isset($objattr['INNER-Y'])) {
9290			$objattr['INNER-Y'] = 0;
9291		}
9292		if (!isset($objattr['INNER-X'])) {
9293			$objattr['INNER-X'] = 0;
9294		}
9295
9296		// Return width-height array
9297		$objattr['OUTER-WIDTH'] = $w;
9298		$objattr['OUTER-HEIGHT'] = $h;
9299		$objattr['OUTER-X'] = $x;
9300		$objattr['OUTER-Y'] = $y;
9301		return $objattr;
9302	}
9303
9304	/* -- END HTML-CSS -- */
9305
9306	// =============================================================
9307	// =============================================================
9308	// =============================================================
9309	// =============================================================
9310	// =============================================================
9311
9312	function SetLineJoin($mode = 0)
9313	{
9314		$s = sprintf('%d j', $mode);
9315		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineJoin']) && $this->pageoutput[$this->page]['LineJoin'] != $s) || !isset($this->pageoutput[$this->page]['LineJoin']))) {
9316			$this->writer->write($s);
9317		}
9318		$this->pageoutput[$this->page]['LineJoin'] = $s;
9319	}
9320
9321	function SetLineCap($mode = 2)
9322	{
9323		$s = sprintf('%d J', $mode);
9324		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineCap']) && $this->pageoutput[$this->page]['LineCap'] != $s) || !isset($this->pageoutput[$this->page]['LineCap']))) {
9325			$this->writer->write($s);
9326		}
9327		$this->pageoutput[$this->page]['LineCap'] = $s;
9328	}
9329
9330	function SetDash($black = false, $white = false)
9331	{
9332		if ($black and $white) {
9333			$s = sprintf('[%.3F %.3F] 0 d', $black * Mpdf::SCALE, $white * Mpdf::SCALE);
9334		} else {
9335			$s = '[] 0 d';
9336		}
9337
9338		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Dash']) && $this->pageoutput[$this->page]['Dash'] != $s) || !isset($this->pageoutput[$this->page]['Dash']))) {
9339			$this->writer->write($s);
9340		}
9341
9342		$this->pageoutput[$this->page]['Dash'] = $s;
9343	}
9344
9345	function SetDisplayPreferences($preferences)
9346	{
9347		// String containing any or none of /HideMenubar/HideToolbar/HideWindowUI/DisplayDocTitle/CenterWindow/FitWindow
9348
9349		$this->DisplayPreferences .= $preferences;
9350	}
9351
9352	function Ln($h = '', $collapsible = 0)
9353	{
9354		// Added collapsible to allow collapsible top-margin on new page
9355		// Line feed; default value is last cell height
9356
9357		$margin = isset($this->blk[$this->blklvl]['outer_left_margin']) ? $this->blk[$this->blklvl]['outer_left_margin'] : 0;
9358
9359		$this->x = $this->lMargin + $margin;
9360
9361		if ($collapsible && ($this->y == $this->tMargin) && (!$this->ColActive)) {
9362			$h = 0;
9363		}
9364
9365		if (is_string($h)) {
9366			$this->y += $this->lasth;
9367		} else {
9368			$this->y += $h;
9369		}
9370	}
9371
9372	/* -- HTML-CSS -- */
9373
9374	function DivLn($h, $level = -3, $move_y = true, $collapsible = false, $state = 0)
9375	{
9376		// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
9377		// Used in Columns and keep-with-table i.e. "kwt"
9378		// writes background block by block so it can be repositioned
9379		// and also used in writingFlowingBlock at top and bottom of blocks to move y (not to draw/paint anything)
9380		// adds lines (y) where DIV bgcolors are filled in
9381		// this->x is returned as it was
9382		// allows .00001 as nominal height used for bookmarks/annotations etc.
9383		if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)) && (!$this->ColActive)) {
9384			return;
9385		}
9386
9387		// mPDF 6 Columns
9388		//   if ($collapsible && (sprintf("%0.4f", $this->y)==sprintf("%0.4f", $this->y0)) && ($this->ColActive) && $this->CurrCol == 0) { return; }	// *COLUMNS*
9389		if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->y0)) && ($this->ColActive)) {
9390			return;
9391		} // *COLUMNS*
9392		// Still use this method if columns or keep-with-table, as it allows repositioning later
9393		// otherwise, now uses PaintDivBB()
9394		if (!$this->ColActive && !$this->kwt) {
9395			if ($move_y && !$this->ColActive) {
9396				$this->y += $h;
9397			}
9398			return;
9399		}
9400
9401		if ($level == -3) {
9402			$level = $this->blklvl;
9403		}
9404		$firstblockfill = $this->GetFirstBlockFill();
9405		if ($firstblockfill && $this->blklvl > 0 && $this->blklvl >= $firstblockfill) {
9406			$last_x = 0;
9407			$last_w = 0;
9408			$last_fc = $this->FillColor;
9409			$bak_x = $this->x;
9410			$bak_h = $this->divheight;
9411			$this->divheight = 0; // Temporarily turn off divheight - as Cell() uses it to check for PageBreak
9412			for ($blvl = $firstblockfill; $blvl <= $level; $blvl++) {
9413				$this->x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];
9414				// mPDF 6
9415				if ($this->blk[$blvl]['bgcolor']) {
9416					$this->SetFColor($this->blk[$blvl]['bgcolorarray']);
9417				}
9418				if ($last_x != ($this->lMargin + $this->blk[$blvl]['outer_left_margin']) || ($last_w != $this->blk[$blvl]['width']) || $last_fc != $this->FillColor || (isset($this->blk[$blvl]['border_top']['s']) && $this->blk[$blvl]['border_top']['s']) || (isset($this->blk[$blvl]['border_bottom']['s']) && $this->blk[$blvl]['border_bottom']['s']) || (isset($this->blk[$blvl]['border_left']['s']) && $this->blk[$blvl]['border_left']['s']) || (isset($this->blk[$blvl]['border_right']['s']) && $this->blk[$blvl]['border_right']['s'])) {
9419					$x = $this->x;
9420					$this->Cell(($this->blk[$blvl]['width']), $h, '', '', 0, '', 1);
9421					$this->x = $x;
9422					if (!$this->keep_block_together && !$this->writingHTMLheader && !$this->writingHTMLfooter) {
9423						// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
9424						if ($blvl == $this->blklvl) {
9425							$this->PaintDivLnBorder($state, $blvl, $h);
9426						} else {
9427							$this->PaintDivLnBorder(0, $blvl, $h);
9428						}
9429					}
9430				}
9431				$last_x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];
9432				$last_w = $this->blk[$blvl]['width'];
9433				$last_fc = $this->FillColor;
9434			}
9435			// Reset current block fill
9436			if (isset($this->blk[$this->blklvl]['bgcolorarray'])) {
9437				$bcor = $this->blk[$this->blklvl]['bgcolorarray'];
9438				$this->SetFColor($bcor);
9439			}
9440			$this->x = $bak_x;
9441			$this->divheight = $bak_h;
9442		}
9443		if ($move_y) {
9444			$this->y += $h;
9445		}
9446	}
9447
9448	/* -- END HTML-CSS -- */
9449
9450	function SetX($x)
9451	{
9452		// Set x position
9453		if ($x >= 0) {
9454			$this->x = $x;
9455		} else {
9456			$this->x = $this->w + $x;
9457		}
9458	}
9459
9460	function SetY($y)
9461	{
9462		// Set y position and reset x
9463		$this->x = $this->lMargin;
9464		if ($y >= 0) {
9465			$this->y = $y;
9466		} else {
9467			$this->y = $this->h + $y;
9468		}
9469	}
9470
9471	function SetXY($x, $y)
9472	{
9473		// Set x and y positions
9474		$this->SetY($y);
9475		$this->SetX($x);
9476	}
9477
9478	function Output($name = '', $dest = '')
9479	{
9480		$this->logger->debug(sprintf('PDF generated in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]);
9481
9482		// Finish document if necessary
9483		if ($this->state < 3) {
9484			$this->Close();
9485		}
9486
9487		if ($this->debug && error_get_last()) {
9488			$e = error_get_last();
9489			if (($e['type'] < 2048 && $e['type'] != 8) || (intval($e['type']) & intval(ini_get("error_reporting")))) {
9490				throw new \Mpdf\MpdfException(
9491					sprintf('Error detected. PDF file generation aborted: %s', $e['message']),
9492					$e['type'],
9493					1,
9494					$e['file'],
9495					$e['line']
9496				);
9497			}
9498		}
9499
9500		if (($this->PDFA || $this->PDFX) && $this->encrypted) {
9501			throw new \Mpdf\MpdfException('PDF/A1-b or PDF/X1-a does not permit encryption of documents.');
9502		}
9503
9504		if (count($this->PDFAXwarnings) && (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto))) {
9505			if ($this->PDFA) {
9506				$standard = 'PDFA/1-b';
9507				$option = '$mpdf->PDFAauto';
9508			} else {
9509				$standard = 'PDFX/1-a ';
9510				$option = '$mpdf->PDFXauto';
9511			}
9512
9513			$this->logger->warning(sprintf('PDF could not be generated as it stands as a %s compliant file.', $standard), ['context' => LogContext::PDFA_PDFX]);
9514			$this->logger->warning(sprintf('These issues can be automatically fixed by mPDF using %s = true;', $option), ['context' => LogContext::PDFA_PDFX]);
9515			$this->logger->warning(sprintf('Action that mPDF will take to automatically force %s compliance are shown further in the log.', $standard), ['context' => LogContext::PDFA_PDFX]);
9516
9517			$this->PDFAXwarnings = array_unique($this->PDFAXwarnings);
9518			foreach ($this->PDFAXwarnings as $w) {
9519				$this->logger->warning($w, ['context' => LogContext::PDFA_PDFX]);
9520			}
9521
9522			throw new \Mpdf\MpdfException('PDFA/PDFX warnings generated. See log for further details');
9523		}
9524
9525		$this->logger->debug(sprintf('Compiled in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]);
9526		$this->logger->debug(sprintf('Peak Memory usage %s MB', number_format(memory_get_peak_usage(true) / (1024 * 1024), 2)), ['context' => LogContext::STATISTICS]);
9527		$this->logger->debug(sprintf('PDF file size %s kB', number_format(strlen($this->buffer) / 1024)), ['context' => LogContext::STATISTICS]);
9528		$this->logger->debug(sprintf('%d fonts used', count($this->fonts)), ['context' => LogContext::STATISTICS]);
9529
9530		if (is_bool($dest)) {
9531			$dest = $dest ? Destination::DOWNLOAD : Destination::FILE;
9532		}
9533
9534		$dest = strtoupper($dest);
9535		if (empty($dest)) {
9536			if (empty($name)) {
9537				$name = 'mpdf.pdf';
9538				$dest = Destination::INLINE;
9539			} else {
9540				$dest = Destination::FILE;
9541			}
9542		}
9543
9544		switch ($dest) {
9545
9546			case Destination::INLINE:
9547
9548				if (headers_sent($filename, $line)) {
9549					throw new \Mpdf\MpdfException(
9550						sprintf('Data has already been sent to output (%s at line %s), unable to output PDF file', $filename, $line)
9551					);
9552				}
9553
9554				if ($this->debug && !$this->allow_output_buffering && ob_get_contents()) {
9555					throw new \Mpdf\MpdfException('Output has already been sent from the script - PDF file generation aborted.');
9556				}
9557
9558				// We send to a browser
9559				if (PHP_SAPI !== 'cli') {
9560					header('Content-Type: application/pdf');
9561
9562					if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
9563						// don't use length if server using compression
9564						header('Content-Length: ' . strlen($this->buffer));
9565					}
9566
9567					header('Content-disposition: inline; filename="' . $name . '"');
9568					header('Cache-Control: public, must-revalidate, max-age=0');
9569					header('Pragma: public');
9570					header('X-Generator: mPDF' . ($this->exposeVersion ? (' ' . static::VERSION) : ''));
9571					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
9572					header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
9573				}
9574
9575				echo $this->buffer;
9576
9577				break;
9578
9579			case Destination::DOWNLOAD:
9580
9581				if (headers_sent()) {
9582					throw new \Mpdf\MpdfException('Data has already been sent to output, unable to output PDF file');
9583				}
9584
9585				header('Content-Description: File Transfer');
9586				header('Content-Transfer-Encoding: binary');
9587				header('Cache-Control: public, must-revalidate, max-age=0');
9588				header('Pragma: public');
9589				header('X-Generator: mPDF' . ($this->exposeVersion ? (' ' . static::VERSION) : ''));
9590				header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
9591				header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
9592				header('Content-Type: application/pdf');
9593
9594				if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
9595					// don't use length if server using compression
9596					header('Content-Length: ' . strlen($this->buffer));
9597				}
9598
9599				header('Content-Disposition: attachment; filename="' . $name . '"');
9600
9601				echo $this->buffer;
9602
9603				break;
9604
9605			case Destination::FILE:
9606				$f = fopen($name, 'wb');
9607
9608				if (!$f) {
9609					throw new \Mpdf\MpdfException(sprintf('Unable to create output file %s', $name));
9610				}
9611
9612				fwrite($f, $this->buffer, strlen($this->buffer));
9613				fclose($f);
9614
9615				break;
9616
9617			case Destination::STRING_RETURN:
9618				$this->cache->clearOld();
9619				return $this->buffer;
9620
9621			default:
9622				throw new \Mpdf\MpdfException(sprintf('Incorrect output destination %s', $dest));
9623		}
9624
9625		$this->cache->clearOld();
9626	}
9627
9628	// *****************************************************************************
9629	//                                                                             *
9630	//                             Protected methods                               *
9631	//                                                                             *
9632	// *****************************************************************************
9633	function _dochecks()
9634	{
9635		// Check for locale-related bug
9636		if (1.1 == 1) {
9637			throw new \Mpdf\MpdfException('Do not alter the locale before including mPDF');
9638		}
9639
9640		// Check for decimal separator
9641		if (sprintf('%.1f', 1.0) != '1.0') {
9642			setlocale(LC_NUMERIC, 'C');
9643		}
9644
9645		if (ini_get('mbstring.func_overload')) {
9646			throw new \Mpdf\MpdfException('Mpdf cannot function properly with mbstring.func_overload enabled');
9647		}
9648
9649		if (!function_exists('mb_substr')) {
9650			throw new \Mpdf\MpdfException('mbstring extension must be loaded in order to run mPDF');
9651		}
9652
9653		if (!function_exists('mb_regex_encoding')) {
9654			if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
9655				$mamp = ' If using MAMP, there is a bug in its PHP build causing this.';
9656			}
9657
9658			throw new \Mpdf\MpdfException('mbstring extension with mbregex support must be loaded in order to run mPDF.' . $mamp);
9659		}
9660	}
9661
9662	function _puthtmlheaders()
9663	{
9664		$this->state = 2;
9665		$nb = $this->page;
9666		for ($n = 1; $n <= $nb; $n++) {
9667			if ($this->mirrorMargins && $n % 2 == 0) {
9668				$OE = 'E';
9669			} // EVEN
9670			else {
9671				$OE = 'O';
9672			}
9673			$this->page = $n;
9674			$pn = $this->docPageNum($n);
9675			if ($pn) {
9676				$pnstr = $this->pagenumPrefix . $pn . $this->pagenumSuffix;
9677			} else {
9678				$pnstr = '';
9679			}
9680
9681			$pnt = $this->docPageNumTotal($n);
9682
9683			if ($pnt) {
9684				$pntstr = $this->nbpgPrefix . $pnt . $this->nbpgSuffix;
9685			} else {
9686				$pntstr = '';
9687			}
9688
9689			if (isset($this->saveHTMLHeader[$n][$OE])) {
9690				$html = isset($this->saveHTMLHeader[$n][$OE]['html']) ? $this->saveHTMLHeader[$n][$OE]['html'] : '';
9691				$this->lMargin = $this->saveHTMLHeader[$n][$OE]['ml'];
9692				$this->rMargin = $this->saveHTMLHeader[$n][$OE]['mr'];
9693				$this->tMargin = $this->saveHTMLHeader[$n][$OE]['mh'];
9694				$this->bMargin = $this->saveHTMLHeader[$n][$OE]['mf'];
9695				$this->margin_header = $this->saveHTMLHeader[$n][$OE]['mh'];
9696				$this->margin_footer = $this->saveHTMLHeader[$n][$OE]['mf'];
9697				$this->w = $this->saveHTMLHeader[$n][$OE]['pw'];
9698				$this->h = $this->saveHTMLHeader[$n][$OE]['ph'];
9699				$rotate = (isset($this->saveHTMLHeader[$n][$OE]['rotate']) ? $this->saveHTMLHeader[$n][$OE]['rotate'] : null);
9700				$this->Reset();
9701				$this->pageoutput[$n] = [];
9702				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
9703				$this->x = $this->lMargin;
9704				$this->y = $this->margin_header;
9705
9706				// Replace of page number aliases and date format
9707				$html = $this->aliasReplace($html, $pnstr, $pntstr, $nb);
9708
9709				$this->HTMLheaderPageLinks = [];
9710				$this->HTMLheaderPageAnnots = [];
9711				$this->HTMLheaderPageForms = [];
9712				$this->pageBackgrounds = [];
9713
9714				$this->writingHTMLheader = true;
9715				$this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
9716				$this->writingHTMLheader = false;
9717				$this->Reset();
9718				$this->pageoutput[$n] = [];
9719
9720				$s = $this->PrintPageBackgrounds();
9721				$this->headerbuffer = $s . $this->headerbuffer;
9722				$os = '';
9723				if ($rotate) {
9724					$os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE));
9725					// To rotate the other way i.e. Header to left of page:
9726					// $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE));
9727				}
9728				$os .= $this->headerbuffer;
9729				if ($rotate) {
9730					$os .= ' Q' . "\n";
9731				}
9732
9733				// Writes over the page background but behind any other output on page
9734				$os = preg_replace(['/\\\\/', '/\$/'], ['\\\\\\\\', '\\\\$'], $os);
9735
9736				$this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]);
9737
9738				$lks = $this->HTMLheaderPageLinks;
9739				foreach ($lks as $lk) {
9740					if ($rotate) {
9741						$lw = $lk[2];
9742						$lh = $lk[3];
9743						$lk[2] = $lh;
9744						$lk[3] = $lw; // swap width and height
9745						$ax = $lk[0] / Mpdf::SCALE;
9746						$ay = $lk[1] / Mpdf::SCALE;
9747						$bx = $ay - ($lh / Mpdf::SCALE);
9748						$by = $this->w - $ax;
9749						$lk[0] = $bx * Mpdf::SCALE;
9750						$lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw;
9751					}
9752					$this->PageLinks[$n][] = $lk;
9753				}
9754				/* -- FORMS -- */
9755				foreach ($this->HTMLheaderPageForms as $f) {
9756					$this->form->forms[$f['n']] = $f;
9757				}
9758				/* -- END FORMS -- */
9759			}
9760
9761			if (isset($this->saveHTMLFooter[$n][$OE])) {
9762
9763				$html = $this->saveHTMLFooter[$this->page][$OE]['html'];
9764
9765				$this->lMargin = $this->saveHTMLFooter[$n][$OE]['ml'];
9766				$this->rMargin = $this->saveHTMLFooter[$n][$OE]['mr'];
9767				$this->tMargin = $this->saveHTMLFooter[$n][$OE]['mh'];
9768				$this->bMargin = $this->saveHTMLFooter[$n][$OE]['mf'];
9769				$this->margin_header = $this->saveHTMLFooter[$n][$OE]['mh'];
9770				$this->margin_footer = $this->saveHTMLFooter[$n][$OE]['mf'];
9771				$this->w = $this->saveHTMLFooter[$n][$OE]['pw'];
9772				$this->h = $this->saveHTMLFooter[$n][$OE]['ph'];
9773				$rotate = (isset($this->saveHTMLFooter[$n][$OE]['rotate']) ? $this->saveHTMLFooter[$n][$OE]['rotate'] : null);
9774				$this->Reset();
9775				$this->pageoutput[$n] = [];
9776				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
9777				$this->x = $this->lMargin;
9778				$top_y = $this->y = $this->h - $this->margin_footer;
9779
9780				// if bottom-margin==0, corrects to avoid division by zero
9781				if ($this->y == $this->h) {
9782					$top_y = $this->y = ($this->h + 0.01);
9783				}
9784
9785				// Replace of page number aliases and date format
9786				$html = $this->aliasReplace($html, $pnstr, $pntstr, $nb);
9787
9788				$this->HTMLheaderPageLinks = [];
9789				$this->HTMLheaderPageAnnots = [];
9790				$this->HTMLheaderPageForms = [];
9791				$this->pageBackgrounds = [];
9792
9793				$this->writingHTMLfooter = true;
9794				$this->InFooter = true;
9795				$this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
9796				$this->InFooter = false;
9797				$this->Reset();
9798				$this->pageoutput[$n] = [];
9799
9800				$fheight = $this->y - $top_y;
9801				$adj = -$fheight;
9802
9803				$s = $this->PrintPageBackgrounds(-$adj);
9804				$this->headerbuffer = $s . $this->headerbuffer;
9805				$this->writingHTMLfooter = false; // mPDF 5.7.3  (moved after PrintPageBackgrounds so can adjust position of images in footer)
9806
9807				$os = '';
9808				$os .= $this->StartTransform(true) . "\n";
9809
9810				if ($rotate) {
9811					$os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE));
9812					// To rotate the other way i.e. Header to left of page:
9813					// $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE));
9814				}
9815
9816				$os .= $this->transformTranslate(0, $adj, true) . "\n";
9817				$os .= $this->headerbuffer;
9818
9819				if ($rotate) {
9820					$os .= ' Q' . "\n";
9821				}
9822
9823				$os .= $this->StopTransform(true) . "\n";
9824
9825				// Writes over the page background but behind any other output on page
9826				$os = preg_replace(['/\\\\/', '/\$/'], ['\\\\\\\\', '\\\\$'], $os);
9827
9828				$this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]);
9829
9830				$lks = $this->HTMLheaderPageLinks;
9831
9832				foreach ($lks as $lk) {
9833
9834					$lk[1] -= $adj * Mpdf::SCALE;
9835
9836					if ($rotate) {
9837						$lw = $lk[2];
9838						$lh = $lk[3];
9839						$lk[2] = $lh;
9840						$lk[3] = $lw; // swap width and height
9841
9842						$ax = $lk[0] / Mpdf::SCALE;
9843						$ay = $lk[1] / Mpdf::SCALE;
9844						$bx = $ay - ($lh / Mpdf::SCALE);
9845						$by = $this->w - $ax;
9846						$lk[0] = $bx * Mpdf::SCALE;
9847						$lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw;
9848					}
9849
9850					$this->PageLinks[$n][] = $lk;
9851				}
9852
9853				/* -- FORMS -- */
9854				foreach ($this->HTMLheaderPageForms as $f) {
9855					$f['y'] += $adj;
9856					$this->form->forms[$f['n']] = $f;
9857				}
9858				/* -- END FORMS -- */
9859			}
9860
9861			// Customization for https://github.com/mpdf/mpdf/issues/172
9862			// Replace of page number aliases and date format
9863			$this->pages[$n] = $this->aliasReplace($this->pages[$n], $pnstr, $pntstr, $nb);
9864		}
9865
9866		$this->page = $nb;
9867		$this->state = 1;
9868	}
9869
9870	/* -- ANNOTATIONS -- */
9871	function Annotation($text, $x = 0, $y = 0, $icon = 'Note', $author = '', $subject = '', $opacity = 0, $colarray = false, $popup = '', $file = '')
9872	{
9873		if (is_array($colarray) && count($colarray) == 3) {
9874			$colarray = $this->colorConverter->convert('rgb(' . $colarray[0] . ',' . $colarray[1] . ',' . $colarray[2] . ')', $this->PDFAXwarnings);
9875		}
9876		if ($colarray === false) {
9877			$colarray = $this->colorConverter->convert('yellow', $this->PDFAXwarnings);
9878		}
9879		if ($x == 0) {
9880			$x = $this->x;
9881		}
9882		if ($y == 0) {
9883			$y = $this->y;
9884		}
9885		$page = $this->page;
9886		if ($page < 1) { // Document has not been started - assume it's for first page
9887			$page = 1;
9888			if ($x == 0) {
9889				$x = $this->lMargin;
9890			}
9891			if ($y == 0) {
9892				$y = $this->tMargin;
9893			}
9894		}
9895
9896		if ($this->PDFA || $this->PDFX) {
9897			if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
9898				$this->PDFAXwarnings[] = "Annotation markers cannot be semi-transparent in PDFA1-b or PDFX/1-a, so they may make underlying text unreadable. (Annotation markers moved to right margin)";
9899			}
9900			$x = ($this->w) - $this->rMargin * 0.66;
9901		}
9902		if (!$this->annotMargin) {
9903			$y -= $this->FontSize / 2;
9904		}
9905
9906		if (!$opacity && $this->annotMargin) {
9907			$opacity = 1;
9908		} elseif (!$opacity) {
9909			$opacity = $this->annotOpacity;
9910		}
9911
9912		$an = ['txt' => $text, 'x' => $x, 'y' => $y, 'opt' => ['Icon' => $icon, 'T' => $author, 'Subj' => $subject, 'C' => $colarray, 'CA' => $opacity, 'popup' => $popup, 'file' => $file]];
9913
9914		if ($this->keep_block_together) { // don't write yet
9915			return;
9916		} elseif ($this->table_rotate) {
9917			$this->tbrot_Annots[$this->page][] = $an;
9918			return;
9919		} elseif ($this->kwt) {
9920			$this->kwt_Annots[$this->page][] = $an;
9921			return;
9922		}
9923
9924		if ($this->writingHTMLheader || $this->writingHTMLfooter) {
9925			$this->HTMLheaderPageAnnots[] = $an;
9926			return;
9927		}
9928
9929		// Put an Annotation on the page
9930		$this->PageAnnots[$page][] = $an;
9931
9932		/* -- COLUMNS -- */
9933		// Save cross-reference to Column buffer
9934		$ref = isset($this->PageAnnots[$this->page]) ? (count($this->PageAnnots[$this->page]) - 1) : -1;
9935		$this->columnAnnots[$this->CurrCol][intval($this->x)][intval($this->y)] = $ref;
9936		/* -- END COLUMNS -- */
9937	}
9938
9939	/* -- END ANNOTATIONS -- */
9940
9941	function _enddoc()
9942	{
9943		// @log Writing Headers & Footers
9944
9945		$this->_puthtmlheaders();
9946
9947		// @log Writing Pages
9948
9949		// Remove references to unused fonts (usually default font)
9950		foreach ($this->fonts as $fk => $font) {
9951			if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {
9952				if ($font['sip'] || $font['smp']) {
9953					foreach ($font['subsetfontids'] as $k => $fid) {
9954						foreach ($this->pages as $pn => $page) {
9955							$this->pages[$pn] = preg_replace('/\s\/F' . $fid . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]);
9956						}
9957					}
9958				} else {
9959					foreach ($this->pages as $pn => $page) {
9960						$this->pages[$pn] = preg_replace('/\s\/F' . $font['i'] . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]);
9961					}
9962				}
9963			}
9964		}
9965
9966		if (count($this->layers)) {
9967			foreach ($this->pages as $pn => $page) {
9968				preg_match_all('/\/OCZ-index \/ZI(\d+) BDC(.*?)(EMCZ)-index/is', $this->pages[$pn], $m1);
9969				preg_match_all('/\/OCBZ-index \/ZI(\d+) BDC(.*?)(EMCBZ)-index/is', $this->pages[$pn], $m2);
9970				preg_match_all('/\/OCGZ-index \/ZI(\d+) BDC(.*?)(EMCGZ)-index/is', $this->pages[$pn], $m3);
9971				$m = [];
9972				for ($i = 0; $i < 4; $i++) {
9973					$m[$i] = array_merge($m1[$i], $m2[$i], $m3[$i]);
9974				}
9975				if (count($m[0])) {
9976					$sortarr = [];
9977					for ($i = 0; $i < count($m[0]); $i++) {
9978						$key = $m[1][$i] * 2;
9979						if ($m[3][$i] == 'EMCZ') {
9980							$key +=2; // background first then gradient then normal
9981						} elseif ($m[3][$i] == 'EMCGZ') {
9982							$key +=1;
9983						}
9984						$sortarr[$i] = $key;
9985					}
9986					asort($sortarr);
9987					foreach ($sortarr as $i => $k) {
9988						$this->pages[$pn] = str_replace($m[0][$i], '', $this->pages[$pn]);
9989						$this->pages[$pn] .= "\n" . $m[0][$i] . "\n";
9990					}
9991					$this->pages[$pn] = preg_replace('/\/OC[BG]{0,1}Z-index \/ZI(\d+) BDC/is', '/OC /ZI\\1 BDC ', $this->pages[$pn]);
9992					$this->pages[$pn] = preg_replace('/EMC[BG]{0,1}Z-index/is', 'EMC', $this->pages[$pn]);
9993				}
9994			}
9995		}
9996
9997		$this->pageWriter->writePages();
9998
9999		// @log Writing document resources
10000
10001		$this->resourceWriter->writeResources();
10002
10003		// Info
10004		$this->writer->object();
10005		$this->InfoRoot = $this->n;
10006		$this->writer->write('<<');
10007
10008		// @log Writing document info
10009		$this->metadataWriter->writeInfo();
10010
10011		$this->writer->write('>>');
10012		$this->writer->write('endobj');
10013
10014		// METADATA
10015		if ($this->PDFA || $this->PDFX) {
10016			$this->metadataWriter->writeMetadata();
10017		}
10018
10019		// OUTPUTINTENT
10020		if ($this->PDFA || $this->PDFX || $this->ICCProfile) {
10021			$this->metadataWriter->writeOutputIntent();
10022		}
10023
10024		// Associated files
10025		if ($this->associatedFiles) {
10026			$this->metadataWriter->writeAssociatedFiles();
10027		}
10028
10029		// Catalog
10030		$this->writer->object();
10031		$this->writer->write('<<');
10032
10033		// @log Writing document catalog
10034
10035		$this->metadataWriter->writeCatalog();
10036
10037		$this->writer->write('>>');
10038		$this->writer->write('endobj');
10039
10040		// Cross-ref
10041		$o = strlen($this->buffer);
10042		$this->writer->write('xref');
10043		$this->writer->write('0 ' . ($this->n + 1));
10044		$this->writer->write('0000000000 65535 f ');
10045
10046		for ($i = 1; $i <= $this->n; $i++) {
10047			$this->writer->write(sprintf('%010d 00000 n ', $this->offsets[$i]));
10048		}
10049
10050		// Trailer
10051		$this->writer->write('trailer');
10052		$this->writer->write('<<');
10053
10054		$this->metadataWriter->writeTrailer();
10055
10056		$this->writer->write('>>');
10057		$this->writer->write('startxref');
10058		$this->writer->write($o);
10059
10060		$this->buffer .= '%%EOF';
10061		$this->state = 3;
10062	}
10063
10064	function _beginpage(
10065		$orientation,
10066		$mgl = '',
10067		$mgr = '',
10068		$mgt = '',
10069		$mgb = '',
10070		$mgh = '',
10071		$mgf = '',
10072		$ohname = '',
10073		$ehname = '',
10074		$ofname = '',
10075		$efname = '',
10076		$ohvalue = 0,
10077		$ehvalue = 0,
10078		$ofvalue = 0,
10079		$efvalue = 0,
10080		$pagesel = '',
10081		$newformat = ''
10082	) {
10083		if (!($pagesel && $this->page == 1 && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)))) {
10084			$this->page++;
10085			$this->pages[$this->page] = '';
10086		}
10087		$this->state = 2;
10088		$resetHTMLHeadersrequired = false;
10089
10090		if ($newformat) {
10091			$this->_setPageSize($newformat, $orientation);
10092		}
10093
10094		/* -- CSS-PAGE -- */
10095		// Paged media (page-box)
10096		if ($pagesel || (isset($this->page_box['using']) && $this->page_box['using'])) {
10097
10098			if ($pagesel || $this->page == 1) {
10099				$first = true;
10100			} else {
10101				$first = false;
10102			}
10103
10104			if ($this->mirrorMargins && ($this->page % 2 == 0)) {
10105				$oddEven = 'E';
10106			} else {
10107				$oddEven = 'O';
10108			}
10109
10110			if ($pagesel) {
10111				$psel = $pagesel;
10112			} elseif ($this->page_box['current']) {
10113				$psel = $this->page_box['current'];
10114			} else {
10115				$psel = '';
10116			}
10117
10118			list($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS($psel, $first, $oddEven);
10119
10120			if ($this->mirrorMargins && ($this->page % 2 == 0)) {
10121
10122				if ($hname) {
10123					$ehvalue = 1;
10124					$ehname = $hname;
10125				} else {
10126					$ehvalue = -1;
10127				}
10128
10129				if ($fname) {
10130					$efvalue = 1;
10131					$efname = $fname;
10132				} else {
10133					$efvalue = -1;
10134				}
10135
10136			} else {
10137
10138				if ($hname) {
10139					$ohvalue = 1;
10140					$ohname = $hname;
10141				} else {
10142					$ohvalue = -1;
10143				}
10144
10145				if ($fname) {
10146					$ofvalue = 1;
10147					$ofname = $fname;
10148				} else {
10149					$ofvalue = -1;
10150				}
10151			}
10152
10153			if ($resetpagenum || $pagenumstyle || $suppress) {
10154				$this->PageNumSubstitutions[] = ['from' => ($this->page), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
10155			}
10156
10157			// PAGED MEDIA - CROP / CROSS MARKS from @PAGE
10158			$this->show_marks = $marks;
10159
10160			// Background color
10161			if (isset($bg['BACKGROUND-COLOR'])) {
10162				$cor = $this->colorConverter->convert($bg['BACKGROUND-COLOR'], $this->PDFAXwarnings);
10163				if ($cor) {
10164					$this->bodyBackgroundColor = $cor;
10165				}
10166			} else {
10167				$this->bodyBackgroundColor = false;
10168			}
10169
10170			/* -- BACKGROUNDS -- */
10171			if (isset($bg['BACKGROUND-GRADIENT'])) {
10172				$this->bodyBackgroundGradient = $bg['BACKGROUND-GRADIENT'];
10173			} else {
10174				$this->bodyBackgroundGradient = false;
10175			}
10176
10177			// Tiling Patterns
10178			if (isset($bg['BACKGROUND-IMAGE']) && $bg['BACKGROUND-IMAGE']) {
10179				$ret = $this->SetBackground($bg, $this->pgwidth);
10180				if ($ret) {
10181					$this->bodyBackgroundImage = $ret;
10182				}
10183			} else {
10184				$this->bodyBackgroundImage = false;
10185			}
10186			/* -- END BACKGROUNDS -- */
10187
10188			$this->page_box['current'] = $psel;
10189			$this->page_box['using'] = true;
10190		}
10191		/* -- END CSS-PAGE -- */
10192
10193		// Page orientation
10194		if (!$orientation) {
10195			$orientation = $this->DefOrientation;
10196		} else {
10197			$orientation = strtoupper(substr($orientation, 0, 1));
10198			if ($orientation != $this->DefOrientation) {
10199				$this->OrientationChanges[$this->page] = true;
10200			}
10201		}
10202
10203		if ($orientation != $this->CurOrientation || $newformat) {
10204
10205			// Change orientation
10206			if ($orientation == 'P') {
10207				$this->wPt = $this->fwPt;
10208				$this->hPt = $this->fhPt;
10209				$this->w = $this->fw;
10210				$this->h = $this->fh;
10211				if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') {
10212					$this->tMargin = $this->orig_tMargin;
10213					$this->bMargin = $this->orig_bMargin;
10214					$this->DeflMargin = $this->orig_lMargin;
10215					$this->DefrMargin = $this->orig_rMargin;
10216					$this->margin_header = $this->orig_hMargin;
10217					$this->margin_footer = $this->orig_fMargin;
10218				} else {
10219					$resetHTMLHeadersrequired = true;
10220				}
10221			} else {
10222				$this->wPt = $this->fhPt;
10223				$this->hPt = $this->fwPt;
10224				$this->w = $this->fh;
10225				$this->h = $this->fw;
10226
10227				if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') {
10228					$this->tMargin = $this->orig_lMargin;
10229					$this->bMargin = $this->orig_rMargin;
10230					$this->DeflMargin = $this->orig_bMargin;
10231					$this->DefrMargin = $this->orig_tMargin;
10232					$this->margin_header = $this->orig_hMargin;
10233					$this->margin_footer = $this->orig_fMargin;
10234				} else {
10235					$resetHTMLHeadersrequired = true;
10236				}
10237			}
10238
10239			$this->CurOrientation = $orientation;
10240			$this->ResetMargins();
10241			$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
10242			$this->PageBreakTrigger = $this->h - $this->bMargin;
10243		}
10244
10245		$this->pageDim[$this->page]['w'] = $this->w;
10246		$this->pageDim[$this->page]['h'] = $this->h;
10247
10248		$this->pageDim[$this->page]['outer_width_LR'] = isset($this->page_box['outer_width_LR']) ? $this->page_box['outer_width_LR'] : 0;
10249		$this->pageDim[$this->page]['outer_width_TB'] = isset($this->page_box['outer_width_TB']) ? $this->page_box['outer_width_TB'] : 0;
10250
10251		if (!isset($this->page_box['outer_width_LR']) && !isset($this->page_box['outer_width_TB'])) {
10252			$this->pageDim[$this->page]['bleedMargin'] = 0;
10253		} elseif ($this->bleedMargin <= $this->page_box['outer_width_LR'] && $this->bleedMargin <= $this->page_box['outer_width_TB']) {
10254			$this->pageDim[$this->page]['bleedMargin'] = $this->bleedMargin;
10255		} else {
10256			$this->pageDim[$this->page]['bleedMargin'] = min($this->page_box['outer_width_LR'], $this->page_box['outer_width_TB']) - 0.01;
10257		}
10258
10259		// If Page Margins are re-defined
10260		// strlen()>0 is used to pick up (integer) 0, (string) '0', or set value
10261		if ((strlen($mgl) > 0 && $this->DeflMargin != $mgl) || (strlen($mgr) > 0 && $this->DefrMargin != $mgr) || (strlen($mgt) > 0 && $this->tMargin != $mgt) || (strlen($mgb) > 0 && $this->bMargin != $mgb) || (strlen($mgh) > 0 && $this->margin_header != $mgh) || (strlen($mgf) > 0 && $this->margin_footer != $mgf)) {
10262
10263			if (strlen($mgl) > 0) {
10264				$this->DeflMargin = $mgl;
10265			}
10266
10267			if (strlen($mgr) > 0) {
10268				$this->DefrMargin = $mgr;
10269			}
10270
10271			if (strlen($mgt) > 0) {
10272				$this->tMargin = $mgt;
10273			}
10274
10275			if (strlen($mgb) > 0) {
10276				$this->bMargin = $mgb;
10277			}
10278
10279			if (strlen($mgh) > 0) {
10280				$this->margin_header = $mgh;
10281			}
10282
10283			if (strlen($mgf) > 0) {
10284				$this->margin_footer = $mgf;
10285			}
10286
10287			$this->ResetMargins();
10288			$this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
10289
10290			$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
10291			$resetHTMLHeadersrequired = true;
10292		}
10293
10294		$this->ResetMargins();
10295		$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
10296		$this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
10297
10298		// Reset column top margin
10299		$this->y0 = $this->tMargin;
10300
10301		$this->x = $this->lMargin;
10302		$this->y = $this->tMargin;
10303		$this->FontFamily = '';
10304
10305		// HEADERS AND FOOTERS	// mPDF 6
10306		if ($ohvalue < 0 || strtoupper($ohvalue) == 'OFF') {
10307			$this->HTMLHeader = '';
10308			$resetHTMLHeadersrequired = true;
10309		} elseif ($ohname && $ohvalue > 0) {
10310			if (preg_match('/^html_(.*)$/i', $ohname, $n)) {
10311				$name = $n[1];
10312			} else {
10313				$name = $ohname;
10314			}
10315			if (isset($this->pageHTMLheaders[$name])) {
10316				$this->HTMLHeader = $this->pageHTMLheaders[$name];
10317			} else {
10318				$this->HTMLHeader = '';
10319			}
10320			$resetHTMLHeadersrequired = true;
10321		}
10322
10323		if ($ehvalue < 0 || strtoupper($ehvalue) == 'OFF') {
10324			$this->HTMLHeaderE = '';
10325			$resetHTMLHeadersrequired = true;
10326		} elseif ($ehname && $ehvalue > 0) {
10327			if (preg_match('/^html_(.*)$/i', $ehname, $n)) {
10328				$name = $n[1];
10329			} else {
10330				$name = $ehname;
10331			}
10332			if (isset($this->pageHTMLheaders[$name])) {
10333				$this->HTMLHeaderE = $this->pageHTMLheaders[$name];
10334			} else {
10335				$this->HTMLHeaderE = '';
10336			}
10337			$resetHTMLHeadersrequired = true;
10338		}
10339
10340		if ($ofvalue < 0 || strtoupper($ofvalue) == 'OFF') {
10341			$this->HTMLFooter = '';
10342			$resetHTMLHeadersrequired = true;
10343		} elseif ($ofname && $ofvalue > 0) {
10344			if (preg_match('/^html_(.*)$/i', $ofname, $n)) {
10345				$name = $n[1];
10346			} else {
10347				$name = $ofname;
10348			}
10349			if (isset($this->pageHTMLfooters[$name])) {
10350				$this->HTMLFooter = $this->pageHTMLfooters[$name];
10351			} else {
10352				$this->HTMLFooter = '';
10353			}
10354			$resetHTMLHeadersrequired = true;
10355		}
10356
10357		if ($efvalue < 0 || strtoupper($efvalue) == 'OFF') {
10358			$this->HTMLFooterE = '';
10359			$resetHTMLHeadersrequired = true;
10360		} elseif ($efname && $efvalue > 0) {
10361			if (preg_match('/^html_(.*)$/i', $efname, $n)) {
10362				$name = $n[1];
10363			} else {
10364				$name = $efname;
10365			}
10366			if (isset($this->pageHTMLfooters[$name])) {
10367				$this->HTMLFooterE = $this->pageHTMLfooters[$name];
10368			} else {
10369				$this->HTMLFooterE = '';
10370			}
10371			$resetHTMLHeadersrequired = true;
10372		}
10373
10374		if ($resetHTMLHeadersrequired) {
10375			$this->SetHTMLHeader($this->HTMLHeader);
10376			$this->SetHTMLHeader($this->HTMLHeaderE, 'E');
10377			$this->SetHTMLFooter($this->HTMLFooter);
10378			$this->SetHTMLFooter($this->HTMLFooterE, 'E');
10379		}
10380
10381
10382		if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
10383			$this->_setAutoHeaderHeight($this->HTMLHeaderE);
10384			$this->_setAutoFooterHeight($this->HTMLFooterE);
10385		} else { // ODD or DEFAULT
10386			$this->_setAutoHeaderHeight($this->HTMLHeader);
10387			$this->_setAutoFooterHeight($this->HTMLFooter);
10388		}
10389
10390		// Reset column top margin
10391		$this->y0 = $this->tMargin;
10392
10393		$this->x = $this->lMargin;
10394		$this->y = $this->tMargin;
10395	}
10396
10397	// mPDF 6
10398	function _setAutoHeaderHeight(&$htmlh)
10399	{
10400		/* When the setAutoTopMargin option is set to pad/stretch, only apply auto header height when a header exists */
10401		if ($this->HTMLHeader === '' && $this->HTMLHeaderE === '') {
10402			return;
10403		}
10404
10405		if ($this->setAutoTopMargin == 'pad') {
10406			if (isset($htmlh['h']) && $htmlh['h']) {
10407				$h = $htmlh['h'];
10408			} // 5.7.3
10409			else {
10410				$h = 0;
10411			}
10412			$this->tMargin = $this->margin_header + $h + $this->orig_tMargin;
10413		} elseif ($this->setAutoTopMargin == 'stretch') {
10414			if (isset($htmlh['h']) && $htmlh['h']) {
10415				$h = $htmlh['h'];
10416			} // 5.7.3
10417			else {
10418				$h = 0;
10419			}
10420			$this->tMargin = max($this->orig_tMargin, $this->margin_header + $h + $this->autoMarginPadding);
10421		}
10422	}
10423
10424	// mPDF 6
10425	function _setAutoFooterHeight(&$htmlf)
10426	{
10427		/* When the setAutoTopMargin option is set to pad/stretch, only apply auto footer height when a footer exists */
10428		if ($this->HTMLFooter === '' && $this->HTMLFooterE === '') {
10429			return;
10430		}
10431
10432		if ($this->setAutoBottomMargin == 'pad') {
10433			if (isset($htmlf['h']) && $htmlf['h']) {
10434				$h = $htmlf['h'];
10435			} // 5.7.3
10436			else {
10437				$h = 0;
10438			}
10439			$this->bMargin = $this->margin_footer + $h + $this->orig_bMargin;
10440			$this->PageBreakTrigger = $this->h - $this->bMargin;
10441		} elseif ($this->setAutoBottomMargin == 'stretch') {
10442			if (isset($htmlf['h']) && $htmlf['h']) {
10443				$h = $htmlf['h'];
10444			} // 5.7.3
10445			else {
10446				$h = 0;
10447			}
10448			$this->bMargin = max($this->orig_bMargin, $this->margin_footer + $h + $this->autoMarginPadding);
10449			$this->PageBreakTrigger = $this->h - $this->bMargin;
10450		}
10451	}
10452
10453	function _endpage()
10454	{
10455		/* -- CSS-IMAGE-FLOAT -- */
10456		$this->printfloatbuffer();
10457		/* -- END CSS-IMAGE-FLOAT -- */
10458
10459		if ($this->visibility != 'visible') {
10460			$this->SetVisibility('visible');
10461		}
10462		$this->EndLayer();
10463		// End of page contents
10464		$this->state = 1;
10465	}
10466
10467	function _dounderline($x, $y, $txt, $OTLdata = false, $textvar = 0)
10468	{
10469		// Now print line exactly where $y secifies - called from Text() and Cell() - adjust  position there
10470		// WORD SPACING
10471		$w = ($this->GetStringWidth($txt, false, $OTLdata, $textvar) * Mpdf::SCALE) + ($this->charspacing * mb_strlen($txt, $this->mb_enc)) + ( $this->ws * mb_substr_count($txt, ' ', $this->mb_enc));
10472		// Draw a line
10473		return sprintf('%.3F %.3F m %.3F %.3F l S', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, ($x * Mpdf::SCALE) + $w, ($this->h - $y) * Mpdf::SCALE);
10474	}
10475
10476
10477
10478	/* -- WATERMARK -- */
10479
10480	// add a watermark
10481	function watermark($texte, $angle = 45, $fontsize = 96, $alpha = 0.2)
10482	{
10483		if ($this->PDFA || $this->PDFX) {
10484			throw new \Mpdf\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');
10485		}
10486
10487		if (!$this->watermark_font) {
10488			$this->watermark_font = $this->default_font;
10489		}
10490
10491		$this->SetFont($this->watermark_font, "B", $fontsize, false); // Don't output
10492		$texte = $this->purify_utf8_text($texte);
10493
10494		if ($this->text_input_as_HTML) {
10495			$texte = $this->all_entities_to_utf8($texte);
10496		}
10497
10498		if ($this->usingCoreFont) {
10499			$texte = mb_convert_encoding($texte, $this->mb_enc, 'UTF-8');
10500		}
10501
10502		// DIRECTIONALITY
10503		if (preg_match("/([" . $this->pregRTLchars . "])/u", $texte)) {
10504			$this->biDirectional = true;
10505		} // *OTL*
10506
10507		$textvar = 0;
10508		$save_OTLtags = $this->OTLtags;
10509		$this->OTLtags = [];
10510		if ($this->useKerning) {
10511			if ($this->CurrentFont['haskernGPOS']) {
10512				$this->OTLtags['Plus'] .= ' kern';
10513			} else {
10514				$textvar = ($textvar | TextVars::FC_KERNING);
10515			}
10516		}
10517
10518		/* -- OTL -- */
10519		// Use OTL OpenType Table Layout - GSUB & GPOS
10520		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
10521			$texte = $this->otl->applyOTL($texte, $this->CurrentFont['useOTL']);
10522			$OTLdata = $this->otl->OTLdata;
10523		}
10524		/* -- END OTL -- */
10525		$this->OTLtags = $save_OTLtags;
10526
10527		$this->magic_reverse_dir($texte, $this->directionality, $OTLdata);
10528
10529		$this->SetAlpha($alpha);
10530
10531		$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
10532
10533		$szfont = $fontsize;
10534		$loop = 0;
10535		$maxlen = (min($this->w, $this->h) ); // sets max length of text as 7/8 width/height of page
10536
10537		while ($loop == 0) {
10538			$this->SetFont($this->watermark_font, "B", $szfont, false); // Don't output
10539			$offset = ((sin(deg2rad($angle))) * ($szfont / Mpdf::SCALE));
10540
10541			$strlen = $this->GetStringWidth($texte, true, $OTLdata, $textvar);
10542			if ($strlen > $maxlen - $offset) {
10543				$szfont --;
10544			} else {
10545				$loop ++;
10546			}
10547		}
10548
10549		$this->SetFont($this->watermark_font, "B", $szfont - 0.1, true, true); // Output The -0.1 is because SetFont above is not written to PDF
10550
10551		// Repeating it will not output anything as mPDF thinks it is set
10552		$adj = ((cos(deg2rad($angle))) * ($strlen / 2));
10553		$opp = ((sin(deg2rad($angle))) * ($strlen / 2));
10554
10555		$wx = ($this->w / 2) - $adj + $offset / 3;
10556		$wy = ($this->h / 2) + $opp;
10557
10558		$this->Rotate($angle, $wx, $wy);
10559		$this->Text($wx, $wy, $texte, $OTLdata, $textvar);
10560		$this->Rotate(0);
10561		$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
10562
10563		$this->SetAlpha(1);
10564	}
10565
10566	function watermarkImg($src, $alpha = 0.2)
10567	{
10568		if ($this->PDFA || $this->PDFX) {
10569			throw new \Mpdf\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');
10570		}
10571
10572		if ($this->watermarkImgBehind) {
10573			$this->watermarkImgAlpha = $this->SetAlpha($alpha, 'Normal', true);
10574		} else {
10575			$this->SetAlpha($alpha, $this->watermarkImgAlphaBlend);
10576		}
10577
10578		$this->Image($src, 0, 0, 0, 0, '', '', true, true, true);
10579
10580		if (!$this->watermarkImgBehind) {
10581			$this->SetAlpha(1);
10582		}
10583	}
10584
10585	/* -- END WATERMARK -- */
10586
10587	function Rotate($angle, $x = -1, $y = -1)
10588	{
10589		if ($x == -1) {
10590			$x = $this->x;
10591		}
10592		if ($y == -1) {
10593			$y = $this->y;
10594		}
10595		if ($this->angle != 0) {
10596			$this->writer->write('Q');
10597		}
10598		$this->angle = $angle;
10599		if ($angle != 0) {
10600			$angle*=M_PI / 180;
10601			$c = cos($angle);
10602			$s = sin($angle);
10603			$cx = $x * Mpdf::SCALE;
10604			$cy = ($this->h - $y) * Mpdf::SCALE;
10605			$this->writer->write(sprintf('q %.5F %.5F %.5F %.5F %.3F %.3F cm 1 0 0 1 %.3F %.3F cm', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy));
10606		}
10607	}
10608
10609	function CircularText($x, $y, $r, $text, $align = 'top', $fontfamily = '', $fontsize = 0, $fontstyle = '', $kerning = 120, $fontwidth = 100, $divider = '')
10610	{
10611		if (empty($this->directWrite)) {
10612			$this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
10613		}
10614
10615		$this->directWrite->CircularText($x, $y, $r, $text, $align, $fontfamily, $fontsize, $fontstyle, $kerning, $fontwidth, $divider);
10616	}
10617
10618	// From Invoice
10619	function RoundedRect($x, $y, $w, $h, $r, $style = '')
10620	{
10621		$hp = $this->h;
10622
10623		if ($style == 'F') {
10624			$op = 'f';
10625		} elseif ($style == 'FD' or $style == 'DF') {
10626			$op = 'B';
10627		} else {
10628			$op = 'S';
10629		}
10630
10631		$MyArc = 4 / 3 * (sqrt(2) - 1);
10632		$this->writer->write(sprintf('%.3F %.3F m', ($x + $r) * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE));
10633		$xc = $x + $w - $r;
10634		$yc = $y + $r;
10635		$this->writer->write(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE));
10636
10637		$this->_Arc($xc + $r * $MyArc, $yc - $r, $xc + $r, $yc - $r * $MyArc, $xc + $r, $yc);
10638		$xc = $x + $w - $r;
10639		$yc = $y + $h - $r;
10640		$this->writer->write(sprintf('%.3F %.3F l', ($x + $w) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE));
10641
10642		$this->_Arc($xc + $r, $yc + $r * $MyArc, $xc + $r * $MyArc, $yc + $r, $xc, $yc + $r);
10643		$xc = $x + $r;
10644		$yc = $y + $h - $r;
10645		$this->writer->write(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - ($y + $h)) * Mpdf::SCALE));
10646
10647		$this->_Arc($xc - $r * $MyArc, $yc + $r, $xc - $r, $yc + $r * $MyArc, $xc - $r, $yc);
10648		$xc = $x + $r;
10649		$yc = $y + $r;
10650		$this->writer->write(sprintf('%.3F %.3F l', ($x) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE));
10651
10652		$this->_Arc($xc - $r, $yc - $r * $MyArc, $xc - $r * $MyArc, $yc - $r, $xc, $yc - $r);
10653		$this->writer->write($op);
10654	}
10655
10656	function _Arc($x1, $y1, $x2, $y2, $x3, $y3)
10657	{
10658		$h = $this->h;
10659		$this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x1 * Mpdf::SCALE, ($h - $y1) * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($h - $y2) * Mpdf::SCALE, $x3 * Mpdf::SCALE, ($h - $y3) * Mpdf::SCALE));
10660	}
10661
10662	// ====================================================
10663
10664
10665
10666	/* -- DIRECTW -- */
10667	function Shaded_box($text, $font = '', $fontstyle = 'B', $szfont = '', $width = '70%', $style = 'DF', $radius = 2.5, $fill = '#FFFFFF', $color = '#000000', $pad = 2)
10668	{
10669		// F (shading - no line),S (line, no shading),DF (both)
10670		if (empty($this->directWrite)) {
10671			$this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
10672		}
10673		$this->directWrite->Shaded_box($text, $font, $fontstyle, $szfont, $width, $style, $radius, $fill, $color, $pad);
10674	}
10675
10676	/* -- END DIRECTW -- */
10677
10678	function UTF8StringToArray($str, $addSubset = true)
10679	{
10680		$out = [];
10681		$len = strlen($str);
10682		for ($i = 0; $i < $len; $i++) {
10683			$uni = -1;
10684			$h = ord($str[$i]);
10685			if ($h <= 0x7F) {
10686				$uni = $h;
10687			} elseif ($h >= 0xC2) {
10688				if (($h <= 0xDF) && ($i < $len - 1)) {
10689					$uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
10690				} elseif (($h <= 0xEF) && ($i < $len - 2)) {
10691					$uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
10692				} elseif (($h <= 0xF4) && ($i < $len - 3)) {
10693					$uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
10694				}
10695			}
10696			if ($uni >= 0) {
10697				$out[] = $uni;
10698				if ($addSubset && isset($this->CurrentFont['subset'])) {
10699					$this->CurrentFont['subset'][$uni] = $uni;
10700				}
10701			}
10702		}
10703		return $out;
10704	}
10705
10706	// Convert utf-8 string to <HHHHHH> for Font Subsets
10707	function UTF8toSubset($str)
10708	{
10709		$ret = '<';
10710		// $str = preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $str );	// mPDF 6 deleted
10711		// $str = preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $str );	// mPDF 6 deleted
10712		$unicode = $this->UTF8StringToArray($str);
10713		$orig_fid = $this->CurrentFont['subsetfontids'][0];
10714		$last_fid = $this->CurrentFont['subsetfontids'][0];
10715		foreach ($unicode as $c) {
10716			/* 	// mPDF 6 deleted
10717			  if ($c == 7 || $c == 8) {
10718			  if ($orig_fid != $last_fid) {
10719			  $ret .= '> Tj /F'.$orig_fid.' '.$this->FontSizePt.' Tf <';
10720			  $last_fid = $orig_fid;
10721			  }
10722			  if ($c == 7) { $ret .= $this->aliasNbPgHex; }
10723			  else { $ret .= $this->aliasNbPgGpHex; }
10724			  continue;
10725			  }
10726			 */
10727			if (!$this->_charDefined($this->CurrentFont['cw'], $c)) {
10728				$c = 0;
10729			} // mPDF 6
10730			for ($i = 0; $i < 99; $i++) {
10731				// return c as decimal char
10732				$init = array_search($c, $this->CurrentFont['subsets'][$i]);
10733				if ($init !== false) {
10734					if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {
10735						$ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';
10736						$last_fid = $this->CurrentFont['subsetfontids'][$i];
10737					}
10738					$ret .= sprintf("%02s", strtoupper(dechex($init)));
10739					break;
10740				} // TrueType embedded SUBSETS
10741				elseif (count($this->CurrentFont['subsets'][$i]) < 255) {
10742					$n = count($this->CurrentFont['subsets'][$i]);
10743					$this->CurrentFont['subsets'][$i][$n] = $c;
10744					if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {
10745						$ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';
10746						$last_fid = $this->CurrentFont['subsetfontids'][$i];
10747					}
10748					$ret .= sprintf("%02s", strtoupper(dechex($n)));
10749					break;
10750				} elseif (!isset($this->CurrentFont['subsets'][($i + 1)])) {
10751					// TrueType embedded SUBSETS
10752					$this->CurrentFont['subsets'][($i + 1)] = [0 => 0];
10753					$new_fid = count($this->fonts) + $this->extraFontSubsets + 1;
10754					$this->CurrentFont['subsetfontids'][($i + 1)] = $new_fid;
10755					$this->extraFontSubsets++;
10756				}
10757			}
10758		}
10759		$ret .= '>';
10760		if ($last_fid != $orig_fid) {
10761			$ret .= ' Tj /F' . $orig_fid . ' ' . $this->FontSizePt . ' Tf <> ';
10762		}
10763		return $ret;
10764	}
10765
10766	/* -- CJK-FONTS -- */
10767
10768	// from class PDF_Chinese CJK EXTENSIONS
10769	function AddCIDFont($family, $style, $name, &$cw, $CMap, $registry, $desc)
10770	{
10771		$fontkey = strtolower($family) . strtoupper($style);
10772		if (isset($this->fonts[$fontkey])) {
10773			throw new \Mpdf\MpdfException("Font already added: $family $style");
10774		}
10775		$i = count($this->fonts) + $this->extraFontSubsets + 1;
10776		$name = str_replace(' ', '', $name);
10777		if ($family == 'sjis') {
10778			$up = -120;
10779		} else {
10780			$up = -130;
10781		}
10782		// ? 'up' and 'ut' do not seem to be referenced anywhere
10783		$this->fonts[$fontkey] = ['i' => $i, 'type' => 'Type0', 'name' => $name, 'up' => $up, 'ut' => 40, 'cw' => $cw, 'CMap' => $CMap, 'registry' => $registry, 'MissingWidth' => 1000, 'desc' => $desc];
10784	}
10785
10786	function AddCJKFont($family)
10787	{
10788
10789		if ($this->PDFA || $this->PDFX) {
10790			throw new \Mpdf\MpdfException("Adobe CJK fonts cannot be embedded in mPDF (required for PDFA1-b and PDFX/1-a).");
10791		}
10792		if ($family == 'big5') {
10793			$this->AddBig5Font();
10794		} elseif ($family == 'gb') {
10795			$this->AddGBFont();
10796		} elseif ($family == 'sjis') {
10797			$this->AddSJISFont();
10798		} elseif ($family == 'uhc') {
10799			$this->AddUHCFont();
10800		}
10801	}
10802
10803	function AddBig5Font()
10804	{
10805		// Add Big5 font with proportional Latin
10806		$family = 'big5';
10807		$name = 'MSungStd-Light-Acro';
10808		$cw = $this->Big5_widths;
10809		$CMap = 'UniCNS-UTF16-H';
10810		$registry = ['ordering' => 'CNS1', 'supplement' => 4];
10811		$desc = [
10812			'Ascent' => 880,
10813			'Descent' => -120,
10814			'CapHeight' => 880,
10815			'Flags' => 6,
10816			'FontBBox' => '[-160 -249 1015 1071]',
10817			'ItalicAngle' => 0,
10818			'StemV' => 93,
10819		];
10820		$this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
10821		$this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
10822		$this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
10823		$this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
10824	}
10825
10826	function AddGBFont()
10827	{
10828		// Add GB font with proportional Latin
10829		$family = 'gb';
10830		$name = 'STSongStd-Light-Acro';
10831		$cw = $this->GB_widths;
10832		$CMap = 'UniGB-UTF16-H';
10833		$registry = ['ordering' => 'GB1', 'supplement' => 4];
10834		$desc = [
10835			'Ascent' => 880,
10836			'Descent' => -120,
10837			'CapHeight' => 737,
10838			'Flags' => 6,
10839			'FontBBox' => '[-25 -254 1000 880]',
10840			'ItalicAngle' => 0,
10841			'StemV' => 58,
10842			'Style' => '<< /Panose <000000000400000000000000> >>',
10843		];
10844		$this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
10845		$this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
10846		$this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
10847		$this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
10848	}
10849
10850	function AddSJISFont()
10851	{
10852		// Add SJIS font with proportional Latin
10853		$family = 'sjis';
10854		$name = 'KozMinPro-Regular-Acro';
10855		$cw = $this->SJIS_widths;
10856		$CMap = 'UniJIS-UTF16-H';
10857		$registry = ['ordering' => 'Japan1', 'supplement' => 5];
10858		$desc = [
10859			'Ascent' => 880,
10860			'Descent' => -120,
10861			'CapHeight' => 740,
10862			'Flags' => 6,
10863			'FontBBox' => '[-195 -272 1110 1075]',
10864			'ItalicAngle' => 0,
10865			'StemV' => 86,
10866			'XHeight' => 502,
10867		];
10868		$this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
10869		$this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
10870		$this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
10871		$this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
10872	}
10873
10874	function AddUHCFont()
10875	{
10876		// Add UHC font with proportional Latin
10877		$family = 'uhc';
10878		$name = 'HYSMyeongJoStd-Medium-Acro';
10879		$cw = $this->UHC_widths;
10880		$CMap = 'UniKS-UTF16-H';
10881		$registry = ['ordering' => 'Korea1', 'supplement' => 2];
10882		$desc = [
10883			'Ascent' => 880,
10884			'Descent' => -120,
10885			'CapHeight' => 720,
10886			'Flags' => 6,
10887			'FontBBox' => '[-28 -148 1001 880]',
10888			'ItalicAngle' => 0,
10889			'StemV' => 60,
10890			'Style' => '<< /Panose <000000000600000000000000> >>',
10891		];
10892		$this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
10893		$this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
10894		$this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
10895		$this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
10896	}
10897
10898	/* -- END CJK-FONTS -- */
10899
10900	//////////////////////////////////////////////////////////////////////////////
10901	//////////////////////////////////////////////////////////////////////////////
10902	//////////////////////////////////////////////////////////////////////////////
10903	//////////////////////////////////////////////////////////////////////////////
10904	//////////////////////////////////////////////////////////////////////////////
10905	//////////////////////////////////////////////////////////////////////////////
10906	//////////////////////////////////////////////////////////////////////////////
10907
10908	function SetDefaultFont($font)
10909	{
10910		// Disallow embedded fonts to be used as defaults in PDFA
10911		if ($this->PDFA || $this->PDFX) {
10912			if (strtolower($font) == 'ctimes') {
10913				$font = 'serif';
10914			}
10915			if (strtolower($font) == 'ccourier') {
10916				$font = 'monospace';
10917			}
10918			if (strtolower($font) == 'chelvetica') {
10919				$font = 'sans-serif';
10920			}
10921		}
10922		$font = $this->SetFont($font); // returns substituted font if necessary
10923		$this->default_font = $font;
10924		$this->original_default_font = $font;
10925		if (!$this->watermark_font) {
10926			$this->watermark_font = $font;
10927		} // *WATERMARK*
10928		$this->defaultCSS['BODY']['FONT-FAMILY'] = $font;
10929		$this->cssManager->CSS['BODY']['FONT-FAMILY'] = $font;
10930	}
10931
10932	function SetDefaultFontSize($fontsize)
10933	{
10934		$this->default_font_size = $fontsize;
10935		$this->original_default_font_size = $fontsize;
10936		$this->SetFontSize($fontsize);
10937		$this->defaultCSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';
10938		$this->cssManager->CSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';
10939	}
10940
10941	function SetDefaultBodyCSS($prop, $val)
10942	{
10943		if ($prop) {
10944			$this->defaultCSS['BODY'][strtoupper($prop)] = $val;
10945			$this->cssManager->CSS['BODY'][strtoupper($prop)] = $val;
10946		}
10947	}
10948
10949	function SetDirectionality($dir = 'ltr')
10950	{
10951		/* -- OTL -- */
10952		if (strtolower($dir) == 'rtl') {
10953			if ($this->directionality != 'rtl') {
10954				// Swop L/R Margins so page 1 RTL is an 'even' page
10955				$tmp = $this->DeflMargin;
10956				$this->DeflMargin = $this->DefrMargin;
10957				$this->DefrMargin = $tmp;
10958				$this->orig_lMargin = $this->DeflMargin;
10959				$this->orig_rMargin = $this->DefrMargin;
10960
10961				$this->SetMargins($this->DeflMargin, $this->DefrMargin, $this->tMargin);
10962			}
10963			$this->directionality = 'rtl';
10964			$this->defaultAlign = 'R';
10965			$this->defaultTableAlign = 'R';
10966		} else {
10967			/* -- END OTL -- */
10968			$this->directionality = 'ltr';
10969			$this->defaultAlign = 'L';
10970			$this->defaultTableAlign = 'L';
10971		} // *OTL*
10972		$this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality;
10973	}
10974
10975	// Return either a number (factor) - based on current set fontsize (if % or em) - or exact lineheight (with 'mm' after it)
10976	function fixLineheight($v)
10977	{
10978		$lh = false;
10979		if (preg_match('/^[0-9\.,]*$/', $v) && $v >= 0) {
10980			return ($v + 0);
10981		} elseif (strtoupper($v) == 'NORMAL' || $v == 'N') {
10982			return 'N';  // mPDF 6
10983		} else {
10984			$tlh = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize, true);
10985			if ($tlh) {
10986				return ($tlh . 'mm');
10987			}
10988		}
10989		return $this->normalLineheight;
10990	}
10991
10992	function _getNormalLineheight($desc = false)
10993	{
10994		if (!$desc) {
10995			$desc = $this->CurrentFont['desc'];
10996		}
10997		if (!isset($desc['Leading'])) {
10998			$desc['Leading'] = 0;
10999		}
11000		if ($this->useFixedNormalLineHeight) {
11001			$lh = $this->normalLineheight;
11002		} elseif (isset($desc['Ascent']) && $desc['Ascent']) {
11003			$lh = ($this->adjustFontDescLineheight * ($desc['Ascent'] - $desc['Descent'] + $desc['Leading']) / 1000);
11004		} else {
11005			$lh = $this->normalLineheight;
11006		}
11007		return $lh;
11008	}
11009
11010	// Set a (fixed) lineheight to an actual value - either to named fontsize(pts) or default
11011	function SetLineHeight($FontPt = '', $lh = '')
11012	{
11013		if (!$FontPt) {
11014			$FontPt = $this->FontSizePt;
11015		}
11016		$fs = $FontPt / Mpdf::SCALE;
11017		$this->lineheight = $this->_computeLineheight($lh, $fs);
11018	}
11019
11020	function _computeLineheight($lh, $fs = '')
11021	{
11022		if ($this->shrin_k > 1) {
11023			$k = $this->shrin_k;
11024		} else {
11025			$k = 1;
11026		}
11027		if (!$fs) {
11028			$fs = $this->FontSize;
11029		}
11030		if ($lh == 'N') {
11031			$lh = $this->_getNormalLineheight();
11032		}
11033		if (preg_match('/mm/', $lh)) {
11034			return (((float) $lh) / $k); // convert to number
11035		} elseif ($lh > 0) {
11036			return ($fs * $lh);
11037		}
11038		return ($fs * $this->normalLineheight);
11039	}
11040
11041	function _setLineYpos(&$fontsize, &$fontdesc, &$CSSlineheight, $blockYpos = false)
11042	{
11043		$ypos['glyphYorigin'] = 0;
11044		$ypos['baseline-shift'] = 0;
11045		$linegap = 0;
11046		$leading = 0;
11047
11048		if (isset($fontdesc['Ascent']) && $fontdesc['Ascent'] && !$this->useFixedTextBaseline) {
11049			// Fontsize uses font metrics - this method seems to produce results compatible with browsers (except IE9)
11050			$ypos['boxtop'] = $fontdesc['Ascent'] / 1000 * $fontsize;
11051			$ypos['boxbottom'] = $fontdesc['Descent'] / 1000 * $fontsize;
11052			if (isset($fontdesc['Leading'])) {
11053				$linegap = $fontdesc['Leading'] / 1000 * $fontsize;
11054			}
11055		} // Default if not set - uses baselineC
11056		else {
11057			$ypos['boxtop'] = (0.5 + $this->baselineC) * $fontsize;
11058			$ypos['boxbottom'] = -(0.5 - $this->baselineC) * $fontsize;
11059		}
11060		$fontheight = $ypos['boxtop'] - $ypos['boxbottom'];
11061
11062		if ($this->shrin_k > 1) {
11063			$shrin_k = $this->shrin_k;
11064		} else {
11065			$shrin_k = 1;
11066		}
11067
11068		$leading = 0;
11069		if ($CSSlineheight == 'N') {
11070			$lh = $this->_getNormalLineheight($fontdesc);
11071			$lineheight = ($fontsize * $lh);
11072			$leading += $linegap; // specified in hhea or sTypo in OpenType tables
11073		} elseif (preg_match('/mm/', $CSSlineheight)) {
11074			$lineheight = (((float) $CSSlineheight) / $shrin_k); // convert to number
11075		} // ??? If lineheight is a factor e.g. 1.3  ?? use factor x 1em or ? use 'normal' lineheight * factor
11076		// Could depend on value for $text_height - a draft CSS value as set above for now
11077		elseif ($CSSlineheight > 0) {
11078			$lineheight = ($fontsize * $CSSlineheight);
11079		} else {
11080			$lineheight = ($fontsize * $this->normalLineheight);
11081		}
11082
11083		// In general, calculate the "leading" - the difference between the fontheight and the lineheight
11084		// and add half to the top and half to the bottom. BUT
11085		// If an inline element has a font-size less than the block element, and the line-height is set as an em or % value
11086		// it will add too much leading below the font and expand the height of the line - so just use the block element exttop/extbottom:
11087		if (preg_match('/mm/', $CSSlineheight)
11088				&& ($blockYpos && $ypos['boxtop'] < $blockYpos['boxtop'])
11089				&& ($blockYpos && $ypos['boxbottom'] > $blockYpos['boxbottom'])) {
11090
11091			$ypos['exttop'] = $blockYpos['exttop'];
11092			$ypos['extbottom'] = $blockYpos['extbottom'];
11093
11094		} else {
11095
11096			$leading += ($lineheight - $fontheight);
11097
11098			$ypos['exttop'] = $ypos['boxtop'] + $leading / 2;
11099			$ypos['extbottom'] = $ypos['boxbottom'] - $leading / 2;
11100		}
11101
11102
11103		// TEMP ONLY FOR DEBUGGING *********************************
11104		// $ypos['lineheight'] = $lineheight;
11105		// $ypos['fontheight'] = $fontheight;
11106		// $ypos['leading'] = $leading;
11107
11108		return $ypos;
11109	}
11110
11111	/* Called from WriteFlowingBlock() and finishFlowingBlock()
11112	  Determines the line hieght and glyph/writing position
11113	  for each element in the line to be written */
11114
11115	function _setInlineBlockHeights(&$lineBox, &$stackHeight, &$content, &$font, $is_table)
11116	{
11117		if ($this->shrin_k > 1) {
11118			$shrin_k = $this->shrin_k;
11119		} else {
11120			$shrin_k = 1;
11121		}
11122
11123		$ypos = [];
11124		$bordypos = [];
11125		$bgypos = [];
11126
11127		if ($is_table) {
11128			// FOR TABLE
11129			$fontsize = $this->FontSize;
11130			$fontkey = $this->FontFamily . $this->FontStyle;
11131			$fontdesc = $this->fonts[$fontkey]['desc'];
11132			$CSSlineheight = $this->cellLineHeight;
11133			$line_stacking_strategy = $this->cellLineStackingStrategy; // inline-line-height [default] | block-line-height | max-height | grid-height
11134			$line_stacking_shift = $this->cellLineStackingShift;  // consider-shifts [default] | disregard-shifts
11135		} else {
11136			// FOR BLOCK FONT
11137			$fontsize = $this->blk[$this->blklvl]['InlineProperties']['size'];
11138			$fontkey = $this->blk[$this->blklvl]['InlineProperties']['family'] . $this->blk[$this->blklvl]['InlineProperties']['style'];
11139			$fontdesc = $this->fonts[$fontkey]['desc'];
11140			$CSSlineheight = $this->blk[$this->blklvl]['line_height'];
11141			// inline-line-height | block-line-height | max-height | grid-height
11142			$line_stacking_strategy = (isset($this->blk[$this->blklvl]['line_stacking_strategy']) ? $this->blk[$this->blklvl]['line_stacking_strategy'] : 'inline-line-height');
11143			// consider-shifts | disregard-shifts
11144			$line_stacking_shift = (isset($this->blk[$this->blklvl]['line_stacking_shift']) ? $this->blk[$this->blklvl]['line_stacking_shift'] : 'consider-shifts');
11145		}
11146		$boxLineHeight = $this->_computeLineheight($CSSlineheight, $fontsize);
11147
11148
11149		// First, set a "strut" using block font at index $lineBox[-1]
11150		$ypos[-1] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight);
11151
11152		// for the block element - always taking the block EXTENDED progression including leading - which may be negative
11153		if ($line_stacking_strategy == 'block-line-height') {
11154			$topy = $ypos[-1]['exttop'];
11155			$bottomy = $ypos[-1]['extbottom'];
11156		} else {
11157			$topy = 0;
11158			$bottomy = 0;
11159		}
11160
11161		// Get text-middle for aligning images/objects
11162		$midpoint = $ypos[-1]['boxtop'] - (($ypos[-1]['boxtop'] - $ypos[-1]['boxbottom']) / 2);
11163
11164		// for images / inline objects / replaced elements
11165		$mta = 0; // Maximum top-aligned
11166		$mba = 0; // Maximum bottom-aligned
11167		foreach ($content as $k => $chunk) {
11168			if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'listmarker') {
11169				$ypos[$k] = $ypos[-1];
11170				// UPDATE Maximums
11171				if ($line_stacking_strategy == 'block-line-height' || $line_stacking_strategy == 'grid-height' || $line_stacking_strategy == 'max-height') { // don't include extended block progression of all inline elements
11172					if ($ypos[$k]['boxtop'] > $topy) {
11173						$topy = $ypos[$k]['boxtop'];
11174					}
11175					if ($ypos[$k]['boxbottom'] < $bottomy) {
11176						$bottomy = $ypos[$k]['boxbottom'];
11177					}
11178				} else {
11179					if ($ypos[$k]['exttop'] > $topy) {
11180						$topy = $ypos[$k]['exttop'];
11181					}
11182					if ($ypos[$k]['extbottom'] < $bottomy) {
11183						$bottomy = $ypos[$k]['extbottom'];
11184					}
11185				}
11186			} elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
11187				$fontsize = $font[$k]['size'];
11188				$fontdesc = $font[$k]['curr']['desc'];
11189				$lh = 1;
11190				$ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $lh, $ypos[-1]); // Lineheight=1 fixed
11191			} elseif (isset($this->objectbuffer[$k])) {
11192				$oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];
11193				$va = $this->objectbuffer[$k]['vertical-align'];
11194
11195				if ($va == 'BS') { //  (BASELINE default)
11196					if ($oh > $topy) {
11197						$topy = $oh;
11198					}
11199				} elseif ($va == 'M') {
11200					if (($midpoint + $oh / 2) > $topy) {
11201						$topy = $midpoint + $oh / 2;
11202					}
11203					if (($midpoint - $oh / 2) < $bottomy) {
11204						$bottomy = $midpoint - $oh / 2;
11205					}
11206				} elseif ($va == 'TT') {
11207					if (($ypos[-1]['boxtop'] - $oh) < $bottomy) {
11208						$bottomy = $ypos[-1]['boxtop'] - $oh;
11209						$topy = max($topy, $ypos[-1]['boxtop']);
11210					}
11211				} elseif ($va == 'TB') {
11212					if (($ypos[-1]['boxbottom'] + $oh) > $topy) {
11213						$topy = $ypos[-1]['boxbottom'] + $oh;
11214						$bottomy = min($bottomy, $ypos[-1]['boxbottom']);
11215					}
11216				} elseif ($va == 'T') {
11217					if ($oh > $mta) {
11218						$mta = $oh;
11219					}
11220				} elseif ($va == 'B') {
11221					if ($oh > $mba) {
11222						$mba = $oh;
11223					}
11224				}
11225			} elseif ($content[$k] || $content[$k] === '0') {
11226				// FOR FLOWING BLOCK
11227				$fontsize = $font[$k]['size'];
11228				$fontdesc = $font[$k]['curr']['desc'];
11229				// In future could set CSS line-height from inline elements; for now, use block level:
11230				$ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight, $ypos[-1]);
11231
11232				if (isset($font[$k]['textparam']['text-baseline']) && $font[$k]['textparam']['text-baseline'] != 0) {
11233					$ypos[$k]['baseline-shift'] = $font[$k]['textparam']['text-baseline'];
11234				}
11235
11236				// DO ALIGNMENT FOR BASELINES *******************
11237				// Until most fonts have OpenType BASE tables, this won't work
11238				// $ypos[$k] compared to $ypos[-1] or $ypos[$k-1] using $dominant_baseline and $baseline_table
11239				// UPDATE Maximums
11240				if ($line_stacking_strategy == 'block-line-height' || $line_stacking_strategy == 'grid-height' || $line_stacking_strategy == 'max-height') { // don't include extended block progression of all inline elements
11241					if ($line_stacking_shift == 'disregard-shifts') {
11242						if ($ypos[$k]['boxtop'] > $topy) {
11243							$topy = $ypos[$k]['boxtop'];
11244						}
11245						if ($ypos[$k]['boxbottom'] < $bottomy) {
11246							$bottomy = $ypos[$k]['boxbottom'];
11247						}
11248					} else {
11249						if (($ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift']) > $topy) {
11250							$topy = $ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift'];
11251						}
11252						if (($ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) {
11253							$bottomy = $ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift'];
11254						}
11255					}
11256				} else {
11257					if ($line_stacking_shift == 'disregard-shifts') {
11258						if ($ypos[$k]['exttop'] > $topy) {
11259							$topy = $ypos[$k]['exttop'];
11260						}
11261						if ($ypos[$k]['extbottom'] < $bottomy) {
11262							$bottomy = $ypos[$k]['extbottom'];
11263						}
11264					} else {
11265						if (($ypos[$k]['exttop'] + $ypos[$k]['baseline-shift']) > $topy) {
11266							$topy = $ypos[$k]['exttop'] + $ypos[$k]['baseline-shift'];
11267						}
11268						if (($ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) {
11269							$bottomy = $ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift'];
11270						}
11271					}
11272				}
11273
11274				// If BORDER set on inline element
11275				if (isset($font[$k]['bord']) && $font[$k]['bord']) {
11276					$bordfontsize = $font[$k]['textparam']['bord-decoration']['fontsize'] / $shrin_k;
11277					$bordfontkey = $font[$k]['textparam']['bord-decoration']['fontkey'];
11278					if ($bordfontkey != $fontkey || $bordfontsize != $fontsize || isset($font[$k]['textparam']['bord-decoration']['baseline'])) {
11279						$bordfontdesc = $this->fonts[$bordfontkey]['desc'];
11280						$bordypos[$k] = $this->_setLineYpos($bordfontsize, $bordfontdesc, $CSSlineheight, $ypos[-1]);
11281						if (isset($font[$k]['textparam']['bord-decoration']['baseline']) && $font[$k]['textparam']['bord-decoration']['baseline'] != 0) {
11282							$bordypos[$k]['baseline-shift'] = $font[$k]['textparam']['bord-decoration']['baseline'] / $shrin_k;
11283						}
11284					}
11285				}
11286				// If BACKGROUND set on inline element
11287				if (isset($font[$k]['spanbgcolor']) && $font[$k]['spanbgcolor']) {
11288					$bgfontsize = $font[$k]['textparam']['bg-decoration']['fontsize'] / $shrin_k;
11289					$bgfontkey = $font[$k]['textparam']['bg-decoration']['fontkey'];
11290					if ($bgfontkey != $fontkey || $bgfontsize != $fontsize || isset($font[$k]['textparam']['bg-decoration']['baseline'])) {
11291						$bgfontdesc = $this->fonts[$bgfontkey]['desc'];
11292						$bgypos[$k] = $this->_setLineYpos($bgfontsize, $bgfontdesc, $CSSlineheight, $ypos[-1]);
11293						if (isset($font[$k]['textparam']['bg-decoration']['baseline']) && $font[$k]['textparam']['bg-decoration']['baseline'] != 0) {
11294							$bgypos[$k]['baseline-shift'] = $font[$k]['textparam']['bg-decoration']['baseline'] / $shrin_k;
11295						}
11296					}
11297				}
11298			}
11299		}
11300
11301
11302		// TOP or BOTTOM aligned images
11303		if ($mta > ($topy - $bottomy)) {
11304			if (($topy - $mta) < $bottomy) {
11305				$bottomy = $topy - $mta;
11306			}
11307		}
11308		if ($mba > ($topy - $bottomy)) {
11309			if (($bottomy + $mba) > $topy) {
11310				$topy = $bottomy + $mba;
11311			}
11312		}
11313
11314		if ($line_stacking_strategy == 'block-line-height') { // fixed height set by block element (whether present or not)
11315			$topy = $ypos[-1]['exttop'];
11316			$bottomy = $ypos[-1]['extbottom'];
11317		}
11318
11319		$inclusiveHeight = $topy - $bottomy;
11320
11321		// SET $stackHeight taking note of line_stacking_strategy
11322		// NB inclusive height already takes account of need to consider block progression height (excludes leading set by lineheight)
11323		// or extended block progression height (includes leading set by lineheight)
11324		if ($line_stacking_strategy == 'block-line-height') { // fixed = extended block progression height of block element
11325			$stackHeight = $boxLineHeight;
11326		} elseif ($line_stacking_strategy == 'max-height') { // smallest height which includes extended block progression height of block element
11327			// and block progression heights of inline elements (NOT extended)
11328			$stackHeight = $inclusiveHeight;
11329		} elseif ($line_stacking_strategy == 'grid-height') { // smallest multiple of block element lineheight to include
11330			// block progression heights of inline elements (NOT extended)
11331			$stackHeight = $boxLineHeight;
11332			while ($stackHeight < $inclusiveHeight) {
11333				$stackHeight += $boxLineHeight;
11334			}
11335		} else { // 'inline-line-height' = default		// smallest height which includes extended block progression height of block element
11336			// AND extended block progression heights of inline elements
11337			$stackHeight = $inclusiveHeight;
11338		}
11339
11340		$diff = $stackHeight - $inclusiveHeight;
11341		$topy += $diff / 2;
11342		$bottomy -= $diff / 2;
11343
11344		// ADJUST $ypos => lineBox using $stackHeight; lineBox are all offsets from the top of stackHeight in mm
11345		// and SET IMAGE OFFSETS
11346		$lineBox[-1]['boxtop'] = $topy - $ypos[-1]['boxtop'];
11347		$lineBox[-1]['boxbottom'] = $topy - $ypos[-1]['boxbottom'];
11348		// $lineBox[-1]['exttop'] = $topy - $ypos[-1]['exttop'];
11349		// $lineBox[-1]['extbottom'] = $topy - $ypos[-1]['extbottom'];
11350		$lineBox[-1]['glyphYorigin'] = $topy - $ypos[-1]['glyphYorigin'];
11351		$lineBox[-1]['baseline-shift'] = $ypos[-1]['baseline-shift'];
11352
11353		$midpoint = $lineBox[-1]['boxbottom'] - (($lineBox[-1]['boxbottom'] - $lineBox[-1]['boxtop']) / 2);
11354
11355		foreach ($content as $k => $chunk) {
11356			if (isset($this->objectbuffer[$k])) {
11357				$oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];
11358				// LIST MARKERS
11359				if ($this->objectbuffer[$k]['type'] == 'listmarker') {
11360					$oh = $fontsize;
11361				} elseif ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
11362					$oh = $font[$k]['size']; // == $this->objectbuffer[$k]['fontsize']/Mpdf::SCALE;
11363					$lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];
11364					$lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];
11365					$lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];
11366					$lineBox[$k]['baseline-shift'] = 0;
11367					// continue;
11368				}
11369				$va = $this->objectbuffer[$k]['vertical-align']; // = $objattr['vertical-align'] = set as M,T,B,S
11370
11371				if ($va == 'BS') { //  (BASELINE default)
11372					$lineBox[$k]['top'] = $lineBox[-1]['glyphYorigin'] - $oh;
11373				} elseif ($va == 'M') {
11374					$lineBox[$k]['top'] = $midpoint - $oh / 2;
11375				} elseif ($va == 'TT') {
11376					$lineBox[$k]['top'] = $lineBox[-1]['boxtop'];
11377				} elseif ($va == 'TB') {
11378					$lineBox[$k]['top'] = $lineBox[-1]['boxbottom'] - $oh;
11379				} elseif ($va == 'T') {
11380					$lineBox[$k]['top'] = 0;
11381				} elseif ($va == 'B') {
11382					$lineBox[$k]['top'] = $stackHeight - $oh;
11383				}
11384			} elseif ($content[$k] || $content[$k] === '0') {
11385				$lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];
11386				$lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];
11387				// $lineBox[$k]['exttop'] = $topy - $ypos[$k]['exttop'];
11388				// $lineBox[$k]['extbottom'] = $topy - $ypos[$k]['extbottom'];
11389				$lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];
11390				$lineBox[$k]['baseline-shift'] = $ypos[$k]['baseline-shift'];
11391				if (isset($bordypos[$k]['boxtop'])) {
11392					$lineBox[$k]['border-boxtop'] = $topy - $bordypos[$k]['boxtop'];
11393					$lineBox[$k]['border-boxbottom'] = $topy - $bordypos[$k]['boxbottom'];
11394					$lineBox[$k]['border-baseline-shift'] = $bordypos[$k]['baseline-shift'];
11395				}
11396				if (isset($bgypos[$k]['boxtop'])) {
11397					$lineBox[$k]['background-boxtop'] = $topy - $bgypos[$k]['boxtop'];
11398					$lineBox[$k]['background-boxbottom'] = $topy - $bgypos[$k]['boxbottom'];
11399					$lineBox[$k]['background-baseline-shift'] = $bgypos[$k]['baseline-shift'];
11400				}
11401			}
11402		}
11403	}
11404
11405	function SetBasePath($str = '')
11406	{
11407		if (isset($_SERVER['HTTP_HOST'])) {
11408			$host = $_SERVER['HTTP_HOST'];
11409		} elseif (isset($_SERVER['SERVER_NAME'])) {
11410			$host = $_SERVER['SERVER_NAME'];
11411		} else {
11412			$host = '';
11413		}
11414		if (!$str) {
11415			if (isset($_SERVER['SCRIPT_NAME'])) {
11416				$currentPath = dirname($_SERVER['SCRIPT_NAME']);
11417			} else {
11418				$currentPath = dirname($_SERVER['PHP_SELF']);
11419			}
11420			$currentPath = str_replace("\\", "/", $currentPath);
11421			if ($currentPath == '/') {
11422				$currentPath = '';
11423			}
11424			if ($host) {  // mPDF 6
11425				if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') {
11426					$currpath = 'https://' . $host . $currentPath . '/';
11427				} else {
11428					$currpath = 'http://' . $host . $currentPath . '/';
11429				}
11430			} else {
11431				$currpath = '';
11432			}
11433			$this->basepath = $currpath;
11434			$this->basepathIsLocal = true;
11435			return;
11436		}
11437		$str = preg_replace('/\?.*/', '', $str);
11438		if (!preg_match('/(http|https|ftp):\/\/.*\//i', $str)) {
11439			$str .= '/';
11440		}
11441		$str .= 'xxx'; // in case $str ends in / e.g. http://www.bbc.co.uk/
11442		$this->basepath = dirname($str) . "/"; // returns e.g. e.g. http://www.google.com/dir1/dir2/dir3/
11443		$this->basepath = str_replace("\\", "/", $this->basepath); // If on Windows
11444		$tr = parse_url($this->basepath);
11445		if (isset($tr['host']) && ($tr['host'] == $host)) {
11446			$this->basepathIsLocal = true;
11447		} else {
11448			$this->basepathIsLocal = false;
11449		}
11450	}
11451
11452	public function GetFullPath(&$path, $basepath = '')
11453	{
11454		// When parsing CSS need to pass temporary basepath - so links are relative to current stylesheet
11455		if (!$basepath) {
11456			$basepath = $this->basepath;
11457		}
11458
11459		// Fix path value
11460		$path = str_replace("\\", '/', $path); // If on Windows
11461
11462		// mPDF 5.7.2
11463		if (substr($path, 0, 2) === '//') {
11464			$scheme = parse_url($basepath, PHP_URL_SCHEME);
11465			$scheme = $scheme ?: 'http';
11466			$path = $scheme . ':' . $path;
11467		}
11468
11469		$path = preg_replace('|^./|', '', $path); // Inadvertently corrects "./path/etc" and "//www.domain.com/etc"
11470
11471		if (substr($path, 0, 1) == '#') {
11472			return;
11473		}
11474
11475		// Skip schemes not supported by installed stream wrappers
11476		$wrappers = stream_get_wrappers();
11477		$pattern = sprintf('@^(?!%s)[a-z0-9\.\-+]+:.*@i', implode('|', $wrappers));
11478		if (preg_match($pattern, $path)) {
11479			return;
11480		}
11481
11482		if (substr($path, 0, 3) == "../") { // It is a relative link
11483
11484			$backtrackamount = substr_count($path, "../");
11485			$maxbacktrack = substr_count($basepath, "/") - 3;
11486			$filepath = str_replace("../", '', $path);
11487			$path = $basepath;
11488
11489			// If it is an invalid relative link, then make it go to directory root
11490			if ($backtrackamount > $maxbacktrack) {
11491				$backtrackamount = $maxbacktrack;
11492			}
11493
11494			// Backtrack some directories
11495			for ($i = 0; $i < $backtrackamount + 1; $i++) {
11496				$path = substr($path, 0, strrpos($path, "/"));
11497			}
11498
11499			$path = $path . "/" . $filepath; // Make it an absolute path
11500
11501		} elseif ((strpos($path, ":/") === false || strpos($path, ":/") > 10) && !@is_file($path)) { // It is a local link. Ignore potential file errors
11502
11503			if (substr($path, 0, 1) == "/") {
11504
11505				$tr = parse_url($basepath);
11506
11507				// mPDF 5.7.2
11508				$root = '';
11509				if (!empty($tr['scheme'])) {
11510					$root .= $tr['scheme'] . '://';
11511				}
11512
11513				$root .= isset($tr['host']) ? $tr['host'] : '';
11514				$root .= ((isset($tr['port']) && $tr['port']) ? (':' . $tr['port']) : ''); // mPDF 5.7.3
11515
11516				$path = $root . $path;
11517
11518			} else {
11519				$path = $basepath . $path;
11520			}
11521		}
11522		// Do nothing if it is an Absolute Link
11523	}
11524
11525	function docPageNum($num = 0, $extras = false)
11526	{
11527		if ($num < 1) {
11528			$num = $this->page;
11529		}
11530
11531		$type = $this->defaultPageNumStyle; // set default Page Number Style
11532		$ppgno = $num;
11533		$suppress = 0;
11534		$offset = 0;
11535		$lastreset = 0;
11536
11537		foreach ($this->PageNumSubstitutions as $psarr) {
11538
11539			if ($num >= $psarr['from']) {
11540
11541				if ($psarr['reset']) {
11542					if ($psarr['reset'] > 1) {
11543						$offset = $psarr['reset'] - 1;
11544					}
11545					$ppgno = $num - $psarr['from'] + 1 + $offset;
11546					$lastreset = $psarr['from'];
11547				}
11548
11549				if ($psarr['type']) {
11550					$type = $psarr['type'];
11551				}
11552
11553				if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
11554					$suppress = 1;
11555				} elseif (strtoupper($psarr['suppress']) == 'OFF') {
11556					$suppress = 0;
11557				}
11558			}
11559		}
11560
11561		if ($suppress) {
11562			return '';
11563		}
11564
11565		$ppgno = $this->_getStyledNumber($ppgno, $type);
11566
11567		if ($extras) {
11568			$ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;
11569		}
11570
11571		return $ppgno;
11572	}
11573
11574	function docPageNumTotal($num = 0, $extras = false)
11575	{
11576		if ($num < 1) {
11577			$num = $this->page;
11578		}
11579
11580		$type = $this->defaultPageNumStyle; // set default Page Number Style
11581		$ppgstart = 1;
11582		$ppgend = count($this->pages) + 1;
11583		$suppress = 0;
11584		$offset = 0;
11585
11586		foreach ($this->PageNumSubstitutions as $psarr) {
11587			if ($num >= $psarr['from']) {
11588				if ($psarr['reset']) {
11589					if ($psarr['reset'] > 1) {
11590						$offset = $psarr['reset'] - 1;
11591					}
11592					$ppgstart = $psarr['from'] + $offset;
11593					$ppgend = count($this->pages) + 1 + $offset;
11594				}
11595				if ($psarr['type']) {
11596					$type = $psarr['type'];
11597				}
11598				if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
11599					$suppress = 1;
11600				} elseif (strtoupper($psarr['suppress']) == 'OFF') {
11601					$suppress = 0;
11602				}
11603			}
11604			if ($num < $psarr['from']) {
11605				if ($psarr['reset']) {
11606					$ppgend = $psarr['from'] + $offset;
11607					break;
11608				}
11609			}
11610		}
11611
11612		if ($suppress) {
11613			return '';
11614		}
11615
11616		$ppgno = $ppgend - $ppgstart + $offset;
11617		$ppgno = $this->_getStyledNumber($ppgno, $type);
11618
11619		if ($extras) {
11620			$ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;
11621		}
11622
11623		return $ppgno;
11624	}
11625
11626	// mPDF 6
11627	function _getStyledNumber($ppgno, $type, $listmarker = false)
11628	{
11629		if ($listmarker) {
11630			$reverse = true; // Reverse RTL numerals (Hebrew) when using for list
11631			$checkfont = true; // Using list - font is set, so check if character is available
11632		} else {
11633			$reverse = false; // For pagenumbers, RTL numerals (Hebrew) will get reversed later by bidi
11634			$checkfont = false; // For pagenumbers - font is not set, so no check
11635		}
11636
11637		$decToAlpha = new Conversion\DecToAlpha();
11638		$decToCjk = new Conversion\DecToCjk();
11639		$decToHebrew = new Conversion\DecToHebrew();
11640		$decToRoman = new Conversion\DecToRoman();
11641		$decToOther = new Conversion\DecToOther($this);
11642
11643		$lowertype = strtolower($type);
11644
11645		if ($lowertype == 'upper-latin' || $lowertype == 'upper-alpha' || $type == 'A') {
11646
11647			$ppgno = $decToAlpha->convert($ppgno, true);
11648
11649		} elseif ($lowertype == 'lower-latin' || $lowertype == 'lower-alpha' || $type == 'a') {
11650
11651			$ppgno = $decToAlpha->convert($ppgno, false);
11652
11653		} elseif ($lowertype == 'upper-roman' || $type == 'I') {
11654
11655			$ppgno = $decToRoman->convert($ppgno, true);
11656
11657		} elseif ($lowertype == 'lower-roman' || $type == 'i') {
11658
11659			$ppgno = $decToRoman->convert($ppgno, false);
11660
11661		} elseif ($lowertype == 'hebrew') {
11662
11663			$ppgno = $decToHebrew->convert($ppgno, $reverse);
11664
11665		} elseif (preg_match('/(arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao|myanmar)/i', $lowertype, $m)) {
11666
11667			$cp = $decToOther->getCodePage($m[1]);
11668			$ppgno = $decToOther->convert($ppgno, $cp, $checkfont);
11669
11670		} elseif ($lowertype == 'cjk-decimal') {
11671
11672			$ppgno = $decToCjk->convert($ppgno);
11673
11674		}
11675
11676		return $ppgno;
11677	}
11678
11679	function docPageSettings($num = 0)
11680	{
11681		// Returns current type (numberstyle), suppression state for this page number;
11682		// reset is only returned if set for this page number
11683		if ($num < 1) {
11684			$num = $this->page;
11685		}
11686
11687		$type = $this->defaultPageNumStyle; // set default Page Number Style
11688		$ppgno = $num;
11689		$suppress = 0;
11690		$offset = 0;
11691		$reset = '';
11692
11693		foreach ($this->PageNumSubstitutions as $psarr) {
11694			if ($num >= $psarr['from']) {
11695				if ($psarr['reset']) {
11696					if ($psarr['reset'] > 1) {
11697						$offset = $psarr['reset'] - 1;
11698					}
11699					$ppgno = $num - $psarr['from'] + 1 + $offset;
11700				}
11701				if ($psarr['type']) {
11702					$type = $psarr['type'];
11703				}
11704				if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
11705					$suppress = 1;
11706				} elseif (strtoupper($psarr['suppress']) == 'OFF') {
11707					$suppress = 0;
11708				}
11709			}
11710			if ($num == $psarr['from']) {
11711				$reset = $psarr['reset'];
11712			}
11713		}
11714
11715		if ($suppress) {
11716			$suppress = 'on';
11717		} else {
11718			$suppress = 'off';
11719		}
11720
11721		return [$type, $suppress, $reset];
11722	}
11723
11724	function RestartDocTemplate()
11725	{
11726		$this->docTemplateStart = $this->page;
11727	}
11728
11729	// Page header
11730	function Header($content = '')
11731	{
11732
11733		$this->cMarginL = 0;
11734		$this->cMarginR = 0;
11735
11736
11737		if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLHeaderE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLHeader) || (!$this->mirrorMargins && $this->HTMLHeader)) {
11738			$this->writeHTMLHeaders();
11739			return;
11740		}
11741	}
11742
11743	/* -- TABLES -- */
11744	function TableHeaderFooter($content = '', $tablestartpage = '', $tablestartcolumn = '', $horf = 'H', $level = 0, $firstSpread = true, $finalSpread = true)
11745	{
11746		if (($horf == 'H' || $horf == 'F') && !empty($content)) { // mPDF 5.7.2
11747			$table = &$this->table[1][1];
11748
11749			// mPDF 5.7.2
11750			if ($horf == 'F') { // Table Footer
11751				$firstrow = count($table['cells']) - $table['footernrows'];
11752				$lastrow = count($table['cells']) - 1;
11753			} else {  // Table Header
11754				$firstrow = 0;
11755				$lastrow = $table['headernrows'] - 1;
11756			}
11757			if (empty($content[$firstrow])) {
11758				if ($this->debug) {
11759					throw new \Mpdf\MpdfException("<tfoot> must precede <tbody> in a table");
11760				} else {
11761					return;
11762				}
11763			}
11764
11765
11766			// Advance down page by half width of top border
11767			if ($horf == 'H') { // Only if header
11768				if ($table['borders_separate']) {
11769					$adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];
11770				} else {
11771					$adv = $table['max_cell_border_width']['T'] / 2;
11772				}
11773				if ($adv) {
11774					if ($this->table_rotate) {
11775						$this->y += ($adv);
11776					} else {
11777						$this->DivLn($adv, $this->blklvl, true);
11778					}
11779				}
11780			}
11781
11782			$topy = $content[$firstrow][0]['y'] - $this->y;
11783
11784			for ($i = $firstrow; $i <= $lastrow; $i++) {
11785				$y = $this->y;
11786
11787				/* -- COLUMNS -- */
11788				// If outside columns, this is done in PaintDivBB
11789				if ($this->ColActive) {
11790					// OUTER FILL BGCOLOR of DIVS
11791					if ($this->blklvl > 0) {
11792						$firstblockfill = $this->GetFirstBlockFill();
11793						if ($firstblockfill && $this->blklvl >= $firstblockfill) {
11794							$divh = $content[$i][0]['h'];
11795							$bak_x = $this->x;
11796							$this->DivLn($divh, -3, false);
11797							// Reset current block fill
11798							$bcor = $this->blk[$this->blklvl]['bgcolorarray'];
11799							$this->SetFColor($bcor);
11800							$this->x = $bak_x;
11801						}
11802					}
11803				}
11804				/* -- END COLUMNS -- */
11805
11806				$colctr = 0;
11807				foreach ($content[$i] as $tablehf) {
11808					$colctr++;
11809					$y = Arrays::get($tablehf, 'y', null) - $topy;
11810					$this->y = $y;
11811					// Set some cell values
11812					$x = Arrays::get($tablehf, 'x', null);
11813					if (($this->mirrorMargins) && ($tablestartpage == 'ODD') && (($this->page) % 2 == 0)) { // EVEN
11814						$x = $x + $this->MarginCorrection;
11815					} elseif (($this->mirrorMargins) && ($tablestartpage == 'EVEN') && (($this->page) % 2 == 1)) { // ODD
11816						$x = $x + $this->MarginCorrection;
11817					}
11818					/* -- COLUMNS -- */
11819					// Added to correct for Columns
11820					if ($this->ColActive) {
11821						if ($this->directionality == 'rtl') { // *OTL*
11822							$x -= ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
11823						} // *OTL*
11824						else { // *OTL*
11825							$x += ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap);
11826						} // *OTL*
11827					}
11828					/* -- END COLUMNS -- */
11829
11830					if ($colctr == 1) {
11831						$x0 = $x;
11832					}
11833
11834					// mPDF ITERATION
11835					if ($this->iterationCounter) {
11836						foreach ($tablehf['textbuffer'] as $k => $t) {
11837							if (!is_array($t[0]) && preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {
11838								$vname = '__' . $m[1] . '_';
11839								if (!isset($this->$vname)) {
11840									$this->$vname = 1;
11841								} else {
11842									$this->$vname++;
11843								}
11844								$tablehf['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $tablehf['textbuffer'][$k][0]);
11845							}
11846						}
11847					}
11848
11849					$w = Arrays::get($tablehf, 'w', null);
11850					$h = Arrays::get($tablehf, 'h', null);
11851					$va = Arrays::get($tablehf, 'va', null);
11852					$R = Arrays::get($tablehf, 'R', null);
11853					$direction = Arrays::get($tablehf, 'direction', null);
11854					$mih = Arrays::get($tablehf, 'mih', null);
11855					$border = Arrays::get($tablehf, 'border', null);
11856					$border_details = Arrays::get($tablehf, 'border_details', null);
11857					$padding = Arrays::get($tablehf, 'padding', null);
11858					$this->tabletheadjustfinished = true;
11859
11860					$textbuffer = Arrays::get($tablehf, 'textbuffer', null);
11861
11862					// Align
11863					$align = Arrays::get($tablehf, 'a', null);
11864					$this->cellTextAlign = $align;
11865
11866					$this->cellLineHeight = Arrays::get($tablehf, 'cellLineHeight', null);
11867					$this->cellLineStackingStrategy = Arrays::get($tablehf, 'cellLineStackingStrategy', null);
11868					$this->cellLineStackingShift = Arrays::get($tablehf, 'cellLineStackingShift', null);
11869
11870					$this->x = $x;
11871
11872					if ($this->ColActive) {
11873						if ($table['borders_separate']) {
11874							$tablefill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;
11875							if ($tablefill) {
11876								$color = $this->colorConverter->convert($tablefill, $this->PDFAXwarnings);
11877								if ($color) {
11878									$xadj = ($table['border_spacing_H'] / 2);
11879									$yadj = ($table['border_spacing_V'] / 2);
11880									$wadj = $table['border_spacing_H'];
11881									$hadj = $table['border_spacing_V'];
11882									if ($i == $firstrow && $horf == 'H') {  // Top
11883										$yadj += $table['padding']['T'] + $table['border_details']['T']['w'];
11884										$hadj += $table['padding']['T'] + $table['border_details']['T']['w'];
11885									}
11886									if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1)) || (!isset($tablehf['rowspan']) && ($i + 1) == ($lastrow + 1))) && $horf == 'F') { // Bottom
11887										$hadj += $table['padding']['B'] + $table['border_details']['B']['w'];
11888									}
11889									if ($colctr == 1) {  // Left
11890										$xadj += $table['padding']['L'] + $table['border_details']['L']['w'];
11891										$wadj += $table['padding']['L'] + $table['border_details']['L']['w'];
11892									}
11893									if ($colctr == count($content[$i])) { // Right
11894										$wadj += $table['padding']['R'] + $table['border_details']['R']['w'];
11895									}
11896									$this->SetFColor($color);
11897									$this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');
11898								}
11899							}
11900						}
11901					}
11902
11903					if ($table['empty_cells'] != 'hide' || !empty($textbuffer) || !$table['borders_separate']) {
11904						$paintcell = true;
11905					} else {
11906						$paintcell = false;
11907					}
11908
11909					// Vertical align
11910					if ($R && intval($R) > 0 && isset($va) && $va != 'B') {
11911						$va = 'B';
11912					}
11913
11914					if (!isset($va) || empty($va) || $va == 'M') {
11915						$this->y += ($h - $mih) / 2;
11916					} elseif (isset($va) && $va == 'B') {
11917						$this->y += $h - $mih;
11918					}
11919
11920
11921					// TABLE ROW OR CELL FILL BGCOLOR
11922					$fill = 0;
11923					if (isset($tablehf['bgcolor']) && $tablehf['bgcolor'] && $tablehf['bgcolor'] != 'transparent') {
11924						$fill = $tablehf['bgcolor'];
11925						$leveladj = 6;
11926					} elseif (isset($content[$i][0]['trbgcolor']) && $content[$i][0]['trbgcolor'] && $content[$i][0]['trbgcolor'] != 'transparent') { // Row color
11927						$fill = $content[$i][0]['trbgcolor'];
11928						$leveladj = 3;
11929					}
11930					if ($fill && $paintcell) {
11931						$color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
11932						if ($color) {
11933							if ($table['borders_separate']) {
11934								if ($this->ColActive) {
11935									$this->SetFColor($color);
11936									$this->Rect($x + ($table['border_spacing_H'] / 2), $y + ($table['border_spacing_V'] / 2), $w - $table['border_spacing_H'], $h - $table['border_spacing_V'], 'F');
11937								} else {
11938									$this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => ($x + ($table['border_spacing_H'] / 2)), 'y' => ($y + ($table['border_spacing_V'] / 2)), 'w' => ($w - $table['border_spacing_H']), 'h' => ($h - $table['border_spacing_V']), 'col' => $color];
11939								}
11940							} else {
11941								if ($this->ColActive) {
11942									$this->SetFColor($color);
11943									$this->Rect($x, $y, $w, $h, 'F');
11944								} else {
11945									$this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color];
11946								}
11947							}
11948						}
11949					}
11950
11951
11952					/* -- BACKGROUNDS -- */
11953					if (isset($tablehf['gradient']) && $tablehf['gradient'] && $paintcell) {
11954						$g = $this->gradient->parseBackgroundGradient($tablehf['gradient']);
11955						if ($g) {
11956							if ($table['borders_separate']) {
11957								$px = $x + ($table['border_spacing_H'] / 2);
11958								$py = $y + ($table['border_spacing_V'] / 2);
11959								$pw = $w - $table['border_spacing_H'];
11960								$ph = $h - $table['border_spacing_V'];
11961							} else {
11962								$px = $x;
11963								$py = $y;
11964								$pw = $w;
11965								$ph = $h;
11966							}
11967							if ($this->ColActive) {
11968								$this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
11969							} else {
11970								$this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
11971							}
11972						}
11973					}
11974
11975					if (isset($tablehf['background-image']) && $paintcell) {
11976						if ($tablehf['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $tablehf['background-image']['gradient'])) {
11977							$g = $this->gradient->parseMozGradient($tablehf['background-image']['gradient']);
11978							if ($g) {
11979								if ($table['borders_separate']) {
11980									$px = $x + ($table['border_spacing_H'] / 2);
11981									$py = $y + ($table['border_spacing_V'] / 2);
11982									$pw = $w - $table['border_spacing_H'];
11983									$ph = $h - $table['border_spacing_V'];
11984								} else {
11985									$px = $x;
11986									$py = $y;
11987									$pw = $w;
11988									$ph = $h;
11989								}
11990								if ($this->ColActive) {
11991									$this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
11992								} else {
11993									$this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
11994								}
11995							}
11996						} elseif ($tablehf['background-image']['image_id']) { // Background pattern
11997							$n = count($this->patterns) + 1;
11998							if ($table['borders_separate']) {
11999								$px = $x + ($table['border_spacing_H'] / 2);
12000								$py = $y + ($table['border_spacing_V'] / 2);
12001								$pw = $w - $table['border_spacing_H'];
12002								$ph = $h - $table['border_spacing_V'];
12003							} else {
12004								$px = $x;
12005								$py = $y;
12006								$pw = $w;
12007								$ph = $h;
12008							}
12009							if ($this->ColActive) {
12010								list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($tablehf['background-image']['orig_w'], $tablehf['background-image']['orig_h'], $pw, $ph, $tablehf['background-image']['resize'], $tablehf['background-image']['x_repeat'], $tablehf['background-image']['y_repeat']);
12011								$this->patterns[$n] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'pgh' => $this->h, 'image_id' => $tablehf['background-image']['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $tablehf['background-image']['x_pos'], 'y_pos' => $tablehf['background-image']['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $tablehf['background-image']['itype']];
12012								if ($tablehf['background-image']['opacity'] > 0 && $tablehf['background-image']['opacity'] < 1) {
12013									$opac = $this->SetAlpha($tablehf['background-image']['opacity'], 'Normal', true);
12014								} else {
12015									$opac = '';
12016								}
12017								$this->writer->write(sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $px * Mpdf::SCALE, ($this->h - $py) * Mpdf::SCALE, $pw * Mpdf::SCALE, -$ph * Mpdf::SCALE));
12018							} else {
12019								$this->tableBackgrounds[$level * 9 + 8][] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'image_id' => $tablehf['background-image']['image_id'], 'orig_w' => $tablehf['background-image']['orig_w'], 'orig_h' => $tablehf['background-image']['orig_h'], 'x_pos' => $tablehf['background-image']['x_pos'], 'y_pos' => $tablehf['background-image']['y_pos'], 'x_repeat' => $tablehf['background-image']['x_repeat'], 'y_repeat' => $tablehf['background-image']['y_repeat'], 'clippath' => '', 'resize' => $tablehf['background-image']['resize'], 'opacity' => $tablehf['background-image']['opacity'], 'itype' => $tablehf['background-image']['itype']];
12020							}
12021						}
12022					}
12023					/* -- END BACKGROUNDS -- */
12024
12025					// Cell Border
12026					if ($table['borders_separate'] && $paintcell && $border) {
12027						$this->_tableRect($x + ($table['border_spacing_H'] / 2) + ($border_details['L']['w'] / 2), $y + ($table['border_spacing_V'] / 2) + ($border_details['T']['w'] / 2), $w - $table['border_spacing_H'] - ($border_details['L']['w'] / 2) - ($border_details['R']['w'] / 2), $h - $table['border_spacing_V'] - ($border_details['T']['w'] / 2) - ($border_details['B']['w'] / 2), $border, $border_details, false, $table['borders_separate']);
12028					} elseif ($paintcell && $border) {
12029						$this->_tableRect($x, $y, $w, $h, $border, $border_details, true, $table['borders_separate']);   // true causes buffer
12030					}
12031
12032					// Print cell content
12033					if (!empty($textbuffer)) {
12034						if ($horf == 'F' && preg_match('/{colsum([0-9]*)[_]*}/', $textbuffer[0][0], $m)) {
12035							$rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$colctr - 1]);
12036							$textbuffer[0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $textbuffer[0][0]);
12037						}
12038
12039						if ($R) {
12040							$cellPtSize = $textbuffer[0][11] / $this->shrin_k;
12041							if (!$cellPtSize) {
12042								$cellPtSize = $this->default_font_size;
12043							}
12044							$cellFontHeight = ($cellPtSize / Mpdf::SCALE);
12045							$opx = $this->x;
12046							$opy = $this->y;
12047							$angle = intval($R);
12048
12049							// Only allow 45 - 90 degrees (when bottom-aligned) or -90
12050							if ($angle > 90) {
12051								$angle = 90;
12052							} elseif ($angle > 0 && (isset($va) && $va != 'B')) {
12053								$angle = 90;
12054							} elseif ($angle > 0 && $angle < 45) {
12055								$angle = 45;
12056							} elseif ($angle < 0) {
12057								$angle = -90;
12058							}
12059
12060							$offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);
12061							if (isset($align) && $align == 'R') {
12062								$this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($padding['R'] + $border_details['R']['w']);
12063							} elseif (!isset($align) || $align == 'C') {
12064								$this->x += ($w / 2) + ($offset);
12065							} else {
12066								$this->x += ($offset) + ($cellFontHeight / 3) + ($padding['L'] + $border_details['L']['w']);
12067							}
12068							$str = '';
12069							foreach ($tablehf['textbuffer'] as $t) {
12070								$str .= $t[0] . ' ';
12071							}
12072							$str = rtrim($str);
12073
12074							if (!isset($va) || $va == 'M') {
12075								$this->y -= ($h - $mih) / 2; // Undo what was added earlier VERTICAL ALIGN
12076								if ($angle > 0) {
12077									$this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']) + ($mih - ($padding['T'] + $border_details['T']['w'] + $border_details['B']['w'] + $padding['B']));
12078								} elseif ($angle < 0) {
12079									$this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']);
12080								}
12081							} elseif (isset($va) && $va == 'B') {
12082								$this->y -= $h - $mih; // Undo what was added earlier VERTICAL ALIGN
12083								if ($angle > 0) {
12084									$this->y += $h - ($border_details['B']['w'] + $padding['B']);
12085								} elseif ($angle < 0) {
12086									$this->y += $h - $mih + ($padding['T'] + $border_details['T']['w']);
12087								}
12088							} elseif (isset($va) && $va == 'T') {
12089								if ($angle > 0) {
12090									$this->y += $mih - ($border_details['B']['w'] + $padding['B']);
12091								} elseif ($angle < 0) {
12092									$this->y += ($padding['T'] + $border_details['T']['w']);
12093								}
12094							}
12095
12096							$this->Rotate($angle, $this->x, $this->y);
12097							$s_fs = $this->FontSizePt;
12098							$s_f = $this->FontFamily;
12099							$s_st = $this->FontStyle;
12100							if (!empty($textbuffer[0][3])) { // Font Color
12101								$cor = $textbuffer[0][3];
12102								$this->SetTColor($cor);
12103							}
12104							$this->SetFont($textbuffer[0][4], $textbuffer[0][2], $cellPtSize, true, true);
12105
12106							$this->magic_reverse_dir($str, $this->directionality, $textbuffer[0][18]);
12107							$this->Text($this->x, $this->y, $str, $textbuffer[0][18], $textbuffer[0][8]); // textvar
12108							$this->Rotate(0);
12109							$this->SetFont($s_f, $s_st, $s_fs, true, true);
12110							$this->SetTColor(0);
12111							$this->x = $opx;
12112							$this->y = $opy;
12113						} else {
12114							if ($table['borders_separate']) { // NB twice border width
12115								$xadj = $border_details['L']['w'] + $padding['L'] + ($table['border_spacing_H'] / 2);
12116								$wadj = $border_details['L']['w'] + $border_details['R']['w'] + $padding['L'] + $padding['R'] + $table['border_spacing_H'];
12117								$yadj = $border_details['T']['w'] + $padding['T'] + ($table['border_spacing_H'] / 2);
12118							} else {
12119								$xadj = $border_details['L']['w'] / 2 + $padding['L'];
12120								$wadj = ($border_details['L']['w'] + $border_details['R']['w']) / 2 + $padding['L'] + $padding['R'];
12121								$yadj = $border_details['T']['w'] / 2 + $padding['T'];
12122							}
12123
12124							$this->divwidth = $w - ($wadj);
12125							$this->x += $xadj;
12126							$this->y += $yadj;
12127							$this->printbuffer($textbuffer, '', true, false, $direction);
12128						}
12129					}
12130					$textbuffer = [];
12131
12132					/* -- BACKGROUNDS -- */
12133					if (!$this->ColActive) {
12134						if (isset($content[$i][0]['trgradients']) && ($colctr == 1 || $table['borders_separate'])) {
12135							$g = $this->gradient->parseBackgroundGradient($content[$i][0]['trgradients']);
12136							if ($g) {
12137								$gx = $x0;
12138								$gy = $y;
12139								$gh = $h;
12140								$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
12141								if ($table['borders_separate']) {
12142									$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
12143									$clx = $x + ($table['border_spacing_H'] / 2);
12144									$cly = $y + ($table['border_spacing_V'] / 2);
12145									$clw = $w - $table['border_spacing_H'];
12146									$clh = $h - $table['border_spacing_V'];
12147									// Set clipping path
12148									$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
12149									$this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s];
12150								} else {
12151									$this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
12152								}
12153							}
12154						}
12155
12156						if (isset($content[$i][0]['trbackground-images']) && ($colctr == 1 || $table['borders_separate'])) {
12157							if ($content[$i][0]['trbackground-images']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $content[$i][0]['trbackground-images']['gradient'])) {
12158								$g = $this->gradient->parseMozGradient($content[$i][0]['trbackground-images']['gradient']);
12159								if ($g) {
12160									$gx = $x0;
12161									$gy = $y;
12162									$gh = $h;
12163									$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
12164									if ($table['borders_separate']) {
12165										$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
12166										$clx = $x + ($table['border_spacing_H'] / 2);
12167										$cly = $y + ($table['border_spacing_V'] / 2);
12168										$clw = $w - $table['border_spacing_H'];
12169										$clh = $h - $table['border_spacing_V'];
12170										// Set clipping path
12171										$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
12172										$this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s];
12173									} else {
12174										$this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
12175									}
12176								}
12177							} else {
12178								$image_id = $content[$i][0]['trbackground-images']['image_id'];
12179								$orig_w = $content[$i][0]['trbackground-images']['orig_w'];
12180								$orig_h = $content[$i][0]['trbackground-images']['orig_h'];
12181								$x_pos = $content[$i][0]['trbackground-images']['x_pos'];
12182								$y_pos = $content[$i][0]['trbackground-images']['y_pos'];
12183								$x_repeat = $content[$i][0]['trbackground-images']['x_repeat'];
12184								$y_repeat = $content[$i][0]['trbackground-images']['y_repeat'];
12185								$resize = $content[$i][0]['trbackground-images']['resize'];
12186								$opacity = $content[$i][0]['trbackground-images']['opacity'];
12187								$itype = $content[$i][0]['trbackground-images']['itype'];
12188
12189								$clippath = '';
12190								$gx = $x0;
12191								$gy = $y;
12192								$gh = $h;
12193								$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
12194								if ($table['borders_separate']) {
12195									$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
12196									$clx = $x + ($table['border_spacing_H'] / 2);
12197									$cly = $y + ($table['border_spacing_V'] / 2);
12198									$clw = $w - $table['border_spacing_H'];
12199									$clh = $h - $table['border_spacing_V'];
12200									// Set clipping path
12201									$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
12202									$this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];
12203								} else {
12204									$this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];
12205								}
12206							}
12207						}
12208					}
12209					/* -- END BACKGROUNDS -- */
12210
12211					// TABLE BORDER - if separate OR collapsed and only table border
12212					if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {
12213						$halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);
12214						$halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);
12215						$halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);
12216						$halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);
12217						$tbx = $x;
12218						$tby = $y;
12219						$tbw = $w;
12220						$tbh = $h;
12221						$tab_bord = 0;
12222						$corner = '';
12223						if ($i == $firstrow && $horf == 'H') {  // Top
12224							$tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);
12225							$tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);
12226							$this->setBorder($tab_bord, Border::TOP);
12227							$corner .= 'T';
12228						}
12229						if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1))) && $horf == 'F') { // Bottom
12230							$tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);
12231							$this->setBorder($tab_bord, Border::BOTTOM);
12232							$corner .= 'B';
12233						}
12234						if ($colctr == 1 && $firstSpread) { // Left
12235							$tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);
12236							$tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);
12237							$this->setBorder($tab_bord, Border::LEFT);
12238							$corner .= 'L';
12239						}
12240						if ($colctr == count($content[$i]) && $finalSpread) { // Right
12241							$tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);
12242							$this->setBorder($tab_bord, Border::RIGHT);
12243							$corner .= 'R';
12244						}
12245						$this->_tableRect($tbx, $tby, $tbw, $tbh, $tab_bord, $table['border_details'], false, $table['borders_separate'], 'table', $corner, $table['border_spacing_V'], $table['border_spacing_H']);
12246					}
12247				}// end column $content
12248				$this->y = $y + $h; // Update y coordinate
12249			}// end row $i
12250			unset($table);
12251			$this->colsums = [];
12252		}
12253	}
12254
12255	/* -- END TABLES -- */
12256
12257	function SetHTMLHeader($header = '', $OE = '', $write = false)
12258	{
12259
12260		$height = 0;
12261		if (is_array($header) && isset($header['html']) && $header['html']) {
12262			$Hhtml = $header['html'];
12263			if ($this->setAutoTopMargin) {
12264				if (isset($header['h'])) {
12265					$height = $header['h'];
12266				} else {
12267					$height = $this->_getHtmlHeight($Hhtml);
12268				}
12269			}
12270		} elseif (!is_array($header) && $header) {
12271			$Hhtml = $header;
12272			if ($this->setAutoTopMargin) {
12273				$height = $this->_getHtmlHeight($Hhtml);
12274			}
12275		} else {
12276			$Hhtml = '';
12277		}
12278
12279		if ($OE !== 'E') {
12280			$OE = 'O';
12281		}
12282
12283		if ($OE === 'E') {
12284			if ($Hhtml) {
12285				$this->HTMLHeaderE = [];
12286				$this->HTMLHeaderE['html'] = $Hhtml;
12287				$this->HTMLHeaderE['h'] = $height;
12288			} else {
12289				$this->HTMLHeaderE = '';
12290			}
12291		} else {
12292			if ($Hhtml) {
12293				$this->HTMLHeader = [];
12294				$this->HTMLHeader['html'] = $Hhtml;
12295				$this->HTMLHeader['h'] = $height;
12296			} else {
12297				$this->HTMLHeader = '';
12298			}
12299		}
12300
12301		if (!$this->mirrorMargins && $OE == 'E') {
12302			return;
12303		}
12304		if ($Hhtml == '') {
12305			return;
12306		}
12307
12308		if ($this->setAutoTopMargin == 'pad') {
12309			$this->tMargin = $this->margin_header + $height + $this->orig_tMargin;
12310			if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {
12311				$this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;
12312			}
12313		} elseif ($this->setAutoTopMargin == 'stretch') {
12314			$this->tMargin = max($this->orig_tMargin, $this->margin_header + $height + $this->autoMarginPadding);
12315			if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {
12316				$this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;
12317			}
12318		}
12319		if ($write && $this->state != 0 && (($this->mirrorMargins && $OE == 'E' && ($this->page) % 2 == 0) || ($this->mirrorMargins && $OE != 'E' && ($this->page) % 2 == 1) || !$this->mirrorMargins)) {
12320			$this->writeHTMLHeaders();
12321		}
12322	}
12323
12324	function SetHTMLFooter($footer = '', $OE = '')
12325	{
12326		$height = 0;
12327		if (is_array($footer) && isset($footer['html']) && $footer['html']) {
12328			$Fhtml = $footer['html'];
12329			if ($this->setAutoBottomMargin) {
12330				if (isset($footer['h'])) {
12331					$height = $footer['h'];
12332				} else {
12333					$height = $this->_getHtmlHeight($Fhtml);
12334				}
12335			}
12336		} elseif (!is_array($footer) && $footer) {
12337			$Fhtml = $footer;
12338			if ($this->setAutoBottomMargin) {
12339				$height = $this->_getHtmlHeight($Fhtml);
12340			}
12341		} else {
12342			$Fhtml = '';
12343		}
12344
12345		if ($OE !== 'E') {
12346			$OE = 'O';
12347		}
12348
12349		if ($OE === 'E') {
12350			if ($Fhtml) {
12351				$this->HTMLFooterE = [];
12352				$this->HTMLFooterE['html'] = $Fhtml;
12353				$this->HTMLFooterE['h'] = $height;
12354			} else {
12355				$this->HTMLFooterE = '';
12356			}
12357		} else {
12358			if ($Fhtml) {
12359				$this->HTMLFooter = [];
12360				$this->HTMLFooter['html'] = $Fhtml;
12361				$this->HTMLFooter['h'] = $height;
12362			} else {
12363				$this->HTMLFooter = '';
12364			}
12365		}
12366
12367		if (!$this->mirrorMargins && $OE == 'E') {
12368			return;
12369		}
12370
12371		if ($Fhtml == '') {
12372			return false;
12373		}
12374
12375		if ($this->setAutoBottomMargin == 'pad') {
12376			$this->bMargin = $this->margin_footer + $height + $this->orig_bMargin;
12377			$this->PageBreakTrigger = $this->h - $this->bMargin;
12378			if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {
12379				$this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;
12380			}
12381		} elseif ($this->setAutoBottomMargin == 'stretch') {
12382			$this->bMargin = max($this->orig_bMargin, $this->margin_footer + $height + $this->autoMarginPadding);
12383			$this->PageBreakTrigger = $this->h - $this->bMargin;
12384			if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {
12385				$this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;
12386			}
12387		}
12388	}
12389
12390	function _getHtmlHeight($html)
12391	{
12392		$save_state = $this->state;
12393		if ($this->state == 0) {
12394			$this->AddPage($this->CurOrientation);
12395		}
12396		$this->state = 2;
12397		$this->Reset();
12398		$this->pageoutput[$this->page] = [];
12399		$save_x = $this->x;
12400		$save_y = $this->y;
12401		$this->x = $this->lMargin;
12402		$this->y = $this->margin_header;
12403
12404		// Replace of page number aliases and date format
12405		$pnstr = $this->pagenumPrefix . $this->docPageNum($this->page) . $this->pagenumSuffix;
12406		$pntstr = $this->nbpgPrefix . $this->docPageNumTotal($this->page) . $this->nbpgSuffix;
12407		$nb = $this->page;
12408		$html = $this->aliasReplace($html, $pnstr, $pntstr, $nb);
12409
12410		$this->HTMLheaderPageLinks = [];
12411		$this->HTMLheaderPageAnnots = [];
12412		$this->HTMLheaderPageForms = [];
12413		$savepb = $this->pageBackgrounds;
12414		$this->writingHTMLheader = true;
12415		$this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
12416		$this->writingHTMLheader = false;
12417		$h = ($this->y - $this->margin_header);
12418		$this->Reset();
12419
12420		// mPDF 5.7.2 - Clear in case Float used in Header/Footer
12421		$this->blk[0]['blockContext'] = 0;
12422		$this->blk[0]['float_endpos'] = 0;
12423
12424		$this->pageoutput[$this->page] = [];
12425		$this->headerbuffer = '';
12426		$this->pageBackgrounds = $savepb;
12427		$this->x = $save_x;
12428		$this->y = $save_y;
12429		$this->state = $save_state;
12430
12431		if ($save_state == 0) {
12432			unset($this->pages[1]);
12433			$this->page = 0;
12434		}
12435		return $h;
12436	}
12437
12438	// Called internally from Header
12439	function writeHTMLHeaders()
12440	{
12441
12442		if ($this->mirrorMargins && ($this->page) % 2 == 0) {
12443			$OE = 'E';
12444		} else {
12445			$OE = 'O';
12446		}
12447
12448		if ($OE === 'E') {
12449			$this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeaderE['html'];
12450		} else {
12451			$this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeader['html'];
12452		}
12453
12454		if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {
12455			$this->saveHTMLHeader[$this->page][$OE]['rotate'] = true;
12456			$this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->tMargin;
12457			$this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->bMargin;
12458			$this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;
12459			$this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;
12460			$this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->h;
12461			$this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->w;
12462		} else {
12463			$this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->lMargin;
12464			$this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->rMargin;
12465			$this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;
12466			$this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;
12467			$this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->w;
12468			$this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->h;
12469		}
12470	}
12471
12472	function writeHTMLFooters()
12473	{
12474
12475		if ($this->mirrorMargins && ($this->page) % 2 == 0) {
12476			$OE = 'E';
12477		} else {
12478			$OE = 'O';
12479		}
12480
12481		if ($OE === 'E') {
12482			$this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooterE['html'];
12483		} else {
12484			$this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooter['html'];
12485		}
12486
12487		if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {
12488			$this->saveHTMLFooter[$this->page][$OE]['rotate'] = true;
12489			$this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->tMargin;
12490			$this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->bMargin;
12491			$this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->rMargin;
12492			$this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->lMargin;
12493			$this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;
12494			$this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;
12495			$this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->h;
12496			$this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->w;
12497		} else {
12498			$this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->lMargin;
12499			$this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->rMargin;
12500			$this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->tMargin;
12501			$this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->bMargin;
12502			$this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;
12503			$this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;
12504			$this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->w;
12505			$this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->h;
12506		}
12507	}
12508
12509	// mPDF 6
12510	function _shareHeaderFooterWidth($cl, $cc, $cr)
12511	{
12512	// mPDF 6
12513		$l = mb_strlen($cl, 'UTF-8');
12514		$c = mb_strlen($cc, 'UTF-8');
12515		$r = mb_strlen($cr, 'UTF-8');
12516		$s = max($l, $r);
12517		$tw = $c + 2 * $s;
12518		if ($tw > 0) {
12519			return [intval($s * 100 / $tw), intval($c * 100 / $tw), intval($s * 100 / $tw)];
12520		} else {
12521			return [33, 33, 33];
12522		}
12523	}
12524
12525	// mPDF 6
12526	// Create an HTML header/footer from array (non-HTML header/footer)
12527	function _createHTMLheaderFooter($arr, $hf)
12528	{
12529		$lContent = (isset($arr['L']['content']) ? $arr['L']['content'] : '');
12530		$cContent = (isset($arr['C']['content']) ? $arr['C']['content'] : '');
12531		$rContent = (isset($arr['R']['content']) ? $arr['R']['content'] : '');
12532
12533		list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($lContent, $cContent, $rContent);
12534
12535		if ($hf == 'H') {
12536			$valign = 'bottom';
12537			$vpadding = '0 0 ' . $this->header_line_spacing . 'em 0';
12538		} else {
12539			$valign = 'top';
12540			$vpadding = '' . $this->footer_line_spacing . 'em 0 0 0';
12541		}
12542
12543		if ($this->directionality == 'rtl') { // table columns get reversed so need different text-alignment
12544			$talignL = 'right';
12545			$talignR = 'left';
12546		} else {
12547			$talignL = 'left';
12548			$talignR = 'right';
12549		}
12550
12551		$html = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: ' . $valign . '; color: #000000; ';
12552
12553		if (isset($arr['line']) && $arr['line']) {
12554			$html .= ' border-' . $valign . ': 0.1mm solid #000000;';
12555		}
12556
12557		$html .= '">';
12558		$html .= '<tr>';
12559		$html .= '<td width="' . $lw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignL . '; ';
12560
12561		if (isset($arr['L']['font-family'])) {
12562			$html .= ' font-family: ' . $arr['L']['font-family'] . ';';
12563		}
12564
12565		if (isset($arr['L']['color'])) {
12566			$html .= ' color: ' . $arr['L']['color'] . ';';
12567		}
12568
12569		if (isset($arr['L']['font-size'])) {
12570			$html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;';
12571		}
12572
12573		if (isset($arr['L']['font-style'])) {
12574			if ($arr['L']['font-style'] == 'B' || $arr['L']['font-style'] == 'BI') {
12575				$html .= ' font-weight: bold;';
12576			}
12577			if ($arr['L']['font-style'] == 'I' || $arr['L']['font-style'] == 'BI') {
12578				$html .= ' font-style: italic;';
12579			}
12580		}
12581
12582		$html .= '">' . $lContent . '</td>';
12583		$html .= '<td width="' . $cw . '%" style="padding: ' . $vpadding . '; text-align: center; ';
12584
12585		if (isset($arr['C']['font-family'])) {
12586			$html .= ' font-family: ' . $arr['C']['font-family'] . ';';
12587		}
12588
12589		if (isset($arr['C']['color'])) {
12590			$html .= ' color: ' . $arr['C']['color'] . ';';
12591		}
12592
12593		if (isset($arr['C']['font-size'])) {
12594			$html .= ' font-size: ' . $arr['C']['font-size'] . 'pt;';
12595		}
12596
12597		if (isset($arr['C']['font-style'])) {
12598			if ($arr['C']['font-style'] == 'B' || $arr['C']['font-style'] == 'BI') {
12599				$html .= ' font-weight: bold;';
12600			}
12601			if ($arr['C']['font-style'] == 'I' || $arr['C']['font-style'] == 'BI') {
12602				$html .= ' font-style: italic;';
12603			}
12604		}
12605
12606		$html .= '">' . $cContent . '</td>';
12607		$html .= '<td width="' . $rw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignR . '; ';
12608
12609		if (isset($arr['R']['font-family'])) {
12610			$html .= ' font-family: ' . $arr['R']['font-family'] . ';';
12611		}
12612
12613		if (isset($arr['R']['color'])) {
12614			$html .= ' color: ' . $arr['R']['color'] . ';';
12615		}
12616
12617		if (isset($arr['R']['font-size'])) {
12618			$html .= ' font-size: ' . $arr['R']['font-size'] . 'pt;';
12619		}
12620
12621		if (isset($arr['R']['font-style'])) {
12622			if ($arr['R']['font-style'] == 'B' || $arr['R']['font-style'] == 'BI') {
12623				$html .= ' font-weight: bold;';
12624			}
12625			if ($arr['R']['font-style'] == 'I' || $arr['R']['font-style'] == 'BI') {
12626				$html .= ' font-style: italic;';
12627			}
12628		}
12629
12630		$html .= '">' . $rContent . '</td>';
12631		$html .= '</tr></table>';
12632
12633		return $html;
12634	}
12635
12636	function DefHeaderByName($name, $arr)
12637	{
12638		if (!$name) {
12639			$name = '_nonhtmldefault';
12640		}
12641		$html = $this->_createHTMLheaderFooter($arr, 'H');
12642
12643		$this->pageHTMLheaders[$name]['html'] = $html;
12644		$this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html);
12645	}
12646
12647	function DefFooterByName($name, $arr)
12648	{
12649		if (!$name) {
12650			$name = '_nonhtmldefault';
12651		}
12652		$html = $this->_createHTMLheaderFooter($arr, 'F');
12653
12654		$this->pageHTMLfooters[$name]['html'] = $html;
12655		$this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html);
12656	}
12657
12658	function SetHeaderByName($name, $side = 'O', $write = false)
12659	{
12660		if (!$name) {
12661			$name = '_nonhtmldefault';
12662		}
12663		$this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);
12664	}
12665
12666	function SetFooterByName($name, $side = 'O')
12667	{
12668		if (!$name) {
12669			$name = '_nonhtmldefault';
12670		}
12671		$this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);
12672	}
12673
12674	function DefHTMLHeaderByName($name, $html)
12675	{
12676		if (!$name) {
12677			$name = '_default';
12678		}
12679
12680		$this->pageHTMLheaders[$name]['html'] = $html;
12681		$this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html);
12682	}
12683
12684	function DefHTMLFooterByName($name, $html)
12685	{
12686		if (!$name) {
12687			$name = '_default';
12688		}
12689
12690		$this->pageHTMLfooters[$name]['html'] = $html;
12691		$this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html);
12692	}
12693
12694	function SetHTMLHeaderByName($name, $side = 'O', $write = false)
12695	{
12696		if (!$name) {
12697			$name = '_default';
12698		}
12699		$this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);
12700	}
12701
12702	function SetHTMLFooterByName($name, $side = 'O')
12703	{
12704		if (!$name) {
12705			$name = '_default';
12706		}
12707		$this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);
12708	}
12709
12710	function SetHeader($Harray = [], $side = '', $write = false)
12711	{
12712		$oddhtml = '';
12713		$evenhtml = '';
12714
12715		if (is_string($Harray)) {
12716
12717			if (strlen($Harray) === 0) {
12718
12719				$oddhtml = '';
12720				$evenhtml = '';
12721
12722			} elseif (strpos($Harray, '|') !== false) {
12723
12724				$hdet = explode('|', $Harray);
12725
12726				list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($hdet[0], $hdet[1], $hdet[2]);
12727				$oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';
12728
12729				if ($this->defaultheaderfontsize) {
12730					$oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
12731				}
12732
12733				if ($this->defaultheaderfontstyle) {
12734
12735					if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
12736						$oddhtml .= ' font-weight: bold;';
12737					}
12738
12739					if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
12740						$oddhtml .= ' font-style: italic;';
12741					}
12742				}
12743
12744				if ($this->defaultheaderline) {
12745					$oddhtml .= ' border-bottom: 0.1mm solid #000000;';
12746				}
12747
12748				$oddhtml .= '">';
12749				$oddhtml .= '<tr>';
12750				$oddhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[0] . '</td>';
12751				$oddhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>';
12752				$oddhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[2] . '</td>';
12753				$oddhtml .= '</tr></table>';
12754
12755				$evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';
12756
12757				if ($this->defaultheaderfontsize) {
12758					$evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
12759				}
12760
12761				if ($this->defaultheaderfontstyle) {
12762					if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
12763						$evenhtml .= ' font-weight: bold;';
12764					}
12765					if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
12766						$evenhtml .= ' font-style: italic;';
12767					}
12768				}
12769
12770				if ($this->defaultheaderline) {
12771					$evenhtml .= ' border-bottom: 0.1mm solid #000000;';
12772				}
12773
12774				$evenhtml .= '">';
12775				$evenhtml .= '<tr>';
12776				$evenhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[2] . '</td>';
12777				$evenhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>';
12778				$evenhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[0] . '</td>';
12779				$evenhtml .= '</tr></table>';
12780
12781			} else {
12782
12783				$oddhtml = '<div style="margin: 0; color: #000000; ';
12784
12785				if ($this->defaultheaderfontsize) {
12786					$oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
12787				}
12788
12789				if ($this->defaultheaderfontstyle) {
12790
12791					if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
12792						$oddhtml .= ' font-weight: bold;';
12793					}
12794
12795					if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
12796						$oddhtml .= ' font-style: italic;';
12797					}
12798				}
12799
12800				if ($this->defaultheaderline) {
12801					$oddhtml .= ' border-bottom: 0.1mm solid #000000;';
12802				}
12803
12804				$oddhtml .= 'text-align: right; ">' . $Harray . '</div>';
12805				$evenhtml = '<div style="margin: 0; color: #000000; ';
12806
12807				if ($this->defaultheaderfontsize) {
12808					$evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
12809				}
12810
12811				if ($this->defaultheaderfontstyle) {
12812
12813					if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
12814						$evenhtml .= ' font-weight: bold;';
12815					}
12816
12817					if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
12818						$evenhtml .= ' font-style: italic;';
12819					}
12820				}
12821
12822				if ($this->defaultheaderline) {
12823					$evenhtml .= ' border-bottom: 0.1mm solid #000000;';
12824				}
12825
12826				$evenhtml .= 'text-align: left; ">' . $Harray . '</div>';
12827			}
12828
12829		} elseif (is_array($Harray) && !empty($Harray)) {
12830
12831			$odd = null;
12832			$even = null;
12833
12834			if ($side === 'O') {
12835				$odd = $Harray;
12836			} elseif ($side === 'E') {
12837				$even = $Harray;
12838			} else {
12839				$odd = Arrays::get($Harray, 'odd', null);
12840				$even = Arrays::get($Harray, 'even', null);
12841			}
12842
12843			$oddhtml = $this->_createHTMLheaderFooter($odd, 'H');
12844			$evenhtml = $this->_createHTMLheaderFooter($even, 'H');
12845		}
12846
12847		if ($side === 'E') {
12848			$this->SetHTMLHeader($evenhtml, 'E', $write);
12849		} elseif ($side === 'O') {
12850			$this->SetHTMLHeader($oddhtml, 'O', $write);
12851		} else {
12852			$this->SetHTMLHeader($oddhtml, 'O', $write);
12853			$this->SetHTMLHeader($evenhtml, 'E', $write);
12854		}
12855	}
12856
12857	function SetFooter($Farray = [], $side = '')
12858	{
12859		$oddhtml = '';
12860		$evenhtml = '';
12861
12862		if (is_string($Farray)) {
12863
12864			if (strlen($Farray) == 0) {
12865
12866				$oddhtml = '';
12867				$evenhtml = '';
12868
12869			} elseif (strpos($Farray, '|') !== false) {
12870
12871				$hdet = explode('|', $Farray);
12872				$oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';
12873
12874				if ($this->defaultfooterfontsize) {
12875					$oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
12876				}
12877
12878				if ($this->defaultfooterfontstyle) {
12879					if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
12880						$oddhtml .= ' font-weight: bold;';
12881					}
12882					if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
12883						$oddhtml .= ' font-style: italic;';
12884					}
12885				}
12886
12887				if ($this->defaultfooterline) {
12888					$oddhtml .= ' border-top: 0.1mm solid #000000;';
12889				}
12890
12891				$oddhtml .= '">';
12892				$oddhtml .= '<tr>';
12893				$oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[0] . '</td>';
12894				$oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>';
12895				$oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[2] . '</td>';
12896				$oddhtml .= '</tr></table>';
12897
12898				$evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';
12899
12900				if ($this->defaultfooterfontsize) {
12901					$evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
12902				}
12903
12904				if ($this->defaultfooterfontstyle) {
12905
12906					if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
12907						$evenhtml .= ' font-weight: bold;';
12908					}
12909
12910					if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
12911						$evenhtml .= ' font-style: italic;';
12912					}
12913				}
12914
12915				if ($this->defaultfooterline) {
12916					$evenhtml .= ' border-top: 0.1mm solid #000000;';
12917				}
12918
12919				$evenhtml .= '">';
12920				$evenhtml .= '<tr>';
12921				$evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[2] . '</td>';
12922				$evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>';
12923				$evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[0] . '</td>';
12924				$evenhtml .= '</tr></table>';
12925
12926			} else {
12927
12928				$oddhtml = '<div style="margin: 0; color: #000000; ';
12929
12930				if ($this->defaultfooterfontsize) {
12931					$oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
12932				}
12933
12934				if ($this->defaultfooterfontstyle) {
12935
12936					if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
12937						$oddhtml .= ' font-weight: bold;';
12938					}
12939
12940					if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
12941						$oddhtml .= ' font-style: italic;';
12942					}
12943				}
12944
12945				if ($this->defaultfooterline) {
12946					$oddhtml .= ' border-top: 0.1mm solid #000000;';
12947				}
12948
12949				$oddhtml .= 'text-align: right; ">' . $Farray . '</div>';
12950
12951				$evenhtml = '<div style="margin: 0; color: #000000; ';
12952
12953				if ($this->defaultfooterfontsize) {
12954					$evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
12955				}
12956
12957				if ($this->defaultfooterfontstyle) {
12958
12959					if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
12960						$evenhtml .= ' font-weight: bold;';
12961					}
12962
12963					if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
12964						$evenhtml .= ' font-style: italic;';
12965					}
12966				}
12967
12968				if ($this->defaultfooterline) {
12969					$evenhtml .= ' border-top: 0.1mm solid #000000;';
12970				}
12971
12972				$evenhtml .= 'text-align: left; ">' . $Farray . '</div>';
12973			}
12974
12975		} elseif (is_array($Farray)) {
12976
12977			$odd = null;
12978			$even = null;
12979
12980			if ($side === 'O') {
12981				$odd = $Farray;
12982			} elseif ($side == 'E') {
12983				$even = $Farray;
12984			} else {
12985				$odd = Arrays::get($Farray, 'odd', null);
12986				$even = Arrays::get($Farray, 'even', null);
12987			}
12988
12989			$oddhtml = $this->_createHTMLheaderFooter($odd, 'F');
12990			$evenhtml = $this->_createHTMLheaderFooter($even, 'F');
12991		}
12992
12993		if ($side === 'E') {
12994			$this->SetHTMLFooter($evenhtml, 'E');
12995		} elseif ($side === 'O') {
12996			$this->SetHTMLFooter($oddhtml, 'O');
12997		} else {
12998			$this->SetHTMLFooter($oddhtml, 'O');
12999			$this->SetHTMLFooter($evenhtml, 'E');
13000		}
13001	}
13002
13003	/* -- WATERMARK -- */
13004
13005	function SetWatermarkText($txt = '', $alpha = -1)
13006	{
13007		if ($alpha >= 0) {
13008			$this->watermarkTextAlpha = $alpha;
13009		}
13010		$this->watermarkText = $txt;
13011	}
13012
13013	function SetWatermarkImage($src, $alpha = -1, $size = 'D', $pos = 'F')
13014	{
13015		if ($alpha >= 0) {
13016			$this->watermarkImageAlpha = $alpha;
13017		}
13018		$this->watermarkImage = $src;
13019		$this->watermark_size = $size;
13020		$this->watermark_pos = $pos;
13021	}
13022
13023	/* -- END WATERMARK -- */
13024
13025	// Page footer
13026	function Footer()
13027	{
13028		/* -- CSS-PAGE -- */
13029		// PAGED MEDIA - CROP / CROSS MARKS from @PAGE
13030		if ($this->show_marks == 'CROP' || $this->show_marks == 'CROPCROSS') {
13031			// Show TICK MARKS
13032			$this->SetLineWidth(0.1); // = 0.1 mm
13033			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
13034			$l = $this->cropMarkLength;
13035			$m = $this->cropMarkMargin; // Distance of crop mark from margin
13036			$b = $this->nonPrintMargin; // Non-printable border at edge of paper sheet
13037			$ax1 = $b;
13038			$bx = $this->page_box['outer_width_LR'] - $m;
13039			$ax = max($ax1, $bx - $l);
13040			$cx1 = $this->w - $b;
13041			$dx = $this->w - $this->page_box['outer_width_LR'] + $m;
13042			$cx = min($cx1, $dx + $l);
13043			$ay1 = $b;
13044			$by = $this->page_box['outer_width_TB'] - $m;
13045			$ay = max($ay1, $by - $l);
13046			$cy1 = $this->h - $b;
13047			$dy = $this->h - $this->page_box['outer_width_TB'] + $m;
13048			$cy = min($cy1, $dy + $l);
13049
13050			$this->Line($ax, $this->page_box['outer_width_TB'], $bx, $this->page_box['outer_width_TB']);
13051			$this->Line($cx, $this->page_box['outer_width_TB'], $dx, $this->page_box['outer_width_TB']);
13052			$this->Line($ax, $this->h - $this->page_box['outer_width_TB'], $bx, $this->h - $this->page_box['outer_width_TB']);
13053			$this->Line($cx, $this->h - $this->page_box['outer_width_TB'], $dx, $this->h - $this->page_box['outer_width_TB']);
13054			$this->Line($this->page_box['outer_width_LR'], $ay, $this->page_box['outer_width_LR'], $by);
13055			$this->Line($this->page_box['outer_width_LR'], $cy, $this->page_box['outer_width_LR'], $dy);
13056			$this->Line($this->w - $this->page_box['outer_width_LR'], $ay, $this->w - $this->page_box['outer_width_LR'], $by);
13057			$this->Line($this->w - $this->page_box['outer_width_LR'], $cy, $this->w - $this->page_box['outer_width_LR'], $dy);
13058
13059			if ($this->printers_info) {
13060				$hd = date('Y-m-d H:i') . '  Page ' . $this->page . ' of {nb}';
13061				$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
13062				$this->SetFont('arial', '', 7.5, true, true);
13063				$this->x = $this->page_box['outer_width_LR'] + 1.5;
13064				$this->y = 1;
13065				$this->Cell(0, $this->FontSize, $hd, 0, 0, 'L', 0, '', 0, 0, 0, 'M');
13066				$this->SetFont($this->default_font, '', $this->original_default_font_size);
13067			}
13068		}
13069		if ($this->show_marks == 'CROSS' || $this->show_marks == 'CROPCROSS') {
13070			$this->SetLineWidth(0.1); // = 0.1 mm
13071			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
13072			$l = 14 / 2; // longer length of the cross line (half)
13073			$w = 6 / 2; // shorter width of the cross line (half)
13074			$r = 1.2; // radius of circle
13075			$m = $this->crossMarkMargin; // Distance of cross mark from margin
13076			$x1 = $this->page_box['outer_width_LR'] - $m;
13077			$x2 = $this->w - $this->page_box['outer_width_LR'] + $m;
13078			$y1 = $this->page_box['outer_width_TB'] - $m;
13079			$y2 = $this->h - $this->page_box['outer_width_TB'] + $m;
13080			// Left
13081			$this->Circle($x1, $this->h / 2, $r, 'S');
13082			$this->Line($x1 - $w, $this->h / 2, $x1 + $w, $this->h / 2);
13083			$this->Line($x1, $this->h / 2 - $l, $x1, $this->h / 2 + $l);
13084			// Right
13085			$this->Circle($x2, $this->h / 2, $r, 'S');
13086			$this->Line($x2 - $w, $this->h / 2, $x2 + $w, $this->h / 2);
13087			$this->Line($x2, $this->h / 2 - $l, $x2, $this->h / 2 + $l);
13088			// Top
13089			$this->Circle($this->w / 2, $y1, $r, 'S');
13090			$this->Line($this->w / 2, $y1 - $w, $this->w / 2, $y1 + $w);
13091			$this->Line($this->w / 2 - $l, $y1, $this->w / 2 + $l, $y1);
13092			// Bottom
13093			$this->Circle($this->w / 2, $y2, $r, 'S');
13094			$this->Line($this->w / 2, $y2 - $w, $this->w / 2, $y2 + $w);
13095			$this->Line($this->w / 2 - $l, $y2, $this->w / 2 + $l, $y2);
13096		}
13097
13098		/* -- END CSS-PAGE -- */
13099
13100		// mPDF 6
13101		// If @page set non-HTML headers/footers named, they were not read until later in the HTML code - so now set them
13102		if ($this->page == 1) {
13103			if ($this->firstPageBoxHeader) {
13104				if (isset($this->pageHTMLheaders[$this->firstPageBoxHeader])) {
13105					$this->HTMLHeader = $this->pageHTMLheaders[$this->firstPageBoxHeader];
13106				}
13107				$this->Header();
13108			}
13109			if ($this->firstPageBoxFooter) {
13110				if (isset($this->pageHTMLfooters[$this->firstPageBoxFooter])) {
13111					$this->HTMLFooter = $this->pageHTMLfooters[$this->firstPageBoxFooter];
13112				}
13113			}
13114			$this->firstPageBoxHeader = '';
13115			$this->firstPageBoxFooter = '';
13116		}
13117
13118
13119		if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLFooterE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLFooter) || (!$this->mirrorMargins && $this->HTMLFooter)) {
13120			$this->writeHTMLFooters();
13121		}
13122
13123		/* -- WATERMARK -- */
13124		if (($this->watermarkText) && ($this->showWatermarkText)) {
13125			$this->watermark($this->watermarkText, $this->watermarkAngle, 120, $this->watermarkTextAlpha); // Watermark text
13126		}
13127		if (($this->watermarkImage) && ($this->showWatermarkImage)) {
13128			$this->watermarkImg($this->watermarkImage, $this->watermarkImageAlpha); // Watermark image
13129		}
13130		/* -- END WATERMARK -- */
13131	}
13132
13133	/* -- HTML-CSS -- */
13134
13135	/**
13136	 * Write HTML code to the document
13137	 *
13138	 * Also used internally to parse HTML into buffers
13139	 *
13140	 * @param string $html
13141	 * @param int    $mode  Use HTMLParserMode constants. Controls what parts of the $html code is parsed.
13142	 * @param bool   $init  Clears and sets buffers to Top level block etc.
13143	 * @param bool   $close If false leaves buffers etc. in current state, so that it can continue a block etc.
13144	 */
13145	function WriteHTML($html, $mode = HTMLParserMode::DEFAULT_MODE, $init = true, $close = true)
13146	{
13147		/* Check $html is an integer, float, string, boolean or class with __toString(), otherwise throw exception */
13148		if (is_scalar($html) === false) {
13149			if (!is_object($html) || ! method_exists($html, '__toString')) {
13150				throw new \Mpdf\MpdfException('WriteHTML() requires $html be an integer, float, string, boolean or an object with the __toString() magic method.');
13151			}
13152		}
13153
13154		// Check the mode is valid
13155		if (in_array($mode, HTMLParserMode::getAllModes(), true) === false) {
13156			throw new \Mpdf\MpdfException('WriteHTML() requires $mode to be one of the modes defined in HTMLParserMode');
13157		}
13158
13159		/* Cast $html as a string */
13160		$html = (string) $html;
13161
13162		// @log Parsing CSS & Headers
13163
13164		if ($init) {
13165			$this->headerbuffer = '';
13166			$this->textbuffer = [];
13167			$this->fixedPosBlockSave = [];
13168		}
13169		if ($mode === HTMLParserMode::HEADER_CSS) {
13170			$html = '<style> ' . $html . ' </style>';
13171		} // stylesheet only
13172
13173		if ($this->allow_charset_conversion) {
13174			if ($mode === HTMLParserMode::DEFAULT_MODE) {
13175				$this->ReadCharset($html);
13176			}
13177			if ($this->charset_in && $mode !== HTMLParserMode::HTML_HEADER_BUFFER) {
13178				$success = iconv($this->charset_in, 'UTF-8//TRANSLIT', $html);
13179				if ($success) {
13180					$html = $success;
13181				}
13182			}
13183		}
13184
13185		$html = $this->purify_utf8($html, false);
13186		if ($init) {
13187			$this->blklvl = 0;
13188			$this->lastblocklevelchange = 0;
13189			$this->blk = [];
13190			$this->initialiseBlock($this->blk[0]);
13191			$this->blk[0]['width'] = & $this->pgwidth;
13192			$this->blk[0]['inner_width'] = & $this->pgwidth;
13193			$this->blk[0]['blockContext'] = $this->blockContext;
13194		}
13195
13196		$zproperties = [];
13197		if ($mode === HTMLParserMode::DEFAULT_MODE || $mode === HTMLParserMode::HEADER_CSS) {
13198			$this->ReadMetaTags($html);
13199
13200			if (preg_match('/<base[^>]*href=["\']([^"\'>]*)["\']/i', $html, $m)) {
13201				$this->SetBasePath($m[1]);
13202			}
13203			$html = $this->cssManager->ReadCSS($html);
13204
13205			if ($this->autoLangToFont && !$this->usingCoreFont && preg_match('/<html [^>]*lang=[\'\"](.*?)[\'\"]/ism', $html, $m)) {
13206				$html_lang = $m[1];
13207			}
13208
13209			if (preg_match('/<html [^>]*dir=[\'\"]\s*rtl\s*[\'\"]/ism', $html)) {
13210				$zproperties['DIRECTION'] = 'rtl';
13211			}
13212
13213			// allow in-line CSS for body tag to be parsed // Get <body> tag inline CSS
13214			if (preg_match('/<body([^>]*)>(.*?)<\/body>/ism', $html, $m) || preg_match('/<body([^>]*)>(.*)$/ism', $html, $m)) {
13215				$html = $m[2];
13216				// Changed to allow style="background: url('bg.jpg')"
13217				if (preg_match('/style=[\"](.*?)[\"]/ism', $m[1], $mm) || preg_match('/style=[\'](.*?)[\']/ism', $m[1], $mm)) {
13218					$zproperties = $this->cssManager->readInlineCSS($mm[1]);
13219				}
13220				if (preg_match('/dir=[\'\"]\s*rtl\s*[\'\"]/ism', $m[1])) {
13221					$zproperties['DIRECTION'] = 'rtl';
13222				}
13223				if (isset($html_lang) && $html_lang) {
13224					$zproperties['LANG'] = $html_lang;
13225				}
13226				if ($this->autoLangToFont && !$this->onlyCoreFonts && preg_match('/lang=[\'\"](.*?)[\'\"]/ism', $m[1], $mm)) {
13227					$zproperties['LANG'] = $mm[1];
13228				}
13229			}
13230		}
13231		$properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
13232		if ($zproperties) {
13233			$properties = $this->cssManager->array_merge_recursive_unique($properties, $zproperties);
13234		}
13235
13236		if (isset($properties['DIRECTION']) && $properties['DIRECTION']) {
13237			$this->cssManager->CSS['BODY']['DIRECTION'] = $properties['DIRECTION'];
13238		}
13239		if (!isset($this->cssManager->CSS['BODY']['DIRECTION'])) {
13240			$this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality;
13241		} else {
13242			$this->SetDirectionality($this->cssManager->CSS['BODY']['DIRECTION']);
13243		}
13244
13245		$this->setCSS($properties, '', 'BODY');
13246
13247		$this->blk[0]['InlineProperties'] = $this->saveInlineProperties();
13248
13249		if ($mode === HTMLParserMode::HEADER_CSS) {
13250			return '';
13251		}
13252		if (!isset($this->cssManager->CSS['BODY'])) {
13253			$this->cssManager->CSS['BODY'] = [];
13254		}
13255
13256		/* -- BACKGROUNDS -- */
13257		if (isset($properties['BACKGROUND-GRADIENT'])) {
13258			$this->bodyBackgroundGradient = $properties['BACKGROUND-GRADIENT'];
13259		}
13260
13261		if (isset($properties['BACKGROUND-IMAGE']) && $properties['BACKGROUND-IMAGE']) {
13262			$ret = $this->SetBackground($properties, $this->pgwidth);
13263			if ($ret) {
13264				$this->bodyBackgroundImage = $ret;
13265			}
13266		}
13267		/* -- END BACKGROUNDS -- */
13268
13269		/* -- CSS-PAGE -- */
13270		// If page-box is set
13271		if ($this->state == 0 && ((isset($this->cssManager->CSS['@PAGE']) && $this->cssManager->CSS['@PAGE']) || (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']) && $this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']))) { // mPDF 5.7.3
13272			$this->page_box['current'] = '';
13273			$this->page_box['using'] = true;
13274			list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', false, 'O');
13275			$this->DefOrientation = $this->CurOrientation = $pborientation;
13276			$this->orig_lMargin = $this->DeflMargin = $pbmgl;
13277			$this->orig_rMargin = $this->DefrMargin = $pbmgr;
13278			$this->orig_tMargin = $this->tMargin = $pbmgt;
13279			$this->orig_bMargin = $this->bMargin = $pbmgb;
13280			$this->orig_hMargin = $this->margin_header = $pbmgh;
13281			$this->orig_fMargin = $this->margin_footer = $pbmgf;
13282			list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', true, 'O'); // first page
13283			$this->show_marks = $marks;
13284			if ($hname) {
13285				$this->firstPageBoxHeader = $hname;
13286			}
13287			if ($fname) {
13288				$this->firstPageBoxFooter = $fname;
13289			}
13290		}
13291		/* -- END CSS-PAGE -- */
13292
13293		$parseonly = false;
13294		$this->bufferoutput = false;
13295		if ($mode == HTMLParserMode::HTML_PARSE_NO_WRITE) {
13296			$parseonly = true;
13297			// Close any open block tags
13298			$arr = [];
13299			$ai = 0;
13300			for ($b = $this->blklvl; $b > 0; $b--) {
13301				$this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
13302			}
13303			// Output any text left in buffer
13304			if (count($this->textbuffer)) {
13305				$this->printbuffer($this->textbuffer);
13306			}
13307			$this->textbuffer = [];
13308		} elseif ($mode === HTMLParserMode::HTML_HEADER_BUFFER) {
13309			// Close any open block tags
13310			$arr = [];
13311			$ai = 0;
13312			for ($b = $this->blklvl; $b > 0; $b--) {
13313				$this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
13314			}
13315			// Output any text left in buffer
13316			if (count($this->textbuffer)) {
13317				$this->printbuffer($this->textbuffer);
13318			}
13319			$this->bufferoutput = true;
13320			$this->textbuffer = [];
13321			$this->headerbuffer = '';
13322			$properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
13323			$this->setCSS($properties, '', 'BODY');
13324		}
13325
13326		mb_internal_encoding('UTF-8');
13327
13328		$html = $this->AdjustHTML($html, $this->tabSpaces); // Try to make HTML look more like XHTML
13329
13330		if ($this->autoScriptToLang) {
13331			$html = $this->markScriptToLang($html);
13332		}
13333
13334		preg_match_all('/<htmlpageheader([^>]*)>(.*?)<\/htmlpageheader>/si', $html, $h);
13335		for ($i = 0; $i < count($h[1]); $i++) {
13336			if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $h[1][$i], $n)) {
13337				$this->pageHTMLheaders[$n[1]]['html'] = $h[2][$i];
13338				$this->pageHTMLheaders[$n[1]]['h'] = $this->_getHtmlHeight($h[2][$i]);
13339			}
13340		}
13341		preg_match_all('/<htmlpagefooter([^>]*)>(.*?)<\/htmlpagefooter>/si', $html, $f);
13342		for ($i = 0; $i < count($f[1]); $i++) {
13343			if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $f[1][$i], $n)) {
13344				$this->pageHTMLfooters[$n[1]]['html'] = $f[2][$i];
13345				$this->pageHTMLfooters[$n[1]]['h'] = $this->_getHtmlHeight($f[2][$i]);
13346			}
13347		}
13348
13349		$html = preg_replace('/<htmlpageheader.*?<\/htmlpageheader>/si', '', $html);
13350		$html = preg_replace('/<htmlpagefooter.*?<\/htmlpagefooter>/si', '', $html);
13351
13352		if ($this->state == 0 && ($mode === HTMLParserMode::DEFAULT_MODE || $mode === HTMLParserMode::HTML_BODY)) {
13353			$this->AddPage($this->CurOrientation);
13354		}
13355
13356
13357		if (isset($hname) && preg_match('/^html_(.*)$/i', $hname, $n)) {
13358			$this->SetHTMLHeader($this->pageHTMLheaders[$n[1]], 'O', true);
13359		}
13360		if (isset($fname) && preg_match('/^html_(.*)$/i', $fname, $n)) {
13361			$this->SetHTMLFooter($this->pageHTMLfooters[$n[1]], 'O');
13362		}
13363
13364
13365
13366		$html = str_replace('<?', '< ', $html); // Fix '<?XML' bug from HTML code generated by MS Word
13367
13368		$this->checkSIP = false;
13369		$this->checkSMP = false;
13370		$this->checkCJK = false;
13371		if ($this->onlyCoreFonts) {
13372			$html = $this->SubstituteChars($html);
13373		} else {
13374			if (preg_match("/([" . $this->pregRTLchars . "])/u", $html)) {
13375				$this->biDirectional = true;
13376			} // *OTL*
13377			if (preg_match("/([\x{20000}-\x{2FFFF}])/u", $html)) {
13378				$this->checkSIP = true;
13379			}
13380			if (preg_match("/([\x{10000}-\x{1FFFF}])/u", $html)) {
13381				$this->checkSMP = true;
13382			}
13383			/* -- CJK-FONTS -- */
13384			if (preg_match("/([" . $this->pregCJKchars . "])/u", $html)) {
13385				$this->checkCJK = true;
13386			}
13387			/* -- END CJK-FONTS -- */
13388		}
13389
13390		// Don't allow non-breaking spaces that are converted to substituted chars or will break anyway and mess up table width calc.
13391		$html = str_replace('<tta>160</tta>', chr(32), $html);
13392		$html = str_replace('</tta><tta>', '|', $html);
13393		$html = str_replace('</tts><tts>', '|', $html);
13394		$html = str_replace('</ttz><ttz>', '|', $html);
13395
13396		// Add new supported tags in the DisableTags function
13397		$html = strip_tags($html, $this->enabledtags); // remove all unsupported tags, but the ones inside the 'enabledtags' string
13398		// Explode the string in order to parse the HTML code
13399		$a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
13400		// ? more accurate regexp that allows e.g. <a name="Silly <name>">
13401		// if changing - also change in fn.SubstituteChars()
13402		// $a = preg_split ('/<((?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
13403
13404		if ($this->mb_enc) {
13405			mb_internal_encoding($this->mb_enc);
13406		}
13407		$pbc = 0;
13408		$this->subPos = -1;
13409		$cnt = count($a);
13410		for ($i = 0; $i < $cnt; $i++) {
13411			$e = $a[$i];
13412			if ($i % 2 == 0) {
13413				// TEXT
13414				if ($this->blk[$this->blklvl]['hide']) {
13415					continue;
13416				}
13417				if ($this->inlineDisplayOff) {
13418					continue;
13419				}
13420				if ($this->inMeter) {
13421					continue;
13422				}
13423
13424				if ($this->inFixedPosBlock) {
13425					$this->fixedPosBlock .= $e;
13426					continue;
13427				} // *CSS-POSITION*
13428				if (strlen($e) == 0) {
13429					continue;
13430				}
13431
13432				if ($this->ignorefollowingspaces && !$this->ispre) {
13433					if (strlen(ltrim($e)) == 0) {
13434						continue;
13435					}
13436					if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && substr($e, 0, 1) == ' ') {
13437						$this->ignorefollowingspaces = false;
13438						$e = ltrim($e);
13439					}
13440				}
13441
13442				$this->OTLdata = null;  // mPDF 5.7.1
13443
13444				$e = UtfString::strcode2utf($e);
13445				$e = $this->lesser_entity_decode($e);
13446
13447				if ($this->usingCoreFont) {
13448					// If core font is selected in document which is not onlyCoreFonts - substitute with non-core font
13449					if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->subPos < $i && !$this->specialcontent) {
13450						$cnt += $this->SubstituteCharsNonCore($a, $i, $e);
13451					}
13452					// CONVERT ENCODING
13453					$e = mb_convert_encoding($e, $this->mb_enc, 'UTF-8');
13454					if ($this->textvar & TextVars::FT_UPPERCASE) {
13455						$e = mb_strtoupper($e, $this->mb_enc);
13456					} // mPDF 5.7.1
13457					elseif ($this->textvar & TextVars::FT_LOWERCASE) {
13458						$e = mb_strtolower($e, $this->mb_enc);
13459					} // mPDF 5.7.1
13460					elseif ($this->textvar & TextVars::FT_CAPITALIZE) {
13461						$e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8");
13462					} // mPDF 5.7.1
13463				} else {
13464					if ($this->checkSIP && $this->CurrentFont['sipext'] && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {
13465						$cnt += $this->SubstituteCharsSIP($a, $i, $e);
13466					}
13467
13468					if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->CurrentFont['type'] != 'Type0' && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {
13469						$cnt += $this->SubstituteCharsMB($a, $i, $e);
13470					}
13471
13472					if ($this->textvar & TextVars::FT_UPPERCASE) {
13473						$e = mb_strtoupper($e, $this->mb_enc);
13474					} elseif ($this->textvar & TextVars::FT_LOWERCASE) {
13475						$e = mb_strtolower($e, $this->mb_enc);
13476					} elseif ($this->textvar & TextVars::FT_CAPITALIZE) {
13477						$e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8");
13478					}
13479
13480					/* -- OTL -- */
13481					// Use OTL OpenType Table Layout - GSUB & GPOS
13482					if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL'] && (!$this->specialcontent || !$this->useActiveForms)) {
13483						if (!$this->otl) {
13484							$this->otl = new Otl($this, $this->fontCache);
13485						}
13486						$e = $this->otl->applyOTL($e, $this->CurrentFont['useOTL']);
13487						$this->OTLdata = $this->otl->OTLdata;
13488						$this->otl->removeChar($e, $this->OTLdata, "\xef\xbb\xbf"); // Remove ZWNBSP (also Byte order mark FEFF)
13489					} /* -- END OTL -- */
13490					else {
13491						// removes U+200E/U+200F LTR and RTL mark and U+200C/U+200D Zero-width Joiner and Non-joiner
13492						$e = preg_replace("/[\xe2\x80\x8c\xe2\x80\x8d\xe2\x80\x8e\xe2\x80\x8f]/u", '', $e);
13493						$e = preg_replace("/[\xef\xbb\xbf]/u", '', $e); // Remove ZWNBSP (also Byte order mark FEFF)
13494					}
13495				}
13496
13497				if (($this->tts) || ($this->ttz) || ($this->tta)) {
13498					$es = explode('|', $e);
13499					$e = '';
13500					foreach ($es as $val) {
13501						$e .= chr($val);
13502					}
13503				}
13504
13505				//  FORM ELEMENTS
13506				if ($this->specialcontent) {
13507					/* -- FORMS -- */
13508					// SELECT tag (form element)
13509					if ($this->specialcontent == "type=select") {
13510						$e = ltrim($e);
13511						if (!empty($this->OTLdata)) {
13512							$this->otl->trimOTLdata($this->OTLdata, true, false);
13513						} // *OTL*
13514						$stringwidth = $this->GetStringWidth($e);
13515						if (!isset($this->selectoption['MAXWIDTH']) || $stringwidth > $this->selectoption['MAXWIDTH']) {
13516							$this->selectoption['MAXWIDTH'] = $stringwidth;
13517						}
13518						if (!isset($this->selectoption['SELECTED']) || $this->selectoption['SELECTED'] == '') {
13519							$this->selectoption['SELECTED'] = $e;
13520							if (!empty($this->OTLdata)) {
13521								$this->selectoption['SELECTED-OTLDATA'] = $this->OTLdata;
13522							} // *OTL*
13523						}
13524						// Active Forms
13525						if (isset($this->selectoption['ACTIVE']) && $this->selectoption['ACTIVE']) {
13526							$this->selectoption['ITEMS'][] = ['exportValue' => $this->selectoption['currentVAL'], 'content' => $e, 'selected' => $this->selectoption['currentSEL']];
13527						}
13528						$this->OTLdata = [];
13529					} // TEXTAREA
13530					else {
13531						$objattr = unserialize($this->specialcontent);
13532						$objattr['text'] = $e;
13533						$objattr['OTLdata'] = $this->OTLdata;
13534						$this->OTLdata = [];
13535						$te = "\xbb\xa4\xactype=textarea,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
13536						if ($this->tdbegin) {
13537							$this->_saveCellTextBuffer($te, $this->HREF);
13538						} else {
13539							$this->_saveTextBuffer($te, $this->HREF);
13540						}
13541					}
13542					/* -- END FORMS -- */
13543				} // TABLE
13544				elseif ($this->tableLevel) {
13545					/* -- TABLES -- */
13546					if ($this->tdbegin) {
13547						if (($this->ignorefollowingspaces) && !$this->ispre) {
13548							$e = ltrim($e);
13549							if (!empty($this->OTLdata)) {
13550								$this->otl->trimOTLdata($this->OTLdata, true, false);
13551							} // *OTL*
13552						}
13553						if ($e || $e === '0') {
13554							if ($this->blockjustfinished && $this->cell[$this->row][$this->col]['s'] > 0) {
13555								$this->_saveCellTextBuffer("\n");
13556								if (!isset($this->cell[$this->row][$this->col]['maxs'])) {
13557									$this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];
13558								} elseif ($this->cell[$this->row][$this->col]['maxs'] < $this->cell[$this->row][$this->col]['s']) {
13559									$this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];
13560								}
13561								$this->cell[$this->row][$this->col]['s'] = 0; // reset
13562							}
13563							$this->blockjustfinished = false;
13564
13565							if (!isset($this->cell[$this->row][$this->col]['R']) || !$this->cell[$this->row][$this->col]['R']) {
13566								if (isset($this->cell[$this->row][$this->col]['s'])) {
13567									$this->cell[$this->row][$this->col]['s'] += $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);
13568								} else {
13569									$this->cell[$this->row][$this->col]['s'] = $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);
13570								}
13571								if (!empty($this->spanborddet)) {
13572									$this->cell[$this->row][$this->col]['s'] += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0) + (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
13573								}
13574							}
13575
13576							$this->_saveCellTextBuffer($e, $this->HREF);
13577
13578							if (substr($this->cell[$this->row][$this->col]['a'], 0, 1) == 'D') {
13579
13580								$dp = $this->decimal_align[substr($this->cell[$this->row][$this->col]['a'], 0, 2)];
13581								$s = preg_split('/' . preg_quote($dp, '/') . '/', $e, 2);  // ? needs to be /u if not core
13582								$s0 = $this->GetStringWidth($s[0], false);
13583
13584								if (isset($s[1]) && $s[1]) {
13585									$s1 = $this->GetStringWidth(($s[1] . $dp), false);
13586								} else {
13587									$s1 = 0;
13588								}
13589
13590								if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'])) {
13591									if ($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'] === false) {
13592										$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'] = [];
13593									}
13594									$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = $s0;
13595								} else {
13596									$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = max($s0, $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0']);
13597								}
13598
13599								if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'])) {
13600									$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = $s1;
13601								} else {
13602									$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = max($s1, $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1']);
13603								}
13604							}
13605
13606							$this->nestedtablejustfinished = false;
13607							$this->linebreakjustfinished = false;
13608						}
13609					}
13610					/* -- END TABLES -- */
13611				} // ALL ELSE
13612				else {
13613					if ($this->ignorefollowingspaces && !$this->ispre) {
13614						$e = ltrim($e);
13615						if (!empty($this->OTLdata)) {
13616							$this->otl->trimOTLdata($this->OTLdata, true, false);
13617						} // *OTL*
13618					}
13619					if ($e || $e === '0') {
13620						$this->_saveTextBuffer($e, $this->HREF);
13621					}
13622				}
13623				if ($e || $e === '0') {
13624					$this->ignorefollowingspaces = false; // mPDF 6
13625				}
13626				if (substr($e, -1, 1) == ' ' && !$this->ispre && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
13627					$this->ignorefollowingspaces = true;
13628				}
13629			} else { // TAG **
13630				if (isset($e[0]) && $e[0] == '/') {
13631					$endtag = trim(strtoupper(substr($e, 1)));
13632
13633					/* -- CSS-POSITION -- */
13634					// mPDF 6
13635					if ($this->inFixedPosBlock) {
13636						if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {
13637							$this->fixedPosBlockDepth--;
13638						}
13639						if ($this->fixedPosBlockDepth == 0) {
13640							$this->fixedPosBlockSave[] = [$this->fixedPosBlock, $this->fixedPosBlockBBox, $this->page];
13641							$this->fixedPosBlock = '';
13642							$this->inFixedPosBlock = false;
13643							continue;
13644						}
13645						$this->fixedPosBlock .= '<' . $e . '>';
13646						continue;
13647					}
13648					/* -- END CSS-POSITION -- */
13649
13650					// mPDF 6
13651					// Correct for tags where HTML5 specifies optional end tags (see also OpenTag() )
13652					if ($this->allow_html_optional_endtags && !$parseonly) {
13653						if (isset($this->blk[$this->blklvl]['tag'])) {
13654							$closed = false;
13655							// li end tag may be omitted if there is no more content in the parent element
13656							if (!$closed && $this->blk[$this->blklvl]['tag'] == 'LI' && $endtag != 'LI' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
13657								$this->tag->CloseTag('LI', $a, $i);
13658								$closed = true;
13659							}
13660							// dd end tag may be omitted if there is no more content in the parent element
13661							if (!$closed && $this->blk[$this->blklvl]['tag'] == 'DD' && $endtag != 'DD' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
13662								$this->tag->CloseTag('DD', $a, $i);
13663								$closed = true;
13664							}
13665							// p end tag may be omitted if there is no more content in the parent element and the parent element is not an A element [??????]
13666							if (!$closed && $this->blk[$this->blklvl]['tag'] == 'P' && $endtag != 'P' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
13667								$this->tag->CloseTag('P', $a, $i);
13668								$closed = true;
13669							}
13670							// option end tag may be omitted if there is no more content in the parent element
13671							if (!$closed && $this->blk[$this->blklvl]['tag'] == 'OPTION' && $endtag != 'OPTION' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
13672								$this->tag->CloseTag('OPTION', $a, $i);
13673								$closed = true;
13674							}
13675						}
13676						/* -- TABLES -- */
13677						// Check for Table tags where HTML specifies optional end tags,
13678						if ($endtag == 'TABLE') {
13679							if ($this->lastoptionaltag == 'THEAD' || $this->lastoptionaltag == 'TBODY' || $this->lastoptionaltag == 'TFOOT') {
13680								$this->tag->CloseTag($this->lastoptionaltag, $a, $i);
13681							}
13682							if ($this->lastoptionaltag == 'TR') {
13683								$this->tag->CloseTag('TR', $a, $i);
13684							}
13685							if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
13686								$this->tag->CloseTag($this->lastoptionaltag, $a, $i);
13687								$this->tag->CloseTag('TR', $a, $i);
13688							}
13689						}
13690						if ($endtag == 'THEAD' || $endtag == 'TBODY' || $endtag == 'TFOOT') {
13691							if ($this->lastoptionaltag == 'TR') {
13692								$this->tag->CloseTag('TR', $a, $i);
13693							}
13694							if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
13695								$this->tag->CloseTag($this->lastoptionaltag, $a, $i);
13696								$this->tag->CloseTag('TR', $a, $i);
13697							}
13698						}
13699						if ($endtag == 'TR') {
13700							if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
13701								$this->tag->CloseTag($this->lastoptionaltag, $a, $i);
13702							}
13703						}
13704						/* -- END TABLES -- */
13705					}
13706
13707
13708					// mPDF 6
13709					if ($this->blk[$this->blklvl]['hide']) {
13710						if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {
13711							unset($this->blk[$this->blklvl]);
13712							$this->blklvl--;
13713						}
13714						continue;
13715					}
13716
13717					// mPDF 6
13718					$this->tag->CloseTag($endtag, $a, $i); // mPDF 6
13719				} else { // OPENING TAG
13720					if ($this->blk[$this->blklvl]['hide']) {
13721						if (strpos($e, ' ')) {
13722							$te = strtoupper(substr($e, 0, strpos($e, ' ')));
13723						} else {
13724							$te = strtoupper($e);
13725						}
13726						// mPDF 6
13727						if ($te == 'THEAD' || $te == 'TBODY' || $te == 'TFOOT' || $te == 'TR' || $te == 'TD' || $te == 'TH') {
13728							$this->lastoptionaltag = $te;
13729						}
13730						if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {
13731							$this->blklvl++;
13732							$this->blk[$this->blklvl]['hide'] = true;
13733							$this->blk[$this->blklvl]['tag'] = $te; // mPDF 6
13734						}
13735						continue;
13736					}
13737
13738					/* -- CSS-POSITION -- */
13739					if ($this->inFixedPosBlock) {
13740						if (strpos($e, ' ')) {
13741							$te = strtoupper(substr($e, 0, strpos($e, ' ')));
13742						} else {
13743							$te = strtoupper($e);
13744						}
13745						$this->fixedPosBlock .= '<' . $e . '>';
13746						if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {
13747							$this->fixedPosBlockDepth++;
13748						}
13749						continue;
13750					}
13751					/* -- END CSS-POSITION -- */
13752					$regexp = '|=\'(.*?)\'|s'; // eliminate single quotes, if any
13753					$e = preg_replace($regexp, "=\"\$1\"", $e);
13754					// changes anykey=anyvalue to anykey="anyvalue" (only do this inside [some] tags)
13755					if (substr($e, 0, 10) != 'pageheader' && substr($e, 0, 10) != 'pagefooter' && substr($e, 0, 12) != 'tocpagebreak' && substr($e, 0, 10) != 'indexentry' && substr($e, 0, 8) != 'tocentry') { // mPDF 6  (ZZZ99H)
13756						$regexp = '| (\\w+?)=([^\\s>"]+)|si';
13757						$e = preg_replace($regexp, " \$1=\"\$2\"", $e);
13758					}
13759
13760					$e = preg_replace('/ (\\S+?)\s*=\s*"/i', " \\1=\"", $e);
13761
13762					// Fix path values, if needed
13763					$orig_srcpath = '';
13764					if ((stristr($e, "href=") !== false) or ( stristr($e, "src=") !== false)) {
13765						$regexp = '/ (href|src)\s*=\s*"(.*?)"/i';
13766						preg_match($regexp, $e, $auxiliararray);
13767						if (isset($auxiliararray[2])) {
13768							$path = $auxiliararray[2];
13769						} else {
13770							$path = '';
13771						}
13772						if (trim($path) != '' && !(stristr($e, "src=") !== false && substr($path, 0, 4) == 'var:') && substr($path, 0, 1) != '@') {
13773							$path = htmlspecialchars_decode($path); // mPDF 5.7.4 URLs
13774							$orig_srcpath = $path;
13775							$this->GetFullPath($path);
13776							$regexp = '/ (href|src)="(.*?)"/i';
13777							$e = preg_replace($regexp, ' \\1="' . $path . '"', $e);
13778						}
13779					}//END of Fix path values
13780					// Extract attributes
13781					$contents = [];
13782					$contents1 = [];
13783					$contents2 = [];
13784					// Changed to allow style="background: url('bg.jpg')"
13785					// Changed to improve performance; maximum length of \S (attribute) = 16
13786					// Increase allowed attribute name to 32 - cutting off "toc-even-header-name" etc.
13787					preg_match_all('/\\S{1,32}=["][^"]*["]/', $e, $contents1);
13788					preg_match_all('/\\S{1,32}=[\'][^\']*[\']/i', $e, $contents2);
13789
13790					$contents = array_merge($contents1, $contents2);
13791					preg_match('/\\S+/', $e, $a2);
13792					$tag = (isset($a2[0]) ? strtoupper($a2[0]) : '');
13793					$attr = [];
13794					if ($orig_srcpath) {
13795						$attr['ORIG_SRC'] = $orig_srcpath;
13796					}
13797					if (!empty($contents)) {
13798						foreach ($contents[0] as $v) {
13799							// Changed to allow style="background: url('bg.jpg')"
13800							if (preg_match('/^([^=]*)=["]?([^"]*)["]?$/', $v, $a3) || preg_match('/^([^=]*)=[\']?([^\']*)[\']?$/', $v, $a3)) {
13801								if (strtoupper($a3[1]) == 'ID' || strtoupper($a3[1]) == 'CLASS') { // 4.2.013 Omits STYLE
13802									$attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));
13803								} // includes header-style-right etc. used for <pageheader>
13804								elseif (preg_match('/^(HEADER|FOOTER)-STYLE/i', $a3[1])) {
13805									$attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));
13806								} else {
13807									$attr[strtoupper($a3[1])] = trim($a3[2]);
13808								}
13809							}
13810						}
13811					}
13812					$this->tag->OpenTag($tag, $attr, $a, $i); // mPDF 6
13813					/* -- CSS-POSITION -- */
13814					if ($this->inFixedPosBlock) {
13815						$this->fixedPosBlockBBox = [$tag, $attr, $this->x, $this->y];
13816						$this->fixedPosBlock = '';
13817						$this->fixedPosBlockDepth = 1;
13818					}
13819					/* -- END CSS-POSITION -- */
13820					if (preg_match('/\/$/', $e)) {
13821						$this->tag->CloseTag($tag, $a, $i);
13822					}
13823				}
13824			} // end TAG
13825		} // end of	foreach($a as $i=>$e)
13826
13827		if ($close) {
13828			// Close any open block tags
13829			for ($b = $this->blklvl; $b > 0; $b--) {
13830				$this->tag->CloseTag($this->blk[$b]['tag'], $a, $i);
13831			}
13832
13833			// Output any text left in buffer
13834			if (count($this->textbuffer) && !$parseonly) {
13835				$this->printbuffer($this->textbuffer);
13836			}
13837			if (!$parseonly) {
13838				$this->textbuffer = [];
13839			}
13840
13841			/* -- CSS-FLOAT -- */
13842			// If ended with a float, need to move to end page
13843			$currpos = $this->page * 1000 + $this->y;
13844			if (isset($this->blk[$this->blklvl]['float_endpos']) && $this->blk[$this->blklvl]['float_endpos'] > $currpos) {
13845				$old_page = $this->page;
13846				$new_page = intval($this->blk[$this->blklvl]['float_endpos'] / 1000);
13847				if ($old_page != $new_page) {
13848					$s = $this->PrintPageBackgrounds();
13849					// Writes after the marker so not overwritten later by page background etc.
13850					$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
13851					$this->pageBackgrounds = [];
13852					$this->page = $new_page;
13853					$this->ResetMargins();
13854					$this->Reset();
13855					$this->pageoutput[$this->page] = [];
13856				}
13857				$this->y = (round($this->blk[$this->blklvl]['float_endpos'] * 1000) % 1000000) / 1000; // mod changes operands to integers before processing
13858			}
13859			/* -- END CSS-FLOAT -- */
13860
13861			/* -- CSS-IMAGE-FLOAT -- */
13862			$this->printfloatbuffer();
13863			/* -- END CSS-IMAGE-FLOAT -- */
13864
13865			// Create Internal Links, if needed
13866			if (!empty($this->internallink)) {
13867
13868				foreach ($this->internallink as $k => $v) {
13869
13870					if (strpos($k, "#") !== false) {
13871						continue;
13872					}
13873
13874					if (!is_array($v)) {
13875						continue;
13876					}
13877
13878					$ypos = $v['Y'];
13879					$pagenum = $v['PAGE'];
13880					$sharp = "#";
13881
13882					while (array_key_exists($sharp . $k, $this->internallink)) {
13883						$internallink = $this->internallink[$sharp . $k];
13884						$this->SetLink($internallink, $ypos, $pagenum);
13885						$sharp .= "#";
13886					}
13887				}
13888			}
13889
13890			$this->bufferoutput = false;
13891
13892			/* -- CSS-POSITION -- */
13893			if (count($this->fixedPosBlockSave)) {
13894				foreach ($this->fixedPosBlockSave as $fpbs) {
13895					$old_page = $this->page;
13896					$this->page = $fpbs[2];
13897					$this->WriteFixedPosHTML($fpbs[0], 0, 0, 100, 100, 'auto', $fpbs[1]);  // 0,0,10,10 are overwritten by bbox
13898					$this->page = $old_page;
13899				}
13900				$this->fixedPosBlockSave = [];
13901			}
13902			/* -- END CSS-POSITION -- */
13903		}
13904	}
13905
13906	/* -- CSS-POSITION -- */
13907
13908	function WriteFixedPosHTML($html, $x, $y, $w, $h, $overflow = 'visible', $bounding = [])
13909	{
13910		// $overflow can be 'hidden', 'visible' or 'auto' - 'auto' causes autofit to size
13911		// Annotations disabled - enabled in mPDF 5.0
13912		// Links do work
13913		// Will always go on current page (or start Page 1 if required)
13914		// Probably INCOMPATIBLE WITH keep with table, columns etc.
13915		// Called externally or interally via <div style="position: [fixed|absolute]">
13916		// When used internally, $x $y $w $h and $overflow are all overridden by $bounding
13917
13918		$overflow = strtolower($overflow);
13919		if ($this->state == 0) {
13920			$this->AddPage($this->CurOrientation);
13921		}
13922		$save_y = $this->y;
13923		$save_x = $this->x;
13924		$this->fullImageHeight = $this->h;
13925		$save_cols = false;
13926		/* -- COLUMNS -- */
13927		if ($this->ColActive) {
13928			$save_cols = true;
13929			$save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off
13930			$this->SetColumns(0);
13931		}
13932		/* -- END COLUMNS -- */
13933		$save_annots = $this->title2annots; // *ANNOTATIONS*
13934		$this->writingHTMLheader = true; // a FIX to stop pagebreaks etc.
13935		$this->writingHTMLfooter = true;
13936		$this->InFooter = true; // suppresses autopagebreaks
13937		$save_bgs = $this->pageBackgrounds;
13938		$checkinnerhtml = preg_replace('/\s/', '', $html);
13939		$rotate = 0;
13940
13941		if ($w > $this->w) {
13942			$x = 0;
13943			$w = $this->w;
13944		}
13945		if ($h > $this->h) {
13946			$y = 0;
13947			$h = $this->h;
13948		}
13949		if ($x > $this->w) {
13950			$x = $this->w - $w;
13951		}
13952		if ($y > $this->h) {
13953			$y = $this->h - $h;
13954		}
13955
13956		if (!empty($bounding)) {
13957			// $cont_ containing block = full physical page (position: absolute) or page inside margins (position: fixed)
13958			// $bbox_ Bounding box is the <div> which is positioned absolutely/fixed
13959			// top/left/right/bottom/width/height/background*/border*/padding*/margin* are taken from bounding
13960			// font*[family/size/style/weight]/line-height/text*[align/decoration/transform/indent]/color are transferred to $inner
13961			// as an enclosing <div> (after having checked ID/CLASS)
13962			// $x, $y, $w, $h are inside of $bbox_ = containing box for $inner_
13963			// $inner_ InnerHTML is the contents of that block to be output
13964			$tag = $bounding[0];
13965			$attr = $bounding[1];
13966			$orig_x0 = $bounding[2];
13967			$orig_y0 = $bounding[3];
13968
13969			// As in WriteHTML() initialising
13970			$this->blklvl = 0;
13971			$this->lastblocklevelchange = 0;
13972			$this->blk = [];
13973			$this->initialiseBlock($this->blk[0]);
13974
13975			$this->blk[0]['width'] = & $this->pgwidth;
13976			$this->blk[0]['inner_width'] = & $this->pgwidth;
13977
13978			$this->blk[0]['blockContext'] = $this->blockContext;
13979
13980			$properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
13981			$this->setCSS($properties, '', 'BODY');
13982			$this->blklvl = 1;
13983			$this->initialiseBlock($this->blk[1]);
13984			$this->blk[1]['tag'] = $tag;
13985			$this->blk[1]['attr'] = $attr;
13986			$this->Reset();
13987			$p = $this->cssManager->MergeCSS('BLOCK', $tag, $attr);
13988			if (isset($p['ROTATE']) && ($p['ROTATE'] == 90 || $p['ROTATE'] == -90 || $p['ROTATE'] == 180)) {
13989				$rotate = $p['ROTATE'];
13990			} // mPDF 6
13991			if (isset($p['OVERFLOW'])) {
13992				$overflow = strtolower($p['OVERFLOW']);
13993			}
13994			if (strtolower($p['POSITION']) == 'fixed') {
13995				$cont_w = $this->pgwidth; // $this->blk[0]['inner_width'];
13996				$cont_h = $this->h - $this->tMargin - $this->bMargin;
13997				$cont_x = $this->lMargin;
13998				$cont_y = $this->tMargin;
13999			} else {
14000				$cont_w = $this->w; // ABSOLUTE;
14001				$cont_h = $this->h;
14002				$cont_x = 0;
14003				$cont_y = 0;
14004			}
14005
14006			// Pass on in-line properties to the innerhtml
14007			$css = '';
14008			if (isset($p['TEXT-ALIGN'])) {
14009				$css .= 'text-align: ' . strtolower($p['TEXT-ALIGN']) . '; ';
14010			}
14011			if (isset($p['TEXT-TRANSFORM'])) {
14012				$css .= 'text-transform: ' . strtolower($p['TEXT-TRANSFORM']) . '; ';
14013			}
14014			if (isset($p['TEXT-INDENT'])) {
14015				$css .= 'text-indent: ' . strtolower($p['TEXT-INDENT']) . '; ';
14016			}
14017			if (isset($p['TEXT-DECORATION'])) {
14018				$css .= 'text-decoration: ' . strtolower($p['TEXT-DECORATION']) . '; ';
14019			}
14020			if (isset($p['FONT-FAMILY'])) {
14021				$css .= 'font-family: ' . strtolower($p['FONT-FAMILY']) . '; ';
14022			}
14023			if (isset($p['FONT-STYLE'])) {
14024				$css .= 'font-style: ' . strtolower($p['FONT-STYLE']) . '; ';
14025			}
14026			if (isset($p['FONT-WEIGHT'])) {
14027				$css .= 'font-weight: ' . strtolower($p['FONT-WEIGHT']) . '; ';
14028			}
14029			if (isset($p['FONT-SIZE'])) {
14030				$css .= 'font-size: ' . strtolower($p['FONT-SIZE']) . '; ';
14031			}
14032			if (isset($p['LINE-HEIGHT'])) {
14033				$css .= 'line-height: ' . strtolower($p['LINE-HEIGHT']) . '; ';
14034			}
14035			if (isset($p['TEXT-SHADOW'])) {
14036				$css .= 'text-shadow: ' . strtolower($p['TEXT-SHADOW']) . '; ';
14037			}
14038			if (isset($p['LETTER-SPACING'])) {
14039				$css .= 'letter-spacing: ' . strtolower($p['LETTER-SPACING']) . '; ';
14040			}
14041			// mPDF 6
14042			if (isset($p['FONT-VARIANT-POSITION'])) {
14043				$css .= 'font-variant-position: ' . strtolower($p['FONT-VARIANT-POSITION']) . '; ';
14044			}
14045			if (isset($p['FONT-VARIANT-CAPS'])) {
14046				$css .= 'font-variant-caps: ' . strtolower($p['FONT-VARIANT-CAPS']) . '; ';
14047			}
14048			if (isset($p['FONT-VARIANT-LIGATURES'])) {
14049				$css .= 'font-variant-ligatures: ' . strtolower($p['FONT-VARIANT-LIGATURES']) . '; ';
14050			}
14051			if (isset($p['FONT-VARIANT-NUMERIC'])) {
14052				$css .= 'font-variant-numeric: ' . strtolower($p['FONT-VARIANT-NUMERIC']) . '; ';
14053			}
14054			if (isset($p['FONT-VARIANT-ALTERNATES'])) {
14055				$css .= 'font-variant-alternates: ' . strtolower($p['FONT-VARIANT-ALTERNATES']) . '; ';
14056			}
14057			if (isset($p['FONT-FEATURE-SETTINGS'])) {
14058				$css .= 'font-feature-settings: ' . strtolower($p['FONT-FEATURE-SETTINGS']) . '; ';
14059			}
14060			if (isset($p['FONT-LANGUAGE-OVERRIDE'])) {
14061				$css .= 'font-language-override: ' . strtolower($p['FONT-LANGUAGE-OVERRIDE']) . '; ';
14062			}
14063			if (isset($p['FONT-KERNING'])) {
14064				$css .= 'font-kerning: ' . strtolower($p['FONT-KERNING']) . '; ';
14065			}
14066
14067			if (isset($p['COLOR'])) {
14068				$css .= 'color: ' . strtolower($p['COLOR']) . '; ';
14069			}
14070			if (isset($p['Z-INDEX'])) {
14071				$css .= 'z-index: ' . $p['Z-INDEX'] . '; ';
14072			}
14073			if ($css) {
14074				$html = '<div style="' . $css . '">' . $html . '</div>';
14075			}
14076			// Copy over (only) the properties to set for border and background
14077			$pb = [];
14078			$pb['MARGIN-TOP'] = (isset($p['MARGIN-TOP']) ? $p['MARGIN-TOP'] : '');
14079			$pb['MARGIN-RIGHT'] = (isset($p['MARGIN-RIGHT']) ? $p['MARGIN-RIGHT'] : '');
14080			$pb['MARGIN-BOTTOM'] = (isset($p['MARGIN-BOTTOM']) ? $p['MARGIN-BOTTOM'] : '');
14081			$pb['MARGIN-LEFT'] = (isset($p['MARGIN-LEFT']) ? $p['MARGIN-LEFT'] : '');
14082			$pb['PADDING-TOP'] = (isset($p['PADDING-TOP']) ? $p['PADDING-TOP'] : '');
14083			$pb['PADDING-RIGHT'] = (isset($p['PADDING-RIGHT']) ? $p['PADDING-RIGHT'] : '');
14084			$pb['PADDING-BOTTOM'] = (isset($p['PADDING-BOTTOM']) ? $p['PADDING-BOTTOM'] : '');
14085			$pb['PADDING-LEFT'] = (isset($p['PADDING-LEFT']) ? $p['PADDING-LEFT'] : '');
14086			$pb['BORDER-TOP'] = (isset($p['BORDER-TOP']) ? $p['BORDER-TOP'] : '');
14087			$pb['BORDER-RIGHT'] = (isset($p['BORDER-RIGHT']) ? $p['BORDER-RIGHT'] : '');
14088			$pb['BORDER-BOTTOM'] = (isset($p['BORDER-BOTTOM']) ? $p['BORDER-BOTTOM'] : '');
14089			$pb['BORDER-LEFT'] = (isset($p['BORDER-LEFT']) ? $p['BORDER-LEFT'] : '');
14090			if (isset($p['BORDER-TOP-LEFT-RADIUS-H'])) {
14091				$pb['BORDER-TOP-LEFT-RADIUS-H'] = $p['BORDER-TOP-LEFT-RADIUS-H'];
14092			}
14093			if (isset($p['BORDER-TOP-LEFT-RADIUS-V'])) {
14094				$pb['BORDER-TOP-LEFT-RADIUS-V'] = $p['BORDER-TOP-LEFT-RADIUS-V'];
14095			}
14096			if (isset($p['BORDER-TOP-RIGHT-RADIUS-H'])) {
14097				$pb['BORDER-TOP-RIGHT-RADIUS-H'] = $p['BORDER-TOP-RIGHT-RADIUS-H'];
14098			}
14099			if (isset($p['BORDER-TOP-RIGHT-RADIUS-V'])) {
14100				$pb['BORDER-TOP-RIGHT-RADIUS-V'] = $p['BORDER-TOP-RIGHT-RADIUS-V'];
14101			}
14102			if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-H'])) {
14103				$pb['BORDER-BOTTOM-LEFT-RADIUS-H'] = $p['BORDER-BOTTOM-LEFT-RADIUS-H'];
14104			}
14105			if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-V'])) {
14106				$pb['BORDER-BOTTOM-LEFT-RADIUS-V'] = $p['BORDER-BOTTOM-LEFT-RADIUS-V'];
14107			}
14108			if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-H'])) {
14109				$pb['BORDER-BOTTOM-RIGHT-RADIUS-H'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-H'];
14110			}
14111			if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-V'])) {
14112				$pb['BORDER-BOTTOM-RIGHT-RADIUS-V'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-V'];
14113			}
14114			if (isset($p['BACKGROUND-COLOR'])) {
14115				$pb['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];
14116			}
14117			if (isset($p['BOX-SHADOW'])) {
14118				$pb['BOX-SHADOW'] = $p['BOX-SHADOW'];
14119			}
14120			/* -- BACKGROUNDS -- */
14121			if (isset($p['BACKGROUND-IMAGE'])) {
14122				$pb['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];
14123			}
14124			if (isset($p['BACKGROUND-IMAGE-RESIZE'])) {
14125				$pb['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];
14126			}
14127			if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {
14128				$pb['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];
14129			}
14130			if (isset($p['BACKGROUND-REPEAT'])) {
14131				$pb['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];
14132			}
14133			if (isset($p['BACKGROUND-POSITION'])) {
14134				$pb['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];
14135			}
14136			if (isset($p['BACKGROUND-GRADIENT'])) {
14137				$pb['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];
14138			}
14139			if (isset($p['BACKGROUND-SIZE'])) {
14140				$pb['BACKGROUND-SIZE'] = $p['BACKGROUND-SIZE'];
14141			}
14142			if (isset($p['BACKGROUND-ORIGIN'])) {
14143				$pb['BACKGROUND-ORIGIN'] = $p['BACKGROUND-ORIGIN'];
14144			}
14145			if (isset($p['BACKGROUND-CLIP'])) {
14146				$pb['BACKGROUND-CLIP'] = $p['BACKGROUND-CLIP'];
14147			}
14148
14149			/* -- END BACKGROUNDS -- */
14150
14151			$this->setCSS($pb, 'BLOCK', $tag);
14152
14153			// ================================================================
14154			$bbox_br = $this->blk[1]['border_right']['w'];
14155			$bbox_bl = $this->blk[1]['border_left']['w'];
14156			$bbox_bt = $this->blk[1]['border_top']['w'];
14157			$bbox_bb = $this->blk[1]['border_bottom']['w'];
14158			$bbox_pr = $this->blk[1]['padding_right'];
14159			$bbox_pl = $this->blk[1]['padding_left'];
14160			$bbox_pt = $this->blk[1]['padding_top'];
14161			$bbox_pb = $this->blk[1]['padding_bottom'];
14162			$bbox_mr = $this->blk[1]['margin_right'];
14163			if (isset($p['MARGIN-RIGHT']) && strtolower($p['MARGIN-RIGHT']) == 'auto') {
14164				$bbox_mr = 'auto';
14165			}
14166			$bbox_ml = $this->blk[1]['margin_left'];
14167			if (isset($p['MARGIN-LEFT']) && strtolower($p['MARGIN-LEFT']) == 'auto') {
14168				$bbox_ml = 'auto';
14169			}
14170			$bbox_mt = $this->blk[1]['margin_top'];
14171			if (isset($p['MARGIN-TOP']) && strtolower($p['MARGIN-TOP']) == 'auto') {
14172				$bbox_mt = 'auto';
14173			}
14174			$bbox_mb = $this->blk[1]['margin_bottom'];
14175			if (isset($p['MARGIN-BOTTOM']) && strtolower($p['MARGIN-BOTTOM']) == 'auto') {
14176				$bbox_mb = 'auto';
14177			}
14178			if (isset($p['LEFT']) && strtolower($p['LEFT']) != 'auto') {
14179				$bbox_left = $this->sizeConverter->convert($p['LEFT'], $cont_w, $this->FontSize, false);
14180			} else {
14181				$bbox_left = 'auto';
14182			}
14183			if (isset($p['TOP']) && strtolower($p['TOP']) != 'auto') {
14184				$bbox_top = $this->sizeConverter->convert($p['TOP'], $cont_h, $this->FontSize, false);
14185			} else {
14186				$bbox_top = 'auto';
14187			}
14188			if (isset($p['RIGHT']) && strtolower($p['RIGHT']) != 'auto') {
14189				$bbox_right = $this->sizeConverter->convert($p['RIGHT'], $cont_w, $this->FontSize, false);
14190			} else {
14191				$bbox_right = 'auto';
14192			}
14193			if (isset($p['BOTTOM']) && strtolower($p['BOTTOM']) != 'auto') {
14194				$bbox_bottom = $this->sizeConverter->convert($p['BOTTOM'], $cont_h, $this->FontSize, false);
14195			} else {
14196				$bbox_bottom = 'auto';
14197			}
14198			if (isset($p['WIDTH']) && strtolower($p['WIDTH']) != 'auto') {
14199				$inner_w = $this->sizeConverter->convert($p['WIDTH'], $cont_w, $this->FontSize, false);
14200			} else {
14201				$inner_w = 'auto';
14202			}
14203			if (isset($p['HEIGHT']) && strtolower($p['HEIGHT']) != 'auto') {
14204				$inner_h = $this->sizeConverter->convert($p['HEIGHT'], $cont_h, $this->FontSize, false);
14205			} else {
14206				$inner_h = 'auto';
14207			}
14208
14209			// If bottom or right pos are set and not left / top - save this to adjust rotated block later
14210			if ($rotate == 90 || $rotate == -90) { // mPDF 6
14211				if ($bbox_left === 'auto' && $bbox_right !== 'auto') {
14212					$rot_rpos = $bbox_right;
14213				} else {
14214					$rot_rpos = false;
14215				}
14216				if ($bbox_top === 'auto' && $bbox_bottom !== 'auto') {
14217					$rot_bpos = $bbox_bottom;
14218				} else {
14219					$rot_bpos = false;
14220				}
14221			}
14222
14223			// ================================================================
14224			if ($checkinnerhtml == '' && $inner_h === 'auto') {
14225				$inner_h = 0.0001;
14226			}
14227			if ($checkinnerhtml == '' && $inner_w === 'auto') {
14228				$inner_w = 2 * $this->GetCharWidth('W', false);
14229			}
14230			// ================================================================
14231			// Algorithm from CSS2.1  See http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
14232			// mPD 5.3.14
14233			// Special case (not CSS) if all not specified, centre vertically on page
14234			$bbox_top_orig = '';
14235			if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_mt === 'auto' && $bbox_mb === 'auto') {
14236				$bbox_top_orig = $bbox_top;
14237				if ($bbox_mt === 'auto') {
14238					$bbox_mt = 0;
14239				}
14240				if ($bbox_mb === 'auto') {
14241					$bbox_mb = 0;
14242				}
14243				$bbox_top = $orig_y0 - $bbox_mt - $cont_y;
14244				// solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
14245			} // mPD 5.3.14
14246			elseif ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto') {
14247				$bbox_top_orig = $bbox_top = $orig_y0 - $cont_y;
14248				if ($bbox_mt === 'auto') {
14249					$bbox_mt = 0;
14250				}
14251				if ($bbox_mb === 'auto') {
14252					$bbox_mb = 0;
14253				}
14254				// solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
14255			} elseif ($bbox_top !== 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {
14256				if ($bbox_mt === 'auto' && $bbox_mb === 'auto') {
14257					$x = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;
14258					$bbox_mt = $bbox_mb = ($x / 2);
14259				} elseif ($bbox_mt === 'auto') {
14260					$bbox_mt = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
14261				} elseif ($bbox_mb === 'auto') {
14262					$bbox_mb = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;
14263				} else {
14264					$bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
14265				}
14266			} else {
14267				if ($bbox_mt === 'auto') {
14268					$bbox_mt = 0;
14269				}
14270				if ($bbox_mb === 'auto') {
14271					$bbox_mb = 0;
14272				}
14273				if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom !== 'auto') {
14274					// solve for $bbox_top when content_h known - $inner_h=='auto' && $bbox_top =='auto'
14275				} elseif ($bbox_top === 'auto' && $bbox_bottom === 'auto' && $inner_h !== 'auto') {
14276					$bbox_top = $orig_y0 - $bbox_mt - $cont_y;
14277					$bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
14278				} elseif ($inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_top !== 'auto') {
14279					// solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
14280				} elseif ($bbox_top === 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {
14281					$bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;
14282				} elseif ($inner_h === 'auto' && $bbox_top !== 'auto' && $bbox_bottom !== 'auto') {
14283					$inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;
14284				} elseif ($bbox_bottom === 'auto' && $bbox_top !== 'auto' && $inner_h !== 'auto') {
14285					$bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
14286				}
14287			}
14288
14289			// THEN DO SAME FOR WIDTH
14290			// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
14291			if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right === 'auto') {
14292				if ($bbox_ml === 'auto') {
14293					$bbox_ml = 0;
14294				}
14295				if ($bbox_mr === 'auto') {
14296					$bbox_mr = 0;
14297				}
14298				// IF containing element RTL, should set $bbox_right
14299				$bbox_left = $orig_x0 - $bbox_ml - $cont_x;
14300				// solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'
14301			} elseif ($bbox_left !== 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {
14302				if ($bbox_ml === 'auto' && $bbox_mr === 'auto') {
14303					$x = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;
14304					$bbox_ml = $bbox_mr = ($x / 2);
14305				} elseif ($bbox_ml === 'auto') {
14306					$bbox_ml = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;
14307				} elseif ($bbox_mr === 'auto') {
14308					$bbox_mr = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;
14309				} else {
14310					$bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
14311				}
14312			} else {
14313				if ($bbox_ml === 'auto') {
14314					$bbox_ml = 0;
14315				}
14316				if ($bbox_mr === 'auto') {
14317					$bbox_mr = 0;
14318				}
14319				if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right !== 'auto') {
14320					// solve for $bbox_left when content_w known - $inner_w=='auto' && $bbox_left =='auto'
14321				} elseif ($bbox_left === 'auto' && $bbox_right === 'auto' && $inner_w !== 'auto') {
14322					// IF containing element RTL, should set $bbox_right
14323					$bbox_left = $orig_x0 - $bbox_ml - $cont_x;
14324					$bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
14325				} elseif ($inner_w === 'auto' && $bbox_right === 'auto' && $bbox_left !== 'auto') {
14326					// solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'
14327				} elseif ($bbox_left === 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {
14328					$bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
14329				} elseif ($inner_w === 'auto' && $bbox_left !== 'auto' && $bbox_right !== 'auto') {
14330					$inner_w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
14331				} elseif ($bbox_right === 'auto' && $bbox_left !== 'auto' && $inner_w !== 'auto') {
14332					$bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
14333				}
14334			}
14335
14336			// ================================================================
14337			// ================================================================
14338			/* -- BACKGROUNDS -- */
14339			if (isset($pb['BACKGROUND-IMAGE']) && $pb['BACKGROUND-IMAGE']) {
14340				$ret = $this->SetBackground($pb, $this->blk[1]['inner_width']);
14341				if ($ret) {
14342					$this->blk[1]['background-image'] = $ret;
14343				}
14344			}
14345			/* -- END BACKGROUNDS -- */
14346
14347			$bbox_top_auto = $bbox_top === 'auto';
14348			$bbox_left_auto = $bbox_left === 'auto';
14349			$bbox_right_auto = $bbox_right === 'auto';
14350			$bbox_bottom_auto = $bbox_bottom === 'auto';
14351
14352			$bbox_top = is_numeric($bbox_top) ? $bbox_top : 0;
14353			$bbox_left = is_numeric($bbox_left) ? $bbox_left : 0;
14354			$bbox_right = is_numeric($bbox_right) ? $bbox_right : 0;
14355			$bbox_bottom = is_numeric($bbox_bottom) ? $bbox_bottom : 0;
14356
14357			$y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;
14358			$h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
14359
14360			$x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;
14361			$w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;
14362
14363			// Set (temporary) values for x y w h to do first paint, if values are auto
14364			if ($inner_h === 'auto' && $bbox_top_auto) {
14365				$y = $cont_y + $bbox_mt + $bbox_bt + $bbox_pt;
14366				$h = $cont_h - ($bbox_bottom + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);
14367			} elseif ($inner_h === 'auto' && $bbox_bottom_auto) {
14368				$y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;
14369				$h = $cont_h - ($bbox_top + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);
14370			}
14371			if ($inner_w === 'auto' && $bbox_left_auto) {
14372				$x = $cont_x + $bbox_ml + $bbox_bl + $bbox_pl;
14373				$w = $cont_w - ($bbox_right + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);
14374			} elseif ($inner_w === 'auto' && $bbox_right_auto) {
14375				$x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;
14376				$w = $cont_w - ($bbox_left + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);
14377			}
14378
14379			$bbox_y = $cont_y + $bbox_top + $bbox_mt;
14380			$bbox_x = $cont_x + $bbox_left + $bbox_ml;
14381
14382			$saved_block1 = $this->blk[1];
14383
14384			unset($p);
14385			unset($pb);
14386
14387			// ================================================================
14388			if ($inner_w === 'auto') { // do a first write
14389				$this->lMargin = $x;
14390				$this->rMargin = $this->w - $w - $x;
14391
14392				// SET POSITION & FONT VALUES
14393				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
14394				$this->pageoutput[$this->page] = [];
14395				$this->x = $x;
14396				$this->y = $y;
14397				$this->HTMLheaderPageLinks = [];
14398				$this->HTMLheaderPageAnnots = [];
14399				$this->HTMLheaderPageForms = [];
14400				$this->pageBackgrounds = [];
14401				$this->maxPosR = 0;
14402				$this->maxPosL = $this->w; // For RTL
14403				$this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
14404				$inner_w = $this->maxPosR - $this->lMargin;
14405				if ($bbox_right_auto) {
14406					$bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
14407				} elseif ($bbox_left_auto) {
14408					$bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
14409					$bbox_x = $cont_x + $bbox_left + $bbox_ml;
14410					$inner_x = $bbox_x + $bbox_bl + $bbox_pl;
14411					$x = $inner_x;
14412				}
14413
14414				$w = $inner_w;
14415				$bbox_y = $cont_y + $bbox_top + $bbox_mt;
14416				$bbox_x = $cont_x + $bbox_left + $bbox_ml;
14417			}
14418
14419			if ($inner_h === 'auto') { // do a first write
14420
14421				$this->lMargin = $x;
14422				$this->rMargin = $this->w - $w - $x;
14423
14424				// SET POSITION & FONT VALUES
14425				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
14426				$this->pageoutput[$this->page] = [];
14427				$this->x = $x;
14428				$this->y = $y;
14429				$this->HTMLheaderPageLinks = [];
14430				$this->HTMLheaderPageAnnots = [];
14431				$this->HTMLheaderPageForms = [];
14432				$this->pageBackgrounds = [];
14433				$this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
14434				$inner_h = $this->y - $y;
14435
14436				if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
14437					if (($this->y + $bbox_pb + $bbox_bb) > ($cont_y + $cont_h)) {
14438						$adj = ($this->y + $bbox_pb + $bbox_bb) - ($cont_y + $cont_h);
14439						$inner_h -= $adj;
14440					}
14441				}
14442				if ($bbox_bottom_auto && $bbox_top_orig === 'auto') {
14443					$bbox_bottom = $bbox_top = ($cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb) / 2;
14444					if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
14445						if ($bbox_top < 0) {
14446							$bbox_top = 0;
14447							$inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
14448						}
14449					}
14450					$bbox_y = $cont_y + $bbox_top + $bbox_mt;
14451					$inner_y = $bbox_y + $bbox_bt + $bbox_pt;
14452					$y = $inner_y;
14453				} elseif ($bbox_bottom_auto) {
14454					$bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb;
14455				} elseif ($bbox_top_auto) {
14456					$bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
14457					if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
14458						if ($bbox_top < 0) {
14459							$bbox_top = 0;
14460							$inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
14461						}
14462					}
14463					$bbox_y = $cont_y + $bbox_top + $bbox_mt;
14464					$inner_y = $bbox_y + $bbox_bt + $bbox_pt;
14465					$y = $inner_y;
14466				}
14467				$h = $inner_h;
14468				$bbox_y = $cont_y + $bbox_top + $bbox_mt;
14469				$bbox_x = $cont_x + $bbox_left + $bbox_ml;
14470			}
14471
14472			$inner_w = $w;
14473			$inner_h = $h;
14474		}
14475
14476		$this->lMargin = $x;
14477		$this->rMargin = $this->w - $w - $x;
14478
14479		// SET POSITION & FONT VALUES
14480		$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
14481		$this->pageoutput[$this->page] = [];
14482
14483		$this->x = $x;
14484		$this->y = $y;
14485
14486		$this->HTMLheaderPageLinks = [];
14487		$this->HTMLheaderPageAnnots = [];
14488		$this->HTMLheaderPageForms = [];
14489
14490		$this->pageBackgrounds = [];
14491
14492		$this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
14493
14494		$actual_h = $this->y - $y;
14495		$use_w = $w;
14496		$use_h = $h;
14497		$ratio = $actual_h / $use_w;
14498
14499		if ($overflow != 'hidden' && $overflow != 'visible') {
14500			$target = $h / $w;
14501			if ($target > 0) {
14502				if (($ratio / $target) > 1) {
14503					$nl = ceil($actual_h / $this->lineheight);
14504					$l = $use_w * $nl;
14505					$est_w = sqrt(($l * $this->lineheight) / $target) * 0.8;
14506					$use_w += ($est_w - $use_w) - ($w / 100);
14507				}
14508				$bpcstart = ($ratio / $target);
14509				$bpcctr = 1;
14510
14511				while (($ratio / $target) > 1) {
14512					// @log 'Auto-sizing fixed-position block $bpcctr++
14513
14514					$this->x = $x;
14515					$this->y = $y;
14516
14517					if (($ratio / $target) > 1.5 || ($ratio / $target) < 0.6) {
14518						$use_w += ($w / $this->incrementFPR1);
14519					} elseif (($ratio / $target) > 1.2 || ($ratio / $target) < 0.85) {
14520						$use_w += ($w / $this->incrementFPR2);
14521					} elseif (($ratio / $target) > 1.1 || ($ratio / $target) < 0.91) {
14522						$use_w += ($w / $this->incrementFPR3);
14523					} else {
14524						$use_w += ($w / $this->incrementFPR4);
14525					}
14526
14527					$use_h = $use_w * $target;
14528					$this->rMargin = $this->w - $use_w - $x;
14529					$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
14530					$this->HTMLheaderPageLinks = [];
14531					$this->HTMLheaderPageAnnots = [];
14532					$this->HTMLheaderPageForms = [];
14533					$this->pageBackgrounds = [];
14534					$this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);
14535					$actual_h = $this->y - $y;
14536					$ratio = $actual_h / $use_w;
14537				}
14538			}
14539		}
14540
14541		$shrink_f = $w / $use_w;
14542
14543		// ================================================================
14544
14545		$this->pages[$this->page] .= '___BEFORE_BORDERS___';
14546		$block_s = $this->PrintPageBackgrounds(); // Save to print later inside clipping path
14547		$this->pageBackgrounds = [];
14548
14549		// ================================================================
14550
14551		if ($rotate == 90 || $rotate == -90) { // mPDF 6
14552			$prerotw = $bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br;
14553			$preroth = $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;
14554			$rot_start = " q\n";
14555			if ($rotate == 90) {
14556				if ($rot_rpos !== false) {
14557					$adjw = $prerotw;
14558				} // width before rotation
14559				else {
14560					$adjw = $preroth;
14561				} // height before rotation
14562				if ($rot_bpos !== false) {
14563					$adjh = -$prerotw + $preroth;
14564				} else {
14565					$adjh = 0;
14566				}
14567			} else {
14568				if ($rot_rpos !== false) {
14569					$adjw = $prerotw - $preroth;
14570				} else {
14571					$adjw = 0;
14572				}
14573				if ($rot_bpos !== false) {
14574					$adjh = $preroth;
14575				} // height before rotation
14576				else {
14577					$adjh = $prerotw;
14578				} // width before rotation
14579			}
14580			$rot_start .= $this->transformTranslate($adjw, $adjh, true) . "\n";
14581			$rot_start .= $this->transformRotate($rotate, $bbox_x, $bbox_y, true) . "\n";
14582			$rot_end = " Q\n";
14583		} elseif ($rotate == 180) { // mPDF 6
14584			$rot_start = " q\n";
14585			$rot_start .= $this->transformTranslate($bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br, $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb, true) . "\n";
14586			$rot_start .= $this->transformRotate(180, $bbox_x, $bbox_y, true) . "\n";
14587			$rot_end = " Q\n";
14588		} else {
14589			$rot_start = '';
14590			$rot_end = '';
14591		}
14592
14593		// ================================================================
14594		if (!empty($bounding)) {
14595			// WHEN HEIGHT // BOTTOM EDGE IS KNOWN and $this->y is set to the bottom
14596			// Re-instate saved $this->blk[1]
14597			$this->blk[1] = $saved_block1;
14598
14599			// These are only needed when painting border/background
14600			$this->blk[1]['width'] = $bbox_w = $cont_w - $bbox_left - $bbox_ml - $bbox_mr - $bbox_right;
14601			$this->blk[1]['x0'] = $bbox_x;
14602			$this->blk[1]['y0'] = $bbox_y;
14603			$this->blk[1]['startpage'] = $this->page;
14604			$this->blk[1]['y1'] = $bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;
14605			$this->writer->write($rot_start);
14606			$this->PaintDivBB('', 0, 1); // Prints borders and sets backgrounds in $this->pageBackgrounds
14607			$this->writer->write($rot_end);
14608		}
14609
14610		$s = $this->PrintPageBackgrounds();
14611		$s = $rot_start . $s . $rot_end;
14612		$this->pages[$this->page] = preg_replace('/___BEFORE_BORDERS___/', "\n" . $s . "\n", $this->pages[$this->page]);
14613		$this->pageBackgrounds = [];
14614
14615		$this->writer->write($rot_start);
14616
14617		// Clipping Output
14618		if ($overflow == 'hidden') {
14619			// Bounding rectangle to clip
14620			$clip_y1 = $this->y;
14621			if (!empty($bounding) && ($this->y + $bbox_pb + $bbox_bb) > ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb )) {
14622				$clip_y1 = ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb ) - ($bbox_pb + $bbox_bb);
14623			}
14624			// $op = 'W* n';	// Clipping
14625			$op = 'W n'; // Clipping alternative mode
14626			$this->writer->write("q");
14627			$ch = $clip_y1 - $y;
14628			$this->writer->write(sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$ch * Mpdf::SCALE, $op));
14629			if (!empty($block_s)) {
14630				$tmp = "q\n" . sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$ch * Mpdf::SCALE, $op);
14631				$tmp .= "\n" . $block_s . "\nQ";
14632				$block_s = $tmp;
14633			}
14634		}
14635
14636
14637		if (!empty($block_s)) {
14638			if ($shrink_f != 1) { // i.e. autofit has resized the box
14639				$tmp = "q\n" . $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y, true);
14640				$tmp .= "\n" . $block_s . "\nQ";
14641				$block_s = $tmp;
14642			}
14643			$this->writer->write($block_s);
14644		}
14645
14646
14647
14648		if ($shrink_f != 1) { // i.e. autofit has resized the box
14649			$this->StartTransform();
14650			$this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y);
14651		}
14652
14653		$this->writer->write($this->headerbuffer);
14654
14655		if ($shrink_f != 1) { // i.e. autofit has resized the box
14656			$this->StopTransform();
14657		}
14658
14659		if ($overflow == 'hidden') {
14660			// End clipping
14661			$this->writer->write("Q");
14662		}
14663
14664		$this->writer->write($rot_end);
14665
14666
14667		// Page Links
14668		foreach ($this->HTMLheaderPageLinks as $lk) {
14669			if ($rotate) {
14670				$tmp = $lk[2]; // Switch h - w
14671				$lk[2] = $lk[3];
14672				$lk[3] = $tmp;
14673
14674				$lx1 = (($lk[0] / Mpdf::SCALE));
14675				$ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)));
14676				if ($rotate == 90) {
14677					$adjx = -($lx1 - $bbox_x) + ($preroth - ($ly1 - $bbox_y));
14678					$adjy = -($ly1 - $bbox_y) + ($lx1 - $bbox_x);
14679					$lk[2] = -$lk[2];
14680				} elseif ($rotate == -90) {
14681					$adjx = -($lx1 - $bbox_x) + ($ly1 - $bbox_y);
14682					$adjy = -($ly1 - $bbox_y) - ($lx1 - $bbox_x) + $prerotw;
14683					$lk[3] = -$lk[3];
14684				}
14685				if ($rot_rpos !== false) {
14686					$adjx += $prerotw - $preroth;
14687				}
14688				if ($rot_bpos !== false) {
14689					$adjy += $preroth - $prerotw;
14690				}
14691				$lx1 += $adjx;
14692				$ly1 += $adjy;
14693
14694				$lk[0] = $lx1 * Mpdf::SCALE;
14695				$lk[1] = ($this->h - $ly1) * Mpdf::SCALE;
14696			}
14697			if ($shrink_f != 1) {  // i.e. autofit has resized the box
14698				$lx1 = (($lk[0] / Mpdf::SCALE) - $x);
14699				$lx2 = $x + ($lx1 * $shrink_f);
14700				$lk[0] = $lx2 * Mpdf::SCALE;
14701				$ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)) - $y);
14702				$ly2 = $y + ($ly1 * $shrink_f);
14703				$lk[1] = ($this->h - $ly2) * Mpdf::SCALE;
14704				$lk[2] *= $shrink_f; // width
14705				$lk[3] *= $shrink_f; // height
14706			}
14707			$this->PageLinks[$this->page][] = $lk;
14708		}
14709
14710		foreach ($this->HTMLheaderPageForms as $n => $f) {
14711			if ($shrink_f != 1) {  // i.e. autofit has resized the box
14712				$f['x'] = $x + (($f['x'] - $x) * $shrink_f);
14713				$f['y'] = $y + (($f['y'] - $y) * $shrink_f);
14714				$f['w'] *= $shrink_f;
14715				$f['h'] *= $shrink_f;
14716				$f['style']['fontsize'] *= $shrink_f;
14717			}
14718			$this->form->forms[$f['n']] = $f;
14719		}
14720		// Page Annotations
14721		foreach ($this->HTMLheaderPageAnnots as $lk) {
14722			if ($rotate) {
14723				if ($rotate == 90) {
14724					$adjx = -($lk['x'] - $bbox_x) + ($preroth - ($lk['y'] - $bbox_y));
14725					$adjy = -($lk['y'] - $bbox_y) + ($lk['x'] - $bbox_x);
14726				} elseif ($rotate == -90) {
14727					$adjx = -($lk['x'] - $bbox_x) + ($lk['y'] - $bbox_y);
14728					$adjy = -($lk['y'] - $bbox_y) - ($lk['x'] - $bbox_x) + $prerotw;
14729				}
14730				if ($rot_rpos !== false) {
14731					$adjx += $prerotw - $preroth;
14732				}
14733				if ($rot_bpos !== false) {
14734					$adjy += $preroth - $prerotw;
14735				}
14736				$lk['x'] += $adjx;
14737				$lk['y'] += $adjy;
14738			}
14739			if ($shrink_f != 1) {  // i.e. autofit has resized the box
14740				$lk['x'] = $x + (($lk['x'] - $x) * $shrink_f);
14741				$lk['y'] = $y + (($lk['y'] - $y) * $shrink_f);
14742			}
14743			$this->PageAnnots[$this->page][] = $lk;
14744		}
14745
14746		// Restore
14747		$this->headerbuffer = '';
14748		$this->HTMLheaderPageLinks = [];
14749		$this->HTMLheaderPageAnnots = [];
14750		$this->HTMLheaderPageForms = [];
14751		$this->pageBackgrounds = $save_bgs;
14752		$this->writingHTMLheader = false;
14753
14754		$this->writingHTMLfooter = false;
14755		$this->fullImageHeight = false;
14756		$this->ResetMargins();
14757		$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
14758		$this->SetXY($save_x, $save_y);
14759		$this->title2annots = $save_annots; // *ANNOTATIONS*
14760		$this->InFooter = false; // turns back on autopagebreaks
14761		$this->pageoutput[$this->page] = [];
14762		$this->pageoutput[$this->page]['Font'] = '';
14763		/* -- COLUMNS -- */
14764		if ($save_cols) {
14765			$this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);
14766		}
14767		/* -- END COLUMNS -- */
14768	}
14769
14770	/* -- END CSS-POSITION -- */
14771
14772	function initialiseBlock(&$blk)
14773	{
14774		$blk['margin_top'] = 0;
14775		$blk['margin_left'] = 0;
14776		$blk['margin_bottom'] = 0;
14777		$blk['margin_right'] = 0;
14778		$blk['padding_top'] = 0;
14779		$blk['padding_left'] = 0;
14780		$blk['padding_bottom'] = 0;
14781		$blk['padding_right'] = 0;
14782		$blk['border_top']['w'] = 0;
14783		$blk['border_left']['w'] = 0;
14784		$blk['border_bottom']['w'] = 0;
14785		$blk['border_right']['w'] = 0;
14786		$blk['direction'] = 'ltr';
14787		$blk['hide'] = false;
14788		$blk['outer_left_margin'] = 0;
14789		$blk['outer_right_margin'] = 0;
14790		$blk['cascadeCSS'] = [];
14791		$blk['block-align'] = false;
14792		$blk['bgcolor'] = false;
14793		$blk['page_break_after_avoid'] = false;
14794		$blk['keep_block_together'] = false;
14795		$blk['float'] = false;
14796		$blk['line_height'] = '';
14797		$blk['margin_collapse'] = false;
14798	}
14799
14800	function border_details($bd)
14801	{
14802		$prop = preg_split('/\s+/', trim($bd));
14803
14804		if (isset($this->blk[$this->blklvl]['inner_width'])) {
14805			$refw = $this->blk[$this->blklvl]['inner_width'];
14806		} elseif (isset($this->blk[$this->blklvl - 1]['inner_width'])) {
14807			$refw = $this->blk[$this->blklvl - 1]['inner_width'];
14808		} else {
14809			$refw = $this->w;
14810		}
14811		if (count($prop) == 1) {
14812			$bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false);
14813			if ($bsize > 0) {
14814				return ['s' => 1, 'w' => $bsize, 'c' => $this->colorConverter->convert(0, $this->PDFAXwarnings), 'style' => 'solid'];
14815			} else {
14816				return ['w' => 0, 's' => 0];
14817			}
14818		} elseif (count($prop) == 2) {
14819			// 1px solid
14820			if (in_array($prop[1], $this->borderstyles) || $prop[1] == 'none' || $prop[1] == 'hidden') {
14821				$prop[2] = '';
14822			} // solid #000000
14823			elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {
14824				$prop[0] = '';
14825				$prop[1] = $prop[0];
14826				$prop[2] = $prop[1];
14827			} // 1px #000000
14828			else {
14829				$prop[1] = '';
14830				$prop[2] = $prop[1];
14831			}
14832		} elseif (count($prop) == 3) {
14833			// Change #000000 1px solid to 1px solid #000000 (proper)
14834			if (substr($prop[0], 0, 1) == '#') {
14835				$tmp = $prop[0];
14836				$prop[0] = $prop[1];
14837				$prop[1] = $prop[2];
14838				$prop[2] = $tmp;
14839			} // Change solid #000000 1px to 1px solid #000000 (proper)
14840			elseif (substr($prop[0], 1, 1) == '#') {
14841				$tmp = $prop[1];
14842				$prop[0] = $prop[2];
14843				$prop[1] = $prop[0];
14844				$prop[2] = $tmp;
14845			} // Change solid 1px #000000 to 1px solid #000000 (proper)
14846			elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {
14847				$tmp = $prop[0];
14848				$prop[0] = $prop[1];
14849				$prop[1] = $tmp;
14850			}
14851		} else {
14852			return ['w' => 0, 's' => 0];
14853		}
14854		// Size
14855		$bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false);
14856		// color
14857		$coul = $this->colorConverter->convert($prop[2], $this->PDFAXwarnings); // returns array
14858		// Style
14859		$prop[1] = strtolower($prop[1]);
14860		if (in_array($prop[1], $this->borderstyles) && $bsize > 0) {
14861			$on = 1;
14862		} elseif ($prop[1] == 'hidden') {
14863			$on = 1;
14864			$bsize = 0;
14865			$coul = '';
14866		} elseif ($prop[1] == 'none') {
14867			$on = 0;
14868			$bsize = 0;
14869			$coul = '';
14870		} else {
14871			$on = 0;
14872			$bsize = 0;
14873			$coul = '';
14874			$prop[1] = '';
14875		}
14876		return ['s' => $on, 'w' => $bsize, 'c' => $coul, 'style' => $prop[1], 'dom' => 0];
14877	}
14878
14879	/* -- END HTML-CSS -- */
14880
14881
14882	/* -- BORDER-RADIUS -- */
14883
14884	function _borderPadding($a, $b, &$px, &$py)
14885	{
14886		// $px and py are padding long axis (x) and short axis (y)
14887		$added = 0; // extra padding
14888
14889		$x = $a - $px;
14890		$y = $b - $py;
14891		// Check if Falls within ellipse of border radius
14892		if (( (($x + $added) * ($x + $added)) / ($a * $a) + (($y + $added) * ($y + $added)) / ($b * $b) ) <= 1) {
14893			return false;
14894		}
14895
14896		$t = atan2($y, $x);
14897
14898		$newx = $b / sqrt((($b * $b) / ($a * $a)) + ( tan($t) * tan($t) ));
14899		$newy = $a / sqrt((($a * $a) / ($b * $b)) + ( (1 / tan($t)) * (1 / tan($t)) ));
14900		$px = max($px, $a - $newx + $added);
14901		$py = max($py, $b - $newy + $added);
14902	}
14903
14904	/* -- END BORDER-RADIUS -- */
14905	/* -- HTML-CSS -- */
14906	/* -- CSS-PAGE -- */
14907
14908	function SetPagedMediaCSS($name, $first, $oddEven)
14909	{
14910		if ($oddEven == 'E') {
14911			if ($this->directionality == 'rtl') {
14912				$side = 'R';
14913			} else {
14914				$side = 'L';
14915			}
14916		} else {
14917			if ($this->directionality == 'rtl') {
14918				$side = 'L';
14919			} else {
14920				$side = 'R';
14921			}
14922		}
14923		$name = strtoupper($name);
14924		$p = [];
14925		$p['SIZE'] = 'AUTO';
14926
14927		// Uses mPDF original margins as default
14928		$p['MARGIN-RIGHT'] = strval($this->orig_rMargin) . 'mm';
14929		$p['MARGIN-LEFT'] = strval($this->orig_lMargin) . 'mm';
14930		$p['MARGIN-TOP'] = strval($this->orig_tMargin) . 'mm';
14931		$p['MARGIN-BOTTOM'] = strval($this->orig_bMargin) . 'mm';
14932		$p['MARGIN-HEADER'] = strval($this->orig_hMargin) . 'mm';
14933		$p['MARGIN-FOOTER'] = strval($this->orig_fMargin) . 'mm';
14934
14935		// Basic page + selector
14936		if (isset($this->cssManager->CSS['@PAGE'])) {
14937			$zp = $this->cssManager->CSS['@PAGE'];
14938		} else {
14939			$zp = [];
14940		}
14941		if (is_array($zp) && !empty($zp)) {
14942			$p = array_merge($p, $zp);
14943		}
14944
14945		if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {
14946			$p['HEADER'] = $p['EVEN-HEADER-NAME'];
14947			unset($p['EVEN-HEADER-NAME']);
14948		}
14949		if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {
14950			$p['HEADER'] = $p['ODD-HEADER-NAME'];
14951			unset($p['ODD-HEADER-NAME']);
14952		}
14953		if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {
14954			$p['FOOTER'] = $p['EVEN-FOOTER-NAME'];
14955			unset($p['EVEN-FOOTER-NAME']);
14956		}
14957		if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {
14958			$p['FOOTER'] = $p['ODD-FOOTER-NAME'];
14959			unset($p['ODD-FOOTER-NAME']);
14960		}
14961
14962		// If right/Odd page
14963		if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT']) && $side == 'R') {
14964			$zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT'];
14965		} else {
14966			$zp = [];
14967		}
14968		if (isset($zp['SIZE'])) {
14969			unset($zp['SIZE']);
14970		}
14971		if (isset($zp['SHEET-SIZE'])) {
14972			unset($zp['SHEET-SIZE']);
14973		}
14974		// Disallow margin-left or -right on :LEFT or :RIGHT
14975		if (isset($zp['MARGIN-LEFT'])) {
14976			unset($zp['MARGIN-LEFT']);
14977		}
14978		if (isset($zp['MARGIN-RIGHT'])) {
14979			unset($zp['MARGIN-RIGHT']);
14980		}
14981		if (is_array($zp) && !empty($zp)) {
14982			$p = array_merge($p, $zp);
14983		}
14984
14985		// If left/Even page
14986		if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT']) && $side == 'L') {
14987			$zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT'];
14988		} else {
14989			$zp = [];
14990		}
14991		if (isset($zp['SIZE'])) {
14992			unset($zp['SIZE']);
14993		}
14994		if (isset($zp['SHEET-SIZE'])) {
14995			unset($zp['SHEET-SIZE']);
14996		}
14997		// Disallow margin-left or -right on :LEFT or :RIGHT
14998		if (isset($zp['MARGIN-LEFT'])) {
14999			unset($zp['MARGIN-LEFT']);
15000		}
15001		if (isset($zp['MARGIN-RIGHT'])) {
15002			unset($zp['MARGIN-RIGHT']);
15003		}
15004		if (is_array($zp) && !empty($zp)) {
15005			$p = array_merge($p, $zp);
15006		}
15007
15008		// If first page
15009		if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']) && $first) {
15010			$zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST'];
15011		} else {
15012			$zp = [];
15013		}
15014		if (isset($zp['SIZE'])) {
15015			unset($zp['SIZE']);
15016		}
15017		if (isset($zp['SHEET-SIZE'])) {
15018			unset($zp['SHEET-SIZE']);
15019		}
15020		// Disallow margin-left or -right on :FIRST	// mPDF 5.7.3
15021		if (isset($zp['MARGIN-LEFT'])) {
15022			unset($zp['MARGIN-LEFT']);
15023		}
15024		if (isset($zp['MARGIN-RIGHT'])) {
15025			unset($zp['MARGIN-RIGHT']);
15026		}
15027		if (is_array($zp) && !empty($zp)) {
15028			$p = array_merge($p, $zp);
15029		}
15030
15031		// If named page
15032		if ($name) {
15033			if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name])) {
15034				$zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name];
15035			} else {
15036				$zp = [];
15037			}
15038			if (is_array($zp) && !empty($zp)) {
15039				$p = array_merge($p, $zp);
15040			}
15041
15042			if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {
15043				$p['HEADER'] = $p['EVEN-HEADER-NAME'];
15044				unset($p['EVEN-HEADER-NAME']);
15045			}
15046			if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {
15047				$p['HEADER'] = $p['ODD-HEADER-NAME'];
15048				unset($p['ODD-HEADER-NAME']);
15049			}
15050			if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {
15051				$p['FOOTER'] = $p['EVEN-FOOTER-NAME'];
15052				unset($p['EVEN-FOOTER-NAME']);
15053			}
15054			if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {
15055				$p['FOOTER'] = $p['ODD-FOOTER-NAME'];
15056				unset($p['ODD-FOOTER-NAME']);
15057			}
15058
15059			// If named right/Odd page
15060			if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT']) && $side == 'R') {
15061				$zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT'];
15062			} else {
15063				$zp = [];
15064			}
15065			if (isset($zp['SIZE'])) {
15066				unset($zp['SIZE']);
15067			}
15068			if (isset($zp['SHEET-SIZE'])) {
15069				unset($zp['SHEET-SIZE']);
15070			}
15071			// Disallow margin-left or -right on :LEFT or :RIGHT
15072			if (isset($zp['MARGIN-LEFT'])) {
15073				unset($zp['MARGIN-LEFT']);
15074			}
15075			if (isset($zp['MARGIN-RIGHT'])) {
15076				unset($zp['MARGIN-RIGHT']);
15077			}
15078			if (is_array($zp) && !empty($zp)) {
15079				$p = array_merge($p, $zp);
15080			}
15081
15082			// If named left/Even page
15083			if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT']) && $side == 'L') {
15084				$zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT'];
15085			} else {
15086				$zp = [];
15087			}
15088			if (isset($zp['SIZE'])) {
15089				unset($zp['SIZE']);
15090			}
15091			if (isset($zp['SHEET-SIZE'])) {
15092				unset($zp['SHEET-SIZE']);
15093			}
15094			// Disallow margin-left or -right on :LEFT or :RIGHT
15095			if (isset($zp['MARGIN-LEFT'])) {
15096				unset($zp['MARGIN-LEFT']);
15097			}
15098			if (isset($zp['MARGIN-RIGHT'])) {
15099				unset($zp['MARGIN-RIGHT']);
15100			}
15101			if (is_array($zp) && !empty($zp)) {
15102				$p = array_merge($p, $zp);
15103			}
15104
15105			// If named first page
15106			if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST']) && $first) {
15107				$zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST'];
15108			} else {
15109				$zp = [];
15110			}
15111			if (isset($zp['SIZE'])) {
15112				unset($zp['SIZE']);
15113			}
15114			if (isset($zp['SHEET-SIZE'])) {
15115				unset($zp['SHEET-SIZE']);
15116			}
15117			// Disallow margin-left or -right on :FIRST	// mPDF 5.7.3
15118			if (isset($zp['MARGIN-LEFT'])) {
15119				unset($zp['MARGIN-LEFT']);
15120			}
15121			if (isset($zp['MARGIN-RIGHT'])) {
15122				unset($zp['MARGIN-RIGHT']);
15123			}
15124			if (is_array($zp) && !empty($zp)) {
15125				$p = array_merge($p, $zp);
15126			}
15127		}
15128
15129		$orientation = $mgl = $mgr = $mgt = $mgb = $mgh = $mgf = '';
15130		$header = $footer = '';
15131		$resetpagenum = $pagenumstyle = $suppress = '';
15132		$marks = '';
15133		$bg = [];
15134
15135		$newformat = '';
15136
15137
15138		if (isset($p['SHEET-SIZE']) && is_array($p['SHEET-SIZE'])) {
15139			$newformat = $p['SHEET-SIZE'];
15140			if ($newformat[0] > $newformat[1]) { // landscape
15141				$newformat = array_reverse($newformat);
15142				$p['ORIENTATION'] = 'L';
15143			} else {
15144				$p['ORIENTATION'] = 'P';
15145			}
15146			$this->_setPageSize($newformat, $p['ORIENTATION']);
15147		}
15148
15149		if (isset($p['SIZE']) && is_array($p['SIZE']) && !$newformat) {
15150			if ($p['SIZE']['W'] > $p['SIZE']['H']) {
15151				$p['ORIENTATION'] = 'L';
15152			} else {
15153				$p['ORIENTATION'] = 'P';
15154			}
15155		}
15156		if (is_array($p['SIZE'])) {
15157			if ($p['SIZE']['W'] > $this->fw) {
15158				$p['SIZE']['W'] = $this->fw;
15159			} // mPD 4.2 use fw not fPt
15160			if ($p['SIZE']['H'] > $this->fh) {
15161				$p['SIZE']['H'] = $this->fh;
15162			}
15163			if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {
15164				$outer_width_LR = ($this->fw - $p['SIZE']['W']) / 2;
15165				$outer_width_TB = ($this->fh - $p['SIZE']['H']) / 2;
15166			} else {
15167				$outer_width_LR = ($this->fh - $p['SIZE']['W']) / 2;
15168				$outer_width_TB = ($this->fw - $p['SIZE']['H']) / 2;
15169			}
15170			$pgw = $p['SIZE']['W'];
15171			$pgh = $p['SIZE']['H'];
15172		} else { // AUTO LANDSCAPE PORTRAIT
15173			$outer_width_LR = 0;
15174			$outer_width_TB = 0;
15175			if (!$newformat) {
15176				if (strtoupper($p['SIZE']) == 'AUTO') {
15177					$p['ORIENTATION'] = $this->DefOrientation;
15178				} elseif (strtoupper($p['SIZE']) == 'LANDSCAPE') {
15179					$p['ORIENTATION'] = 'L';
15180				} else {
15181					$p['ORIENTATION'] = 'P';
15182				}
15183			}
15184			if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {
15185				$pgw = $this->fw;
15186				$pgh = $this->fh;
15187			} else {
15188				$pgw = $this->fh;
15189				$pgh = $this->fw;
15190			}
15191		}
15192
15193		if (isset($p['HEADER']) && $p['HEADER']) {
15194			$header = $p['HEADER'];
15195		}
15196		if (isset($p['FOOTER']) && $p['FOOTER']) {
15197			$footer = $p['FOOTER'];
15198		}
15199		if (isset($p['RESETPAGENUM']) && $p['RESETPAGENUM']) {
15200			$resetpagenum = $p['RESETPAGENUM'];
15201		}
15202		if (isset($p['PAGENUMSTYLE']) && $p['PAGENUMSTYLE']) {
15203			$pagenumstyle = $p['PAGENUMSTYLE'];
15204		}
15205		if (isset($p['SUPPRESS']) && $p['SUPPRESS']) {
15206			$suppress = $p['SUPPRESS'];
15207		}
15208
15209		if (isset($p['MARKS'])) {
15210			if (preg_match('/cross/i', $p['MARKS']) && preg_match('/crop/i', $p['MARKS'])) {
15211				$marks = 'CROPCROSS';
15212			} elseif (strtoupper($p['MARKS']) == 'CROP') {
15213				$marks = 'CROP';
15214			} elseif (strtoupper($p['MARKS']) == 'CROSS') {
15215				$marks = 'CROSS';
15216			}
15217		}
15218
15219		if (isset($p['BACKGROUND-COLOR']) && $p['BACKGROUND-COLOR']) {
15220			$bg['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];
15221		}
15222		/* -- BACKGROUNDS -- */
15223		if (isset($p['BACKGROUND-GRADIENT']) && $p['BACKGROUND-GRADIENT']) {
15224			$bg['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];
15225		}
15226		if (isset($p['BACKGROUND-IMAGE']) && $p['BACKGROUND-IMAGE']) {
15227			$bg['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];
15228		}
15229		if (isset($p['BACKGROUND-REPEAT']) && $p['BACKGROUND-REPEAT']) {
15230			$bg['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];
15231		}
15232		if (isset($p['BACKGROUND-POSITION']) && $p['BACKGROUND-POSITION']) {
15233			$bg['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];
15234		}
15235		if (isset($p['BACKGROUND-IMAGE-RESIZE']) && $p['BACKGROUND-IMAGE-RESIZE']) {
15236			$bg['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];
15237		}
15238		if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {
15239			$bg['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];
15240		}
15241		/* -- END BACKGROUNDS -- */
15242
15243		if (isset($p['MARGIN-LEFT'])) {
15244			$mgl = $this->sizeConverter->convert($p['MARGIN-LEFT'], $pgw) + $outer_width_LR;
15245		}
15246		if (isset($p['MARGIN-RIGHT'])) {
15247			$mgr = $this->sizeConverter->convert($p['MARGIN-RIGHT'], $pgw) + $outer_width_LR;
15248		}
15249		if (isset($p['MARGIN-BOTTOM'])) {
15250			$mgb = $this->sizeConverter->convert($p['MARGIN-BOTTOM'], $pgh) + $outer_width_TB;
15251		}
15252		if (isset($p['MARGIN-TOP'])) {
15253			$mgt = $this->sizeConverter->convert($p['MARGIN-TOP'], $pgh) + $outer_width_TB;
15254		}
15255		if (isset($p['MARGIN-HEADER'])) {
15256			$mgh = $this->sizeConverter->convert($p['MARGIN-HEADER'], $pgh) + $outer_width_TB;
15257		}
15258		if (isset($p['MARGIN-FOOTER'])) {
15259			$mgf = $this->sizeConverter->convert($p['MARGIN-FOOTER'], $pgh) + $outer_width_TB;
15260		}
15261
15262		if (isset($p['ORIENTATION']) && $p['ORIENTATION']) {
15263			$orientation = $p['ORIENTATION'];
15264		}
15265		$this->page_box['outer_width_LR'] = $outer_width_LR; // Used in MARKS:crop etc.
15266		$this->page_box['outer_width_TB'] = $outer_width_TB;
15267
15268		return [$orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $header, $footer, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat];
15269	}
15270
15271	/* -- END CSS-PAGE -- */
15272
15273
15274
15275	/* -- CSS-FLOAT -- */
15276
15277	// Added mPDF 3.0 Float DIV - CLEAR
15278	function ClearFloats($clear, $blklvl = 0)
15279	{
15280		list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($blklvl, true);
15281		$end = $currpos = ($this->page * 1000 + $this->y);
15282		if ($clear == 'BOTH' && ($l_exists || $r_exists)) {
15283			$this->pageoutput[$this->page] = [];
15284			$end = max($l_max, $r_max, $currpos);
15285		} elseif ($clear == 'RIGHT' && $r_exists) {
15286			$this->pageoutput[$this->page] = [];
15287			$end = max($r_max, $currpos);
15288		} elseif ($clear == 'LEFT' && $l_exists) {
15289			$this->pageoutput[$this->page] = [];
15290			$end = max($l_max, $currpos);
15291		} else {
15292			return;
15293		}
15294		$old_page = $this->page;
15295		$new_page = intval($end / 1000);
15296		if ($old_page != $new_page) {
15297			$s = $this->PrintPageBackgrounds();
15298			// Writes after the marker so not overwritten later by page background etc.
15299			$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
15300			$this->pageBackgrounds = [];
15301			$this->page = $new_page;
15302		}
15303		$this->ResetMargins();
15304		$this->pageoutput[$this->page] = [];
15305
15306		$this->y = (round($end * 1000) % 1000000) / 1000; // mod changes operands to integers before processing
15307	}
15308
15309	// Added mPDF 3.0 Float DIV
15310	function GetFloatDivInfo($blklvl = 0, $clear = false)
15311	{
15312		// If blklvl specified, only returns floats at that level - for ClearFloats
15313		$l_exists = false;
15314		$r_exists = false;
15315		$l_max = 0;
15316		$r_max = 0;
15317		$l_width = 0;
15318		$r_width = 0;
15319		if (count($this->floatDivs)) {
15320			$currpos = ($this->page * 1000 + $this->y);
15321			foreach ($this->floatDivs as $f) {
15322				if (($clear && $f['blockContext'] == $this->blk[$blklvl]['blockContext']) || (!$clear && $currpos >= $f['startpos'] && $currpos < ($f['endpos'] - 0.001) && $f['blklvl'] > $blklvl && $f['blockContext'] == $this->blk[$blklvl]['blockContext'])) {
15323					if ($f['side'] == 'L') {
15324						$l_exists = true;
15325						$l_max = max($l_max, $f['endpos']);
15326						$l_width = max($l_width, $f['w']);
15327					}
15328					if ($f['side'] == 'R') {
15329						$r_exists = true;
15330						$r_max = max($r_max, $f['endpos']);
15331						$r_width = max($r_width, $f['w']);
15332					}
15333				}
15334			}
15335		}
15336		return [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width];
15337	}
15338
15339	/* -- END CSS-FLOAT -- */
15340
15341	// LIST MARKERS	// mPDF 6  Lists
15342	function _setListMarker($listitemtype, $listitemimage, $listitemposition)
15343	{
15344		// if position:inside (and NOT table) - output now as a textbuffer; (so if next is block, will move to new line)
15345		// elseif position:outside (and NOT table) - output in front of first textbuffer output by setting listitem (cf. _saveTextBuffer)
15346		$e = '';
15347		$this->listitem = '';
15348		$spacer = ' ';
15349		// IMAGE
15350		if ($listitemimage && $listitemimage != 'none') {
15351			$listitemimage = trim(preg_replace('/url\(["\']*(.*?)["\']*\)/', '\\1', $listitemimage));
15352
15353			// ? Restrict maximum height/width of list marker??
15354			$maxWidth = 100;
15355			$maxHeight = 100;
15356
15357			$objattr = [];
15358			$objattr['margin_top'] = 0;
15359			$objattr['margin_bottom'] = 0;
15360			$objattr['margin_left'] = 0;
15361			$objattr['margin_right'] = 0;
15362			$objattr['padding_top'] = 0;
15363			$objattr['padding_bottom'] = 0;
15364			$objattr['padding_left'] = 0;
15365			$objattr['padding_right'] = 0;
15366			$objattr['width'] = 0;
15367			$objattr['height'] = 0;
15368			$objattr['border_top']['w'] = 0;
15369			$objattr['border_bottom']['w'] = 0;
15370			$objattr['border_left']['w'] = 0;
15371			$objattr['border_right']['w'] = 0;
15372			$objattr['visibility'] = 'visible';
15373			$srcpath = $listitemimage;
15374			$orig_srcpath = $listitemimage;
15375
15376			$objattr['vertical-align'] = 'BS'; // vertical alignment of marker (baseline)
15377			$w = 0;
15378			$h = 0;
15379
15380			// Image file
15381			$info = $this->imageProcessor->getImage($srcpath, true, true, $orig_srcpath);
15382			if (!$info) {
15383				return;
15384			}
15385
15386			if ($info['w'] == 0 && $info['h'] == 0) {
15387				$info['h'] = $this->sizeConverter->convert('1em', $this->blk[$this->blklvl]['inner_width'], $this->FontSize, false);
15388			}
15389
15390			$objattr['file'] = $srcpath;
15391
15392			// Default width and height calculation if needed
15393			if ($w == 0 and $h == 0) {
15394				/* -- IMAGES-WMF -- */
15395				if ($info['type'] == 'wmf') {
15396					// WMF units are twips (1/20pt)
15397					// divide by 20 to get points
15398					// divide by k to get user units
15399					$w = abs($info['w']) / (20 * Mpdf::SCALE);
15400					$h = abs($info['h']) / (20 * Mpdf::SCALE);
15401				} else { 				/* -- END IMAGES-WMF -- */
15402					if ($info['type'] == 'svg') {
15403						// SVG units are pixels
15404						$w = abs($info['w']) / Mpdf::SCALE;
15405						$h = abs($info['h']) / Mpdf::SCALE;
15406					} else {
15407						// Put image at default image dpi
15408						$w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi);
15409						$h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi);
15410					}
15411				}
15412			}
15413			// IF WIDTH OR HEIGHT SPECIFIED
15414			if ($w == 0) {
15415				$w = abs($h * $info['w'] / $info['h']);
15416			}
15417			if ($h == 0) {
15418				$h = abs($w * $info['h'] / $info['w']);
15419			}
15420
15421			if ($w > $maxWidth) {
15422				$w = $maxWidth;
15423				$h = abs($w * $info['h'] / $info['w']);
15424			}
15425
15426			if ($h > $maxHeight) {
15427				$h = $maxHeight;
15428				$w = abs($h * $info['w'] / $info['h']);
15429			}
15430
15431			$objattr['type'] = 'image';
15432			$objattr['itype'] = $info['type'];
15433
15434			$objattr['orig_h'] = $info['h'];
15435			$objattr['orig_w'] = $info['w'];
15436
15437			/* -- IMAGES-WMF -- */
15438			if ($info['type'] == 'wmf') {
15439				$objattr['wmf_x'] = $info['x'];
15440				$objattr['wmf_y'] = $info['y'];
15441			} else { 			/* -- END IMAGES-WMF -- */
15442				if ($info['type'] == 'svg') {
15443					$objattr['wmf_x'] = $info['x'];
15444					$objattr['wmf_y'] = $info['y'];
15445				}
15446			}
15447
15448			$objattr['height'] = $h;
15449			$objattr['width'] = $w;
15450			$objattr['image_height'] = $h;
15451			$objattr['image_width'] = $w;
15452
15453			$objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
15454			$objattr['listmarker'] = true;
15455
15456			$objattr['listmarkerposition'] = $listitemposition;
15457
15458			$e = "\xbb\xa4\xactype=image,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
15459			$this->_saveTextBuffer($e);
15460
15461			if ($listitemposition == 'inside') {
15462				$e = $spacer;
15463				$this->_saveTextBuffer($e);
15464			}
15465		} elseif ($listitemtype == 'disc' || $listitemtype == 'circle' || $listitemtype == 'square') { // SYMBOL (needs new font)
15466			$objattr = [];
15467			$objattr['type'] = 'listmarker';
15468			$objattr['listmarkerposition'] = $listitemposition;
15469			$objattr['width'] = 0;
15470			$size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize);
15471			$objattr['size'] = $size;
15472			$objattr['offset'] = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
15473
15474			if ($listitemposition == 'inside') {
15475				$objattr['width'] = $size + $objattr['offset'];
15476			}
15477
15478			$objattr['height'] = $this->FontSize;
15479			$objattr['vertical-align'] = 'T';
15480			$objattr['text'] = '';
15481			$objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
15482			$objattr['bullet'] = $listitemtype;
15483			$objattr['colorarray'] = $this->colorarray;
15484			$objattr['fontfamily'] = $this->FontFamily;
15485			$objattr['fontsize'] = $this->FontSize;
15486			$objattr['fontsizept'] = $this->FontSizePt;
15487			$objattr['fontstyle'] = $this->FontStyle;
15488
15489			$e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
15490			$this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
15491
15492		} elseif (preg_match('/U\+([a-fA-F0-9]+)/i', $listitemtype, $m)) { // SYMBOL 2 (needs new font)
15493
15494			if ($this->_charDefined($this->CurrentFont['cw'], hexdec($m[1]))) {
15495				$list_item_marker = UtfString::codeHex2utf($m[1]);
15496			} else {
15497				$list_item_marker = '-';
15498			}
15499			if (preg_match('/rgb\(.*?\)/', $listitemtype, $m)) {
15500				$list_item_color = $this->colorConverter->convert($m[0], $this->PDFAXwarnings);
15501			} else {
15502				$list_item_color = '';
15503			}
15504
15505			// SAVE then SET COLR
15506			$save_colorarray = $this->colorarray;
15507			if ($list_item_color) {
15508				$this->colorarray = $list_item_color;
15509			}
15510
15511			if ($listitemposition == 'inside') {
15512				$e = $list_item_marker . $spacer;
15513				$this->_saveTextBuffer($e);
15514			} else {
15515				$objattr = [];
15516				$objattr['type'] = 'listmarker';
15517				$objattr['width'] = 0;
15518				$objattr['height'] = $this->FontSize;
15519				$objattr['vertical-align'] = 'T';
15520				$objattr['text'] = $list_item_marker;
15521				$objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
15522				$objattr['colorarray'] = $this->colorarray;
15523				$objattr['fontfamily'] = $this->FontFamily;
15524				$objattr['fontsize'] = $this->FontSize;
15525				$objattr['fontsizept'] = $this->FontSizePt;
15526				$objattr['fontstyle'] = $this->FontStyle;
15527				$e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
15528				$this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
15529			}
15530
15531			// RESET COLOR
15532			$this->colorarray = $save_colorarray;
15533
15534		} else { // TEXT
15535			$counter = $this->listcounter[$this->listlvl];
15536
15537			if ($listitemtype == 'none') {
15538				return;
15539			}
15540
15541			$num = $this->_getStyledNumber($counter, $listitemtype, true);
15542
15543			if ($listitemposition == 'inside') {
15544				$e = $num . $this->list_number_suffix . $spacer;
15545				$this->_saveTextBuffer($e);
15546			} else {
15547				if (isset($this->blk[$this->blklvl]['direction']) && $this->blk[$this->blklvl]['direction'] == 'rtl') {
15548					// REPLACE MIRRORED RTL $this->list_number_suffix  e.g. ) -> (  (NB could use Ucdn::$mirror_pairs)
15549					$m = strtr($this->list_number_suffix, ")]}", "([{") . $num;
15550				} else {
15551					$m = $num . $this->list_number_suffix;
15552				}
15553
15554				$objattr = [];
15555				$objattr['type'] = 'listmarker';
15556				$objattr['width'] = 0;
15557				$objattr['height'] = $this->FontSize;
15558				$objattr['vertical-align'] = 'T';
15559				$objattr['text'] = $m;
15560				$objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
15561				$objattr['colorarray'] = $this->colorarray;
15562				$objattr['fontfamily'] = $this->FontFamily;
15563				$objattr['fontsize'] = $this->FontSize;
15564				$objattr['fontsizept'] = $this->FontSizePt;
15565				$objattr['fontstyle'] = $this->FontStyle;
15566				$e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
15567
15568				$this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
15569			}
15570		}
15571	}
15572
15573	// mPDF Lists
15574	function _getListMarkerWidth(&$currblk, &$a, &$i)
15575	{
15576		$blt_width = 0;
15577
15578		$markeroffset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
15579
15580		// Get Maximum number in the list
15581		$maxnum = $this->listcounter[$this->listlvl];
15582		if ($currblk['list_style_type'] != 'disc' && $currblk['list_style_type'] != 'circle' && $currblk['list_style_type'] != 'square') {
15583			$lvl = 1;
15584			for ($j = $i + 2; $j < count($a); $j+=2) {
15585				$e = $a[$j];
15586				if (!$e) {
15587					continue;
15588				}
15589				if ($e[0] == '/') { // end tag
15590					$e = strtoupper(substr($e, 1));
15591					if ($e == 'OL' || $e == 'UL') {
15592						if ($lvl == 1) {
15593							break;
15594						}
15595						$lvl--;
15596					}
15597				} else { // opening tag
15598					if (strpos($e, ' ')) {
15599						$e = substr($e, 0, strpos($e, ' '));
15600					}
15601					$e = strtoupper($e);
15602					if ($e == 'LI') {
15603						if ($lvl == 1) {
15604							$maxnum++;
15605						}
15606					} elseif ($e == 'OL' || $e == 'UL') {
15607						$lvl++;
15608					}
15609				}
15610			}
15611		}
15612
15613		$decToAlpha = new Conversion\DecToAlpha();
15614		$decToRoman = new Conversion\DecToRoman();
15615		$decToOther = new Conversion\DecToOther($this);
15616
15617		switch ($currblk['list_style_type']) {
15618			case 'decimal':
15619			case '1':
15620				$blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);
15621				break;
15622			case 'none':
15623				$blt_width = 0;
15624				break;
15625			case 'upper-alpha':
15626			case 'upper-latin':
15627			case 'A':
15628				$maxnumA = $decToAlpha->convert($maxnum, true);
15629				if ($maxnum < 13) {
15630					$blt_width = $this->GetStringWidth('D' . $this->list_number_suffix);
15631				} else {
15632					$blt_width = $this->GetStringWidth(str_repeat('W', strlen($maxnumA)) . $this->list_number_suffix);
15633				}
15634				break;
15635			case 'lower-alpha':
15636			case 'lower-latin':
15637			case 'a':
15638				$maxnuma = $decToAlpha->convert($maxnum, false);
15639				if ($maxnum < 13) {
15640					$blt_width = $this->GetStringWidth('b' . $this->list_number_suffix);
15641				} else {
15642					$blt_width = $this->GetStringWidth(str_repeat('m', strlen($maxnuma)) . $this->list_number_suffix);
15643				}
15644				break;
15645			case 'upper-roman':
15646			case 'I':
15647				if ($maxnum > 87) {
15648					$bbit = 87;
15649				} elseif ($maxnum > 86) {
15650					$bbit = 86;
15651				} elseif ($maxnum > 37) {
15652					$bbit = 38;
15653				} elseif ($maxnum > 36) {
15654					$bbit = 37;
15655				} elseif ($maxnum > 27) {
15656					$bbit = 28;
15657				} elseif ($maxnum > 26) {
15658					$bbit = 27;
15659				} elseif ($maxnum > 17) {
15660					$bbit = 18;
15661				} elseif ($maxnum > 16) {
15662					$bbit = 17;
15663				} elseif ($maxnum > 7) {
15664					$bbit = 8;
15665				} elseif ($maxnum > 6) {
15666					$bbit = 7;
15667				} elseif ($maxnum > 3) {
15668					$bbit = 4;
15669				} else {
15670					$bbit = $maxnum;
15671				}
15672
15673				$maxlnum = $decToRoman->convert($bbit, true);
15674				$blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);
15675
15676				break;
15677			case 'lower-roman':
15678			case 'i':
15679				if ($maxnum > 87) {
15680					$bbit = 87;
15681				} elseif ($maxnum > 86) {
15682					$bbit = 86;
15683				} elseif ($maxnum > 37) {
15684					$bbit = 38;
15685				} elseif ($maxnum > 36) {
15686					$bbit = 37;
15687				} elseif ($maxnum > 27) {
15688					$bbit = 28;
15689				} elseif ($maxnum > 26) {
15690					$bbit = 27;
15691				} elseif ($maxnum > 17) {
15692					$bbit = 18;
15693				} elseif ($maxnum > 16) {
15694					$bbit = 17;
15695				} elseif ($maxnum > 7) {
15696					$bbit = 8;
15697				} elseif ($maxnum > 6) {
15698					$bbit = 7;
15699				} elseif ($maxnum > 3) {
15700					$bbit = 4;
15701				} else {
15702					$bbit = $maxnum;
15703				}
15704				$maxlnum = $decToRoman->convert($bbit, false);
15705				$blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);
15706				break;
15707
15708			case 'disc':
15709			case 'circle':
15710			case 'square':
15711				$size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize);
15712				$offset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
15713				$blt_width = $size + $offset;
15714				break;
15715
15716			case 'arabic-indic':
15717				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0660), strlen($maxnum)) . $this->list_number_suffix);
15718				break;
15719			case 'persian':
15720			case 'urdu':
15721				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x06F0), strlen($maxnum)) . $this->list_number_suffix);
15722				break;
15723			case 'bengali':
15724				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x09E6), strlen($maxnum)) . $this->list_number_suffix);
15725				break;
15726			case 'devanagari':
15727				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0966), strlen($maxnum)) . $this->list_number_suffix);
15728				break;
15729			case 'gujarati':
15730				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0AE6), strlen($maxnum)) . $this->list_number_suffix);
15731				break;
15732			case 'gurmukhi':
15733				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0A66), strlen($maxnum)) . $this->list_number_suffix);
15734				break;
15735			case 'kannada':
15736				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0CE6), strlen($maxnum)) . $this->list_number_suffix);
15737				break;
15738			case 'malayalam':
15739				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(6, 0x0D66), strlen($maxnum)) . $this->list_number_suffix);
15740				break;
15741			case 'oriya':
15742				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0B66), strlen($maxnum)) . $this->list_number_suffix);
15743				break;
15744			case 'telugu':
15745				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0C66), strlen($maxnum)) . $this->list_number_suffix);
15746				break;
15747			case 'tamil':
15748				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(9, 0x0BE6), strlen($maxnum)) . $this->list_number_suffix);
15749				break;
15750			case 'thai':
15751				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(5, 0x0E50), strlen($maxnum)) . $this->list_number_suffix);
15752				break;
15753			default:
15754				$blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);
15755				break;
15756		}
15757
15758		return ($blt_width + $markeroffset);
15759	}
15760
15761	function _saveTextBuffer($t, $link = '', $intlink = '', $return = false)
15762	{
15763	// mPDF 6  Lists
15764		$arr = [];
15765		$arr[0] = $t;
15766		if (isset($link) && $link) {
15767			$arr[1] = $link;
15768		}
15769		$arr[2] = $this->currentfontstyle;
15770		if (isset($this->colorarray) && $this->colorarray) {
15771			$arr[3] = $this->colorarray;
15772		}
15773		$arr[4] = $this->currentfontfamily;
15774		$arr[5] = $this->currentLang; // mPDF 6
15775		if (isset($intlink) && $intlink) {
15776			$arr[7] = $intlink;
15777		}
15778		// mPDF 6
15779		// If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,
15780		// set for kerning via kern table
15781		// e.g. Latin script when useOTL set as 0x80
15782		if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {
15783			$this->textvar = ($this->textvar | TextVars::FC_KERNING);
15784		}
15785		$arr[8] = $this->textvar; // mPDF 5.7.1
15786		if (isset($this->textparam) && $this->textparam) {
15787			$arr[9] = $this->textparam;
15788		}
15789		if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) {
15790			$arr[10] = $this->spanbgcolorarray;
15791		}
15792		$arr[11] = $this->currentfontsize;
15793		if (isset($this->ReqFontStyle) && $this->ReqFontStyle) {
15794			$arr[12] = $this->ReqFontStyle;
15795		}
15796		if (isset($this->lSpacingCSS) && $this->lSpacingCSS) {
15797			$arr[14] = $this->lSpacingCSS;
15798		}
15799		if (isset($this->wSpacingCSS) && $this->wSpacingCSS) {
15800			$arr[15] = $this->wSpacingCSS;
15801		}
15802		if (isset($this->spanborddet) && $this->spanborddet) {
15803			$arr[16] = $this->spanborddet;
15804		}
15805		if (isset($this->textshadow) && $this->textshadow) {
15806			$arr[17] = $this->textshadow;
15807		}
15808		if (isset($this->OTLdata) && $this->OTLdata) {
15809			$arr[18] = $this->OTLdata;
15810			$this->OTLdata = [];
15811		} // mPDF 5.7.1
15812		else {
15813			$arr[18] = null;
15814		}
15815		// mPDF 6  Lists
15816		if ($return) {
15817			return ($arr);
15818		}
15819		if ($this->listitem) {
15820			$this->textbuffer[] = $this->listitem;
15821			$this->listitem = [];
15822		}
15823		$this->textbuffer[] = $arr;
15824	}
15825
15826	function _saveCellTextBuffer($t, $link = '', $intlink = '')
15827	{
15828		$arr = [];
15829		$arr[0] = $t;
15830		if (isset($link) && $link) {
15831			$arr[1] = $link;
15832		}
15833		$arr[2] = $this->currentfontstyle;
15834		if (isset($this->colorarray) && $this->colorarray) {
15835			$arr[3] = $this->colorarray;
15836		}
15837		$arr[4] = $this->currentfontfamily;
15838		if (isset($intlink) && $intlink) {
15839			$arr[7] = $intlink;
15840		}
15841		// mPDF 6
15842		// If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,
15843		// set for kerning via kern table
15844		// e.g. Latin script when useOTL set as 0x80
15845		if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {
15846			$this->textvar = ($this->textvar | TextVars::FC_KERNING);
15847		}
15848		$arr[8] = $this->textvar; // mPDF 5.7.1
15849		if (isset($this->textparam) && $this->textparam) {
15850			$arr[9] = $this->textparam;
15851		}
15852		if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) {
15853			$arr[10] = $this->spanbgcolorarray;
15854		}
15855		$arr[11] = $this->currentfontsize;
15856		if (isset($this->ReqFontStyle) && $this->ReqFontStyle) {
15857			$arr[12] = $this->ReqFontStyle;
15858		}
15859		if (isset($this->lSpacingCSS) && $this->lSpacingCSS) {
15860			$arr[14] = $this->lSpacingCSS;
15861		}
15862		if (isset($this->wSpacingCSS) && $this->wSpacingCSS) {
15863			$arr[15] = $this->wSpacingCSS;
15864		}
15865		if (isset($this->spanborddet) && $this->spanborddet) {
15866			$arr[16] = $this->spanborddet;
15867		}
15868		if (isset($this->textshadow) && $this->textshadow) {
15869			$arr[17] = $this->textshadow;
15870		}
15871		if (isset($this->OTLdata) && $this->OTLdata) {
15872			$arr[18] = $this->OTLdata;
15873			$this->OTLdata = [];
15874		} // mPDF 5.7.1
15875		else {
15876			$arr[18] = null;
15877		}
15878		$this->cell[$this->row][$this->col]['textbuffer'][] = $arr;
15879	}
15880
15881	function printbuffer($arrayaux, $blockstate = 0, $is_table = false, $table_draft = false, $cell_dir = '')
15882	{
15883		// $blockstate = 0;	// NO margins/padding
15884		// $blockstate = 1;	// Top margins/padding only
15885		// $blockstate = 2;	// Bottom margins/padding only
15886		// $blockstate = 3;	// Top & bottom margins/padding
15887		$this->spanbgcolorarray = '';
15888		$this->spanbgcolor = false;
15889		$this->spanborder = false;
15890		$this->spanborddet = [];
15891		$paint_ht_corr = 0;
15892		/* -- CSS-FLOAT -- */
15893		if (count($this->floatDivs)) {
15894			list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
15895			if (($this->blk[$this->blklvl]['inner_width'] - $l_width - $r_width) < (2 * $this->GetCharWidth('W', false))) {
15896				// Too narrow to fit - try to move down past L or R float
15897				if ($l_max < $r_max && ($this->blk[$this->blklvl]['inner_width'] - $r_width) > (2 * $this->GetCharWidth('W', false))) {
15898					$this->ClearFloats('LEFT', $this->blklvl);
15899				} elseif ($r_max < $l_max && ($this->blk[$this->blklvl]['inner_width'] - $l_width) > (2 * $this->GetCharWidth('W', false))) {
15900					$this->ClearFloats('RIGHT', $this->blklvl);
15901				} else {
15902					$this->ClearFloats('BOTH', $this->blklvl);
15903				}
15904			}
15905		}
15906		/* -- END CSS-FLOAT -- */
15907		$bak_y = $this->y;
15908		$bak_x = $this->x;
15909		$align = '';
15910		if (!$is_table) {
15911			if (isset($this->blk[$this->blklvl]['align']) && $this->blk[$this->blklvl]['align']) {
15912				$align = $this->blk[$this->blklvl]['align'];
15913			}
15914			// Block-align is set by e.g. <.. align="center"> Takes priority for this block but not inherited
15915			if (isset($this->blk[$this->blklvl]['block-align']) && $this->blk[$this->blklvl]['block-align']) {
15916				$align = $this->blk[$this->blklvl]['block-align'];
15917			}
15918			if (isset($this->blk[$this->blklvl]['direction'])) {
15919				$blockdir = $this->blk[$this->blklvl]['direction'];
15920			} else {
15921				$blockdir = "";
15922			}
15923			$this->divwidth = $this->blk[$this->blklvl]['width'];
15924		} else {
15925			$align = $this->cellTextAlign;
15926			$blockdir = $cell_dir;
15927		}
15928		$oldpage = $this->page;
15929
15930		// ADDED for Out of Block now done as Flowing Block
15931		if ($this->divwidth == 0) {
15932			$this->divwidth = $this->pgwidth;
15933		}
15934
15935		if (!$is_table) {
15936			$this->SetLineHeight($this->FontSizePt, $this->blk[$this->blklvl]['line_height']);
15937		}
15938		$this->divheight = $this->lineheight;
15939		$old_height = $this->divheight;
15940
15941		// As a failsafe - if font has been set but not output to page
15942		if (!$table_draft) {
15943			$this->SetFont($this->default_font, '', $this->default_font_size, true, true); // force output to page
15944		}
15945
15946		$this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, true, $blockdir, $table_draft);
15947
15948		$array_size = count($arrayaux);
15949
15950		// Added - Otherwise <div><div><p> did not output top margins/padding for 1st/2nd div
15951		if ($array_size == 0) {
15952			$this->finishFlowingBlock(true);
15953		} // true = END of flowing block
15954		// mPDF 6
15955		// ALL the chunks of textbuffer need to have at least basic OTLdata set
15956		// First make sure each element/chunk has the OTLdata for Bidi set.
15957		for ($i = 0; $i < $array_size; $i++) {
15958			if (empty($arrayaux[$i][18])) {
15959				if (substr($arrayaux[$i][0], 0, 3) == "\xbb\xa4\xac") { // object identifier has been identified!
15960					$unicode = [0xFFFC]; // Object replacement character
15961				} else {
15962					$unicode = $this->UTF8StringToArray($arrayaux[$i][0], false);
15963				}
15964				$is_strong = false;
15965				$this->getBasicOTLdata($arrayaux[$i][18], $unicode, $is_strong);
15966			}
15967			// Gets messed up if try and use core fonts inside a paragraph of text which needs to be BiDi re-ordered or OTLdata set
15968			if (($blockdir == 'rtl' || $this->biDirectional) && isset($arrayaux[$i][4]) && in_array($arrayaux[$i][4], ['ccourier', 'ctimes', 'chelvetica', 'csymbol', 'czapfdingbats'])) {
15969				throw new \Mpdf\MpdfException("You cannot use core fonts in a document which contains RTL text.");
15970			}
15971		}
15972		// mPDF 6
15973		// Process bidirectional text ready for bidi-re-ordering (which is done after line-breaks are established in WriteFlowingBlock etc.)
15974		if (($blockdir == 'rtl' || $this->biDirectional) && !$table_draft) {
15975			if (empty($this->otl)) {
15976				$this->otl = new Otl($this, $this->fontCache);
15977			}
15978			$this->otl->bidiPrepare($arrayaux, $blockdir);
15979			$array_size = count($arrayaux);
15980		}
15981
15982
15983		// Remove empty items // mPDF 6
15984		for ($i = $array_size - 1; $i > 0; $i--) {
15985			if (empty($arrayaux[$i][0]) && (isset($arrayaux[$i][16]) && $arrayaux[$i][16] !== '0') && empty($arrayaux[$i][7])) {
15986				unset($arrayaux[$i]);
15987			}
15988		}
15989
15990		// Correct adjoining borders for inline elements
15991		if (isset($arrayaux[0][16])) {
15992			$lastspanborder = $arrayaux[0][16];
15993		} else {
15994			$lastspanborder = false;
15995		}
15996		for ($i = 1; $i < $array_size; $i++) {
15997			if (isset($arrayaux[$i][16]) && $arrayaux[$i][16] == $lastspanborder &&
15998				((!isset($arrayaux[$i][9]['bord-decoration']) && !isset($arrayaux[$i - 1][9]['bord-decoration'])) ||
15999				(isset($arrayaux[$i][9]['bord-decoration']) && isset($arrayaux[$i - 1][9]['bord-decoration']) && $arrayaux[$i][9]['bord-decoration'] == $arrayaux[$i - 1][9]['bord-decoration'])
16000				)
16001			) {
16002				if (isset($arrayaux[$i][16]['R'])) {
16003					$lastspanborder = $arrayaux[$i][16];
16004				} else {
16005					$lastspanborder = false;
16006				}
16007				$arrayaux[$i][16]['L']['s'] = 0;
16008				$arrayaux[$i][16]['L']['w'] = 0;
16009				$arrayaux[$i - 1][16]['R']['s'] = 0;
16010				$arrayaux[$i - 1][16]['R']['w'] = 0;
16011			} else {
16012				if (isset($arrayaux[$i][16]['R'])) {
16013					$lastspanborder = $arrayaux[$i][16];
16014				} else {
16015					$lastspanborder = false;
16016				}
16017			}
16018		}
16019
16020		for ($i = 0; $i < $array_size; $i++) {
16021			// COLS
16022			$oldcolumn = $this->CurrCol;
16023			$vetor = isset($arrayaux[$i]) ? $arrayaux[$i] : null;
16024			if ($i == 0 && $vetor[0] != "\n" && ! $this->ispre) {
16025				$vetor[0] = ltrim($vetor[0]);
16026				if (!empty($vetor[18])) {
16027					$this->otl->trimOTLdata($vetor[18], true, false);
16028				} // *OTL*
16029			}
16030
16031			// FIXED TO ALLOW IT TO SHOW '0'
16032			if (empty($vetor[0]) && !($vetor[0] === '0') && empty($vetor[7])) { // Ignore empty text and not carrying an internal link
16033				// Check if it is the last element. If so then finish printing the block
16034				if ($i == ($array_size - 1)) {
16035					$this->finishFlowingBlock(true);
16036				} // true = END of flowing block
16037				continue;
16038			}
16039
16040
16041			// Activating buffer properties
16042			if (isset($vetor[11]) && $vetor[11] != '') {   // Font Size
16043				if ($is_table && $this->shrin_k) {
16044					$this->SetFontSize($vetor[11] / $this->shrin_k, false);
16045				} else {
16046					$this->SetFontSize($vetor[11], false);
16047				}
16048			}
16049
16050			if (isset($vetor[17]) && !empty($vetor[17])) { // TextShadow
16051				$this->textshadow = $vetor[17];
16052			}
16053			if (isset($vetor[16]) && !empty($vetor[16])) { // Border
16054				$this->spanborddet = $vetor[16];
16055				$this->spanborder = true;
16056			}
16057
16058			if (isset($vetor[15])) {   // Word spacing
16059				$this->wSpacingCSS = $vetor[15];
16060				if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
16061					$this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
16062				}
16063			}
16064			if (isset($vetor[14])) {   // Letter spacing
16065				$this->lSpacingCSS = $vetor[14];
16066				if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
16067					$this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
16068				}
16069			}
16070
16071
16072			if (isset($vetor[10]) and ! empty($vetor[10])) { // Background color
16073				$this->spanbgcolorarray = $vetor[10];
16074				$this->spanbgcolor = true;
16075			}
16076			if (isset($vetor[9]) and ! empty($vetor[9])) { // Text parameters - Outline + hyphens
16077				$this->textparam = $vetor[9];
16078				$this->SetTextOutline($this->textparam);
16079				// mPDF 5.7.3  inline text-decoration parameters
16080				if ($is_table && $this->shrin_k) {
16081					if (isset($this->textparam['text-baseline'])) {
16082						$this->textparam['text-baseline'] /= $this->shrin_k;
16083					}
16084					if (isset($this->textparam['decoration-baseline'])) {
16085						$this->textparam['decoration-baseline'] /= $this->shrin_k;
16086					}
16087					if (isset($this->textparam['decoration-fontsize'])) {
16088						$this->textparam['decoration-fontsize'] /= $this->shrin_k;
16089					}
16090				}
16091			}
16092			if (isset($vetor[8])) {  // mPDF 5.7.1
16093				$this->textvar = $vetor[8];
16094			}
16095			if (isset($vetor[7]) and $vetor[7] != '') { // internal target: <a name="anyvalue">
16096				$ily = $this->y;
16097				if ($this->table_rotate) {
16098					$this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "tbrot" => true];
16099				} elseif ($this->kwt) {
16100					$this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "kwt" => true];
16101				} elseif ($this->ColActive) {
16102					$this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol];
16103				} elseif (!$this->keep_block_together) {
16104					$this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page];
16105				}
16106				if (empty($vetor[0])) { // Ignore empty text
16107					// Check if it is the last element. If so then finish printing the block
16108					if ($i == ($array_size - 1)) {
16109						$this->finishFlowingBlock(true);
16110					} // true = END of flowing block
16111					continue;
16112				}
16113			}
16114			if (isset($vetor[5]) and $vetor[5] != '') {  // Language	// mPDF 6
16115				$this->currentLang = $vetor[5];
16116			}
16117			if (isset($vetor[4]) and $vetor[4] != '') {  // Font Family
16118				$font = $this->SetFont($vetor[4], $this->FontStyle, 0, false);
16119			}
16120			if (!empty($vetor[3])) { // Font Color
16121				$cor = $vetor[3];
16122				$this->SetTColor($cor);
16123			}
16124			if (isset($vetor[2]) and $vetor[2] != '') { // Bold,Italic styles
16125				$this->SetStyles($vetor[2]);
16126			}
16127
16128			if (isset($vetor[12]) and $vetor[12] != '') { // Requested Bold,Italic
16129				$this->ReqFontStyle = $vetor[12];
16130			}
16131			if (isset($vetor[1]) and $vetor[1] != '') { // LINK
16132				if (strpos($vetor[1], ".") === false && strpos($vetor[1], "@") !== 0) { // assuming every external link has a dot indicating extension (e.g: .html .txt .zip www.somewhere.com etc.)
16133					// Repeated reference to same anchor?
16134					while (array_key_exists($vetor[1], $this->internallink)) {
16135						$vetor[1] = "#" . $vetor[1];
16136					}
16137					$this->internallink[$vetor[1]] = $this->AddLink();
16138					$vetor[1] = $this->internallink[$vetor[1]];
16139				}
16140				$this->HREF = $vetor[1];     // HREF link style set here ******
16141			}
16142
16143			// SPECIAL CONTENT - IMAGES & FORM OBJECTS
16144			// Print-out special content
16145
16146			if (substr($vetor[0], 0, 3) == "\xbb\xa4\xac") { // identifier has been identified!
16147				$objattr = $this->_getObjAttr($vetor[0]);
16148
16149				/* -- TABLES -- */
16150				if ($objattr['type'] == 'nestedtable') {
16151					if ($objattr['nestedcontent']) {
16152						$level = $objattr['level'];
16153						$table = &$this->table[$level][$objattr['table']];
16154
16155						if ($table_draft) {
16156							$this->y += $this->table[($level + 1)][$objattr['nestedcontent']]['h']; // nested table height
16157							$this->finishFlowingBlock(false, 'nestedtable');
16158						} else {
16159							$cell = &$table['cells'][$objattr['row']][$objattr['col']];
16160							$this->finishFlowingBlock(false, 'nestedtable');
16161							$save_dw = $this->divwidth;
16162							$save_buffer = $this->cellBorderBuffer;
16163							$this->cellBorderBuffer = [];
16164							$ncx = $this->x;
16165							list($dummyx, $w) = $this->_tableGetWidth($table, $objattr['row'], $objattr['col']);
16166							$ntw = $this->table[($level + 1)][$objattr['nestedcontent']]['w']; // nested table width
16167							if (!$this->simpleTables) {
16168								if ($this->packTableData) {
16169									list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cell['borderbin']);
16170								} else {
16171									$br = $cell['border_details']['R']['w'];
16172									$bl = $cell['border_details']['L']['w'];
16173								}
16174								if ($table['borders_separate']) {
16175									$innerw = $w - $bl - $br - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];
16176								} else {
16177									$innerw = $w - $bl / 2 - $br / 2 - $cell['padding']['L'] - $cell['padding']['R'];
16178								}
16179							} elseif ($this->simpleTables) {
16180								if ($table['borders_separate']) {
16181									$innerw = $w - $table['simple']['border_details']['L']['w'] - $table['simple']['border_details']['R']['w'] - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];
16182								} else {
16183									$innerw = $w - $table['simple']['border_details']['L']['w'] / 2 - $table['simple']['border_details']['R']['w'] / 2 - $cell['padding']['L'] - $cell['padding']['R'];
16184								}
16185							}
16186							if ($cell['a'] == 'C' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'C') {
16187								$ncx += ($innerw - $ntw) / 2;
16188							} elseif ($cell['a'] == 'R' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'R') {
16189								$ncx += $innerw - $ntw;
16190							}
16191							$this->x = $ncx;
16192
16193							$this->_tableWrite($this->table[($level + 1)][$objattr['nestedcontent']]);
16194							$this->cellBorderBuffer = $save_buffer;
16195							$this->x = $bak_x;
16196							$this->divwidth = $save_dw;
16197						}
16198
16199						$this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
16200					}
16201				} else {
16202					/* -- END TABLES -- */
16203					if ($is_table) { // *TABLES*
16204						$maxWidth = $this->divwidth;  // *TABLES*
16205					} // *TABLES*
16206					else { // *TABLES*
16207						$maxWidth = $this->divwidth - ($this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_right'] + $this->blk[$this->blklvl]['border_right']['w']);
16208					} // *TABLES*
16209
16210					/* -- CSS-IMAGE-FLOAT -- */
16211					// If float (already) exists at this level
16212					if (isset($this->floatmargins['R']) && $this->y <= $this->floatmargins['R']['y1'] && $this->y >= $this->floatmargins['R']['y0']) {
16213						$maxWidth -= $this->floatmargins['R']['w'];
16214					}
16215					if (isset($this->floatmargins['L']) && $this->y <= $this->floatmargins['L']['y1'] && $this->y >= $this->floatmargins['L']['y0']) {
16216						$maxWidth -= $this->floatmargins['L']['w'];
16217					}
16218					/* -- END CSS-IMAGE-FLOAT -- */
16219
16220					list($skipln) = $this->inlineObject($objattr['type'], '', $this->y, $objattr, $this->lMargin, ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE), $maxWidth, $this->flowingBlockAttr['height'], false, $is_table);
16221					//  1 -> New line needed because of width
16222					// -1 -> Will fit width on line but NEW PAGE REQUIRED because of height
16223					// -2 -> Will not fit on line therefore needs new line but thus NEW PAGE REQUIRED
16224					$iby = $this->y;
16225					$oldpage = $this->page;
16226					$oldcol = $this->CurrCol;
16227					if (($skipln == 1 || $skipln == -2) && !isset($objattr['float'])) {
16228						$this->finishFlowingBlock(false, $objattr['type']);
16229						$this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
16230					}
16231
16232					if (!$table_draft) {
16233						$thispage = $this->page;
16234						if ($this->CurrCol != $oldcol) {
16235							$changedcol = true;
16236						} else {
16237							$changedcol = false;
16238						}
16239
16240						// the previous lines can already have triggered page break or column change
16241						if (!$changedcol && $skipln < 0 && $this->AcceptPageBreak() && $thispage == $oldpage) {
16242							$this->AddPage($this->CurOrientation);
16243
16244							// Added to correct Images already set on line before page advanced
16245							// i.e. if second inline image on line is higher than first and forces new page
16246							if (count($this->objectbuffer)) {
16247								$yadj = $iby - $this->y;
16248								foreach ($this->objectbuffer as $ib => $val) {
16249									if ($this->objectbuffer[$ib]['OUTER-Y']) {
16250										$this->objectbuffer[$ib]['OUTER-Y'] -= $yadj;
16251									}
16252									if ($this->objectbuffer[$ib]['BORDER-Y']) {
16253										$this->objectbuffer[$ib]['BORDER-Y'] -= $yadj;
16254									}
16255									if ($this->objectbuffer[$ib]['INNER-Y']) {
16256										$this->objectbuffer[$ib]['INNER-Y'] -= $yadj;
16257									}
16258								}
16259							}
16260						}
16261
16262						// Added to correct for OddEven Margins
16263						if ($this->page != $oldpage) {
16264							if (($this->page - $oldpage) % 2 == 1) {
16265								$bak_x += $this->MarginCorrection;
16266							}
16267							$oldpage = $this->page;
16268							$y = $this->tMargin - $paint_ht_corr;
16269							$this->oldy = $this->tMargin - $paint_ht_corr;
16270							$old_height = 0;
16271						}
16272						$this->x = $bak_x;
16273						/* -- COLUMNS -- */
16274						// COLS
16275						// OR COLUMN CHANGE
16276						if ($this->CurrCol != $oldcolumn) {
16277							if ($this->directionality == 'rtl') { // *OTL*
16278								$bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
16279							} // *OTL*
16280							else { // *OTL*
16281								$bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
16282							} // *OTL*
16283							$this->x = $bak_x;
16284							$oldcolumn = $this->CurrCol;
16285							$y = $this->y0 - $paint_ht_corr;
16286							$this->oldy = $this->y0 - $paint_ht_corr;
16287							$old_height = 0;
16288						}
16289						/* -- END COLUMNS -- */
16290					}
16291
16292					/* -- CSS-IMAGE-FLOAT -- */
16293					if ($objattr['type'] == 'image' && isset($objattr['float'])) {
16294						$fy = $this->y;
16295
16296						// DIV TOP MARGIN/BORDER/PADDING
16297						if ($this->flowingBlockAttr['newblock'] && ($this->flowingBlockAttr['blockstate'] == 1 || $this->flowingBlockAttr['blockstate'] == 3) && $this->flowingBlockAttr['lineCount'] == 0) {
16298							$fy += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
16299						}
16300
16301						if ($objattr['float'] == 'R') {
16302							$fx = $this->w - $this->rMargin - $objattr['width'] - ($this->blk[$this->blklvl]['outer_right_margin'] + $this->blk[$this->blklvl]['border_right']['w'] + $this->blk[$this->blklvl]['padding_right']);
16303						} elseif ($objattr['float'] == 'L') {
16304							$fx = $this->lMargin + ($this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left']);
16305						}
16306						$w = $objattr['width'];
16307						$h = abs($objattr['height']);
16308
16309						$widthLeft = $maxWidth - ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE);
16310						$maxHeight = $this->h - ($this->tMargin + $this->margin_header + $this->bMargin + 10);
16311						// For Images
16312						$extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']);
16313						$extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']);
16314
16315						if ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg') {
16316							$file = $objattr['file'];
16317							$info = $this->formobjects[$file];
16318						} else {
16319							$file = $objattr['file'];
16320							$info = $this->images[$file];
16321						}
16322						$img_w = $w - $extraWidth;
16323						$img_h = $h - $extraHeight;
16324						if ($objattr['border_left']['w']) {
16325							$objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] + $objattr['border_right']['w']) / 2);
16326							$objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] + $objattr['border_bottom']['w']) / 2);
16327							$objattr['BORDER-X'] = $fx + $objattr['margin_left'] + (($objattr['border_left']['w']) / 2);
16328							$objattr['BORDER-Y'] = $fy + $objattr['margin_top'] + (($objattr['border_top']['w']) / 2);
16329						}
16330						$objattr['INNER-WIDTH'] = $img_w;
16331						$objattr['INNER-HEIGHT'] = $img_h;
16332						$objattr['INNER-X'] = $fx + $objattr['margin_left'] + ($objattr['border_left']['w']);
16333						$objattr['INNER-Y'] = $fy + $objattr['margin_top'] + ($objattr['border_top']['w']);
16334						$objattr['ID'] = $info['i'];
16335						$objattr['OUTER-WIDTH'] = $w;
16336						$objattr['OUTER-HEIGHT'] = $h;
16337						$objattr['OUTER-X'] = $fx;
16338						$objattr['OUTER-Y'] = $fy;
16339						if ($objattr['float'] == 'R') {
16340							// If R float already exists at this level
16341							$this->floatmargins['R']['skipline'] = false;
16342							if (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {
16343								$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
16344							} // If L float already exists at this level
16345							elseif (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {
16346								// Final check distance between floats is not now too narrow to fit text
16347								$mw = 2 * $this->GetCharWidth('W', false);
16348								if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['L']['w']) < $mw) {
16349									$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
16350								} else {
16351									$this->floatmargins['R']['x'] = $fx;
16352									$this->floatmargins['R']['w'] = $w;
16353									$this->floatmargins['R']['y0'] = $fy;
16354									$this->floatmargins['R']['y1'] = $fy + $h;
16355									if ($skipln == 1) {
16356										$this->floatmargins['R']['skipline'] = true;
16357										$this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;
16358										$objattr['skipline'] = true;
16359									}
16360									$this->floatbuffer[] = $objattr;
16361								}
16362							} else {
16363								$this->floatmargins['R']['x'] = $fx;
16364								$this->floatmargins['R']['w'] = $w;
16365								$this->floatmargins['R']['y0'] = $fy;
16366								$this->floatmargins['R']['y1'] = $fy + $h;
16367								if ($skipln == 1) {
16368									$this->floatmargins['R']['skipline'] = true;
16369									$this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;
16370									$objattr['skipline'] = true;
16371								}
16372								$this->floatbuffer[] = $objattr;
16373							}
16374						} elseif ($objattr['float'] == 'L') {
16375							// If L float already exists at this level
16376							$this->floatmargins['L']['skipline'] = false;
16377							if (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {
16378								$this->floatmargins['L']['skipline'] = false;
16379								$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
16380							} // If R float already exists at this level
16381							elseif (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {
16382								// Final check distance between floats is not now too narrow to fit text
16383								$mw = 2 * $this->GetCharWidth('W', false);
16384								if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['R']['w']) < $mw) {
16385									$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
16386								} else {
16387									$this->floatmargins['L']['x'] = $fx + $w;
16388									$this->floatmargins['L']['w'] = $w;
16389									$this->floatmargins['L']['y0'] = $fy;
16390									$this->floatmargins['L']['y1'] = $fy + $h;
16391									if ($skipln == 1) {
16392										$this->floatmargins['L']['skipline'] = true;
16393										$this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;
16394										$objattr['skipline'] = true;
16395									}
16396									$this->floatbuffer[] = $objattr;
16397								}
16398							} else {
16399								$this->floatmargins['L']['x'] = $fx + $w;
16400								$this->floatmargins['L']['w'] = $w;
16401								$this->floatmargins['L']['y0'] = $fy;
16402								$this->floatmargins['L']['y1'] = $fy + $h;
16403								if ($skipln == 1) {
16404									$this->floatmargins['L']['skipline'] = true;
16405									$this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;
16406									$objattr['skipline'] = true;
16407								}
16408								$this->floatbuffer[] = $objattr;
16409							}
16410						}
16411					} else {
16412						/* -- END CSS-IMAGE-FLOAT -- */
16413						$this->WriteFlowingBlock($vetor[0], (isset($vetor[18]) ? $vetor[18] : null));  // mPDF 5.7.1
16414						/* -- CSS-IMAGE-FLOAT -- */
16415					}
16416					/* -- END CSS-IMAGE-FLOAT -- */
16417				} // *TABLES*
16418			} // END If special content
16419			else { // THE text
16420				if ($this->tableLevel) {
16421					$paint_ht_corr = 0;
16422				} // To move the y up when new column/page started if div border needed
16423				else {
16424					$paint_ht_corr = $this->blk[$this->blklvl]['border_top']['w'];
16425				}
16426
16427				if ($vetor[0] == "\n") { // We are reading a <BR> now turned into newline ("\n")
16428					if ($this->flowingBlockAttr['content']) {
16429						$this->finishFlowingBlock(false, 'br');
16430					} elseif ($is_table) {
16431						$this->y+= $this->_computeLineheight($this->cellLineHeight);
16432					} elseif (!$is_table) {
16433						$this->DivLn($this->lineheight);
16434						if ($this->ColActive) {
16435							$this->breakpoints[$this->CurrCol][] = $this->y;
16436						} // *COLUMNS*
16437					}
16438					// Added to correct for OddEven Margins
16439					if ($this->page != $oldpage) {
16440						if (($this->page - $oldpage) % 2 == 1) {
16441							$bak_x += $this->MarginCorrection;
16442						}
16443						$oldpage = $this->page;
16444						$y = $this->tMargin - $paint_ht_corr;
16445						$this->oldy = $this->tMargin - $paint_ht_corr;
16446						$old_height = 0;
16447					}
16448					$this->x = $bak_x;
16449					/* -- COLUMNS -- */
16450					// COLS
16451					// OR COLUMN CHANGE
16452					if ($this->CurrCol != $oldcolumn) {
16453						if ($this->directionality == 'rtl') { // *OTL*
16454							$bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
16455						} // *OTL*
16456						else { // *OTL*
16457							$bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
16458						} // *OTL*
16459						$this->x = $bak_x;
16460						$oldcolumn = $this->CurrCol;
16461						$y = $this->y0 - $paint_ht_corr;
16462						$this->oldy = $this->y0 - $paint_ht_corr;
16463						$old_height = 0;
16464					}
16465					/* -- END COLUMNS -- */
16466					$this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
16467				} else {
16468					$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
16469					// Added to correct for OddEven Margins
16470					if ($this->page != $oldpage) {
16471						if (($this->page - $oldpage) % 2 == 1) {
16472							$bak_x += $this->MarginCorrection;
16473							$this->x = $bak_x;
16474						}
16475						$oldpage = $this->page;
16476						$y = $this->tMargin - $paint_ht_corr;
16477						$this->oldy = $this->tMargin - $paint_ht_corr;
16478						$old_height = 0;
16479					}
16480					/* -- COLUMNS -- */
16481					// COLS
16482					// OR COLUMN CHANGE
16483					if ($this->CurrCol != $oldcolumn) {
16484						if ($this->directionality == 'rtl') { // *OTL*
16485							$bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
16486						} // *OTL*
16487						else { // *OTL*
16488							$bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
16489						} // *OTL*
16490						$this->x = $bak_x;
16491						$oldcolumn = $this->CurrCol;
16492						$y = $this->y0 - $paint_ht_corr;
16493						$this->oldy = $this->y0 - $paint_ht_corr;
16494						$old_height = 0;
16495					}
16496					/* -- END COLUMNS -- */
16497				}
16498			}
16499
16500			// Check if it is the last element. If so then finish printing the block
16501			if ($i == ($array_size - 1)) {
16502				$this->finishFlowingBlock(true); // true = END of flowing block
16503				// Added to correct for OddEven Margins
16504				if ($this->page != $oldpage) {
16505					if (($this->page - $oldpage) % 2 == 1) {
16506						$bak_x += $this->MarginCorrection;
16507						$this->x = $bak_x;
16508					}
16509					$oldpage = $this->page;
16510					$y = $this->tMargin - $paint_ht_corr;
16511					$this->oldy = $this->tMargin - $paint_ht_corr;
16512					$old_height = 0;
16513				}
16514
16515				/* -- COLUMNS -- */
16516				// COLS
16517				// OR COLUMN CHANGE
16518				if ($this->CurrCol != $oldcolumn) {
16519					if ($this->directionality == 'rtl') { // *OTL*
16520						$bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
16521					} // *OTL*
16522					else { // *OTL*
16523						$bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
16524					} // *OTL*
16525					$this->x = $bak_x;
16526					$oldcolumn = $this->CurrCol;
16527					$y = $this->y0 - $paint_ht_corr;
16528					$this->oldy = $this->y0 - $paint_ht_corr;
16529					$old_height = 0;
16530				}
16531				/* -- END COLUMNS -- */
16532			}
16533
16534			// RESETTING VALUES
16535			$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
16536			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
16537			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
16538			$this->colorarray = '';
16539			$this->spanbgcolorarray = '';
16540			$this->spanbgcolor = false;
16541			$this->spanborder = false;
16542			$this->spanborddet = [];
16543			$this->HREF = '';
16544			$this->textparam = [];
16545			$this->SetTextOutline();
16546
16547			$this->textvar = 0x00; // mPDF 5.7.1
16548			$this->OTLtags = [];
16549			$this->textshadow = '';
16550
16551			$this->currentfontfamily = '';
16552			$this->currentfontsize = '';
16553			$this->currentfontstyle = '';
16554			$this->currentLang = $this->default_lang;  // mPDF 6
16555			$this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6
16556			/* -- TABLES -- */
16557			if ($this->tableLevel) {
16558				$this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES*
16559			} else { 			/* -- END TABLES -- */
16560				if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {
16561					$this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height
16562				}
16563			}
16564			$this->ResetStyles();
16565			$this->lSpacingCSS = '';
16566			$this->wSpacingCSS = '';
16567			$this->fixedlSpacing = false;
16568			$this->minwSpacing = 0;
16569			$this->SetDash();
16570			$this->dash_on = false;
16571			$this->dotted_on = false;
16572		}//end of for(i=0;i<arraysize;i++)
16573
16574		$this->Reset(); // mPDF 6
16575		// PAINT DIV BORDER	// DISABLED IN COLUMNS AS DOESN'T WORK WHEN BROKEN ACROSS COLS??
16576		if ((isset($this->blk[$this->blklvl]['border']) || isset($this->blk[$this->blklvl]['bgcolor']) || isset($this->blk[$this->blklvl]['box_shadow'])) && $blockstate && ($this->y != $this->oldy)) {
16577			$bottom_y = $this->y; // Does not include Bottom Margin
16578			if (isset($this->blk[$this->blklvl]['startpage']) && $this->blk[$this->blklvl]['startpage'] != $this->page && $blockstate != 1) {
16579				$this->PaintDivBB('pagetop', $blockstate);
16580			} elseif ($blockstate != 1) {
16581				$this->PaintDivBB('', $blockstate);
16582			}
16583			$this->y = $bottom_y;
16584			$this->x = $bak_x;
16585		}
16586
16587		// Reset Font
16588		$this->SetFontSize($this->default_font_size, false);
16589		if ($table_draft) {
16590			$ch = $this->y - $bak_y;
16591			$this->y = $bak_y;
16592			$this->x = $bak_x;
16593			return $ch;
16594		}
16595	}
16596
16597	function _setDashBorder($style, $div, $cp, $side)
16598	{
16599		if ($style == 'dashed' && (($side == 'L' || $side == 'R') || ($side == 'T' && $div != 'pagetop' && !$cp) || ($side == 'B' && $div != 'pagebottom') )) {
16600			$dashsize = 2; // final dash will be this + 1*linewidth
16601			$dashsizek = 1.5; // ratio of Dash/Blank
16602			$this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));
16603		} elseif ($style == 'dotted' || ($side == 'T' && ($div == 'pagetop' || $cp)) || ($side == 'B' && $div == 'pagebottom')) {
16604			// Round join and cap
16605			$this->SetLineJoin(1);
16606			$this->SetLineCap(1);
16607			$this->SetDash(0.001, ($this->LineWidth * 3));
16608		}
16609	}
16610
16611	function _setBorderLine($b, $k = 1)
16612	{
16613		$this->SetLineWidth($b['w'] / $k);
16614		$this->SetDColor($b['c']);
16615		if ($b['c'][0] == 5) { // RGBa
16616			$this->SetAlpha(ord($b['c'][4]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2
16617		} elseif ($b['c'][0] == 6) { // CMYKa
16618			$this->SetAlpha(ord($b['c'][5]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2
16619		}
16620	}
16621
16622	function PaintDivBB($divider = '', $blockstate = 0, $blvl = 0)
16623	{
16624		// Borders & backgrounds are done elsewhere for columns - messes up the repositioning in printcolumnbuffer
16625		if ($this->ColActive) {
16626			return;
16627		} // *COLUMNS*
16628		if ($this->keep_block_together) {
16629			return;
16630		} // mPDF 6
16631		$save_y = $this->y;
16632		if (!$blvl) {
16633			$blvl = $this->blklvl;
16634		}
16635		$x0 = $x1 = $y0 = $y1 = 0;
16636
16637		// Added mPDF 3.0 Float DIV
16638		if (isset($this->blk[$blvl]['bb_painted'][$this->page]) && $this->blk[$blvl]['bb_painted'][$this->page]) {
16639			return;
16640		} // *CSS-FLOAT*
16641
16642		if (isset($this->blk[$blvl]['x0'])) {
16643			$x0 = $this->blk[$blvl]['x0'];
16644		} // left
16645		if (isset($this->blk[$blvl]['y1'])) {
16646			$y1 = $this->blk[$blvl]['y1'];
16647		} // bottom
16648		// Added mPDF 3.0 Float DIV - ensures backgrounds/borders are drawn to bottom of page
16649		if ($y1 == 0) {
16650			if ($divider == 'pagebottom') {
16651				$y1 = $this->h - $this->bMargin;
16652			} else {
16653				$y1 = $this->y;
16654			}
16655		}
16656
16657		$continuingpage = (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page);
16658
16659		if (isset($this->blk[$blvl]['y0'])) {
16660			$y0 = $this->blk[$blvl]['y0'];
16661		}
16662		$h = $y1 - $y0;
16663		$w = $this->blk[$blvl]['width'];
16664		$x1 = $x0 + $w;
16665
16666		// Set border-widths as used here
16667		$border_top = $this->blk[$blvl]['border_top']['w'];
16668		$border_bottom = $this->blk[$blvl]['border_bottom']['w'];
16669		$border_left = $this->blk[$blvl]['border_left']['w'];
16670		$border_right = $this->blk[$blvl]['border_right']['w'];
16671		if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {
16672			$border_top = 0;
16673		}
16674		if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {
16675			$border_bottom = 0;
16676		}
16677
16678		$brTL_H = 0;
16679		$brTL_V = 0;
16680		$brTR_H = 0;
16681		$brTR_V = 0;
16682		$brBL_H = 0;
16683		$brBL_V = 0;
16684		$brBR_H = 0;
16685		$brBR_V = 0;
16686
16687		$brset = false;
16688		/* -- BORDER-RADIUS -- */
16689		if (isset($this->blk[$blvl]['border_radius_TL_H'])) {
16690			$brTL_H = $this->blk[$blvl]['border_radius_TL_H'];
16691			$brset = true;
16692		}
16693		if (isset($this->blk[$blvl]['border_radius_TL_V'])) {
16694			$brTL_V = $this->blk[$blvl]['border_radius_TL_V'];
16695			$brset = true;
16696		}
16697		if (isset($this->blk[$blvl]['border_radius_TR_H'])) {
16698			$brTR_H = $this->blk[$blvl]['border_radius_TR_H'];
16699			$brset = true;
16700		}
16701		if (isset($this->blk[$blvl]['border_radius_TR_V'])) {
16702			$brTR_V = $this->blk[$blvl]['border_radius_TR_V'];
16703			$brset = true;
16704		}
16705		if (isset($this->blk[$blvl]['border_radius_BR_H'])) {
16706			$brBR_H = $this->blk[$blvl]['border_radius_BR_H'];
16707			$brset = true;
16708		}
16709		if (isset($this->blk[$blvl]['border_radius_BR_V'])) {
16710			$brBR_V = $this->blk[$blvl]['border_radius_BR_V'];
16711			$brset = true;
16712		}
16713		if (isset($this->blk[$blvl]['border_radius_BL_H'])) {
16714			$brBL_H = $this->blk[$blvl]['border_radius_BL_H'];
16715			$brset = true;
16716		}
16717		if (isset($this->blk[$blvl]['border_radius_BL_V'])) {
16718			$brBL_V = $this->blk[$blvl]['border_radius_BL_V'];
16719			$brset = true;
16720		}
16721
16722		if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {
16723			$brTL_H = 0;
16724			$brTL_V = 0;
16725			$brTR_H = 0;
16726			$brTR_V = 0;
16727		}
16728		if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {
16729			$brBL_H = 0;
16730			$brBL_V = 0;
16731			$brBR_H = 0;
16732			$brBR_V = 0;
16733		}
16734
16735		// Disallow border-radius if it is smaller than the border width.
16736		if ($brTL_H < min($border_left, $border_top)) {
16737			$brTL_H = $brTL_V = 0;
16738		}
16739		if ($brTL_V < min($border_left, $border_top)) {
16740			$brTL_V = $brTL_H = 0;
16741		}
16742		if ($brTR_H < min($border_right, $border_top)) {
16743			$brTR_H = $brTR_V = 0;
16744		}
16745		if ($brTR_V < min($border_right, $border_top)) {
16746			$brTR_V = $brTR_H = 0;
16747		}
16748		if ($brBL_H < min($border_left, $border_bottom)) {
16749			$brBL_H = $brBL_V = 0;
16750		}
16751		if ($brBL_V < min($border_left, $border_bottom)) {
16752			$brBL_V = $brBL_H = 0;
16753		}
16754		if ($brBR_H < min($border_right, $border_bottom)) {
16755			$brBR_H = $brBR_V = 0;
16756		}
16757		if ($brBR_V < min($border_right, $border_bottom)) {
16758			$brBR_V = $brBR_H = 0;
16759		}
16760
16761		// CHECK FOR radii that sum to > width or height of div ********
16762		$f = min($h / ($brTL_V + $brBL_V + 0.001), $h / ($brTR_V + $brBR_V + 0.001), $w / ($brTL_H + $brTR_H + 0.001), $w / ($brBL_H + $brBR_H + 0.001));
16763		if ($f < 1) {
16764			$brTL_H *= $f;
16765			$brTL_V *= $f;
16766			$brTR_H *= $f;
16767			$brTR_V *= $f;
16768			$brBL_H *= $f;
16769			$brBL_V *= $f;
16770			$brBR_H *= $f;
16771			$brBR_V *= $f;
16772		}
16773		/* -- END BORDER-RADIUS -- */
16774
16775		$tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
16776		for ($l = 0; $l <= $blvl; $l++) {
16777			if ($this->blk[$l]['bgcolor']) {
16778				$tbcol = $this->blk[$l]['bgcolorarray'];
16779			}
16780		}
16781
16782		// BORDERS
16783		if (isset($this->blk[$blvl]['y0']) && $this->blk[$blvl]['y0']) {
16784			$y0 = $this->blk[$blvl]['y0'];
16785		}
16786		$h = $y1 - $y0;
16787		$w = $this->blk[$blvl]['width'];
16788
16789		if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
16790			$tbd = $this->blk[$blvl]['border_top'];
16791
16792			$legend = '';
16793			$legbreakL = 0;
16794			$legbreakR = 0;
16795			// BORDER LEGEND
16796			if (isset($this->blk[$blvl]['border_legend']) && $this->blk[$blvl]['border_legend']) {
16797				$legend = $this->blk[$blvl]['border_legend']; // Same structure array as textbuffer
16798				$txt = $legend[0] = ltrim($legend[0]);
16799				if (!empty($legend[18])) {
16800					$this->otl->trimOTLdata($legend[18], true, false);
16801				} // *OTL*
16802				// Set font, size, style, color
16803				$this->SetFont($legend[4], $legend[2], $legend[11]);
16804				if (isset($legend[3]) && $legend[3]) {
16805					$cor = $legend[3];
16806					$this->SetTColor($cor);
16807				}
16808				$stringWidth = $this->GetStringWidth($txt, true, $legend[18], $legend[8]);
16809				$save_x = $this->x;
16810				$save_y = $this->y;
16811				$save_currentfontfamily = $this->FontFamily;
16812				$save_currentfontsize = $this->FontSizePt;
16813				$save_currentfontstyle = $this->FontStyle;
16814				$this->y = $y0 - $this->FontSize / 2 + $this->blk[$blvl]['border_top']['w'] / 2;
16815				$this->x = $x0 + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_left']['w'];
16816
16817				// Set the distance from the border line to the text ? make configurable variable
16818				$gap = 0.2 * $this->FontSize;
16819				$legbreakL = $this->x - $gap;
16820				$legbreakR = $this->x + $stringWidth + $gap;
16821				$this->magic_reverse_dir($txt, $this->blk[$blvl]['direction'], $legend[18]);
16822				$fill = '';
16823				$this->Cell($stringWidth, $this->FontSize, $txt, '', 0, 'C', $fill, '', 0, 0, 0, 'M', $fill, false, $legend[18], $legend[8]);
16824				// Reset
16825				$this->x = $save_x;
16826				$this->y = $save_y;
16827				$this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);
16828				$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
16829			}
16830
16831			if (isset($tbd['s']) && $tbd['s']) {
16832				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
16833					$this->writer->write('q');
16834					$this->SetLineWidth(0);
16835					$this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
16836					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
16837					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
16838					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
16839					$this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path
16840				}
16841
16842				$this->_setBorderLine($tbd);
16843				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
16844					$legbreakL -= $border_top / 2; // because line cap different
16845					$legbreakR += $border_top / 2;
16846					$this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'T');
16847				} /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brTR_V && $brTR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
16848					$this->SetLineJoin(0);
16849					$this->SetLineCap(0);
16850				}
16851				$s = '';
16852				if ($brTR_H && $brTR_V) {
16853					$s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_top / 2, $brTR_V - $border_top / 2, 1, 2, true)) . "\n";
16854				} else { 				/* -- END BORDER-RADIUS -- */
16855					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
16856						$s .= (sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16857					} else {
16858						$s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16859					}
16860				}
16861				/* -- BORDER-RADIUS -- */
16862				if ($brTL_H && $brTL_V) {
16863					if ($legend) {
16864						if ($legbreakR < ($x0 + $w - $brTR_H)) {
16865							$s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16866						}
16867						if ($legbreakL > ($x0 + $brTL_H )) {
16868							$s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16869							$s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE) . "\n");
16870						} else {
16871							$s .= (sprintf('%.3F %.3F m ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16872						}
16873					} else {
16874						$s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16875					}
16876					$s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_top / 2, $brTL_V - $border_top / 2, 2, 1)) . "\n";
16877				} else {
16878					/* -- END BORDER-RADIUS -- */
16879					if ($legend) {
16880						if ($legbreakR < ($x0 + $w)) {
16881							$s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16882						}
16883						if ($legbreakL > ($x0)) {
16884							$s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16885							if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
16886								$s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16887							} else {
16888								$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16889							}
16890						} elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
16891							$s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16892						} else {
16893							$s .= (sprintf('%.3F %.3F m ', ($x0 + $border_top / 2) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16894						}
16895					} elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
16896						$s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16897					} else {
16898						$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
16899					}
16900					/* -- BORDER-RADIUS -- */
16901				}
16902				/* -- END BORDER-RADIUS -- */
16903				$s .= 'S' . "\n";
16904				$this->writer->write($s);
16905
16906				if ($tbd['style'] == 'double') {
16907					$this->SetLineWidth($tbd['w'] / 3);
16908					$this->SetDColor($tbcol);
16909					$this->writer->write($s);
16910				}
16911				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
16912					$this->writer->write('Q');
16913				}
16914
16915				// Reset Corners and Dash off
16916				$this->SetLineWidth(0.1);
16917				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
16918				$this->SetLineJoin(2);
16919				$this->SetLineCap(2);
16920				$this->SetDash();
16921			}
16922		}
16923		// Reinstate line above for dotted line divider when block border crosses a page
16924		// elseif ($divider == 'pagetop' || $continuingpage) {
16925
16926		if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
16927			$tbd = $this->blk[$blvl]['border_bottom'];
16928			if (isset($tbd['s']) && $tbd['s']) {
16929				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
16930					$this->writer->write('q');
16931					$this->SetLineWidth(0);
16932					$this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
16933					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
16934					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
16935					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
16936					$this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path
16937				}
16938
16939				$this->_setBorderLine($tbd);
16940				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
16941					$this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'B');
16942				} /* -- BORDER-RADIUS -- */ elseif (($brBL_V && $brBL_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
16943					$this->SetLineJoin(0);
16944					$this->SetLineCap(0);
16945				}
16946				$s = '';
16947				if ($brBL_H && $brBL_V) {
16948					$s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_bottom / 2, $brBL_V - $border_bottom / 2, 3, 2, true)) . "\n";
16949				} else { 				/* -- END BORDER-RADIUS -- */
16950					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
16951						$s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
16952					} else {
16953						$s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
16954					}
16955				}
16956				/* -- BORDER-RADIUS -- */
16957				if ($brBR_H && $brBR_V) {
16958					$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2) - $brBR_H ) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
16959					$s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_bottom / 2, $brBR_V - $border_bottom / 2, 4, 1)) . "\n";
16960				} else { 				/* -- END BORDER-RADIUS -- */
16961					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
16962						$s .= (sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
16963					} else {
16964						$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
16965					}
16966				}
16967				$s .= 'S' . "\n";
16968				$this->writer->write($s);
16969
16970				if ($tbd['style'] == 'double') {
16971					$this->SetLineWidth($tbd['w'] / 3);
16972					$this->SetDColor($tbcol);
16973					$this->writer->write($s);
16974				}
16975				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
16976					$this->writer->write('Q');
16977				}
16978
16979				// Reset Corners and Dash off
16980				$this->SetLineWidth(0.1);
16981				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
16982				$this->SetLineJoin(2);
16983				$this->SetLineCap(2);
16984				$this->SetDash();
16985			}
16986		}
16987		// Reinstate line below for dotted line divider when block border crosses a page
16988		// elseif ($blockstate == 1 || $divider == 'pagebottom') {
16989
16990		if ($this->blk[$blvl]['border_left']) {
16991			$tbd = $this->blk[$blvl]['border_left'];
16992			if (isset($tbd['s']) && $tbd['s']) {
16993				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
16994					$this->writer->write('q');
16995					$this->SetLineWidth(0);
16996					$this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
16997					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
16998					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
16999					$this->writer->write(sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
17000					$this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path
17001				}
17002
17003				$this->_setBorderLine($tbd);
17004				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17005					$this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'L');
17006				} /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brBL_V && $brBL_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
17007					$this->SetLineJoin(0);
17008					$this->SetLineCap(0);
17009				}
17010				$s = '';
17011				if ($brTL_V && $brTL_H) {
17012					$s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_left / 2, $brTL_V - $border_left / 2, 2, 2, true)) . "\n";
17013				} else { 				/* -- END BORDER-RADIUS -- */
17014					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
17015						$s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)) . "\n";
17016					} else {
17017						$s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_left / 2))) * Mpdf::SCALE)) . "\n";
17018					}
17019				}
17020				/* -- BORDER-RADIUS -- */
17021				if ($brBL_V && $brBL_H) {
17022					$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2) - $brBL_V) ) * Mpdf::SCALE)) . "\n";
17023					$s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_left / 2, $brBL_V - $border_left / 2, 3, 1)) . "\n";
17024				} else { 				/* -- END BORDER-RADIUS -- */
17025					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
17026						$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h) ) * Mpdf::SCALE)) . "\n";
17027					} else {
17028						$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2)) ) * Mpdf::SCALE)) . "\n";
17029					}
17030				}
17031				$s .= 'S' . "\n";
17032				$this->writer->write($s);
17033
17034				if ($tbd['style'] == 'double') {
17035					$this->SetLineWidth($tbd['w'] / 3);
17036					$this->SetDColor($tbcol);
17037					$this->writer->write($s);
17038				}
17039				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
17040					$this->writer->write('Q');
17041				}
17042
17043				// Reset Corners and Dash off
17044				$this->SetLineWidth(0.1);
17045				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
17046				$this->SetLineJoin(2);
17047				$this->SetLineCap(2);
17048				$this->SetDash();
17049			}
17050		}
17051		if ($this->blk[$blvl]['border_right']) {
17052			$tbd = $this->blk[$blvl]['border_right'];
17053			if (isset($tbd['s']) && $tbd['s']) {
17054				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
17055					$this->writer->write('q');
17056					$this->SetLineWidth(0);
17057					$this->writer->write(sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
17058					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
17059					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
17060					$this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
17061					$this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path
17062				}
17063
17064				$this->_setBorderLine($tbd);
17065				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17066					$this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'R');
17067				} /* -- BORDER-RADIUS -- */ elseif (($brTR_V && $brTR_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
17068					$this->SetLineJoin(0);
17069					$this->SetLineCap(0);
17070				}
17071				$s = '';
17072				if ($brBR_V && $brBR_H) {
17073					$s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_right / 2, $brBR_V - $border_right / 2, 4, 2, true)) . "\n";
17074				} else { 				/* -- END BORDER-RADIUS -- */
17075					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
17076						$s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)) . "\n";
17077					} else {
17078						$s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_right / 2))) * Mpdf::SCALE)) . "\n";
17079					}
17080				}
17081				/* -- BORDER-RADIUS -- */
17082				if ($brTR_V && $brTR_H) {
17083					$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2) + $brTR_V) ) * Mpdf::SCALE)) . "\n";
17084					$s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_right / 2, $brTR_V - $border_right / 2, 1, 1)) . "\n";
17085				} else { 				/* -- END BORDER-RADIUS -- */
17086					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
17087						$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0) ) * Mpdf::SCALE)) . "\n";
17088					} else {
17089						$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2)) ) * Mpdf::SCALE)) . "\n";
17090					}
17091				}
17092				$s .= 'S' . "\n";
17093				$this->writer->write($s);
17094
17095				if ($tbd['style'] == 'double') {
17096					$this->SetLineWidth($tbd['w'] / 3);
17097					$this->SetDColor($tbcol);
17098					$this->writer->write($s);
17099				}
17100				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
17101					$this->writer->write('Q');
17102				}
17103
17104				// Reset Corners and Dash off
17105				$this->SetLineWidth(0.1);
17106				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
17107				$this->SetLineJoin(2);
17108				$this->SetLineCap(2);
17109				$this->SetDash();
17110			}
17111		}
17112
17113
17114		$this->SetDash();
17115		$this->y = $save_y;
17116
17117
17118		// BACKGROUNDS are disabled in columns/kbt/headers - messes up the repositioning in printcolumnbuffer
17119		if ($this->ColActive || $this->kwt || $this->keep_block_together) {
17120			return;
17121		}
17122
17123
17124		$bgx0 = $x0;
17125		$bgx1 = $x1;
17126		$bgy0 = $y0;
17127		$bgy1 = $y1;
17128
17129		// Defined br values represent the radius of the outer curve - need to take border-width/2 from each radius for drawing the borders
17130		if (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'padding-box') {
17131			$brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w']);
17132			$brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w']);
17133			$brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w']);
17134			$brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w']);
17135			$brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w']);
17136			$brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w']);
17137			$brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w']);
17138			$brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w']);
17139			$bgx0 += $this->blk[$blvl]['border_left']['w'];
17140			$bgx1 -= $this->blk[$blvl]['border_right']['w'];
17141			if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
17142				$bgy0 += $this->blk[$blvl]['border_top']['w'];
17143			}
17144			if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
17145				$bgy1 -= $this->blk[$blvl]['border_bottom']['w'];
17146			}
17147		} elseif (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'content-box') {
17148			$brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);
17149			$brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);
17150			$brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);
17151			$brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);
17152			$brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);
17153			$brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);
17154			$brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);
17155			$brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);
17156			$bgx0 += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
17157			$bgx1 -= $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right'];
17158			if (($this->blk[$blvl]['border_top']['w'] || $this->blk[$blvl]['padding_top']) && $divider != 'pagetop' && !$continuingpage) {
17159				$bgy0 += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
17160			}
17161			if (($this->blk[$blvl]['border_bottom']['w'] || $this->blk[$blvl]['padding_bottom']) && $blockstate != 1 && $divider != 'pagebottom') {
17162				$bgy1 -= $this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom'];
17163			}
17164		} else {
17165			$brbgTL_H = $brTL_H;
17166			$brbgTL_V = $brTL_V;
17167			$brbgTR_H = $brTR_H;
17168			$brbgTR_V = $brTR_V;
17169			$brbgBL_H = $brBL_H;
17170			$brbgBL_V = $brBL_V;
17171			$brbgBR_H = $brBR_H;
17172			$brbgBR_V = $brBR_V;
17173		}
17174
17175		// Set clipping path
17176		$s = ' q 0 w '; // Line width=0
17177		$s .= sprintf('%.3F %.3F m ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // start point TL before the arc
17178		/* -- BORDER-RADIUS -- */
17179		if ($brbgTL_H || $brbgTL_V) {
17180			$s .= $this->_EllipseArc($bgx0 + $brbgTL_H, $bgy0 + $brbgTL_V, $brbgTL_H, $brbgTL_V, 2); // segment 2 TL
17181		}
17182		/* -- END BORDER-RADIUS -- */
17183		$s .= sprintf('%.3F %.3F l ', ($bgx0) * Mpdf::SCALE, ($this->h - ($bgy1 - $brbgBL_V )) * Mpdf::SCALE); // line to BL
17184		/* -- BORDER-RADIUS -- */
17185		if ($brbgBL_H || $brbgBL_V) {
17186			$s .= $this->_EllipseArc($bgx0 + $brbgBL_H, $bgy1 - $brbgBL_V, $brbgBL_H, $brbgBL_V, 3); // segment 3 BL
17187		}
17188		/* -- END BORDER-RADIUS -- */
17189		$s .= sprintf('%.3F %.3F l ', ($bgx1 - $brbgBR_H ) * Mpdf::SCALE, ($this->h - ($bgy1)) * Mpdf::SCALE); // line to BR
17190		/* -- BORDER-RADIUS -- */
17191		if ($brbgBR_H || $brbgBR_V) {
17192			$s .= $this->_EllipseArc($bgx1 - $brbgBR_H, $bgy1 - $brbgBR_V, $brbgBR_H, $brbgBR_V, 4); // segment 4 BR
17193		}
17194		/* -- END BORDER-RADIUS -- */
17195		$s .= sprintf('%.3F %.3F l ', ($bgx1) * Mpdf::SCALE, ($this->h - ($bgy0 + $brbgTR_V)) * Mpdf::SCALE); // line to TR
17196		/* -- BORDER-RADIUS -- */
17197		if ($brbgTR_H || $brbgTR_V) {
17198			$s .= $this->_EllipseArc($bgx1 - $brbgTR_H, $bgy0 + $brbgTR_V, $brbgTR_H, $brbgTR_V, 1); // segment 1 TR
17199		}
17200		/* -- END BORDER-RADIUS -- */
17201		$s .= sprintf('%.3F %.3F l ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // line to TL
17202		// Box Shadow
17203		$shadow = '';
17204		if (isset($this->blk[$blvl]['box_shadow']) && $this->blk[$blvl]['box_shadow'] && $h > 0) {
17205			foreach ($this->blk[$blvl]['box_shadow'] as $sh) {
17206				// Colors
17207				if ($sh['col'][0] == 1) {
17208					$colspace = 'Gray';
17209					if ($sh['col'][2] == 1) {
17210						$col1 = '1' . $sh['col'][1] . '1' . $sh['col'][3];
17211					} else {
17212						$col1 = '1' . $sh['col'][1] . '1' . chr(100);
17213					}
17214					$col2 = '1' . $sh['col'][1] . '1' . chr(0);
17215				} elseif ($sh['col'][0] == 4) { // CMYK
17216					$colspace = 'CMYK';
17217					$col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(100);
17218					$col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);
17219				} elseif ($sh['col'][0] == 5) { // RGBa
17220					$colspace = 'RGB';
17221					$col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4];
17222					$col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);
17223				} elseif ($sh['col'][0] == 6) { // CMYKa
17224					$colspace = 'CMYK';
17225					$col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . $sh['col'][5];
17226					$col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);
17227				} else {
17228					$colspace = 'RGB';
17229					$col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(100);
17230					$col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);
17231				}
17232
17233				// Use clipping path as set above (and rectangle around page) to clip area outside box
17234				$shadow .= $s; // Use the clipping path with W*
17235				$shadow .= sprintf('0 %.3F m %.3F %.3F l ', $this->h * Mpdf::SCALE, $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE);
17236				$shadow .= sprintf('%.3F 0 l 0 0 l 0 %.3F l ', $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE);
17237				$shadow .= 'W n' . "\n";
17238
17239				$sh['blur'] = abs($sh['blur']); // cannot have negative blur value
17240				// Ensure spread/blur do not make effective shadow width/height < 0
17241				// Could do more complex things but this just adjusts spread value
17242				if (-$sh['spread'] + $sh['blur'] / 2 > min($w / 2, $h / 2)) {
17243					$sh['spread'] = $sh['blur'] / 2 - min($w / 2, $h / 2) + 0.01;
17244				}
17245				// Shadow Offset
17246				if ($sh['x'] || $sh['y']) {
17247					$shadow .= sprintf(' q 1 0 0 1 %.4F %.4F cm', $sh['x'] * Mpdf::SCALE, -$sh['y'] * Mpdf::SCALE) . "\n";
17248				}
17249
17250				// Set path for INNER shadow
17251				$shadow .= ' q 0 w ';
17252				$shadow .= $this->SetFColor($col1, true) . "\n";
17253				if ($col1[0] == 5 && ord($col1[4]) < 100) { // RGBa
17254					$shadow .= $this->SetAlpha(ord($col1[4]) / 100, 'Normal', true, 'F') . "\n";
17255				} elseif ($col1[0] == 6 && ord($col1[5]) < 100) { // CMYKa
17256					$shadow .= $this->SetAlpha(ord($col1[5]) / 100, 'Normal', true, 'F') . "\n";
17257				} elseif ($col1[0] == 1 && $col1[2] == 1 && ord($col1[3]) < 100) { // Gray
17258					$shadow .= $this->SetAlpha(ord($col1[3]) / 100, 'Normal', true, 'F') . "\n";
17259				}
17260
17261				// Blur edges
17262				$mag = 0.551784; // Bezier Control magic number for 4-part spline for circle/ellipse
17263				$mag2 = 0.551784; // Bezier Control magic number to fill in edge of blurred rectangle
17264				$d1 = $sh['spread'] + $sh['blur'] / 2;
17265				$d2 = $sh['spread'] - $sh['blur'] / 2;
17266				$bl = $sh['blur'];
17267				$x00 = $x0 - $d1;
17268				$y00 = $y0 - $d1;
17269				$w00 = $w + $d1 * 2;
17270				$h00 = $h + $d1 * 2;
17271
17272				// If any border-radius is greater width-negative spread(inner edge), ignore radii for shadow or screws up
17273				$flatten = false;
17274				if (max($brbgTR_H, $brbgTL_H, $brbgBR_H, $brbgBL_H) >= $w + $d2) {
17275					$flatten = true;
17276				}
17277				if (max($brbgTR_V, $brbgTL_V, $brbgBR_V, $brbgBL_V) >= $h + $d2) {
17278					$flatten = true;
17279				}
17280
17281
17282				// TOP RIGHT corner
17283				$p1x = $x00 + $w00 - $d1 - $brbgTR_H;
17284				$p1c2x = $p1x + ($d2 + $brbgTR_H) * $mag;
17285				$p1y = $y00 + $bl;
17286				$p2x = $x00 + $w00 - $d1 - $brbgTR_H;
17287				$p2c2x = $p2x + ($d1 + $brbgTR_H) * $mag;
17288				$p2y = $y00;
17289				$p2c1y = $p2y + $bl / 2;
17290				$p3x = $x00 + $w00;
17291				$p3c2x = $p3x - $bl / 2;
17292				$p3y = $y00 + $d1 + $brbgTR_V;
17293				$p3c1y = $p3y - ($d1 + $brbgTR_V) * $mag;
17294				$p4x = $x00 + $w00 - $bl;
17295				$p4y = $y00 + $d1 + $brbgTR_V;
17296				$p4c2y = $p4y - ($d2 + $brbgTR_V) * $mag;
17297				if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
17298					$p1x = $x00 + $w00 - $bl;
17299					$p1c2x = $p1x;
17300					$p2x = $x00 + $w00 - $bl;
17301					$p2c2x = $p2x + $bl * $mag2;
17302					$p3y = $y00 + $bl;
17303					$p3c1y = $p3y - $bl * $mag2;
17304					$p4y = $y00 + $bl;
17305					$p4c2y = $p4y;
17306				}
17307
17308				$shadow .= sprintf('%.3F %.3F m ', ($p1x ) * Mpdf::SCALE, ($this->h - ($p1y )) * Mpdf::SCALE);
17309				$shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1c2x) * Mpdf::SCALE, ($this->h - ($p1y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4c2y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE);
17310				$patch_array[0]['f'] = 0;
17311				$patch_array[0]['points'] = [$p1x, $p1y, $p1x, $p1y,
17312					$p2x, $p2c1y, $p2x, $p2y, $p2c2x, $p2y,
17313					$p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,
17314					$p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,
17315					$p1c2x, $p1y];
17316				$patch_array[0]['colors'] = [$col1, $col2, $col2, $col1];
17317
17318
17319				// RIGHT
17320				$p1x = $x00 + $w00; // control point only matches p3 preceding
17321				$p1y = $y00 + $d1 + $brbgTR_V;
17322				$p2x = $x00 + $w00 - $bl; // control point only matches p4 preceding
17323				$p2y = $y00 + $d1 + $brbgTR_V;
17324				$p3x = $x00 + $w00 - $bl;
17325				$p3y = $y00 + $h00 - $d1 - $brbgBR_V;
17326				$p4x = $x00 + $w00;
17327				$p4c1x = $p4x - $bl / 2;
17328				$p4y = $y00 + $h00 - $d1 - $brbgBR_V;
17329				if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
17330					$p1y = $y00 + $bl;
17331					$p2y = $y00 + $bl;
17332				}
17333				if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
17334					$p3y = $y00 + $h00 - $bl;
17335					$p4y = $y00 + $h00 - $bl;
17336				}
17337
17338				$shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
17339				$patch_array[1]['f'] = 2;
17340				$patch_array[1]['points'] = [$p2x, $p2y,
17341					$p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
17342					$p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,
17343					$p1x, $p1y];
17344				$patch_array[1]['colors'] = [$col1, $col2];
17345
17346
17347				// BOTTOM RIGHT corner
17348				$p1x = $x00 + $w00 - $bl;  // control points only matches p3 preceding
17349				$p1y = $y00 + $h00 - $d1 - $brbgBR_V;
17350				$p1c2y = $p1y + ($d2 + $brbgBR_V) * $mag;
17351				$p2x = $x00 + $w00;     // control point only matches p4 preceding
17352				$p2y = $y00 + $h00 - $d1 - $brbgBR_V;
17353				$p2c2y = $p2y + ($d1 + $brbgBR_V) * $mag;
17354				$p3x = $x00 + $w00 - $d1 - $brbgBR_H;
17355				$p3c1x = $p3x + ($d1 + $brbgBR_H) * $mag;
17356				$p3y = $y00 + $h00;
17357				$p3c2y = $p3y - $bl / 2;
17358				$p4x = $x00 + $w00 - $d1 - $brbgBR_H;
17359				$p4c2x = $p4x + ($d2 + $brbgBR_H) * $mag;
17360				$p4y = $y00 + $h00 - $bl;
17361
17362				if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
17363					$p1y = $y00 + $h00 - $bl;
17364					$p1c2y = $p1y;
17365					$p2y = $y00 + $h00 - $bl;
17366					$p2c2y = $p2y + $bl * $mag2;
17367					$p3x = $x00 + $w00 - $bl;
17368					$p3c1x = $p3x + $bl * $mag2;
17369					$p4x = $x00 + $w00 - $bl;
17370					$p4c2x = $p4x;
17371				}
17372
17373				$shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1x) * Mpdf::SCALE, ($this->h - ($p1c2y)) * Mpdf::SCALE, ($p4c2x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE);
17374				$patch_array[2]['f'] = 2;
17375				$patch_array[2]['points'] = [$p2x, $p2c2y,
17376					$p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,
17377					$p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,
17378					$p1x, $p1c2y];
17379				$patch_array[2]['colors'] = [$col2, $col1];
17380
17381
17382
17383				// BOTTOM
17384				$p1x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p3 preceding
17385				$p1y = $y00 + $h00;
17386				$p2x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p4 preceding
17387				$p2y = $y00 + $h00 - $bl;
17388				$p3x = $x00 + $d1 + $brbgBL_H;
17389				$p3y = $y00 + $h00 - $bl;
17390				$p4x = $x00 + $d1 + $brbgBL_H;
17391				$p4y = $y00 + $h00;
17392				$p4c1y = $p4y - $bl / 2;
17393
17394				if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
17395					$p1x = $x00 + $w00 - $bl;
17396					$p2x = $x00 + $w00 - $bl;
17397				}
17398				if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
17399					$p3x = $x00 + $bl;
17400					$p4x = $x00 + $bl;
17401				}
17402
17403				$shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
17404				$patch_array[3]['f'] = 2;
17405				$patch_array[3]['points'] = [$p2x, $p2y,
17406					$p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
17407					$p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,
17408					$p1x, $p1y];
17409				$patch_array[3]['colors'] = [$col1, $col2];
17410
17411				// BOTTOM LEFT corner
17412				$p1x = $x00 + $d1 + $brbgBL_H;
17413				$p1c2x = $p1x - ($d2 + $brbgBL_H) * $mag; // control points only matches p3 preceding
17414				$p1y = $y00 + $h00 - $bl;
17415				$p2x = $x00 + $d1 + $brbgBL_H;
17416				$p2c2x = $p2x - ($d1 + $brbgBL_H) * $mag; // control point only matches p4 preceding
17417				$p2y = $y00 + $h00;
17418				$p3x = $x00;
17419				$p3c2x = $p3x + $bl / 2;
17420				$p3y = $y00 + $h00 - $d1 - $brbgBL_V;
17421				$p3c1y = $p3y + ($d1 + $brbgBL_V) * $mag;
17422				$p4x = $x00 + $bl;
17423				$p4y = $y00 + $h00 - $d1 - $brbgBL_V;
17424				$p4c2y = $p4y + ($d2 + $brbgBL_V) * $mag;
17425				if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
17426					$p1x = $x00 + $bl;
17427					$p1c2x = $p1x;
17428					$p2x = $x00 + $bl;
17429					$p2c2x = $p2x - $bl * $mag2;
17430					$p3y = $y00 + $h00 - $bl;
17431					$p3c1y = $p3y + $bl * $mag2;
17432					$p4y = $y00 + $h00 - $bl;
17433					$p4c2y = $p4y;
17434				}
17435
17436				$shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1c2x) * Mpdf::SCALE, ($this->h - ($p1y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4c2y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE);
17437				$patch_array[4]['f'] = 2;
17438				$patch_array[4]['points'] = [$p2c2x, $p2y,
17439					$p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,
17440					$p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,
17441					$p1c2x, $p1y];
17442				$patch_array[4]['colors'] = [$col2, $col1];
17443
17444
17445				// LEFT - joins on the right (C3-C4 of previous): f = 2
17446				$p1x = $x00; // control point only matches p3 preceding
17447				$p1y = $y00 + $h00 - $d1 - $brbgBL_V;
17448				$p2x = $x00 + $bl; // control point only matches p4 preceding
17449				$p2y = $y00 + $h00 - $d1 - $brbgBL_V;
17450				$p3x = $x00 + $bl;
17451				$p3y = $y00 + $d1 + $brbgTL_V;
17452				$p4x = $x00;
17453				$p4c1x = $p4x + $bl / 2;
17454				$p4y = $y00 + $d1 + $brbgTL_V;
17455				if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
17456					$p1y = $y00 + $h00 - $bl;
17457					$p2y = $y00 + $h00 - $bl;
17458				}
17459				if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
17460					$p3y = $y00 + $bl;
17461					$p4y = $y00 + $bl;
17462				}
17463
17464				$shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
17465				$patch_array[5]['f'] = 2;
17466				$patch_array[5]['points'] = [$p2x, $p2y,
17467					$p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
17468					$p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,
17469					$p1x, $p1y];
17470				$patch_array[5]['colors'] = [$col1, $col2];
17471
17472				// TOP LEFT corner
17473				$p1x = $x00 + $bl;  // control points only matches p3 preceding
17474				$p1y = $y00 + $d1 + $brbgTL_V;
17475				$p1c2y = $p1y - ($d2 + $brbgTL_V) * $mag;
17476				$p2x = $x00;   // control point only matches p4 preceding
17477				$p2y = $y00 + $d1 + $brbgTL_V;
17478				$p2c2y = $p2y - ($d1 + $brbgTL_V) * $mag;
17479				$p3x = $x00 + $d1 + $brbgTL_H;
17480				$p3c1x = $p3x - ($d1 + $brbgTL_H) * $mag;
17481				$p3y = $y00;
17482				$p3c2y = $p3y + $bl / 2;
17483				$p4x = $x00 + $d1 + $brbgTL_H;
17484				$p4c2x = $p4x - ($d2 + $brbgTL_H) * $mag;
17485				$p4y = $y00 + $bl;
17486
17487				if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
17488					$p1y = $y00 + $bl;
17489					$p1c2y = $p1y;
17490					$p2y = $y00 + $bl;
17491					$p2c2y = $p2y - $bl * $mag2;
17492					$p3x = $x00 + $bl;
17493					$p3c1x = $p3x - $bl * $mag2;
17494					$p4x = $x00 + $bl;
17495					$p4c2x = $p4x;
17496				}
17497
17498				$shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1x) * Mpdf::SCALE, ($this->h - ($p1c2y)) * Mpdf::SCALE, ($p4c2x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE);
17499				$patch_array[6]['f'] = 2;
17500				$patch_array[6]['points'] = [$p2x, $p2c2y,
17501					$p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,
17502					$p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,
17503					$p1x, $p1c2y];
17504				$patch_array[6]['colors'] = [$col2, $col1];
17505
17506
17507				// TOP - joins on the right (C3-C4 of previous): f = 2
17508				$p1x = $x00 + $d1 + $brbgTL_H; // control point only matches p3 preceding
17509				$p1y = $y00;
17510				$p2x = $x00 + $d1 + $brbgTL_H; // control point only matches p4 preceding
17511				$p2y = $y00 + $bl;
17512				$p3x = $x00 + $w00 - $d1 - $brbgTR_H;
17513				$p3y = $y00 + $bl;
17514				$p4x = $x00 + $w00 - $d1 - $brbgTR_H;
17515				$p4y = $y00;
17516				$p4c1y = $p4y + $bl / 2;
17517				if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
17518					$p1x = $x00 + $bl;
17519					$p2x = $x00 + $bl;
17520				}
17521				if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
17522					$p3x = $x00 + $w00 - $bl;
17523					$p4x = $x00 + $w00 - $bl;
17524				}
17525
17526				$shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
17527				$patch_array[7]['f'] = 2;
17528				$patch_array[7]['points'] = [$p2x, $p2y,
17529					$p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
17530					$p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,
17531					$p1x, $p1y];
17532				$patch_array[7]['colors'] = [$col1, $col2];
17533
17534				$shadow .= ' h f Q ' . "\n"; // Close path and Fill the inner solid shadow
17535
17536				if ($bl) {
17537					$shadow .= $this->gradient->CoonsPatchMesh($x00, $y00, $w00, $h00, $patch_array, $x00, $x00 + $w00, $y00, $y00 + $h00, $colspace, true);
17538				}
17539
17540				if ($sh['x'] || $sh['y']) {
17541					$shadow .= ' Q' . "\n";  // Shadow Offset
17542				}
17543				$shadow .= ' Q' . "\n"; // Ends path no-op & Sets the clipping path
17544			}
17545		}
17546
17547		$s .= ' W n '; // Ends path no-op & Sets the clipping path
17548
17549		if ($this->blk[$blvl]['bgcolor']) {
17550			$this->pageBackgrounds[$blvl][] = [
17551				'x' => $x0,
17552				'y' => $y0,
17553				'w' => $w,
17554				'h' => $h,
17555				'col' => $this->blk[$blvl]['bgcolorarray'],
17556				'clippath' => $s,
17557				'visibility' => $this->visibility,
17558				'shadow' => $shadow,
17559				'z-index' => $this->current_layer,
17560			];
17561		} elseif ($shadow) {
17562			$this->pageBackgrounds[$blvl][] = [
17563				'x' => 0,
17564				'y' => 0,
17565				'w' => 0,
17566				'h' => 0,
17567				'shadowonly' => true,
17568				'col' => '',
17569				'clippath' => '',
17570				'visibility' => $this->visibility,
17571				'shadow' => $shadow,
17572				'z-index' => $this->current_layer,
17573			];
17574		}
17575
17576		/* -- BACKGROUNDS -- */
17577		if (isset($this->blk[$blvl]['gradient'])) {
17578			$g = $this->gradient->parseBackgroundGradient($this->blk[$blvl]['gradient']);
17579			if ($g) {
17580				$gx = $x0;
17581				$gy = $y0;
17582				$this->pageBackgrounds[$blvl][] = [
17583					'gradient' => true,
17584					'x' => $gx,
17585					'y' => $gy,
17586					'w' => $w,
17587					'h' => $h,
17588					'gradtype' => $g['type'],
17589					'stops' => $g['stops'],
17590					'colorspace' => $g['colorspace'],
17591					'coords' => $g['coords'],
17592					'extend' => $g['extend'],
17593					'clippath' => $s,
17594					'visibility' => $this->visibility,
17595					'z-index' => $this->current_layer
17596				];
17597			}
17598		}
17599
17600		if (isset($this->blk[$blvl]['background-image'])) {
17601			if (isset($this->blk[$blvl]['background-image']['gradient']) && $this->blk[$blvl]['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->blk[$blvl]['background-image']['gradient'])) {
17602				$g = $this->gradient->parseMozGradient($this->blk[$blvl]['background-image']['gradient']);
17603				if ($g) {
17604					$gx = $x0;
17605					$gy = $y0;
17606					// origin specifies the background-positioning-area (bpa)
17607					if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {
17608						$gx += $this->blk[$blvl]['border_left']['w'];
17609						$w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);
17610						if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
17611							$gy += $this->blk[$blvl]['border_top']['w'];
17612						}
17613						if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
17614							$gy1 = $y1 - $this->blk[$blvl]['border_bottom']['w'];
17615						} else {
17616							$gy1 = $y1;
17617						}
17618						$h = $gy1 - $gy;
17619					} elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {
17620						$gx += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
17621						$w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']);
17622						if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
17623							$gy += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
17624						}
17625						if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
17626							$gy1 = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);
17627						} else {
17628							$gy1 = $y1 - $this->blk[$blvl]['padding_bottom'];
17629						}
17630						$h = $gy1 - $gy;
17631					}
17632
17633					if (isset($this->blk[$blvl]['background-image']['size']['w']) && $this->blk[$blvl]['background-image']['size']['w']) {
17634						$size = $this->blk[$blvl]['background-image']['size'];
17635						if ($size['w'] != 'contain' && $size['w'] != 'cover') {
17636							if (stristr($size['w'], '%')) {
17637								$size['w'] = (float) $size['w'];
17638								$size['w'] /= 100;
17639								$w *= $size['w'];
17640							} elseif ($size['w'] != 'auto') {
17641								$w = $size['w'];
17642							}
17643							if (stristr($size['h'], '%')) {
17644								$size['h'] = (float) $size['h'];
17645								$size['h'] /= 100;
17646								$h *= $size['h'];
17647							} elseif ($size['h'] != 'auto') {
17648								$h = $size['h'];
17649							}
17650						}
17651					}
17652					$this->pageBackgrounds[$blvl][] = [
17653						'gradient' => true,
17654						'x' => $gx,
17655						'y' => $gy,
17656						'w' => $w,
17657						'h' => $h,
17658						'gradtype' => $g['type'],
17659						'stops' => $g['stops'],
17660						'colorspace' => $g['colorspace'],
17661						'coords' => $g['coords'],
17662						'extend' => $g['extend'],
17663						'clippath' => $s,
17664						'visibility' => $this->visibility,
17665						'z-index' => $this->current_layer
17666					];
17667				}
17668
17669			} else {
17670
17671				$image_id = $this->blk[$blvl]['background-image']['image_id'];
17672				$orig_w = $this->blk[$blvl]['background-image']['orig_w'];
17673				$orig_h = $this->blk[$blvl]['background-image']['orig_h'];
17674				$x_pos = $this->blk[$blvl]['background-image']['x_pos'];
17675				$y_pos = $this->blk[$blvl]['background-image']['y_pos'];
17676				$x_repeat = $this->blk[$blvl]['background-image']['x_repeat'];
17677				$y_repeat = $this->blk[$blvl]['background-image']['y_repeat'];
17678				$resize = $this->blk[$blvl]['background-image']['resize'];
17679				$opacity = $this->blk[$blvl]['background-image']['opacity'];
17680				$itype = $this->blk[$blvl]['background-image']['itype'];
17681				$size = $this->blk[$blvl]['background-image']['size'];
17682				// origin specifies the background-positioning-area (bpa)
17683
17684				$bpa = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h];
17685
17686				if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {
17687
17688					$bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'];
17689					$bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);
17690					if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
17691						$bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'];
17692					} else {
17693						$bpa['y'] = $y0;
17694					}
17695					if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
17696						$bpay = $y1 - $this->blk[$blvl]['border_bottom']['w'];
17697					} else {
17698						$bpay = $y1;
17699					}
17700					$bpa['h'] = $bpay - $bpa['y'];
17701
17702				} elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {
17703
17704					$bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
17705					$bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']);
17706					if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
17707						$bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
17708					} else {
17709						$bpa['y'] = $y0 + $this->blk[$blvl]['padding_top'];
17710					}
17711					if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
17712						$bpay = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);
17713					} else {
17714						$bpay = $y1 - $this->blk[$blvl]['padding_bottom'];
17715					}
17716					$bpa['h'] = $bpay - $bpa['y'];
17717
17718				}
17719
17720				$this->pageBackgrounds[$blvl][] = [
17721					'x' => $x0,
17722					'y' => $y0,
17723					'w' => $w,
17724					'h' => $h,
17725					'image_id' => $image_id,
17726					'orig_w' => $orig_w,
17727					'orig_h' => $orig_h,
17728					'x_pos' => $x_pos,
17729					'y_pos' => $y_pos,
17730					'x_repeat' => $x_repeat,
17731					'y_repeat' => $y_repeat,
17732					'clippath' => $s,
17733					'resize' => $resize,
17734					'opacity' => $opacity,
17735					'itype' => $itype,
17736					'visibility' => $this->visibility,
17737					'z-index' => $this->current_layer,
17738					'size' => $size,
17739					'bpa' => $bpa
17740				];
17741			}
17742		}
17743		/* -- END BACKGROUNDS -- */
17744
17745		// Float DIV
17746		$this->blk[$blvl]['bb_painted'][$this->page] = true;
17747	}
17748	/* -- BORDER-RADIUS -- */
17749
17750	function _EllipseArc($x0, $y0, $rx, $ry, $seg = 1, $part = false, $start = false)
17751	{
17752		// Anticlockwise segment 1-4 TR-TL-BL-BR (part=1 or 2)
17753		$s = '';
17754
17755		if ($rx < 0) {
17756			$rx = 0;
17757		}
17758
17759		if ($ry < 0) {
17760			$ry = 0;
17761		}
17762
17763		$rx *= Mpdf::SCALE;
17764		$ry *= Mpdf::SCALE;
17765
17766		$astart = 0;
17767
17768		if ($seg == 1) { // Top Right
17769			$afinish = 90;
17770			$nSeg = 4;
17771		} elseif ($seg == 2) { // Top Left
17772			$afinish = 180;
17773			$nSeg = 8;
17774		} elseif ($seg == 3) { // Bottom Left
17775			$afinish = 270;
17776			$nSeg = 12;
17777		} else {   // Bottom Right
17778			$afinish = 360;
17779			$nSeg = 16;
17780		}
17781
17782		$astart = deg2rad((float) $astart);
17783		$afinish = deg2rad((float) $afinish);
17784
17785		$totalAngle = $afinish - $astart;
17786		$dt = $totalAngle / $nSeg; // segment angle
17787		$dtm = $dt / 3;
17788		$x0 *= Mpdf::SCALE;
17789		$y0 = ($this->h - $y0) * Mpdf::SCALE;
17790		$t1 = $astart;
17791		$a0 = $x0 + ($rx * cos($t1));
17792		$b0 = $y0 + ($ry * sin($t1));
17793		$c0 = -$rx * sin($t1);
17794		$d0 = $ry * cos($t1);
17795		$op = false;
17796
17797		for ($i = 1; $i <= $nSeg; $i++) {
17798			// Draw this bit of the total curve
17799			$t1 = ($i * $dt) + $astart;
17800			$a1 = $x0 + ($rx * cos($t1));
17801			$b1 = $y0 + ($ry * sin($t1));
17802			$c1 = -$rx * sin($t1);
17803			$d1 = $ry * cos($t1);
17804			if ($i > ($nSeg - 4) && (!$part || ($part == 1 && $i <= $nSeg - 2) || ($part == 2 && $i > $nSeg - 2))) {
17805				if ($start && !$op) {
17806					$s .= sprintf('%.3F %.3F m ', $a0, $b0);
17807				}
17808				$s .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($a0 + ($c0 * $dtm)), ($b0 + ($d0 * $dtm)), ($a1 - ($c1 * $dtm)), ($b1 - ($d1 * $dtm)), $a1, $b1);
17809				$op = true;
17810			}
17811			$a0 = $a1;
17812			$b0 = $b1;
17813			$c0 = $c1;
17814			$d0 = $d1;
17815		}
17816
17817		return $s;
17818	}
17819
17820	/* -- END BORDER-RADIUS -- */
17821
17822	function PaintDivLnBorder($state = 0, $blvl = 0, $h = 0)
17823	{
17824		// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
17825		$this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
17826
17827		$save_y = $this->y;
17828
17829		$w = $this->blk[$blvl]['width'];
17830		$x0 = $this->x;    // left
17831		$y0 = $this->y;    // top
17832		$x1 = $this->x + $w;   // bottom
17833		$y1 = $this->y + $h;   // bottom
17834		$continuingpage = (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page);
17835
17836		if ($this->blk[$blvl]['border_top'] && ($state == 1 || $state == 3)) {
17837			$tbd = $this->blk[$blvl]['border_top'];
17838			if (isset($tbd['s']) && $tbd['s']) {
17839				$this->_setBorderLine($tbd);
17840				$this->y = $y0 + ($tbd['w'] / 2);
17841				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17842					$this->_setDashBorder($tbd['style'], '', $continuingpage, 'T');
17843					$this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);
17844				} else {
17845					$this->SetLineJoin(0);
17846					$this->SetLineCap(0);
17847					$this->Line($x0, $this->y, $x0 + $w, $this->y);
17848				}
17849				$this->y += $tbd['w'];
17850				// Reset Corners and Dash off
17851				$this->SetLineJoin(2);
17852				$this->SetLineCap(2);
17853				$this->SetDash();
17854			}
17855		}
17856		if ($this->blk[$blvl]['border_left']) {
17857			$tbd = $this->blk[$blvl]['border_left'];
17858			if (isset($tbd['s']) && $tbd['s']) {
17859				$this->_setBorderLine($tbd);
17860				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17861					$this->y = $y0 + ($tbd['w'] / 2);
17862					$this->_setDashBorder($tbd['style'], '', $continuingpage, 'L');
17863					$this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));
17864				} else {
17865					$this->y = $y0;
17866					$this->SetLineJoin(0);
17867					$this->SetLineCap(0);
17868					$this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h);
17869				}
17870				$this->y += $tbd['w'];
17871				// Reset Corners and Dash off
17872				$this->SetLineJoin(2);
17873				$this->SetLineCap(2);
17874				$this->SetDash();
17875			}
17876		}
17877		if ($this->blk[$blvl]['border_right']) {
17878			$tbd = $this->blk[$blvl]['border_right'];
17879			if (isset($tbd['s']) && $tbd['s']) {
17880				$this->_setBorderLine($tbd);
17881				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17882					$this->y = $y0 + ($tbd['w'] / 2);
17883					$this->_setDashBorder($tbd['style'], '', $continuingpage, 'R');
17884					$this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));
17885				} else {
17886					$this->y = $y0;
17887					$this->SetLineJoin(0);
17888					$this->SetLineCap(0);
17889					$this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h);
17890				}
17891				$this->y += $tbd['w'];
17892				// Reset Corners and Dash off
17893				$this->SetLineJoin(2);
17894				$this->SetLineCap(2);
17895				$this->SetDash();
17896			}
17897		}
17898		if ($this->blk[$blvl]['border_bottom'] && $state > 1) {
17899			$tbd = $this->blk[$blvl]['border_bottom'];
17900			if (isset($tbd['s']) && $tbd['s']) {
17901				$this->_setBorderLine($tbd);
17902				$this->y = $y0 + $h - ($tbd['w'] / 2);
17903				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17904					$this->_setDashBorder($tbd['style'], '', $continuingpage, 'B');
17905					$this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);
17906				} else {
17907					$this->SetLineJoin(0);
17908					$this->SetLineCap(0);
17909					$this->Line($x0, $this->y, $x0 + $w, $this->y);
17910				}
17911				$this->y += $tbd['w'];
17912				// Reset Corners and Dash off
17913				$this->SetLineJoin(2);
17914				$this->SetLineCap(2);
17915				$this->SetDash();
17916			}
17917		}
17918		$this->SetDash();
17919		$this->y = $save_y;
17920	}
17921
17922	function PaintImgBorder($objattr, $is_table)
17923	{
17924		// Borders are disabled in columns - messes up the repositioning in printcolumnbuffer
17925		if ($this->ColActive) {
17926			return;
17927		} // *COLUMNS*
17928		if ($is_table) {
17929			$k = $this->shrin_k;
17930		} else {
17931			$k = 1;
17932		}
17933		$h = (isset($objattr['BORDER-HEIGHT']) ? $objattr['BORDER-HEIGHT'] : 0);
17934		$w = (isset($objattr['BORDER-WIDTH']) ? $objattr['BORDER-WIDTH'] : 0);
17935		$x0 = (isset($objattr['BORDER-X']) ? $objattr['BORDER-X'] : 0);
17936		$y0 = (isset($objattr['BORDER-Y']) ? $objattr['BORDER-Y'] : 0);
17937
17938		// BORDERS
17939		if ($objattr['border_top']) {
17940			$tbd = $objattr['border_top'];
17941			if (!empty($tbd['s'])) {
17942				$this->_setBorderLine($tbd, $k);
17943				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17944					$this->_setDashBorder($tbd['style'], '', '', 'T');
17945				}
17946				$this->Line($x0, $y0, $x0 + $w, $y0);
17947				// Reset Corners and Dash off
17948				$this->SetLineJoin(2);
17949				$this->SetLineCap(2);
17950				$this->SetDash();
17951			}
17952		}
17953		if ($objattr['border_left']) {
17954			$tbd = $objattr['border_left'];
17955			if (!empty($tbd['s'])) {
17956				$this->_setBorderLine($tbd, $k);
17957				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17958					$this->_setDashBorder($tbd['style'], '', '', 'L');
17959				}
17960				$this->Line($x0, $y0, $x0, $y0 + $h);
17961				// Reset Corners and Dash off
17962				$this->SetLineJoin(2);
17963				$this->SetLineCap(2);
17964				$this->SetDash();
17965			}
17966		}
17967		if ($objattr['border_right']) {
17968			$tbd = $objattr['border_right'];
17969			if (!empty($tbd['s'])) {
17970				$this->_setBorderLine($tbd, $k);
17971				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17972					$this->_setDashBorder($tbd['style'], '', '', 'R');
17973				}
17974				$this->Line($x0 + $w, $y0, $x0 + $w, $y0 + $h);
17975				// Reset Corners and Dash off
17976				$this->SetLineJoin(2);
17977				$this->SetLineCap(2);
17978				$this->SetDash();
17979			}
17980		}
17981		if ($objattr['border_bottom']) {
17982			$tbd = $objattr['border_bottom'];
17983			if (!empty($tbd['s'])) {
17984				$this->_setBorderLine($tbd, $k);
17985				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
17986					$this->_setDashBorder($tbd['style'], '', '', 'B');
17987				}
17988				$this->Line($x0, $y0 + $h, $x0 + $w, $y0 + $h);
17989				// Reset Corners and Dash off
17990				$this->SetLineJoin(2);
17991				$this->SetLineCap(2);
17992				$this->SetDash();
17993			}
17994		}
17995		$this->SetDash();
17996		$this->SetAlpha(1);
17997	}
17998
17999	/* -- END HTML-CSS -- */
18000
18001	function Reset()
18002	{
18003		$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18004		$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18005		$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
18006		$this->SetAlpha(1);
18007		$this->colorarray = '';
18008
18009		$this->spanbgcolorarray = '';
18010		$this->spanbgcolor = false;
18011		$this->spanborder = false;
18012		$this->spanborddet = [];
18013
18014		$this->ResetStyles();
18015
18016		$this->HREF = '';
18017		$this->textparam = [];
18018		$this->SetTextOutline();
18019
18020		$this->textvar = 0x00; // mPDF 5.7.1
18021		$this->OTLtags = [];
18022		$this->textshadow = '';
18023
18024		$this->currentLang = $this->default_lang;  // mPDF 6
18025		$this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6
18026		$this->SetFont($this->default_font, '', 0, false);
18027		$this->SetFontSize($this->default_font_size, false);
18028
18029		$this->currentfontfamily = '';
18030		$this->currentfontsize = '';
18031		$this->currentfontstyle = '';
18032
18033		if ($this->tableLevel && isset($this->table[1][1]['cellLineHeight'])) {
18034			$this->SetLineHeight('', $this->table[1][1]['cellLineHeight']);
18035		} else {
18036			if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {
18037				$this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height
18038			}
18039		}
18040
18041		$this->lSpacingCSS = '';
18042		$this->wSpacingCSS = '';
18043		$this->fixedlSpacing = false;
18044		$this->minwSpacing = 0;
18045		$this->SetDash(); // restore to no dash
18046		$this->dash_on = false;
18047		$this->dotted_on = false;
18048		$this->divwidth = 0;
18049		$this->divheight = 0;
18050		$this->cellTextAlign = '';
18051		$this->cellLineHeight = '';
18052		$this->cellLineStackingStrategy = '';
18053		$this->cellLineStackingShift = '';
18054		$this->oldy = -1;
18055
18056		$bodystyle = [];
18057
18058		if (isset($this->cssManager->CSS['BODY']['FONT-STYLE'])) {
18059			$bodystyle['FONT-STYLE'] = $this->cssManager->CSS['BODY']['FONT-STYLE'];
18060		}
18061
18062		if (isset($this->cssManager->CSS['BODY']['FONT-WEIGHT'])) {
18063			$bodystyle['FONT-WEIGHT'] = $this->cssManager->CSS['BODY']['FONT-WEIGHT'];
18064		}
18065
18066		if (isset($this->cssManager->CSS['BODY']['COLOR'])) {
18067			$bodystyle['COLOR'] = $this->cssManager->CSS['BODY']['COLOR'];
18068		}
18069
18070		if (isset($bodystyle)) {
18071			$this->setCSS($bodystyle, 'BLOCK', 'BODY');
18072		}
18073	}
18074
18075	/* -- HTML-CSS -- */
18076
18077	function ReadMetaTags($html)
18078	{
18079		// changes anykey=anyvalue to anykey="anyvalue" (only do this when this happens inside tags)
18080		$regexp = '/ (\\w+?)=([^\\s>"]+)/si';
18081		$html = preg_replace($regexp, " \$1=\"\$2\"", $html);
18082		if (preg_match('/<title>(.*?)<\/title>/si', $html, $m)) {
18083			$this->SetTitle($m[1]);
18084		}
18085		preg_match_all('/<meta [^>]*?(name|content)="([^>]*?)" [^>]*?(name|content)="([^>]*?)".*?>/si', $html, $aux);
18086		$firstattr = $aux[1];
18087		$secondattr = $aux[3];
18088		for ($i = 0; $i < count($aux[0]); $i++) {
18089			$name = ( strtoupper($firstattr[$i]) == "NAME" ) ? strtoupper($aux[2][$i]) : strtoupper($aux[4][$i]);
18090			$content = ( strtoupper($firstattr[$i]) == "CONTENT" ) ? $aux[2][$i] : $aux[4][$i];
18091			switch ($name) {
18092				case "KEYWORDS":
18093					$this->SetKeywords($content);
18094					break;
18095				case "AUTHOR":
18096					$this->SetAuthor($content);
18097					break;
18098				case "DESCRIPTION":
18099					$this->SetSubject($content);
18100					break;
18101			}
18102		}
18103	}
18104
18105	function ReadCharset($html)
18106	{
18107		// Charset conversion
18108		if ($this->allow_charset_conversion) {
18109			if (preg_match('/<head.*charset=([^\'\"\s]*).*<\/head>/si', $html, $m)) {
18110				if (strtoupper($m[1]) != 'UTF-8') {
18111					$this->charset_in = strtoupper($m[1]);
18112				}
18113			}
18114		}
18115	}
18116
18117	function setCSS($arrayaux, $type = '', $tag = '')
18118	{
18119	// type= INLINE | BLOCK | TABLECELL // tag= BODY
18120		if (!is_array($arrayaux)) {
18121			return; // Removes PHP Warning
18122		}
18123
18124		// mPDF 5.7.3  inline text-decoration parameters
18125		$preceeding_fontkey = $this->FontFamily . $this->FontStyle;
18126		$preceeding_fontsize = $this->FontSize;
18127		$spanbordset = false;
18128		$spanbgset = false;
18129		// mPDF 6
18130		$prevlevel = (($this->blklvl == 0) ? 0 : $this->blklvl - 1);
18131
18132		// Set font size first so that e.g. MARGIN 0.83em works on font size for this element
18133		if (isset($arrayaux['FONT-SIZE'])) {
18134			$v = $arrayaux['FONT-SIZE'];
18135			$firstLetter = substr($v, 0, 1);
18136			if (is_numeric($firstLetter) || ($firstLetter === '.')) {
18137				if ($type == 'BLOCK' && $this->blklvl > 0 && isset($this->blk[$this->blklvl - 1]['InlineProperties']) && isset($this->blk[$this->blklvl - 1]['InlineProperties']['size'])) {
18138					$mmsize = $this->sizeConverter->convert($v, $this->blk[$this->blklvl - 1]['InlineProperties']['size']);
18139				} elseif ($type == 'TABLECELL') {
18140					$mmsize = $this->sizeConverter->convert($v, $this->default_font_size / Mpdf::SCALE);
18141				} else {
18142					$mmsize = $this->sizeConverter->convert($v, $this->FontSize);
18143				}
18144				$this->SetFontSize($mmsize * (Mpdf::SCALE), false); // Get size in points (pt)
18145			} else {
18146				$v = strtoupper($v);
18147				if (isset($this->fontsizes[$v])) {
18148					$this->SetFontSize($this->fontsizes[$v] * $this->default_font_size, false);
18149				}
18150			}
18151			if ($tag == 'BODY') {
18152				$this->SetDefaultFontSize($this->FontSizePt);
18153			}
18154		}
18155
18156		// mPDF 6
18157		if (isset($arrayaux['LANG']) && $arrayaux['LANG']) {
18158			if ($this->autoLangToFont && !$this->usingCoreFont) {
18159				if ($arrayaux['LANG'] != $this->default_lang && $arrayaux['LANG'] != 'UTF-8') {
18160					list ($coreSuitable, $mpdf_pdf_unifont) = $this->languageToFont->getLanguageOptions($arrayaux['LANG'], $this->useAdobeCJK);
18161					if ($mpdf_pdf_unifont) {
18162						$arrayaux['FONT-FAMILY'] = $mpdf_pdf_unifont;
18163					}
18164					if ($tag == 'BODY') {
18165						$this->default_lang = $arrayaux['LANG'];
18166					}
18167				}
18168			}
18169			$this->currentLang = $arrayaux['LANG'];
18170		}
18171
18172		// FOR INLINE and BLOCK OR 'BODY'
18173		if (isset($arrayaux['FONT-FAMILY'])) {
18174			$v = $arrayaux['FONT-FAMILY'];
18175			// If it is a font list, get all font types
18176			$aux_fontlist = explode(",", $v);
18177			$found = 0;
18178			foreach ($aux_fontlist as $f) {
18179				$fonttype = trim($f);
18180				$fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
18181				$fonttype = preg_replace('/ /', '', $fonttype);
18182				$v = strtolower(trim($fonttype));
18183				if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {
18184					$v = $this->fonttrans[$v];
18185				}
18186				if ((!$this->onlyCoreFonts && in_array($v, $this->available_unifonts)) ||
18187					in_array($v, ['ccourier', 'ctimes', 'chelvetica']) ||
18188					($this->onlyCoreFonts && in_array($v, ['courier', 'times', 'helvetica', 'arial'])) ||
18189					in_array($v, ['sjis', 'uhc', 'big5', 'gb'])) {
18190					$fonttype = $v;
18191					$found = 1;
18192					break;
18193				}
18194			}
18195			if (!$found) {
18196				foreach ($aux_fontlist as $f) {
18197					$fonttype = trim($f);
18198					$fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
18199					$fonttype = preg_replace('/ /', '', $fonttype);
18200					$v = strtolower(trim($fonttype));
18201					if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {
18202						$v = $this->fonttrans[$v];
18203					}
18204					if (in_array($v, $this->sans_fonts) || in_array($v, $this->serif_fonts) || in_array($v, $this->mono_fonts)) {
18205						$fonttype = $v;
18206						break;
18207					}
18208				}
18209			}
18210
18211			if ($tag == 'BODY') {
18212				$this->SetDefaultFont($fonttype);
18213			}
18214			$this->SetFont($fonttype, $this->currentfontstyle, 0, false);
18215		} else {
18216			$this->SetFont($this->currentfontfamily, $this->currentfontstyle, 0, false);
18217		}
18218
18219		foreach ($arrayaux as $k => $v) {
18220			if ($type != 'INLINE' && $tag != 'BODY' && $type != 'TABLECELL') {
18221				switch ($k) {
18222					// BORDERS
18223					case 'BORDER-TOP':
18224						$this->blk[$this->blklvl]['border_top'] = $this->border_details($v);
18225						if ($this->blk[$this->blklvl]['border_top']['s']) {
18226							$this->blk[$this->blklvl]['border'] = 1;
18227						}
18228						break;
18229					case 'BORDER-BOTTOM':
18230						$this->blk[$this->blklvl]['border_bottom'] = $this->border_details($v);
18231						if ($this->blk[$this->blklvl]['border_bottom']['s']) {
18232							$this->blk[$this->blklvl]['border'] = 1;
18233						}
18234						break;
18235					case 'BORDER-LEFT':
18236						$this->blk[$this->blklvl]['border_left'] = $this->border_details($v);
18237						if ($this->blk[$this->blklvl]['border_left']['s']) {
18238							$this->blk[$this->blklvl]['border'] = 1;
18239						}
18240						break;
18241					case 'BORDER-RIGHT':
18242						$this->blk[$this->blklvl]['border_right'] = $this->border_details($v);
18243						if ($this->blk[$this->blklvl]['border_right']['s']) {
18244							$this->blk[$this->blklvl]['border'] = 1;
18245						}
18246						break;
18247
18248					// PADDING
18249					case 'PADDING-TOP':
18250						$this->blk[$this->blklvl]['padding_top'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18251						break;
18252					case 'PADDING-BOTTOM':
18253						$this->blk[$this->blklvl]['padding_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18254						break;
18255					case 'PADDING-LEFT':
18256						if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {
18257							$this->blk[$this->blklvl]['padding_left'] = 'auto';
18258							break;
18259						}
18260						$this->blk[$this->blklvl]['padding_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18261						break;
18262					case 'PADDING-RIGHT':
18263						if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {
18264							$this->blk[$this->blklvl]['padding_right'] = 'auto';
18265							break;
18266						}
18267						$this->blk[$this->blklvl]['padding_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18268						break;
18269
18270					// MARGINS
18271					case 'MARGIN-TOP':
18272						$tmp = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18273						if (isset($this->blk[$this->blklvl]['lastbottommargin'])) {
18274							if ($tmp > $this->blk[$this->blklvl]['lastbottommargin']) {
18275								$tmp -= $this->blk[$this->blklvl]['lastbottommargin'];
18276							} else {
18277								$tmp = 0;
18278							}
18279						}
18280						$this->blk[$this->blklvl]['margin_top'] = $tmp;
18281						break;
18282					case 'MARGIN-BOTTOM':
18283						$this->blk[$this->blklvl]['margin_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18284						break;
18285					case 'MARGIN-LEFT':
18286						$this->blk[$this->blklvl]['margin_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18287						break;
18288					case 'MARGIN-RIGHT':
18289						$this->blk[$this->blklvl]['margin_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18290						break;
18291
18292					/* -- BORDER-RADIUS -- */
18293					case 'BORDER-TOP-LEFT-RADIUS-H':
18294						$this->blk[$this->blklvl]['border_radius_TL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18295						break;
18296					case 'BORDER-TOP-LEFT-RADIUS-V':
18297						$this->blk[$this->blklvl]['border_radius_TL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18298						break;
18299					case 'BORDER-TOP-RIGHT-RADIUS-H':
18300						$this->blk[$this->blklvl]['border_radius_TR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18301						break;
18302					case 'BORDER-TOP-RIGHT-RADIUS-V':
18303						$this->blk[$this->blklvl]['border_radius_TR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18304						break;
18305					case 'BORDER-BOTTOM-LEFT-RADIUS-H':
18306						$this->blk[$this->blklvl]['border_radius_BL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18307						break;
18308					case 'BORDER-BOTTOM-LEFT-RADIUS-V':
18309						$this->blk[$this->blklvl]['border_radius_BL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18310						break;
18311					case 'BORDER-BOTTOM-RIGHT-RADIUS-H':
18312						$this->blk[$this->blklvl]['border_radius_BR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18313						break;
18314					case 'BORDER-BOTTOM-RIGHT-RADIUS-V':
18315						$this->blk[$this->blklvl]['border_radius_BR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18316						break;
18317					/* -- END BORDER-RADIUS -- */
18318
18319					case 'BOX-SHADOW':
18320						$bs = $this->cssManager->setCSSboxshadow($v);
18321						if ($bs) {
18322							$this->blk[$this->blklvl]['box_shadow'] = $bs;
18323						}
18324						break;
18325
18326					case 'BACKGROUND-CLIP':
18327						if (strtoupper($v) == 'PADDING-BOX') {
18328							$this->blk[$this->blklvl]['background_clip'] = 'padding-box';
18329						} elseif (strtoupper($v) == 'CONTENT-BOX') {
18330							$this->blk[$this->blklvl]['background_clip'] = 'content-box';
18331						}
18332						break;
18333
18334					case 'PAGE-BREAK-AFTER':
18335						if (strtoupper($v) == 'AVOID') {
18336							$this->blk[$this->blklvl]['page_break_after_avoid'] = true;
18337						} elseif (strtoupper($v) == 'ALWAYS' || strtoupper($v) == 'LEFT' || strtoupper($v) == 'RIGHT') {
18338							$this->blk[$this->blklvl]['page_break_after'] = strtoupper($v);
18339						}
18340						break;
18341
18342					// mPDF 6 pagebreaktype
18343					case 'BOX-DECORATION-BREAK':
18344						if (strtoupper($v) == 'CLONE') {
18345							$this->blk[$this->blklvl]['box_decoration_break'] = 'clone';
18346						} elseif (strtoupper($v) == 'SLICE') {
18347							$this->blk[$this->blklvl]['box_decoration_break'] = 'slice';
18348						}
18349						break;
18350
18351					case 'WIDTH':
18352						if (strtoupper($v) != 'AUTO') {
18353							$this->blk[$this->blklvl]['css_set_width'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
18354						}
18355						break;
18356
18357					// mPDF 6  Lists
18358					// LISTS
18359					case 'LIST-STYLE-TYPE':
18360						$this->blk[$this->blklvl]['list_style_type'] = strtolower($v);
18361						break;
18362					case 'LIST-STYLE-IMAGE':
18363						$this->blk[$this->blklvl]['list_style_image'] = strtolower($v);
18364						break;
18365					case 'LIST-STYLE-POSITION':
18366						$this->blk[$this->blklvl]['list_style_position'] = strtolower($v);
18367						break;
18368				}//end of switch($k)
18369			}
18370
18371
18372			if ($type != 'INLINE' && $type != 'TABLECELL') { // All block-level, including BODY tag
18373				switch ($k) {
18374					case 'TEXT-INDENT':
18375						// Computed value - to inherit
18376						$this->blk[$this->blklvl]['text_indent'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false) . 'mm';
18377						break;
18378
18379					case 'MARGIN-COLLAPSE': // Custom tag to collapse margins at top and bottom of page
18380						if (strtoupper($v) == 'COLLAPSE') {
18381							$this->blk[$this->blklvl]['margin_collapse'] = true;
18382						}
18383						break;
18384
18385					case 'LINE-HEIGHT':
18386						$this->blk[$this->blklvl]['line_height'] = $this->fixLineheight($v);
18387						if (!$this->blk[$this->blklvl]['line_height']) {
18388							$this->blk[$this->blklvl]['line_height'] = 'N';
18389						} // mPDF 6
18390						break;
18391
18392					// mPDF 6
18393					case 'LINE-STACKING-STRATEGY':
18394						$this->blk[$this->blklvl]['line_stacking_strategy'] = strtolower($v);
18395						break;
18396
18397					case 'LINE-STACKING-SHIFT':
18398						$this->blk[$this->blklvl]['line_stacking_shift'] = strtolower($v);
18399						break;
18400
18401					case 'TEXT-ALIGN': // left right center justify
18402						switch (strtoupper($v)) {
18403							case 'LEFT':
18404								$this->blk[$this->blklvl]['align'] = "L";
18405								break;
18406							case 'CENTER':
18407								$this->blk[$this->blklvl]['align'] = "C";
18408								break;
18409							case 'RIGHT':
18410								$this->blk[$this->blklvl]['align'] = "R";
18411								break;
18412							case 'JUSTIFY':
18413								$this->blk[$this->blklvl]['align'] = "J";
18414								break;
18415						}
18416						break;
18417
18418					/* -- BACKGROUNDS -- */
18419					case 'BACKGROUND-GRADIENT':
18420						if ($type == 'BLOCK') {
18421							$this->blk[$this->blklvl]['gradient'] = $v;
18422						}
18423						break;
18424					/* -- END BACKGROUNDS -- */
18425
18426					case 'DIRECTION':
18427						if ($v) {
18428							$this->blk[$this->blklvl]['direction'] = strtolower($v);
18429						}
18430						break;
18431				}
18432			}
18433
18434			// FOR INLINE ONLY
18435			if ($type == 'INLINE') {
18436				switch ($k) {
18437					case 'DISPLAY':
18438						if (strtoupper($v) == 'NONE') {
18439							$this->inlineDisplayOff = true;
18440						}
18441						break;
18442					case 'DIRECTION':
18443						break;
18444				}
18445			}
18446			// FOR INLINE ONLY
18447			if ($type == 'INLINE') {
18448				switch ($k) {
18449					// BORDERS
18450					case 'BORDER-TOP':
18451						$this->spanborddet['T'] = $this->border_details($v);
18452						$this->spanborder = true;
18453						$spanbordset = true;
18454						break;
18455					case 'BORDER-BOTTOM':
18456						$this->spanborddet['B'] = $this->border_details($v);
18457						$this->spanborder = true;
18458						$spanbordset = true;
18459						break;
18460					case 'BORDER-LEFT':
18461						$this->spanborddet['L'] = $this->border_details($v);
18462						$this->spanborder = true;
18463						$spanbordset = true;
18464						break;
18465					case 'BORDER-RIGHT':
18466						$this->spanborddet['R'] = $this->border_details($v);
18467						$this->spanborder = true;
18468						$spanbordset = true;
18469						break;
18470					case 'VISIBILITY': // block is set in OpenTag
18471						$v = strtolower($v);
18472						if ($v == 'visible' || $v == 'hidden' || $v == 'printonly' || $v == 'screenonly') {
18473							$this->textparam['visibility'] = $v;
18474						}
18475						break;
18476				}//end of switch($k)
18477			}
18478
18479			if ($type != 'TABLECELL') {
18480				// FOR INLINE and BLOCK
18481				switch ($k) {
18482					case 'TEXT-ALIGN': // left right center justify
18483						if (strtoupper($v) == 'NOJUSTIFY' && $this->blk[$this->blklvl]['align'] == "J") {
18484							$this->blk[$this->blklvl]['align'] = "";
18485						}
18486						break;
18487					// bgcolor only - to stay consistent with original html2fpdf
18488					case 'BACKGROUND':
18489					case 'BACKGROUND-COLOR':
18490						$cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
18491						if ($cor) {
18492							if ($tag == 'BODY') {
18493								$this->bodyBackgroundColor = $cor;
18494							} elseif ($type == 'INLINE') {
18495								$this->spanbgcolorarray = $cor;
18496								$this->spanbgcolor = true;
18497								$spanbgset = true;
18498							} else {
18499								$this->blk[$this->blklvl]['bgcolorarray'] = $cor;
18500								$this->blk[$this->blklvl]['bgcolor'] = true;
18501							}
18502						} elseif ($type != 'INLINE') {
18503							if ($this->ColActive) {
18504								$this->blk[$this->blklvl]['bgcolorarray'] = $this->blk[$prevlevel]['bgcolorarray'];
18505								$this->blk[$this->blklvl]['bgcolor'] = $this->blk[$prevlevel]['bgcolor'];
18506							}
18507						}
18508						break;
18509
18510					case 'VERTICAL-ALIGN': // super and sub only dealt with here e.g. <SUB> and <SUP>
18511						switch (strtoupper($v)) {
18512							case 'SUPER':
18513								$this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT); // mPDF 5.7.1
18514								$this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
18515								// mPDF 5.7.3  inline text-decoration parameters
18516								if (isset($this->textparam['text-baseline'])) {
18517									$this->textparam['text-baseline'] += ($this->baselineSup) * $preceeding_fontsize;
18518								} else {
18519									$this->textparam['text-baseline'] = ($this->baselineSup) * $preceeding_fontsize;
18520								}
18521								break;
18522							case 'SUB':
18523								$this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT);
18524								$this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
18525								// mPDF 5.7.3  inline text-decoration parameters
18526								if (isset($this->textparam['text-baseline'])) {
18527									$this->textparam['text-baseline'] += ($this->baselineSub) * $preceeding_fontsize;
18528								} else {
18529									$this->textparam['text-baseline'] = ($this->baselineSub) * $preceeding_fontsize;
18530								}
18531								break;
18532							case 'BASELINE':
18533								$this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
18534								$this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
18535								// mPDF 5.7.3  inline text-decoration parameters
18536								if (isset($this->textparam['text-baseline'])) {
18537									unset($this->textparam['text-baseline']);
18538								}
18539								break;
18540							// mPDF 5.7.3  inline text-decoration parameters
18541							default:
18542								$lh = $this->_computeLineheight($this->blk[$this->blklvl]['line_height']);
18543								$sz = $this->sizeConverter->convert($v, $lh, $this->FontSize, false);
18544								$this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
18545								$this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
18546								if ($sz) {
18547									if ($sz > 0) {
18548										$this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT);
18549									} else {
18550										$this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT);
18551									}
18552									if (isset($this->textparam['text-baseline'])) {
18553										$this->textparam['text-baseline'] += $sz;
18554									} else {
18555										$this->textparam['text-baseline'] = $sz;
18556									}
18557								}
18558						}
18559						break;
18560				}//end of switch($k)
18561			}
18562
18563
18564			// FOR ALL
18565			switch ($k) {
18566				case 'LETTER-SPACING':
18567					$this->lSpacingCSS = $v;
18568					if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
18569						$this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize);
18570					}
18571					break;
18572
18573				case 'WORD-SPACING':
18574					$this->wSpacingCSS = $v;
18575					if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
18576						$this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize);
18577					}
18578					break;
18579
18580				case 'FONT-STYLE': // italic normal oblique
18581					switch (strtoupper($v)) {
18582						case 'ITALIC':
18583						case 'OBLIQUE':
18584							$this->SetStyle('I', true);
18585							break;
18586						case 'NORMAL':
18587							$this->SetStyle('I', false);
18588							break;
18589					}
18590					break;
18591
18592				case 'FONT-WEIGHT': // normal bold // Does not support: bolder, lighter, 100..900(step value=100)
18593					switch (strtoupper($v)) {
18594						case 'BOLD':
18595							$this->SetStyle('B', true);
18596							break;
18597						case 'NORMAL':
18598							$this->SetStyle('B', false);
18599							break;
18600					}
18601					break;
18602
18603				case 'FONT-KERNING':
18604					if (strtoupper($v) == 'NORMAL' || (strtoupper($v) == 'AUTO' && $this->useKerning)) {
18605						/* -- OTL -- */
18606						if ($this->CurrentFont['haskernGPOS']) {
18607							if (isset($this->OTLtags['Plus'])) {
18608								$this->OTLtags['Plus'] .= ' kern';
18609							} else {
18610								$this->OTLtags['Plus'] = ' kern';
18611							}
18612						} /* -- END OTL -- */ else {  // *OTL*
18613							$this->textvar = ($this->textvar | TextVars::FC_KERNING);
18614						} // *OTL*
18615					} elseif (strtoupper($v) == 'NONE' || (strtoupper($v) == 'AUTO' && !$this->useKerning)) {
18616						if (isset($this->OTLtags['Plus'])) {
18617							$this->OTLtags['Plus'] = str_replace('kern', '', $this->OTLtags['Plus']); // *OTL*
18618						}
18619						if (isset($this->OTLtags['FFPlus'])) {
18620							$this->OTLtags['FFPlus'] = preg_replace('/kern[\d]*/', '', $this->OTLtags['FFPlus']);
18621						}
18622						$this->textvar = ($this->textvar & ~TextVars::FC_KERNING);
18623					}
18624					break;
18625
18626				/* -- OTL -- */
18627				case 'FONT-LANGUAGE-OVERRIDE':
18628					$v = strtoupper($v);
18629					if (strpos($v, 'NORMAL') !== false) {
18630						$this->fontLanguageOverride = '';
18631					} else {
18632						$this->fontLanguageOverride = trim($v);
18633					}
18634					break;
18635
18636
18637				case 'FONT-VARIANT-POSITION':
18638					if (isset($this->OTLtags['Plus'])) {
18639						$this->OTLtags['Plus'] = str_replace(['sups', 'subs'], '', $this->OTLtags['Plus']);
18640					}
18641					switch (strtoupper($v)) {
18642						case 'SUPER':
18643							$this->OTLtags['Plus'] .= ' sups';
18644							break;
18645						case 'SUB':
18646							$this->OTLtags['Plus'] .= ' subs';
18647							break;
18648						case 'NORMAL':
18649							break;
18650					}
18651					break;
18652
18653				case 'FONT-VARIANT-CAPS':
18654					$v = strtoupper($v);
18655					if (!isset($this->OTLtags['Plus'])) {
18656						$this->OTLtags['Plus'] = '';
18657					}
18658					$this->OTLtags['Plus'] = str_replace(['c2sc', 'smcp', 'c2pc', 'pcap', 'unic', 'titl'], '', $this->OTLtags['Plus']);
18659					$this->textvar = ($this->textvar & ~TextVars::FC_SMALLCAPS);   // ?????????????? <small-caps>
18660					if (strpos($v, 'ALL-SMALL-CAPS') !== false) {
18661						$this->OTLtags['Plus'] .= ' c2sc smcp';
18662					} elseif (strpos($v, 'SMALL-CAPS') !== false) {
18663						if (isset($this->CurrentFont['hassmallcapsGSUB']) && $this->CurrentFont['hassmallcapsGSUB']) {
18664							$this->OTLtags['Plus'] .= ' smcp';
18665						} else {
18666							$this->textvar = ($this->textvar | TextVars::FC_SMALLCAPS);
18667						}
18668					} elseif (strpos($v, 'ALL-PETITE-CAPS') !== false) {
18669						$this->OTLtags['Plus'] .= ' c2pc pcap';
18670					} elseif (strpos($v, 'PETITE-CAPS') !== false) {
18671						$this->OTLtags['Plus'] .= ' pcap';
18672					} elseif (strpos($v, 'UNICASE') !== false) {
18673						$this->OTLtags['Plus'] .= ' unic';
18674					} elseif (strpos($v, 'TITLING-CAPS') !== false) {
18675						$this->OTLtags['Plus'] .= ' titl';
18676					}
18677					break;
18678
18679				case 'FONT-VARIANT-LIGATURES':
18680					$v = strtoupper($v);
18681					if (!isset($this->OTLtags['Plus'])) {
18682						$this->OTLtags['Plus'] = '';
18683					}
18684					if (!isset($this->OTLtags['Minus'])) {
18685						$this->OTLtags['Minus'] = '';
18686					}
18687					if (strpos($v, 'NORMAL') !== false) {
18688						$this->OTLtags['Minus'] = str_replace(['liga', 'clig', 'calt'], '', $this->OTLtags['Minus']);
18689						$this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']);
18690					} elseif (strpos($v, 'NONE') !== false) {
18691						$this->OTLtags['Minus'] .= ' liga clig calt';
18692						$this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']);
18693					}
18694					if (strpos($v, 'NO-COMMON-LIGATURES') !== false) {
18695						$this->OTLtags['Minus'] .= ' liga clig';
18696					} elseif (strpos($v, 'COMMON-LIGATURES') !== false) {
18697						$this->OTLtags['Minus'] = str_replace(['liga', 'clig'], '', $this->OTLtags['Minus']);
18698					}
18699					if (strpos($v, 'NO-CONTEXTUAL') !== false) {
18700						$this->OTLtags['Minus'] .= ' calt';
18701					} elseif (strpos($v, 'CONTEXTUAL') !== false) {
18702						$this->OTLtags['Minus'] = str_replace('calt', '', $this->OTLtags['Minus']);
18703					}
18704					if (strpos($v, 'NO-DISCRETIONARY-LIGATURES') !== false) {
18705						$this->OTLtags['Plus'] = str_replace('dlig', '', $this->OTLtags['Plus']);
18706					} elseif (strpos($v, 'DISCRETIONARY-LIGATURES') !== false) {
18707						$this->OTLtags['Plus'] .= ' dlig';
18708					}
18709					if (strpos($v, 'NO-HISTORICAL-LIGATURES') !== false) {
18710						$this->OTLtags['Plus'] = str_replace('hlig', '', $this->OTLtags['Plus']);
18711					} elseif (strpos($v, 'HISTORICAL-LIGATURES') !== false) {
18712						$this->OTLtags['Plus'] .= ' hlig';
18713					}
18714
18715					break;
18716
18717				case 'FONT-VARIANT-NUMERIC':
18718					$v = strtoupper($v);
18719					if (!isset($this->OTLtags['Plus'])) {
18720						$this->OTLtags['Plus'] = '';
18721					}
18722					if (strpos($v, 'NORMAL') !== false) {
18723						$this->OTLtags['Plus'] = str_replace(['ordn', 'zero', 'lnum', 'onum', 'pnum', 'tnum', 'frac', 'afrc'], '', $this->OTLtags['Plus']);
18724					}
18725					if (strpos($v, 'ORDINAL') !== false) {
18726						$this->OTLtags['Plus'] .= ' ordn';
18727					}
18728					if (strpos($v, 'SLASHED-ZERO') !== false) {
18729						$this->OTLtags['Plus'] .= ' zero';
18730					}
18731					if (strpos($v, 'LINING-NUMS') !== false) {
18732						$this->OTLtags['Plus'] .= ' lnum';
18733						$this->OTLtags['Plus'] = str_replace('onum', '', $this->OTLtags['Plus']);
18734					} elseif (strpos($v, 'OLDSTYLE-NUMS') !== false) {
18735						$this->OTLtags['Plus'] .= ' onum';
18736						$this->OTLtags['Plus'] = str_replace('lnum', '', $this->OTLtags['Plus']);
18737					}
18738					if (strpos($v, 'PROPORTIONAL-NUMS') !== false) {
18739						$this->OTLtags['Plus'] .= ' pnum';
18740						$this->OTLtags['Plus'] = str_replace('tnum', '', $this->OTLtags['Plus']);
18741					} elseif (strpos($v, 'TABULAR-NUMS') !== false) {
18742						$this->OTLtags['Plus'] .= ' tnum';
18743						$this->OTLtags['Plus'] = str_replace('pnum', '', $this->OTLtags['Plus']);
18744					}
18745					if (strpos($v, 'DIAGONAL-FRACTIONS') !== false) {
18746						$this->OTLtags['Plus'] .= ' frac';
18747						$this->OTLtags['Plus'] = str_replace('afrc', '', $this->OTLtags['Plus']);
18748					} elseif (strpos($v, 'STACKED-FRACTIONS') !== false) {
18749						$this->OTLtags['Plus'] .= ' afrc';
18750						$this->OTLtags['Plus'] = str_replace('frac', '', $this->OTLtags['Plus']);
18751					}
18752					break;
18753
18754				case 'FONT-VARIANT-ALTERNATES':  // Only supports historical-forms
18755					$v = strtoupper($v);
18756					if (!isset($this->OTLtags['Plus'])) {
18757						$this->OTLtags['Plus'] = '';
18758					}
18759					if (strpos($v, 'NORMAL') !== false) {
18760						$this->OTLtags['Plus'] = str_replace('hist', '', $this->OTLtags['Plus']);
18761					}
18762					if (strpos($v, 'HISTORICAL-FORMS') !== false) {
18763						$this->OTLtags['Plus'] .= ' hist';
18764					}
18765					break;
18766
18767
18768				case 'FONT-FEATURE-SETTINGS':
18769					$v = strtolower($v);
18770					if (strpos($v, 'normal') !== false) {
18771						$this->OTLtags['FFMinus'] = '';
18772						$this->OTLtags['FFPlus'] = '';
18773					} else {
18774						if (!isset($this->OTLtags['FFPlus'])) {
18775							$this->OTLtags['FFPlus'] = '';
18776						}
18777						if (!isset($this->OTLtags['FFMinus'])) {
18778							$this->OTLtags['FFMinus'] = '';
18779						}
18780						$tags = preg_split('/[,]/', $v);
18781						foreach ($tags as $t) {
18782							if (preg_match('/[\"\']([a-zA-Z0-9]{4})[\"\']\s*(on|off|\d*){0,1}/', $t, $m)) {
18783								if ($m[2] == 'off' || $m[2] === '0') {
18784									if (strpos($this->OTLtags['FFMinus'], $m[1]) === false) {
18785										$this->OTLtags['FFMinus'] .= ' ' . $m[1];
18786									}
18787									$this->OTLtags['FFPlus'] = preg_replace('/' . $m[1] . '[\d]*/', '', $this->OTLtags['FFPlus']);
18788								} else {
18789									if ($m[2] == 'on') {
18790										$m[2] = '1';
18791									}
18792									if (strpos($this->OTLtags['FFPlus'], $m[1]) === false) {
18793										$this->OTLtags['FFPlus'] .= ' ' . $m[1] . $m[2];
18794									}
18795									$this->OTLtags['FFMinus'] = str_replace($m[1], '', $this->OTLtags['FFMinus']);
18796								}
18797							}
18798						}
18799					}
18800					break;
18801				/* -- END OTL -- */
18802
18803
18804				case 'TEXT-TRANSFORM': // none uppercase lowercase // Does support: capitalize
18805					switch (strtoupper($v)) { // Not working 100%
18806						case 'CAPITALIZE':
18807							$this->textvar = ($this->textvar | TextVars::FT_CAPITALIZE); // mPDF 5.7.1
18808							$this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
18809							$this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
18810							break;
18811						case 'UPPERCASE':
18812							$this->textvar = ($this->textvar | TextVars::FT_UPPERCASE); // mPDF 5.7.1
18813							$this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
18814							$this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
18815							break;
18816						case 'LOWERCASE':
18817							$this->textvar = ($this->textvar | TextVars::FT_LOWERCASE); // mPDF 5.7.1
18818							$this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
18819							$this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
18820							break;
18821						case 'NONE':
18822							break;
18823							$this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
18824							$this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
18825							$this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
18826					}
18827					break;
18828
18829				case 'TEXT-SHADOW':
18830					$ts = $this->cssManager->setCSStextshadow($v);
18831					if ($ts) {
18832						$this->textshadow = $ts;
18833					}
18834					break;
18835
18836				case 'HYPHENS':
18837					if (strtoupper($v) == 'NONE') {
18838						$this->textparam['hyphens'] = 2;
18839					} elseif (strtoupper($v) == 'AUTO') {
18840						$this->textparam['hyphens'] = 1;
18841					} elseif (strtoupper($v) == 'MANUAL') {
18842						$this->textparam['hyphens'] = 0;
18843					}
18844					break;
18845
18846				case 'TEXT-OUTLINE':
18847					if (strtoupper($v) == 'NONE') {
18848						$this->textparam['outline-s'] = false;
18849					}
18850					break;
18851
18852				case 'TEXT-OUTLINE-WIDTH':
18853				case 'OUTLINE-WIDTH':
18854					switch (strtoupper($v)) {
18855						case 'THIN':
18856							$v = '0.03em';
18857							break;
18858						case 'MEDIUM':
18859							$v = '0.05em';
18860							break;
18861						case 'THICK':
18862							$v = '0.07em';
18863							break;
18864					}
18865					$w = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize);
18866					if ($w) {
18867						$this->textparam['outline-WIDTH'] = $w;
18868						$this->textparam['outline-s'] = true;
18869					} else {
18870						$this->textparam['outline-s'] = false;
18871					}
18872					break;
18873
18874				case 'TEXT-OUTLINE-COLOR':
18875				case 'OUTLINE-COLOR':
18876					if (strtoupper($v) == 'INVERT') {
18877						if ($this->colorarray) {
18878							$cor = $this->colorarray;
18879							$this->textparam['outline-COLOR'] = $this->colorConverter->invert($cor);
18880						} else {
18881							$this->textparam['outline-COLOR'] = $this->colorConverter->convert(255, $this->PDFAXwarnings);
18882						}
18883					} else {
18884						$cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
18885						if ($cor) {
18886							$this->textparam['outline-COLOR'] = $cor;
18887						}
18888					}
18889					break;
18890
18891				case 'COLOR': // font color
18892					$cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
18893					if ($cor) {
18894						$this->colorarray = $cor;
18895						$this->SetTColor($cor);
18896					}
18897					break;
18898			}//end of switch($k)
18899		}//end of foreach
18900		// mPDF 5.7.3  inline text-decoration parameters
18901		// Needs to be set at the end - after vertical-align = super/sub, so that textparam['text-baseline'] is set
18902		if (isset($arrayaux['TEXT-DECORATION'])) {
18903			$v = $arrayaux['TEXT-DECORATION']; // none underline line-through (strikeout) // Does not support: blink
18904			if (stristr($v, 'LINE-THROUGH')) {
18905				$this->textvar = ($this->textvar | TextVars::FD_LINETHROUGH);
18906				// mPDF 5.7.3  inline text-decoration parameters
18907				if (isset($this->textparam['text-baseline'])) {
18908					$this->textparam['s-decoration']['baseline'] = $this->textparam['text-baseline'];
18909				} else {
18910					$this->textparam['s-decoration']['baseline'] = 0;
18911				}
18912				$this->textparam['s-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
18913				$this->textparam['s-decoration']['fontsize'] = $this->FontSize;
18914				$this->textparam['s-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
18915			}
18916			if (stristr($v, 'UNDERLINE')) {
18917				$this->textvar = ($this->textvar | TextVars::FD_UNDERLINE);
18918				// mPDF 5.7.3  inline text-decoration parameters
18919				if (isset($this->textparam['text-baseline'])) {
18920					$this->textparam['u-decoration']['baseline'] = $this->textparam['text-baseline'];
18921				} else {
18922					$this->textparam['u-decoration']['baseline'] = 0;
18923				}
18924				$this->textparam['u-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
18925				$this->textparam['u-decoration']['fontsize'] = $this->FontSize;
18926				$this->textparam['u-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
18927			}
18928			if (stristr($v, 'OVERLINE')) {
18929				$this->textvar = ($this->textvar | TextVars::FD_OVERLINE);
18930				// mPDF 5.7.3  inline text-decoration parameters
18931				if (isset($this->textparam['text-baseline'])) {
18932					$this->textparam['o-decoration']['baseline'] = $this->textparam['text-baseline'];
18933				} else {
18934					$this->textparam['o-decoration']['baseline'] = 0;
18935				}
18936				$this->textparam['o-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
18937				$this->textparam['o-decoration']['fontsize'] = $this->FontSize;
18938				$this->textparam['o-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
18939			}
18940			if (stristr($v, 'NONE')) {
18941				$this->textvar = ($this->textvar & ~TextVars::FD_UNDERLINE);
18942				$this->textvar = ($this->textvar & ~TextVars::FD_LINETHROUGH);
18943				$this->textvar = ($this->textvar & ~TextVars::FD_OVERLINE);
18944				// mPDF 5.7.3  inline text-decoration parameters
18945				if (isset($this->textparam['u-decoration'])) {
18946					unset($this->textparam['u-decoration']);
18947				}
18948				if (isset($this->textparam['s-decoration'])) {
18949					unset($this->textparam['s-decoration']);
18950				}
18951				if (isset($this->textparam['o-decoration'])) {
18952					unset($this->textparam['o-decoration']);
18953				}
18954			}
18955		}
18956		// mPDF 6
18957		if ($spanbordset) { // BORDER has been set on this INLINE element
18958			if (isset($this->textparam['text-baseline'])) {
18959				$this->textparam['bord-decoration']['baseline'] = $this->textparam['text-baseline'];
18960			} else {
18961				$this->textparam['bord-decoration']['baseline'] = 0;
18962			}
18963			$this->textparam['bord-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
18964			$this->textparam['bord-decoration']['fontsize'] = $this->FontSize;
18965		}
18966		if ($spanbgset) { // BACKGROUND[-COLOR] has been set on this INLINE element
18967			if (isset($this->textparam['text-baseline'])) {
18968				$this->textparam['bg-decoration']['baseline'] = $this->textparam['text-baseline'];
18969			} else {
18970				$this->textparam['bg-decoration']['baseline'] = 0;
18971			}
18972			$this->textparam['bg-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
18973			$this->textparam['bg-decoration']['fontsize'] = $this->FontSize;
18974		}
18975	}
18976
18977	/* -- END HTML-CSS -- */
18978
18979	function SetStyle($tag, $enable)
18980	{
18981		$this->$tag = $enable;
18982		$style = '';
18983		foreach (['B', 'I'] as $s) {
18984			if ($this->$s) {
18985				$style .= $s;
18986			}
18987		}
18988		$this->currentfontstyle = $style;
18989		$this->SetFont('', $style, 0, false);
18990	}
18991
18992	// Set multiple styles at one time
18993	function SetStylesArray($arr)
18994	{
18995		$style = '';
18996		foreach (['B', 'I'] as $s) {
18997			if (isset($arr[$s])) {
18998				if ($arr[$s]) {
18999					$this->$s = true;
19000					$style .= $s;
19001				} else {
19002					$this->$s = false;
19003				}
19004			} elseif ($this->$s) {
19005				$style .= $s;
19006			}
19007		}
19008		$this->currentfontstyle = $style;
19009		$this->SetFont('', $style, 0, false);
19010	}
19011
19012	// Set multiple styles at one $str e.g. "BI"
19013	function SetStyles($str)
19014	{
19015		$style = '';
19016		foreach (['B', 'I'] as $s) {
19017			if (strpos($str, $s) !== false) {
19018				$this->$s = true;
19019				$style .= $s;
19020			} else {
19021				$this->$s = false;
19022			}
19023		}
19024		$this->currentfontstyle = $style;
19025		$this->SetFont('', $style, 0, false);
19026	}
19027
19028	function ResetStyles()
19029	{
19030		foreach (['B', 'I'] as $s) {
19031			$this->$s = false;
19032		}
19033		$this->currentfontstyle = '';
19034		$this->SetFont('', '', 0, false);
19035	}
19036
19037	function DisableTags($str = '')
19038	{
19039		if ($str == '') { // enable all tags
19040			// Insert new supported tags in the long string below.
19041			$this->enabledtags = "<a><acronym><address><article><aside><b><bdi><bdo><big><blockquote><br><caption><center><cite><code><del><details><dd><div><dl><dt><em><fieldset><figcaption><figure><font><form><h1><h2><h3><h4><h5><h6><hgroup><hr><i><img><input><ins><kbd><legend><li><main><mark><meter><nav><ol><option><p><pre><progress><q><s><samp><section><select><small><span><strike><strong><sub><summary><sup><table><tbody><td><template><textarea><tfoot><th><thead><time><tr><tt><u><ul><var><footer><header><annotation><bookmark><textcircle><barcode><dottab><indexentry><indexinsert><watermarktext><watermarkimage><tts><ttz><tta><column_break><columnbreak><newcolumn><newpage><page_break><pagebreak><formfeed><columns><toc><tocentry><tocpagebreak><pageheader><pagefooter><setpageheader><setpagefooter><sethtmlpageheader><sethtmlpagefooter>";
19042		} else {
19043			$str = explode(",", $str);
19044			foreach ($str as $v) {
19045				$this->enabledtags = str_replace(trim($v), '', $this->enabledtags);
19046			}
19047		}
19048	}
19049
19050	/* -- TABLES -- */
19051
19052	function TableCheckMinWidth($maxwidth, $forcewrap = 0, $textbuffer = [], $checkletter = false)
19053	{
19054	// mPDF 6
19055		$acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)
19056		$acclongest = 0; // mPDF 6 (accumulated length max across > 1 chunk)
19057		$biggestword = 0;
19058		$toonarrow = false;
19059		if ((count($textbuffer) == 0) or ( (count($textbuffer) == 1) && ($textbuffer[0][0] == ''))) {
19060			return 0;
19061		}
19062
19063		foreach ($textbuffer as $chunk) {
19064			$line = $chunk[0];
19065			$OTLdata = (isset($chunk[18]) ? $chunk[18] : null);
19066
19067			// mPDF ITERATION
19068			if ($this->iterationCounter) {
19069				$line = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $line);
19070			}
19071
19072			// IMAGES & FORM ELEMENTS
19073			if (substr($line, 0, 3) == "\xbb\xa4\xac") { // inline object - FORM element or IMAGE!
19074				$objattr = $this->_getObjAttr($line);
19075				if ($objattr['type'] != 'hr' && isset($objattr['width']) && ($objattr['width'] / $this->shrin_k) > ($maxwidth + 0.0001)) {
19076					if (($objattr['width'] / $this->shrin_k) > $biggestword) {
19077						$biggestword = ($objattr['width'] / $this->shrin_k);
19078					}
19079					$toonarrow = true;
19080				}
19081				continue;
19082			}
19083
19084			if ($line == "\n") {
19085				$acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)
19086				continue;
19087			}
19088			$line = trim($line);
19089			if (!empty($OTLdata)) {
19090				$this->otl->trimOTLdata($OTLdata, true, true);
19091			} // *OTL*
19092			// SET FONT SIZE/STYLE from $chunk[n]
19093			// FONTSIZE
19094			if (isset($chunk[11]) and $chunk[11] != '') {
19095				if ($this->shrin_k) {
19096					$this->SetFontSize($chunk[11] / $this->shrin_k, false);
19097				} else {
19098					$this->SetFontSize($chunk[11], false);
19099				}
19100			}
19101			// FONTFAMILY
19102			if (isset($chunk[4]) and $chunk[4] != '') {
19103				$font = $this->SetFont($chunk[4], $this->FontStyle, 0, false);
19104			}
19105			// B I
19106			if (isset($chunk[2]) and $chunk[2] != '') {
19107				$this->SetStyles($chunk[2]);
19108			}
19109
19110			$lbw = $rbw = 0; // Border widths
19111			if (isset($chunk[16]) && !empty($chunk[16])) { // Border
19112				$this->spanborddet = $chunk[16];
19113				$lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
19114				$rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
19115			}
19116			if (isset($chunk[15])) {   // Word spacing
19117				$this->wSpacingCSS = $chunk[15];
19118				if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
19119					$this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
19120				}
19121			}
19122			if (isset($chunk[14])) {   // Letter spacing
19123				$this->lSpacingCSS = $chunk[14];
19124				if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
19125					$this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
19126				}
19127			}
19128			if (isset($chunk[8])) { // mPDF 5.7.1
19129				$this->textvar = $chunk[8];
19130			}
19131
19132			// mPDF 6
19133			// If overflow==wrap ($checkletter) OR (No word breaks and contains CJK)
19134			if ($checkletter || (!preg_match('/(\xe2\x80\x8b| )/', trim($line)) && preg_match("/([" . $this->pregCJKchars . "])/u", $line) )) {
19135				if (preg_match("/([" . $this->pregCJKchars . "])/u", $line)) {
19136					$checkCJK = true;
19137				} else {
19138					$checkCJK = false;
19139				}
19140
19141				$letters = preg_split('//u', $line);
19142				foreach ($letters as $k => $letter) {
19143					// mPDF 6
19144					if ($checkCJK) {
19145						if (preg_match("/[" . $this->CJKleading . "]/u", $letter) && $k > 0) {
19146							$letter = $letters[$k - 1] . $letter;
19147						}
19148						if (preg_match("/[" . $this->CJKfollowing . "]/u", $letter) && $k < (count($letters) - 1)) {
19149							$letter = $letter . $letters[$k + 1];
19150						}
19151					}
19152
19153					$letterwidth = $this->GetStringWidth($letter, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here
19154					// so don't have to split OTLdata for each word
19155					if ($k == 0) {
19156						$letterwidth += $lbw;
19157					}
19158					if ($k == (count($letters) - 1)) {
19159						$letterwidth += $rbw;
19160					}
19161
19162					// Warn user that maxwidth is insufficient
19163					if ($letterwidth > $maxwidth + 0.0001) {
19164						if ($letterwidth > $biggestword) {
19165							$biggestword = $letterwidth;
19166						}
19167						$toonarrow = true;
19168					}
19169				}
19170			} else {
19171				// mPDF 6
19172				// Need to account for any XAdvance in GPOSinfo (OTLdata = $chunk[18])
19173				$wordXAdvance = [];
19174				if (isset($chunk[18]) && $chunk[18]) {
19175					preg_match_all('/(\xe2\x80\x8b| )/', $line, $spaces, PREG_OFFSET_CAPTURE); // U+200B Zero Width word boundary, or space
19176					$lastoffset = 0;
19177					$k = -1; // Added so that if no spaces found, "last word" later is calculated for the one and only word
19178					foreach ($spaces[0] as $k => $m) {
19179						$offset = $m[1];
19180						// ...TableCheckMinWidth...
19181						// At this point, BIDI not applied, Writing direction is not set, and XAdvanceL balances XAdvanceR
19182						for ($n = $lastoffset; $n < $offset; $n++) {
19183							if (isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {
19184								if (isset($wordXAdvance[$k])) {
19185									$wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
19186								} else {
19187									$wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
19188								}
19189							}
19190						}
19191						$lastoffset = $offset + 1;
19192					}
19193
19194					$k++;  // last word
19195					foreach ($chunk[18]['GPOSinfo'] as $n => $gpos) {
19196						if ($n >= $lastoffset && isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {
19197							if (isset($wordXAdvance[$k])) {
19198								$wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
19199							} else {
19200								$wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
19201							}
19202						}
19203					}
19204				}
19205
19206				$words = preg_split('/(\xe2\x80\x8b| )/', $line); // U+200B Zero Width word boundary, or space
19207				foreach ($words as $k => $word) {
19208					$word = trim($word);
19209					$wordwidth = $this->GetStringWidth($word, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here
19210					// so don't have to split OTLdata for each word
19211					if (isset($wordXAdvance[$k])) {
19212						$wordwidth += ($wordXAdvance[$k] * 1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000);
19213					}
19214					if ($k == 0) {
19215						$wordwidth += $lbw;
19216					}
19217					if ($k == (count($words) - 1)) {
19218						$wordwidth += $rbw;
19219					}
19220
19221					// mPDF 6
19222					if (count($words) == 1 && substr($chunk[0], 0, 1) != ' ') {
19223						$acclength += $wordwidth;
19224					} elseif (count($words) > 1 && $k == 0 && substr($chunk[0], 0, 1) != ' ') {
19225						$acclength += $wordwidth;
19226					} else {
19227						$acclength = $wordwidth;
19228					}
19229					$acclongest = max($acclongest, $acclength);
19230					if (count($words) == 1 && substr($chunk[0], -1, 1) == ' ') {
19231						$acclength = 0;
19232					} elseif (count($words) > 1 && ($k != (count($words) - 1) || substr($chunk[0], -1, 1) == ' ')) {
19233						$acclength = 0;
19234					}
19235
19236					// Warn user that maxwidth is insufficient
19237					if ($wordwidth > $maxwidth + 0.0001) {
19238						if ($wordwidth > $biggestword) {
19239							$biggestword = $wordwidth;
19240						}
19241						$toonarrow = true;
19242					}
19243				}
19244			}
19245
19246			// mPDF 6  Accumulated length of biggest word - across multiple chunks
19247			if ($acclongest > $maxwidth + 0.0001) {
19248				if ($acclongest > $biggestword) {
19249					$biggestword = $acclongest;
19250				}
19251				$toonarrow = true;
19252			}
19253
19254			// RESET FONT SIZE/STYLE
19255			// RESETTING VALUES
19256			// Now we must deactivate what we have used
19257			if (isset($chunk[2]) and $chunk[2] != '') {
19258				$this->ResetStyles();
19259			}
19260			if (isset($chunk[4]) and $chunk[4] != '') {
19261				$this->SetFont($this->default_font, $this->FontStyle, 0, false);
19262			}
19263			if (isset($chunk[11]) and $chunk[11] != '') {
19264				$this->SetFontSize($this->default_font_size, false);
19265			}
19266			$this->spanborddet = [];
19267			$this->textvar = 0x00; // mPDF 5.7.1
19268			$this->OTLtags = [];
19269			$this->lSpacingCSS = '';
19270			$this->wSpacingCSS = '';
19271			$this->fixedlSpacing = false;
19272			$this->minwSpacing = 0;
19273		}
19274
19275		// Return -(wordsize) if word is bigger than maxwidth
19276		// ADDED
19277		if (($toonarrow) && ($this->table_error_report)) {
19278			throw new \Mpdf\MpdfException("Word is too long to fit in table - " . $this->table_error_report_param);
19279		}
19280		if ($toonarrow) {
19281			return -$biggestword;
19282		} else {
19283			return 1;
19284		}
19285	}
19286
19287	function shrinkTable(&$table, $k)
19288	{
19289		$table['border_spacing_H'] /= $k;
19290		$table['border_spacing_V'] /= $k;
19291
19292		$table['padding']['T'] /= $k;
19293		$table['padding']['R'] /= $k;
19294		$table['padding']['B'] /= $k;
19295		$table['padding']['L'] /= $k;
19296
19297		$table['margin']['T'] /= $k;
19298		$table['margin']['R'] /= $k;
19299		$table['margin']['B'] /= $k;
19300		$table['margin']['L'] /= $k;
19301
19302		$table['border_details']['T']['w'] /= $k;
19303		$table['border_details']['R']['w'] /= $k;
19304		$table['border_details']['B']['w'] /= $k;
19305		$table['border_details']['L']['w'] /= $k;
19306
19307		if (isset($table['max_cell_border_width']['T'])) {
19308			$table['max_cell_border_width']['T'] /= $k;
19309		}
19310		if (isset($table['max_cell_border_width']['R'])) {
19311			$table['max_cell_border_width']['R'] /= $k;
19312		}
19313		if (isset($table['max_cell_border_width']['B'])) {
19314			$table['max_cell_border_width']['B'] /= $k;
19315		}
19316		if (isset($table['max_cell_border_width']['L'])) {
19317			$table['max_cell_border_width']['L'] /= $k;
19318		}
19319
19320		if ($this->simpleTables) {
19321			$table['simple']['border_details']['T']['w'] /= $k;
19322			$table['simple']['border_details']['R']['w'] /= $k;
19323			$table['simple']['border_details']['B']['w'] /= $k;
19324			$table['simple']['border_details']['L']['w'] /= $k;
19325		}
19326
19327		$table['miw'] /= $k;
19328		$table['maw'] /= $k;
19329
19330		for ($j = 0; $j < $table['nc']; $j++) { // columns
19331
19332			$table['wc'][$j]['miw'] = isset($table['wc'][$j]['miw']) ? $table['wc'][$j]['miw'] : 0;
19333			$table['wc'][$j]['maw'] = isset($table['wc'][$j]['maw']) ? $table['wc'][$j]['maw'] : 0;
19334
19335			$table['wc'][$j]['miw'] /= $k;
19336			$table['wc'][$j]['maw'] /= $k;
19337
19338			if (isset($table['decimal_align'][$j]['maxs0']) && $table['decimal_align'][$j]['maxs0']) {
19339				$table['decimal_align'][$j]['maxs0'] /= $k;
19340			}
19341
19342			if (isset($table['decimal_align'][$j]['maxs1']) && $table['decimal_align'][$j]['maxs1']) {
19343				$table['decimal_align'][$j]['maxs1'] /= $k;
19344			}
19345
19346			if (isset($table['wc'][$j]['absmiw']) && $table['wc'][$j]['absmiw']) {
19347				$table['wc'][$j]['absmiw'] /= $k;
19348			}
19349
19350			for ($i = 0; $i < $table['nr']; $i++) { // rows
19351
19352				$c = &$table['cells'][$i][$j];
19353
19354				if (isset($c) && $c) {
19355
19356					if (!$this->simpleTables) {
19357
19358						if ($this->packTableData) {
19359
19360							$cell = $this->_unpackCellBorder($c['borderbin']);
19361
19362							$cell['border_details']['T']['w'] /= $k;
19363							$cell['border_details']['R']['w'] /= $k;
19364							$cell['border_details']['B']['w'] /= $k;
19365							$cell['border_details']['L']['w'] /= $k;
19366							$cell['border_details']['mbw']['TL'] /= $k;
19367							$cell['border_details']['mbw']['TR'] /= $k;
19368							$cell['border_details']['mbw']['BL'] /= $k;
19369							$cell['border_details']['mbw']['BR'] /= $k;
19370							$cell['border_details']['mbw']['LT'] /= $k;
19371							$cell['border_details']['mbw']['LB'] /= $k;
19372							$cell['border_details']['mbw']['RT'] /= $k;
19373							$cell['border_details']['mbw']['RB'] /= $k;
19374
19375							$c['borderbin'] = $this->_packCellBorder($cell);
19376
19377						} else {
19378
19379							$c['border_details']['T']['w'] /= $k;
19380							$c['border_details']['R']['w'] /= $k;
19381							$c['border_details']['B']['w'] /= $k;
19382							$c['border_details']['L']['w'] /= $k;
19383							$c['border_details']['mbw']['TL'] /= $k;
19384							$c['border_details']['mbw']['TR'] /= $k;
19385							$c['border_details']['mbw']['BL'] /= $k;
19386							$c['border_details']['mbw']['BR'] /= $k;
19387							$c['border_details']['mbw']['LT'] /= $k;
19388							$c['border_details']['mbw']['LB'] /= $k;
19389							$c['border_details']['mbw']['RT'] /= $k;
19390							$c['border_details']['mbw']['RB'] /= $k;
19391						}
19392					}
19393
19394					$c['padding']['T'] /= $k;
19395					$c['padding']['R'] /= $k;
19396					$c['padding']['B'] /= $k;
19397					$c['padding']['L'] /= $k;
19398
19399					$c['maxs'] = isset($c['maxs']) ? $c['maxs'] /= $k : null;
19400					$c['w'] = isset($c['w']) ? $c['w'] /= $k : null;
19401
19402					$c['s'] = isset($c['s']) ? $c['s'] /= $k : 0;
19403					$c['h'] = isset($c['h']) ? $c['h'] /= $k : null;
19404
19405					$c['miw'] = isset($c['miw']) ? $c['miw'] /= $k : 0;
19406					$c['maw'] = isset($c['maw']) ? $c['maw'] /= $k : 0;
19407
19408					$c['absmiw'] = isset($c['absmiw']) ? $c['absmiw'] /= $k : null;
19409
19410					$c['nestedmaw'] = isset($c['nestedmaw']) ? $c['nestedmaw'] /= $k : null;
19411					$c['nestedmiw'] = isset($c['nestedmiw']) ? $c['nestedmiw'] /= $k : null;
19412
19413					if (isset($c['textbuffer'])) {
19414						foreach ($c['textbuffer'] as $n => $tb) {
19415							if (!empty($tb[16])) {
19416								!isset($c['textbuffer'][$n][16]['T']) || $c['textbuffer'][$n][16]['T']['w'] /= $k;
19417								!isset($c['textbuffer'][$n][16]['B']) || $c['textbuffer'][$n][16]['B']['w'] /= $k;
19418								!isset($c['textbuffer'][$n][16]['L']) || $c['textbuffer'][$n][16]['L']['w'] /= $k;
19419								!isset($c['textbuffer'][$n][16]['R']) || $c['textbuffer'][$n][16]['R']['w'] /= $k;
19420							}
19421						}
19422					}
19423
19424					unset($c);
19425				}
19426
19427			} // rows
19428		} // columns
19429	}
19430
19431	function read_short(&$fh)
19432	{
19433		$s = fread($fh, 2);
19434		$a = (ord($s[0]) << 8) + ord($s[1]);
19435		if ($a & (1 << 15)) {
19436			$a = ($a - (1 << 16));
19437		}
19438		return $a;
19439	}
19440
19441	function _packCellBorder($cell)
19442	{
19443		if (!is_array($cell) || !isset($cell)) {
19444			return '';
19445		}
19446
19447		if (!$this->packTableData) {
19448			return $cell;
19449		}
19450		// = 186 bytes
19451		$bindata = pack("nnda6A10nnda6A10nnda6A10nnda6A10nd9", $cell['border'], $cell['border_details']['R']['s'], $cell['border_details']['R']['w'], $cell['border_details']['R']['c'], $cell['border_details']['R']['style'], $cell['border_details']['R']['dom'], $cell['border_details']['L']['s'], $cell['border_details']['L']['w'], $cell['border_details']['L']['c'], $cell['border_details']['L']['style'], $cell['border_details']['L']['dom'], $cell['border_details']['T']['s'], $cell['border_details']['T']['w'], $cell['border_details']['T']['c'], $cell['border_details']['T']['style'], $cell['border_details']['T']['dom'], $cell['border_details']['B']['s'], $cell['border_details']['B']['w'], $cell['border_details']['B']['c'], $cell['border_details']['B']['style'], $cell['border_details']['B']['dom'], $cell['border_details']['mbw']['BL'], $cell['border_details']['mbw']['BR'], $cell['border_details']['mbw']['RT'], $cell['border_details']['mbw']['RB'], $cell['border_details']['mbw']['TL'], $cell['border_details']['mbw']['TR'], $cell['border_details']['mbw']['LT'], $cell['border_details']['mbw']['LB'], (isset($cell['border_details']['cellposdom']) ? $cell['border_details']['cellposdom'] : 0));
19452		return $bindata;
19453	}
19454
19455	function _getBorderWidths($bindata)
19456	{
19457		if (!$bindata) {
19458			return [0, 0, 0, 0];
19459		}
19460		if (!$this->packTableData) {
19461			return [$bindata['border_details']['T']['w'], $bindata['border_details']['R']['w'], $bindata['border_details']['B']['w'], $bindata['border_details']['L']['w']];
19462		}
19463
19464		$bd = unpack("nbord/nrs/drw/a6rca/A10rst/nrd/nls/dlw/a6lca/A10lst/nld/nts/dtw/a6tca/A10tst/ntd/nbs/dbw/a6bca/A10bst/nbd/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd", $bindata);
19465		$cell['border_details']['R']['w'] = $bd['rw'];
19466		$cell['border_details']['L']['w'] = $bd['lw'];
19467		$cell['border_details']['T']['w'] = $bd['tw'];
19468		$cell['border_details']['B']['w'] = $bd['bw'];
19469		return [$bd['tw'], $bd['rw'], $bd['bw'], $bd['lw']];
19470	}
19471
19472	function _unpackCellBorder($bindata)
19473	{
19474		if (!$bindata) {
19475			return [];
19476		}
19477		if (!$this->packTableData) {
19478			return $bindata;
19479		}
19480
19481		$bd = unpack("nbord/nrs/drw/a6rca/A10rst/nrd/nls/dlw/a6lca/A10lst/nld/nts/dtw/a6tca/A10tst/ntd/nbs/dbw/a6bca/A10bst/nbd/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd", $bindata);
19482
19483		$cell['border'] = $bd['bord'];
19484		$cell['border_details']['R']['s'] = $bd['rs'];
19485		$cell['border_details']['R']['w'] = $bd['rw'];
19486		$cell['border_details']['R']['c'] = str_pad($bd['rca'], 6, "\x00");
19487		$cell['border_details']['R']['style'] = trim($bd['rst']);
19488		$cell['border_details']['R']['dom'] = $bd['rd'];
19489
19490		$cell['border_details']['L']['s'] = $bd['ls'];
19491		$cell['border_details']['L']['w'] = $bd['lw'];
19492		$cell['border_details']['L']['c'] = str_pad($bd['lca'], 6, "\x00");
19493		$cell['border_details']['L']['style'] = trim($bd['lst']);
19494		$cell['border_details']['L']['dom'] = $bd['ld'];
19495
19496		$cell['border_details']['T']['s'] = $bd['ts'];
19497		$cell['border_details']['T']['w'] = $bd['tw'];
19498		$cell['border_details']['T']['c'] = str_pad($bd['tca'], 6, "\x00");
19499		$cell['border_details']['T']['style'] = trim($bd['tst']);
19500		$cell['border_details']['T']['dom'] = $bd['td'];
19501
19502		$cell['border_details']['B']['s'] = $bd['bs'];
19503		$cell['border_details']['B']['w'] = $bd['bw'];
19504		$cell['border_details']['B']['c'] = str_pad($bd['bca'], 6, "\x00");
19505		$cell['border_details']['B']['style'] = trim($bd['bst']);
19506		$cell['border_details']['B']['dom'] = $bd['bd'];
19507
19508		$cell['border_details']['mbw']['BL'] = $bd['mbl'];
19509		$cell['border_details']['mbw']['BR'] = $bd['mbr'];
19510		$cell['border_details']['mbw']['RT'] = $bd['mrt'];
19511		$cell['border_details']['mbw']['RB'] = $bd['mrb'];
19512		$cell['border_details']['mbw']['TL'] = $bd['mtl'];
19513		$cell['border_details']['mbw']['TR'] = $bd['mtr'];
19514		$cell['border_details']['mbw']['LT'] = $bd['mlt'];
19515		$cell['border_details']['mbw']['LB'] = $bd['mlb'];
19516		$cell['border_details']['cellposdom'] = $bd['cpd'];
19517
19518
19519		return($cell);
19520	}
19521
19522	////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
19523	////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
19524	////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
19525	// table		Array of (w, h, bc, nr, wc, hr, cells)
19526	// w			Width of table
19527	// h			Height of table
19528	// nc			Number column
19529	// nr			Number row
19530	// hr			List of height of each row
19531	// wc			List of width of each column
19532	// cells		List of cells of each rows, cells[i][j] is a cell in the table
19533	function _tableColumnWidth(&$table, $firstpass = false)
19534	{
19535		$cs = &$table['cells'];
19536
19537		$nc = $table['nc'];
19538		$nr = $table['nr'];
19539		$listspan = [];
19540
19541		if ($table['borders_separate']) {
19542			$tblbw = $table['border_details']['L']['w'] + $table['border_details']['R']['w'] + $table['margin']['L'] + $table['margin']['R'] + $table['padding']['L'] + $table['padding']['R'] + $table['border_spacing_H'];
19543		} else {
19544			$tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];
19545		}
19546
19547		// ADDED table['l'][colno]
19548		// = total length of text approx (using $c['s']) in that column - used to approximately distribute col widths in _tableWidth
19549		//
19550		for ($j = 0; $j < $nc; $j++) { // columns
19551			$wc = &$table['wc'][$j];
19552			for ($i = 0; $i < $nr; $i++) { // rows
19553				if (isset($cs[$i][$j]) && $cs[$i][$j]) {
19554					$c = &$cs[$i][$j];
19555
19556					if ($this->simpleTables) {
19557						if ($table['borders_separate']) { // NB twice border width
19558							$extrcw = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
19559						} else {
19560							$extrcw = $table['simple']['border_details']['L']['w'] / 2 + $table['simple']['border_details']['R']['w'] / 2 + $c['padding']['L'] + $c['padding']['R'];
19561						}
19562					} else {
19563						if ($this->packTableData) {
19564							list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
19565						} else {
19566							$br = $c['border_details']['R']['w'];
19567							$bl = $c['border_details']['L']['w'];
19568						}
19569						if ($table['borders_separate']) { // NB twice border width
19570							$extrcw = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
19571						} else {
19572							$extrcw = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];
19573						}
19574					}
19575
19576					// $mw = $this->GetStringWidth('W') + $extrcw ;
19577					$mw = $extrcw; // mPDF 6
19578					if (substr($c['a'], 0, 1) == 'D') {
19579						$mw = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'] + $extrcw;
19580					}
19581
19582					$c['absmiw'] = $mw;
19583
19584					if (isset($c['R']) && $c['R']) {
19585						$c['maw'] = $c['miw'] = $this->FontSize + $extrcw;
19586						if (isset($c['w'])) { // If cell width is specified
19587							if ($c['miw'] < $c['w']) {
19588								$c['miw'] = $c['w'];
19589							}
19590						}
19591						if (!isset($c['colspan'])) {
19592							if ($wc['miw'] < $c['miw']) {
19593								$wc['miw'] = $c['miw'];
19594							}
19595							if ($wc['maw'] < $c['maw']) {
19596								$wc['maw'] = $c['maw'];
19597							}
19598
19599							if ($firstpass) {
19600								if (isset($table['l'][$j])) {
19601									$table['l'][$j] += $c['miw'];
19602								} else {
19603									$table['l'][$j] = $c['miw'];
19604								}
19605							}
19606						}
19607						if ($c['miw'] > $wc['miw']) {
19608							$wc['miw'] = $c['miw'];
19609						}
19610						if ($wc['miw'] > $wc['maw']) {
19611							$wc['maw'] = $wc['miw'];
19612						}
19613						continue;
19614					}
19615
19616					if ($firstpass) {
19617						if (isset($c['s'])) {
19618							$c['s'] += $extrcw;
19619						}
19620						if (isset($c['maxs'])) {
19621							$c['maxs'] += $extrcw;
19622						}
19623						if (isset($c['nestedmiw'])) {
19624							$c['nestedmiw'] += $extrcw;
19625						}
19626						if (isset($c['nestedmaw'])) {
19627							$c['nestedmaw'] += $extrcw;
19628						}
19629					}
19630
19631
19632					// If minimum width has already been set by a nested table or inline object (image/form), use it
19633					if (isset($c['nestedmiw']) && (!isset($this->table[1][1]['overflow']) || $this->table[1][1]['overflow'] != 'visible')) {
19634						$miw = $c['nestedmiw'];
19635					} else {
19636						$miw = $mw;
19637					}
19638
19639					if (isset($c['maxs']) && $c['maxs'] != '') {
19640						$c['s'] = $c['maxs'];
19641					}
19642
19643					// If maximum width has already been set by a nested table, use it
19644					if (isset($c['nestedmaw'])) {
19645						$c['maw'] = $c['nestedmaw'];
19646					} else {
19647						$c['maw'] = $c['s'];
19648					}
19649
19650					if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {
19651						if (($c['maw'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
19652							$c['maw'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;
19653						}
19654					}
19655
19656					if (isset($c['nowrap']) && $c['nowrap']) {
19657						$miw = $c['maw'];
19658					}
19659
19660					if (isset($c['wpercent']) && $firstpass) {
19661						if (isset($c['colspan'])) { // Not perfect - but % set on colspan is shared equally on cols.
19662							for ($k = 0; $k < $c['colspan']; $k++) {
19663								$table['wc'][($j + $k)]['wpercent'] = $c['wpercent'] / $c['colspan'];
19664							}
19665						} else {
19666							if (isset($table['w']) && $table['w']) {
19667								$c['w'] = $c['wpercent'] / 100 * ($table['w'] - $tblbw );
19668							}
19669							$wc['wpercent'] = $c['wpercent'];
19670						}
19671					}
19672
19673					if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {
19674						if (isset($c['w']) && ($c['w'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
19675							$c['w'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;
19676						}
19677					}
19678
19679
19680					if (isset($c['w'])) { // If cell width is specified
19681						if ($miw < $c['w']) {
19682							$c['miw'] = $c['w'];
19683						} // Cell min width = that specified
19684						if ($miw > $c['w']) {
19685							$c['miw'] = $c['w'] = $miw;
19686						} // If width specified is less than minimum allowed (W) increase it
19687						// mPDF 5.7.4  Do not set column width in colspan
19688						// cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug
19689						if (!isset($c['colspan'])) {
19690							if (!isset($wc['w'])) {
19691								$wc['w'] = 1;
19692							}  // If the Col width is not specified = set it to 1
19693						}
19694						// mPDF 5.7.3  cf. http://www.mpdf1.com/forum/discussion/1648/nested-table-bug-
19695						$c['maw'] = $c['w'];
19696					} else {
19697						$c['miw'] = $miw;
19698					} // If cell width not specified -> set Cell min width it to minimum allowed (W)
19699
19700					if (isset($c['miw']) && $c['maw'] < $c['miw']) {
19701						$c['maw'] = $c['miw'];
19702					} // If Cell max width < Minwidth - increase it to =
19703					if (!isset($c['colspan'])) {
19704						if (isset($c['miw']) && $wc['miw'] < $c['miw']) {
19705							$wc['miw'] = $c['miw'];
19706						} // Update Col Minimum and maximum widths
19707						if ($wc['maw'] < $c['maw']) {
19708							$wc['maw'] = $c['maw'];
19709						}
19710						if ((isset($wc['absmiw']) && $wc['absmiw'] < $c['absmiw']) || !isset($wc['absmiw'])) {
19711							$wc['absmiw'] = $c['absmiw'];
19712						} // Update Col Minimum and maximum widths
19713
19714						if (isset($table['l'][$j])) {
19715							$table['l'][$j] += $c['s'];
19716						} else {
19717							$table['l'][$j] = $c['s'];
19718						}
19719					} else {
19720						$listspan[] = [$i, $j];
19721					}
19722
19723					// Check if minimum width of the whole column is big enough for largest word to fit
19724					// mPDF 6
19725					if (isset($c['textbuffer'])) {
19726						if (isset($table['overflow']) && $table['overflow'] == 'wrap') {
19727							$letter = true;
19728						} // check for maximum width of letters
19729						else {
19730							$letter = false;
19731						}
19732						$minwidth = $this->TableCheckMinWidth($wc['miw'] - $extrcw, 0, $c['textbuffer'], $letter);
19733					} else {
19734						$minwidth = 0;
19735					}
19736					if ($minwidth < 0) {
19737						// increase minimum width
19738						if (!isset($c['colspan'])) {
19739							$wc['miw'] = max((isset($wc['miw']) ? $wc['miw'] : 0), ((-$minwidth) + $extrcw));
19740						} else {
19741							$c['miw'] = max((isset($c['miw']) ? $c['miw'] : 0), ((-$minwidth) + $extrcw));
19742						}
19743					}
19744					if (!isset($c['colspan'])) {
19745						if ($wc['miw'] > $wc['maw']) {
19746							$wc['maw'] = $wc['miw'];
19747						} // update maximum width, if needed
19748					}
19749				}
19750				unset($c);
19751			}//rows
19752		}//columns
19753		// COLUMN SPANS
19754		$wc = &$table['wc'];
19755		foreach ($listspan as $span) {
19756			list($i, $j) = $span;
19757			$c = &$cs[$i][$j];
19758			$lc = $j + $c['colspan'];
19759			if ($lc > $nc) {
19760				$lc = $nc;
19761			}
19762			$wis = $wisa = 0;
19763			$was = $wasa = 0;
19764			$list = [];
19765			for ($k = $j; $k < $lc; $k++) {
19766				if (isset($table['l'][$k])) {
19767					if ($c['R']) {
19768						$table['l'][$k] += $c['miw'] / $c['colspan'];
19769					} else {
19770						$table['l'][$k] += $c['s'] / $c['colspan'];
19771					}
19772				} else {
19773					if ($c['R']) {
19774						$table['l'][$k] = $c['miw'] / $c['colspan'];
19775					} else {
19776						$table['l'][$k] = $c['s'] / $c['colspan'];
19777					}
19778				}
19779				$wis += $wc[$k]['miw'];   // $wis is the sum of the column miw in the colspan
19780				$was += $wc[$k]['maw'];   // $was is the sum of the column maw in the colspan
19781				if (!isset($c['w'])) {
19782					$list[] = $k;
19783					$wisa += $wc[$k]['miw']; // $wisa is the sum of the column miw in cells with no width specified in the colspan
19784					$wasa += $wc[$k]['maw']; // $wasa is the sum of the column maw in cells with no width specified in the colspan
19785				}
19786			}
19787			if ($c['miw'] > $wis) {
19788				if (!$wis) {
19789					for ($k = $j; $k < $lc; $k++) {
19790						$wc[$k]['miw'] = $c['miw'] / $c['colspan'];
19791					}
19792				} elseif (!count($list) && $wis != 0) {
19793					$wi = $c['miw'] - $wis;
19794					for ($k = $j; $k < $lc; $k++) {
19795						$wc[$k]['miw'] += ($wc[$k]['miw'] / $wis) * $wi;
19796					}
19797				} else {
19798					$wi = $c['miw'] - $wis;
19799					// mPDF 5.7.2   Extra min width distributed proportionately to all cells in colspan without a specified width
19800					// cf. http://www.mpdf1.com/forum/discussion/1607#Item_4
19801					foreach ($list as $k) {
19802						if (!isset($wc[$k]['w']) || !$wc[$k]['w']) {
19803							$wc[$k]['miw'] += ($wc[$k]['miw'] / $wisa) * $wi;
19804						}
19805					} // mPDF 5.7.2
19806				}
19807			}
19808			if ($c['maw'] > $was) {
19809				if (!$wis) {
19810					for ($k = $j; $k < $lc; $k++) {
19811						$wc[$k]['maw'] = $c['maw'] / $c['colspan'];
19812					}
19813				} elseif (!count($list) && $was != 0) {
19814					$wi = $c['maw'] - $was;
19815					for ($k = $j; $k < $lc; $k++) {
19816						$wc[$k]['maw'] += ($wc[$k]['maw'] / $was) * $wi;
19817					}
19818				} else {
19819					$wi = $c['maw'] - $was;
19820					// mPDF 5.7.4  Extra max width distributed evenly to all cells in colspan without a specified width
19821					// cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug
19822					foreach ($list as $k) {
19823						$wc[$k]['maw'] += $wi / count($list);
19824					}
19825				}
19826			}
19827			unset($c);
19828		}
19829
19830		$checkminwidth = 0;
19831		$checkmaxwidth = 0;
19832		$totallength = 0;
19833
19834		for ($i = 0; $i < $nc; $i++) {
19835			$checkminwidth += $table['wc'][$i]['miw'];
19836			$checkmaxwidth += $table['wc'][$i]['maw'];
19837			$totallength += isset($table['l']) ? $table['l'][$i] : 0;
19838		}
19839
19840		if (!isset($table['w']) && $firstpass) {
19841			$sumpc = 0;
19842			$notset = 0;
19843			for ($i = 0; $i < $nc; $i++) {
19844				if (isset($table['wc'][$i]['wpercent']) && $table['wc'][$i]['wpercent']) {
19845					$sumpc += $table['wc'][$i]['wpercent'];
19846				} else {
19847					$notset++;
19848				}
19849			}
19850
19851			// If sum of widths as %  >= 100% and not all columns are set
19852			// Set a nominal width of 1% for unset columns
19853			if ($sumpc >= 100 && $notset) {
19854				for ($i = 0; $i < $nc; $i++) {
19855					if ((!isset($table['wc'][$i]['wpercent']) || !$table['wc'][$i]['wpercent']) &&
19856						(!isset($table['wc'][$i]['w']) || !$table['wc'][$i]['w'])) {
19857						$table['wc'][$i]['wpercent'] = 1;
19858					}
19859				}
19860			}
19861
19862
19863			if ($sumpc) { // if any percents are set
19864				$sumnonpc = (100 - $sumpc);
19865				$sumpc = max($sumpc, 100);
19866				$miwleft = 0;
19867				$miwleftcount = 0;
19868				$miwsurplusnonpc = 0;
19869				$maxcalcmiw = 0;
19870				$mawleft = 0;
19871				$mawleftcount = 0;
19872				$mawsurplusnonpc = 0;
19873				$maxcalcmaw = 0;
19874				$mawnon = 0;
19875				$miwnon = 0;
19876				for ($i = 0; $i < $nc; $i++) {
19877					if (isset($table['wc'][$i]['wpercent'])) {
19878						$maxcalcmiw = max($maxcalcmiw, ($table['wc'][$i]['miw'] * $sumpc / $table['wc'][$i]['wpercent']));
19879						$maxcalcmaw = max($maxcalcmaw, ($table['wc'][$i]['maw'] * $sumpc / $table['wc'][$i]['wpercent']));
19880					} else {
19881						$miwleft += $table['wc'][$i]['miw'];
19882						$mawleft += $table['wc'][$i]['maw'];
19883						if (!isset($table['wc'][$i]['w'])) {
19884							$miwleftcount++;
19885							$mawleftcount++;
19886						}
19887					}
19888				}
19889				if ($miwleft && $sumnonpc > 0) {
19890					$miwnon = $miwleft * 100 / $sumnonpc;
19891				}
19892				if ($mawleft && $sumnonpc > 0) {
19893					$mawnon = $mawleft * 100 / $sumnonpc;
19894				}
19895				if (($miwnon > $checkminwidth || $maxcalcmiw > $checkminwidth) && $this->keep_table_proportions) {
19896					if ($miwnon > $maxcalcmiw) {
19897						$miwsurplusnonpc = round((($miwnon * $sumnonpc / 100) - $miwleft), 3);
19898						$checkminwidth = $miwnon;
19899					} else {
19900						$checkminwidth = $maxcalcmiw;
19901					}
19902					for ($i = 0; $i < $nc; $i++) {
19903						if (isset($table['wc'][$i]['wpercent'])) {
19904							$newmiw = $checkminwidth * $table['wc'][$i]['wpercent'] / 100;
19905							if ($table['wc'][$i]['miw'] < $newmiw) {
19906								$table['wc'][$i]['miw'] = $newmiw;
19907							}
19908							$table['wc'][$i]['w'] = 1;
19909						} elseif ($miwsurplusnonpc && !$table['wc'][$i]['w']) {
19910							$table['wc'][$i]['miw'] += $miwsurplusnonpc / $miwleftcount;
19911						}
19912					}
19913				}
19914				if (($mawnon > $checkmaxwidth || $maxcalcmaw > $checkmaxwidth)) {
19915					if ($mawnon > $maxcalcmaw) {
19916						$mawsurplusnonpc = round((($mawnon * $sumnonpc / 100) - $mawleft), 3);
19917						$checkmaxwidth = $mawnon;
19918					} else {
19919						$checkmaxwidth = $maxcalcmaw;
19920					}
19921					for ($i = 0; $i < $nc; $i++) {
19922						if (isset($table['wc'][$i]['wpercent'])) {
19923							$newmaw = $checkmaxwidth * $table['wc'][$i]['wpercent'] / 100;
19924							if ($table['wc'][$i]['maw'] < $newmaw) {
19925								$table['wc'][$i]['maw'] = $newmaw;
19926							}
19927							$table['wc'][$i]['w'] = 1;
19928						} elseif ($mawsurplusnonpc && !$table['wc'][$i]['w']) {
19929							$table['wc'][$i]['maw'] += $mawsurplusnonpc / $mawleftcount;
19930						}
19931						if ($table['wc'][$i]['maw'] < $table['wc'][$i]['miw']) {
19932							$table['wc'][$i]['maw'] = $table['wc'][$i]['miw'];
19933						}
19934					}
19935				}
19936				if ($checkminwidth > $checkmaxwidth) {
19937					$checkmaxwidth = $checkminwidth;
19938				}
19939			}
19940		}
19941
19942		if (isset($table['wpercent']) && $table['wpercent']) {
19943			$checkminwidth *= (100 / $table['wpercent']);
19944			$checkmaxwidth *= (100 / $table['wpercent']);
19945		}
19946
19947
19948		$checkminwidth += $tblbw;
19949		$checkmaxwidth += $tblbw;
19950
19951		// Table['miw'] set by percent in first pass may be larger than sum of column miw
19952		if ((isset($table['miw']) && $checkminwidth > $table['miw']) || !isset($table['miw'])) {
19953			$table['miw'] = $checkminwidth;
19954		}
19955		if ((isset($table['maw']) && $checkmaxwidth > $table['maw']) || !isset($table['maw'])) {
19956			$table['maw'] = $checkmaxwidth;
19957		}
19958		$table['tl'] = $totallength;
19959
19960		// mPDF 6
19961		if ($this->table_rotate) {
19962			$mxw = $this->tbrot_maxw;
19963		} else {
19964			$mxw = $this->blk[$this->blklvl]['inner_width'];
19965		}
19966
19967		if (!isset($table['overflow'])) {
19968			$table['overflow'] = null;
19969		}
19970
19971		if ($table['overflow'] == 'visible') {
19972			return [0, 0];
19973		} elseif ($table['overflow'] == 'hidden' && !$this->table_rotate && !$this->ColActive && $checkminwidth > $mxw) {
19974			$table['w'] = $table['miw'];
19975			return [0, 0];
19976		}
19977		// elseif ($table['overflow']=='wrap') { return array(0,0); }	// mPDF 6
19978
19979		if (isset($table['w']) && $table['w']) {
19980
19981			if ($table['w'] >= $checkminwidth && $table['w'] <= $mxw) {
19982				$table['maw'] = $mxw = $table['w'];
19983			} elseif ($table['w'] >= $checkminwidth && $table['w'] > $mxw && $this->keep_table_proportions) {
19984				$checkminwidth = $table['w'];
19985			} elseif ($table['w'] < $checkminwidth && $checkminwidth < $mxw && $this->keep_table_proportions) {
19986				$table['maw'] = $table['w'] = $checkminwidth;
19987			} else {
19988				unset($table['w']);
19989			}
19990		}
19991
19992		$ratio = $checkminwidth / $mxw;
19993
19994		if ($checkminwidth > $mxw) {
19995			return [($ratio + 0.001), $checkminwidth]; // 0.001 to allow for rounded numbers when resizing
19996		}
19997
19998		unset($cs);
19999
20000		return [0, 0];
20001	}
20002
20003	function _tableWidth(&$table)
20004	{
20005		$widthcols = &$table['wc'];
20006		$numcols = $table['nc'];
20007		$tablewidth = 0;
20008
20009		if ($table['borders_separate']) {
20010			$tblbw = $table['border_details']['L']['w'] + $table['border_details']['R']['w'] + $table['margin']['L'] + $table['margin']['R'] + $table['padding']['L'] + $table['padding']['R'] + $table['border_spacing_H'];
20011		} else {
20012			$tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];
20013		}
20014
20015		if ($table['level'] > 1 && isset($table['w'])) {
20016
20017			if (isset($table['wpercent']) && $table['wpercent']) {
20018				$table['w'] = $temppgwidth = (($table['w'] - $tblbw) * $table['wpercent'] / 100) + $tblbw;
20019			} else {
20020				$temppgwidth = $table['w'];
20021			}
20022
20023		} elseif ($this->table_rotate) {
20024
20025			$temppgwidth = $this->tbrot_maxw;
20026
20027			// If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin) then allow for this
20028			$enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];
20029
20030			if ($enddiv / $temppgwidth < 0.05) {
20031				$temppgwidth -= $enddiv;
20032			}
20033
20034		} else {
20035
20036			if (isset($table['w']) && $table['w'] < $this->blk[$this->blklvl]['inner_width']) {
20037				$notfullwidth = 1;
20038				$temppgwidth = $table['w'];
20039			} elseif ($table['overflow'] == 'visible' && $table['level'] == 1) {
20040				$temppgwidth = null;
20041			} elseif ($table['overflow'] == 'hidden' && !$this->ColActive && isset($table['w']) && $table['w'] > $this->blk[$this->blklvl]['inner_width'] && $table['w'] == $table) {
20042				// $temppgwidth = $this->blk[$this->blklvl]['inner_width'];
20043				$temppgwidth = $table['w'];
20044			} else {
20045				$temppgwidth = $this->blk[$this->blklvl]['inner_width'];
20046			}
20047
20048		}
20049
20050		$totaltextlength = 0; // Added - to sum $table['l'][colno]
20051		$totalatextlength = 0; // Added - to sum $table['l'][colno] for those columns where width not set
20052		$percentages_set = 0;
20053
20054		for ($i = 0; $i < $numcols; $i++) {
20055			if (isset($widthcols[$i]['wpercent'])) {
20056				$tablewidth += $widthcols[$i]['maw'];
20057				$percentages_set = 1;
20058			} elseif (isset($widthcols[$i]['w'])) {
20059				$tablewidth += $widthcols[$i]['miw'];
20060			} else {
20061				$tablewidth += $widthcols[$i]['maw'];
20062			}
20063			$totaltextlength += isset($table['l']) ? $table['l'][$i] : 0;
20064		}
20065
20066		if (!$totaltextlength) {
20067			$totaltextlength = 1;
20068		}
20069
20070		$tablewidth += $tblbw; // Outer half of table borders
20071
20072		if ($tablewidth > $temppgwidth) {
20073			$table['w'] = $temppgwidth;
20074		} elseif ($tablewidth < $temppgwidth && !isset($table['w']) && $percentages_set) { // if any widths set as percentages and max width fits < page width
20075			$table['w'] = $table['maw'];
20076		}
20077
20078		// if table width is set and is > allowed width
20079		if (isset($table['w']) && $table['w'] > $temppgwidth) {
20080			$table['w'] = $temppgwidth;
20081		}
20082
20083		// IF the table width is now set - Need to distribute columns widths
20084		// mPDF 5.7.3
20085		// If the table width is already set to the maximum width (e.g. nested table), then use maximum column widths exactly
20086		if (isset($table['w']) && ($table['w'] == $tablewidth) && !$percentages_set) {
20087
20088			// This sets the columns all to maximum width
20089			for ($i = 0; $i < $numcols; $i++) {
20090				$widthcols[$i] = $widthcols[$i]['maw'];
20091			}
20092
20093		} elseif (isset($table['w'])) { // elseif the table width is set distribute width using algorithm
20094
20095			$wis = $wisa = 0;
20096			$list = [];
20097			$notsetlist = [];
20098
20099			for ($i = 0; $i < $numcols; $i++) {
20100				$wis += $widthcols[$i]['miw'];
20101				if (!isset($widthcols[$i]['w']) || ($widthcols[$i]['w'] && $table['w'] > $temppgwidth && !$this->keep_table_proportions && !$notfullwidth )) {
20102					$list[] = $i;
20103					$wisa += $widthcols[$i]['miw'];
20104					$totalatextlength += $table['l'][$i];
20105				}
20106			}
20107
20108			if (!$totalatextlength) {
20109				$totalatextlength = 1;
20110			}
20111
20112			// Allocate spare (more than col's minimum width) across the cols according to their approx total text length
20113			// Do it by setting minimum width here
20114			if ($table['w'] > $wis + $tblbw) {
20115
20116				// First set any cell widths set as percentages
20117				if ($table['w'] < $temppgwidth || $this->keep_table_proportions) {
20118					for ($k = 0; $k < $numcols; $k++) {
20119						if (isset($widthcols[$k]['wpercent'])) {
20120							$curr = $widthcols[$k]['miw'];
20121							$widthcols[$k]['miw'] = ($table['w'] - $tblbw) * $widthcols[$k]['wpercent'] / 100;
20122							$wis += $widthcols[$k]['miw'] - $curr;
20123							$wisa += $widthcols[$k]['miw'] - $curr;
20124						}
20125					}
20126				}
20127
20128				// Now allocate surplus up to maximum width of each column
20129				$surplus = 0;
20130				$ttl = 0; // number of surplus columns
20131
20132				if (!count($list)) {
20133
20134					$wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute
20135
20136					for ($k = 0; $k < $numcols; $k++) {
20137
20138						$spareratio = ($table['l'][$k] / $totaltextlength); //  gives ratio to divide up free space
20139
20140						// Don't allocate more than Maximum required width - save rest in surplus
20141						if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3
20142							$surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);
20143							$widthcols[$k]['miw'] = $widthcols[$k]['maw'];
20144						} else {
20145							$notsetlist[] = $k;
20146							$ttl += $table['l'][$k];
20147							$widthcols[$k]['miw'] += ($wi * $spareratio);
20148						}
20149					}
20150
20151				} else {
20152
20153					$wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute
20154
20155					foreach ($list as $k) {
20156
20157						$spareratio = ($table['l'][$k] / $totalatextlength); //  gives ratio to divide up free space
20158
20159						// Don't allocate more than Maximum required width - save rest in surplus
20160						if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3
20161							$surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);
20162							$widthcols[$k]['miw'] = $widthcols[$k]['maw'];
20163						} else {
20164							$notsetlist[] = $k;
20165							$ttl += $table['l'][$k];
20166							$widthcols[$k]['miw'] += ($wi * $spareratio);
20167						}
20168					}
20169				}
20170
20171				// If surplus still left over apportion it across columns
20172				if ($surplus) {
20173
20174					if (count($notsetlist) && count($notsetlist) < $numcols) { // if some are set only add to remaining - otherwise add to all of them
20175						foreach ($notsetlist as $i) {
20176							if ($ttl) {
20177								$widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;
20178							}
20179						}
20180					} elseif (count($list) && count($list) < $numcols) { // If some widths are defined, and others have been added up to their maxmum
20181						foreach ($list as $i) {
20182							$widthcols[$i]['miw'] += $surplus / count($list);
20183						}
20184					} elseif ($numcols) { // If all columns
20185						$ttl = array_sum($table['l']);
20186						if ($ttl) {
20187							for ($i = 0; $i < $numcols; $i++) {
20188								$widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;
20189							}
20190						}
20191					}
20192				}
20193			}
20194
20195			// This sets the columns all to minimum width (which has been increased above if appropriate)
20196			for ($i = 0; $i < $numcols; $i++) {
20197				$widthcols[$i] = $widthcols[$i]['miw'];
20198			}
20199
20200			// TABLE NOT WIDE ENOUGH EVEN FOR MINIMUM CONTENT WIDTH
20201			// If sum of column widths set are too wide for table
20202			$checktablewidth = 0;
20203			for ($i = 0; $i < $numcols; $i++) {
20204				$checktablewidth += $widthcols[$i];
20205			}
20206
20207			if ($checktablewidth > ($temppgwidth + 0.001 - $tblbw)) {
20208
20209				$usedup = 0;
20210				$numleft = 0;
20211
20212				for ($i = 0; $i < $numcols; $i++) {
20213					if ((isset($widthcols[$i]) && $widthcols[$i] > (($temppgwidth - $tblbw) / $numcols)) && (!isset($widthcols[$i]['w']))) {
20214						$numleft++;
20215						unset($widthcols[$i]);
20216					} else {
20217						$usedup += $widthcols[$i];
20218					}
20219				}
20220
20221				for ($i = 0; $i < $numcols; $i++) {
20222					if (!isset($widthcols[$i]) || !$widthcols[$i]) {
20223						$widthcols[$i] = ((($temppgwidth - $tblbw) - $usedup) / ($numleft));
20224					}
20225				}
20226			}
20227
20228		} else { // table has no width defined
20229
20230			$table['w'] = $tablewidth;
20231
20232			for ($i = 0; $i < $numcols; $i++) {
20233
20234				if (isset($widthcols[$i]['wpercent']) && $this->keep_table_proportions) {
20235					$colwidth = $widthcols[$i]['maw'];
20236				} elseif (isset($widthcols[$i]['w'])) {
20237					$colwidth = $widthcols[$i]['miw'];
20238				} else {
20239					$colwidth = $widthcols[$i]['maw'];
20240				}
20241
20242				unset($widthcols[$i]);
20243				$widthcols[$i] = $colwidth;
20244
20245			}
20246		}
20247
20248		if ($table['overflow'] === 'visible' && $table['level'] == 1) {
20249
20250			if ($tablewidth > $this->blk[$this->blklvl]['inner_width']) {
20251
20252				for ($j = 0; $j < $numcols; $j++) { // columns
20253
20254					for ($i = 0; $i < $table['nr']; $i++) { // rows
20255
20256						if (isset($table['cells'][$i][$j]) && $table['cells'][$i][$j]) {
20257
20258							$colspan = (isset($table['cells'][$i][$j]['colspan']) ? $table['cells'][$i][$j]['colspan'] : 1);
20259
20260							if ($colspan > 1) {
20261								$w = 0;
20262
20263								for ($c = $j; $c < ($j + $colspan); $c++) {
20264									$w += $widthcols[$c];
20265								}
20266
20267								if ($w > $this->blk[$this->blklvl]['inner_width']) {
20268									$diff = $w - ($this->blk[$this->blklvl]['inner_width'] - $tblbw);
20269									for ($c = $j; $c < ($j + $colspan); $c++) {
20270										$widthcols[$c] -= $diff * ($widthcols[$c] / $w);
20271									}
20272									$table['w'] -= $diff;
20273									$table['csp'][$j] = $w - $diff;
20274								}
20275							}
20276						}
20277					}
20278				}
20279			}
20280
20281			$pgNo = 0;
20282			$currWc = 0;
20283
20284			for ($i = 0; $i < $numcols; $i++) { // columns
20285
20286				if (isset($table['csp'][$i])) {
20287					$w = $table['csp'][$i];
20288					unset($table['csp'][$i]);
20289				} else {
20290					$w = $widthcols[$i];
20291				}
20292
20293				if (($currWc + $w + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
20294					$pgNo++;
20295					$currWc = $widthcols[$i];
20296				} else {
20297					$currWc += $widthcols[$i];
20298				}
20299
20300				$table['colPg'][$i] = $pgNo;
20301			}
20302		}
20303	}
20304
20305	function _tableHeight(&$table)
20306	{
20307		$level = $table['level'];
20308		$levelid = $table['levelid'];
20309		$cells = &$table['cells'];
20310		$numcols = $table['nc'];
20311		$numrows = $table['nr'];
20312		$listspan = [];
20313		$checkmaxheight = 0;
20314		$headerrowheight = 0;
20315		$checkmaxheightplus = 0;
20316		$headerrowheightplus = 0;
20317		$firstrowheight = 0;
20318
20319		$footerrowheight = 0;
20320		$footerrowheightplus = 0;
20321		if ($this->table_rotate) {
20322			$temppgheight = $this->tbrot_maxh;
20323			$remainingpage = $this->tbrot_maxh;
20324		} else {
20325			$temppgheight = ($this->h - $this->bMargin - $this->tMargin) - $this->kwt_height;
20326			$remainingpage = ($this->h - $this->bMargin - $this->y) - $this->kwt_height;
20327
20328			// If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin)
20329			// then allow for this
20330			$enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $table['margin']['B'];
20331			if ($remainingpage > $enddiv && $enddiv / $remainingpage < 0.05) {
20332				$remainingpage -= $enddiv;
20333			} elseif ($remainingpage == 0) {
20334				$remainingpage = 0.001;
20335			}
20336			if ($temppgheight > $enddiv && $enddiv / $temppgheight < 0.05) {
20337				$temppgheight -= $enddiv;
20338			} elseif ($temppgheight == 0) {
20339				$temppgheight = 0.001;
20340			}
20341		}
20342		if ($remainingpage < 0) {
20343			$remainingpage = 0.001;
20344		}
20345		if ($temppgheight < 0) {
20346			$temppgheight = 0.001;
20347		}
20348
20349		for ($i = 0; $i < $numrows; $i++) { // rows
20350			$heightrow = &$table['hr'][$i];
20351			for ($j = 0; $j < $numcols; $j++) { // columns
20352				if (isset($cells[$i][$j]) && $cells[$i][$j]) {
20353					$c = &$cells[$i][$j];
20354
20355					if ($this->simpleTables) {
20356						if ($table['borders_separate']) { // NB twice border width
20357							$extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) + ($c['padding']['L'] + $c['padding']['R']) + $table['border_spacing_H'];
20358							$extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) + ($c['padding']['T'] + $c['padding']['B']) + $table['border_spacing_V'];
20359						} else {
20360							$extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + ($c['padding']['L'] + $c['padding']['R']);
20361							$extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) / 2 + ($c['padding']['T'] + $c['padding']['B']);
20362						}
20363					} else {
20364						if ($this->packTableData) {
20365							list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
20366						} else {
20367							$bt = $c['border_details']['T']['w'];
20368							$bb = $c['border_details']['B']['w'];
20369							$br = $c['border_details']['R']['w'];
20370							$bl = $c['border_details']['L']['w'];
20371						}
20372						if ($table['borders_separate']) { // NB twice border width
20373							$extraWLR = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
20374							$extrh = $bt + $bb + $c['padding']['T'] + $c['padding']['B'] + $table['border_spacing_V'];
20375						} else {
20376							$extraWLR = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];
20377							$extrh = $bt / 2 + $bb / 2 + $c['padding']['T'] + $c['padding']['B'];
20378						}
20379					}
20380
20381					if ($table['overflow'] == 'visible' && $level == 1) {
20382						list($x, $cw) = $this->_splitTableGetWidth($table, $i, $j);
20383					} else {
20384						list($x, $cw) = $this->_tableGetWidth($table, $i, $j);
20385					}
20386
20387
20388					// Get CELL HEIGHT
20389					// ++ extra parameter forces wrap to break word
20390					if ($c['R'] && isset($c['textbuffer'])) {
20391						$str = '';
20392						foreach ($c['textbuffer'] as $t) {
20393							$str .= $t[0] . ' ';
20394						}
20395						$str = rtrim($str);
20396						$s_fs = $this->FontSizePt;
20397						$s_f = $this->FontFamily;
20398						$s_st = $this->FontStyle;
20399						$this->SetFont($c['textbuffer'][0][4], $c['textbuffer'][0][2], $c['textbuffer'][0][11] / $this->shrin_k, true, true);
20400						$tempch = $this->GetStringWidth($str, true, $c['textbuffer'][0][18], $c['textbuffer'][0][8]);
20401						if ($c['R'] >= 45 && $c['R'] < 90) {
20402							$tempch = ((sin(deg2rad($c['R']))) * $tempch ) + ((sin(deg2rad($c['R']))) * (($c['textbuffer'][0][11] / Mpdf::SCALE) / $this->shrin_k));
20403						}
20404						$this->SetFont($s_f, $s_st, $s_fs, true, true);
20405						$ch = ($tempch ) + $extrh;
20406					} else {
20407						if (isset($c['textbuffer']) && !empty($c['textbuffer'])) {
20408							$this->cellLineHeight = $c['cellLineHeight'];
20409							$this->cellLineStackingStrategy = $c['cellLineStackingStrategy'];
20410							$this->cellLineStackingShift = $c['cellLineStackingShift'];
20411							$this->divwidth = $cw - $extraWLR;
20412							$tempch = $this->printbuffer($c['textbuffer'], '', true, true);
20413						} else {
20414							$tempch = 0;
20415						}
20416
20417						// Added cellpadding top and bottom. (Lineheight already adjusted)
20418						$ch = $tempch + $extrh;
20419					}
20420					// If height is defined and it is bigger than calculated $ch then update values
20421					if (isset($c['h']) && $c['h'] > $ch) {
20422						$c['mih'] = $ch; // in order to keep valign working
20423						$ch = $c['h'];
20424					} else {
20425						$c['mih'] = $ch;
20426					}
20427					if (isset($c['rowspan'])) {
20428						$listspan[] = [$i, $j];
20429					} elseif ($heightrow < $ch) {
20430						$heightrow = $ch;
20431					}
20432
20433					// this is the extra used in _tableWrite to determine whether to trigger a page change
20434					if ($table['borders_separate']) {
20435						if ($i == ($numrows - 1) || (isset($c['rowspan']) && ($i + $c['rowspan']) == ($numrows))) {
20436							$extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
20437						} else {
20438							$extra = $table['border_spacing_V'] / 2;
20439						}
20440					} else {
20441						if (!$this->simpleTables) {
20442							$extra = $bb / 2;
20443						} elseif ($this->simpleTables) {
20444							$extra = $table['simple']['border_details']['B']['w'] / 2;
20445						}
20446					}
20447					if (isset($table['is_thead'][$i]) && $table['is_thead'][$i]) {
20448						if ($j == 0) {
20449							$headerrowheight += $ch;
20450							$headerrowheightplus += $ch + $extra;
20451						}
20452					} elseif (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {
20453						if ($j == 0) {
20454							$footerrowheight += $ch;
20455							$footerrowheightplus += $ch + $extra;
20456						}
20457					} else {
20458						$checkmaxheight = max($checkmaxheight, $ch);
20459						$checkmaxheightplus = max($checkmaxheightplus, $ch + $extra);
20460					}
20461					if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {
20462						$firstrowheight = max($ch, $firstrowheight);
20463					}
20464					unset($c);
20465				}
20466			}//end of columns
20467		}//end of rows
20468
20469		$heightrow = &$table['hr'];
20470		foreach ($listspan as $span) {
20471			list($i, $j) = $span;
20472			$c = &$cells[$i][$j];
20473			$lr = $i + $c['rowspan'];
20474			if ($lr > $numrows) {
20475				$lr = $numrows;
20476			}
20477			$hs = $hsa = 0;
20478			$list = [];
20479			for ($k = $i; $k < $lr; $k++) {
20480				$hs += $heightrow[$k];
20481				// mPDF 6
20482				$sh = false; // specified height
20483				for ($m = 0; $m < $numcols; $m++) { // columns
20484					$tc = &$cells[$k][$m];
20485					if (isset($tc['rowspan'])) {
20486						continue;
20487					}
20488					if (isset($tc['h'])) {
20489						$sh = true;
20490						break;
20491					}
20492				}
20493				if (!$sh) {
20494					$list[] = $k;
20495				}
20496			}
20497
20498			if ($table['borders_separate']) {
20499				if ($i == ($numrows - 1) || ($i + $c['rowspan']) == ($numrows)) {
20500					$extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
20501				} else {
20502					$extra = $table['border_spacing_V'] / 2;
20503				}
20504			} else {
20505				if (!$this->simpleTables) {
20506					if ($this->packTableData) {
20507						list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
20508					} else {
20509						$bb = $c['border_details']['B']['w'];
20510					}
20511					$extra = $bb / 2;
20512				} elseif ($this->simpleTables) {
20513					$extra = $table['simple']['border_details']['B']['w'] / 2;
20514				}
20515			}
20516			if (!empty($table['is_thead'][$i])) {
20517				$headerrowheight = max($headerrowheight, $hs);
20518				$headerrowheightplus = max($headerrowheightplus, $hs + $extra);
20519			} elseif (!empty($table['is_tfoot'][$i])) {
20520				$footerrowheight = max($footerrowheight, $hs);
20521				$footerrowheightplus = max($footerrowheightplus, $hs + $extra);
20522			} else {
20523				$checkmaxheight = max($checkmaxheight, $hs);
20524				$checkmaxheightplus = max($checkmaxheightplus, $hs + $extra);
20525			}
20526			if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {
20527				$firstrowheight = max($hs, $firstrowheight);
20528			}
20529
20530			if ($c['mih'] > $hs) {
20531				if (!$hs) {
20532					for ($k = $i; $k < $lr; $k++) {
20533						$heightrow[$k] = $c['mih'] / $c['rowspan'];
20534					}
20535				} elseif (!count($list)) { // no rows in the rowspan have a height specified, so share amongst all rows equally
20536					$hi = $c['mih'] - $hs;
20537					for ($k = $i; $k < $lr; $k++) {
20538						$heightrow[$k] += ($heightrow[$k] / $hs) * $hi;
20539					}
20540				} else {
20541					$hi = $c['mih'] - $hs; // mPDF 6
20542					foreach ($list as $k) {
20543						$heightrow[$k] += $hi / (count($list)); // mPDF 6
20544					}
20545				}
20546			}
20547			unset($c);
20548
20549			// If rowspans overlap so that one or more rows do not have a height set...
20550			// i.e. for one or more rows, the only cells (explicit) in that row have rowspan>1
20551			// so heightrow is still == 0
20552			if ($heightrow[$i] == 0) {
20553				// Get row extent to analyse above and below
20554				$top = $i;
20555				foreach ($listspan as $checkspan) {
20556					list($cki, $ckj) = $checkspan;
20557					$c = &$cells[$cki][$ckj];
20558					if (isset($c['rowspan']) && $c['rowspan'] > 1) {
20559						if (($cki + $c['rowspan'] - 1) >= $i) {
20560							$top = min($top, $cki);
20561						}
20562					}
20563				}
20564				$bottom = $i + $c['rowspan'] - 1;
20565				// Check for overconstrained conditions
20566				for ($k = $top; $k <= $bottom; $k++) {
20567					// if ['hr'] for any of the others is also 0, then abort (too complicated)
20568					if ($k != $i && $heightrow[$k] == 0) {
20569						break(1);
20570					}
20571					// check again that top and bottom are not crossed by rowspans - or abort (too complicated)
20572					if ($k == $top) {
20573						// ???? take account of colspan as well???
20574						for ($m = 0; $m < $numcols; $m++) { // columns
20575							if (!isset($cells[$k][$m]) || $cells[$k][$m] == 0) {
20576								break(2);
20577							}
20578						}
20579					} elseif ($k == $bottom) {
20580						// ???? take account of colspan as well???
20581						for ($m = 0; $m < $numcols; $m++) { // columns
20582							$c = &$cells[$k][$m];
20583							if (isset($c['rowspan']) && $c['rowspan'] > 1) {
20584								break(2);
20585							}
20586						}
20587					}
20588				}
20589				// By columns add up col height using ['h'] if set or ['mih'] if not
20590				// Intentionally do not substract border-spacing
20591				$colH = [];
20592				$extH = 0;
20593				$newhr = [];
20594				for ($m = 0; $m < $numcols; $m++) { // columns
20595					for ($k = $top; $k <= $bottom; $k++) {
20596						if (isset($cells[$k][$m]) && $cells[$k][$m] != 0) {
20597							$c = &$cells[$k][$m];
20598							if (isset($c['h']) && $c['h']) {
20599								$useh = $c['h'];
20600							} // ???? take account of colspan as well???
20601							else {
20602								$useh = $c['mih'];
20603							}
20604							if (isset($colH[$m])) {
20605								$colH[$m] += $useh;
20606							} else {
20607								$colH[$m] = $useh;
20608							}
20609							if (!isset($c['rowspan']) || $c['rowspan'] < 2) {
20610								$newhr[$k] = max((isset($newhr[$k]) ? $newhr[$k] : 0), $useh);
20611							}
20612						}
20613					}
20614					$extH = max($extH, $colH[$m]); // mPDF 6
20615				}
20616				$newhr[$i] = $extH - array_sum($newhr);
20617				for ($k = $top; $k <= $bottom; $k++) {
20618					$heightrow[$k] = $newhr[$k];
20619				}
20620			}
20621
20622
20623			unset($c);
20624		}
20625
20626		$table['h'] = array_sum($heightrow);
20627		unset($heightrow);
20628
20629		if ($table['borders_separate']) {
20630			$table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['border_details']['T']['w'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] + $table['padding']['T'] + $table['padding']['B'];
20631		} else {
20632			$table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['max_cell_border_width']['T'] / 2 + $table['max_cell_border_width']['B'] / 2;
20633		}
20634
20635		$maxrowheight = $checkmaxheightplus + $headerrowheightplus + $footerrowheightplus;
20636		$maxfirstrowheight = $firstrowheight + $headerrowheightplus + $footerrowheightplus; // includes thead, 1st row and tfoot
20637		return [$table['h'], $maxrowheight, $temppgheight, $remainingpage, $maxfirstrowheight];
20638	}
20639
20640	function _tableGetWidth(&$table, $i, $j)
20641	{
20642		$cell = &$table['cells'][$i][$j];
20643		if ($cell) {
20644			if (isset($cell['x0'])) {
20645				return [$cell['x0'], $cell['w0']];
20646			}
20647			$x = 0;
20648			$widthcols = &$table['wc'];
20649			for ($k = 0; $k < $j; $k++) {
20650				$x += $widthcols[$k];
20651			}
20652			$w = $widthcols[$j];
20653			if (isset($cell['colspan'])) {
20654				for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) {
20655					$w += $widthcols[$k];
20656				}
20657			}
20658			$cell['x0'] = $x;
20659			$cell['w0'] = $w;
20660			return [$x, $w];
20661		}
20662		return [0, 0];
20663	}
20664
20665	function _splitTableGetWidth(&$table, $i, $j)
20666	{
20667		$cell = &$table['cells'][$i][$j];
20668		if ($cell) {
20669			if (isset($cell['x0'])) {
20670				return [$cell['x0'], $cell['w0']];
20671			}
20672			$x = 0;
20673			$widthcols = &$table['wc'];
20674			$pg = $table['colPg'][$j];
20675			for ($k = 0; $k < $j; $k++) {
20676				if ($table['colPg'][$k] == $pg) {
20677					$x += $widthcols[$k];
20678				}
20679			}
20680			$w = $widthcols[$j];
20681			if (isset($cell['colspan'])) {
20682				for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) {
20683					if ($table['colPg'][$k] == $pg) {
20684						$w += $widthcols[$k];
20685					}
20686				}
20687			}
20688			$cell['x0'] = $x;
20689			$cell['w0'] = $w;
20690			return [$x, $w];
20691		}
20692		return [0, 0];
20693	}
20694
20695	function _tableGetHeight(&$table, $i, $j)
20696	{
20697		$cell = &$table['cells'][$i][$j];
20698		if ($cell) {
20699			if (isset($cell['y0'])) {
20700				return [$cell['y0'], $cell['h0']];
20701			}
20702			$y = 0;
20703			$heightrow = &$table['hr'];
20704			for ($k = 0; $k < $i; $k++) {
20705				$y += $heightrow[$k];
20706			}
20707			$h = $heightrow[$i];
20708			if (isset($cell['rowspan'])) {
20709				for ($k = $i + $cell['rowspan'] - 1; $k > $i; $k--) {
20710					if (array_key_exists($k, $heightrow)) {
20711						$h += $heightrow[$k];
20712					} else {
20713						$this->logger->debug('Possible non-wellformed HTML markup in a table', ['context' => LogContext::HTML_MARKUP]);
20714					}
20715				}
20716			}
20717			$cell['y0'] = $y;
20718			$cell['h0'] = $h;
20719			return [$y, $h];
20720		}
20721		return [0, 0];
20722	}
20723
20724	function _tableGetMaxRowHeight($table, $row)
20725	{
20726		if ($row == $table['nc'] - 1) {
20727			return $table['hr'][$row];
20728		}
20729		$maxrowheight = $table['hr'][$row];
20730		for ($i = $row + 1; $i < $table['nr']; $i++) {
20731			$cellsset = 0;
20732			for ($j = 0; $j < $table['nc']; $j++) {
20733				if ($table['cells'][$i][$j]) {
20734					if (isset($table['cells'][$i][$j]['colspan'])) {
20735						$cellsset += $table['cells'][$i][$j]['colspan'];
20736					} else {
20737						$cellsset += 1;
20738					}
20739				}
20740			}
20741			if ($cellsset == $table['nc']) {
20742				return $maxrowheight;
20743			} else {
20744				$maxrowheight += $table['hr'][$i];
20745			}
20746		}
20747		return $maxrowheight;
20748	}
20749
20750	// CHANGED TO ALLOW TABLE BORDER TO BE SPECIFIED CORRECTLY - added border_details
20751	function _tableRect($x, $y, $w, $h, $bord = -1, $details = [], $buffer = false, $bSeparate = false, $cort = 'cell', $tablecorner = '', $bsv = 0, $bsh = 0)
20752	{
20753		$cellBorderOverlay = [];
20754
20755		if ($bord == -1) {
20756			$this->Rect($x, $y, $w, $h);
20757		} elseif ($this->simpleTables && ($cort == 'cell')) {
20758			$this->SetLineWidth($details['L']['w']);
20759			if ($details['L']['c']) {
20760				$this->SetDColor($details['L']['c']);
20761			} else {
20762				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
20763			}
20764			$this->SetLineJoin(0);
20765			$this->Rect($x, $y, $w, $h);
20766		} elseif ($bord) {
20767			if (!$bSeparate && $buffer) {
20768				$priority = 'LRTB';
20769				for ($p = 0; $p < strlen($priority); $p++) {
20770					$side = $priority[$p];
20771					$details['p'] = $side;
20772
20773					$dom = 0;
20774					if (isset($details[$side]['w'])) {
20775						$dom += ($details[$side]['w'] * 100000);
20776					}
20777					if (isset($details[$side]['style'])) {
20778						$dom += (array_search($details[$side]['style'], $this->borderstyles) * 100);
20779					}
20780					if (isset($details[$side]['dom'])) {
20781						$dom += ($details[$side]['dom'] * 10);
20782					}
20783
20784					// Precedence to darker colours at joins
20785					$coldom = 0;
20786					if (isset($details[$side]['c']) && is_array($details[$side]['c'])) {
20787						if ($details[$side]['c'][0] == 3) {  // RGB
20788							$coldom = 10 - (((ord($details[$side]['c'][1]) * 1.00) + (ord($details[$side]['c'][2]) * 1.00) + (ord($details[$side]['c'][3]) * 1.00)) / 76.5);
20789						}
20790					} // 10 black - 0 white
20791					if ($coldom) {
20792						$dom += $coldom;
20793					}
20794					// Lastly precedence to RIGHT and BOTTOM cells at joins
20795					if (isset($details['cellposdom'])) {
20796						$dom += $details['cellposdom'];
20797					}
20798
20799					$save = false;
20800					if ($side == 'T' && $this->issetBorder($bord, Border::TOP)) {
20801						$cbord = Border::TOP;
20802						$save = true;
20803					} elseif ($side == 'L' && $this->issetBorder($bord, Border::LEFT)) {
20804						$cbord = Border::LEFT;
20805						$save = true;
20806					} elseif ($side == 'R' && $this->issetBorder($bord, Border::RIGHT)) {
20807						$cbord = Border::RIGHT;
20808						$save = true;
20809					} elseif ($side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) {
20810						$cbord = Border::BOTTOM;
20811						$save = true;
20812					}
20813
20814					if ($save) {
20815						$this->cellBorderBuffer[] = pack("A16nCnda6A10d14", str_pad(sprintf("%08.7f", $dom), 16, "0", STR_PAD_LEFT), $cbord, ord($side), $details[$side]['s'], $details[$side]['w'], $details[$side]['c'], $details[$side]['style'], $x, $y, $w, $h, $details['mbw']['BL'], $details['mbw']['BR'], $details['mbw']['RT'], $details['mbw']['RB'], $details['mbw']['TL'], $details['mbw']['TR'], $details['mbw']['LT'], $details['mbw']['LB'], $details['cellposdom'], 0);
20816						if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset' || $details[$side]['style'] == 'double') {
20817							$details[$side]['overlay'] = true;
20818							$this->cellBorderBuffer[] = pack("A16nCnda6A10d14", str_pad(sprintf("%08.7f", ($dom + 4)), 16, "0", STR_PAD_LEFT), $cbord, ord($side), $details[$side]['s'], $details[$side]['w'], $details[$side]['c'], $details[$side]['style'], $x, $y, $w, $h, $details['mbw']['BL'], $details['mbw']['BR'], $details['mbw']['RT'], $details['mbw']['RB'], $details['mbw']['TL'], $details['mbw']['TR'], $details['mbw']['LT'], $details['mbw']['LB'], $details['cellposdom'], 1);
20819						}
20820					}
20821				}
20822				return;
20823			}
20824
20825			if (isset($details['p']) && strlen($details['p']) > 1) {
20826				$priority = $details['p'];
20827			} else {
20828				$priority = 'LTRB';
20829			}
20830			$Tw = 0;
20831			$Rw = 0;
20832			$Bw = 0;
20833			$Lw = 0;
20834			if (isset($details['T']['w'])) {
20835				$Tw = $details['T']['w'];
20836			}
20837			if (isset($details['R']['w'])) {
20838				$Rw = $details['R']['w'];
20839			}
20840			if (isset($details['B']['w'])) {
20841				$Bw = $details['B']['w'];
20842			}
20843			if (isset($details['L']['w'])) {
20844				$Lw = $details['L']['w'];
20845			}
20846
20847			$x2 = $x + $w;
20848			$y2 = $y + $h;
20849			$oldlinewidth = $this->LineWidth;
20850
20851			for ($p = 0; $p < strlen($priority); $p++) {
20852				$side = $priority[$p];
20853				$xadj = 0;
20854				$xadj2 = 0;
20855				$yadj = 0;
20856				$yadj2 = 0;
20857				$print = false;
20858				if ($Tw && $side == 'T' && $this->issetBorder($bord, Border::TOP)) { // TOP
20859					$ly1 = $y;
20860					$ly2 = $y;
20861					$lx1 = $x;
20862					$lx2 = $x2;
20863					$this->SetLineWidth($Tw);
20864					if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {
20865						if ($Tw > $Lw) {
20866							$xadj = ($Tw - $Lw) / 2;
20867						}
20868						if ($Tw < $Lw) {
20869							$xadj = ($Tw + $Lw) / 2;
20870						}
20871					} else {
20872						$xadj = $Tw / 2 - $bsh / 2;
20873					}
20874					if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {
20875						if ($Tw > $Rw) {
20876							$xadj2 = ($Tw - $Rw) / 2;
20877						}
20878						if ($Tw < $Rw) {
20879							$xadj2 = ($Tw + $Rw) / 2;
20880						}
20881					} else {
20882						$xadj2 = $Tw / 2 - $bsh / 2;
20883					}
20884					if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TL'])) {
20885						$xadj = ($Tw - $details['mbw']['TL']) / 2;
20886					}
20887					if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TR'])) {
20888						$xadj2 = ($Tw - $details['mbw']['TR']) / 2;
20889					}
20890					$print = true;
20891				}
20892				if ($Lw && $side == 'L' && $this->issetBorder($bord, Border::LEFT)) { // LEFT
20893					$ly1 = $y;
20894					$ly2 = $y2;
20895					$lx1 = $x;
20896					$lx2 = $x;
20897					$this->SetLineWidth($Lw);
20898					if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {
20899						if ($Lw > $Tw) {
20900							$yadj = ($Lw - $Tw) / 2;
20901						}
20902						if ($Lw < $Tw) {
20903							$yadj = ($Lw + $Tw) / 2;
20904						}
20905					} else {
20906						$yadj = $Lw / 2 - $bsv / 2;
20907					}
20908					if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {
20909						if ($Lw > $Bw) {
20910							$yadj2 = ($Lw - $Bw) / 2;
20911						}
20912						if ($Lw < $Bw) {
20913							$yadj2 = ($Lw + $Bw) / 2;
20914						}
20915					} else {
20916						$yadj2 = $Lw / 2 - $bsv / 2;
20917					}
20918					if (!$bSeparate && $details['mbw']['LT']) {
20919						$yadj = ($Lw - $details['mbw']['LT']) / 2;
20920					}
20921					if (!$bSeparate && $details['mbw']['LB']) {
20922						$yadj2 = ($Lw - $details['mbw']['LB']) / 2;
20923					}
20924					$print = true;
20925				}
20926				if ($Rw && $side == 'R' && $this->issetBorder($bord, Border::RIGHT)) { // RIGHT
20927					$ly1 = $y;
20928					$ly2 = $y2;
20929					$lx1 = $x2;
20930					$lx2 = $x2;
20931					$this->SetLineWidth($Rw);
20932					if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {
20933						if ($Rw < $Tw) {
20934							$yadj = ($Rw + $Tw) / 2;
20935						}
20936						if ($Rw > $Tw) {
20937							$yadj = ($Rw - $Tw) / 2;
20938						}
20939					} else {
20940						$yadj = $Rw / 2 - $bsv / 2;
20941					}
20942
20943					if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {
20944						if ($Rw > $Bw) {
20945							$yadj2 = ($Rw - $Bw) / 2;
20946						}
20947						if ($Rw < $Bw) {
20948							$yadj2 = ($Rw + $Bw) / 2;
20949						}
20950					} else {
20951						$yadj2 = $Rw / 2 - $bsv / 2;
20952					}
20953
20954					if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RT'])) {
20955						$yadj = ($Rw - $details['mbw']['RT']) / 2;
20956					}
20957					if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RB'])) {
20958						$yadj2 = ($Rw - $details['mbw']['RB']) / 2;
20959					}
20960					$print = true;
20961				}
20962				if ($Bw && $side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) { // BOTTOM
20963					$ly1 = $y2;
20964					$ly2 = $y2;
20965					$lx1 = $x;
20966					$lx2 = $x2;
20967					$this->SetLineWidth($Bw);
20968					if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {
20969						if ($Bw > $Lw) {
20970							$xadj = ($Bw - $Lw) / 2;
20971						}
20972						if ($Bw < $Lw) {
20973							$xadj = ($Bw + $Lw) / 2;
20974						}
20975					} else {
20976						$xadj = $Bw / 2 - $bsh / 2;
20977					}
20978					if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {
20979						if ($Bw > $Rw) {
20980							$xadj2 = ($Bw - $Rw) / 2;
20981						}
20982						if ($Bw < $Rw) {
20983							$xadj2 = ($Bw + $Rw) / 2;
20984						}
20985					} else {
20986						$xadj2 = $Bw / 2 - $bsh / 2;
20987					}
20988					if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BL'])) {
20989						$xadj = ($Bw - $details['mbw']['BL']) / 2;
20990					}
20991					if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BR'])) {
20992						$xadj2 = ($Bw - $details['mbw']['BR']) / 2;
20993					}
20994					$print = true;
20995				}
20996
20997				// Now draw line
20998				if ($print) {
20999					/* -- TABLES-ADVANCED-BORDERS -- */
21000					if ($details[$side]['style'] == 'double') {
21001						if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {
21002							if ($details[$side]['c']) {
21003								$this->SetDColor($details[$side]['c']);
21004							} else {
21005								$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
21006							}
21007							$this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
21008						}
21009						if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {
21010							if ($bSeparate && $cort == 'table') {
21011								if ($side == 'T') {
21012									$xadj -= $this->LineWidth / 2;
21013									$xadj2 -= $this->LineWidth;
21014									if ($this->issetBorder($bord, Border::LEFT)) {
21015										$xadj += $this->LineWidth / 2;
21016									}
21017									if ($this->issetBorder($bord, Border::RIGHT)) {
21018										$xadj2 += $this->LineWidth;
21019									}
21020								}
21021								if ($side == 'L') {
21022									$yadj -= $this->LineWidth / 2;
21023									$yadj2 -= $this->LineWidth;
21024									if ($this->issetBorder($bord, Border::TOP)) {
21025										$yadj += $this->LineWidth / 2;
21026									}
21027									if ($this->issetBorder($bord, Border::BOTTOM)) {
21028										$yadj2 += $this->LineWidth;
21029									}
21030								}
21031								if ($side == 'B') {
21032									$xadj -= $this->LineWidth / 2;
21033									$xadj2 -= $this->LineWidth;
21034									if ($this->issetBorder($bord, Border::LEFT)) {
21035										$xadj += $this->LineWidth / 2;
21036									}
21037									if ($this->issetBorder($bord, Border::RIGHT)) {
21038										$xadj2 += $this->LineWidth;
21039									}
21040								}
21041								if ($side == 'R') {
21042									$yadj -= $this->LineWidth / 2;
21043									$yadj2 -= $this->LineWidth;
21044									if ($this->issetBorder($bord, Border::TOP)) {
21045										$yadj += $this->LineWidth / 2;
21046									}
21047									if ($this->issetBorder($bord, Border::BOTTOM)) {
21048										$yadj2 += $this->LineWidth;
21049									}
21050								}
21051							}
21052
21053							$this->SetLineWidth($this->LineWidth / 3);
21054
21055							$tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
21056							for ($l = 0; $l <= $this->blklvl; $l++) {
21057								if ($this->blk[$l]['bgcolor']) {
21058									$tbcol = ($this->blk[$l]['bgcolorarray']);
21059								}
21060							}
21061
21062							if ($bSeparate) {
21063								$cellBorderOverlay[] = [
21064									'x' => $lx1 + $xadj,
21065									'y' => $ly1 + $yadj,
21066									'x2' => $lx2 - $xadj2,
21067									'y2' => $ly2 - $yadj2,
21068									'col' => $tbcol,
21069									'lw' => $this->LineWidth,
21070								];
21071							} else {
21072								$this->SetDColor($tbcol);
21073								$this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
21074							}
21075						}
21076					} elseif (isset($details[$side]['style']) && ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset')) {
21077						if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {
21078							if ($details[$side]['c']) {
21079								$this->SetDColor($details[$side]['c']);
21080							} else {
21081								$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
21082							}
21083							if ($details[$side]['style'] == 'outset' || $details[$side]['style'] == 'groove') {
21084								$nc = $this->colorConverter->darken($details[$side]['c']);
21085								$this->SetDColor($nc);
21086							} elseif ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {
21087								$nc = $this->colorConverter->lighten($details[$side]['c']);
21088								$this->SetDColor($nc);
21089							}
21090							$this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
21091						}
21092						if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {
21093							if ($details[$side]['c']) {
21094								$this->SetDColor($details[$side]['c']);
21095							} else {
21096								$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
21097							}
21098							$doubleadj = ($this->LineWidth) / 3;
21099							$this->SetLineWidth($this->LineWidth / 2);
21100							$xadj3 = $yadj3 = $wadj3 = $hadj3 = 0;
21101
21102							if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {
21103								$nc = $this->colorConverter->darken($details[$side]['c']);
21104
21105								if ($bSeparate && $cort == 'table') {
21106									if ($side == 'T') {
21107										$yadj3 = $this->LineWidth / 2;
21108										$xadj3 = -$this->LineWidth / 2;
21109										$wadj3 = $this->LineWidth;
21110										if ($this->issetBorder($bord, Border::LEFT)) {
21111											$xadj3 += $this->LineWidth;
21112											$wadj3 -= $this->LineWidth;
21113										}
21114										if ($this->issetBorder($bord, Border::RIGHT)) {
21115											$wadj3 -= $this->LineWidth * 2;
21116										}
21117									}
21118									if ($side == 'L') {
21119										$xadj3 = $this->LineWidth / 2;
21120										$yadj3 = -$this->LineWidth / 2;
21121										$hadj3 = $this->LineWidth;
21122										if ($this->issetBorder($bord, Border::TOP)) {
21123											$yadj3 += $this->LineWidth;
21124											$hadj3 -= $this->LineWidth;
21125										}
21126										if ($this->issetBorder($bord, Border::BOTTOM)) {
21127											$hadj3 -= $this->LineWidth * 2;
21128										}
21129									}
21130									if ($side == 'B') {
21131										$yadj3 = $this->LineWidth / 2;
21132										$xadj3 = -$this->LineWidth / 2;
21133										$wadj3 = $this->LineWidth;
21134									}
21135									if ($side == 'R') {
21136										$xadj3 = $this->LineWidth / 2;
21137										$yadj3 = -$this->LineWidth / 2;
21138										$hadj3 = $this->LineWidth;
21139									}
21140								} elseif ($side == 'T') {
21141									$yadj3 = $this->LineWidth / 2;
21142									$xadj3 = $this->LineWidth / 2;
21143									$wadj3 = -$this->LineWidth * 2;
21144								} elseif ($side == 'L') {
21145									$xadj3 = $this->LineWidth / 2;
21146									$yadj3 = $this->LineWidth / 2;
21147									$hadj3 = -$this->LineWidth * 2;
21148								} elseif ($side == 'B' && $bSeparate) {
21149									$yadj3 = $this->LineWidth / 2;
21150									$wadj3 = $this->LineWidth / 2;
21151								} elseif ($side == 'R' && $bSeparate) {
21152									$xadj3 = $this->LineWidth / 2;
21153									$hadj3 = $this->LineWidth / 2;
21154								} elseif ($side == 'B') {
21155									$yadj3 = $this->LineWidth / 2;
21156									$xadj3 = $this->LineWidth / 2;
21157								} elseif ($side == 'R') {
21158									$xadj3 = $this->LineWidth / 2;
21159									$yadj3 = $this->LineWidth / 2;
21160								}
21161							} else {
21162								$nc = $this->colorConverter->lighten($details[$side]['c']);
21163
21164								if ($bSeparate && $cort == 'table') {
21165									if ($side == 'T') {
21166										$yadj3 = $this->LineWidth / 2;
21167										$xadj3 = -$this->LineWidth / 2;
21168										$wadj3 = $this->LineWidth;
21169										if ($this->issetBorder($bord, Border::LEFT)) {
21170											$xadj3 += $this->LineWidth;
21171											$wadj3 -= $this->LineWidth;
21172										}
21173									}
21174									if ($side == 'L') {
21175										$xadj3 = $this->LineWidth / 2;
21176										$yadj3 = -$this->LineWidth / 2;
21177										$hadj3 = $this->LineWidth;
21178										if ($this->issetBorder($bord, Border::TOP)) {
21179											$yadj3 += $this->LineWidth;
21180											$hadj3 -= $this->LineWidth;
21181										}
21182									}
21183									if ($side == 'B') {
21184										$yadj3 = $this->LineWidth / 2;
21185										$xadj3 = -$this->LineWidth / 2;
21186										$wadj3 = $this->LineWidth;
21187										if ($this->issetBorder($bord, Border::LEFT)) {
21188											$xadj3 += $this->LineWidth;
21189											$wadj3 -= $this->LineWidth;
21190										}
21191									}
21192									if ($side == 'R') {
21193										$xadj3 = $this->LineWidth / 2;
21194										$yadj3 = -$this->LineWidth / 2;
21195										$hadj3 = $this->LineWidth;
21196										if ($this->issetBorder($bord, Border::TOP)) {
21197											$yadj3 += $this->LineWidth;
21198											$hadj3 -= $this->LineWidth;
21199										}
21200									}
21201								} elseif ($side == 'T') {
21202									$yadj3 = $this->LineWidth / 2;
21203									$xadj3 = $this->LineWidth / 2;
21204								} elseif ($side == 'L') {
21205									$xadj3 = $this->LineWidth / 2;
21206									$yadj3 = $this->LineWidth / 2;
21207								} elseif ($side == 'B' && $bSeparate) {
21208									$yadj3 = $this->LineWidth / 2;
21209									$xadj3 = $this->LineWidth / 2;
21210								} elseif ($side == 'R' && $bSeparate) {
21211									$xadj3 = $this->LineWidth / 2;
21212									$yadj3 = $this->LineWidth / 2;
21213								} elseif ($side == 'B') {
21214									$yadj3 = $this->LineWidth / 2;
21215									$xadj3 = -$this->LineWidth / 2;
21216									$wadj3 = $this->LineWidth;
21217								} elseif ($side == 'R') {
21218									$xadj3 = $this->LineWidth / 2;
21219									$yadj3 = -$this->LineWidth / 2;
21220									$hadj3 = $this->LineWidth;
21221								}
21222							}
21223
21224							if ($bSeparate) {
21225								$cellBorderOverlay[] = [
21226									'x' => $lx1 + $xadj + $xadj3,
21227									'y' => $ly1 + $yadj + $yadj3,
21228									'x2' => $lx2 - $xadj2 + $xadj3 + $wadj3,
21229									'y2' => $ly2 - $yadj2 + $yadj3 + $hadj3,
21230									'col' => $nc,
21231									'lw' => $this->LineWidth,
21232								];
21233							} else {
21234								$this->SetDColor($nc);
21235								$this->Line($lx1 + $xadj + $xadj3, $ly1 + $yadj + $yadj3, $lx2 - $xadj2 + $xadj3 + $wadj3, $ly2 - $yadj2 + $yadj3 + $hadj3);
21236							}
21237						}
21238					} else {
21239						/* -- END TABLES-ADVANCED-BORDERS -- */
21240						if ($details[$side]['style'] == 'dashed') {
21241							$dashsize = 2; // final dash will be this + 1*linewidth
21242							$dashsizek = 1.5; // ratio of Dash/Blank
21243							$this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));
21244						} elseif ($details[$side]['style'] == 'dotted') {
21245							$this->SetLineJoin(1);
21246							$this->SetLineCap(1);
21247							$this->SetDash(0.001, ($this->LineWidth * 2));
21248						}
21249						if ($details[$side]['c']) {
21250							$this->SetDColor($details[$side]['c']);
21251						} else {
21252							$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
21253						}
21254						$this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
21255						/* -- TABLES-ADVANCED-BORDERS -- */
21256					}
21257					/* -- END TABLES-ADVANCED-BORDERS -- */
21258
21259					// Reset Corners
21260					$this->SetDash();
21261					// BUTT style line cap
21262					$this->SetLineCap(2);
21263				}
21264			}
21265
21266			if ($bSeparate && count($cellBorderOverlay)) {
21267				foreach ($cellBorderOverlay as $cbo) {
21268					$this->SetLineWidth($cbo['lw']);
21269					$this->SetDColor($cbo['col']);
21270					$this->Line($cbo['x'], $cbo['y'], $cbo['x2'], $cbo['y2']);
21271				}
21272			}
21273
21274			// $this->SetLineWidth($oldlinewidth);
21275			// $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
21276		}
21277	}
21278
21279	/* -- TABLES -- */
21280	/* -- TABLES-ADVANCED-BORDERS -- */
21281
21282	/* -- END TABLES-ADVANCED-BORDERS -- */
21283
21284	function setBorder(&$var, $flag, $set = true)
21285	{
21286		$flag = intval($flag);
21287		if ($set) {
21288			$set = true;
21289		}
21290		$var = intval($var);
21291		$var = $set ? ($var | $flag) : ($var & ~$flag);
21292	}
21293
21294	function issetBorder($var, $flag)
21295	{
21296		$flag = intval($flag);
21297		$var = intval($var);
21298		return (($var & $flag) == $flag);
21299	}
21300
21301	function _table2cellBorder(&$tableb, &$cbdb, &$cellb, $bval)
21302	{
21303		if ($tableb && $tableb['w'] > $cbdb['w']) {
21304			$cbdb = $tableb;
21305			$this->setBorder($cellb, $bval);
21306		} elseif ($tableb && $tableb['w'] == $cbdb['w'] && array_search($tableb['style'], $this->borderstyles) > array_search($cbdb['style'], $this->borderstyles)) {
21307			$cbdb = $tableb;
21308			$this->setBorder($cellb, $bval);
21309		}
21310	}
21311
21312	// FIX BORDERS ********************************************
21313	function _fixTableBorders(&$table)
21314	{
21315		if (!$table['borders_separate'] && $table['border_details']['L']['w']) {
21316			$table['max_cell_border_width']['L'] = $table['border_details']['L']['w'];
21317		}
21318		if (!$table['borders_separate'] && $table['border_details']['R']['w']) {
21319			$table['max_cell_border_width']['R'] = $table['border_details']['R']['w'];
21320		}
21321		if (!$table['borders_separate'] && $table['border_details']['T']['w']) {
21322			$table['max_cell_border_width']['T'] = $table['border_details']['T']['w'];
21323		}
21324		if (!$table['borders_separate'] && $table['border_details']['B']['w']) {
21325			$table['max_cell_border_width']['B'] = $table['border_details']['B']['w'];
21326		}
21327		if ($this->simpleTables) {
21328			return;
21329		}
21330		$cells = &$table['cells'];
21331		$numcols = $table['nc'];
21332		$numrows = $table['nr'];
21333		/* -- TABLES-ADVANCED-BORDERS -- */
21334		if (isset($table['topntail']) && $table['topntail']) {
21335			$tntborddet = $this->border_details($table['topntail']);
21336		}
21337		if (isset($table['thead-underline']) && $table['thead-underline']) {
21338			$thuborddet = $this->border_details($table['thead-underline']);
21339		}
21340		/* -- END TABLES-ADVANCED-BORDERS -- */
21341
21342		for ($i = 0; $i < $numrows; $i++) { // Rows
21343			for ($j = 0; $j < $numcols; $j++) { // Columns
21344				if (isset($cells[$i][$j]) && $cells[$i][$j]) {
21345					$cell = &$cells[$i][$j];
21346					if ($this->packTableData) {
21347						$cbord = $this->_unpackCellBorder($cell['borderbin']);
21348					} else {
21349						$cbord = &$cells[$i][$j];
21350					}
21351
21352					// mPDF 5.7.3
21353					if (!$cbord['border'] && $cbord['border'] !== 0 && isset($table['border']) && $table['border'] && $this->table_border_attr_set) {
21354						$cbord['border'] = $table['border'];
21355						$cbord['border_details'] = $table['border_details'];
21356					}
21357
21358					if (isset($cell['colspan']) && $cell['colspan'] > 1) {
21359						$ccolsp = $cell['colspan'];
21360					} else {
21361						$ccolsp = 1;
21362					}
21363					if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {
21364						$crowsp = $cell['rowspan'];
21365					} else {
21366						$crowsp = 1;
21367					}
21368
21369					$cbord['border_details']['cellposdom'] = ((($i + 1) / $numrows) / 10000 ) + ((($j + 1) / $numcols) / 10 );
21370					// Inherit Cell border from Table border
21371					if ($this->table_border_css_set && !$table['borders_separate']) {
21372						if ($i == 0) {
21373							$this->_table2cellBorder($table['border_details']['T'], $cbord['border_details']['T'], $cbord['border'], Border::TOP);
21374						}
21375						if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {
21376							$this->_table2cellBorder($table['border_details']['B'], $cbord['border_details']['B'], $cbord['border'], Border::BOTTOM);
21377						}
21378						if ($j == 0) {
21379							$this->_table2cellBorder($table['border_details']['L'], $cbord['border_details']['L'], $cbord['border'], Border::LEFT);
21380						}
21381						if ($j == ($numcols - 1) || ($j + $ccolsp) == ($numcols)) {
21382							$this->_table2cellBorder($table['border_details']['R'], $cbord['border_details']['R'], $cbord['border'], Border::RIGHT);
21383						}
21384					}
21385
21386					/* -- TABLES-ADVANCED-BORDERS -- */
21387					$fixbottom = true;
21388					if (isset($table['topntail']) && $table['topntail']) {
21389						if ($i == 0) {
21390							$cbord['border_details']['T'] = $tntborddet;
21391							$this->setBorder($cbord['border'], Border::TOP);
21392						}
21393						if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {
21394							$cbord['border_details']['B'] = $tntborddet;
21395							$this->setBorder($cbord['border'], Border::BOTTOM);
21396							$fixbottom = false;
21397						} elseif ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows']) {
21398							if (!$table['borders_separate']) {
21399								$cbord['border_details']['T'] = $tntborddet;
21400								$this->setBorder($cbord['border'], Border::TOP);
21401							}
21402						}
21403						if ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'] - 1)) {
21404							if (!$table['borders_separate']) {
21405								$cbord['border_details']['B'] = $tntborddet;
21406								$this->setBorder($cbord['border'], Border::BOTTOM);
21407								$fixbottom = false;
21408							}
21409						} elseif ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'])) {
21410							$cbord['border_details']['T'] = $tntborddet;
21411							$this->setBorder($cbord['border'], Border::TOP);
21412						}
21413						if ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader
21414							if (!$table['borders_separate']) {
21415								$cbord['border_details']['T'] = $tntborddet;
21416								$this->setBorder($cbord['border'], Border::TOP);
21417							}
21418						}
21419						if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {
21420							$cbord['border_details']['B'] = $tntborddet;
21421							$this->setBorder($cbord['border'], Border::BOTTOM);
21422						}
21423					}
21424					if (isset($table['thead-underline']) && $table['thead-underline']) {
21425						if ($table['borders_separate']) {
21426							if ($i == 0) {
21427								$cbord['border_details']['B'] = $thuborddet;
21428								$this->setBorder($cbord['border'], Border::BOTTOM);
21429								$fixbottom = false;
21430							}
21431						} else {
21432							if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {
21433								$cbord['border_details']['T'] = $thuborddet;
21434								$this->setBorder($cbord['border'], Border::TOP);
21435							} elseif ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader
21436								$cbord['border_details']['T'] = $thuborddet;
21437								$this->setBorder($cbord['border'], Border::TOP);
21438							}
21439						}
21440					}
21441
21442					// Collapse Border - Algorithm for conflicting borders
21443					// Hidden >> Width >> double>solid>dashed>dotted... >> style set on cell>table >> top/left>bottom/right
21444					// Do not turn off border which is overridden
21445					// Needed for page break for TOP/BOTTOM both to be defined in Collapsed borders
21446					// Means it is painted twice. (Left/Right can still disable overridden border)
21447					if (!$table['borders_separate']) {
21448
21449						if (($i < ($numrows - 1) || ($i + $crowsp) < $numrows ) && $fixbottom) { // Bottom
21450
21451							for ($cspi = 0; $cspi < $ccolsp; $cspi++) {
21452
21453								// already defined Top for adjacent cell below
21454								if (isset($cells[($i + $crowsp)][$j + $cspi])) {
21455									if ($this->packTableData) {
21456										$adjc = $cells[($i + $crowsp)][$j + $cspi];
21457										$celladj = $this->_unpackCellBorder($adjc['borderbin']);
21458									} else {
21459										$celladj = & $cells[($i + $crowsp)][$j + $cspi];
21460									}
21461								} else {
21462									$celladj = false;
21463								}
21464
21465								if (isset($celladj['border_details']['T']['s']) && $celladj['border_details']['T']['s'] == 1) {
21466
21467									$csadj = $celladj['border_details']['T']['w'];
21468									$csthis = $cbord['border_details']['B']['w'];
21469
21470									// Hidden
21471									if ($cbord['border_details']['B']['style'] == 'hidden') {
21472
21473										$celladj['border_details']['T'] = $cbord['border_details']['B'];
21474										$this->setBorder($celladj['border'], Border::TOP, false);
21475										$this->setBorder($cbord['border'], Border::BOTTOM, false);
21476
21477									} elseif ($celladj['border_details']['T']['style'] == 'hidden') {
21478
21479										$cbord['border_details']['B'] = $celladj['border_details']['T'];
21480										$this->setBorder($cbord['border'], Border::BOTTOM, false);
21481										$this->setBorder($celladj['border'], Border::TOP, false);
21482
21483									} elseif ($csthis > $csadj) { // Width
21484
21485										if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span
21486											$celladj['border_details']['T'] = $cbord['border_details']['B'];
21487											$this->setBorder($cbord['border'], Border::BOTTOM);
21488										}
21489
21490									} elseif ($csadj > $csthis) {
21491
21492										if ($ccolsp < 2) { // don't overwrite this cell if it spans
21493											$cbord['border_details']['B'] = $celladj['border_details']['T'];
21494											$this->setBorder($celladj['border'], Border::TOP);
21495										}
21496
21497									} elseif (array_search($cbord['border_details']['B']['style'], $this->borderstyles) > array_search($celladj['border_details']['T']['style'], $this->borderstyles)) { // double>solid>dashed>dotted...
21498
21499										if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span
21500											$celladj['border_details']['T'] = $cbord['border_details']['B'];
21501											$this->setBorder($cbord['border'], Border::BOTTOM);
21502										}
21503
21504									} elseif (array_search($celladj['border_details']['T']['style'], $this->borderstyles) > array_search($cbord['border_details']['B']['style'], $this->borderstyles)) {
21505
21506										if ($ccolsp < 2) { // don't overwrite this cell if it spans
21507											$cbord['border_details']['B'] = $celladj['border_details']['T'];
21508											$this->setBorder($celladj['border'], Border::TOP);
21509										}
21510
21511									} elseif ($celladj['border_details']['T']['dom'] > $celladj['border_details']['B']['dom']) { // Style set on cell vs. table
21512
21513										if ($ccolsp < 2) { // don't overwrite this cell if it spans
21514											$cbord['border_details']['B'] = $celladj['border_details']['T'];
21515											$this->setBorder($celladj['border'], Border::TOP);
21516										}
21517
21518									} else { // Style set on cell vs. table  - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT
21519
21520										if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span
21521											$celladj['border_details']['T'] = $cbord['border_details']['B'];
21522											$this->setBorder($cbord['border'], Border::BOTTOM);
21523										}
21524
21525									}
21526
21527								} elseif ($celladj) {
21528
21529									if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span
21530										$celladj['border_details']['T'] = $cbord['border_details']['B'];
21531									}
21532
21533								}
21534
21535								// mPDF 5.7.4
21536								if ($celladj && $this->packTableData) {
21537									$cells[$i + $crowsp][$j + $cspi]['borderbin'] = $this->_packCellBorder($celladj);
21538								}
21539
21540								unset($celladj);
21541							}
21542						}
21543
21544						if ($j < ($numcols - 1) || ($j + $ccolsp) < $numcols) { // Right-Left
21545
21546							for ($cspi = 0; $cspi < $crowsp; $cspi++) {
21547
21548								// already defined Left for adjacent cell to R
21549								if (isset($cells[($i + $cspi)][$j + $ccolsp])) {
21550									if ($this->packTableData) {
21551										$adjc = $cells[($i + $cspi)][$j + $ccolsp];
21552										$celladj = $this->_unpackCellBorder($adjc['borderbin']);
21553									} else {
21554										$celladj = & $cells[$i + $cspi][$j + $ccolsp];
21555									}
21556								} else {
21557									$celladj = false;
21558								}
21559								if ($celladj && $celladj['border_details']['L']['s'] == 1) {
21560									$csadj = $celladj['border_details']['L']['w'];
21561									$csthis = $cbord['border_details']['R']['w'];
21562									// Hidden
21563									if ($cbord['border_details']['R']['style'] == 'hidden') {
21564										$celladj['border_details']['L'] = $cbord['border_details']['R'];
21565										$this->setBorder($celladj['border'], Border::LEFT, false);
21566										$this->setBorder($cbord['border'], Border::RIGHT, false);
21567									} elseif ($celladj['border_details']['L']['style'] == 'hidden') {
21568										$cbord['border_details']['R'] = $celladj['border_details']['L'];
21569										$this->setBorder($cbord['border'], Border::RIGHT, false);
21570										$this->setBorder($celladj['border'], Border::LEFT, false);
21571									} // Width
21572									elseif ($csthis > $csadj) {
21573										if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span
21574											$celladj['border_details']['L'] = $cbord['border_details']['R'];
21575											$this->setBorder($cbord['border'], Border::RIGHT);
21576											$this->setBorder($celladj['border'], Border::LEFT, false);
21577										}
21578									} elseif ($csadj > $csthis) {
21579										if ($crowsp < 2) { // don't overwrite this cell if it spans
21580											$cbord['border_details']['R'] = $celladj['border_details']['L'];
21581											$this->setBorder($cbord['border'], Border::RIGHT, false);
21582											$this->setBorder($celladj['border'], Border::LEFT);
21583										}
21584									} // double>solid>dashed>dotted...
21585									elseif (array_search($cbord['border_details']['R']['style'], $this->borderstyles) > array_search($celladj['border_details']['L']['style'], $this->borderstyles)) {
21586										if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span
21587											$celladj['border_details']['L'] = $cbord['border_details']['R'];
21588											$this->setBorder($celladj['border'], Border::LEFT, false);
21589											$this->setBorder($cbord['border'], Border::RIGHT);
21590										}
21591									} elseif (array_search($celladj['border_details']['L']['style'], $this->borderstyles) > array_search($cbord['border_details']['R']['style'], $this->borderstyles)) {
21592										if ($crowsp < 2) { // don't overwrite this cell if it spans
21593											$cbord['border_details']['R'] = $celladj['border_details']['L'];
21594											$this->setBorder($cbord['border'], Border::RIGHT, false);
21595											$this->setBorder($celladj['border'], Border::LEFT);
21596										}
21597									} // Style set on cell vs. table
21598									elseif ($celladj['border_details']['L']['dom'] > $cbord['border_details']['R']['dom']) {
21599										if ($crowsp < 2) { // don't overwrite this cell if it spans
21600											$cbord['border_details']['R'] = $celladj['border_details']['L'];
21601											$this->setBorder($celladj['border'], Border::LEFT);
21602										}
21603									} // Style set on cell vs. table  - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT
21604									else {
21605										if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span
21606											$celladj['border_details']['L'] = $cbord['border_details']['R'];
21607											$this->setBorder($cbord['border'], Border::RIGHT);
21608										}
21609									}
21610								} elseif ($celladj) {
21611									// if right-cell border is not set
21612									if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span
21613										$celladj['border_details']['L'] = $cbord['border_details']['R'];
21614									}
21615								}
21616								// mPDF 5.7.4
21617								if ($celladj && $this->packTableData) {
21618									$cells[$i + $cspi][$j + $ccolsp]['borderbin'] = $this->_packCellBorder($celladj);
21619								}
21620								unset($celladj);
21621							}
21622						}
21623					}
21624
21625
21626					// Set maximum cell border width meeting at LRTB edges of cell - used for extended cell border
21627					// ['border_details']['mbw']['LT'] = meeting border width - Left border - Top end
21628					if (!$table['borders_separate']) {
21629
21630						$cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $cbord['border_details']['L']['w']);
21631						$cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $cbord['border_details']['R']['w']);
21632						$cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $cbord['border_details']['T']['w']);
21633						$cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $cbord['border_details']['B']['w']);
21634						$cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $cbord['border_details']['L']['w']);
21635						$cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $cbord['border_details']['R']['w']);
21636						$cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $cbord['border_details']['T']['w']);
21637						$cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $cbord['border_details']['B']['w']);
21638
21639						if (($i + $crowsp) < $numrows && isset($cells[$i + $crowsp][$j])) { // Has Bottom adjoining cell
21640
21641							if ($this->packTableData) {
21642								$adjc = $cells[$i + $crowsp][$j];
21643								$celladj = $this->_unpackCellBorder($adjc['borderbin']);
21644							} else {
21645								$celladj = & $cells[$i + $crowsp][$j];
21646							}
21647
21648							$cbord['border_details']['mbw']['BL'] = max(
21649								$cbord['border_details']['mbw']['BL'],
21650								$celladj ? $celladj['border_details']['L']['w'] : 0,
21651								$celladj ? $celladj['border_details']['mbw']['TL']: 0
21652							);
21653
21654							$cbord['border_details']['mbw']['BR'] = max(
21655								$cbord['border_details']['mbw']['BR'],
21656								$celladj ? $celladj['border_details']['R']['w'] : 0,
21657								$celladj ? $celladj['border_details']['mbw']['TR']: 0
21658							);
21659
21660							$cbord['border_details']['mbw']['LB'] = max(
21661								$cbord['border_details']['mbw']['LB'],
21662								$celladj ? $celladj['border_details']['mbw']['LT'] : 0
21663							);
21664
21665							$cbord['border_details']['mbw']['RB'] = max(
21666								$cbord['border_details']['mbw']['RB'],
21667								$celladj ? $celladj['border_details']['mbw']['RT'] : 0
21668							);
21669
21670							unset($celladj);
21671						}
21672
21673						if (($j + $ccolsp) < $numcols && isset($cells[$i][$j + $ccolsp])) { // Has Right adjoining cell
21674
21675							if ($this->packTableData) {
21676								$adjc = $cells[$i][$j + $ccolsp];
21677								$celladj = $this->_unpackCellBorder($adjc['borderbin']);
21678							} else {
21679								$celladj = & $cells[$i][$j + $ccolsp];
21680							}
21681
21682							$cbord['border_details']['mbw']['RT'] = max(
21683								$cbord['border_details']['mbw']['RT'],
21684								$celladj ? $celladj['border_details']['T']['w'] : 0,
21685								$celladj ? $celladj['border_details']['mbw']['LT'] : 0
21686							);
21687
21688							$cbord['border_details']['mbw']['RB'] = max(
21689								$cbord['border_details']['mbw']['RB'],
21690								$celladj ? $celladj['border_details']['B']['w'] : 0,
21691								$celladj ? $celladj['border_details']['mbw']['LB'] : 0
21692							);
21693
21694							$cbord['border_details']['mbw']['TR'] = max(
21695								$cbord['border_details']['mbw']['TR'],
21696								$celladj ? $celladj['border_details']['mbw']['TL'] : 0
21697							);
21698
21699							$cbord['border_details']['mbw']['BR'] = max(
21700								$cbord['border_details']['mbw']['BR'],
21701								$celladj ? $celladj['border_details']['mbw']['BL'] : 0
21702							);
21703
21704							unset($celladj);
21705						}
21706
21707						if ($i > 0 && isset($cells[$i - 1][$j]) && is_array($cells[$i - 1][$j]) && (($this->packTableData && $cells[$i - 1][$j]['borderbin']) || $cells[$i - 1][$j]['border'])) { // Has Top adjoining cell
21708
21709							if ($this->packTableData) {
21710								$adjc = $cells[$i - 1][$j];
21711								$celladj = $this->_unpackCellBorder($adjc['borderbin']);
21712							} else {
21713								$celladj = & $cells[$i - 1][$j];
21714							}
21715
21716							$cbord['border_details']['mbw']['TL'] = max(
21717								$cbord['border_details']['mbw']['TL'],
21718								$celladj ? $celladj['border_details']['L']['w'] : 0,
21719								$celladj ? $celladj['border_details']['mbw']['BL'] : 0
21720							);
21721
21722							$cbord['border_details']['mbw']['TR'] = max(
21723								$cbord['border_details']['mbw']['TR'],
21724								$celladj ? $celladj['border_details']['R']['w'] : 0,
21725								$celladj ? $celladj['border_details']['mbw']['BR'] : 0
21726							);
21727
21728							$cbord['border_details']['mbw']['LT'] = max(
21729								$cbord['border_details']['mbw']['LT'],
21730								$celladj ? $celladj['border_details']['mbw']['LB'] : 0
21731							);
21732
21733							$cbord['border_details']['mbw']['RT'] = max(
21734								$cbord['border_details']['mbw']['RT'],
21735								$celladj ? $celladj['border_details']['mbw']['RB'] : 0
21736							);
21737
21738							if ($celladj['border_details']['mbw']['BL']) {
21739								$celladj['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['mbw']['BL']);
21740							}
21741
21742							if ($celladj['border_details']['mbw']['BR']) {
21743								$celladj['border_details']['mbw']['BR'] = max($celladj['border_details']['mbw']['BR'], $cbord['border_details']['mbw']['TR']);
21744							}
21745
21746							if ($this->packTableData) {
21747								$cells[$i - 1][$j]['borderbin'] = $this->_packCellBorder($celladj);
21748							}
21749							unset($celladj);
21750						}
21751
21752						if ($j > 0 && isset($cells[$i][$j - 1]) && is_array($cells[$i][$j - 1]) && (($this->packTableData && $cells[$i][$j - 1]['borderbin']) || $cells[$i][$j - 1]['border'])) { // Has Left adjoining cell
21753
21754							if ($this->packTableData) {
21755								$adjc = $cells[$i][$j - 1];
21756								$celladj = $this->_unpackCellBorder($adjc['borderbin']);
21757							} else {
21758								$celladj = & $cells[$i][$j - 1];
21759							}
21760
21761							$cbord['border_details']['mbw']['LT'] = max(
21762								$cbord['border_details']['mbw']['LT'],
21763								$celladj ? $celladj['border_details']['T']['w'] : 0,
21764								$celladj ? $celladj['border_details']['mbw']['RT'] : 0
21765							);
21766
21767							$cbord['border_details']['mbw']['LB'] = max(
21768								$cbord['border_details']['mbw']['LB'],
21769								$celladj ? $celladj['border_details']['B']['w'] : 0,
21770								$celladj ? $celladj['border_details']['mbw']['RB'] : 0
21771							);
21772
21773							$cbord['border_details']['mbw']['BL'] = max(
21774								$cbord['border_details']['mbw']['BL'],
21775								$celladj ? $celladj['border_details']['mbw']['BR'] : 0
21776							);
21777
21778							$cbord['border_details']['mbw']['TL'] = max(
21779								$cbord['border_details']['mbw']['TL'],
21780								$celladj ? $celladj['border_details']['mbw']['TR'] : 0
21781							);
21782
21783							if ($celladj['border_details']['mbw']['RT']) {
21784								$celladj['border_details']['mbw']['RT'] = max($celladj['border_details']['mbw']['RT'], $cbord['border_details']['mbw']['LT']);
21785							}
21786
21787							if ($celladj['border_details']['mbw']['RB']) {
21788								$celladj['border_details']['mbw']['RB'] = max($celladj['border_details']['mbw']['RB'], $cbord['border_details']['mbw']['LB']);
21789							}
21790
21791							if ($this->packTableData) {
21792								$cells[$i][$j - 1]['borderbin'] = $this->_packCellBorder($celladj);
21793							}
21794
21795							unset($celladj);
21796						}
21797
21798
21799						// Update maximum cell border width at LRTB edges of table - used for overall table width
21800						if ($j == 0 && $cbord['border_details']['L']['w']) {
21801							$table['max_cell_border_width']['L'] = max($table['max_cell_border_width']['L'], $cbord['border_details']['L']['w']);
21802						}
21803						if (($j == ($numcols - 1) || ($j + $ccolsp) == $numcols ) && $cbord['border_details']['R']['w']) {
21804							$table['max_cell_border_width']['R'] = max($table['max_cell_border_width']['R'], $cbord['border_details']['R']['w']);
21805						}
21806						if ($i == 0 && $cbord['border_details']['T']['w']) {
21807							$table['max_cell_border_width']['T'] = max($table['max_cell_border_width']['T'], $cbord['border_details']['T']['w']);
21808						}
21809						if (($i == ($numrows - 1) || ($i + $crowsp) == $numrows ) && $cbord['border_details']['B']['w']) {
21810							$table['max_cell_border_width']['B'] = max($table['max_cell_border_width']['B'], $cbord['border_details']['B']['w']);
21811						}
21812					}
21813					/* -- END TABLES-ADVANCED-BORDERS -- */
21814
21815					if ($this->packTableData) {
21816						$cell['borderbin'] = $this->_packCellBorder($cbord);
21817					}
21818
21819					unset($cbord);
21820
21821					unset($cell);
21822				}
21823			}
21824		}
21825		unset($cell);
21826	}
21827
21828	// END FIX BORDERS ************************************************************************************
21829
21830	function _reverseTableDir(&$table)
21831	{
21832		$cells = &$table['cells'];
21833		$numcols = $table['nc'];
21834		$numrows = $table['nr'];
21835		for ($i = 0; $i < $numrows; $i++) { // Rows
21836			$row = [];
21837			for ($j = ($numcols - 1); $j >= 0; $j--) { // Columns
21838				if (isset($cells[$i][$j]) && $cells[$i][$j]) {
21839					$cell = &$cells[$i][$j];
21840					$col = $numcols - $j - 1;
21841					if (isset($cell['colspan']) && $cell['colspan'] > 1) {
21842						$col -= ($cell['colspan'] - 1);
21843					}
21844					// Nested content
21845					if (isset($cell['textbuffer'])) {
21846						for ($n = 0; $n < count($cell['textbuffer']); $n++) {
21847							$t = $cell['textbuffer'][$n][0];
21848							if (substr($t, 0, 19) == "\xbb\xa4\xactype=nestedtable") {
21849								$objattr = $this->_getObjAttr($t);
21850								$objattr['col'] = $col;
21851								$cell['textbuffer'][$n][0] = "\xbb\xa4\xactype=nestedtable,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
21852								$this->table[($this->tableLevel + 1)][$objattr['nestedcontent']]['nestedpos'][1] = $col;
21853							}
21854						}
21855					}
21856					$row[$col] = $cells[$i][$j];
21857					unset($cell);
21858				}
21859			}
21860			for ($f = 0; $f < $numcols; $f++) {
21861				if (!isset($row[$f])) {
21862					$row[$f] = 0;
21863				}
21864			}
21865			$table['cells'][$i] = $row;
21866		}
21867	}
21868
21869	function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $splitpg = 0, $rety = 0)
21870	{
21871		$level = $table['level'];
21872		$levelid = $table['levelid'];
21873
21874		$cells = &$table['cells'];
21875		$numcols = $table['nc'];
21876		$numrows = $table['nr'];
21877		$maxbwtop = 0;
21878		if ($this->ColActive && $level == 1) {
21879			$this->breakpoints[$this->CurrCol][] = $this->y;
21880		} // *COLUMNS*
21881
21882		if (!$split || ($startrow == 0 && $splitpg == 0) || $startrow > 0) {
21883			// TABLE TOP MARGIN
21884			if ($table['margin']['T']) {
21885				if (!$this->table_rotate && $level == 1) {
21886					$this->DivLn($table['margin']['T'], $this->blklvl, true, 1);  // collapsible
21887				} else {
21888					$this->y += ($table['margin']['T']);
21889				}
21890			}
21891			// Advance down page by half width of top border
21892			if ($table['borders_separate']) {
21893				if ($startrow > 0 && (!isset($table['is_thead']) || count($table['is_thead']) == 0)) {
21894					$adv = $table['border_spacing_V'] / 2;
21895				} else {
21896					$adv = $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
21897				}
21898			} else {
21899				$adv = $table['max_cell_border_width']['T'] / 2;
21900			}
21901			if (!$this->table_rotate && $level == 1) {
21902				$this->DivLn($adv);
21903			} else {
21904				$this->y += $adv;
21905			}
21906		}
21907
21908		if ($level == 1) {
21909			$this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'];
21910			$x0 = $this->x;
21911			$y0 = $this->y;
21912			$right = $x0 + $this->blk[$this->blklvl]['inner_width'];
21913			$outerfilled = $this->y; // Keep track of how far down the outer DIV bgcolor is painted (NB rowspans)
21914			$this->outerfilled = $this->y;
21915			$this->colsums = [];
21916		} else {
21917			$x0 = $this->x;
21918			$y0 = $this->y;
21919			$right = $x0 + $table['w'];
21920		}
21921
21922		if ($this->table_rotate) {
21923			$temppgwidth = $this->tbrot_maxw;
21924			$this->PageBreakTrigger = $pagetrigger = $y0 + ($this->blk[$this->blklvl]['inner_width']);
21925			if ($level == 1) {
21926				$this->tbrot_y0 = $this->y - $adv - $table['margin']['T'];
21927				$this->tbrot_x0 = $this->x;
21928				$this->tbrot_w = $table['w'];
21929				if ($table['borders_separate']) {
21930					$this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
21931				} else {
21932					$this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['max_cell_border_width']['T'];
21933				}
21934			}
21935		} else {
21936			$this->PageBreakTrigger = $pagetrigger = ($this->h - $this->bMargin);
21937			if ($level == 1) {
21938				$temppgwidth = $this->blk[$this->blklvl]['inner_width'];
21939				if (isset($table['a']) and ( $table['w'] < $this->blk[$this->blklvl]['inner_width'])) {
21940					if ($table['a'] == 'C') {
21941						$x0 += ((($right - $x0) - $table['w']) / 2);
21942					} elseif ($table['a'] == 'R') {
21943						$x0 = $right - $table['w'];
21944					}
21945				}
21946			} else {
21947				$temppgwidth = $table['w'];
21948			}
21949		}
21950		if (!isset($table['overflow'])) {
21951			$table['overflow'] = null;
21952		}
21953		if ($table['overflow'] == 'hidden' && $level == 1 && !$this->table_rotate && !$this->ColActive) {
21954			// Bounding rectangle to clip
21955			$this->tableClipPath = sprintf('q %.3F %.3F %.3F %.3F re W n', $x0 * Mpdf::SCALE, $this->h * Mpdf::SCALE, $this->blk[$this->blklvl]['inner_width'] * Mpdf::SCALE, -$this->h * Mpdf::SCALE);
21956			$this->writer->write($this->tableClipPath);
21957		} else {
21958			$this->tableClipPath = '';
21959		}
21960
21961
21962		if ($table['borders_separate']) {
21963			$indent = $table['margin']['L'] + $table['border_details']['L']['w'] + $table['padding']['L'] + $table['border_spacing_H'] / 2;
21964		} else {
21965			$indent = $table['margin']['L'] + $table['max_cell_border_width']['L'] / 2;
21966		}
21967		$x0 += $indent;
21968
21969		$returny = 0;
21970		$lastCol = 0;
21971		$tableheader = [];
21972		$tablefooter = [];
21973		$tableheaderrowheight = 0;
21974		$tablefooterrowheight = 0;
21975		$footery = 0;
21976
21977		// mPD 3.0 Set the Page & Column where table starts
21978		if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
21979			$tablestartpage = 'EVEN';
21980		} elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD
21981			$tablestartpage = 'ODD';
21982		} else {
21983			$tablestartpage = '';
21984		}
21985		if ($this->ColActive) {
21986			$tablestartcolumn = $this->CurrCol;
21987		} else {
21988			$tablestartcolumn = '';
21989		}
21990
21991		$y = $h = 0;
21992		for ($i = 0; $i < $numrows; $i++) { // Rows
21993			if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i] && $level == 1) {
21994				$tablefooterrowheight += $table['hr'][$i];
21995				$tablefooter[$i][0]['trbackground-images'] = $table['trbackground-images'][$i];
21996				$tablefooter[$i][0]['trgradients'] = $table['trgradients'][$i];
21997				$tablefooter[$i][0]['trbgcolor'] = $table['bgcolor'][$i];
21998				for ($j = $startcol; $j < $numcols; $j++) { // Columns
21999					if (isset($cells[$i][$j]) && $cells[$i][$j]) {
22000						$cell = &$cells[$i][$j];
22001						if ($split) {
22002							if ($table['colPg'][$j] != $splitpg) {
22003								continue;
22004							}
22005							list($x, $w) = $this->_splitTableGetWidth($table, $i, $j);
22006							$js = $j - $startcol;
22007						} else {
22008							list($x, $w) = $this->_tableGetWidth($table, $i, $j);
22009							$js = $j;
22010						}
22011
22012						list($y, $h) = $this->_tableGetHeight($table, $i, $j);
22013						$x += $x0;
22014						$y += $y0;
22015						// Get info of tfoot ==>> table footer
22016						$tablefooter[$i][$js]['x'] = $x;
22017						$tablefooter[$i][$js]['y'] = $y;
22018						$tablefooter[$i][$js]['h'] = $h;
22019						$tablefooter[$i][$js]['w'] = $w;
22020						if (isset($cell['textbuffer'])) {
22021							$tablefooter[$i][$js]['textbuffer'] = $cell['textbuffer'];
22022						} else {
22023							$tablefooter[$i][$js]['textbuffer'] = '';
22024						}
22025						$tablefooter[$i][$js]['a'] = $cell['a'];
22026						$tablefooter[$i][$js]['R'] = $cell['R'];
22027						$tablefooter[$i][$js]['va'] = $cell['va'];
22028						$tablefooter[$i][$js]['mih'] = $cell['mih'];
22029						if (isset($cell['gradient'])) {
22030							$tablefooter[$i][$js]['gradient'] = $cell['gradient']; // *BACKGROUNDS*
22031						}
22032						if (isset($cell['background-image'])) {
22033							$tablefooter[$i][$js]['background-image'] = $cell['background-image']; // *BACKGROUNDS*
22034						}
22035
22036						// CELL FILL BGCOLOR
22037						if (!$this->simpleTables) {
22038							if ($this->packTableData) {
22039								$c = $this->_unpackCellBorder($cell['borderbin']);
22040								$tablefooter[$i][$js]['border'] = $c['border'];
22041								$tablefooter[$i][$js]['border_details'] = $c['border_details'];
22042							} else {
22043								$tablefooter[$i][$js]['border'] = $cell['border'];
22044								$tablefooter[$i][$js]['border_details'] = $cell['border_details'];
22045							}
22046						} elseif ($this->simpleTables) {
22047							$tablefooter[$i][$js]['border'] = $table['simple']['border'];
22048							$tablefooter[$i][$js]['border_details'] = $table['simple']['border_details'];
22049						}
22050						$tablefooter[$i][$js]['bgcolor'] = $cell['bgcolor'];
22051						$tablefooter[$i][$js]['padding'] = $cell['padding'];
22052						if (isset($cell['rowspan'])) {
22053							$tablefooter[$i][$js]['rowspan'] = $cell['rowspan'];
22054						}
22055						if (isset($cell['colspan'])) {
22056							$tablefooter[$i][$js]['colspan'] = $cell['colspan'];
22057						}
22058						if (isset($cell['direction'])) {
22059							$tablefooter[$i][$js]['direction'] = $cell['direction'];
22060						}
22061						if (isset($cell['cellLineHeight'])) {
22062							$tablefooter[$i][$js]['cellLineHeight'] = $cell['cellLineHeight'];
22063						}
22064						if (isset($cell['cellLineStackingStrategy'])) {
22065							$tablefooter[$i][$js]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];
22066						}
22067						if (isset($cell['cellLineStackingShift'])) {
22068							$tablefooter[$i][$js]['cellLineStackingShift'] = $cell['cellLineStackingShift'];
22069						}
22070					}
22071				}
22072			}
22073		}
22074
22075		if ($level == 1) {
22076			$this->writer->write('___TABLE___BACKGROUNDS' . $this->uniqstr);
22077		}
22078		$tableheaderadj = 0;
22079		$tablefooteradj = 0;
22080
22081		$tablestartpageno = $this->page;
22082
22083		// Draw Table Contents and Borders
22084		for ($i = 0; $i < $numrows; $i++) { // Rows
22085			if ($split && $startrow > 0) {
22086				$thnr = (isset($table['is_thead']) ? count($table['is_thead']) : 0);
22087				if ($i >= $thnr && $i < $startrow) {
22088					continue;
22089				}
22090				if ($i == $startrow) {
22091					$returny = $rety - $tableheaderrowheight;
22092				}
22093			}
22094
22095			// Get Maximum row/cell height in row - including rowspan>1 + 1 overlapping
22096			$maxrowheight = $this->_tableGetMaxRowHeight($table, $i);
22097
22098			$skippage = false;
22099			$newpagestarted = false;
22100			for ($j = $startcol; $j < $numcols; $j++) { // Columns
22101				if ($split) {
22102					if ($table['colPg'][$j] > $splitpg) {
22103						break;
22104					}
22105					$lastCol = $j;
22106				}
22107				if (isset($cells[$i][$j]) && $cells[$i][$j]) {
22108					$cell = &$cells[$i][$j];
22109					if ($split) {
22110						$lastCol = $j + (isset($cell['colspan']) ? ($cell['colspan'] - 1) : 0);
22111						list($x, $w) = $this->_splitTableGetWidth($table, $i, $j);
22112					} else {
22113						list($x, $w) = $this->_tableGetWidth($table, $i, $j);
22114					}
22115
22116					list($y, $h) = $this->_tableGetHeight($table, $i, $j);
22117					$x += $x0;
22118					$y += $y0;
22119					$y -= $returny;
22120
22121					if ($table['borders_separate']) {
22122						if (!empty($tablefooter) || $i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) {
22123							$extra = $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
22124							// $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V']/2;
22125						} else {
22126							$extra = $table['border_spacing_V'] / 2;
22127						}
22128					} else {
22129						$extra = $table['max_cell_border_width']['B'] / 2;
22130					}
22131
22132					if ($j == $startcol && ((($y + $maxrowheight + $extra ) > ($pagetrigger + 0.001)) || (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && ($y + $maxrowheight + $tablefooterrowheight + $extra) > $pagetrigger) && ($this->tableLevel == 1 && $i < ($numrows - $table['headernrows']))) && ($y0 > 0 || $x0 > 0) && !$this->InFooter && $this->autoPageBreak) {
22133						if (!$skippage) {
22134							$finalSpread = true;
22135							$firstSpread = true;
22136							if ($split) {
22137								for ($t = $startcol; $t < $numcols; $t++) {
22138									// Are there more columns to print on a next page?
22139									if ($table['colPg'][$t] > $splitpg) {
22140										$finalSpread = false;
22141										break;
22142									}
22143								}
22144								if ($startcol > 0) {
22145									$firstSpread = false;
22146								}
22147							}
22148
22149							if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {
22150								$this->y = $y;
22151								$ya = $this->y;
22152								$this->TableHeaderFooter($tablefooter, $tablestartpage, $tablestartcolumn, 'F', $level, $firstSpread, $finalSpread);
22153								if ($this->table_rotate) {
22154									$this->tbrot_h += $this->y - $ya;
22155								}
22156								$tablefooteradj = $this->y - $ya;
22157							}
22158							$y -= $y0;
22159							$returny += $y;
22160
22161							$oldcolumn = $this->CurrCol;
22162							if ($this->AcceptPageBreak()) {
22163								$newpagestarted = true;
22164								$this->y = $y + $y0;
22165
22166								// Move down to account for border-spacing or
22167								// extra half border width in case page breaks in middle
22168								if ($i > 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {
22169									if ($table['borders_separate']) {
22170										$adv = $table['border_spacing_V'] / 2;
22171										// If table footer
22172										if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {
22173											$adv += ($table['padding']['B'] + $table['border_details']['B']['w']);
22174										}
22175									} else {
22176										$maxbwtop = 0;
22177										$maxbwbottom = 0;
22178										if (!$this->simpleTables) {
22179											if (!empty($tablefooter)) {
22180												$maxbwbottom = $table['max_cell_border_width']['B'];
22181											} else {
22182												$brow = $i - 1;
22183												for ($ctj = 0; $ctj < $numcols; $ctj++) {
22184													if (isset($cells[$brow][$ctj]) && $cells[$brow][$ctj]) {
22185														if ($this->packTableData) {
22186															list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$brow][$ctj]['borderbin']);
22187														} else {
22188															$bb = $cells[$brow][$ctj]['border_details']['B']['w'];
22189														}
22190														$maxbwbottom = max($maxbwbottom, $bb);
22191													}
22192												}
22193											}
22194											if (!empty($tableheader)) {
22195												$maxbwtop = $table['max_cell_border_width']['T'];
22196											} else {
22197												$trow = $i - 1;
22198												for ($ctj = 0; $ctj < $numcols; $ctj++) {
22199													if (isset($cells[$trow][$ctj]) && $cells[$trow][$ctj]) {
22200														if ($this->packTableData) {
22201															list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$trow][$ctj]['borderbin']);
22202														} else {
22203															$bt = $cells[$trow][$ctj]['border_details']['T']['w'];
22204														}
22205														$maxbwtop = max($maxbwtop, $bt);
22206													}
22207												}
22208											}
22209										} elseif ($this->simpleTables) {
22210											$maxbwtop = $table['simple']['border_details']['T']['w'];
22211											$maxbwbottom = $table['simple']['border_details']['B']['w'];
22212										}
22213										$adv = $maxbwbottom / 2;
22214									}
22215									$this->y += $adv;
22216								}
22217
22218								// Rotated table split over pages - needs this->y for borders/backgrounds
22219								if ($i > 0 && $this->table_rotate && $level == 1) {
22220									// 		$this->y = $y0 + $this->tbrot_w;
22221								}
22222
22223								if ($this->tableClipPath) {
22224									$this->writer->write("Q");
22225								}
22226
22227								$bx = $x0;
22228								$by = $y0;
22229
22230								if ($table['borders_separate']) {
22231									$bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);
22232									if ($tablestartpageno != $this->page) { // IF already broken across a previous pagebreak
22233										$by += $table['max_cell_border_width']['T'] / 2;
22234										if (empty($tableheader)) {
22235											$by -= ($table['border_spacing_V'] / 2);
22236										}
22237									} else {
22238										$by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);
22239									}
22240								} elseif ($tablestartpageno != $this->page && !empty($tableheader)) {
22241									$by += $maxbwtop / 2;
22242								}
22243
22244								$by -= $tableheaderadj;
22245								$bh = $this->y - $by + $tablefooteradj;
22246								if (!$table['borders_separate']) {
22247									$bh -= $adv;
22248								}
22249								if ($split) {
22250									$bw = 0;
22251									for ($t = $startcol; $t < $numcols; $t++) {
22252										if ($table['colPg'][$t] == $splitpg) {
22253											$bw += $table['wc'][$t];
22254										}
22255										if ($table['colPg'][$t] > $splitpg) {
22256											break;
22257										}
22258									}
22259									if ($table['borders_separate']) {
22260										if ($firstSpread) {
22261											$bw += $table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'];
22262										} else {
22263											$bx += ($table['padding']['L'] + $table['border_details']['L']['w']);
22264											$bw += $table['border_spacing_H'];
22265										}
22266										if ($finalSpread) {
22267											$bw += $table['padding']['R'] + $table['border_details']['R']['w'] / 2 + $table['border_spacing_H'];
22268										}
22269									}
22270								} else {
22271									$bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
22272								}
22273
22274								if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tablefooter) && $i > 0 && $table['border_details']['B']['w']) {
22275									$prevDrawColor = $this->DrawColor;
22276									$lw = $this->LineWidth;
22277									$this->SetLineWidth($this->splitTableBorderWidth);
22278									$this->SetDColor($table['border_details']['B']['c']);
22279									$this->SetLineJoin(0);
22280									$this->SetLineCap(0);
22281									$blx = $bx;
22282									$blw = $bw;
22283									if (!$table['borders_separate']) {
22284										$blx -= ($table['max_cell_border_width']['L'] / 2);
22285										$blw += ($table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2);
22286									}
22287									$this->Line($blx, $this->y + ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y + ($this->splitTableBorderWidth / 2));
22288									$this->DrawColor = $prevDrawColor;
22289									$this->writer->write($this->DrawColor);
22290									$this->SetLineWidth($lw);
22291									$this->SetLineJoin(2);
22292									$this->SetLineCap(2);
22293								}
22294
22295								if (!$this->ColActive && ($i > 0 || $j > 0)) {
22296									if (isset($table['bgcolor'][-1])) {
22297										$color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings);
22298										if ($color) {
22299											if (!$table['borders_separate']) {
22300												$bh -= $table['max_cell_border_width']['B'] / 2;
22301											}
22302											$this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color];
22303										}
22304									}
22305
22306									/* -- BACKGROUNDS -- */
22307									if (isset($table['gradient'])) {
22308										$g = $this->gradient->parseBackgroundGradient($table['gradient']);
22309										if ($g) {
22310											$this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
22311										}
22312									}
22313
22314									if (isset($table['background-image'])) {
22315										if ($table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {
22316											$g = $this->gradient->parseMozGradient($table['background-image']['gradient']);
22317											if ($g) {
22318												$this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
22319											}
22320										} else {
22321											$image_id = $table['background-image']['image_id'];
22322											$orig_w = $table['background-image']['orig_w'];
22323											$orig_h = $table['background-image']['orig_h'];
22324											$x_pos = $table['background-image']['x_pos'];
22325											$y_pos = $table['background-image']['y_pos'];
22326											$x_repeat = $table['background-image']['x_repeat'];
22327											$y_repeat = $table['background-image']['y_repeat'];
22328											$resize = $table['background-image']['resize'];
22329											$opacity = $table['background-image']['opacity'];
22330											$itype = $table['background-image']['itype'];
22331											$this->tableBackgrounds[$level * 9 + 2][] = ['x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];
22332										}
22333									}
22334									/* -- END BACKGROUNDS -- */
22335								}
22336
22337								// $this->AcceptPageBreak() has moved tablebuffer to $this->pages content
22338								if ($this->tableBackgrounds) {
22339									$s = $this->PrintTableBackgrounds();
22340									if ($this->bufferoutput) {
22341										$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer);
22342										$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer);
22343									} else {
22344										$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
22345										$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]);
22346									}
22347									$this->tableBackgrounds = [];
22348								}
22349
22350								if ($split) {
22351									if ($i == 0 && $j == 0) {
22352										$y0 = -1;
22353									} elseif ($finalSpread) {
22354										$splitpg = 0;
22355										$startcol = 0;
22356										$startrow = $i;
22357									} else {
22358										$splitpg++;
22359										$startcol = $t;
22360										$returny -= $y;
22361									}
22362									return [false, $startrow, $startcol, $splitpg, $returny, $y0];
22363								}
22364
22365								$this->AddPage($this->CurOrientation);
22366
22367								$this->writer->write('___TABLE___BACKGROUNDS' . $this->uniqstr);
22368
22369
22370								if ($this->tableClipPath) {
22371									$this->writer->write($this->tableClipPath);
22372								}
22373
22374								// Added to correct for OddEven Margins
22375								$x = $x + $this->MarginCorrection;
22376								$x0 = $x0 + $this->MarginCorrection;
22377
22378								if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tableheader) && $i > 0 && $table['border_details']['T']['w']) {
22379									$prevDrawColor = $this->DrawColor;
22380									$lw = $this->LineWidth;
22381									$this->SetLineWidth($this->splitTableBorderWidth);
22382									$this->SetDColor($table['border_details']['T']['c']);
22383									$this->SetLineJoin(0);
22384									$this->SetLineCap(0);
22385									$blx += $this->MarginCorrection;
22386									$this->Line($blx, $this->y - ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y - ($this->splitTableBorderWidth / 2));
22387									$this->DrawColor = $prevDrawColor;
22388									$this->writer->write($this->DrawColor);
22389									$this->SetLineWidth($lw);
22390									$this->SetLineJoin(2);
22391									$this->SetLineCap(2);
22392								}
22393
22394								// Move down to account for half of top border-spacing or
22395								// extra half border width in case page was broken in middle
22396								if ($i > 0 && !$this->table_rotate && $level == 1 && $table['headernrows'] == 0) {
22397									if ($table['borders_separate']) {
22398										$adv = $table['border_spacing_V'] / 2;
22399									} else {
22400										$maxbwtop = 0;
22401										for ($ctj = 0; $ctj < $numcols; $ctj++) {
22402											if (isset($cells[$i][$ctj]) && $cells[$i][$ctj]) {
22403												if (!$this->simpleTables) {
22404													if ($this->packTableData) {
22405														list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$i][$ctj]['borderbin']);
22406													} else {
22407														$bt = $cells[$i][$ctj]['border_details']['T']['w'];
22408													}
22409													$maxbwtop = max($maxbwtop, $bt);
22410												} elseif ($this->simpleTables) {
22411													$maxbwtop = max($maxbwtop, $table['simple']['border_details']['T']['w']);
22412												}
22413											}
22414										}
22415										$adv = $maxbwtop / 2;
22416									}
22417									$this->y += $adv;
22418								}
22419
22420
22421								if ($this->table_rotate) {
22422									$this->tbrot_x0 = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'];
22423									if ($table['borders_separate']) {
22424										$this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
22425									} else {
22426										$this->tbrot_h = $table['margin']['T'] + $table['max_cell_border_width']['T'];
22427									}
22428									$this->tbrot_y0 = $this->y;
22429									$pagetrigger = $y0 - $tableheaderadj + ($this->blk[$this->blklvl]['inner_width']);
22430								} else {
22431									$pagetrigger = $this->PageBreakTrigger;
22432								}
22433
22434								if ($this->kwt_saved && $level == 1) {
22435									$this->kwt_moved = true;
22436								}
22437
22438
22439								if (!empty($tableheader)) {
22440									$ya = $this->y;
22441									$this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);
22442									if ($this->table_rotate) {
22443										$this->tbrot_h = $this->y - $ya;
22444									}
22445									$tableheaderadj = $this->y - $ya;
22446								} elseif ($i == 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {
22447									// Advance down page
22448									if ($table['borders_separate']) {
22449										$adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];
22450									} else {
22451										$adv = $table['max_cell_border_width']['T'] / 2;
22452									}
22453									if ($adv) {
22454										if ($this->table_rotate) {
22455											$this->y += ($adv);
22456										} else {
22457											$this->DivLn($adv, $this->blklvl, true);
22458										}
22459									}
22460								}
22461
22462								$outerfilled = 0;
22463								$y = $y0 = $this->y;
22464							}
22465
22466							/* -- COLUMNS -- */
22467							// COLS
22468							// COLUMN CHANGE
22469							if ($this->CurrCol != $oldcolumn) {
22470								// Added to correct for Columns
22471								$x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
22472								$x0 += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
22473								if ($this->CurrCol == 0) {  // just added a page - possibly with tableheader
22474									$y0 = $this->y;  // this->y0 is global used by Columns - $y0 is internal to tablewrite
22475								} else {
22476									$y0 = $this->y0;  // this->y0 is global used by Columns - $y0 is internal to tablewrite
22477								}
22478								$y = $y0;
22479								$outerfilled = 0;
22480								if ($this->CurrCol != 0 && ($this->keepColumns && $this->ColActive) && !empty($tableheader) && $i > 0) {
22481									$this->x = $x;
22482									$this->y = $y;
22483									$this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);
22484									$y0 = $y = $this->y;
22485								}
22486							}
22487							/* -- END COLUMNS -- */
22488						}
22489						$skippage = true;
22490					}
22491
22492					$this->x = $x;
22493					$this->y = $y;
22494
22495					if ($this->kwt_saved && $level == 1) {
22496						$this->printkwtbuffer();
22497						$x0 = $x = $this->x;
22498						$y0 = $y = $this->y;
22499						$this->kwt_moved = false;
22500						$this->kwt_saved = false;
22501					}
22502
22503
22504					// Set the Page & Column where table actually starts
22505					if ($i == 0 && $j == 0 && $level == 1) {
22506						if (($this->mirrorMargins) && (($this->page) % 2 == 0)) {    // EVEN
22507							$tablestartpage = 'EVEN';
22508						} elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) {    // ODD
22509							$tablestartpage = 'ODD';
22510						} else {
22511							$tablestartpage = '';
22512						}
22513						$tablestartpageno = $this->page;
22514						if ($this->ColActive) {
22515							$tablestartcolumn = $this->CurrCol;
22516						} // *COLUMNS*
22517					}
22518
22519					// ALIGN
22520					$align = $cell['a'];
22521
22522					/* -- COLUMNS -- */
22523					// If outside columns, this is done in PaintDivBB
22524					if ($this->ColActive) {
22525						// OUTER FILL BGCOLOR of DIVS
22526						if ($this->blklvl > 0 && ($j == 0) && !$this->table_rotate && $level == 1) {
22527							$firstblockfill = $this->GetFirstBlockFill();
22528							if ($firstblockfill && $this->blklvl >= $firstblockfill) {
22529								$divh = $maxrowheight;
22530								// Last row
22531								if ((!isset($cell['rowspan']) && $i == $numrows - 1) || (isset($cell['rowspan']) && (($i == $numrows - 1 && $cell['rowspan'] < 2) || ($cell['rowspan'] > 1 && ($i + $cell['rowspan'] - 1) == $numrows - 1)))) {
22532									if ($table['borders_separate']) {
22533										$adv = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
22534									} else {
22535										$adv = $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;
22536									}
22537									$divh += $adv;  // last row: fill bottom half of bottom border (y advanced at end)
22538								}
22539
22540								if (($this->y + $divh) > $outerfilled) { // if not already painted by previous rowspan
22541									$bak_x = $this->x;
22542									$bak_y = $this->y;
22543									if ($outerfilled > $this->y) {
22544										$divh = ($this->y + $divh) - $outerfilled;
22545										$this->y = $outerfilled;
22546									}
22547
22548									$this->DivLn($divh, -3, false);
22549									$outerfilled = $this->y + $divh;
22550									// Reset current block fill
22551									$bcor = $this->blk[$this->blklvl]['bgcolorarray'];
22552									if ($bcor) {
22553										$this->SetFColor($bcor);
22554									}
22555									$this->x = $bak_x;
22556									$this->y = $bak_y;
22557								}
22558							}
22559						}
22560					}
22561
22562					// TABLE BACKGROUND FILL BGCOLOR - for cellSpacing
22563					if ($this->ColActive) {
22564						if ($table['borders_separate']) {
22565							$fill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;
22566							if ($fill) {
22567								$color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
22568								if ($color) {
22569									$xadj = ($table['border_spacing_H'] / 2);
22570									$yadj = ($table['border_spacing_V'] / 2);
22571									$wadj = $table['border_spacing_H'];
22572									$hadj = $table['border_spacing_V'];
22573									if ($i == 0) {  // Top
22574										$yadj += $table['padding']['T'] + $table['border_details']['T']['w'];
22575										$hadj += $table['padding']['T'] + $table['border_details']['T']['w'];
22576									}
22577									if ($j == 0) {  // Left
22578										$xadj += $table['padding']['L'] + $table['border_details']['L']['w'];
22579										$wadj += $table['padding']['L'] + $table['border_details']['L']['w'];
22580									}
22581									if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) { // Bottom
22582										$hadj += $table['padding']['B'] + $table['border_details']['B']['w'];
22583									}
22584									if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols) || (!isset($cell['colspan']) && ($j + 1) == $numcols)) { // Right
22585										$wadj += $table['padding']['R'] + $table['border_details']['R']['w'];
22586									}
22587									$this->SetFColor($color);
22588									$this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');
22589								}
22590							}
22591						}
22592					}
22593					/* -- END COLUMNS -- */
22594
22595					if ($table['empty_cells'] != 'hide' || !empty($cell['textbuffer']) || (isset($cell['nestedcontent']) && $cell['nestedcontent']) || !$table['borders_separate']) {
22596						$paintcell = true;
22597					} else {
22598						$paintcell = false;
22599					}
22600
22601					// Set Borders
22602					$bord = 0;
22603					$bord_det = [];
22604
22605					if (!$this->simpleTables) {
22606						if ($this->packTableData) {
22607							$c = $this->_unpackCellBorder($cell['borderbin']);
22608							$bord = $c['border'];
22609							$bord_det = $c['border_details'];
22610						} else {
22611							$bord = $cell['border'];
22612							$bord_det = $cell['border_details'];
22613						}
22614					} elseif ($this->simpleTables) {
22615						$bord = $table['simple']['border'];
22616						$bord_det = $table['simple']['border_details'];
22617					}
22618
22619					// TABLE ROW OR CELL FILL BGCOLOR
22620					$fill = 0;
22621					if (isset($cell['bgcolor']) && $cell['bgcolor'] && $cell['bgcolor'] != 'transparent') {
22622						$fill = $cell['bgcolor'];
22623						$leveladj = 6;
22624					} elseif (isset($table['bgcolor'][$i]) && $table['bgcolor'][$i] && $table['bgcolor'][$i] != 'transparent') { // Row color
22625						$fill = $table['bgcolor'][$i];
22626						$leveladj = 3;
22627					}
22628					if ($fill && $paintcell) {
22629						$color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
22630						if ($color) {
22631							if ($table['borders_separate']) {
22632								if ($this->ColActive) {
22633									$this->SetFColor($color);
22634									$this->Rect($x + ($table['border_spacing_H'] / 2), $y + ($table['border_spacing_V'] / 2), $w - $table['border_spacing_H'], $h - $table['border_spacing_V'], 'F');
22635								} else {
22636									$this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => ($x + ($table['border_spacing_H'] / 2)), 'y' => ($y + ($table['border_spacing_V'] / 2)), 'w' => ($w - $table['border_spacing_H']), 'h' => ($h - $table['border_spacing_V']), 'col' => $color];
22637								}
22638							} else {
22639								if ($this->ColActive) {
22640									$this->SetFColor($color);
22641									$this->Rect($x, $y, $w, $h, 'F');
22642								} else {
22643									$this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color];
22644								}
22645							}
22646						}
22647					}
22648
22649					/* -- BACKGROUNDS -- */
22650					if (isset($cell['gradient']) && $cell['gradient'] && $paintcell) {
22651						$g = $this->gradient->parseBackgroundGradient($cell['gradient']);
22652						if ($g) {
22653							if ($table['borders_separate']) {
22654								$px = $x + ($table['border_spacing_H'] / 2);
22655								$py = $y + ($table['border_spacing_V'] / 2);
22656								$pw = $w - $table['border_spacing_H'];
22657								$ph = $h - $table['border_spacing_V'];
22658							} else {
22659								$px = $x;
22660								$py = $y;
22661								$pw = $w;
22662								$ph = $h;
22663							}
22664							if ($this->ColActive) {
22665								$this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
22666							} else {
22667								$this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
22668							}
22669						}
22670					}
22671
22672					if (isset($cell['background-image']) && $paintcell) {
22673						if (isset($cell['background-image']['gradient']) && $cell['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $cell['background-image']['gradient'])) {
22674							$g = $this->gradient->parseMozGradient($cell['background-image']['gradient']);
22675							if ($g) {
22676								if ($table['borders_separate']) {
22677									$px = $x + ($table['border_spacing_H'] / 2);
22678									$py = $y + ($table['border_spacing_V'] / 2);
22679									$pw = $w - $table['border_spacing_H'];
22680									$ph = $h - $table['border_spacing_V'];
22681								} else {
22682									$px = $x;
22683									$py = $y;
22684									$pw = $w;
22685									$ph = $h;
22686								}
22687								if ($this->ColActive) {
22688									$this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
22689								} else {
22690									$this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
22691								}
22692							}
22693						} elseif (isset($cell['background-image']['image_id']) && $cell['background-image']['image_id']) { // Background pattern
22694							$n = count($this->patterns) + 1;
22695							if ($table['borders_separate']) {
22696								$px = $x + ($table['border_spacing_H'] / 2);
22697								$py = $y + ($table['border_spacing_V'] / 2);
22698								$pw = $w - $table['border_spacing_H'];
22699								$ph = $h - $table['border_spacing_V'];
22700							} else {
22701								$px = $x;
22702								$py = $y;
22703								$pw = $w;
22704								$ph = $h;
22705							}
22706							if ($this->ColActive) {
22707								list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($cell['background-image']['orig_w'], $cell['background-image']['orig_h'], $pw, $ph, $cell['background-image']['resize'], $cell['background-image']['x_repeat'], $cell['background-image']['y_repeat']);
22708								$this->patterns[$n] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'pgh' => $this->h, 'image_id' => $cell['background-image']['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $cell['background-image']['x_pos'], 'y_pos' => $cell['background-image']['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat];
22709								if ($cell['background-image']['opacity'] > 0 && $cell['background-image']['opacity'] < 1) {
22710									$opac = $this->SetAlpha($cell['background-image']['opacity'], 'Normal', true);
22711								} else {
22712									$opac = '';
22713								}
22714								$this->writer->write(sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $px * Mpdf::SCALE, ($this->h - $py) * Mpdf::SCALE, $pw * Mpdf::SCALE, -$ph * Mpdf::SCALE));
22715							} else {
22716								$image_id = $cell['background-image']['image_id'];
22717								$orig_w = $cell['background-image']['orig_w'];
22718								$orig_h = $cell['background-image']['orig_h'];
22719								$x_pos = $cell['background-image']['x_pos'];
22720								$y_pos = $cell['background-image']['y_pos'];
22721								$x_repeat = $cell['background-image']['x_repeat'];
22722								$y_repeat = $cell['background-image']['y_repeat'];
22723								$resize = $cell['background-image']['resize'];
22724								$opacity = $cell['background-image']['opacity'];
22725								$itype = $cell['background-image']['itype'];
22726								$this->tableBackgrounds[$level * 9 + 8][] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];
22727							}
22728						}
22729					}
22730					/* -- END BACKGROUNDS -- */
22731
22732					if (isset($cell['colspan']) && $cell['colspan'] > 1) {
22733						$ccolsp = $cell['colspan'];
22734					} else {
22735						$ccolsp = 1;
22736					}
22737					if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {
22738						$crowsp = $cell['rowspan'];
22739					} else {
22740						$crowsp = 1;
22741					}
22742
22743
22744					// but still need to do this for repeated headers...
22745					if (!$table['borders_separate'] && $this->tabletheadjustfinished && !$this->simpleTables) {
22746						if (isset($table['topntail']) && $table['topntail']) {
22747							$bord_det['T'] = $this->border_details($table['topntail']);
22748							$bord_det['T']['w'] /= $this->shrin_k;
22749							$this->setBorder($bord, Border::TOP);
22750						}
22751						if (isset($table['thead-underline']) && $table['thead-underline']) {
22752							$bord_det['T'] = $this->border_details($table['thead-underline']);
22753							$bord_det['T']['w'] /= $this->shrin_k;
22754							$this->setBorder($bord, Border::TOP);
22755						}
22756					}
22757
22758
22759					// Get info of first row ==>> table header
22760					// Use > 1 row if THEAD
22761					if (isset($table['is_thead'][$i]) && $table['is_thead'][$i] && $level == 1) {
22762						if ($j == 0) {
22763							$tableheaderrowheight += $table['hr'][$i];
22764						}
22765						$tableheader[$i][0]['trbackground-images'] = (isset($table['trbackground-images'][$i]) ? $table['trbackground-images'][$i] : null);
22766						$tableheader[$i][0]['trgradients'] = (isset($table['trgradients'][$i]) ? $table['trgradients'][$i] : null);
22767						$tableheader[$i][0]['trbgcolor'] = (isset($table['bgcolor'][$i]) ? $table['bgcolor'][$i] : null);
22768						$tableheader[$i][$j]['x'] = $x;
22769						$tableheader[$i][$j]['y'] = $y;
22770						$tableheader[$i][$j]['h'] = $h;
22771						$tableheader[$i][$j]['w'] = $w;
22772						if (isset($cell['textbuffer'])) {
22773							$tableheader[$i][$j]['textbuffer'] = $cell['textbuffer'];
22774						} else {
22775							$tableheader[$i][$j]['textbuffer'] = '';
22776						}
22777						$tableheader[$i][$j]['a'] = $cell['a'];
22778						$tableheader[$i][$j]['R'] = $cell['R'];
22779
22780						$tableheader[$i][$j]['va'] = $cell['va'];
22781						$tableheader[$i][$j]['mih'] = $cell['mih'];
22782						$tableheader[$i][$j]['gradient'] = (isset($cell['gradient']) ? $cell['gradient'] : null); // *BACKGROUNDS*
22783						$tableheader[$i][$j]['background-image'] = (isset($cell['background-image']) ? $cell['background-image'] : null); // *BACKGROUNDS*
22784						$tableheader[$i][$j]['rowspan'] = (isset($cell['rowspan']) ? $cell['rowspan'] : null);
22785						$tableheader[$i][$j]['colspan'] = (isset($cell['colspan']) ? $cell['colspan'] : null);
22786						$tableheader[$i][$j]['bgcolor'] = $cell['bgcolor'];
22787
22788						if (!$this->simpleTables) {
22789							$tableheader[$i][$j]['border'] = $bord;
22790							$tableheader[$i][$j]['border_details'] = $bord_det;
22791						} elseif ($this->simpleTables) {
22792							$tableheader[$i][$j]['border'] = $table['simple']['border'];
22793							$tableheader[$i][$j]['border_details'] = $table['simple']['border_details'];
22794						}
22795						$tableheader[$i][$j]['padding'] = $cell['padding'];
22796						if (isset($cell['direction'])) {
22797							$tableheader[$i][$j]['direction'] = $cell['direction'];
22798						}
22799						if (isset($cell['cellLineHeight'])) {
22800							$tableheader[$i][$j]['cellLineHeight'] = $cell['cellLineHeight'];
22801						}
22802						if (isset($cell['cellLineStackingStrategy'])) {
22803							$tableheader[$i][$j]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];
22804						}
22805						if (isset($cell['cellLineStackingShift'])) {
22806							$tableheader[$i][$j]['cellLineStackingShift'] = $cell['cellLineStackingShift'];
22807						}
22808					}
22809
22810					// CELL BORDER
22811					if ($bord) {
22812						if ($table['borders_separate'] && $paintcell) {
22813							$this->_tableRect($x + ($table['border_spacing_H'] / 2) + ($bord_det['L']['w'] / 2), $y + ($table['border_spacing_V'] / 2) + ($bord_det['T']['w'] / 2), $w - $table['border_spacing_H'] - ($bord_det['L']['w'] / 2) - ($bord_det['R']['w'] / 2), $h - $table['border_spacing_V'] - ($bord_det['T']['w'] / 2) - ($bord_det['B']['w'] / 2), $bord, $bord_det, false, $table['borders_separate']);
22814						} elseif (!$table['borders_separate']) {
22815							$this->_tableRect($x, $y, $w, $h, $bord, $bord_det, true, $table['borders_separate']);  // true causes buffer
22816						}
22817					}
22818
22819					// VERTICAL ALIGN
22820					if ($cell['R'] && intval($cell['R']) > 0 && intval($cell['R']) < 90 && isset($cell['va']) && $cell['va'] != 'B') {
22821						$cell['va'] = 'B';
22822					}
22823					if (!isset($cell['va']) || $cell['va'] == 'M') {
22824						$this->y += ($h - $cell['mih']) / 2;
22825					} elseif (isset($cell['va']) && $cell['va'] == 'B') {
22826						$this->y += $h - $cell['mih'];
22827					}
22828
22829					// NESTED CONTENT
22830					// TEXT (and nested tables)
22831
22832					$this->divwidth = $w;
22833					if (!empty($cell['textbuffer'])) {
22834						$this->cellTextAlign = $align;
22835						$this->cellLineHeight = $cell['cellLineHeight'];
22836						$this->cellLineStackingStrategy = $cell['cellLineStackingStrategy'];
22837						$this->cellLineStackingShift = $cell['cellLineStackingShift'];
22838						if ($level == 1) {
22839							if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {
22840								if (preg_match('/{colsum([0-9]*)[_]*}/', $cell['textbuffer'][0][0], $m)) {
22841									$rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$j]);
22842									$cell['textbuffer'][0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $cell['textbuffer'][0][0]);
22843								}
22844							} elseif (!isset($table['is_thead'][$i])) {
22845								if (isset($this->colsums[$j])) {
22846									$this->colsums[$j] += $this->toFloat($cell['textbuffer'][0][0]);
22847								} else {
22848									$this->colsums[$j] = $this->toFloat($cell['textbuffer'][0][0]);
22849								}
22850							}
22851						}
22852						$opy = $this->y;
22853						// mPDF ITERATION
22854						if ($this->iterationCounter) {
22855							foreach ($cell['textbuffer'] as $k => $t) {
22856								if (preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {
22857									$vname = '__' . $m[1] . '_';
22858									if (!isset($this->$vname)) {
22859										$this->$vname = 1;
22860									} else {
22861										$this->$vname++;
22862									}
22863									$cell['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $cell['textbuffer'][$k][0]);
22864								}
22865							}
22866						}
22867
22868
22869						if ($cell['R']) {
22870							$cellPtSize = $cell['textbuffer'][0][11] / $this->shrin_k;
22871							if (!$cellPtSize) {
22872								$cellPtSize = $this->default_font_size;
22873							}
22874							$cellFontHeight = ($cellPtSize / Mpdf::SCALE);
22875							$opx = $this->x;
22876							$angle = intval($cell['R']);
22877							// Only allow 45 to 89 degrees (when bottom-aligned) or exactly 90 or -90
22878							if ($angle > 90) {
22879								$angle = 90;
22880							} elseif ($angle > 0 && $angle < 45) {
22881								$angle = 45;
22882							} elseif ($angle < 0) {
22883								$angle = -90;
22884							}
22885							$offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);
22886							if (isset($cell['a']) && $cell['a'] == 'R') {
22887								$this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($cell['padding']['R'] + ($table['border_spacing_H'] / 2));
22888							} elseif (!isset($cell['a']) || $cell['a'] == 'C') {
22889								$this->x += ($w / 2) + ($offset);
22890							} else {
22891								$this->x += ($offset) + ($cellFontHeight / 3) + ($cell['padding']['L'] + ($table['border_spacing_H'] / 2));
22892							}
22893							$str = '';
22894							foreach ($cell['textbuffer'] as $t) {
22895								$str .= $t[0] . ' ';
22896							}
22897							$str = rtrim($str);
22898							if (!isset($cell['va']) || $cell['va'] == 'M') {
22899								$this->y -= ($h - $cell['mih']) / 2; // Undo what was added earlier VERTICAL ALIGN
22900								if ($angle > 0) {
22901									$this->y += (($h - $cell['mih']) / 2) + $cell['padding']['T'] + ($cell['mih'] - ($cell['padding']['T'] + $cell['padding']['B']));
22902								} elseif ($angle < 0) {
22903									$this->y += (($h - $cell['mih']) / 2) + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
22904								}
22905							} elseif (isset($cell['va']) && $cell['va'] == 'B') {
22906								$this->y -= $h - $cell['mih']; // Undo what was added earlier VERTICAL ALIGN
22907								if ($angle > 0) {
22908									$this->y += $h - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));
22909								} elseif ($angle < 0) {
22910									$this->y += $h - $cell['mih'] + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
22911								}
22912							} elseif (isset($cell['va']) && $cell['va'] == 'T') {
22913								if ($angle > 0) {
22914									$this->y += $cell['mih'] - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));
22915								} elseif ($angle < 0) {
22916									$this->y += ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
22917								}
22918							}
22919							$this->Rotate($angle, $this->x, $this->y);
22920							$s_fs = $this->FontSizePt;
22921							$s_f = $this->FontFamily;
22922							$s_st = $this->FontStyle;
22923							if (!empty($cell['textbuffer'][0][3])) { // Font Color
22924								$cor = $cell['textbuffer'][0][3];
22925								$this->SetTColor($cor);
22926							}
22927							$this->SetFont($cell['textbuffer'][0][4], $cell['textbuffer'][0][2], $cellPtSize, true, true);
22928
22929							$this->magic_reverse_dir($str, $this->directionality, $cell['textbuffer'][0][18]);
22930							$this->Text($this->x, $this->y, $str, $cell['textbuffer'][0][18], $cell['textbuffer'][0][8]); // textvar
22931							$this->Rotate(0);
22932							$this->SetFont($s_f, $s_st, $s_fs, true, true);
22933							$this->SetTColor(0);
22934							$this->x = $opx;
22935						} else {
22936							if (!$this->simpleTables) {
22937								if ($bord_det) {
22938									$btlw = $bord_det['L']['w'];
22939									$btrw = $bord_det['R']['w'];
22940									$bttw = $bord_det['T']['w'];
22941								} else {
22942									$btlw = 0;
22943									$btrw = 0;
22944									$bttw = 0;
22945								}
22946								if ($table['borders_separate']) {
22947									$xadj = $btlw + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);
22948									$wadj = $btlw + $btrw + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];
22949									$yadj = $bttw + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);
22950								} else {
22951									$xadj = $btlw / 2 + $cell['padding']['L'];
22952									$wadj = ($btlw + $btrw) / 2 + $cell['padding']['L'] + $cell['padding']['R'];
22953									$yadj = $bttw / 2 + $cell['padding']['T'];
22954								}
22955							} elseif ($this->simpleTables) {
22956								if ($table['borders_separate']) { // NB twice border width
22957									$xadj = $table['simple']['border_details']['L']['w'] + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);
22958									$wadj = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];
22959									$yadj = $table['simple']['border_details']['T']['w'] + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);
22960								} else {
22961									$xadj = $table['simple']['border_details']['L']['w'] / 2 + $cell['padding']['L'];
22962									$wadj = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + $cell['padding']['L'] + $cell['padding']['R'];
22963									$yadj = $table['simple']['border_details']['T']['w'] / 2 + $cell['padding']['T'];
22964								}
22965							}
22966							$this->decimal_offset = 0;
22967							if (substr($cell['a'], 0, 1) == 'D') {
22968								if (isset($cell['colspan']) && $cell['colspan'] > 1) {
22969									$this->cellTextAlign = $c['a'] = substr($cell['a'], 2, 1);
22970								} else {
22971									$smax = $table['decimal_align'][$j]['maxs0'];
22972									$d_content = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'];
22973									$this->decimal_offset = $smax;
22974									$extra = ($w - $d_content - $wadj);
22975									if ($extra > 0) {
22976										if (substr($cell['a'], 2, 1) == 'R') {
22977											$this->decimal_offset += $extra;
22978										} elseif (substr($cell['a'], 2, 1) == 'C') {
22979											$this->decimal_offset += ($extra) / 2;
22980										}
22981									}
22982								}
22983							}
22984							$this->divwidth = $w - $wadj;
22985							if ($this->divwidth == 0) {
22986								$this->divwidth = 0.0001;
22987							}
22988							$this->x += $xadj;
22989							$this->y += $yadj;
22990							$this->printbuffer($cell['textbuffer'], '', true, false, $cell['direction']);
22991						}
22992						$this->y = $opy;
22993					}
22994
22995					/* -- BACKGROUNDS -- */
22996					if (!$this->ColActive) {
22997						if (isset($table['trgradients'][$i]) && ($j == 0 || $table['borders_separate'])) {
22998							$g = $this->gradient->parseBackgroundGradient($table['trgradients'][$i]);
22999							if ($g) {
23000								$gx = $x0;
23001								$gy = $y;
23002								$gh = $h;
23003								$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
23004								if ($table['borders_separate']) {
23005									$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
23006									$clx = $x + ($table['border_spacing_H'] / 2);
23007									$cly = $y + ($table['border_spacing_V'] / 2);
23008									$clw = $w - $table['border_spacing_H'];
23009									$clh = $h - $table['border_spacing_V'];
23010									// Set clipping path
23011									$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
23012									$this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s];
23013								} else {
23014									$this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
23015								}
23016							}
23017						}
23018						if (isset($table['trbackground-images'][$i]) && ($j == 0 || $table['borders_separate'])) {
23019							if (isset($table['trbackground-images'][$i]['gradient']) && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['trbackground-images'][$i]['gradient'])) {
23020								$g = $this->gradient->parseMozGradient($table['trbackground-images'][$i]['gradient']);
23021								if ($g) {
23022									$gx = $x0;
23023									$gy = $y;
23024									$gh = $h;
23025									$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
23026									if ($table['borders_separate']) {
23027										$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
23028										$clx = $x + ($table['border_spacing_H'] / 2);
23029										$cly = $y + ($table['border_spacing_V'] / 2);
23030										$clw = $w - $table['border_spacing_H'];
23031										$clh = $h - $table['border_spacing_V'];
23032										// Set clipping path
23033										$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
23034										$this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s];
23035									} else {
23036										$this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
23037									}
23038								}
23039							} else {
23040								$image_id = $table['trbackground-images'][$i]['image_id'];
23041								$orig_w = $table['trbackground-images'][$i]['orig_w'];
23042								$orig_h = $table['trbackground-images'][$i]['orig_h'];
23043								$x_pos = $table['trbackground-images'][$i]['x_pos'];
23044								$y_pos = $table['trbackground-images'][$i]['y_pos'];
23045								$x_repeat = $table['trbackground-images'][$i]['x_repeat'];
23046								$y_repeat = $table['trbackground-images'][$i]['y_repeat'];
23047								$resize = $table['trbackground-images'][$i]['resize'];
23048								$opacity = $table['trbackground-images'][$i]['opacity'];
23049								$itype = $table['trbackground-images'][$i]['itype'];
23050								$clippath = '';
23051								$gx = $x0;
23052								$gy = $y;
23053								$gh = $h;
23054								$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
23055								if ($table['borders_separate']) {
23056									$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
23057									$clx = $x + ($table['border_spacing_H'] / 2);
23058									$cly = $y + ($table['border_spacing_V'] / 2);
23059									$clw = $w - $table['border_spacing_H'];
23060									$clh = $h - $table['border_spacing_V'];
23061									// Set clipping path
23062									$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
23063									$this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];
23064								} else {
23065									$this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];
23066								}
23067							}
23068						}
23069					}
23070
23071					/* -- END BACKGROUNDS -- */
23072
23073					// TABLE BORDER - if separate
23074					if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {
23075						$halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);
23076						$halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);
23077						$halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);
23078						$halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);
23079						$tbx = $x;
23080						$tby = $y;
23081						$tbw = $w;
23082						$tbh = $h;
23083						$tab_bord = 0;
23084
23085						$corner = '';
23086						if ($i == 0) {  // Top
23087							$tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);
23088							$tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);
23089							$this->setBorder($tab_bord, Border::TOP);
23090							$corner .= 'T';
23091						}
23092						if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows)) { // Bottom
23093							$tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);
23094							$this->setBorder($tab_bord, Border::BOTTOM);
23095							$corner .= 'B';
23096						}
23097						if ($j == 0) {  // Left
23098							$tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);
23099							$tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);
23100							$this->setBorder($tab_bord, Border::LEFT);
23101							$corner .= 'L';
23102						}
23103						if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols)) { // Right
23104							$tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);
23105							$this->setBorder($tab_bord, Border::RIGHT);
23106							$corner .= 'R';
23107						}
23108						$this->_tableRect($tbx, $tby, $tbw, $tbh, $tab_bord, $table['border_details'], false, $table['borders_separate'], 'table', $corner, $table['border_spacing_V'], $table['border_spacing_H']);
23109					}
23110
23111					unset($cell);
23112					// Reset values
23113					$this->Reset();
23114				}//end of (if isset(cells)...)
23115			}// end of columns
23116
23117			$newpagestarted = false;
23118			$this->tabletheadjustfinished = false;
23119
23120			/* -- COLUMNS -- */
23121			if ($this->ColActive) {
23122				if (!$this->table_keep_together && $i < $numrows - 1 && $level == 1) {
23123					$this->breakpoints[$this->CurrCol][] = $y + $h;
23124				} // mPDF 6
23125				if (count($this->cellBorderBuffer)) {
23126					$this->printcellbuffer();
23127				}
23128			}
23129			/* -- END COLUMNS -- */
23130
23131			if ($i == $numrows - 1) {
23132				$this->y = $y + $h;
23133			} // last row jump (update this->y position)
23134			if ($this->table_rotate && $level == 1) {
23135				$this->tbrot_h += $h;
23136			}
23137		} // end of rows
23138
23139		if (count($this->cellBorderBuffer)) {
23140			$this->printcellbuffer();
23141		}
23142
23143
23144		if ($this->tableClipPath) {
23145			$this->writer->write("Q");
23146		}
23147		$this->tableClipPath = '';
23148
23149		// Advance down page by half width of bottom border
23150		if ($table['borders_separate']) {
23151			$this->y += $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
23152		} else {
23153			$this->y += $table['max_cell_border_width']['B'] / 2;
23154		}
23155
23156		if ($table['borders_separate'] && $level == 1) {
23157			$this->tbrot_h += $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
23158		} elseif ($level == 1) {
23159			$this->tbrot_h += $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;
23160		}
23161
23162		$bx = $x0;
23163		$by = $y0;
23164		if ($table['borders_separate']) {
23165			$bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);
23166			if ($tablestartpageno != $this->page) { // IF broken across page
23167				$by += $table['max_cell_border_width']['T'] / 2;
23168				if (empty($tableheader)) {
23169					$by -= ($table['border_spacing_V'] / 2);
23170				}
23171			} elseif ($split && $startrow > 0 && empty($tableheader)) {
23172				$by -= ($table['border_spacing_V'] / 2);
23173			} else {
23174				$by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);
23175			}
23176		} elseif ($tablestartpageno != $this->page && !empty($tableheader)) {
23177			$by += $maxbwtop / 2;
23178		}
23179		$by -= $tableheaderadj;
23180		$bh = $this->y - $by;
23181		if (!$table['borders_separate']) {
23182			$bh -= $table['max_cell_border_width']['B'] / 2;
23183		}
23184
23185		if ($split) {
23186			$bw = 0;
23187			$finalSpread = true;
23188			for ($t = $startcol; $t < $numcols; $t++) {
23189				if ($table['colPg'][$t] == $splitpg) {
23190					$bw += $table['wc'][$t];
23191				}
23192				if ($table['colPg'][$t] > $splitpg) {
23193					$finalSpread = false;
23194					break;
23195				}
23196			}
23197			if ($startcol == 0) {
23198				$firstSpread = true;
23199			} else {
23200				$firstSpread = false;
23201			}
23202			if ($table['borders_separate']) {
23203				$bw += $table['border_spacing_H'];
23204				if ($firstSpread) {
23205					$bw += $table['padding']['L'] + $table['border_details']['L']['w'];
23206				} else {
23207					$bx += ($table['padding']['L'] + $table['border_details']['L']['w']);
23208				}
23209				if ($finalSpread) {
23210					$bw += $table['padding']['R'] + $table['border_details']['R']['w'];
23211				}
23212			}
23213		} else {
23214			$bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
23215		}
23216
23217		if (!$this->ColActive) {
23218			if (isset($table['bgcolor'][-1])) {
23219				$color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings);
23220				if ($color) {
23221					$this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color];
23222				}
23223			}
23224
23225			/* -- BACKGROUNDS -- */
23226			if (isset($table['gradient'])) {
23227				$g = $this->gradient->parseBackgroundGradient($table['gradient']);
23228				if ($g) {
23229					$this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
23230				}
23231			}
23232
23233			if (isset($table['background-image'])) {
23234				if (isset($table['background-image']['gradient']) && $table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {
23235					$g = $this->gradient->parseMozGradient($table['background-image']['gradient']);
23236					if ($g) {
23237						$this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];
23238					}
23239				} else {
23240					$image_id = $table['background-image']['image_id'];
23241					$orig_w = $table['background-image']['orig_w'];
23242					$orig_h = $table['background-image']['orig_h'];
23243					$x_pos = $table['background-image']['x_pos'];
23244					$y_pos = $table['background-image']['y_pos'];
23245					$x_repeat = $table['background-image']['x_repeat'];
23246					$y_repeat = $table['background-image']['y_repeat'];
23247					$resize = $table['background-image']['resize'];
23248					$opacity = $table['background-image']['opacity'];
23249					$itype = $table['background-image']['itype'];
23250					$this->tableBackgrounds[$level * 9 + 2][] = ['x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];
23251				}
23252			}
23253			/* -- END BACKGROUNDS -- */
23254		}
23255
23256		if ($this->tableBackgrounds && $level == 1) {
23257			$s = $this->PrintTableBackgrounds();
23258			if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) {
23259				$this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->tablebuffer);
23260				if ($level == 1) {
23261					$this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->tablebuffer);
23262				}
23263			} elseif ($this->bufferoutput) {
23264				$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer);
23265				if ($level == 1) {
23266					$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer);
23267				}
23268			} else {
23269				$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
23270				if ($level == 1) {
23271					$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]);
23272				}
23273			}
23274			$this->tableBackgrounds = [];
23275		}
23276
23277
23278		// TABLE BOTTOM MARGIN
23279		if ($table['margin']['B']) {
23280			if (!$this->table_rotate && $level == 1) {
23281				$this->DivLn($table['margin']['B'], $this->blklvl, true);  // collapsible
23282			} else {
23283				$this->y += ($table['margin']['B']);
23284			}
23285		}
23286
23287		if ($this->ColActive && $level == 1) {
23288			$this->breakpoints[$this->CurrCol][] = $this->y;
23289		} // *COLUMNS*
23290
23291		if ($split) {
23292			// Are there more columns to print on a next page?
23293			if ($lastCol < $numcols - 1) {
23294				$splitpg++;
23295				$startcol = $lastCol + 1;
23296				return [false, $startrow, $startcol, $splitpg, $returny, $y0];
23297			} else {
23298				return [true, 0, 0, 0, false, false];
23299			}
23300		}
23301	}
23302
23303	// END OF FUNCTION _tableWrite()
23304	/////////////////////////END OF TABLE CODE//////////////////////////////////
23305	/* -- END TABLES -- */
23306
23307	function _putextgstates()
23308	{
23309		for ($i = 1; $i <= count($this->extgstates); $i++) {
23310			$this->writer->object();
23311			$this->extgstates[$i]['n'] = $this->n;
23312			$this->writer->write('<</Type /ExtGState');
23313			foreach ($this->extgstates[$i]['parms'] as $k => $v) {
23314				$this->writer->write('/' . $k . ' ' . $v);
23315			}
23316			$this->writer->write('>>');
23317			$this->writer->write('endobj');
23318		}
23319	}
23320
23321	function SetProtection($permissions = [], $user_pass = '', $owner_pass = null, $length = 40)
23322	{
23323		$this->encrypted = $this->protection->setProtection($permissions, $user_pass, $owner_pass, $length);
23324	}
23325
23326	// =========================================
23327	// FROM class PDF_Bookmark
23328	function Bookmark($txt, $level = 0, $y = 0)
23329	{
23330		$txt = $this->purify_utf8_text($txt);
23331		if ($this->text_input_as_HTML) {
23332			$txt = $this->all_entities_to_utf8($txt);
23333		}
23334		if ($y == -1) {
23335			if (!$this->ColActive) {
23336				$y = $this->y;
23337			} else {
23338				$y = $this->y0;
23339			} // If columns are on - mark top of columns
23340		}
23341
23342		// else y is used as set, or =0 i.e. top of page
23343		// DIRECTIONALITY RTL
23344		$bmo = ['t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->page];
23345
23346		if ($this->keep_block_together) {
23347			// do nothing
23348		} elseif ($this->table_rotate) {
23349			$this->tbrot_BMoutlines[] = $bmo;
23350		} elseif ($this->kwt) {
23351			$this->kwt_BMoutlines[] = $bmo;
23352		} elseif ($this->ColActive) {
23353			$this->col_BMoutlines[] = $bmo;
23354		} else {
23355			$this->BMoutlines[] = $bmo;
23356		}
23357	}
23358
23359	/**
23360	 * Initiate, and Mark a place for the Table of Contents to be inserted
23361	 */
23362	function TOC(
23363		$tocfont = '',
23364		$tocfontsize = 0,
23365		$tocindent = 0,
23366		$resetpagenum = '',
23367		$pagenumstyle = '',
23368		$suppress = '',
23369		$toc_orientation = '',
23370		$TOCusePaging = true,
23371		$TOCuseLinking = false,
23372		$toc_id = 0,
23373		$tocoutdent = ''
23374	) {
23375
23376		$this->tableOfContents->TOC(
23377			$tocfont,
23378			$tocfontsize,
23379			$tocindent,
23380			$resetpagenum,
23381			$pagenumstyle,
23382			$suppress,
23383			$toc_orientation,
23384			$TOCusePaging,
23385			$TOCuseLinking,
23386			$toc_id,
23387			$tocoutdent
23388		);
23389	}
23390
23391	function TOCpagebreakByArray($a)
23392	{
23393		if (!is_array($a)) {
23394			$a = [];
23395		}
23396		$tocoutdent = (isset($a['tocoutdent']) ? $a['tocoutdent'] : (isset($a['outdent']) ? $a['outdent'] : ''));
23397		$TOCusePaging = (isset($a['TOCusePaging']) ? $a['TOCusePaging'] : (isset($a['paging']) ? $a['paging'] : true));
23398		$TOCuseLinking = (isset($a['TOCuseLinking']) ? $a['TOCuseLinking'] : (isset($a['links']) ? $a['links'] : ''));
23399		$toc_orientation = (isset($a['toc_orientation']) ? $a['toc_orientation'] : (isset($a['toc-orientation']) ? $a['toc-orientation'] : ''));
23400		$toc_mgl = (isset($a['toc_mgl']) ? $a['toc_mgl'] : (isset($a['toc-margin-left']) ? $a['toc-margin-left'] : ''));
23401		$toc_mgr = (isset($a['toc_mgr']) ? $a['toc_mgr'] : (isset($a['toc-margin-right']) ? $a['toc-margin-right'] : ''));
23402		$toc_mgt = (isset($a['toc_mgt']) ? $a['toc_mgt'] : (isset($a['toc-margin-top']) ? $a['toc-margin-top'] : ''));
23403		$toc_mgb = (isset($a['toc_mgb']) ? $a['toc_mgb'] : (isset($a['toc-margin-bottom']) ? $a['toc-margin-bottom'] : ''));
23404		$toc_mgh = (isset($a['toc_mgh']) ? $a['toc_mgh'] : (isset($a['toc-margin-header']) ? $a['toc-margin-header'] : ''));
23405		$toc_mgf = (isset($a['toc_mgf']) ? $a['toc_mgf'] : (isset($a['toc-margin-footer']) ? $a['toc-margin-footer'] : ''));
23406		$toc_ohname = (isset($a['toc_ohname']) ? $a['toc_ohname'] : (isset($a['toc-odd-header-name']) ? $a['toc-odd-header-name'] : ''));
23407		$toc_ehname = (isset($a['toc_ehname']) ? $a['toc_ehname'] : (isset($a['toc-even-header-name']) ? $a['toc-even-header-name'] : ''));
23408		$toc_ofname = (isset($a['toc_ofname']) ? $a['toc_ofname'] : (isset($a['toc-odd-footer-name']) ? $a['toc-odd-footer-name'] : ''));
23409		$toc_efname = (isset($a['toc_efname']) ? $a['toc_efname'] : (isset($a['toc-even-footer-name']) ? $a['toc-even-footer-name'] : ''));
23410		$toc_ohvalue = (isset($a['toc_ohvalue']) ? $a['toc_ohvalue'] : (isset($a['toc-odd-header-value']) ? $a['toc-odd-header-value'] : 0));
23411		$toc_ehvalue = (isset($a['toc_ehvalue']) ? $a['toc_ehvalue'] : (isset($a['toc-even-header-value']) ? $a['toc-even-header-value'] : 0));
23412		$toc_ofvalue = (isset($a['toc_ofvalue']) ? $a['toc_ofvalue'] : (isset($a['toc-odd-footer-value']) ? $a['toc-odd-footer-value'] : 0));
23413		$toc_efvalue = (isset($a['toc_efvalue']) ? $a['toc_efvalue'] : (isset($a['toc-even-footer-value']) ? $a['toc-even-footer-value'] : 0));
23414		$toc_preHTML = (isset($a['toc_preHTML']) ? $a['toc_preHTML'] : (isset($a['toc-preHTML']) ? $a['toc-preHTML'] : ''));
23415		$toc_postHTML = (isset($a['toc_postHTML']) ? $a['toc_postHTML'] : (isset($a['toc-postHTML']) ? $a['toc-postHTML'] : ''));
23416		$toc_bookmarkText = (isset($a['toc_bookmarkText']) ? $a['toc_bookmarkText'] : (isset($a['toc-bookmarkText']) ? $a['toc-bookmarkText'] : ''));
23417		$resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');
23418		$pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');
23419		$suppress = (isset($a['suppress']) ? $a['suppress'] : '');
23420		$orientation = (isset($a['orientation']) ? $a['orientation'] : '');
23421		$mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));
23422		$mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));
23423		$mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));
23424		$mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));
23425		$mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));
23426		$mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));
23427		$ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));
23428		$ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));
23429		$ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));
23430		$efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));
23431		$ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));
23432		$ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));
23433		$ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));
23434		$efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));
23435		$toc_id = (isset($a['toc_id']) ? $a['toc_id'] : (isset($a['name']) ? $a['name'] : 0));
23436		$pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));
23437		$toc_pagesel = (isset($a['toc_pagesel']) ? $a['toc_pagesel'] : (isset($a['toc-pageselector']) ? $a['toc-pageselector'] : ''));
23438		$sheetsize = (isset($a['sheetsize']) ? $a['sheetsize'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));
23439		$toc_sheetsize = (isset($a['toc_sheetsize']) ? $a['toc_sheetsize'] : (isset($a['toc-sheet-size']) ? $a['toc-sheet-size'] : ''));
23440
23441		$this->TOCpagebreak('', '', '', $TOCusePaging, $TOCuseLinking, $toc_orientation, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_preHTML, $toc_postHTML, $toc_bookmarkText, $resetpagenum, $pagenumstyle, $suppress, $orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $toc_id, $pagesel, $toc_pagesel, $sheetsize, $toc_sheetsize, $tocoutdent);
23442	}
23443
23444	function TOCpagebreak($tocfont = '', $tocfontsize = '', $tocindent = '', $TOCusePaging = true, $TOCuseLinking = '', $toc_orientation = '', $toc_mgl = '', $toc_mgr = '', $toc_mgt = '', $toc_mgb = '', $toc_mgh = '', $toc_mgf = '', $toc_ohname = '', $toc_ehname = '', $toc_ofname = '', $toc_efname = '', $toc_ohvalue = 0, $toc_ehvalue = 0, $toc_ofvalue = 0, $toc_efvalue = 0, $toc_preHTML = '', $toc_postHTML = '', $toc_bookmarkText = '', $resetpagenum = '', $pagenumstyle = '', $suppress = '', $orientation = '', $mgl = '', $mgr = '', $mgt = '', $mgb = '', $mgh = '', $mgf = '', $ohname = '', $ehname = '', $ofname = '', $efname = '', $ohvalue = 0, $ehvalue = 0, $ofvalue = 0, $efvalue = 0, $toc_id = 0, $pagesel = '', $toc_pagesel = '', $sheetsize = '', $toc_sheetsize = '', $tocoutdent = '')
23445	{
23446		// Start a new page
23447		if ($this->state == 0) {
23448			$this->AddPage();
23449		}
23450		if ($this->y == $this->tMargin && (!$this->mirrorMargins || ($this->mirrorMargins && $this->page % 2 == 1))) {
23451			// Don't add a page
23452			if ($this->page == 1 && count($this->PageNumSubstitutions) == 0) {
23453				if (!$suppress) {
23454					$suppress = 'off';
23455				}
23456				// $this->PageNumSubstitutions[] = array('from'=>1, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=> $suppress);
23457			}
23458			$this->PageNumSubstitutions[] = ['from' => $this->page, 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
23459		} else {
23460			$this->AddPage($orientation, 'NEXT-ODD', $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $sheetsize);
23461		}
23462		$this->tableOfContents->TOCpagebreak($tocfont, $tocfontsize, $tocindent, $TOCusePaging, $TOCuseLinking, $toc_orientation, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_preHTML, $toc_postHTML, $toc_bookmarkText, $resetpagenum, $pagenumstyle, $suppress, $orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $toc_id, $pagesel, $toc_pagesel, $sheetsize, $toc_sheetsize, $tocoutdent);
23463	}
23464
23465	function TOC_Entry($txt, $level = 0, $toc_id = 0)
23466	{
23467		if ($this->ColActive) {
23468			$ily = $this->y0;
23469		} else {
23470			$ily = $this->y;
23471		} // use top of columns
23472
23473		$linkn = $this->AddLink();
23474		$uid = '__mpdfinternallink_' . $linkn;
23475		if ($this->table_rotate) {
23476			$this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "tbrot" => true];
23477		} elseif ($this->kwt) {
23478			$this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "kwt" => true];
23479		} elseif ($this->ColActive) {
23480			$this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol];
23481		} elseif (!$this->keep_block_together) {
23482			$this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page];
23483		}
23484		$this->internallink['#' . $uid] = $linkn;
23485		$this->SetLink($linkn, $ily, $this->page);
23486
23487		if (strtoupper($toc_id) == 'ALL') {
23488			$toc_id = '_mpdf_all';
23489		} elseif (!$toc_id) {
23490			$toc_id = 0;
23491		} else {
23492			$toc_id = strtolower($toc_id);
23493		}
23494		$btoc = ['t' => $txt, 'l' => $level, 'p' => $this->page, 'link' => $linkn, 'toc_id' => $toc_id];
23495		if ($this->keep_block_together) {
23496			// do nothing
23497		} /* -- TABLES -- */ elseif ($this->table_rotate) {
23498			$this->tbrot_toc[] = $btoc;
23499		} elseif ($this->kwt) {
23500			$this->kwt_toc[] = $btoc;
23501		} /* -- END TABLES -- */ elseif ($this->ColActive) {  // *COLUMNS*
23502			$this->col_toc[] = $btoc; // *COLUMNS*
23503		} // *COLUMNS*
23504		else {
23505			$this->tableOfContents->_toc[] = $btoc;
23506		}
23507	}
23508
23509	/* -- END TOC -- */
23510
23511	// ======================================================
23512	function MovePages($target_page, $start_page, $end_page = -1)
23513	{
23514		// move a page/pages EARLIER in the document
23515		if ($end_page < 1) {
23516			$end_page = $start_page;
23517		}
23518		$n_toc = $end_page - $start_page + 1;
23519
23520		// Set/Update PageNumSubstitutions changes before moving anything
23521		if (count($this->PageNumSubstitutions)) {
23522			$tp_present = false;
23523			$sp_present = false;
23524			$ep_present = false;
23525			foreach ($this->PageNumSubstitutions as $k => $v) {
23526				if ($this->PageNumSubstitutions[$k]['from'] == $target_page) {
23527					$tp_present = true;
23528					if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
23529						$this->PageNumSubstitutions[$k]['suppress'] = 'off';
23530					}
23531				}
23532				if ($this->PageNumSubstitutions[$k]['from'] == $start_page) {
23533					$sp_present = true;
23534					if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
23535						$this->PageNumSubstitutions[$k]['suppress'] = 'off';
23536					}
23537				}
23538				if ($this->PageNumSubstitutions[$k]['from'] == ($end_page + 1)) {
23539					$ep_present = true;
23540					if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
23541						$this->PageNumSubstitutions[$k]['suppress'] = 'off';
23542					}
23543				}
23544			}
23545
23546			if (!$tp_present) {
23547				list($tp_type, $tp_suppress, $tp_reset) = $this->docPageSettings($target_page);
23548			}
23549			if (!$sp_present) {
23550				list($sp_type, $sp_suppress, $sp_reset) = $this->docPageSettings($start_page);
23551			}
23552			if (!$ep_present) {
23553				list($ep_type, $ep_suppress, $ep_reset) = $this->docPageSettings($start_page - 1);
23554			}
23555		}
23556
23557		$last = [];
23558		// store pages
23559		for ($i = $start_page; $i <= $end_page; $i++) {
23560			$last[] = $this->pages[$i];
23561		}
23562		// move pages
23563		for ($i = $start_page - 1; $i >= ($target_page); $i--) {
23564			$this->pages[$i + $n_toc] = $this->pages[$i];
23565		}
23566		// Put toc pages at insert point
23567		for ($i = 0; $i < $n_toc; $i++) {
23568			$this->pages[$target_page + $i] = $last[$i];
23569		}
23570
23571		/* -- BOOKMARKS -- */
23572		// Update Bookmarks
23573		foreach ($this->BMoutlines as $i => $o) {
23574			if ($o['p'] >= $target_page) {
23575				$this->BMoutlines[$i]['p'] += $n_toc;
23576			}
23577		}
23578		/* -- END BOOKMARKS -- */
23579
23580		// Update Page Links
23581		if (count($this->PageLinks)) {
23582			$newarr = [];
23583			foreach ($this->PageLinks as $i => $o) {
23584				foreach ($this->PageLinks[$i] as $key => $pl) {
23585					if (strpos($pl[4], '@') === 0) {
23586						$p = substr($pl[4], 1);
23587						if ($p >= $start_page && $p <= $end_page) {
23588							$this->PageLinks[$i][$key][4] = '@' . ($p + ($target_page - $start_page));
23589						} elseif ($p >= $target_page && $p < $start_page) {
23590							$this->PageLinks[$i][$key][4] = '@' . ($p + $n_toc);
23591						}
23592					}
23593				}
23594				if ($i >= $start_page && $i <= $end_page) {
23595					$newarr[($i + ($target_page - $start_page))] = $this->PageLinks[$i];
23596				} elseif ($i >= $target_page && $i < $start_page) {
23597					$newarr[($i + $n_toc)] = $this->PageLinks[$i];
23598				} else {
23599					$newarr[$i] = $this->PageLinks[$i];
23600				}
23601			}
23602			$this->PageLinks = $newarr;
23603		}
23604
23605		// OrientationChanges
23606		if (count($this->OrientationChanges)) {
23607			$newarr = [];
23608			foreach ($this->OrientationChanges as $p => $v) {
23609				if ($p >= $start_page && $p <= $end_page) {
23610					$newarr[($p + ($target_page - $start_page))] = $this->OrientationChanges[$p];
23611				} elseif ($p >= $target_page && $p < $start_page) {
23612					$newarr[$p + $n_toc] = $this->OrientationChanges[$p];
23613				} else {
23614					$newarr[$p] = $this->OrientationChanges[$p];
23615				}
23616			}
23617			ksort($newarr);
23618			$this->OrientationChanges = $newarr;
23619		}
23620
23621		// Page Dimensions
23622		if (count($this->pageDim)) {
23623			$newarr = [];
23624			foreach ($this->pageDim as $p => $v) {
23625				if ($p >= $start_page && $p <= $end_page) {
23626					$newarr[($p + ($target_page - $start_page))] = $this->pageDim[$p];
23627				} elseif ($p >= $target_page && $p < $start_page) {
23628					$newarr[$p + $n_toc] = $this->pageDim[$p];
23629				} else {
23630					$newarr[$p] = $this->pageDim[$p];
23631				}
23632			}
23633			ksort($newarr);
23634			$this->pageDim = $newarr;
23635		}
23636
23637		// HTML Headers & Footers
23638		if (count($this->saveHTMLHeader)) {
23639			$newarr = [];
23640			foreach ($this->saveHTMLHeader as $p => $v) {
23641				if ($p >= $start_page && $p <= $end_page) {
23642					$newarr[($p + ($target_page - $start_page))] = $this->saveHTMLHeader[$p];
23643				} elseif ($p >= $target_page && $p < $start_page) {
23644					$newarr[$p + $n_toc] = $this->saveHTMLHeader[$p];
23645				} else {
23646					$newarr[$p] = $this->saveHTMLHeader[$p];
23647				}
23648			}
23649			ksort($newarr);
23650			$this->saveHTMLHeader = $newarr;
23651		}
23652		if (count($this->saveHTMLFooter)) {
23653			$newarr = [];
23654			foreach ($this->saveHTMLFooter as $p => $v) {
23655				if ($p >= $start_page && $p <= $end_page) {
23656					$newarr[($p + ($target_page - $start_page))] = $this->saveHTMLFooter[$p];
23657				} elseif ($p >= $target_page && $p < $start_page) {
23658					$newarr[$p + $n_toc] = $this->saveHTMLFooter[$p];
23659				} else {
23660					$newarr[$p] = $this->saveHTMLFooter[$p];
23661				}
23662			}
23663			ksort($newarr);
23664			$this->saveHTMLFooter = $newarr;
23665		}
23666
23667		// Update Internal Links
23668		if (count($this->internallink)) {
23669			foreach ($this->internallink as $key => $o) {
23670				if (is_array($o) && $o['PAGE'] >= $start_page && $o['PAGE'] <= $end_page) {
23671					$this->internallink[$key]['PAGE'] += ($target_page - $start_page);
23672				} elseif (is_array($o) && $o['PAGE'] >= $target_page && $o['PAGE'] < $start_page) {
23673					$this->internallink[$key]['PAGE'] += $n_toc;
23674				}
23675			}
23676		}
23677
23678		// Update Links
23679		if (count($this->links)) {
23680			foreach ($this->links as $key => $o) {
23681				if ($o[0] >= $start_page && $o[0] <= $end_page) {
23682					$this->links[$key][0] += ($target_page - $start_page);
23683				}
23684				if ($o[0] >= $target_page && $o[0] < $start_page) {
23685					$this->links[$key][0] += $n_toc;
23686				}
23687			}
23688		}
23689
23690		// Update Form fields
23691		if (count($this->form->forms)) {
23692			foreach ($this->form->forms as $key => $f) {
23693				if ($f['page'] >= $start_page && $f['page'] <= $end_page) {
23694					$this->form->forms[$key]['page'] += ($target_page - $start_page);
23695				}
23696				if ($f['page'] >= $target_page && $f['page'] < $start_page) {
23697					$this->form->forms[$key]['page'] += $n_toc;
23698				}
23699			}
23700		}
23701
23702		/* -- ANNOTATIONS -- */
23703		// Update Annotations
23704		if (count($this->PageAnnots)) {
23705			$newarr = [];
23706			foreach ($this->PageAnnots as $p => $anno) {
23707				if ($p >= $start_page && $p <= $end_page) {
23708					$np = $p + ($target_page - $start_page);
23709					foreach ($anno as $o) {
23710						$newarr[$np][] = $o;
23711					}
23712				} elseif ($p >= $target_page && $p < $start_page) {
23713					$np = $p + $n_toc;
23714					foreach ($anno as $o) {
23715						$newarr[$np][] = $o;
23716					}
23717				} else {
23718					$newarr[$p] = $this->PageAnnots[$p];
23719				}
23720			}
23721			$this->PageAnnots = $newarr;
23722			unset($newarr);
23723		}
23724		/* -- END ANNOTATIONS -- */
23725
23726		// Update TOC pages
23727		if (count($this->tableOfContents->_toc)) {
23728			foreach ($this->tableOfContents->_toc as $key => $t) {
23729				if ($t['p'] >= $start_page && $t['p'] <= $end_page) {
23730					$this->tableOfContents->_toc[$key]['p'] += ($target_page - $start_page);
23731				}
23732				if ($t['p'] >= $target_page && $t['p'] < $start_page) {
23733					$this->tableOfContents->_toc[$key]['p'] += $n_toc;
23734				}
23735			}
23736		}
23737
23738		// Update PageNumSubstitutions
23739		if (count($this->PageNumSubstitutions)) {
23740			$newarr = [];
23741			foreach ($this->PageNumSubstitutions as $k => $v) {
23742				if ($this->PageNumSubstitutions[$k]['from'] >= $start_page && $this->PageNumSubstitutions[$k]['from'] <= $end_page) {
23743					$this->PageNumSubstitutions[$k]['from'] += ($target_page - $start_page);
23744					$newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
23745				} elseif ($this->PageNumSubstitutions[$k]['from'] >= $target_page && $this->PageNumSubstitutions[$k]['from'] < $start_page) {
23746					$this->PageNumSubstitutions[$k]['from'] += $n_toc;
23747					$newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
23748				} else {
23749					$newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
23750				}
23751			}
23752
23753			if (!$sp_present) {
23754				$newarr[$target_page] = ['from' => $target_page, 'suppress' => $sp_suppress, 'reset' => $sp_reset, 'type' => $sp_type];
23755			}
23756			if (!$tp_present) {
23757				$newarr[($target_page + $n_toc)] = ['from' => ($target_page + $n_toc), 'suppress' => $tp_suppress, 'reset' => $tp_reset, 'type' => $tp_type];
23758			}
23759			if (!$ep_present && $end_page > count($this->pages)) {
23760				$newarr[($end_page + 1)] = ['from' => ($end_page + 1), 'suppress' => $ep_suppress, 'reset' => $ep_reset, 'type' => $ep_type];
23761			}
23762			ksort($newarr);
23763			$this->PageNumSubstitutions = [];
23764			foreach ($newarr as $v) {
23765				$this->PageNumSubstitutions[] = $v;
23766			}
23767		}
23768	}
23769
23770	function DeletePages($start_page, $end_page = -1)
23771	{
23772		// move a page/pages EARLIER in the document
23773		if ($end_page < 1) {
23774			$end_page = $start_page;
23775		}
23776		$n_tod = $end_page - $start_page + 1;
23777		$last_page = count($this->pages);
23778		$n_atend = $last_page - $end_page + 1;
23779
23780		// move pages
23781		for ($i = 0; $i < $n_atend; $i++) {
23782			$this->pages[$start_page + $i] = $this->pages[$end_page + 1 + $i];
23783		}
23784		// delete pages
23785		for ($i = 0; $i < $n_tod; $i++) {
23786			unset($this->pages[$last_page - $i]);
23787		}
23788
23789
23790		/* -- BOOKMARKS -- */
23791		// Update Bookmarks
23792		foreach ($this->BMoutlines as $i => $o) {
23793			if ($o['p'] >= $end_page) {
23794				$this->BMoutlines[$i]['p'] -= $n_tod;
23795			} elseif ($p < $start_page) {
23796				unset($this->BMoutlines[$i]);
23797			}
23798		}
23799		/* -- END BOOKMARKS -- */
23800
23801		// Update Page Links
23802		if (count($this->PageLinks)) {
23803			$newarr = [];
23804			foreach ($this->PageLinks as $i => $o) {
23805				foreach ($this->PageLinks[$i] as $key => $pl) {
23806					if (strpos($pl[4], '@') === 0) {
23807						$p = substr($pl[4], 1);
23808						if ($p > $end_page) {
23809							$this->PageLinks[$i][$key][4] = '@' . ($p - $n_tod);
23810						} elseif ($p < $start_page) {
23811							unset($this->PageLinks[$i][$key]);
23812						}
23813					}
23814				}
23815				if ($i > $end_page) {
23816					$newarr[($i - $n_tod)] = $this->PageLinks[$i];
23817				} elseif ($p < $start_page) {
23818					$newarr[$i] = $this->PageLinks[$i];
23819				}
23820			}
23821			$this->PageLinks = $newarr;
23822		}
23823
23824		// OrientationChanges
23825		if (count($this->OrientationChanges)) {
23826			$newarr = [];
23827			foreach ($this->OrientationChanges as $p => $v) {
23828				if ($p > $end_page) {
23829					$newarr[($p - $t_tod)] = $this->OrientationChanges[$p];
23830				} elseif ($p < $start_page) {
23831					$newarr[$p] = $this->OrientationChanges[$p];
23832				}
23833			}
23834			ksort($newarr);
23835			$this->OrientationChanges = $newarr;
23836		}
23837
23838		// Page Dimensions
23839		if (count($this->pageDim)) {
23840			$newarr = [];
23841			foreach ($this->pageDim as $p => $v) {
23842				if ($p > $end_page) {
23843					$newarr[($p - $n_tod)] = $this->pageDim[$p];
23844				} elseif ($p < $start_page) {
23845					$newarr[$p] = $this->pageDim[$p];
23846				}
23847			}
23848			ksort($newarr);
23849			$this->pageDim = $newarr;
23850		}
23851
23852		// HTML Headers & Footers
23853		if (count($this->saveHTMLHeader)) {
23854			foreach ($this->saveHTMLHeader as $p => $v) {
23855				if ($p > $end_page) {
23856					$newarr[($p - $n_tod)] = $this->saveHTMLHeader[$p];
23857				} // mPDF 5.7.3
23858				elseif ($p < $start_page) {
23859					$newarr[$p] = $this->saveHTMLHeader[$p];
23860				}
23861			}
23862			ksort($newarr);
23863			$this->saveHTMLHeader = $newarr;
23864		}
23865		if (count($this->saveHTMLFooter)) {
23866			$newarr = [];
23867			foreach ($this->saveHTMLFooter as $p => $v) {
23868				if ($p > $end_page) {
23869					$newarr[($p - $n_tod)] = $this->saveHTMLFooter[$p];
23870				} elseif ($p < $start_page) {
23871					$newarr[$p] = $this->saveHTMLFooter[$p];
23872				}
23873			}
23874			ksort($newarr);
23875			$this->saveHTMLFooter = $newarr;
23876		}
23877
23878		// Update Internal Links
23879		foreach ($this->internallink as $key => $o) {
23880			if ($o['PAGE'] > $end_page) {
23881				$this->internallink[$key]['PAGE'] -= $n_tod;
23882			} elseif ($o['PAGE'] < $start_page) {
23883				unset($this->internallink[$key]);
23884			}
23885		}
23886
23887		// Update Links
23888		foreach ($this->links as $key => $o) {
23889			if ($o[0] > $end_page) {
23890				$this->links[$key][0] -= $n_tod;
23891			} elseif ($o[0] < $start_page) {
23892				unset($this->links[$key]);
23893			}
23894		}
23895
23896		// Update Form fields
23897		foreach ($this->form->forms as $key => $f) {
23898			if ($f['page'] > $end_page) {
23899				$this->form->forms[$key]['page'] -= $n_tod;
23900			} elseif ($f['page'] < $start_page) {
23901				unset($this->form->forms[$key]);
23902			}
23903		}
23904
23905		/* -- ANNOTATIONS -- */
23906		// Update Annotations
23907		if (count($this->PageAnnots)) {
23908			$newarr = [];
23909			foreach ($this->PageAnnots as $p => $anno) {
23910				if ($p > $end_page) {
23911					foreach ($anno as $o) {
23912						$newarr[($p - $n_tod)][] = $o;
23913					}
23914				} elseif ($p < $start_page) {
23915					$newarr[$p] = $this->PageAnnots[$p];
23916				}
23917			}
23918			ksort($newarr);
23919			$this->PageAnnots = $newarr;
23920		}
23921		/* -- END ANNOTATIONS -- */
23922
23923		// Update PageNumSubstitutions
23924		foreach ($this->PageNumSubstitutions as $k => $v) {
23925			if ($this->PageNumSubstitutions[$k]['from'] > $end_page) {
23926				$this->PageNumSubstitutions[$k]['from'] -= $n_tod;
23927			} elseif ($this->PageNumSubstitutions[$k]['from'] < $start_page) {
23928				unset($this->PageNumSubstitutions[$k]);
23929			}
23930		}
23931
23932		unset($newarr);
23933		$this->page = count($this->pages);
23934	}
23935
23936	// ======================================================
23937		/* -- INDEX -- */
23938	// FROM class PDF_Ref == INDEX
23939
23940	function IndexEntry($txt, $xref = '')
23941	{
23942		if ($xref) {
23943			$this->IndexEntrySee($txt, $xref);
23944			return;
23945		}
23946
23947		// Search the reference (AND Ref/PageNo) in the array
23948		$Present = false;
23949		if ($this->keep_block_together) {
23950			// do nothing
23951		} /* -- TABLES -- */ elseif ($this->kwt) {
23952			$size = count($this->kwt_Reference);
23953			for ($i = 0; $i < $size; $i++) {
23954				if (isset($this->kwt_Reference[$i]['t']) && $this->kwt_Reference[$i]['t'] == $txt) {
23955					$Present = true;
23956					if ($this->page != $this->kwt_Reference[$i]['op']) {
23957						$this->kwt_Reference[$i]['op'] = $this->page;
23958					}
23959				}
23960			}
23961			if (!$Present) { // If not found, add it
23962				$this->kwt_Reference[] = ['t' => $txt, 'op' => $this->page];
23963			}
23964		} /* -- END TABLES -- */ else {
23965			$size = count($this->Reference);
23966			for ($i = 0; $i < $size; $i++) {
23967				if (isset($this->Reference[$i]['t']) && $this->Reference[$i]['t'] == $txt) {
23968					$Present = true;
23969					if (!in_array($this->page, $this->Reference[$i]['p'])) {
23970						$this->Reference[$i]['p'][] = $this->page;
23971					}
23972				}
23973			}
23974			if (!$Present) { // If not found, add it
23975				$this->Reference[] = ['t' => $txt, 'p' => [$this->page]];
23976			}
23977		}
23978	}
23979
23980	// Added function to add a reference "Elephants. See Chickens"
23981	function IndexEntrySee($txta, $txtb)
23982	{
23983		if ($this->directionality == 'rtl') { // *OTL*
23984			// ONLY DO THIS IF NOT IN TAGS
23985			if ($txta == strip_tags($txta)) {
23986				$txta = str_replace(':', ' - ', $txta); // *OTL*
23987			}
23988			if ($txtb == strip_tags($txtb)) {
23989				$txtb = str_replace(':', ' - ', $txtb); // *OTL*
23990			}
23991		} // *OTL*
23992		else { // *OTL*
23993			if ($txta == strip_tags($txta)) {
23994				$txta = str_replace(':', ', ', $txta);
23995			}
23996			if ($txtb == strip_tags($txtb)) {
23997				$txtb = str_replace(':', ', ', $txtb);
23998			}
23999		} // *OTL*
24000		$this->Reference[] = ['t' => $txta . ' - see ' . $txtb, 'p' => []];
24001	}
24002
24003	private function filesInDir($directory)
24004	{
24005		$files = [];
24006		foreach ((new \DirectoryIterator($directory)) as $v) {
24007			if ($v->isDir() || $v->isDot()) {
24008				continue;
24009			}
24010
24011			$files[] = $v->getPathname();
24012		}
24013
24014		return $files;
24015	}
24016
24017	function InsertIndex($usedivletters = 1, $useLinking = false, $indexCollationLocale = '', $indexCollationGroup = '')
24018	{
24019		$size = count($this->Reference);
24020		if ($size == 0) {
24021			return false;
24022		}
24023
24024		// $spacer used after named entry
24025		// $sep  separates number [groups], $joiner joins numbers in range
24026		//  e.g. "elephant 73, 97-99"  =  elephant[$spacer]73[$sep]97[$joiner]99
24027		// $subEntrySeparator separates main and subentry (if $this->indexUseSubentries == false;) e.g.
24028		// Mammal:elephant => Mammal[$subEntrySeparator]elephant
24029		// $subEntryInset specifies what precedes a subentry (if $this->indexUseSubentries == true;) e.g.
24030		// Mammal:elephant => [$subEntryInset]elephant
24031		$spacer = "\xc2\xa0 ";
24032		if ($this->directionality == 'rtl') {
24033			$sep = '&#x060c; ';
24034			$joiner = '-';
24035			$subEntrySeparator = '&#x060c; ';
24036			$subEntryInset = ' - ';
24037		} else {
24038			$sep = ', ';
24039			$joiner = '-';
24040			$subEntrySeparator = ', ';
24041			$subEntryInset = ' - ';
24042		}
24043
24044		for ($i = 0; $i < $size; $i++) {
24045			$txt = $this->Reference[$i]['t'];
24046			$txt = strip_tags($txt); // mPDF 6
24047			$txt = $this->purify_utf8($txt);
24048			$this->Reference[$i]['uf'] = $txt; // Unformatted e.g. pure utf-8 encoded characters, no mark-up/tags
24049			// Used for ordering and collation
24050		}
24051
24052		if ($usedivletters) {
24053			if ($indexCollationGroup && \in_array(strtolower($indexCollationGroup), array_map(function ($v) {
24054					return strtolower(basename($v, '.php'));
24055			}, $this->filesInDir(__DIR__ . '/../data/collations/')))) {
24056				$collation = require __DIR__ . '/../data/collations/' . $indexCollationGroup . '.php';
24057			} else {
24058				$collation = [];
24059			}
24060			for ($i = 0; $i < $size; $i++) {
24061				if ($this->Reference[$i]['uf']) {
24062					$l = mb_substr($this->Reference[$i]['uf'], 0, 1, 'UTF-8');
24063					if (isset($indexCollationGroup) && $indexCollationGroup) {
24064						$uni = $this->UTF8StringToArray($l);
24065						$ucode = $uni[0];
24066						if (isset($collation[$ucode])) {
24067							$this->Reference[$i]['d'] = UtfString::code2utf($collation[$ucode]);
24068						} else {
24069							$this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');
24070						}
24071					} else {
24072						$this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');
24073					}
24074				}
24075			}
24076		}
24077
24078		// Alphabetic sort of the references
24079		$originalLocale = setlocale(LC_COLLATE, 0);
24080		if ($indexCollationLocale) {
24081			setlocale(LC_COLLATE, $indexCollationLocale);
24082		}
24083
24084		usort($this->Reference, function ($a, $b) {
24085			return strcoll(strtolower($a['uf']), strtolower($b['uf']));
24086		});
24087
24088		if ($indexCollationLocale) {
24089			setlocale(LC_COLLATE, $originalLocale);
24090		}
24091
24092		$html = '<div class="mpdf_index_main">';
24093
24094		$lett = '';
24095		$last_lett = '';
24096		$mainentry = '';
24097		for ($i = 0; $i < $size; $i++) {
24098			if ($this->Reference[$i]['t']) {
24099				if ($usedivletters) {
24100					$lett = $this->Reference[$i]['d'];
24101					if ($lett != $last_lett) {
24102						$html .= '<div class="mpdf_index_letter">' . $lett . '</div>';
24103					}
24104				}
24105				$txt = $this->Reference[$i]['t'];
24106
24107				// Sub-entries e.g. Mammals:elephant
24108				// But allow for tags e.g. <b>Mammal</b>:elephants
24109				$a = preg_split('/(<.*?>)/', $txt, -1, PREG_SPLIT_DELIM_CAPTURE);
24110				$txt = '';
24111				$marker = false;
24112				foreach ($a as $k => $e) {
24113					if ($k % 2 == 0 && !$marker) {
24114						if (strpos($e, ':') !== false) { // == SubEntry
24115							if ($this->indexUseSubentries) {
24116								// If the Main entry does not have any page numbers associated with it
24117								// create and insert an entry
24118								list($txtmain, $sub) = preg_split('/[:]/', $e, 2);
24119								if (strip_tags($txt . $txtmain) != $mainentry) {
24120									$html .= '<div class="mpdf_index_entry">' . $txt . $txtmain . '</div>';
24121									$mainentry = strip_tags($txt . $txtmain);
24122								}
24123
24124								$txt = $subEntryInset;
24125								$e = $sub; // Only replace first one
24126							} else {
24127								$e = preg_replace('/[:]/', $subEntrySeparator, $e, 1); // Only replace first one
24128							}
24129							$marker = true; // Don't replace any more once the subentry marker has been found
24130						}
24131					}
24132					$txt .= $e;
24133				}
24134
24135				if (!$marker) {
24136					$mainentry = strip_tags($txt);
24137				}
24138
24139				$html .= '<div class="mpdf_index_entry">';
24140				$html .= $txt;
24141				$ppp = $this->Reference[$i]['p']; // = array of page numbers to point to
24142				if (count($ppp)) {
24143					sort($ppp);
24144					$newarr = [];
24145					$range_start = $ppp[0];
24146					$range_end = 0;
24147
24148					$html .= $spacer;
24149
24150					for ($zi = 1; $zi < count($ppp); $zi++) {
24151						if ($ppp[$zi] == ($ppp[($zi - 1)] + 1)) {
24152							$range_end = $ppp[$zi];
24153						} else {
24154							if ($range_end) {
24155								if ($range_end == $range_start + 1) {
24156									if ($useLinking) {
24157										$html .= '<a class="mpdf_index_link" href="@' . $range_start . '">';
24158									}
24159									$html .= $this->docPageNum($range_start);
24160									if ($useLinking) {
24161										$html .= '</a>';
24162									}
24163									$html .= $sep;
24164
24165									if ($useLinking) {
24166										$html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">';
24167									}
24168									$html .= $this->docPageNum($ppp[$zi - 1]);
24169									if ($useLinking) {
24170										$html .= '</a>';
24171									}
24172									$html .= $sep;
24173								}
24174							} else {
24175								if ($useLinking) {
24176									$html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">';
24177								}
24178								$html .= $this->docPageNum($ppp[$zi - 1]);
24179								if ($useLinking) {
24180									$html .= '</a>';
24181								}
24182								$html .= $sep;
24183							}
24184							$range_start = $ppp[$zi];
24185							$range_end = 0;
24186						}
24187					}
24188
24189					if ($range_end) {
24190						if ($useLinking) {
24191							$html .= '<a class="mpdf_index_link" href="@' . $range_start . '">';
24192						}
24193						$html .= $this->docPageNum($range_start);
24194						if ($range_end == $range_start + 1) {
24195							if ($useLinking) {
24196								$html .= '</a>';
24197							}
24198							$html .= $sep;
24199							if ($useLinking) {
24200								$html .= '<a class="mpdf_index_link" href="@' . $range_end . '">';
24201							}
24202							$html .= $this->docPageNum($range_end);
24203							if ($useLinking) {
24204								$html .= '</a>';
24205							}
24206						} else {
24207							$html .= $joiner;
24208							$html .= $this->docPageNum($range_end);
24209							if ($useLinking) {
24210								$html .= '</a>';
24211							}
24212						}
24213					} else {
24214						if ($useLinking) {
24215							$html .= '<a class="mpdf_index_link" href="@' . $ppp[(count($ppp) - 1)] . '">';
24216						}
24217						$html .= $this->docPageNum($ppp[(count($ppp) - 1)]);
24218						if ($useLinking) {
24219							$html .= '</a>';
24220						}
24221					}
24222				}
24223			}
24224			$html .= '</div>';
24225			$last_lett = $lett;
24226		}
24227		$html .= '</div>';
24228		$save_fpb = $this->fixedPosBlockSave;
24229		$this->WriteHTML($html);
24230		$this->fixedPosBlockSave = $save_fpb;
24231
24232		$this->breakpoints[$this->CurrCol][] = $this->y;  // *COLUMNS*
24233	}
24234
24235	/* -- END INDEX -- */
24236
24237	function AcceptPageBreak()
24238	{
24239		if (count($this->cellBorderBuffer)) {
24240			$this->printcellbuffer();
24241		} // *TABLES*
24242		/* -- COLUMNS -- */
24243		if ($this->ColActive == 1) {
24244			if ($this->CurrCol < $this->NbCol - 1) {
24245				// Go to the next column
24246				$this->CurrCol++;
24247				$this->SetCol($this->CurrCol);
24248				$this->y = $this->y0;
24249				$this->ChangeColumn = 1; // Number (and direction) of columns changed +1, +2, -2 etc.
24250				// DIRECTIONALITY RTL
24251				if ($this->directionality == 'rtl') {
24252					$this->ChangeColumn = -($this->ChangeColumn);
24253				} // *OTL*
24254				// Stay on the page
24255				return false;
24256			} else {
24257				// Go back to the first column - NEW PAGE
24258				if (count($this->columnbuffer)) {
24259					$this->printcolumnbuffer();
24260				}
24261				$this->SetCol(0);
24262				$this->y0 = $this->tMargin;
24263				$this->ChangeColumn = -($this->NbCol - 1);
24264				// DIRECTIONALITY RTL
24265				if ($this->directionality == 'rtl') {
24266					$this->ChangeColumn = -($this->ChangeColumn);
24267				} // *OTL*
24268				// Page break
24269				return true;
24270			}
24271		} /* -- END COLUMNS -- */
24272		/* -- TABLES -- */ elseif ($this->table_rotate) {
24273			if ($this->tablebuffer) {
24274				$this->printtablebuffer();
24275			}
24276			return true;
24277		} /* -- END TABLES -- */ else { // *COLUMNS*
24278			$this->ChangeColumn = 0;
24279			return $this->autoPageBreak;
24280		} // *COLUMNS*
24281		return $this->autoPageBreak;
24282	}
24283
24284	// ----------- COLUMNS ---------------------
24285	/* -- COLUMNS -- */
24286
24287	function SetColumns($NbCol, $vAlign = '', $gap = 5)
24288	{
24289		// NbCol = number of columns
24290		// Anything less than 2 turns columns off
24291		if ($NbCol < 2) { // SET COLUMNS OFF
24292			if ($this->ColActive) {
24293				$this->ColActive = 0;
24294				if (count($this->columnbuffer)) {
24295					$this->printcolumnbuffer();
24296				}
24297				$this->NbCol = 1;
24298				$this->ResetMargins();
24299				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
24300				$this->divwidth = 0;
24301				$this->Ln();
24302			}
24303			$this->ColActive = 0;
24304			$this->columnbuffer = [];
24305			$this->ColDetails = [];
24306			$this->columnLinks = [];
24307			$this->columnAnnots = [];
24308			$this->columnForms = [];
24309			$this->col_BMoutlines = [];
24310			$this->col_toc = [];
24311			$this->breakpoints = [];
24312		} else { // SET COLUMNS ON
24313			if ($this->ColActive) {
24314				$this->ColActive = 0;
24315				if (count($this->columnbuffer)) {
24316					$this->printcolumnbuffer();
24317				}
24318				$this->ResetMargins();
24319			}
24320			if (isset($this->y) && $this->y > $this->tMargin) {
24321				$this->Ln();
24322			}
24323			$this->NbCol = $NbCol;
24324			$this->ColGap = $gap;
24325			$this->divwidth = 0;
24326			$this->ColActive = 1;
24327			$this->ColumnAdjust = true; // enables column height adjustment for the page
24328			$this->columnbuffer = [];
24329			$this->ColDetails = [];
24330			$this->columnLinks = [];
24331			$this->columnAnnots = [];
24332			$this->columnForms = [];
24333			$this->col_BMoutlines = [];
24334			$this->col_toc = [];
24335			$this->breakpoints = [];
24336			if ((strtoupper($vAlign) == 'J') || (strtoupper($vAlign) == 'JUSTIFY')) {
24337				$vAlign = 'J';
24338			} else {
24339				$vAlign = '';
24340			}
24341			$this->colvAlign = $vAlign;
24342			// Save the ordinate
24343			$absL = $this->DeflMargin - ($gap / 2);
24344			$absR = $this->DefrMargin - ($gap / 2);
24345			$PageWidth = $this->w - $absL - $absR; // virtual pagewidth for calculation only
24346			$ColWidth = (($PageWidth - ($gap * ($NbCol))) / $NbCol);
24347			$this->ColWidth = $ColWidth;
24348			/* -- OTL -- */
24349
24350			if ($this->directionality == 'rtl') {
24351				for ($i = 0; $i < $this->NbCol; $i++) {
24352					$this->ColL[$i] = $absL + ($gap / 2) + (($NbCol - ($i + 1)) * ($PageWidth / $NbCol));
24353					$this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos
24354				}
24355			} else {
24356				/* -- END OTL -- */
24357				for ($i = 0; $i < $this->NbCol; $i++) {
24358					$this->ColL[$i] = $absL + ($gap / 2) + ($i * ($PageWidth / $NbCol) );
24359					$this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos
24360				}
24361			} // *OTL*
24362			$this->pgwidth = $ColWidth;
24363			$this->SetCol(0);
24364			$this->y0 = $this->y;
24365		}
24366		$this->x = $this->lMargin;
24367	}
24368
24369	function SetCol($CurrCol)
24370	{
24371		// Used internally to set column by number: 0 is 1st column
24372		// Set position on a column
24373		$this->CurrCol = $CurrCol;
24374		$x = $this->ColL[$CurrCol];
24375		$xR = $this->ColR[$CurrCol]; // NB This is not R margin -> R pos
24376		if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
24377			$x += $this->MarginCorrection;
24378			$xR += $this->MarginCorrection;
24379		}
24380		$this->SetMargins($x, ($this->w - $xR), $this->tMargin);
24381	}
24382
24383	function AddColumn()
24384	{
24385		$this->NewColumn();
24386		$this->ColumnAdjust = false; // disables all column height adjustment for the page.
24387	}
24388
24389	function NewColumn()
24390	{
24391		if ($this->ColActive == 1) {
24392			if ($this->CurrCol < $this->NbCol - 1) {
24393				// Go to the next column
24394				$this->CurrCol++;
24395				$this->SetCol($this->CurrCol);
24396				$this->y = $this->y0;
24397				$this->ChangeColumn = 1;
24398				// DIRECTIONALITY RTL
24399				if ($this->directionality == 'rtl') {
24400					$this->ChangeColumn = -($this->ChangeColumn);
24401				} // *OTL*
24402				// Stay on the page
24403			} else {
24404				// Go back to the first column
24405				// Page break
24406				if (count($this->columnbuffer)) {
24407					$this->printcolumnbuffer();
24408				}
24409				$this->AddPage($this->CurOrientation);
24410				$this->SetCol(0);
24411				$this->y0 = $this->tMargin;
24412				$this->ChangeColumn = -($this->NbCol - 1);
24413				// DIRECTIONALITY RTL
24414				if ($this->directionality == 'rtl') {
24415					$this->ChangeColumn = -($this->ChangeColumn);
24416				} // *OTL*
24417			}
24418			$this->x = $this->lMargin;
24419		} else {
24420			$this->AddPage($this->CurOrientation);
24421		}
24422	}
24423
24424	function printcolumnbuffer()
24425	{
24426		// Columns ended (but page not ended) -> try to match all columns - unless disabled by using a custom column-break
24427		if (!$this->ColActive && $this->ColumnAdjust && !$this->keepColumns) {
24428			// Calculate adjustment to add to each column to calculate rel_y value
24429			$this->ColDetails[0]['add_y'] = 0;
24430			$last_col = 0;
24431			// Recursively add previous column's height
24432			for ($i = 1; $i < $this->NbCol; $i++) {
24433				if (isset($this->ColDetails[$i]['bottom_margin']) && $this->ColDetails[$i]['bottom_margin']) { // If any entries in the column
24434					$this->ColDetails[$i]['add_y'] = ($this->ColDetails[$i - 1]['bottom_margin'] - $this->y0) + $this->ColDetails[$i - 1]['add_y'];
24435					$last_col = $i;  // Last column actually printed
24436				}
24437			}
24438
24439			// Calculate value for each position sensitive entry as though for one column
24440			foreach ($this->columnbuffer as $key => $s) {
24441				$t = $s['s'];
24442				if ($t == 'ACROFORM') {
24443					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
24444					$this->columnbuffer[$key]['s'] = '';
24445				} elseif (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) {
24446					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
24447				} elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) {
24448					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
24449				} elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) {
24450					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
24451				} elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) {
24452					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
24453				} elseif (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) {
24454					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
24455				} elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t)) {
24456					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
24457				}
24458			}
24459			foreach ($this->internallink as $key => $f) {
24460				if (is_array($f) && isset($f['col'])) {
24461					$this->internallink[$key]['rel_y'] = $f['Y'] + $this->ColDetails[$f['col']]['add_y'] - $this->y0;
24462				}
24463			}
24464
24465			$breaks = [];
24466			foreach ($this->breakpoints as $c => $bpa) {
24467				foreach ($bpa as $rely) {
24468					$breaks[] = $rely + $this->ColDetails[$c]['add_y'] - $this->y0;
24469				}
24470			}
24471
24472
24473			if (isset($this->ColDetails[$last_col]['bottom_margin'])) {
24474				$lcbm = $this->ColDetails[$last_col]['bottom_margin'];
24475			} else {
24476				$lcbm = 0;
24477			}
24478			$sum_h = $this->ColDetails[$last_col]['add_y'] + $lcbm - $this->y0;
24479			// $sum_h = max($this->ColDetails[$last_col]['add_y'] + $this->ColDetails[$last_col]['bottom_margin'] - $this->y0, end($breaks));
24480			$target_h = ($sum_h / $this->NbCol);
24481
24482			$cbr = [];
24483			for ($i = 1; $i < $this->NbCol; $i++) {
24484				$th = ($sum_h * $i / $this->NbCol);
24485				foreach ($breaks as $bk => $val) {
24486					if ($val > $th) {
24487						if (($val - $th) < ($th - $breaks[$bk - 1])) {
24488							$cbr[$i - 1] = $val;
24489						} else {
24490							$cbr[$i - 1] = $breaks[$bk - 1];
24491						}
24492						break;
24493					}
24494				}
24495			}
24496			$cbr[($this->NbCol - 1)] = $sum_h;
24497
24498			// mPDF 6
24499			// Avoid outputing with 1st column empty
24500			if (isset($cbr[0]) && $cbr[0] == 0) {
24501				for ($i = 0; $i < $this->NbCol - 1; $i++) {
24502					$cbr[$i] = $cbr[$i + 1];
24503				}
24504			}
24505
24506			// Now update the columns - divide into columns of approximately equal value
24507			$last_new_col = 0;
24508			$yadj = 0; // mm
24509			$xadj = 0;
24510			$last_col_bottom = 0;
24511			$lowest_bottom_y = 0;
24512			$block_bottom = 0;
24513			$newcolumn = 0;
24514			foreach ($this->columnbuffer as $key => $s) {
24515				if (isset($s['rel_y'])) { // only process position sensitive data
24516					if ($s['rel_y'] >= $cbr[$newcolumn]) {
24517						$newcolumn++;
24518					} else {
24519						$newcolumn = $last_new_col;
24520					}
24521
24522
24523					$block_bottom = max($block_bottom, ($s['rel_y'] + $s['h']));
24524
24525					if ($this->directionality == 'rtl') { // *OTL*
24526						$xadj = -(($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap)); // *OTL*
24527					} // *OTL*
24528					else { // *OTL*
24529						$xadj = ($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap);
24530					} // *OTL*
24531
24532					if ($last_new_col != $newcolumn) { // Added new column
24533						$last_col_bottom = $this->columnbuffer[$key]['rel_y'];
24534						$block_bottom = 0;
24535					}
24536					$yadj = ($s['rel_y'] - $s['y']) - ($last_col_bottom) + $this->y0;
24537					// callback function
24538					$t = $s['s'];
24539
24540					// mPDF 5.7+
24541					$t = $this->columnAdjustPregReplace('Td', $xadj, $yadj, '/BT (\d+\.\d\d+) (\d+\.\d\d+) Td/', $t);
24542					$t = $this->columnAdjustPregReplace('re', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) ([\-]{0,1}\d+\.\d\d+) re/', $t);
24543					$t = $this->columnAdjustPregReplace('l', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) l/', $t);
24544					$t = $this->columnAdjustPregReplace('img', $xadj, $yadj, '/q (\d+\.\d\d+) 0 0 (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) cm \/(I|FO)/', $t);
24545					$t = $this->columnAdjustPregReplace('draw', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) m/', $t);
24546					$t = $this->columnAdjustPregReplace('bezier', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) c/', $t);
24547
24548					$this->columnbuffer[$key]['s'] = $t;
24549					$this->columnbuffer[$key]['newcol'] = $newcolumn;
24550					$this->columnbuffer[$key]['newy'] = $s['y'] + $yadj;
24551					$last_new_col = $newcolumn;
24552					$clb = $s['y'] + $yadj + $s['h']; // bottom_margin of current
24553					if ((isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb > $this->ColDetails[$newcolumn]['max_bottom']) || (!isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb)) {
24554						$this->ColDetails[$newcolumn]['max_bottom'] = $clb;
24555					}
24556					if ($clb > $lowest_bottom_y) {
24557						$lowest_bottom_y = $clb;
24558					}
24559					// Adjust LINKS
24560					if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
24561						$ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
24562						$this->PageLinks[$this->page][$ref][0] += ($xadj * Mpdf::SCALE);
24563						$this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE);
24564						unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
24565					}
24566					// Adjust FORM FIELDS
24567					if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
24568						$ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
24569						$this->form->forms[$ref]['x'] += ($xadj);
24570						$this->form->forms[$ref]['y'] += ($yadj);
24571						unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
24572					}
24573					/* -- ANNOTATIONS -- */
24574					if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
24575						$ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
24576						if ($this->PageAnnots[$this->page][$ref]['x'] < 0) {
24577							$this->PageAnnots[$this->page][$ref]['x'] -= ($xadj);
24578						} else {
24579							$this->PageAnnots[$this->page][$ref]['x'] += ($xadj);
24580						}
24581						$this->PageAnnots[$this->page][$ref]['y'] += ($yadj); // unlike PageLinks, Page annots has y values from top in mm
24582						unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
24583					}
24584					/* -- END ANNOTATIONS -- */
24585				}
24586			}
24587
24588			/* -- BOOKMARKS -- */
24589			// Adjust Bookmarks
24590			foreach ($this->col_BMoutlines as $v) {
24591				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']];
24592			}
24593			/* -- END BOOKMARKS -- */
24594
24595			/* -- TOC -- */
24596
24597			// Adjust ToC
24598			foreach ($this->col_toc as $v) {
24599				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
24600				$this->links[$v['link']][1] = $this->y0;
24601			}
24602			/* -- END TOC -- */
24603
24604			// Adjust column length to be equal
24605			if ($this->colvAlign == 'J') {
24606				foreach ($this->columnbuffer as $key => $s) {
24607					if (isset($s['rel_y'])) { // only process position sensitive data
24608						// Set ratio to expand y values or heights
24609						if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {
24610							$ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));
24611						} else {
24612							$ratio = 1;
24613						}
24614						if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
24615							$yadj = ($s['newy'] - $this->y0) * ($ratio - 1);
24616
24617							// Adjust LINKS
24618							if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
24619								$ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
24620								$this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value
24621								$this->PageLinks[$this->page][$ref][3] *= $ratio; // height
24622								unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
24623							}
24624							// Adjust FORM FIELDS
24625							if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
24626								$ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
24627								$this->form->forms[$ref]['x'] += ($xadj);
24628								$this->form->forms[$ref]['y'] += ($yadj);
24629								unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
24630							}
24631							/* -- ANNOTATIONS -- */
24632							if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
24633								$ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
24634								$this->PageAnnots[$this->page][$ref]['y'] += ($yadj);
24635								unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
24636							}
24637							/* -- END ANNOTATIONS -- */
24638						}
24639					}
24640				}
24641				foreach ($this->internallink as $key => $f) {
24642					if (is_array($f) && isset($f['col'])) {
24643						$last_col_bottom = 0;
24644						for ($nbc = 0; $nbc < $this->NbCol; $nbc++) {
24645							if ($f['rel_y'] >= $cbr[$nbc]) {
24646								$last_col_bottom = $cbr[$nbc];
24647							}
24648						}
24649						$yadj = ($f['rel_y'] - $f['Y']) - $last_col_bottom + $this->y0;
24650						$f['Y'] += $yadj;
24651						unset($f['col']);
24652						unset($f['rel_y']);
24653						$this->internallink[$key] = $f;
24654					}
24655				}
24656
24657				$last_col = -1;
24658				$trans_on = false;
24659				foreach ($this->columnbuffer as $key => $s) {
24660					if (isset($s['rel_y'])) { // only process position sensitive data
24661						// Set ratio to expand y values or heights
24662						if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {
24663							$ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));
24664						} else {
24665							$ratio = 1;
24666						}
24667						if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
24668							// Start Transformation
24669							$this->pages[$this->page] .= $this->StartTransform(true) . "\n";
24670							$this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n";
24671							$trans_on = true;
24672						}
24673					}
24674					// Now output the adjusted values
24675					$this->pages[$this->page] .= $s['s'] . "\n";
24676					if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) { // only process position sensitive data
24677						// Stop Transformation
24678						$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
24679						$trans_on = false;
24680					}
24681				}
24682				if ($trans_on) {
24683					$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
24684				}
24685			} else { // if NOT $this->colvAlign == 'J'
24686				// Now output the adjusted values
24687				foreach ($this->columnbuffer as $s) {
24688					$this->pages[$this->page] .= $s['s'] . "\n";
24689				}
24690			}
24691			if ($lowest_bottom_y > 0) {
24692				$this->y = $lowest_bottom_y;
24693			}
24694		} // Columns not ended but new page -> align columns (can leave the columns alone - just tidy up the height)
24695		elseif ($this->colvAlign == 'J' && $this->ColumnAdjust && !$this->keepColumns) {
24696			// calculate the lowest bottom margin
24697			$lowest_bottom_y = 0;
24698			foreach ($this->columnbuffer as $key => $s) {
24699				// Only process output data
24700				$t = $s['s'];
24701				if ($t == 'ACROFORM' || (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) ||
24702					(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) ||
24703					(preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) ||
24704					(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) ||
24705					(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) {
24706					$clb = $s['y'] + $s['h'];
24707					if ((isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom']) || !isset($this->ColDetails[$s['col']]['max_bottom'])) {
24708						$this->ColDetails[$s['col']]['max_bottom'] = $clb;
24709					}
24710					if ($clb > $lowest_bottom_y) {
24711						$lowest_bottom_y = $clb;
24712					}
24713					$this->columnbuffer[$key]['rel_y'] = $s['y']; // Marks position sensitive data to process later
24714					if ($t == 'ACROFORM') {
24715						$this->columnbuffer[$key]['s'] = '';
24716					}
24717				}
24718			}
24719			// Adjust column length equal
24720			foreach ($this->columnbuffer as $key => $s) {
24721				// Set ratio to expand y values or heights
24722				if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) {
24723					$ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));
24724				} else {
24725					$ratio = 1;
24726				}
24727				if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
24728					$yadj = ($s['y'] - $this->y0) * ($ratio - 1);
24729
24730					// Adjust LINKS
24731					if (isset($s['rel_y'])) { // only process position sensitive data
24732						// otherwise triggers for all entries in column buffer (.e.g. formatting) and makes below adjustments more than once
24733						if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
24734							$ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
24735							$this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value
24736							$this->PageLinks[$this->page][$ref][3] *= $ratio; // height
24737							unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
24738						}
24739						// Adjust FORM FIELDS
24740						if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
24741							$ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
24742							$this->form->forms[$ref]['x'] += ($xadj);
24743							$this->form->forms[$ref]['y'] += ($yadj);
24744							unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
24745						}
24746						/* -- ANNOTATIONS -- */
24747						if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
24748							$ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
24749							$this->PageAnnots[$this->page][$ref]['y'] += ($yadj);
24750							unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
24751						}
24752						/* -- END ANNOTATIONS -- */
24753					}
24754				}
24755			}
24756
24757			/* -- BOOKMARKS -- */
24758
24759			// Adjust Bookmarks
24760			foreach ($this->col_BMoutlines as $v) {
24761				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']];
24762			}
24763			/* -- END BOOKMARKS -- */
24764
24765			/* -- TOC -- */
24766
24767			// Adjust ToC
24768			foreach ($this->col_toc as $v) {
24769				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
24770				$this->links[$v['link']][1] = $this->y0;
24771			}
24772			/* -- END TOC -- */
24773
24774			$trans_on = false;
24775			foreach ($this->columnbuffer as $key => $s) {
24776
24777				if (isset($s['rel_y'])) { // only process position sensitive data
24778
24779					// Set ratio to expand y values or heights
24780					if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) {
24781						$ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));
24782					} else {
24783						$ratio = 1;
24784					}
24785
24786					if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
24787						// Start Transformation
24788						$this->pages[$this->page] .= $this->StartTransform(true) . "\n";
24789						$this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n";
24790						$trans_on = true;
24791					}
24792				}
24793
24794				// Now output the adjusted values
24795				$this->pages[$this->page] .= $s['s'] . "\n";
24796				if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
24797					// Stop Transformation
24798					$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
24799					$trans_on = false;
24800				}
24801			}
24802
24803			if ($trans_on) {
24804				$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
24805			}
24806
24807			if ($lowest_bottom_y > 0) {
24808				$this->y = $lowest_bottom_y;
24809			}
24810
24811		} else { // Just reproduce the page as it was
24812
24813			// If page has not ended but height adjustment was disabled by custom column-break - adjust y
24814			$lowest_bottom_y = 0;
24815
24816			if (!$this->ColActive && (!$this->ColumnAdjust || $this->keepColumns)) {
24817
24818				// calculate the lowest bottom margin
24819				foreach ($this->columnbuffer as $key => $s) {
24820
24821					// Only process output data
24822					$t = $s['s'];
24823					if ($t === 'ACROFORM'
24824							|| (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t))
24825							|| (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t))
24826							|| (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t))
24827							|| (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t))
24828							|| (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t))
24829							|| (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) {
24830
24831						$clb = $s['y'] + $s['h'];
24832
24833						if (isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom'] || (!isset($this->ColDetails[$s['col']]['max_bottom']) && $clb)) {
24834							$this->ColDetails[$s['col']]['max_bottom'] = $clb;
24835						}
24836
24837						if ($clb > $lowest_bottom_y) {
24838							$lowest_bottom_y = $clb;
24839						}
24840					}
24841				}
24842			}
24843
24844			foreach ($this->columnbuffer as $key => $s) {
24845				if ($s['s'] != 'ACROFORM') {
24846					$this->pages[$this->page] .= $s['s'] . "\n";
24847				}
24848			}
24849
24850			if ($lowest_bottom_y > 0) {
24851				$this->y = $lowest_bottom_y;
24852			}
24853
24854			/* -- BOOKMARKS -- */
24855			// Output Bookmarks
24856			foreach ($this->col_BMoutlines as $v) {
24857				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
24858			}
24859			/* -- END BOOKMARKS -- */
24860
24861			/* -- TOC -- */
24862			// Output ToC
24863			foreach ($this->col_toc as $v) {
24864				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
24865			}
24866			/* -- END TOC -- */
24867		}
24868
24869		foreach ($this->internallink as $key => $f) {
24870
24871			if (isset($this->internallink[$key]['col'])) {
24872				unset($this->internallink[$key]['col']);
24873			}
24874
24875			if (isset($this->internallink[$key]['rel_y'])) {
24876				unset($this->internallink[$key]['rel_y']);
24877			}
24878		}
24879
24880		$this->columnbuffer = [];
24881		$this->ColDetails = [];
24882		$this->columnLinks = [];
24883		$this->columnAnnots = [];
24884		$this->columnForms = [];
24885
24886		$this->col_BMoutlines = [];
24887		$this->col_toc = [];
24888		$this->breakpoints = [];
24889	}
24890
24891	// mPDF 5.7+
24892	function columnAdjustPregReplace($type, $xadj, $yadj, $pattern, $subject)
24893	{
24894		preg_match($pattern, $subject, $matches);
24895
24896		if (!count($matches)) {
24897			return $subject;
24898		}
24899
24900		if (!isset($matches[3])) {
24901			$matches[3] = 0;
24902		}
24903
24904		if (!isset($matches[4])) {
24905			$matches[4] = 0;
24906		}
24907
24908		if (!isset($matches[5])) {
24909			$matches[5] = 0;
24910		}
24911
24912		if (!isset($matches[6])) {
24913			$matches[6] = 0;
24914		}
24915
24916		return str_replace($matches[0], $this->columnAdjustAdd($type, Mpdf::SCALE, $xadj, $yadj, $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]), $subject);
24917	}
24918	/* -- END COLUMNS -- */
24919
24920	// ==================================================================
24921	/* -- TABLES -- */
24922	function printcellbuffer()
24923	{
24924		if (count($this->cellBorderBuffer)) {
24925
24926			sort($this->cellBorderBuffer);
24927
24928			foreach ($this->cellBorderBuffer as $cbb) {
24929
24930				$cba = unpack("A16dom/nbord/A1side/ns/dbw/a6ca/A10style/dx/dy/dw/dh/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd/dover/", $cbb);
24931				$side = $cba['side'];
24932				$color = str_pad($cba['ca'], 6, "\x00");
24933
24934				$details = [];
24935
24936				$details[$side]['dom'] = (float) $cba['dom'];
24937				$details[$side]['s'] = $cba['s'];
24938				$details[$side]['w'] = $cba['bw'];
24939				$details[$side]['c'] = $color;
24940				$details[$side]['style'] = trim($cba['style']);
24941
24942				$details['mbw']['BL'] = $cba['mbl'];
24943				$details['mbw']['BR'] = $cba['mbr'];
24944				$details['mbw']['RT'] = $cba['mrt'];
24945				$details['mbw']['RB'] = $cba['mrb'];
24946				$details['mbw']['TL'] = $cba['mtl'];
24947				$details['mbw']['TR'] = $cba['mtr'];
24948				$details['mbw']['LT'] = $cba['mlt'];
24949				$details['mbw']['LB'] = $cba['mlb'];
24950
24951				$details['cellposdom'] = $cba['cpd'];
24952
24953				$details['p'] = $side;
24954
24955				if ($cba['over'] == 1) {
24956					$details[$side]['overlay'] = true;
24957				} else {
24958					$details[$side]['overlay'] = false;
24959				}
24960
24961				$this->_tableRect($cba['x'], $cba['y'], $cba['w'], $cba['h'], $cba['bord'], $details, false, false);
24962			}
24963
24964			$this->cellBorderBuffer = [];
24965		}
24966	}
24967
24968	// ==================================================================
24969	function printtablebuffer()
24970	{
24971
24972		if (!$this->table_rotate) {
24973
24974			$this->pages[$this->page] .= $this->tablebuffer;
24975
24976			foreach ($this->tbrot_Links as $p => $l) {
24977				foreach ($l as $v) {
24978					$this->PageLinks[$p][] = $v;
24979				}
24980			}
24981			$this->tbrot_Links = [];
24982
24983			/* -- ANNOTATIONS -- */
24984			foreach ($this->tbrot_Annots as $p => $l) {
24985				foreach ($l as $v) {
24986					$this->PageAnnots[$p][] = $v;
24987				}
24988			}
24989			$this->tbrot_Annots = [];
24990			/* -- END ANNOTATIONS -- */
24991
24992			/* -- BOOKMARKS -- */
24993			// Output Bookmarks
24994			foreach ($this->tbrot_BMoutlines as $v) {
24995				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
24996			}
24997			$this->tbrot_BMoutlines = [];
24998			/* -- END BOOKMARKS -- */
24999
25000			/* -- TOC -- */
25001			// Output ToC
25002			foreach ($this->tbrot_toc as $v) {
25003				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
25004			}
25005			$this->tbrot_toc = [];
25006			/* -- END TOC -- */
25007
25008			return;
25009		}
25010
25011		// elseif rotated
25012		$lm = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left'];
25013		$pw = $this->blk[$this->blklvl]['inner_width'];
25014
25015		// Start Transformation
25016		$this->pages[$this->page] .= $this->StartTransform(true) . "\n";
25017
25018		if ($this->table_rotate > 1) { // clockwise
25019
25020			if ($this->tbrot_align == 'L') {
25021				$xadj = $this->tbrot_h; // align L (as is)
25022			} elseif ($this->tbrot_align == 'R') {
25023				$xadj = $lm - $this->tbrot_x0 + ($pw); // align R
25024			} else {
25025				$xadj = $lm - $this->tbrot_x0 + (($pw + $this->tbrot_h) / 2); // align C
25026			}
25027
25028			$yadj = 0;
25029
25030		} else { // anti-clockwise
25031
25032			if ($this->tbrot_align == 'L') {
25033				$xadj = 0; // align L (as is)
25034			} elseif ($this->tbrot_align == 'R') {
25035				$xadj = $lm - $this->tbrot_x0 + ($pw - $this->tbrot_h); // align R
25036			} else {
25037				$xadj = $lm - $this->tbrot_x0 + (($pw - $this->tbrot_h) / 2); // align C
25038			}
25039
25040			$yadj = $this->tbrot_w;
25041		}
25042
25043
25044		$this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n";
25045		$this->pages[$this->page] .= $this->transformRotate($this->table_rotate, $this->tbrot_x0, $this->tbrot_y0, true) . "\n";
25046
25047		// Now output the adjusted values
25048		$this->pages[$this->page] .= $this->tablebuffer;
25049
25050		foreach ($this->tbrot_Links as $p => $l) {
25051
25052			foreach ($l as $v) {
25053
25054				$w = $v[2] / Mpdf::SCALE;
25055				$h = $v[3] / Mpdf::SCALE;
25056				$ax = ($v[0] / Mpdf::SCALE) - $this->tbrot_x0;
25057				$ay = (($this->hPt - $v[1]) / Mpdf::SCALE) - $this->tbrot_y0;
25058
25059				if ($this->table_rotate > 1) { // clockwise
25060					$bx = $this->tbrot_x0 + $xadj - $ay - $h;
25061					$by = $this->tbrot_y0 + $yadj + $ax;
25062				} else {
25063					$bx = $this->tbrot_x0 + $xadj + $ay;
25064					$by = $this->tbrot_y0 + $yadj - $ax - $w;
25065				}
25066
25067				$v[0] = $bx * Mpdf::SCALE;
25068				$v[1] = ($this->h - $by) * Mpdf::SCALE;
25069				$v[2] = $h * Mpdf::SCALE; // swap width and height
25070				$v[3] = $w * Mpdf::SCALE;
25071
25072				$this->PageLinks[$p][] = $v;
25073			}
25074		}
25075
25076		$this->tbrot_Links = [];
25077		foreach ($this->internallink as $key => $f) {
25078			if (is_array($f) && isset($f['tbrot'])) {
25079				$f['Y'] = $this->tbrot_y0;
25080				$f['PAGE'] = $this->page;
25081				unset($f['tbrot']);
25082				$this->internallink[$key] = $f;
25083			}
25084		}
25085
25086		/* -- ANNOTATIONS -- */
25087		foreach ($this->tbrot_Annots as $p => $l) {
25088			foreach ($l as $v) {
25089				$ax = abs($v['x']) - $this->tbrot_x0; // abs because -ve values are internally set and held for reference if annotMargin set
25090				$ay = $v['y'] - $this->tbrot_y0;
25091
25092				if ($this->table_rotate > 1) { // clockwise
25093					$bx = $this->tbrot_x0 + $xadj - $ay;
25094					$by = $this->tbrot_y0 + $yadj + $ax;
25095				} else {
25096					$bx = $this->tbrot_x0 + $xadj + $ay;
25097					$by = $this->tbrot_y0 + $yadj - $ax;
25098				}
25099
25100				if ($v['x'] < 0) {
25101					$v['x'] = -$bx;
25102				} else {
25103					$v['x'] = $bx;
25104				}
25105
25106				$v['y'] = ($by);
25107				$this->PageAnnots[$p][] = $v;
25108			}
25109		}
25110
25111		$this->tbrot_Annots = [];
25112		/* -- END ANNOTATIONS -- */
25113
25114		/* -- BOOKMARKS -- */
25115		// Adjust Bookmarks
25116		foreach ($this->tbrot_BMoutlines as $v) {
25117			$v['y'] = $this->tbrot_y0;
25118			$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page];
25119		}
25120		/* -- END BOOKMARKS -- */
25121
25122		/* -- TOC -- */
25123		// Adjust ToC - uses document page number
25124		foreach ($this->tbrot_toc as $v) {
25125			$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']];
25126			$this->links[$v['link']][1] = $this->tbrot_y0;
25127		}
25128		/* -- END TOC -- */
25129
25130		$this->tbrot_BMoutlines = [];
25131		$this->tbrot_toc = [];
25132
25133		// Stop Transformation
25134		$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
25135
25136		$this->y = $this->tbrot_y0 + $this->tbrot_w;
25137		$this->x = $this->lMargin;
25138
25139		$this->tablebuffer = '';
25140	}
25141
25142	/**
25143	 * Keep-with-table This buffers contents of h1-6 to keep on page with table
25144	 */
25145	function printkwtbuffer()
25146	{
25147		if (!$this->kwt_moved) {
25148
25149			foreach ($this->kwt_buffer as $s) {
25150				$this->pages[$this->page] .= $s['s'] . "\n";
25151			}
25152
25153			foreach ($this->kwt_Links as $p => $l) {
25154				foreach ($l as $v) {
25155					$this->PageLinks[$p][] = $v;
25156				}
25157			}
25158
25159			$this->kwt_Links = [];
25160
25161			/* -- ANNOTATIONS -- */
25162			foreach ($this->kwt_Annots as $p => $l) {
25163				foreach ($l as $v) {
25164					$this->PageAnnots[$p][] = $v;
25165				}
25166			}
25167			$this->kwt_Annots = [];
25168			/* -- END ANNOTATIONS -- */
25169
25170			/* -- INDEX -- */
25171			// Output Reference (index)
25172			foreach ($this->kwt_Reference as $v) {
25173
25174				$Present = 0;
25175
25176				for ($i = 0; $i < count($this->Reference); $i++) {
25177					if ($this->Reference[$i]['t'] == $v['t']) {
25178						$Present = 1;
25179						if (!in_array($v['op'], $this->Reference[$i]['p'])) {
25180							$this->Reference[$i]['p'][] = $v['op'];
25181						}
25182					}
25183				}
25184
25185				if ($Present == 0) {
25186					$this->Reference[] = ['t' => $v['t'], 'p' => [$v['op']]];
25187				}
25188			}
25189			$this->kwt_Reference = [];
25190			/* -- END INDEX -- */
25191
25192			/* -- BOOKMARKS -- */
25193			// Output Bookmarks
25194			foreach ($this->kwt_BMoutlines as $v) {
25195				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
25196			}
25197			$this->kwt_BMoutlines = [];
25198			/* -- END BOOKMARKS -- */
25199
25200			/* -- TOC -- */
25201			// Output ToC
25202			foreach ($this->kwt_toc as $v) {
25203				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
25204			}
25205			$this->kwt_toc = [];
25206			/* -- END TOC -- */
25207
25208			$this->pageoutput[$this->page] = []; // mPDF 6
25209
25210			return;
25211		}
25212
25213		// Start Transformation
25214		$this->pages[$this->page] .= $this->StartTransform(true) . "\n";
25215		$xadj = $this->lMargin - $this->kwt_x0;
25216		// $yadj = $this->y - $this->kwt_y0 ;
25217		$yadj = $this->tMargin - $this->kwt_y0;
25218
25219		$this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n";
25220
25221		// Now output the adjusted values
25222		foreach ($this->kwt_buffer as $s) {
25223			$this->pages[$this->page] .= $s['s'] . "\n";
25224		}
25225
25226		// Adjust hyperLinks
25227		foreach ($this->kwt_Links as $p => $l) {
25228			foreach ($l as $v) {
25229				$bx = $this->kwt_x0 + $xadj;
25230				$by = $this->kwt_y0 + $yadj;
25231				$v[0] = $bx * Mpdf::SCALE;
25232				$v[1] = ($this->h - $by) * Mpdf::SCALE;
25233				$this->PageLinks[$p][] = $v;
25234			}
25235		}
25236
25237		foreach ($this->internallink as $key => $f) {
25238			if (is_array($f) && isset($f['kwt'])) {
25239				$f['Y'] += $yadj;
25240				$f['PAGE'] = $this->page;
25241				unset($f['kwt']);
25242				$this->internallink[$key] = $f;
25243			}
25244		}
25245
25246		/* -- ANNOTATIONS -- */
25247		foreach ($this->kwt_Annots as $p => $l) {
25248			foreach ($l as $v) {
25249				$bx = $this->kwt_x0 + $xadj;
25250				$by = $this->kwt_y0 + $yadj;
25251				if ($v['x'] < 0) {
25252					$v['x'] = -$bx;
25253				} else {
25254					$v['x'] = $bx;
25255				}
25256				$v['y'] = $by;
25257				$this->PageAnnots[$p][] = $v;
25258			}
25259		}
25260		/* -- END ANNOTATIONS -- */
25261
25262		/* -- BOOKMARKS -- */
25263
25264		// Adjust Bookmarks
25265		foreach ($this->kwt_BMoutlines as $v) {
25266			if ($v['y'] != 0) {
25267				$v['y'] += $yadj;
25268			}
25269			$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page];
25270		}
25271		/* -- END BOOKMARKS -- */
25272
25273		/* -- INDEX -- */
25274		// Adjust Reference (index)
25275		foreach ($this->kwt_Reference as $v) {
25276
25277			$Present = 0;
25278
25279			// Search the reference (AND Ref/PageNo) in the array
25280			for ($i = 0; $i < count($this->Reference); $i++) {
25281				if ($this->Reference[$i]['t'] == $v['t']) {
25282					$Present = 1;
25283					if (!in_array($this->page, $this->Reference[$i]['p'])) {
25284						$this->Reference[$i]['p'][] = $this->page;
25285					}
25286				}
25287			}
25288
25289			if ($Present == 0) {
25290				$this->Reference[] = ['t' => $v['t'], 'p' => [$this->page]];
25291			}
25292		}
25293		/* -- END INDEX -- */
25294
25295		/* -- TOC -- */
25296
25297		// Adjust ToC
25298		foreach ($this->kwt_toc as $v) {
25299			$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']];
25300			$this->links[$v['link']][0] = $this->page;
25301			$this->links[$v['link']][1] += $yadj;
25302		}
25303		/* -- END TOC -- */
25304
25305
25306		$this->kwt_Links = [];
25307		$this->kwt_Annots = [];
25308
25309		$this->kwt_Reference = [];
25310		$this->kwt_BMoutlines = [];
25311		$this->kwt_toc = [];
25312
25313		// Stop Transformation
25314		$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
25315
25316		$this->kwt_buffer = [];
25317
25318		$this->y += $this->kwt_height;
25319		$this->pageoutput[$this->page] = []; // mPDF 6
25320	}
25321	/* -- END TABLES -- */
25322
25323	function printfloatbuffer()
25324	{
25325		if (count($this->floatbuffer)) {
25326			$this->objectbuffer = $this->floatbuffer;
25327			$this->printobjectbuffer(false);
25328			$this->objectbuffer = [];
25329			$this->floatbuffer = [];
25330			$this->floatmargins = [];
25331		}
25332	}
25333
25334	function Circle($x, $y, $r, $style = 'S')
25335	{
25336		$this->Ellipse($x, $y, $r, $r, $style);
25337	}
25338
25339	function Ellipse($x, $y, $rx, $ry, $style = 'S')
25340	{
25341		if ($style === 'F') {
25342			$op = 'f';
25343		} elseif ($style === 'FD' or $style === 'DF') {
25344			$op = 'B';
25345		} else {
25346			$op = 'S';
25347		}
25348
25349		$lx = 4 / 3 * (M_SQRT2 - 1) * $rx;
25350		$ly = 4 / 3 * (M_SQRT2 - 1) * $ry;
25351
25352		$h = $this->h;
25353
25354		$this->writer->write(sprintf('%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c', ($x + $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - ($y - $ly)) * Mpdf::SCALE, ($x + $lx) * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE, $x * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE));
25355		$this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c', ($x - $lx) * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE, ($x - $rx) * Mpdf::SCALE, ($h - ($y - $ly)) * Mpdf::SCALE, ($x - $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE));
25356		$this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c', ($x - $rx) * Mpdf::SCALE, ($h - ($y + $ly)) * Mpdf::SCALE, ($x - $lx) * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE, $x * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE));
25357		$this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c %s', ($x + $lx) * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - ($y + $ly)) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE, $op));
25358	}
25359
25360	/* -- DIRECTW -- */
25361	function AutosizeText($text, $w, $font, $style, $szfont = 72)
25362	{
25363
25364		$text = ' ' . $text . ' ';
25365
25366		$this->SetFont($font, $style, $szfont, false);
25367
25368		$text = $this->purify_utf8_text($text);
25369		if ($this->text_input_as_HTML) {
25370			$text = $this->all_entities_to_utf8($text);
25371		}
25372		if ($this->usingCoreFont) {
25373			$text = mb_convert_encoding($text, $this->mb_enc, 'UTF-8');
25374		}
25375
25376		// DIRECTIONALITY
25377		if (preg_match("/([" . $this->pregRTLchars . "])/u", $text)) {
25378			$this->biDirectional = true;
25379		}
25380
25381		$textvar = 0;
25382		$save_OTLtags = $this->OTLtags;
25383		$this->OTLtags = [];
25384
25385		if ($this->useKerning) {
25386			if ($this->CurrentFont['haskernGPOS']) {
25387				$this->OTLtags['Plus'] .= ' kern';
25388			} else {
25389				$textvar = ($textvar | TextVars::FC_KERNING);
25390			}
25391		}
25392
25393		/* -- OTL -- */
25394		// Use OTL OpenType Table Layout - GSUB & GPOS
25395		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
25396			$text = $this->otl->applyOTL($text, $this->CurrentFont['useOTL']);
25397			$OTLdata = $this->otl->OTLdata;
25398		}
25399		/* -- END OTL -- */
25400
25401		$this->OTLtags = $save_OTLtags;
25402
25403		$this->magic_reverse_dir($text, $this->directionality, $OTLdata);
25404
25405		$width = $this->sizeConverter->convert($w);
25406		$loop = 0;
25407
25408		while ($loop == 0) {
25409
25410			$this->SetFont($font, $style, $szfont, false);
25411			$sz = $this->GetStringWidth($text, true, $OTLdata, $textvar);
25412
25413			if ($sz > $w) {
25414				$szfont --;
25415			} else {
25416				$loop ++;
25417			}
25418		}
25419
25420		$this->SetFont($font, $style, $szfont, true, true);
25421		$this->Cell($w, 0, $text, 0, 0, "C", 0, '', 0, 0, 0, 'M', 0, false, $OTLdata, $textvar);
25422	}
25423	/* -- END DIRECTW -- */
25424
25425	// ====================================================
25426	// ====================================================
25427
25428	function magic_reverse_dir(&$chunk, $dir, &$chunkOTLdata)
25429	{
25430		/* -- OTL -- */
25431		if ($this->usingCoreFont) {
25432			return 0;
25433		}
25434
25435		if ($chunk == '') {
25436			return 0;
25437		}
25438
25439		if ($this->biDirectional || $dir == 'rtl') {
25440
25441			// check if string contains RTL text
25442			// including any added from OTL tables (in PUA)
25443			$pregRTLchars = $this->pregRTLchars;
25444
25445			if (isset($this->CurrentFont['rtlPUAstr']) && $this->CurrentFont['rtlPUAstr']) {
25446				$pregRTLchars .= $this->CurrentFont['rtlPUAstr'];
25447			}
25448
25449			if (!preg_match("/[" . $pregRTLchars . "]/u", $chunk) && $dir != 'rtl') {
25450				return 0;
25451			}   // Chunk doesn't contain RTL characters
25452
25453			$unicode = $this->UTF8StringToArray($chunk, false);
25454
25455			$isStrong = false;
25456			if (empty($chunkOTLdata)) {
25457				$this->getBasicOTLdata($chunkOTLdata, $unicode, $isStrong);
25458			}
25459
25460			$useGPOS = isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0x80);
25461
25462			// NB Returned $chunk may be a shorter string (with adjusted $cOTLdata) by removal of LRE, RLE etc embedding codes.
25463			list($chunk, $rtl_content) = $this->otl->bidiSort($unicode, $chunk, $dir, $chunkOTLdata, $useGPOS);
25464
25465			return $rtl_content;
25466		}
25467
25468		/* -- END OTL -- */
25469		return 0;
25470	}
25471
25472	/* -- OTL -- */
25473
25474	function getBasicOTLdata(&$chunkOTLdata, $unicode, &$is_strong)
25475	{
25476		if (empty($this->otl)) {
25477			$this->otl = new Otl($this, $this->fontCache);
25478		}
25479
25480		$chunkOTLdata['group'] = '';
25481		$chunkOTLdata['GPOSinfo'] = [];
25482		$chunkOTLdata['char_data'] = [];
25483
25484		foreach ($unicode as $char) {
25485
25486			$ucd_record = Ucdn::get_ucd_record($char);
25487			$chunkOTLdata['char_data'][] = ['bidi_class' => $ucd_record[2], 'uni' => $char];
25488
25489			if ($ucd_record[2] == 0 || $ucd_record[2] == 3 || $ucd_record[2] == 4) {
25490				$is_strong = true;
25491			} // contains strong character
25492
25493			if ($ucd_record[0] == Ucdn::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
25494				$chunkOTLdata['group'] .= 'M';
25495			} elseif ($char == 32 || $char == 12288) {
25496				$chunkOTLdata['group'] .= 'S';
25497			} else {
25498				$chunkOTLdata['group'] .= 'C';
25499			}
25500		}
25501	}
25502
25503	function _setBidiCodes($mode = 'start', $bdf = '')
25504	{
25505		$s = '';
25506
25507		if ($mode == 'end') {
25508
25509			// PDF comes before PDI to close isolate-override (e.g. "LRILROPDFPDI")
25510			if (strpos($bdf, 'PDF') !== false) {
25511				$s .= UtfString::code2utf(0x202C);
25512			} // POP DIRECTIONAL FORMATTING
25513
25514			if (strpos($bdf, 'PDI') !== false) {
25515				$s .= UtfString::code2utf(0x2069);
25516			} // POP DIRECTIONAL ISOLATE
25517
25518		} elseif ($mode == 'start') {
25519
25520			// LRI comes before LRO to open isolate-override (e.g. "LRILROPDFPDI")
25521			if (strpos($bdf, 'LRI') !== false) {  // U+2066 LRI
25522				$s .= UtfString::code2utf(0x2066);
25523			} elseif (strpos($bdf, 'RLI') !== false) { // U+2067 RLI
25524				$s .= UtfString::code2utf(0x2067);
25525			} elseif (strpos($bdf, 'FSI') !== false) { // U+2068 FSI
25526				$s .= UtfString::code2utf(0x2068);
25527			}
25528
25529			if (strpos($bdf, 'LRO') !== false) { // U+202D LRO
25530				$s .= UtfString::code2utf(0x202D);
25531			} elseif (strpos($bdf, 'RLO') !== false) { // U+202E RLO
25532				$s .= UtfString::code2utf(0x202E);
25533			} elseif (strpos($bdf, 'LRE') !== false) { // U+202A LRE
25534				$s .= UtfString::code2utf(0x202A);
25535			} elseif (strpos($bdf, 'RLE') !== false) { // U+202B RLE
25536				$s .= UtfString::code2utf(0x202B);
25537			}
25538		}
25539
25540		return $s;
25541	}
25542	/* -- END OTL -- */
25543
25544	function SetSubstitutions()
25545	{
25546		$subsarray = [];
25547		require __DIR__ . '/../data/subs_win-1252.php';
25548		$this->substitute = [];
25549		foreach ($subsarray as $key => $val) {
25550			$this->substitute[UtfString::code2utf($key)] = $val;
25551		}
25552	}
25553
25554	function SubstituteChars($html)
25555	{
25556		// only substitute characters between tags
25557		if (count($this->substitute)) {
25558			$a = preg_split('/(<.*?>)/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
25559			$html = '';
25560			foreach ($a as $i => $e) {
25561				if ($i % 2 == 0) {
25562					$e = strtr($e, $this->substitute);
25563				}
25564				$html .= $e;
25565			}
25566		}
25567
25568		return $html;
25569	}
25570
25571	function SubstituteCharsSIP(&$writehtml_a, &$writehtml_i, &$writehtml_e)
25572	{
25573		if (preg_match("/^(.*?)([\x{20000}-\x{2FFFF}]+)(.*)/u", $writehtml_e, $m)) {
25574			if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {
25575				$font = $this->CurrentFont['sipext'];
25576				if (!in_array($font, $this->available_unifonts)) {
25577					return 0;
25578				}
25579				$writehtml_a[$writehtml_i] = $writehtml_e = $m[1];
25580				array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
25581				$this->subPos = $writehtml_i;
25582				return 4;
25583			}
25584		}
25585
25586		return 0;
25587	}
25588
25589	/**
25590	 * If core font is selected in document which is not onlyCoreFonts - substitute with non-core font
25591	 */
25592	function SubstituteCharsNonCore(&$writehtml_a, &$writehtml_i, &$writehtml_e)
25593	{
25594		// Ignore if in Textarea
25595		if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {
25596			return 0;
25597		}
25598
25599		if (mb_convert_encoding(mb_convert_encoding($writehtml_e, $this->mb_enc, "UTF-8"), "UTF-8", $this->mb_enc) == $writehtml_e) {
25600			return 0;
25601		}
25602
25603		$cw = &$this->CurrentFont['cw'];
25604		$unicode = $this->UTF8StringToArray($writehtml_e, false);
25605		$start = -1;
25606		$end = 0;
25607		$flag = 0;
25608		$ftype = '';
25609		$u = [];
25610
25611		if (!$this->subArrMB) {
25612
25613			require __DIR__ . '/../data/subs_core.php';
25614
25615			$this->subArrMB['a'] = $aarr;
25616			$this->subArrMB['s'] = $sarr;
25617			$this->subArrMB['z'] = $zarr;
25618		}
25619
25620		foreach ($unicode as $c => $char) {
25621
25622			if (($char > 127 || ($flag == 1 && $char == 32)) && $char != 173 && (!isset($this->subArrMB['a'][$char]) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) {
25623				if ($flag == 0) {
25624					$start = $c;
25625				}
25626				$flag = 1;
25627				$u[] = $char;
25628			} elseif ($flag > 0) {
25629				$end = $c - 1;
25630				break;
25631			}
25632		}
25633
25634		if ($flag > 0 && !$end) {
25635			$end = count($unicode) - 1;
25636		}
25637
25638		if ($start == -1) {
25639			return 0;
25640		}
25641
25642		// Try in backup subs font
25643		if (!is_array($this->backupSubsFont)) {
25644			$this->backupSubsFont = ["$this->backupSubsFont"];
25645		}
25646
25647		foreach ($this->backupSubsFont as $bsfctr => $bsf) {
25648
25649			if ($this->fonttrans[$bsf] == 'chelvetica' || $this->fonttrans[$bsf] == 'ctimes' || $this->fonttrans[$bsf] == 'ccourier') {
25650				continue;
25651			}
25652
25653			$font = $bsf;
25654			unset($cw);
25655			$cw = '';
25656
25657			if (isset($this->fonts[$font])) {
25658				$cw = &$this->fonts[$font]['cw'];
25659			} elseif ($this->fontCache->has($font . '.cw.dat')) {
25660				$cw = $this->fontCache->load($font . '.cw.dat');
25661			} else {
25662				$prevFontFamily = $this->FontFamily;
25663				$prevFontStyle = $this->currentfontstyle;
25664				$prevFontSizePt = $this->FontSizePt;
25665				$this->SetFont($bsf, '', '', false);
25666				$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
25667			}
25668
25669			if (!$cw) {
25670				continue;
25671			}
25672
25673			$l = 0;
25674			foreach ($u as $char) {
25675				if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) {
25676					$l++;
25677				} else {
25678					if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font
25679						$cont = mb_substr($writehtml_e, $start + 1);
25680						$writehtml_e = mb_substr($writehtml_e, 0, $start + 1, 'UTF-8');
25681						array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]);
25682						$this->subPos = $writehtml_i + 1;
25683
25684						return 2;
25685					} else {
25686						break;
25687					}
25688				}
25689			}
25690
25691			if ($l > 0) {
25692				$patt = mb_substr($writehtml_e, $start, $l, 'UTF-8');
25693				if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
25694					$writehtml_e = $m[1];
25695					array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
25696					$this->subPos = $writehtml_i + 3;
25697
25698					return 4;
25699				}
25700			}
25701		}
25702
25703		unset($cw);
25704
25705		return 0;
25706	}
25707
25708	function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e)
25709	{
25710		// Ignore if in Textarea
25711		if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {
25712			return 0;
25713		}
25714
25715		$cw = &$this->CurrentFont['cw'];
25716		$unicode = $this->UTF8StringToArray($writehtml_e, false);
25717		$start = -1;
25718		$end = 0;
25719		$flag = 0;
25720		$ftype = '';
25721		$u = [];
25722
25723		foreach ($unicode as $c => $char) {
25724
25725			if (($flag == 0 || $flag == 2) && (!$this->_charDefined($cw, $char) || ($flag == 2 && $char == 32)) && $this->checkSIP && $char > 131071) {  // Unicode Plane 2 (SIP)
25726
25727				if (in_array($this->FontFamily, $this->available_CJK_fonts)) {
25728					return 0;
25729				}
25730
25731				if ($flag == 0) {
25732					$start = $c;
25733				}
25734
25735				$flag = 2;
25736				$u[] = $char;
25737
25738				// elseif (($flag == 0 || $flag==1) && $char != 173 && !$this->_charDefined($cw,$char) && ($char<1423 ||  ($char>3583 && $char < 11263))) {
25739
25740			} elseif (($flag == 0 || $flag == 1) && $char != 173 && (!$this->_charDefined($cw, $char) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) {
25741
25742				if ($flag == 0) {
25743					$start = $c;
25744				}
25745
25746				$flag = 1;
25747				$u[] = $char;
25748
25749			} elseif ($flag > 0) {
25750
25751				$end = $c - 1;
25752				break;
25753
25754			}
25755		}
25756
25757		if ($flag > 0 && !$end) {
25758			$end = count($unicode) - 1;
25759		}
25760
25761		if ($start == -1) {
25762			return 0;
25763		}
25764
25765		if ($flag == 2) { // SIP
25766
25767			// Check if current CJK font has a ext-B related font
25768			if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {
25769				$font = $this->CurrentFont['sipext'];
25770				unset($cw);
25771				$cw = '';
25772
25773				if (isset($this->fonts[$font])) {
25774					$cw = &$this->fonts[$font]['cw'];
25775				} elseif ($this->fontCache->has($font . '.cw.dat')) {
25776					$cw = $this->fontCache->load($font . '.cw.dat');
25777				} else {
25778					$prevFontFamily = $this->FontFamily;
25779					$prevFontStyle = $this->currentfontstyle;
25780					$prevFontSizePt = $this->FontSizePt;
25781					$this->SetFont($font, '', '', false);
25782					$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
25783				}
25784
25785				if (!$cw) {
25786					return 0;
25787				}
25788
25789				$l = 0;
25790				foreach ($u as $char) {
25791					if ($this->_charDefined($cw, $char) || $char > 131071) {
25792						$l++;
25793					} else {
25794						break;
25795					}
25796				}
25797
25798				if ($l > 0) {
25799					$patt = mb_substr($writehtml_e, $start, $l);
25800					if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
25801						$writehtml_e = $m[1];
25802						array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
25803						$this->subPos = $writehtml_i + 3;
25804						return 4;
25805					}
25806				}
25807			}
25808
25809			// Check Backup SIP font (defined in Config\FontVariables)
25810			if (isset($this->backupSIPFont) && $this->backupSIPFont) {
25811
25812				if ($this->currentfontfamily != $this->backupSIPFont) {
25813					$font = $this->backupSIPFont;
25814				} else {
25815					unset($cw);
25816					return 0;
25817				}
25818
25819				unset($cw);
25820				$cw = '';
25821
25822				if (isset($this->fonts[$font])) {
25823					$cw = &$this->fonts[$font]['cw'];
25824				} elseif ($this->fontCache->has($font . '.cw.dat')) {
25825					$cw = $this->fontCache->load($font . '.cw.dat');
25826				} else {
25827					$prevFontFamily = $this->FontFamily;
25828					$prevFontStyle = $this->currentfontstyle;
25829					$prevFontSizePt = $this->FontSizePt;
25830					$this->SetFont($this->backupSIPFont, '', '', false);
25831					$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
25832				}
25833
25834				if (!$cw) {
25835					return 0;
25836				}
25837
25838				$l = 0;
25839				foreach ($u as $char) {
25840					if ($this->_charDefined($cw, $char) || $char > 131071) {
25841						$l++;
25842					} else {
25843						break;
25844					}
25845				}
25846
25847				if ($l > 0) {
25848					$patt = mb_substr($writehtml_e, $start, $l);
25849					if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
25850						$writehtml_e = $m[1];
25851						array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
25852						$this->subPos = $writehtml_i + 3;
25853						return 4;
25854					}
25855				}
25856			}
25857
25858			return 0;
25859		}
25860
25861		// FIRST TRY CORE FONTS (when appropriate)
25862		if (!$this->PDFA && !$this->PDFX && !$this->biDirectional) {  // mPDF 6
25863			$repl = [];
25864			if (!$this->subArrMB) {
25865				require __DIR__ . '/../data/subs_core.php';
25866				$this->subArrMB['a'] = $aarr;
25867				$this->subArrMB['s'] = $sarr;
25868				$this->subArrMB['z'] = $zarr;
25869			}
25870			if (isset($this->subArrMB['a'][$u[0]])) {
25871				$font = 'tta';
25872				$ftype = 'C';
25873				foreach ($u as $char) {
25874					if (isset($this->subArrMB['a'][$char])) {
25875						$repl[] = $this->subArrMB['a'][$char];
25876					} else {
25877						break;
25878					}
25879				}
25880			} elseif (isset($this->subArrMB['z'][$u[0]])) {
25881				$font = 'ttz';
25882				$ftype = 'C';
25883				foreach ($u as $char) {
25884					if (isset($this->subArrMB['z'][$char])) {
25885						$repl[] = $this->subArrMB['z'][$char];
25886					} else {
25887						break;
25888					}
25889				}
25890			} elseif (isset($this->subArrMB['s'][$u[0]])) {
25891				$font = 'tts';
25892				$ftype = 'C';
25893				foreach ($u as $char) {
25894					if (isset($this->subArrMB['s'][$char])) {
25895						$repl[] = $this->subArrMB['s'][$char];
25896					} else {
25897						break;
25898					}
25899				}
25900			}
25901			if ($ftype == 'C') {
25902				$patt = mb_substr($writehtml_e, $start, count($repl));
25903				if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
25904					$writehtml_e = $m[1];
25905					array_splice($writehtml_a, $writehtml_i + 1, 0, [$font, implode('|', $repl), '/' . $font, $m[3]]); // e.g. <tts>
25906					$this->subPos = $writehtml_i + 3;
25907					return 4;
25908				}
25909				return 0;
25910			}
25911		}
25912
25913		// LASTLY TRY IN BACKUP SUBS FONT
25914		if (!is_array($this->backupSubsFont)) {
25915			$this->backupSubsFont = ["$this->backupSubsFont"];
25916		}
25917
25918		foreach ($this->backupSubsFont as $bsfctr => $bsf) {
25919			if ($this->currentfontfamily != $bsf) {
25920				$font = $bsf;
25921			} else {
25922				continue;
25923			}
25924
25925			unset($cw);
25926			$cw = '';
25927
25928			if (isset($this->fonts[$font])) {
25929				$cw = &$this->fonts[$font]['cw'];
25930			} elseif ($this->fontCache->has($font . '.cw.dat')) {
25931				$cw = $this->fontCache->load($font . '.cw.dat');
25932			} else {
25933				$prevFontFamily = $this->FontFamily;
25934				$prevFontStyle = $this->currentfontstyle;
25935				$prevFontSizePt = $this->FontSizePt;
25936				$this->SetFont($bsf, '', '', false);
25937				$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
25938				if ($this->fontCache->has($font . '.cw.dat')) {
25939					$cw = $this->fontCache->load($font . '.cw.dat');
25940				}
25941			}
25942
25943			if (!$cw) {
25944				continue;
25945			}
25946
25947			$l = 0;
25948			foreach ($u as $char) {
25949				if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) {  // Arabic and Indic
25950					$l++;
25951				} else {
25952					if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font
25953						$cont = mb_substr($writehtml_e, $start + 1);
25954						$writehtml_e = mb_substr($writehtml_e, 0, $start + 1);
25955						array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]);
25956						$this->subPos = $writehtml_i + 1;
25957						return 2;
25958					} else {
25959						break;
25960					}
25961				}
25962			}
25963
25964			if ($l > 0) {
25965				$patt = mb_substr($writehtml_e, $start, $l);
25966				if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
25967					$writehtml_e = $m[1];
25968					array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
25969					$this->subPos = $writehtml_i + 3;
25970					return 4;
25971				}
25972			}
25973		}
25974
25975		unset($cw);
25976
25977		return 0;
25978	}
25979
25980	function setHiEntitySubstitutions()
25981	{
25982		$entarr = include __DIR__ . '/../data/entity_substitutions.php';
25983
25984		foreach ($entarr as $key => $val) {
25985			$this->entsearch[] = '&' . $key . ';';
25986			$this->entsubstitute[] = UtfString::code2utf($val);
25987		}
25988	}
25989
25990	function SubstituteHiEntities($html)
25991	{
25992		// converts html_entities > ASCII 127 to unicode
25993		// Leaves in particular &lt; to distinguish from tag marker
25994		if (count($this->entsearch)) {
25995			$html = str_replace($this->entsearch, $this->entsubstitute, $html);
25996		}
25997
25998		return $html;
25999	}
26000
26001	/**
26002	 * Edited v1.2 Pass by reference; option to continue if invalid UTF-8 chars
26003	 */
26004	function is_utf8(&$string)
26005	{
26006		if ($string === mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32")) {
26007			return true;
26008		}
26009
26010		if ($this->ignore_invalid_utf8) {
26011			$string = mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32");
26012			return true;
26013		}
26014
26015		return false;
26016	}
26017
26018	/**
26019	 * For HTML
26020	 *
26021	 * Checks string is valid UTF-8 encoded
26022	 * converts html_entities > ASCII 127 to UTF-8
26023	 * Only exception - leaves low ASCII entities e.g. &lt; &amp; etc.
26024	 * Leaves in particular &lt; to distinguish from tag marker
26025	 */
26026	function purify_utf8($html, $lo = true)
26027	{
26028		if (!$this->is_utf8($html)) {
26029
26030			while (mb_convert_encoding(mb_convert_encoding($html, "UTF-32", "UTF-8"), "UTF-8", "UTF-32") != $html) {
26031
26032				$a = @iconv('UTF-8', 'UTF-8', $html);
26033				$error = error_get_last();
26034				if ($error && $error['message'] === 'iconv(): Detected an illegal character in input string') {
26035					throw new \Mpdf\MpdfException('Invalid input characters. Did you set $mpdf->in_charset properly?');
26036				}
26037
26038				$pos = $start = strlen($a);
26039				$err = '';
26040				while (ord(substr($html, $pos, 1)) > 128) {
26041					$err .= '[[#' . ord(substr($html, $pos, 1)) . ']]';
26042					$pos++;
26043				}
26044
26045				$this->logger->error($err, ['context' => LogContext::UTF8]);
26046				$html = substr($html, $pos);
26047			}
26048
26049			throw new \Mpdf\MpdfException("HTML contains invalid UTF-8 character(s). See log for further details");
26050		}
26051
26052		$html = preg_replace("/\r/", "", $html);
26053
26054		// converts html_entities > ASCII 127 to UTF-8
26055		// Leaves in particular &lt; to distinguish from tag marker
26056		$html = $this->SubstituteHiEntities($html);
26057
26058		// converts all &#nnn; or &#xHHH; to UTF-8 multibyte
26059		// If $lo==true then includes ASCII < 128
26060		$html = UtfString::strcode2utf($html, $lo);
26061
26062		return $html;
26063	}
26064
26065	/**
26066	 * For TEXT
26067	 */
26068	function purify_utf8_text($txt)
26069	{
26070		// Make sure UTF-8 string of characters
26071		if (!$this->is_utf8($txt)) {
26072			throw new \Mpdf\MpdfException("Text contains invalid UTF-8 character(s)");
26073		}
26074
26075		$txt = preg_replace("/\r/", "", $txt);
26076
26077		return ($txt);
26078	}
26079
26080	function all_entities_to_utf8($txt)
26081	{
26082		// converts txt_entities > ASCII 127 to UTF-8
26083		// Leaves in particular &lt; to distinguish from tag marker
26084		$txt = $this->SubstituteHiEntities($txt);
26085
26086		// converts all &#nnn; or &#xHHH; to UTF-8 multibyte
26087		$txt = UtfString::strcode2utf($txt);
26088
26089		$txt = $this->lesser_entity_decode($txt);
26090		return ($txt);
26091	}
26092
26093	/* -- BARCODES -- */
26094	/**
26095	 * UPC/EAN barcode
26096	 *
26097	 * EAN13, EAN8, UPCA, UPCE, ISBN, ISSN
26098	 * Accepts 12 or 13 digits with or without - hyphens
26099	 */
26100	function WriteBarcode($code, $showtext = 1, $x = '', $y = '', $size = 1, $border = 0, $paddingL = 1, $paddingR = 1, $paddingT = 2, $paddingB = 2, $height = 1, $bgcol = false, $col = false, $btype = 'ISBN', $supplement = '0', $supplement_code = '', $k = 1)
26101	{
26102		if (empty($code)) {
26103			return;
26104		}
26105
26106		$codestr = $code;
26107		$code = preg_replace('/\-/', '', $code);
26108
26109		$this->barcode = new Barcode();
26110		if ($btype == 'ISSN' || $btype == 'ISBN') {
26111			$arrcode = $this->barcode->getBarcodeArray($code, 'EAN13');
26112		} else {
26113			$arrcode = $this->barcode->getBarcodeArray($code, $btype);
26114		}
26115
26116		if ($arrcode === false) {
26117			throw new \Mpdf\MpdfException('Error in barcode string: ' . $codestr);
26118		}
26119
26120		if ((($btype === 'EAN13' || $btype === 'ISBN' || $btype === 'ISSN') && strlen($code) === 12)
26121				|| ($btype == 'UPCA' && strlen($code) === 11)
26122				|| ($btype == 'UPCE' && strlen($code) === 11)
26123				|| ($btype == 'EAN8' && strlen($code) === 7)) {
26124
26125			$code .= $arrcode['checkdigit'];
26126
26127			if (stristr($codestr, '-')) {
26128				$codestr .= '-' . $arrcode['checkdigit'];
26129			} else {
26130				$codestr .= $arrcode['checkdigit'];
26131			}
26132		}
26133
26134		if ($btype === 'ISBN') {
26135			$codestr = 'ISBN ' . $codestr;
26136		}
26137
26138		if ($btype === 'ISSN') {
26139			$codestr = 'ISSN ' . $codestr;
26140		}
26141
26142		if (empty($x)) {
26143			$x = $this->x;
26144		}
26145
26146		if (empty($y)) {
26147			$y = $this->y;
26148		}
26149
26150		// set foreground color
26151		$prevDrawColor = $this->DrawColor;
26152		$prevTextColor = $this->TextColor;
26153		$prevFillColor = $this->FillColor;
26154
26155		$lw = $this->LineWidth;
26156		$this->SetLineWidth(0.01);
26157
26158		$size /= $k; // in case resized in a table
26159
26160		$xres = $arrcode['nom-X'] * $size;
26161		$llm = $arrcode['lightmL'] * $arrcode['nom-X'] * $size; // Left Light margin
26162		$rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin
26163
26164		$bcw = ($arrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size
26165
26166		$fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
26167		$ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding
26168
26169		$fbwi = $fbw - 2; // Full barcode width incl. light margins - 2mm - for isbn string
26170		// cf. http://www.gs1uk.org/downloads/bar_code/Bar coding getting it right.pdf
26171		$num_height = 3 * $size;     // Height of numerals
26172		$fbh = $arrcode['nom-H'] * $size * $height;  // Full barcode height incl. numerals
26173		$bch = $fbh - (1.5 * $size);     // Barcode height of bars	 (3mm for numerals)
26174
26175		if (($btype == 'EAN13' && $showtext) || $btype == 'ISSN' || $btype == 'ISBN') { // Add height for ISBN string + margin from top of bars
26176			$tisbnm = 1.5 * $size; // Top margin between isbn (if shown) & bars
26177			$codestr_fontsize = 2.1 * $size;
26178			$paddingT += $codestr_fontsize + $tisbnm;
26179		}
26180
26181		$oh = $fbh + $paddingT + $paddingB;  // Full overall height incl. user-defined padding
26182
26183		// PRINT border background color
26184		$xpos = $x;
26185		$ypos = $y;
26186
26187		if ($col) {
26188			$this->SetDColor($col);
26189			$this->SetTColor($col);
26190		} else {
26191			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
26192			$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
26193		}
26194
26195		if ($bgcol) {
26196			$this->SetFColor($bgcol);
26197		} else {
26198			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
26199		}
26200
26201		if (!$bgcol && !$col) { // fn. called directly - not via HTML
26202
26203			if ($border) {
26204				$fillb = 'DF';
26205			} else {
26206				$fillb = 'F';
26207			}
26208
26209			$this->Rect($xpos, $ypos, $ow, $oh, $fillb);
26210		}
26211
26212
26213		// PRINT BARS
26214		$xpos = $x + $paddingL + $llm;
26215		$ypos = $y + $paddingT;
26216
26217		if ($col) {
26218			$this->SetFColor($col);
26219		} else {
26220			$this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
26221		}
26222
26223		if ($arrcode !== false) {
26224			foreach ($arrcode["bcode"] as $v) {
26225				$bw = ($v["w"] * $xres);
26226				if ($v["t"]) {
26227					// draw a vertical bar
26228					$this->Rect($xpos, $ypos, $bw, $bch, 'F');
26229				}
26230				$xpos += $bw;
26231			}
26232		}
26233
26234		// print text
26235		$prevFontFamily = $this->FontFamily;
26236		$prevFontStyle = $this->FontStyle;
26237		$prevFontSizePt = $this->FontSizePt;
26238
26239		// ISBN string
26240		if (($btype === 'EAN13' && $showtext) || $btype === 'ISBN' || $btype === 'ISSN') {
26241
26242			if ($this->onlyCoreFonts) {
26243				$this->SetFont('chelvetica');
26244			} else {
26245				$this->SetFont('sans');
26246			}
26247
26248			if ($bgcol) {
26249				$this->SetFColor($bgcol);
26250			} else {
26251				$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
26252			}
26253
26254			$this->x = $x + $paddingL + 1; // 1mm left margin (cf. $fbwi above)
26255
26256			// max width is $fbwi
26257			$loop = 0;
26258			while ($loop == 0) {
26259				$this->SetFontSize($codestr_fontsize * 1.4 * Mpdf::SCALE, false); // don't write
26260				$sz = $this->GetStringWidth($codestr);
26261
26262				if ($sz > $fbwi) {
26263					$codestr_fontsize -= 0.1;
26264				} else {
26265					$loop ++;
26266				}
26267			}
26268
26269			$this->SetFont('', '', $codestr_fontsize * 1.4 * Mpdf::SCALE, true, true); // * 1.4 because font height is only 7/10 of given mm
26270			// WORD SPACING
26271			if ($fbwi > $sz) {
26272				$xtra = $fbwi - $sz;
26273				$charspacing = $xtra / (strlen($codestr) - 1);
26274				if ($charspacing) {
26275					$this->writer->write(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE));
26276				}
26277			}
26278
26279			$this->y = $y + $paddingT - ($codestr_fontsize ) - $tisbnm;
26280			$this->Cell($fbw, $codestr_fontsize, $codestr);
26281
26282			if ($charspacing) {
26283				$this->writer->write('BT 0 Tc ET');
26284			}
26285		}
26286
26287
26288		// Bottom NUMERALS
26289		// mPDF 5.7.4
26290		if ($this->onlyCoreFonts) {
26291			$this->SetFont('ccourier');
26292			$fh = 1.3;
26293		} else {
26294			$this->SetFont('ocrb');
26295			$fh = 1.06;
26296		}
26297
26298		$charRO = '';
26299
26300		if ($btype === 'EAN13' || $btype === 'ISBN' || $btype === 'ISSN') {
26301
26302			$outerfontsize = 3; // Inner fontsize = 3
26303			$outerp = $xres * 4;
26304			$innerp = $xres * 2.5;
26305			$textw = ($bcw * 0.5) - $outerp - $innerp;
26306			$chars = 6; // number of numerals in each half
26307			$charLO = substr($code, 0, 1); // Left Outer
26308			$charLI = substr($code, 1, 6); // Left Inner
26309			$charRI = substr($code, 7, 6); // Right Inner
26310
26311			if (!$supplement) {
26312				$charRO = '>'; // Right Outer
26313			}
26314
26315		} elseif ($btype === 'UPCA') {
26316
26317			$outerfontsize = 2.3; // Inner fontsize = 3
26318			$outerp = $xres * 10;
26319			$innerp = $xres * 2.5;
26320			$textw = ($bcw * 0.5) - $outerp - $innerp;
26321			$chars = 5;
26322			$charLO = substr($code, 0, 1); // Left Outer
26323			$charLI = substr($code, 1, 5); // Left Inner
26324			$charRI = substr($code, 6, 5); // Right Inner
26325			$charRO = substr($code, 11, 1); // Right Outer
26326
26327		} elseif ($btype === 'UPCE') {
26328
26329			$outerfontsize = 2.3; // Inner fontsize = 3
26330			$outerp = $xres * 4;
26331			$innerp = 0;
26332			$textw = ($bcw * 0.5) - $outerp - $innerp;
26333			$chars = 3;
26334			$upce_code = $arrcode['code'];
26335			$charLO = substr($code, 0, 1); // Left Outer
26336			$charLI = substr($upce_code, 0, 3); // Left Inner
26337			$charRI = substr($upce_code, 3, 3); // Right Inner
26338			$charRO = substr($code, 11, 1); // Right Outer
26339
26340		} elseif ($btype === 'EAN8') {
26341
26342			$outerfontsize = 3; // Inner fontsize = 3
26343			$outerp = $xres * 4;
26344			$innerp = $xres * 2.5;
26345			$textw = ($bcw * 0.5) - $outerp - $innerp;
26346			$chars = 4;
26347			$charLO = '<'; // Left Outer
26348			$charLI = substr($code, 0, 4); // Left Inner
26349			$charRI = substr($code, 4, 4); // Right Inner
26350
26351			if (!$supplement) {
26352				$charRO = '>'; // Right Outer
26353			}
26354		}
26355
26356		$this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
26357
26358		if (!$this->usingCoreFont) { // character width at 3mm
26359			$cw = $this->_getCharWidth($this->CurrentFont['cw'], 32) * 3 * $fh * $size / 1000;
26360		} else {
26361			$cw = 600 * 3 * $fh * $size / 1000;
26362		}
26363
26364		// Outer left character
26365		$y_text = $y + $paddingT + $bch - ($num_height / 2);
26366		$y_text_outer = $y + $paddingT + $bch - ($num_height * ($outerfontsize / 3) / 2);
26367
26368		$this->x = $x + $paddingL - ($cw * ($outerfontsize / 3) * 0.1); // 0.1 is correction as char does not fill full width;
26369		$this->y = $y_text_outer;
26370		$this->Cell($cw, $num_height, $charLO);
26371
26372		// WORD SPACING for inner chars
26373		$xtra = $textw - ($cw * $chars);
26374		$charspacing = $xtra / ($chars - 1);
26375		if ($charspacing) {
26376			$this->writer->write(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE));
26377		}
26378
26379		if ($bgcol) {
26380			$this->SetFColor($bgcol);
26381		} else {
26382			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
26383		}
26384
26385		$this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
26386
26387		// Inner left half characters
26388		$this->x = $x + $paddingL + $llm + $outerp;
26389		$this->y = $y_text;
26390		$this->Cell($textw, $num_height, $charLI, 0, 0, '', 1);
26391
26392		// Inner right half characters
26393		$this->x = $x + $paddingL + $llm + ($bcw * 0.5) + $innerp;
26394		$this->y = $y_text;
26395		$this->Cell($textw, $num_height, $charRI, 0, 0, '', 1);
26396
26397		if ($charspacing) {
26398			$this->writer->write('BT 0 Tc ET');
26399		}
26400
26401		// Outer Right character
26402		$this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
26403
26404		$this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * ($outerfontsize / 3) * 0.9); // 0.9 is correction as char does not fill full width
26405		$this->y = $y_text_outer;
26406		$this->Cell($cw * ($outerfontsize / 3), $num_height, $charRO, 0, 0, 'R');
26407
26408		if ($supplement) { // EAN-2 or -5 Supplement
26409			// PRINT BARS
26410			$supparrcode = $this->barcode->getBarcodeArray($supplement_code, 'EAN' . $supplement);
26411
26412			if ($supparrcode === false) {
26413				throw new \Mpdf\MpdfException('Error in barcode string (supplement): ' . $codestr . ' ' . $supplement_code);
26414			}
26415
26416			if (strlen($supplement_code) != $supplement) {
26417				throw new \Mpdf\MpdfException('Barcode supplement incorrect: ' . $supplement_code);
26418			}
26419
26420			$llm = $fbw - (($arrcode['lightmR'] - $supparrcode['sepM']) * $arrcode['nom-X'] * $size); // Left Light margin
26421			$rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin
26422
26423			$bcw = ($supparrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size
26424
26425			$fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
26426			$ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding
26427			$bch = $fbh - (1.5 * $size) - ($num_height + 0.5);  // Barcode height of bars	 (3mm for numerals)
26428
26429			$xpos = $x + $paddingL + $llm;
26430			$ypos = $y + $paddingT + $num_height + 0.5;
26431
26432			if ($col) {
26433				$this->SetFColor($col);
26434			} else {
26435				$this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
26436			}
26437
26438			if ($supparrcode !== false) {
26439				foreach ($supparrcode["bcode"] as $v) {
26440					$bw = ($v["w"] * $xres);
26441					if ($v["t"]) {
26442						// draw a vertical bar
26443						$this->Rect($xpos, $ypos, $bw, $bch, 'F');
26444					}
26445					$xpos += $bw;
26446				}
26447			}
26448
26449			// Characters
26450			if ($bgcol) {
26451				$this->SetFColor($bgcol);
26452			} else {
26453				$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
26454			}
26455
26456			$this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
26457			$this->x = $x + $paddingL + $llm;
26458			$this->y = $y + $paddingT;
26459			$this->Cell($bcw, $num_height, $supplement_code, 0, 0, 'C');
26460
26461			// Outer Right character (light margin)
26462			$this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
26463			$this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * 0.9); // 0.9 is correction as char does not fill full width
26464			$this->y = $y + $paddingT;
26465			$this->Cell($cw * ($outerfontsize / 3), $num_height, '>', 0, 0, 'R');
26466		}
26467
26468		// Restore **************
26469		$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
26470		$this->DrawColor = $prevDrawColor;
26471		$this->TextColor = $prevTextColor;
26472		$this->FillColor = $prevFillColor;
26473		$this->SetLineWidth($lw);
26474		$this->SetY($y);
26475	}
26476
26477	/**
26478	 * POSTAL and OTHER barcodes
26479	 */
26480	function WriteBarcode2($code, $x = '', $y = '', $size = 1, $height = 1, $bgcol = false, $col = false, $btype = 'IMB', $print_ratio = '', $k = 1, $quiet_zone_left = null, $quiet_zone_right = null)
26481	{
26482		if (empty($code)) {
26483			return;
26484		}
26485
26486		$this->barcode = new Barcode();
26487		$arrcode = $this->barcode->getBarcodeArray($code, $btype, $print_ratio, $quiet_zone_left, $quiet_zone_right);
26488
26489		if (empty($x)) {
26490			$x = $this->x;
26491		}
26492
26493		if (empty($y)) {
26494			$y = $this->y;
26495		}
26496
26497		$prevDrawColor = $this->DrawColor;
26498		$prevTextColor = $this->TextColor;
26499		$prevFillColor = $this->FillColor;
26500		$lw = $this->LineWidth;
26501		$this->SetLineWidth(0.01);
26502		$size /= $k; // in case resized in a table
26503		$xres = $arrcode['nom-X'] * $size;
26504
26505		if ($btype === 'IMB' || $btype === 'RM4SCC' || $btype === 'KIX' || $btype === 'POSTNET' || $btype === 'PLANET') {
26506			$llm = $arrcode['quietL'] / $k; // Left Quiet margin
26507			$rlm = $arrcode['quietR'] / $k; // Right Quiet margin
26508			$tlm = $blm = $arrcode['quietTB'] / $k;
26509			$height = 1;  // Overrides
26510		} elseif (in_array($btype, ['C128A', 'C128B', 'C128C', 'C128RAW', 'EAN128A', 'EAN128B', 'EAN128C', 'C39', 'C39+', 'C39E', 'C39E+', 'S25', 'S25+', 'I25', 'I25+', 'I25B', 'I25B+', 'C93', 'MSI', 'MSI+', 'CODABAR', 'CODE11'])) {
26511			$llm = $arrcode['lightmL'] * $xres; // Left Quiet margin
26512			$rlm = $arrcode['lightmR'] * $xres; // Right Quiet margin
26513			$tlm = $blm = $arrcode['lightTB'] * $xres * $height;
26514		}
26515
26516		$bcw = ($arrcode["maxw"] * $xres);
26517		$fbw = $bcw + $llm + $rlm;  // Full barcode width incl. light margins
26518
26519		$bch = ($arrcode["nom-H"] * $size * $height);
26520		$fbh = $bch + $tlm + $blm;  // Full barcode height
26521
26522		// PRINT border background color
26523		$xpos = $x;
26524		$ypos = $y;
26525
26526		if ($col) {
26527			$this->SetDColor($col);
26528			$this->SetTColor($col);
26529		} else {
26530			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
26531			$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
26532		}
26533
26534		if ($bgcol) {
26535			$this->SetFColor($bgcol);
26536		} else {
26537			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
26538		}
26539
26540		// PRINT BARS
26541		if ($col) {
26542			$this->SetFColor($col);
26543		} else {
26544			$this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
26545		}
26546		$xpos = $x + $llm;
26547
26548		if ($arrcode !== false) {
26549			foreach ($arrcode["bcode"] as $v) {
26550				$bw = ($v["w"] * $xres);
26551				if ($v["t"]) {
26552					$ypos = $y + $tlm + ($bch * $v['p'] / $arrcode['maxh']);
26553					$this->Rect($xpos, $ypos, $bw, ($v['h'] * $bch / $arrcode['maxh']), 'F');
26554				}
26555				$xpos += $bw;
26556			}
26557		}
26558
26559		// PRINT BEARER BARS
26560		if ($btype == 'I25B' || $btype == 'I25B+') {
26561			$this->Rect($x, $y, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');
26562			$this->Rect($x, $y + $tlm + $bch, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');
26563		}
26564
26565		// Restore **************
26566		$this->DrawColor = $prevDrawColor;
26567		$this->TextColor = $prevTextColor;
26568		$this->FillColor = $prevFillColor;
26569		$this->SetLineWidth($lw);
26570		$this->SetY($y);
26571	}
26572	/* -- END BARCODES -- */
26573
26574	function StartTransform($returnstring = false)
26575	{
26576		if ($returnstring) {
26577			return('q');
26578		} else {
26579			$this->writer->write('q');
26580		}
26581	}
26582
26583	function StopTransform($returnstring = false)
26584	{
26585		if ($returnstring) {
26586			return('Q');
26587		} else {
26588			$this->writer->write('Q');
26589		}
26590	}
26591
26592	function transformScale($s_x, $s_y, $x = '', $y = '', $returnstring = false)
26593	{
26594		if ($x === '') {
26595			$x = $this->x;
26596		}
26597
26598		if ($y === '') {
26599			$y = $this->y;
26600		}
26601
26602		if (($s_x == 0) or ( $s_y == 0)) {
26603			throw new \Mpdf\MpdfException('Please do not use values equal to zero for scaling');
26604		}
26605
26606		$y = ($this->h - $y) * Mpdf::SCALE;
26607		$x *= Mpdf::SCALE;
26608
26609		// calculate elements of transformation matrix
26610		$s_x /= 100;
26611		$s_y /= 100;
26612		$tm = [];
26613		$tm[0] = $s_x;
26614		$tm[1] = 0;
26615		$tm[2] = 0;
26616		$tm[3] = $s_y;
26617		$tm[4] = $x * (1 - $s_x);
26618		$tm[5] = $y * (1 - $s_y);
26619
26620		// scale the coordinate system
26621		if ($returnstring) {
26622			return($this->_transform($tm, true));
26623		} else {
26624			$this->_transform($tm);
26625		}
26626	}
26627
26628	function transformTranslate($t_x, $t_y, $returnstring = false)
26629	{
26630		// calculate elements of transformation matrix
26631		$tm = [];
26632		$tm[0] = 1;
26633		$tm[1] = 0;
26634		$tm[2] = 0;
26635		$tm[3] = 1;
26636		$tm[4] = $t_x * Mpdf::SCALE;
26637		$tm[5] = -$t_y * Mpdf::SCALE;
26638
26639		// translate the coordinate system
26640		if ($returnstring) {
26641			return($this->_transform($tm, true));
26642		} else {
26643			$this->_transform($tm);
26644		}
26645	}
26646
26647	function transformRotate($angle, $x = '', $y = '', $returnstring = false)
26648	{
26649		if ($x === '') {
26650			$x = $this->x;
26651		}
26652
26653		if ($y === '') {
26654			$y = $this->y;
26655		}
26656
26657		$angle = -$angle;
26658		$y = ($this->h - $y) * Mpdf::SCALE;
26659		$x *= Mpdf::SCALE;
26660
26661		// calculate elements of transformation matrix
26662		$tm = [];
26663		$tm[0] = cos(deg2rad($angle));
26664		$tm[1] = sin(deg2rad($angle));
26665		$tm[2] = -$tm[1];
26666		$tm[3] = $tm[0];
26667		$tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
26668		$tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
26669
26670		// rotate the coordinate system around ($x,$y)
26671		if ($returnstring) {
26672			return $this->_transform($tm, true);
26673		} else {
26674			$this->_transform($tm);
26675		}
26676	}
26677
26678	/**
26679	 * mPDF 5.7.3 TRANSFORMS
26680	 */
26681	function transformSkew($angle_x, $angle_y, $x = '', $y = '', $returnstring = false)
26682	{
26683		if ($x === '') {
26684			$x = $this->x;
26685		}
26686
26687		if ($y === '') {
26688			$y = $this->y;
26689		}
26690
26691		$angle_x = -$angle_x;
26692		$angle_y = -$angle_y;
26693
26694		$x *= Mpdf::SCALE;
26695		$y = ($this->h - $y) * Mpdf::SCALE;
26696
26697		// calculate elements of transformation matrix
26698		$tm = [];
26699		$tm[0] = 1;
26700		$tm[1] = tan(deg2rad($angle_y));
26701		$tm[2] = tan(deg2rad($angle_x));
26702		$tm[3] = 1;
26703		$tm[4] = -$tm[2] * $y;
26704		$tm[5] = -$tm[1] * $x;
26705
26706		// skew the coordinate system
26707		if ($returnstring) {
26708			return $this->_transform($tm, true);
26709		} else {
26710			$this->_transform($tm);
26711		}
26712	}
26713
26714	function _transform($tm, $returnstring = false)
26715	{
26716		if ($returnstring) {
26717			return(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
26718		} else {
26719			$this->writer->write(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
26720		}
26721	}
26722
26723	// AUTOFONT =========================
26724	function markScriptToLang($html)
26725	{
26726		if ($this->onlyCoreFonts) {
26727			return $html;
26728		}
26729
26730		$n = '';
26731		$a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
26732		foreach ($a as $i => $e) {
26733			if ($i % 2 == 0) {
26734
26735				// ignore if in Textarea
26736				if ($i > 0 && strtolower(substr($a[$i - 1], 1, 8)) == 'textarea') {
26737					$a[$i] = $e;
26738					continue;
26739				}
26740
26741				$e = UtfString::strcode2utf($e);
26742				$e = $this->lesser_entity_decode($e);
26743
26744				$earr = $this->UTF8StringToArray($e, false);
26745
26746				$scriptblock = 0;
26747				$scriptblocks = [];
26748				$scriptblocks[0] = 0;
26749				$chardata = [];
26750				$subchunk = 0;
26751				$charctr = 0;
26752
26753				foreach ($earr as $char) {
26754
26755					$ucd_record = Ucdn::get_ucd_record($char);
26756					$sbl = $ucd_record[6];
26757
26758					if ($sbl && $sbl != 40 && $sbl != 102) {
26759						if ($scriptblock == 0) {
26760							$scriptblock = $sbl;
26761							$scriptblocks[$subchunk] = $scriptblock;
26762						} elseif ($scriptblock > 0 && $scriptblock != $sbl) {
26763							// NEW (non-common) Script encountered in this chunk.
26764							// Start a new subchunk
26765							$subchunk++;
26766							$scriptblock = $sbl;
26767							$charctr = 0;
26768							$scriptblocks[$subchunk] = $scriptblock;
26769						}
26770					}
26771
26772					$chardata[$subchunk][$charctr]['script'] = $sbl;
26773					$chardata[$subchunk][$charctr]['uni'] = $char;
26774					$charctr++;
26775				}
26776
26777				// If scriptblock[x] = common & non-baseScript
26778				// and scriptblock[x+1] = baseScript
26779				// Move common script from end of x to start of x+1
26780				for ($sch = 0; $sch < $subchunk; $sch++) {
26781					if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $scriptblocks[$sch + 1] == $this->baseScript) {
26782						$end = count($chardata[$sch]) - 1;
26783						while ($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script
26784							$tmp = array_pop($chardata[$sch]);
26785							array_unshift($chardata[$sch + 1], $tmp);
26786							$end--;
26787						}
26788					}
26789				}
26790
26791				$o = '';
26792				for ($sch = 0; $sch <= $subchunk; $sch++) {
26793
26794					if (isset($chardata[$sch])) {
26795						$s = '';
26796						for ($j = 0; $j < count($chardata[$sch]); $j++) {
26797							$s .= UtfString::code2utf($chardata[$sch][$j]['uni']);
26798						}
26799
26800						// ZZZ99 Undo lesser_entity_decode as above - but only for <>&
26801						$s = str_replace("&", "&amp;", $s);
26802						$s = str_replace("<", "&lt;", $s);
26803						$s = str_replace(">", "&gt;", $s);
26804
26805						// Check Vietnamese if Latin script - even if Basescript
26806						if ($scriptblocks[$sch] == Ucdn::SCRIPT_LATIN && $this->autoVietnamese && preg_match("/([" . $this->scriptToLanguage->getLanguageDelimiters('viet') . "])/u", $s)) {
26807							$o .= '<span lang="vi" class="lang_vi">' . $s . '</span>';
26808						} elseif ($scriptblocks[$sch] == Ucdn::SCRIPT_ARABIC && $this->autoArabic) { // Check Arabic for different languages if Arabic script - even if Basescript
26809							if (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('sindhi') . "]/u", $s)) {
26810								$o .= '<span lang="sd" class="lang_sd">' . $s . '</span>';
26811							} elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('urdu') . "]/u", $s)) {
26812								$o .= '<span lang="ur" class="lang_ur">' . $s . '</span>';
26813							} elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('pashto') . "]/u", $s)) {
26814								$o .= '<span lang="ps" class="lang_ps">' . $s . '</span>';
26815							} elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('persian') . "]/u", $s)) {
26816								$o .= '<span lang="fa" class="lang_fa">' . $s . '</span>';
26817							} elseif ($this->baseScript != Ucdn::SCRIPT_ARABIC && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) {
26818								$o .= '<span lang="' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '" class="lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '">' . $s . '</span>';
26819							} else {
26820								// Just output chars
26821								$o .= $s;
26822							}
26823						} elseif ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) { // Identify Script block if not Basescript, and mark up as language
26824							// Encase in <span>
26825							$o .= '<span lang="' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '" class="lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '">';
26826							$o .= $s;
26827							$o .= '</span>';
26828						} else {
26829							// Just output chars
26830							$o .= $s;
26831						}
26832					}
26833				}
26834
26835				$a[$i] = $o;
26836			} else {
26837				$a[$i] = '<' . $e . '>';
26838			}
26839		}
26840
26841		$n = implode('', $a);
26842
26843		return $n;
26844	}
26845
26846	/* -- COLUMNS -- */
26847	/**
26848	 * Callback function from function printcolumnbuffer in mpdf
26849	 */
26850	function columnAdjustAdd($type, $k, $xadj, $yadj, $a, $b, $c = 0, $d = 0, $e = 0, $f = 0)
26851	{
26852		if ($type === 'Td') {  // xpos,ypos
26853
26854			$a += ($xadj * $k);
26855			$b -= ($yadj * $k);
26856
26857			return 'BT ' . sprintf('%.3F %.3F', $a, $b) . ' Td';
26858
26859		} elseif ($type === 're') {  // xpos,ypos,width,height
26860
26861			$a += ($xadj * $k);
26862			$b -= ($yadj * $k);
26863
26864			return sprintf('%.3F %.3F %.3F %.3F', $a, $b, $c, $d) . ' re';
26865
26866		} elseif ($type === 'l') {  // xpos,ypos,x2pos,y2pos
26867
26868			$a += ($xadj * $k);
26869			$b -= ($yadj * $k);
26870
26871			return sprintf('%.3F %.3F l', $a, $b);
26872
26873		} elseif ($type === 'img') {  // width,height,xpos,ypos
26874
26875			$c += ($xadj * $k);
26876			$d -= ($yadj * $k);
26877
26878			return sprintf('q %.3F 0 0 %.3F %.3F %.3F', $a, $b, $c, $d) . ' cm /' . $e;
26879
26880		} elseif ($type === 'draw') {  // xpos,ypos
26881
26882			$a += ($xadj * $k);
26883			$b -= ($yadj * $k);
26884
26885			return sprintf('%.3F %.3F m', $a, $b);
26886
26887		} elseif ($type === 'bezier') {  // xpos,ypos,x2pos,y2pos,x3pos,y3pos
26888
26889			$a += ($xadj * $k);
26890			$b -= ($yadj * $k);
26891			$c += ($xadj * $k);
26892			$d -= ($yadj * $k);
26893			$e += ($xadj * $k);
26894			$f -= ($yadj * $k);
26895
26896			return sprintf('%.3F %.3F %.3F %.3F %.3F %.3F', $a, $b, $c, $d, $e, $f) . ' c';
26897		}
26898	}
26899
26900	/* -- END COLUMNS -- */
26901
26902	// mPDF 5.7.3 TRANSFORMS
26903	function ConvertAngle($s, $makepositive = true)
26904	{
26905		if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i', $s, $m)) {
26906
26907			$angle = $m[1] + 0;
26908
26909			if (strtolower($m[2]) == 'deg') {
26910				$angle = $angle;
26911			} elseif (strtolower($m[2]) == 'grad') {
26912				$angle *= (360 / 400);
26913			} elseif (strtolower($m[2]) == 'rad') {
26914				$angle = rad2deg($angle);
26915			}
26916
26917			while ($angle >= 360) {
26918				$angle -= 360;
26919			}
26920
26921			while ($angle <= -360) {
26922				$angle += 360;
26923			}
26924
26925			if ($makepositive) { // always returns an angle between 0 and 360deg
26926				if ($angle < 0) {
26927					$angle += 360;
26928				}
26929			}
26930
26931		} else {
26932			$angle = $s + 0;
26933		}
26934
26935		return $angle;
26936	}
26937
26938	function lesser_entity_decode($html)
26939	{
26940		// supports the most used entity codes (only does ascii safe characters)
26941		$html = str_replace("&lt;", "<", $html);
26942		$html = str_replace("&gt;", ">", $html);
26943
26944		$html = str_replace("&apos;", "'", $html);
26945		$html = str_replace("&quot;", '"', $html);
26946		$html = str_replace("&amp;", "&", $html);
26947
26948		return $html;
26949	}
26950
26951	function AdjustHTML($html, $tabSpaces = 8)
26952	{
26953		$limit = ini_get('pcre.backtrack_limit');
26954
26955		if (0 >= (int) $limit) {
26956			throw new \Mpdf\MpdfException(sprintf(
26957				'mPDF will not process HTML with disabled pcre.backtrack_limit to prevent unexpected behaviours, please set a positive backtrack limit.',
26958				$limit
26959			));
26960		}
26961
26962		if (strlen($html) > (int) $limit) {
26963			throw new \Mpdf\MpdfException(sprintf(
26964				'The HTML code size is larger than pcre.backtrack_limit %d. You should use WriteHTML() with smaller string lengths.',
26965				$limit
26966			));
26967		}
26968
26969		preg_match_all("/(<annotation.*?>)/si", $html, $m);
26970		if (count($m[1])) {
26971			for ($i = 0; $i < count($m[1]); $i++) {
26972				$sub = preg_replace("/\n/si", "\xbb\xa4\xac", $m[1][$i]);
26973				$html = preg_replace('/' . preg_quote($m[1][$i], '/') . '/si', $sub, $html);
26974			}
26975		}
26976
26977		preg_match_all("/(<svg.*?<\/svg>)/si", $html, $svgi);
26978		if (count($svgi[0])) {
26979			for ($i = 0; $i < count($svgi[0]); $i++) {
26980				$file = $this->cache->write('/_tempSVG' . uniqid(random_int(1, 100000), true) . '_' . $i . '.svg', $svgi[0][$i]);
26981				$html = str_replace($svgi[0][$i], '<img src="' . $file . '" />', $html);
26982			}
26983		}
26984
26985		// Remove javascript code from HTML (should not appear in the PDF file)
26986		$html = preg_replace('/<script.*?<\/script>/is', '', $html);
26987
26988		// Remove special comments
26989		$html = preg_replace('/<!--mpdf/i', '', $html);
26990		$html = preg_replace('/mpdf-->/i', '', $html);
26991
26992		// Remove comments from HTML (should not appear in the PDF file)
26993		$html = preg_replace('/<!--.*?-->/s', '', $html);
26994
26995		$html = preg_replace('/\f/', '', $html); // replace formfeed by nothing
26996		$html = preg_replace('/\r/', '', $html); // replace carriage return by nothing
26997
26998		// Well formed XHTML end tags
26999		$html = preg_replace('/<(br|hr)>/i', "<\\1 />", $html); // mPDF 6
27000		$html = preg_replace('/<(br|hr)\/>/i', "<\\1 />", $html);
27001
27002		// Get rid of empty <thead></thead> etc
27003		$html = preg_replace('/<tr>\s*<\/tr>/i', '', $html);
27004		$html = preg_replace('/<thead>\s*<\/thead>/i', '', $html);
27005		$html = preg_replace('/<tfoot>\s*<\/tfoot>/i', '', $html);
27006		$html = preg_replace('/<table[^>]*>\s*<\/table>/i', '', $html);
27007
27008		// Remove spaces at end of table cells
27009		$html = preg_replace("/[ \n\r]+<\/t(d|h)/", '</t\\1', $html);
27010
27011		$html = preg_replace("/[ ]*<dottab\s*[\/]*>[ ]*/", '<dottab />', $html);
27012
27013		// Concatenates any Substitute characters from symbols/dingbats
27014		$html = str_replace('</tts><tts>', '|', $html);
27015		$html = str_replace('</ttz><ttz>', '|', $html);
27016		$html = str_replace('</tta><tta>', '|', $html);
27017
27018		$html = preg_replace('/<br \/>\s*/is', "<br />", $html);
27019
27020		$html = preg_replace('/<wbr[ \/]*>\s*/is', "&#173;", $html);
27021
27022		// Preserve '\n's in content between the tags <pre> and </pre>
27023		if (preg_match('/<pre/', $html)) {
27024
27025			$html_a = preg_split('/(\<\/?pre[^\>]*\>)/', $html, -1, 2);
27026			$h = [];
27027			$c = 0;
27028
27029			foreach ($html_a as $s) {
27030				if ($c > 1 && preg_match('/^<\/pre/i', $s)) {
27031					$c--;
27032					$s = preg_replace('/<\/pre/i', '</innerpre', $s);
27033				} elseif ($c > 0 && preg_match('/^<pre/i', $s)) {
27034					$c++;
27035					$s = preg_replace('/<pre/i', '<innerpre', $s);
27036				} elseif (preg_match('/^<pre/i', $s)) {
27037					$c++;
27038				} elseif (preg_match('/^<\/pre/i', $s)) {
27039					$c--;
27040				}
27041				array_push($h, $s);
27042			}
27043
27044			$html = implode('', $h);
27045		}
27046
27047		$thereispre = preg_match_all('#<pre(.*?)>(.*?)</pre>#si', $html, $temp);
27048
27049		// Preserve '\n's in content between the tags <textarea> and </textarea>
27050		$thereistextarea = preg_match_all('#<textarea(.*?)>(.*?)</textarea>#si', $html, $temp2);
27051		$html = preg_replace('/[\n]/', ' ', $html); // replace linefeed by spaces
27052		$html = preg_replace('/[\t]/', ' ', $html); // replace tabs by spaces
27053
27054		// Converts < to &lt; when not a tag
27055		$html = preg_replace('/<([^!\/a-zA-Z_:])/i', '&lt;\\1', $html); // mPDF 5.7.3
27056		$html = preg_replace("/[ ]+/", ' ', $html);
27057
27058		$html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html);
27059		$html = preg_replace('/\/(u|o)l>\s+<\/li/i', '/\\1l></li', $html);
27060		$html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html);
27061		$html = preg_replace('/\/li>\s+<li/i', '/li><li', $html);
27062		$html = preg_replace('/<(u|o)l([^>]*)>[ ]+/i', '<\\1l\\2>', $html);
27063		$html = preg_replace('/[ ]+<(u|o)l/i', '<\\1l', $html);
27064
27065		// Make self closing tabs valid XHTML
27066		// Tags which are self-closing: 1) Replaceable and 2) Non-replaced items
27067		$selftabs = 'input|hr|img|br|barcode|dottab';
27068		$selftabs2 = 'indexentry|indexinsert|bookmark|watermarktext|watermarkimage|column_break|columnbreak|newcolumn|newpage|page_break|pagebreak|formfeed|columns|toc|tocpagebreak|setpageheader|setpagefooter|sethtmlpageheader|sethtmlpagefooter|annotation';
27069
27070		// Fix self-closing tags which don't close themselves
27071		$html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . ')[^>\/]*)>/i', '\\1 />', $html);
27072
27073		// Fix self-closing tags that don't include a space between the tag name and the closing slash
27074		$html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . '))\/>/i', '\\1 />', $html);
27075
27076		$iterator = 0;
27077		while ($thereispre) { // Recover <pre attributes>content</pre>
27078			$temp[2][$iterator] = preg_replace('/<([^!\/a-zA-Z_:])/', '&lt;\\1', $temp[2][$iterator]); // mPDF 5.7.2	// mPDF 5.7.3
27079
27080			$temp[2][$iterator] = preg_replace_callback("/^([^\n\t]*?)\t/m", [$this, 'tabs2spaces_callback'], $temp[2][$iterator]); // mPDF 5.7+
27081			$temp[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp[2][$iterator]);
27082
27083			$temp[2][$iterator] = preg_replace('/\n/', "<br />", $temp[2][$iterator]);
27084			$temp[2][$iterator] = str_replace('\\', "\\\\", $temp[2][$iterator]);
27085			// $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.$temp[2][$iterator].'</erp>',$html,1);
27086			$html = preg_replace('#<pre(.*?)>(.*?)</pre>#si', '<erp' . $temp[1][$iterator] . '>' . str_replace('$', '\$', $temp[2][$iterator]) . '</erp>', $html, 1); // mPDF 5.7+
27087			$thereispre--;
27088			$iterator++;
27089		}
27090
27091		$iterator = 0;
27092		while ($thereistextarea) { // Recover <textarea attributes>content</textarea>
27093			$temp2[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp2[2][$iterator]);
27094			$temp2[2][$iterator] = str_replace('\\', "\\\\", $temp2[2][$iterator]);
27095			$html = preg_replace('#<textarea(.*?)>(.*?)</textarea>#si', '<aeratxet' . $temp2[1][$iterator] . '>' . trim($temp2[2][$iterator]) . '</aeratxet>', $html, 1);
27096			$thereistextarea--;
27097			$iterator++;
27098		}
27099
27100		// Restore original tag names
27101		$html = str_replace("<erp", "<pre", $html);
27102		$html = str_replace("</erp>", "</pre>", $html);
27103		$html = str_replace("<aeratxet", "<textarea", $html);
27104		$html = str_replace("</aeratxet>", "</textarea>", $html);
27105		$html = str_replace("</innerpre", "</pre", $html);
27106		$html = str_replace("<innerpre", "<pre", $html);
27107
27108		$html = preg_replace('/<textarea([^>]*)><\/textarea>/si', '<textarea\\1> </textarea>', $html);
27109		$html = preg_replace('/(<table[^>]*>)\s*(<caption)(.*?<\/caption>)(.*?<\/table>)/si', '\\2 position="top"\\3\\1\\4\\2 position="bottom"\\3', $html); // *TABLES*
27110		$html = preg_replace('/<(h[1-6])([^>]*)(>(?:(?!h[1-6]).)*?<\/\\1>\s*<table)/si', '<\\1\\2 keep-with-table="1"\\3', $html); // *TABLES*
27111		$html = preg_replace("/\xbb\xa4\xac/", "\n", $html);
27112
27113		// Fixes <p>&#8377</p> which browser copes with even though it is wrong!
27114		$html = preg_replace("/(&#[x]{0,1}[0-9a-f]{1,5})</i", "\\1;<", $html);
27115
27116		return $html;
27117	}
27118
27119	// mPDF 5.7+
27120	function tabs2spaces_callback($matches)
27121	{
27122		return (stripslashes($matches[1]) . str_repeat(' ', $this->tabSpaces - (mb_strlen(stripslashes($matches[1])) % $this->tabSpaces)));
27123	}
27124
27125	// mPDF 5.7+
27126	function date_callback($matches)
27127	{
27128		return date($matches[1]);
27129	}
27130
27131	// ========== OVERWRITE SEARCH STRING IN A PDF FILE ================
27132	function OverWrite($file_in, $search, $replacement, $dest = Destination::DOWNLOAD, $file_out = "mpdf")
27133	{
27134		$pdf = file_get_contents($file_in);
27135
27136		if (!is_array($search)) {
27137			$x = $search;
27138			$search = [$x];
27139		}
27140		if (!is_array($replacement)) {
27141			$x = $replacement;
27142			$replacement = [$x]; // mPDF 5.7.4
27143		}
27144
27145		if (!$this->onlyCoreFonts && !$this->usingCoreFont) {
27146			foreach ($search as $k => $val) {
27147				$search[$k] = $this->writer->utf8ToUtf16BigEndian($search[$k], false);
27148				$search[$k] = $this->writer->escape($search[$k]);
27149				$replacement[$k] = $this->writer->utf8ToUtf16BigEndian($replacement[$k], false);
27150				$replacement[$k] = $this->writer->escape($replacement[$k]);
27151			}
27152		} else {
27153			foreach ($replacement as $k => $val) {
27154				$replacement[$k] = mb_convert_encoding($replacement[$k], $this->mb_enc, 'utf-8');
27155				$replacement[$k] = $this->writer->escape($replacement[$k]);
27156			}
27157		}
27158
27159		// Get xref into array
27160		$xref = [];
27161		preg_match("/xref\n0 (\d+)\n(.*?)\ntrailer/s", $pdf, $m);
27162		$xref_objid = $m[1];
27163		preg_match_all('/(\d{10}) (\d{5}) (f|n)/', $m[2], $x);
27164		for ($i = 0; $i < count($x[0]); $i++) {
27165			$xref[] = [intval($x[1][$i]), $x[2][$i], $x[3][$i]];
27166		}
27167
27168		$changes = [];
27169		preg_match("/<<\s*\/Type\s*\/Pages\s*\/Kids\s*\[(.*?)\]\s*\/Count/s", $pdf, $m);
27170		preg_match_all("/(\d+) 0 R /s", $m[1], $o);
27171		$objlist = $o[1];
27172
27173		foreach ($objlist as $obj) {
27174			if ($this->compress) {
27175				preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Filter\s*\/FlateDecode\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m);
27176			} else {
27177				preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m);
27178			}
27179
27180			$s = $m[2];
27181			if (!$s) {
27182				continue;
27183			}
27184
27185			$oldlen = $m[1];
27186
27187			if ($this->encrypted) {
27188				$s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s);
27189			}
27190
27191			if ($this->compress) {
27192				$s = gzuncompress($s);
27193			}
27194
27195			foreach ($search as $k => $val) {
27196				$s = str_replace($search[$k], $replacement[$k], $s);
27197			}
27198
27199			if ($this->compress) {
27200				$s = gzcompress($s);
27201			}
27202
27203			if ($this->encrypted) {
27204				$s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s);
27205			}
27206
27207			$newlen = strlen($s);
27208
27209			$changes[($xref[$obj + 1][0])] = ($newlen - $oldlen) + (strlen($newlen) - strlen($oldlen));
27210
27211			if ($this->compress) {
27212				$newstr = ($obj + 1) . " 0 obj\n<</Filter /FlateDecode /Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n";
27213			} else {
27214				$newstr = ($obj + 1) . " 0 obj\n<</Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n";
27215			}
27216
27217			$pdf = str_replace($m[0], $newstr, $pdf);
27218		}
27219
27220		// Update xref in PDF
27221		krsort($changes);
27222		$newxref = "xref\n0 " . $xref_objid . "\n";
27223		foreach ($xref as $v) {
27224			foreach ($changes as $ck => $cv) {
27225				if ($v[0] > $ck) {
27226					$v[0] += $cv;
27227				}
27228			}
27229			$newxref .= sprintf('%010d', $v[0]) . ' ' . $v[1] . ' ' . $v[2] . " \n";
27230		}
27231		$newxref .= "trailer";
27232		$pdf = preg_replace("/xref\n0 \d+\n.*?\ntrailer/s", $newxref, $pdf);
27233
27234		// Update startxref in PDF
27235		preg_match("/startxref\n(\d+)\n%%EOF/s", $pdf, $m);
27236		$startxref = $m[1];
27237		$startxref += array_sum($changes);
27238		$pdf = preg_replace("/startxref\n(\d+)\n%%EOF/s", "startxref\n" . $startxref . "\n%%EOF", $pdf);
27239
27240		// OUTPUT
27241		switch ($dest) {
27242			case Destination::INLINE:
27243				if (isset($_SERVER['SERVER_NAME'])) {
27244					// We send to a browser
27245					header('Content-Type: application/pdf');
27246					header('Content-Length: ' . strlen($pdf));
27247					header('Content-disposition: inline; filename=' . $file_out);
27248				}
27249
27250				echo $pdf;
27251
27252				break;
27253
27254			case Destination::FILE:
27255				if (!$file_out) {
27256					$file_out = 'mpdf.pdf';
27257				}
27258
27259				$f = fopen($file_out, 'wb');
27260
27261				if (!$f) {
27262					throw new \Mpdf\MpdfException('Unable to create output file: ' . $file_out);
27263				}
27264
27265				fwrite($f, $pdf, strlen($pdf));
27266
27267				fclose($f);
27268
27269				break;
27270
27271			case Destination::STRING_RETURN:
27272				return $pdf;
27273
27274			case Destination::DOWNLOAD: // Download file
27275			default:
27276				if (isset($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
27277					header('Content-Type: application/force-download');
27278				} else {
27279					header('Content-Type: application/octet-stream');
27280				}
27281
27282				header('Content-Length: ' . strlen($pdf));
27283				header('Content-disposition: attachment; filename=' . $file_out);
27284
27285				echo $pdf;
27286
27287				break;
27288		}
27289	}
27290
27291
27292	function Thumbnail($file, $npr = 3, $spacing = 10)
27293	{
27294		// $npr = number per row
27295		$w = (($this->pgwidth + $spacing) / $npr) - $spacing;
27296		$oldlinewidth = $this->LineWidth;
27297		$this->SetLineWidth(0.02);
27298		$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
27299		$h = 0;
27300		$maxh = 0;
27301		$x = $_x = $this->lMargin;
27302		$_y = $this->tMargin;
27303
27304		if ($this->y == 0) {
27305			$y = $_y;
27306		} else {
27307			$y = $this->y;
27308		}
27309
27310		$pagecount = $this->setSourceFile($file);
27311
27312		for ($n = 1; $n <= $pagecount; $n++) {
27313			$tplidx = $this->importPage($n);
27314			$size = $this->useTemplate($tplidx, $x, $y, $w);
27315			$this->Rect($x, $y, $size['width'], $size['height']);
27316			$h = max($h, $size['height']);
27317			$maxh = max($h, $maxh);
27318
27319			if ($n % $npr == 0) {
27320				if (($y + $h + $spacing + $maxh) > $this->PageBreakTrigger && $n != $pagecount) {
27321					$this->AddPage();
27322					$x = $_x;
27323					$y = $_y;
27324				} else {
27325					$y += $h + $spacing;
27326					$x = $_x;
27327					$h = 0;
27328				}
27329			} else {
27330				$x += $w + $spacing;
27331			}
27332		}
27333		$this->SetLineWidth($oldlinewidth);
27334	}
27335
27336	function SetPageTemplate($tplidx = '')
27337	{
27338		if (!isset($this->importedPages[$tplidx])) {
27339			$this->pageTemplate = '';
27340			return false;
27341		}
27342		$this->pageTemplate = $tplidx;
27343	}
27344
27345	function SetDocTemplate($file = '', $continue = 0, $continue2pages = 0)
27346	{
27347		$this->docTemplate = $file;
27348		$this->docTemplateContinue = $continue;
27349		$this->docTemplateContinue2pages = $continue2pages;
27350
27351		if ($this->docTemplateContinue2pages) { // Enable continue when continue2pages is set
27352			$this->docTemplateContinue = $this->docTemplateContinue2pages;
27353		}
27354	}
27355
27356	/* -- END IMPORTS -- */
27357
27358	// JAVASCRIPT
27359	function _set_object_javascript($string)
27360	{
27361		$this->writer->object();
27362		$this->writer->write('<<');
27363		$this->writer->write('/S /JavaScript ');
27364		$this->writer->write('/JS ' . $this->writer->string($string));
27365		$this->writer->write('>>');
27366		$this->writer->write('endobj');
27367	}
27368
27369	function SetJS($script)
27370	{
27371		$this->js = $script;
27372	}
27373
27374	/**
27375	 * This function takes the last comma or dot (if any) to make a clean float, ignoring thousand separator, currency or any other letter
27376	 *
27377	 * @param string $num
27378	 * @see http://php.net/manual/de/function.floatval.php#114486
27379	 * @return float
27380	 */
27381	public function toFloat($num)
27382	{
27383		$dotPos = strrpos($num, '.');
27384		$commaPos = strrpos($num, ',');
27385		$sep = (($dotPos > $commaPos) && $dotPos) ? $dotPos : ((($commaPos > $dotPos) && $commaPos) ? $commaPos : false);
27386
27387		if (!$sep) {
27388			return floatval(preg_replace('/[^0-9]/', '', $num));
27389		}
27390
27391		return floatval(
27392			preg_replace('/[^0-9]/', '', substr($num, 0, $sep)) . '.' .
27393			preg_replace('/[^0-9]/', '', substr($num, $sep+1, strlen($num)))
27394		);
27395	}
27396
27397	public function getFontDescriptor()
27398	{
27399		return $this->fontDescriptor;
27400	}
27401
27402	/**
27403	 * Temporarily return the method to preserve example 44 yearbook
27404	 */
27405	public function _out($s)
27406	{
27407		$this->writer->write($s);
27408	}
27409
27410	/**
27411	 * @param string $html
27412	 * @param string $PAGENO
27413	 * @param string $NbPgGp
27414	 * @param string $NbPg
27415	 * @return string
27416	 */
27417	protected function aliasReplace($html, $PAGENO, $NbPgGp, $NbPg)
27418	{
27419		// Replaces for header and footer
27420		$html = str_replace('{PAGENO}', $PAGENO, $html);
27421		$html = str_replace($this->aliasNbPgGp, $NbPgGp, $html); // {nbpg}
27422		$html = str_replace($this->aliasNbPg, $NbPg, $html); // {nb}
27423
27424		// Replaces for the body
27425		$html = str_replace(mb_convert_encoding('{PAGENO}', 'UTF-16BE', 'UTF-8'), mb_convert_encoding($PAGENO, 'UTF-16BE', 'UTF-8'), $html);
27426		$html = str_replace(mb_convert_encoding($this->aliasNbPgGp, 'UTF-16BE', 'UTF-8'), mb_convert_encoding($NbPgGp, 'UTF-16BE', 'UTF-8'), $html); // {nbpg}
27427		$html = str_replace(mb_convert_encoding($this->aliasNbPg, 'UTF-16BE', 'UTF-8'), mb_convert_encoding($NbPg, 'UTF-16BE', 'UTF-8'), $html); // {nb}
27428
27429		// Date replace
27430		$html = preg_replace_callback('/\{DATE\s+(.*?)\}/', [$this, 'date_callback'], $html); // mPDF 5.7
27431
27432		return $html;
27433	}
27434
27435}
27436