1<?php
2
3namespace Mpdf;
4
5use fpdi_pdf_parser;
6use pdf_parser;
7
8use Mpdf\Config\ConfigVariables;
9use Mpdf\Config\FontVariables;
10
11use Mpdf\Color\ColorConverter;
12use Mpdf\Color\ColorModeConverter;
13use Mpdf\Color\ColorSpaceRestrictor;
14
15use Mpdf\Conversion;
16
17use Mpdf\Css\Border;
18use Mpdf\Css\TextVars;
19
20use Mpdf\Image\ImageProcessor;
21
22use Mpdf\Language\LanguageToFont;
23use Mpdf\Language\ScriptToLanguage;
24
25use Mpdf\Log\Context as LogContext;
26
27use Mpdf\Fonts\FontCache;
28use Mpdf\Fonts\FontFileFinder;
29use Mpdf\Fonts\MetricsGenerator;
30
31use Mpdf\Output\Destination;
32
33use Mpdf\Pdf\Protection;
34use Mpdf\Pdf\Protection\UniqidGenerator;
35
36use Mpdf\QrCode;
37
38use Mpdf\Utils\Arrays;
39use Mpdf\Utils\PdfDate;
40use Mpdf\Utils\NumericString;
41use Mpdf\Utils\UtfString;
42
43use Psr\Log\LoggerInterface;
44use Psr\Log\NullLogger;
45
46/**
47 * mPDF, PHP library generating PDF files from UTF-8 encoded HTML
48 *
49 * based on FPDF by Olivier Plathey
50 *      and HTML2FPDF by Renato Coelho
51 *
52 * @version 7.0
53 * @license GPL-2.0
54 */
55class Mpdf implements \Psr\Log\LoggerAwareInterface
56{
57
58	const VERSION = '7.1.4';
59
60	const SCALE = 72 / 25.4;
61
62	var $useFixedNormalLineHeight; // mPDF 6
63	var $useFixedTextBaseline; // mPDF 6
64	var $adjustFontDescLineheight; // mPDF 6
65	var $interpolateImages; // mPDF 6
66	var $defaultPagebreakType; // mPDF 6 pagebreaktype
67	var $indexUseSubentries; // mPDF 6
68
69	var $autoScriptToLang; // mPDF 6
70	var $baseScript; // mPDF 6
71	var $autoVietnamese; // mPDF 6
72	var $autoArabic; // mPDF 6
73
74	var $CJKforceend;
75	var $h2bookmarks;
76	var $h2toc;
77	var $decimal_align;
78	var $margBuffer;
79	var $splitTableBorderWidth;
80
81	var $bookmarkStyles;
82	var $useActiveForms;
83
84	var $repackageTTF;
85	var $allowCJKorphans;
86	var $allowCJKoverflow;
87
88	var $useKerning;
89	var $restrictColorSpace;
90	var $bleedMargin;
91	var $crossMarkMargin;
92	var $cropMarkMargin;
93	var $cropMarkLength;
94	var $nonPrintMargin;
95
96	var $PDFX;
97	var $PDFXauto;
98
99	var $PDFA;
100	var $PDFAversion = '1-B';
101	var $PDFAauto;
102	var $ICCProfile;
103
104	var $printers_info;
105	var $iterationCounter;
106	var $smCapsScale;
107	var $smCapsStretch;
108
109	var $backupSubsFont;
110	var $backupSIPFont;
111	var $fonttrans;
112	var $debugfonts;
113	var $useAdobeCJK;
114	var $percentSubset;
115	var $maxTTFFilesize;
116	var $BMPonly;
117
118	var $tableMinSizePriority;
119
120	var $dpi;
121	var $watermarkImgAlphaBlend;
122	var $watermarkImgBehind;
123	var $justifyB4br;
124	var $packTableData;
125	var $pgsIns;
126	var $simpleTables;
127	var $enableImports;
128
129	var $debug;
130
131	var $setAutoTopMargin;
132	var $setAutoBottomMargin;
133	var $autoMarginPadding;
134	var $collapseBlockMargins;
135	var $falseBoldWeight;
136	var $normalLineheight;
137	var $incrementFPR1;
138	var $incrementFPR2;
139	var $incrementFPR3;
140	var $incrementFPR4;
141
142	var $SHYlang;
143	var $SHYleftmin;
144	var $SHYrightmin;
145	var $SHYcharmin;
146	var $SHYcharmax;
147	var $SHYlanguages;
148
149	// PageNumber Conditional Text
150	var $pagenumPrefix;
151	var $pagenumSuffix;
152
153	var $nbpgPrefix;
154	var $nbpgSuffix;
155	var $showImageErrors;
156	var $allow_output_buffering;
157	var $autoPadding;
158	var $tabSpaces;
159	var $autoLangToFont;
160	var $watermarkTextAlpha;
161	var $watermarkImageAlpha;
162	var $watermark_size;
163	var $watermark_pos;
164	var $annotSize;
165	var $annotMargin;
166	var $annotOpacity;
167	var $title2annots;
168	var $keepColumns;
169	var $keep_table_proportions;
170	var $ignore_table_widths;
171	var $ignore_table_percents;
172	var $list_number_suffix;
173
174	var $list_auto_mode; // mPDF 6
175	var $list_indent_first_level; // mPDF 6
176	var $list_indent_default; // mPDF 6
177	var $list_marker_offset; // mPDF 6
178	var $list_symbol_size;
179
180	var $useSubstitutions;
181	var $CSSselectMedia;
182
183	var $forcePortraitHeaders;
184	var $forcePortraitMargins;
185	var $displayDefaultOrientation;
186	var $ignore_invalid_utf8;
187	var $allowedCSStags;
188	var $onlyCoreFonts;
189	var $allow_charset_conversion;
190
191	var $jSWord;
192	var $jSmaxChar;
193	var $jSmaxCharLast;
194	var $jSmaxWordLast;
195
196	var $max_colH_correction;
197
198	var $table_error_report;
199	var $table_error_report_param;
200	var $biDirectional;
201	var $text_input_as_HTML;
202	var $anchor2Bookmark;
203	var $shrink_tables_to_fit;
204
205	var $allow_html_optional_endtags;
206
207	var $img_dpi;
208
209	var $defaultheaderfontsize;
210	var $defaultheaderfontstyle;
211	var $defaultheaderline;
212	var $defaultfooterfontsize;
213	var $defaultfooterfontstyle;
214	var $defaultfooterline;
215	var $header_line_spacing;
216	var $footer_line_spacing;
217
218	var $pregCJKchars;
219	var $pregRTLchars;
220	var $pregCURSchars; // mPDF 6
221
222	var $mirrorMargins;
223	var $watermarkText;
224	var $watermarkAngle;
225	var $watermarkImage;
226	var $showWatermarkText;
227	var $showWatermarkImage;
228
229	var $svgAutoFont;
230	var $svgClasses;
231
232	var $fontsizes;
233
234	var $defaultPageNumStyle; // mPDF 6
235
236	//////////////////////
237	// INTERNAL VARIABLES
238	//////////////////////
239	var $extrapagebreak; // mPDF 6 pagebreaktype
240
241	var $uniqstr; // mPDF 5.7.2
242	var $hasOC;
243
244	var $textvar; // mPDF 5.7.1
245	var $fontLanguageOverride; // mPDF 5.7.1
246	var $OTLtags; // mPDF 5.7.1
247	var $OTLdata;  // mPDF 5.7.1
248
249	var $writingToC;
250	var $layers;
251	var $layerDetails;
252	var $current_layer;
253	var $open_layer_pane;
254	var $decimal_offset;
255	var $inMeter;
256
257	var $CJKleading;
258	var $CJKfollowing;
259	var $CJKoverflow;
260
261	var $textshadow;
262
263	var $colsums;
264	var $spanborder;
265	var $spanborddet;
266
267	var $visibility;
268
269	var $kerning;
270	var $fixedlSpacing;
271	var $minwSpacing;
272	var $lSpacingCSS;
273	var $wSpacingCSS;
274
275	var $spotColorIDs;
276	var $SVGcolors;
277	var $spotColors;
278	var $defTextColor;
279	var $defDrawColor;
280	var $defFillColor;
281
282	var $tableBackgrounds;
283	var $inlineDisplayOff;
284	var $kt_y00;
285	var $kt_p00;
286	var $upperCase;
287	var $checkSIP;
288	var $checkSMP;
289	var $checkCJK;
290
291	var $watermarkImgAlpha;
292	var $PDFAXwarnings;
293
294	var $MetadataRoot;
295	var $OutputIntentRoot;
296	var $InfoRoot;
297	var $associatedFilesRoot;
298
299	var $current_filename;
300	var $parsers;
301	var $current_parser;
302	var $_obj_stack;
303	var $_don_obj_stack;
304	var $_current_obj_id;
305	var $tpls;
306	var $tpl;
307	var $tplprefix;
308	var $_res;
309
310	var $pdf_version;
311
312	private $fontDir;
313
314	var $tempDir;
315
316	var $allowAnnotationFiles;
317
318	var $fontdata;
319
320	var $noImageFile;
321	var $lastblockbottommargin;
322	var $baselineC;
323
324	// mPDF 5.7.3  inline text-decoration parameters
325	var $baselineSup;
326	var $baselineSub;
327	var $baselineS;
328	var $baselineO;
329
330	var $subPos;
331	var $subArrMB;
332	var $ReqFontStyle;
333	var $tableClipPath;
334
335	var $fullImageHeight;
336
337	var $inFixedPosBlock;  // Internal flag for position:fixed block
338	var $fixedPosBlock;  // Buffer string for position:fixed block
339	var $fixedPosBlockDepth;
340	var $fixedPosBlockBBox;
341	var $fixedPosBlockSave;
342	var $maxPosL;
343	var $maxPosR;
344	var $loaded;
345
346	var $extraFontSubsets;
347
348	var $docTemplateStart;  // Internal flag for page (page no. -1) that docTemplate starts on
349
350	var $time0;
351
352	var $hyphenationDictionaryFile;
353
354	var $spanbgcolorarray;
355	var $default_font;
356	var $headerbuffer;
357	var $lastblocklevelchange;
358	var $nestedtablejustfinished;
359	var $linebreakjustfinished;
360	var $cell_border_dominance_L;
361	var $cell_border_dominance_R;
362	var $cell_border_dominance_T;
363	var $cell_border_dominance_B;
364	var $table_keep_together;
365	var $plainCell_properties;
366	var $shrin_k1;
367	var $outerfilled;
368
369	var $blockContext;
370	var $floatDivs;
371
372	var $patterns;
373	var $pageBackgrounds;
374
375	var $bodyBackgroundGradient;
376	var $bodyBackgroundImage;
377	var $bodyBackgroundColor;
378
379	var $writingHTMLheader; // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
380	var $writingHTMLfooter;
381
382	var $angle;
383
384	var $gradients;
385
386	var $kwt_Reference;
387	var $kwt_BMoutlines;
388	var $kwt_toc;
389
390	var $tbrot_BMoutlines;
391	var $tbrot_toc;
392
393	var $col_BMoutlines;
394	var $col_toc;
395
396	var $floatbuffer;
397	var $floatmargins;
398
399	var $bullet;
400	var $bulletarray;
401
402	var $currentLang;
403	var $default_lang;
404
405	var $default_available_fonts;
406
407	var $pageTemplate;
408	var $docTemplate;
409	var $docTemplateContinue;
410
411	var $arabGlyphs;
412	var $arabHex;
413	var $persianGlyphs;
414	var $persianHex;
415	var $arabVowels;
416	var $arabPrevLink;
417	var $arabNextLink;
418
419	var $formobjects; // array of Form Objects for WMF
420	var $InlineProperties;
421	var $InlineAnnots;
422	var $InlineBDF; // mPDF 6 Bidirectional formatting
423	var $InlineBDFctr; // mPDF 6
424
425	var $ktAnnots;
426	var $tbrot_Annots;
427	var $kwt_Annots;
428	var $columnAnnots;
429	var $columnForms;
430	var $tbrotForms;
431
432	var $PageAnnots;
433
434	var $pageDim; // Keep track of page wxh for orientation changes - set in _beginpage, used in _putannots
435
436	var $breakpoints;
437
438	var $tableLevel;
439	var $tbctr;
440	var $innermostTableLevel;
441	var $saveTableCounter;
442	var $cellBorderBuffer;
443
444	var $saveHTMLFooter_height;
445	var $saveHTMLFooterE_height;
446
447	var $firstPageBoxHeader;
448	var $firstPageBoxHeaderEven;
449	var $firstPageBoxFooter;
450	var $firstPageBoxFooterEven;
451
452	var $page_box;
453
454	var $show_marks; // crop or cross marks
455	var $basepathIsLocal;
456
457	var $use_kwt;
458	var $kwt;
459	var $kwt_height;
460	var $kwt_y0;
461	var $kwt_x0;
462	var $kwt_buffer;
463	var $kwt_Links;
464	var $kwt_moved;
465	var $kwt_saved;
466
467	var $PageNumSubstitutions;
468
469	var $table_borders_separate;
470	var $base_table_properties;
471	var $borderstyles;
472
473	var $blockjustfinished;
474
475	var $orig_bMargin;
476	var $orig_tMargin;
477	var $orig_lMargin;
478	var $orig_rMargin;
479	var $orig_hMargin;
480	var $orig_fMargin;
481
482	var $pageHTMLheaders;
483	var $pageHTMLfooters;
484
485	var $saveHTMLHeader;
486	var $saveHTMLFooter;
487
488	var $HTMLheaderPageLinks;
489	var $HTMLheaderPageAnnots;
490	var $HTMLheaderPageForms;
491
492	// See Config\FontVariables for these next 5 values
493	var $available_unifonts;
494	var $sans_fonts;
495	var $serif_fonts;
496	var $mono_fonts;
497	var $defaultSubsFont;
498
499	// List of ALL available CJK fonts (incl. styles) (Adobe add-ons)  hw removed
500	var $available_CJK_fonts;
501
502	var $HTMLHeader;
503	var $HTMLFooter;
504	var $HTMLHeaderE;
505	var $HTMLFooterE;
506	var $bufferoutput;
507
508	// CJK fonts
509	var $Big5_widths;
510	var $GB_widths;
511	var $SJIS_widths;
512	var $UHC_widths;
513
514	// SetProtection
515	var $encrypted;
516
517	var $enc_obj_id; // encryption object id
518
519	// Bookmark
520	var $BMoutlines;
521	var $OutlineRoot;
522
523	// INDEX
524	var $ColActive;
525	var $Reference;
526	var $CurrCol;
527	var $NbCol;
528	var $y0;   // Top ordinate of columns
529
530	var $ColL;
531	var $ColWidth;
532	var $ColGap;
533
534	// COLUMNS
535	var $ColR;
536	var $ChangeColumn;
537	var $columnbuffer;
538	var $ColDetails;
539	var $columnLinks;
540	var $colvAlign;
541
542	// Substitutions
543	var $substitute;  // Array of substitution strings e.g. <ttz>112</ttz>
544	var $entsearch;  // Array of HTML entities (>ASCII 127) to substitute
545	var $entsubstitute; // Array of substitution decimal unicode for the Hi entities
546
547	// Default values if no style sheet offered	(cf. http://www.w3.org/TR/CSS21/sample.html)
548	var $defaultCSS;
549	var $defaultCssFile;
550
551	var $lastoptionaltag; // Save current block item which HTML specifies optionsl endtag
552	var $pageoutput;
553	var $charset_in;
554	var $blk;
555	var $blklvl;
556	var $ColumnAdjust;
557
558	var $ws; // Word spacing
559
560	var $HREF;
561	var $pgwidth;
562	var $fontlist;
563	var $oldx;
564	var $oldy;
565	var $B;
566	var $I;
567
568	var $tdbegin;
569	var $table;
570	var $cell;
571	var $col;
572	var $row;
573
574	var $divbegin;
575	var $divwidth;
576	var $divheight;
577	var $spanbgcolor;
578
579	// mPDF 6 Used for table cell (block-type) properties
580	var $cellTextAlign;
581	var $cellLineHeight;
582	var $cellLineStackingStrategy;
583	var $cellLineStackingShift;
584
585	// mPDF 6  Lists
586	var $listcounter;
587	var $listlvl;
588	var $listtype;
589	var $listitem;
590
591	var $pjustfinished;
592	var $ignorefollowingspaces;
593	var $SMALL;
594	var $BIG;
595	var $dash_on;
596	var $dotted_on;
597
598	var $textbuffer;
599	var $currentfontstyle;
600	var $currentfontfamily;
601	var $currentfontsize;
602	var $colorarray;
603	var $bgcolorarray;
604	var $internallink;
605	var $enabledtags;
606
607	var $lineheight;
608	var $basepath;
609	var $textparam;
610
611	var $specialcontent;
612	var $selectoption;
613	var $objectbuffer;
614
615	// Table Rotation
616	var $table_rotate;
617	var $tbrot_maxw;
618	var $tbrot_maxh;
619	var $tablebuffer;
620	var $tbrot_align;
621	var $tbrot_Links;
622
623	var $keep_block_together; // Keep a Block from page-break-inside: avoid
624
625	var $tbrot_y0;
626	var $tbrot_x0;
627	var $tbrot_w;
628	var $tbrot_h;
629
630	var $mb_enc;
631	var $originalMbEnc;
632	var $originalMbRegexEnc;
633
634	var $directionality;
635
636	var $extgstates; // Used for alpha channel - Transparency (Watermark)
637	var $mgl;
638	var $mgt;
639	var $mgr;
640	var $mgb;
641
642	var $tts;
643	var $ttz;
644	var $tta;
645
646	// Best to alter the below variables using default stylesheet above
647	var $page_break_after_avoid;
648	var $margin_bottom_collapse;
649	var $default_font_size; // in pts
650	var $original_default_font_size; // used to save default sizes when using table default
651	var $original_default_font;
652	var $watermark_font;
653	var $defaultAlign;
654
655	// TABLE
656	var $defaultTableAlign;
657	var $tablethead;
658	var $thead_font_weight;
659	var $thead_font_style;
660	var $thead_font_smCaps;
661	var $thead_valign_default;
662	var $thead_textalign_default;
663	var $tabletfoot;
664	var $tfoot_font_weight;
665	var $tfoot_font_style;
666	var $tfoot_font_smCaps;
667	var $tfoot_valign_default;
668	var $tfoot_textalign_default;
669
670	var $trow_text_rotate;
671
672	var $cellPaddingL;
673	var $cellPaddingR;
674	var $cellPaddingT;
675	var $cellPaddingB;
676	var $table_border_attr_set;
677	var $table_border_css_set;
678
679	var $shrin_k; // factor with which to shrink tables - used internally - do not change
680	var $shrink_this_table_to_fit; // 0 or false to disable; value (if set) gives maximum factor to reduce fontsize
681	var $MarginCorrection; // corrects for OddEven Margins
682	var $margin_footer;
683	var $margin_header;
684
685	var $tabletheadjustfinished;
686	var $usingCoreFont;
687	var $charspacing;
688
689	var $js;
690
691	/**
692	 * Set timeout for cURL
693	 *
694	 * @var int
695	 */
696	var $curlTimeout;
697
698	/**
699	 * Set to true to follow redirects with cURL.
700	 *
701	 * @var bool
702	 */
703	var $curlFollowLocation;
704
705	/**
706	 * Set to true to allow unsafe SSL HTTPS requests.
707	 *
708	 * Can be useful when using CDN with HTTPS and if you don't want to configure settings with SSL certificates.
709	 *
710	 * @var bool
711	 */
712	var $curlAllowUnsafeSslRequests;
713
714	// Private properties FROM FPDF
715	var $DisplayPreferences;
716	var $flowingBlockAttr;
717
718	var $page; // current page number
719
720	var $n; // current object number
721	var $n_js; // current object number
722
723	var $n_ocg_hidden;
724	var $n_ocg_print;
725	var $n_ocg_view;
726
727	var $offsets; // array of object offsets
728	var $buffer; // buffer holding in-memory PDF
729	var $pages; // array containing pages
730	var $state; // current document state
731	var $compress; // compression flag
732
733	var $DefOrientation; // default orientation
734	var $CurOrientation; // current orientation
735	var $OrientationChanges; // array indicating orientation changes
736
737	var $k; // scale factor (number of points in user unit)
738
739	var $fwPt;
740	var $fhPt; // dimensions of page format in points
741	var $fw;
742	var $fh; // dimensions of page format in user unit
743	var $wPt;
744	var $hPt; // current dimensions of page in points
745
746	var $w;
747	var $h; // current dimensions of page in user unit
748
749	var $lMargin; // left margin
750	var $tMargin; // top margin
751	var $rMargin; // right margin
752	var $bMargin; // page break margin
753	var $cMarginL; // cell margin Left
754	var $cMarginR; // cell margin Right
755	var $cMarginT; // cell margin Left
756	var $cMarginB; // cell margin Right
757
758	var $DeflMargin; // Default left margin
759	var $DefrMargin; // Default right margin
760
761	var $x;
762	var $y; // current position in user unit for cell positioning
763
764	var $lasth; // height of last cell printed
765	var $LineWidth; // line width in user unit
766
767	var $CoreFonts; // array of standard font names
768	var $fonts; // array of used fonts
769	var $FontFiles; // array of font files
770
771	var $images; // array of used images
772	var $imageVars = []; // array of image vars
773
774	var $PageLinks; // array of links in pages
775	var $links; // array of internal links
776	var $FontFamily; // current font family
777	var $FontStyle; // current font style
778	var $CurrentFont; // current font info
779	var $FontSizePt; // current font size in points
780	var $FontSize; // current font size in user unit
781	var $DrawColor; // commands for drawing color
782	var $FillColor; // commands for filling color
783	var $TextColor; // commands for text color
784	var $ColorFlag; // indicates whether fill and text colors are different
785	var $autoPageBreak; // automatic page breaking
786	var $PageBreakTrigger; // threshold used to trigger page breaks
787	var $InFooter; // flag set when processing footer
788
789	var $InHTMLFooter;
790	var $processingFooter; // flag set when processing footer - added for columns
791	var $processingHeader; // flag set when processing header - added for columns
792	var $ZoomMode; // zoom display mode
793	var $LayoutMode; // layout display mode
794	var $title; // title
795	var $subject; // subject
796	var $author; // author
797	var $keywords; // keywords
798	var $creator; // creator
799
800	var $customProperties; // array of custom document properties
801
802	var $associatedFiles; // associated files (see SetAssociatedFiles below)
803	var $additionalXmpRdf; // additional rdf added in xmp
804
805	var $aliasNbPg; // alias for total number of pages
806	var $aliasNbPgGp; // alias for total number of pages in page group
807
808	var $ispre;
809	var $outerblocktags;
810	var $innerblocktags;
811
812	/**
813	 * @var string
814	 */
815	private $fontDescriptor;
816
817	/**
818	 * @var \Mpdf\Otl
819	 */
820	private $otl;
821
822	/**
823	 * @var \Mpdf\CssManager
824	 */
825	private $cssManager;
826
827	/**
828	 * @var \Mpdf\Gradient
829	 */
830	private $gradient;
831
832	/**
833	 * @var \Mpdf\Image\Bmp
834	 */
835	private $bmp;
836
837	/**
838	 * @var \Mpdf\Image\Wmf
839	 */
840	private $wmf;
841
842	/**
843	 * @var \Mpdf\TableOfContents
844	 */
845	private $tableOfContents;
846
847	/**
848	 * @var \Mpdf\Form
849	 */
850	private $form;
851
852	/**
853	 * @var \Mpdf\DirectWrite
854	 */
855	private $directWrite;
856
857	/**
858	 * @var \Mpdf\Cache
859	 */
860	private $cache;
861
862	/**
863	 * @var \Mpdf\Fonts\FontCache
864	 */
865	private $fontCache;
866
867	/**
868	 * @var \Mpdf\Fonts\FontFileFinder
869	 */
870	private $fontFileFinder;
871
872	/**
873	 * @var \Mpdf\Tag
874	 */
875	private $tag;
876
877	/**
878	 * @var \Mpdf\Barcode
879	 * @todo solve Tag dependency and make private
880	 */
881	public $barcode;
882
883	/**
884	 * @var \Mpdf\QrCode\QrCode
885	 */
886	private $qrcode;
887
888	/**
889	 * @var \Mpdf\SizeConverter
890	 */
891	private $sizeConverter;
892
893	/**
894	 * @var \Mpdf\Color\ColorConverter
895	 */
896	private $colorConverter;
897
898	/**
899	 * @var \Mpdf\Color\ColorModeConverter
900	 */
901	private $colorModeConverter;
902
903	/**
904	 * @var \Mpdf\Color\ColorSpaceRestrictor
905	 */
906	private $colorSpaceRestrictor;
907
908	/**
909	 * @var \Mpdf\Hyphenator
910	 */
911	private $hyphenator;
912
913	/**
914	 * @var \Mpdf\Pdf\Protection
915	 */
916	private $protection;
917
918	/**
919	 * @var \Mpdf\Image\ImageProcessor
920	 */
921	private $imageProcessor;
922
923	/**
924	 * @var \Mpdf\Language\LanguageToFontInterface
925	 */
926	private $languageToFont;
927
928	/**
929	 * @var \Mpdf\Language\ScriptToLanguageInterface
930	 */
931	private $scriptToLanguage;
932
933	/**
934	 * @var \Psr\Log\LoggerInterface
935	 */
936	private $logger;
937
938	/**
939	 * @var string[]
940	 */
941	private $services;
942
943	/**
944	 * @param mixed[] $config
945	 */
946	public function __construct(array $config = [])
947	{
948		$this->_dochecks();
949
950		list(
951			$mode,
952			$format,
953			$default_font_size,
954			$default_font,
955			$mgl,
956			$mgr,
957			$mgt,
958			$mgb,
959			$mgh,
960			$mgf,
961			$orientation
962		) = $this->initConstructorParams($config);
963
964		$this->logger = new NullLogger();
965
966		$originalConfig = $config;
967		$config = $this->initConfig($originalConfig);
968
969		$this->sizeConverter = new SizeConverter($this->dpi, $this->default_font_size, $this, $this->logger);
970
971		$this->colorModeConverter = new ColorModeConverter();
972		$this->colorSpaceRestrictor = new ColorSpaceRestrictor(
973			$this,
974			$this->colorModeConverter,
975			$this->restrictColorSpace
976		);
977		$this->colorConverter = new ColorConverter($this, $this->colorModeConverter, $this->colorSpaceRestrictor);
978
979
980		$this->gradient = new Gradient($this, $this->sizeConverter, $this->colorConverter);
981		$this->tableOfContents = new TableOfContents($this, $this->sizeConverter);
982
983		$this->cache = new Cache($config['tempDir']);
984		$this->fontCache = new FontCache(new Cache($config['tempDir'] . '/ttfontdata'));
985
986		$this->fontFileFinder = new FontFileFinder($config['fontDir']);
987
988		$this->cssManager = new CssManager($this, $this->cache, $this->sizeConverter, $this->colorConverter);
989;
990		$this->otl = new Otl($this, $this->fontCache);
991
992		$this->form = new Form($this, $this->otl, $this->colorConverter);
993
994		$this->hyphenator = new Hyphenator($this);
995
996		$this->imageProcessor = new ImageProcessor(
997			$this,
998			$this->otl,
999			$this->cssManager,
1000			$this->sizeConverter,
1001			$this->colorConverter,
1002			$this->colorModeConverter,
1003			$this->cache,
1004			$this->languageToFont,
1005			$this->scriptToLanguage,
1006			$this->logger
1007		);
1008
1009		$this->tag = new Tag(
1010			$this,
1011			$this->cache,
1012			$this->cssManager,
1013			$this->form,
1014			$this->otl,
1015			$this->tableOfContents,
1016			$this->sizeConverter,
1017			$this->colorConverter,
1018			$this->imageProcessor,
1019			$this->languageToFont
1020		);
1021
1022		$this->services = [
1023			'otl',
1024			'bmp',
1025			'cache',
1026			'cssManager',
1027			'directWrite',
1028			'fontCache',
1029			'fontFileFinder',
1030			'form',
1031			'gradient',
1032			'tableOfContents',
1033			'tag',
1034			'wmf',
1035			'sizeConverter',
1036			'colorConverter',
1037			'hyphenator',
1038			'imageProcessor',
1039			'protection',
1040			'languageToFont',
1041			'scriptToLanguage',
1042		];
1043
1044		$this->time0 = microtime(true);
1045
1046		$this->writingToC = false;
1047
1048		$this->layers = [];
1049		$this->current_layer = 0;
1050		$this->open_layer_pane = false;
1051
1052		$this->visibility = 'visible';
1053
1054		$this->tableBackgrounds = [];
1055		$this->uniqstr = '20110230'; // mPDF 5.7.2
1056		$this->kt_y00 = '';
1057		$this->kt_p00 = '';
1058		$this->BMPonly = [];
1059		$this->page = 0;
1060		$this->n = 2;
1061		$this->buffer = '';
1062		$this->objectbuffer = [];
1063		$this->pages = [];
1064		$this->OrientationChanges = [];
1065		$this->state = 0;
1066		$this->fonts = [];
1067		$this->FontFiles = [];
1068		$this->images = [];
1069		$this->links = [];
1070		$this->InFooter = false;
1071		$this->processingFooter = false;
1072		$this->processingHeader = false;
1073		$this->lasth = 0;
1074		$this->FontFamily = '';
1075		$this->FontStyle = '';
1076		$this->FontSizePt = 9;
1077
1078		// Small Caps
1079		$this->inMeter = false;
1080		$this->decimal_offset = 0;
1081
1082		$this->PDFAXwarnings = [];
1083
1084		$this->defTextColor = $this->TextColor = $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true);
1085		$this->defDrawColor = $this->DrawColor = $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true);
1086		$this->defFillColor = $this->FillColor = $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings), true);
1087
1088		$this->upperCase = require __DIR__ . '/../data/upperCase.php';
1089
1090		$this->extrapagebreak = true; // mPDF 6 pagebreaktype
1091
1092		$this->ColorFlag = false;
1093		$this->extgstates = [];
1094
1095		$this->mb_enc = 'windows-1252';
1096		$this->originalMbEnc = mb_internal_encoding();
1097		$this->originalMbRegexEnc = mb_regex_encoding();
1098
1099		$this->directionality = 'ltr';
1100		$this->defaultAlign = 'L';
1101		$this->defaultTableAlign = 'L';
1102
1103		$this->fixedPosBlockSave = [];
1104		$this->extraFontSubsets = 0;
1105
1106		$this->blockContext = 1;
1107		$this->floatDivs = [];
1108		$this->DisplayPreferences = '';
1109
1110		// Tiling patterns used for backgrounds
1111		$this->patterns = [];
1112		$this->pageBackgrounds = [];
1113		$this->gradients = [];
1114
1115		// internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
1116		$this->writingHTMLheader = false;
1117		// internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
1118		$this->writingHTMLfooter = false;
1119
1120		$this->kwt_Reference = [];
1121		$this->kwt_BMoutlines = [];
1122		$this->kwt_toc = [];
1123
1124		$this->tbrot_BMoutlines = [];
1125		$this->tbrot_toc = [];
1126
1127		$this->col_BMoutlines = [];
1128		$this->col_toc = [];
1129
1130		$this->pgsIns = [];
1131		$this->PDFAXwarnings = [];
1132		$this->inlineDisplayOff = false;
1133		$this->lSpacingCSS = '';
1134		$this->wSpacingCSS = '';
1135		$this->fixedlSpacing = false;
1136		$this->minwSpacing = 0;
1137
1138		// Baseline for text
1139		$this->baselineC = 0.35;
1140
1141		// mPDF 5.7.3  inline text-decoration parameters
1142		// Sets default change in baseline for <sup> text as factor of preceeding fontsize
1143		// 0.35 has been recommended; 0.5 matches applications like MS Word
1144		$this->baselineSup = 0.5;
1145
1146		// Sets default change in baseline for <sub> text as factor of preceeding fontsize
1147		$this->baselineSub = -0.2;
1148		// Sets default height for <strike> text as factor of fontsize
1149		$this->baselineS = 0.3;
1150		// Sets default height for overline text as factor of fontsize
1151		$this->baselineO = 1.1;
1152
1153		$this->noImageFile = __DIR__ . '/../data/no_image.jpg';
1154		$this->subPos = 0;
1155
1156		$this->fullImageHeight = false;
1157		$this->floatbuffer = [];
1158		$this->floatmargins = [];
1159		$this->formobjects = []; // array of Form Objects for WMF
1160		$this->InlineProperties = [];
1161		$this->InlineAnnots = [];
1162		$this->InlineBDF = []; // mPDF 6
1163		$this->InlineBDFctr = 0; // mPDF 6
1164		$this->tbrot_Annots = [];
1165		$this->kwt_Annots = [];
1166		$this->columnAnnots = [];
1167		$this->PageLinks = [];
1168		$this->OrientationChanges = [];
1169		$this->pageDim = [];
1170		$this->saveHTMLHeader = [];
1171		$this->saveHTMLFooter = [];
1172		$this->PageAnnots = [];
1173		$this->PageNumSubstitutions = [];
1174		$this->breakpoints = []; // used in columnbuffer
1175		$this->tableLevel = 0;
1176		$this->tbctr = []; // counter for nested tables at each level
1177		$this->page_box = [];
1178		$this->show_marks = ''; // crop or cross marks
1179		$this->kwt = false;
1180		$this->kwt_height = 0;
1181		$this->kwt_y0 = 0;
1182		$this->kwt_x0 = 0;
1183		$this->kwt_buffer = [];
1184		$this->kwt_Links = [];
1185		$this->kwt_moved = false;
1186		$this->kwt_saved = false;
1187		$this->PageNumSubstitutions = [];
1188		$this->base_table_properties = [];
1189		$this->borderstyles = ['inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double'];
1190		$this->tbrot_align = 'C';
1191
1192		$this->pageHTMLheaders = [];
1193		$this->pageHTMLfooters = [];
1194		$this->HTMLheaderPageLinks = [];
1195		$this->HTMLheaderPageAnnots = [];
1196
1197		$this->HTMLheaderPageForms = [];
1198		$this->columnForms = [];
1199		$this->tbrotForms = [];
1200
1201		$this->pageoutput = [];
1202
1203		$this->bufferoutput = false;
1204
1205		$this->encrypted = false;
1206
1207		$this->BMoutlines = [];
1208		$this->ColActive = 0;          // Flag indicating that columns are on (the index is being processed)
1209		$this->Reference = [];    // Array containing the references
1210		$this->CurrCol = 0;               // Current column number
1211		$this->ColL = [0];   // Array of Left pos of columns - absolute - needs Margin correction for Odd-Even
1212		$this->ColR = [0];   // Array of Right pos of columns - absolute pos - needs Margin correction for Odd-Even
1213		$this->ChangeColumn = 0;
1214		$this->columnbuffer = [];
1215		$this->ColDetails = [];  // Keeps track of some column details
1216		$this->columnLinks = [];  // Cross references PageLinks
1217		$this->substitute = [];  // Array of substitution strings e.g. <ttz>112</ttz>
1218		$this->entsearch = [];  // Array of HTML entities (>ASCII 127) to substitute
1219		$this->entsubstitute = []; // Array of substitution decimal unicode for the Hi entities
1220		$this->lastoptionaltag = '';
1221		$this->charset_in = '';
1222		$this->blk = [];
1223		$this->blklvl = 0;
1224		$this->tts = false;
1225		$this->ttz = false;
1226		$this->tta = false;
1227		$this->ispre = false;
1228
1229		$this->checkSIP = false;
1230		$this->checkSMP = false;
1231		$this->checkCJK = false;
1232
1233		$this->page_break_after_avoid = false;
1234		$this->margin_bottom_collapse = false;
1235		$this->tablethead = 0;
1236		$this->tabletfoot = 0;
1237		$this->table_border_attr_set = 0;
1238		$this->table_border_css_set = 0;
1239		$this->shrin_k = 1.0;
1240		$this->shrink_this_table_to_fit = 0;
1241		$this->MarginCorrection = 0;
1242
1243		$this->tabletheadjustfinished = false;
1244		$this->usingCoreFont = false;
1245		$this->charspacing = 0;
1246
1247		$this->autoPageBreak = true;
1248
1249		$this->_setPageSize($format, $orientation);
1250		$this->DefOrientation = $orientation;
1251
1252		$this->margin_header = $mgh;
1253		$this->margin_footer = $mgf;
1254
1255		$bmargin = $mgb;
1256
1257		$this->DeflMargin = $mgl;
1258		$this->DefrMargin = $mgr;
1259
1260		$this->orig_tMargin = $mgt;
1261		$this->orig_bMargin = $bmargin;
1262		$this->orig_lMargin = $this->DeflMargin;
1263		$this->orig_rMargin = $this->DefrMargin;
1264		$this->orig_hMargin = $this->margin_header;
1265		$this->orig_fMargin = $this->margin_footer;
1266
1267		if ($this->setAutoTopMargin == 'pad') {
1268			$mgt += $this->margin_header;
1269		}
1270		if ($this->setAutoBottomMargin == 'pad') {
1271			$mgb += $this->margin_footer;
1272		}
1273
1274		// sets l r t margin
1275		$this->SetMargins($this->DeflMargin, $this->DefrMargin, $mgt);
1276
1277		// Automatic page break
1278		// sets $this->bMargin & PageBreakTrigger
1279		$this->SetAutoPageBreak($this->autoPageBreak, $bmargin);
1280
1281		$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
1282
1283		// Interior cell margin (1 mm) ? not used
1284		$this->cMarginL = 1;
1285		$this->cMarginR = 1;
1286
1287		// Line width (0.2 mm)
1288		$this->LineWidth = .567 / Mpdf::SCALE;
1289
1290		// Enable all tags as default
1291		$this->DisableTags();
1292		// Full width display mode
1293		$this->SetDisplayMode(100); // fullwidth? 'fullpage'
1294
1295		// Compression
1296		$this->SetCompression(true);
1297		// Set default display preferences
1298		$this->SetDisplayPreferences('');
1299
1300		$this->initFontConfig($originalConfig);
1301
1302		// Available fonts
1303		$this->available_unifonts = [];
1304		foreach ($this->fontdata as $f => $fs) {
1305			if (isset($fs['R']) && $fs['R']) {
1306				$this->available_unifonts[] = $f;
1307			}
1308			if (isset($fs['B']) && $fs['B']) {
1309				$this->available_unifonts[] = $f . 'B';
1310			}
1311			if (isset($fs['I']) && $fs['I']) {
1312				$this->available_unifonts[] = $f . 'I';
1313			}
1314			if (isset($fs['BI']) && $fs['BI']) {
1315				$this->available_unifonts[] = $f . 'BI';
1316			}
1317		}
1318
1319		$this->default_available_fonts = $this->available_unifonts;
1320
1321		$optcore = false;
1322		$onlyCoreFonts = false;
1323		if (preg_match('/([\-+])aCJK/i', $mode, $m)) {
1324			$mode = preg_replace('/([\-+])aCJK/i', '', $mode); // mPDF 6
1325			if ($m[1] == '+') {
1326				$this->useAdobeCJK = true;
1327			} else {
1328				$this->useAdobeCJK = false;
1329			}
1330		}
1331
1332		if (strlen($mode) == 1) {
1333			if ($mode == 's') {
1334				$this->percentSubset = 100;
1335				$mode = '';
1336			} elseif ($mode == 'c') {
1337				$onlyCoreFonts = true;
1338				$mode = '';
1339			}
1340		} elseif (substr($mode, -2) == '-s') {
1341			$this->percentSubset = 100;
1342			$mode = substr($mode, 0, strlen($mode) - 2);
1343		} elseif (substr($mode, -2) == '-c') {
1344			$onlyCoreFonts = true;
1345			$mode = substr($mode, 0, strlen($mode) - 2);
1346		} elseif (substr($mode, -2) == '-x') {
1347			$optcore = true;
1348			$mode = substr($mode, 0, strlen($mode) - 2);
1349		}
1350
1351		// Autodetect if mode is a language_country string (en-GB or en_GB or en)
1352		if ($mode && $mode != 'UTF-8') { // mPDF 6
1353			list ($coreSuitable, $mpdf_pdf_unifont) = $this->languageToFont->getLanguageOptions($mode, $this->useAdobeCJK);
1354			if ($coreSuitable && $optcore) {
1355				$onlyCoreFonts = true;
1356			}
1357			if ($mpdf_pdf_unifont) {  // mPDF 6
1358				$default_font = $mpdf_pdf_unifont;
1359			}
1360			$this->currentLang = $mode;
1361			$this->default_lang = $mode;
1362		}
1363
1364		$this->onlyCoreFonts = $onlyCoreFonts;
1365
1366		if ($this->onlyCoreFonts) {
1367			$this->setMBencoding('windows-1252'); // sets $this->mb_enc
1368		} else {
1369			$this->setMBencoding('UTF-8'); // sets $this->mb_enc
1370		}
1371		@mb_regex_encoding('UTF-8'); // required only for mb_ereg... and mb_split functions
1372
1373		// Adobe CJK fonts
1374		$this->available_CJK_fonts = [
1375			'gb',
1376			'big5',
1377			'sjis',
1378			'uhc',
1379			'gbB',
1380			'big5B',
1381			'sjisB',
1382			'uhcB',
1383			'gbI',
1384			'big5I',
1385			'sjisI',
1386			'uhcI',
1387			'gbBI',
1388			'big5BI',
1389			'sjisBI',
1390			'uhcBI',
1391		];
1392
1393		// Standard fonts
1394		$this->CoreFonts = [
1395			'ccourier' => 'Courier',
1396			'ccourierB' => 'Courier-Bold',
1397			'ccourierI' => 'Courier-Oblique',
1398			'ccourierBI' => 'Courier-BoldOblique',
1399			'chelvetica' => 'Helvetica',
1400			'chelveticaB' => 'Helvetica-Bold',
1401			'chelveticaI' => 'Helvetica-Oblique',
1402			'chelveticaBI' => 'Helvetica-BoldOblique',
1403			'ctimes' => 'Times-Roman',
1404			'ctimesB' => 'Times-Bold',
1405			'ctimesI' => 'Times-Italic',
1406			'ctimesBI' => 'Times-BoldItalic',
1407			'csymbol' => 'Symbol',
1408			'czapfdingbats' => 'ZapfDingbats'
1409		];
1410
1411		$this->fontlist = [
1412			"ctimes",
1413			"ccourier",
1414			"chelvetica",
1415			"csymbol",
1416			"czapfdingbats"
1417		];
1418
1419		// Substitutions
1420		$this->setHiEntitySubstitutions();
1421
1422		if ($this->onlyCoreFonts) {
1423			$this->useSubstitutions = true;
1424			$this->SetSubstitutions();
1425		} else {
1426			$this->useSubstitutions = $config['useSubstitutions'];
1427		}
1428
1429		if (file_exists($this->defaultCssFile)) {
1430			$css = file_get_contents($this->defaultCssFile);
1431			$this->cssManager->ReadCSS('<style> ' . $css . ' </style>');
1432		} else {
1433			throw new \Mpdf\MpdfException(sprintf('Unable to read default CSS file "%s"', $this->defaultCssFile));
1434		}
1435
1436		if ($default_font == '') {
1437			if ($this->onlyCoreFonts) {
1438				if (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->mono_fonts)) {
1439					$default_font = 'ccourier';
1440				} elseif (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->sans_fonts)) {
1441					$default_font = 'chelvetica';
1442				} else {
1443					$default_font = 'ctimes';
1444				}
1445			} else {
1446				$default_font = $this->defaultCSS['BODY']['FONT-FAMILY'];
1447			}
1448		}
1449		if (!$default_font_size) {
1450			$mmsize = $this->sizeConverter->convert($this->defaultCSS['BODY']['FONT-SIZE']);
1451			$default_font_size = $mmsize * (Mpdf::SCALE);
1452		}
1453
1454		if ($default_font) {
1455			$this->SetDefaultFont($default_font);
1456		}
1457		if ($default_font_size) {
1458			$this->SetDefaultFontSize($default_font_size);
1459		}
1460
1461		$this->SetLineHeight(); // lineheight is in mm
1462
1463		$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
1464		$this->HREF = '';
1465		$this->oldy = -1;
1466		$this->B = 0;
1467		$this->I = 0;
1468
1469		// mPDF 6  Lists
1470		$this->listlvl = 0;
1471		$this->listtype = [];
1472		$this->listitem = [];
1473		$this->listcounter = [];
1474
1475		$this->tdbegin = false;
1476		$this->table = [];
1477		$this->cell = [];
1478		$this->col = -1;
1479		$this->row = -1;
1480		$this->cellBorderBuffer = [];
1481
1482		$this->divbegin = false;
1483		// mPDF 6
1484		$this->cellTextAlign = '';
1485		$this->cellLineHeight = '';
1486		$this->cellLineStackingStrategy = '';
1487		$this->cellLineStackingShift = '';
1488
1489		$this->divwidth = 0;
1490		$this->divheight = 0;
1491		$this->spanbgcolor = false;
1492		$this->spanborder = false;
1493		$this->spanborddet = [];
1494
1495		$this->blockjustfinished = false;
1496		$this->ignorefollowingspaces = true; // in order to eliminate exceeding left-side spaces
1497		$this->dash_on = false;
1498		$this->dotted_on = false;
1499		$this->textshadow = '';
1500
1501		$this->currentfontfamily = '';
1502		$this->currentfontsize = '';
1503		$this->currentfontstyle = '';
1504		$this->colorarray = ''; // mPDF 6
1505		$this->spanbgcolorarray = ''; // mPDF 6
1506		$this->textbuffer = [];
1507		$this->internallink = [];
1508		$this->basepath = "";
1509
1510		$this->SetBasePath('');
1511
1512		$this->textparam = [];
1513
1514		$this->specialcontent = '';
1515		$this->selectoption = [];
1516
1517		/* -- IMPORTS -- */
1518		$this->parsers = [];
1519		$this->tpls = [];
1520		$this->tpl = 0;
1521		$this->tplprefix = "/TPL";
1522		/* -- END IMPORTS -- */
1523	}
1524
1525	public function cleanup()
1526	{
1527		mb_internal_encoding($this->originalMbEnc);
1528		@mb_regex_encoding($this->originalMbRegexEnc);
1529	}
1530
1531	/**
1532	 * @param \Psr\Log\LoggerInterface
1533	 *
1534	 * @return \Mpdf\Mpdf
1535	 */
1536	public function setLogger(LoggerInterface $logger)
1537	{
1538		$this->logger = $logger;
1539
1540		foreach ($this->services as $name) {
1541			if ($this->$name && $this->$name instanceof \Psr\Log\LoggerAwareInterface) {
1542				$this->$name->setLogger($logger);
1543			}
1544		}
1545
1546		return $this;
1547	}
1548
1549	private function initConfig(array $config)
1550	{
1551		$configObject = new ConfigVariables();
1552		$defaults = $configObject->getDefaults();
1553		$config = array_intersect_key($config + $defaults, $defaults);
1554
1555		foreach ($config as $var => $val) {
1556			$this->{$var} = $val;
1557		}
1558
1559		return $config;
1560	}
1561
1562	private function initConstructorParams(array $config)
1563	{
1564		$constructor = [
1565			'mode' => '',
1566			'format' => 'A4',
1567			'default_font_size' => 0,
1568			'default_font' => '',
1569			'margin_left' => 15,
1570			'margin_right' => 15,
1571			'margin_top' => 16,
1572			'margin_bottom' => 16,
1573			'margin_header' => 9,
1574			'margin_footer' => 9,
1575			'orientation' => 'P',
1576		];
1577
1578		foreach ($constructor as $key => $val) {
1579			if (isset($config[$key])) {
1580				$constructor[$key] = $config[$key];
1581			}
1582		}
1583
1584		return array_values($constructor);
1585	}
1586
1587	private function initFontConfig(array $config)
1588	{
1589		$configObject = new FontVariables();
1590		$defaults = $configObject->getDefaults();
1591		$config = array_intersect_key($config + $defaults, $defaults);
1592		foreach ($config as $var => $val) {
1593			$this->{$var} = $val;
1594		}
1595
1596		return $config;
1597	}
1598
1599	function _setPageSize($format, &$orientation)
1600	{
1601		if (is_string($format)) {
1602
1603			if (empty($format)) {
1604				$format = 'A4';
1605			}
1606
1607			// e.g. A4-L = A4 landscape, A4-P = A4 portrait
1608			if (preg_match('/([0-9a-zA-Z]*)-([P,L])/i', $format, $m)) {
1609				$format = $m[1];
1610				$orientation = $m[2];
1611			} elseif (empty($orientation)) {
1612				$orientation = 'P';
1613			}
1614
1615			$format = PageFormat::getSizeFromName($format);
1616
1617			$this->fwPt = $format[0];
1618			$this->fhPt = $format[1];
1619
1620		} else {
1621
1622			if (!$format[0] || !$format[1]) {
1623				throw new \Mpdf\MpdfException('Invalid page format: ' . $format[0] . ' ' . $format[1]);
1624			}
1625
1626			$this->fwPt = $format[0] * Mpdf::SCALE;
1627			$this->fhPt = $format[1] * Mpdf::SCALE;
1628		}
1629
1630		$this->fw = $this->fwPt / Mpdf::SCALE;
1631		$this->fh = $this->fhPt / Mpdf::SCALE;
1632
1633		// Page orientation
1634		$orientation = strtolower($orientation);
1635		if ($orientation === 'p' || $orientation == 'portrait') {
1636			$orientation = 'P';
1637			$this->wPt = $this->fwPt;
1638			$this->hPt = $this->fhPt;
1639		} elseif ($orientation === 'l' || $orientation == 'landscape') {
1640			$orientation = 'L';
1641			$this->wPt = $this->fhPt;
1642			$this->hPt = $this->fwPt;
1643		} else {
1644			throw new \Mpdf\MpdfException('Incorrect orientation: ' . $orientation);
1645		}
1646
1647		$this->CurOrientation = $orientation;
1648
1649		$this->w = $this->wPt / Mpdf::SCALE;
1650		$this->h = $this->hPt / Mpdf::SCALE;
1651	}
1652
1653	function RestrictUnicodeFonts($res)
1654	{
1655		// $res = array of (Unicode) fonts to restrict to: e.g. norasi|norasiB - language specific
1656		if (count($res)) { // Leave full list of available fonts if passed blank array
1657			$this->available_unifonts = $res;
1658		} else {
1659			$this->available_unifonts = $this->default_available_fonts;
1660		}
1661		if (count($this->available_unifonts) == 0) {
1662			$this->available_unifonts[] = $this->default_available_fonts[0];
1663		}
1664		$this->available_unifonts = array_values($this->available_unifonts);
1665	}
1666
1667	function setMBencoding($enc)
1668	{
1669		if ($this->mb_enc != $enc) {
1670			$this->mb_enc = $enc;
1671			mb_internal_encoding($this->mb_enc);
1672		}
1673	}
1674
1675	function SetMargins($left, $right, $top)
1676	{
1677		// Set left, top and right margins
1678		$this->lMargin = $left;
1679		$this->rMargin = $right;
1680		$this->tMargin = $top;
1681	}
1682
1683	function ResetMargins()
1684	{
1685		// ReSet left, top margins
1686		if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P' && $this->CurOrientation == 'L') {
1687			if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
1688				$this->tMargin = $this->orig_rMargin;
1689				$this->bMargin = $this->orig_lMargin;
1690			} else { // ODD	// OR NOT MIRRORING MARGINS/FOOTERS
1691				$this->tMargin = $this->orig_lMargin;
1692				$this->bMargin = $this->orig_rMargin;
1693			}
1694			$this->lMargin = $this->DeflMargin;
1695			$this->rMargin = $this->DefrMargin;
1696			$this->MarginCorrection = 0;
1697			$this->PageBreakTrigger = $this->h - $this->bMargin;
1698		} elseif (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
1699			$this->lMargin = $this->DefrMargin;
1700			$this->rMargin = $this->DeflMargin;
1701			$this->MarginCorrection = $this->DefrMargin - $this->DeflMargin;
1702		} else { // ODD	// OR NOT MIRRORING MARGINS/FOOTERS
1703			$this->lMargin = $this->DeflMargin;
1704			$this->rMargin = $this->DefrMargin;
1705			if ($this->mirrorMargins) {
1706				$this->MarginCorrection = $this->DeflMargin - $this->DefrMargin;
1707			}
1708		}
1709		$this->x = $this->lMargin;
1710	}
1711
1712	function SetLeftMargin($margin)
1713	{
1714		// Set left margin
1715		$this->lMargin = $margin;
1716		if ($this->page > 0 and $this->x < $margin) {
1717			$this->x = $margin;
1718		}
1719	}
1720
1721	function SetTopMargin($margin)
1722	{
1723		// Set top margin
1724		$this->tMargin = $margin;
1725	}
1726
1727	function SetRightMargin($margin)
1728	{
1729		// Set right margin
1730		$this->rMargin = $margin;
1731	}
1732
1733	function SetAutoPageBreak($auto, $margin = 0)
1734	{
1735		// Set auto page break mode and triggering margin
1736		$this->autoPageBreak = $auto;
1737		$this->bMargin = $margin;
1738		$this->PageBreakTrigger = $this->h - $margin;
1739	}
1740
1741	function SetDisplayMode($zoom, $layout = 'continuous')
1742	{
1743		// Set display mode in viewer
1744		if ($zoom == 'fullpage' or $zoom == 'fullwidth' or $zoom == 'real' or $zoom == 'default' or ! is_string($zoom)) {
1745			$this->ZoomMode = $zoom;
1746		} else {
1747			throw new \Mpdf\MpdfException('Incorrect zoom display mode: ' . $zoom);
1748		}
1749		if ($layout == 'single' or $layout == 'continuous' or $layout == 'two' or $layout == 'twoleft' or $layout == 'tworight' or $layout == 'default') {
1750			$this->LayoutMode = $layout;
1751		} else {
1752			throw new \Mpdf\MpdfException('Incorrect layout display mode: ' . $layout);
1753		}
1754	}
1755
1756	function SetCompression($compress)
1757	{
1758		// Set page compression
1759		if (function_exists('gzcompress')) {
1760			$this->compress = $compress;
1761		} else {
1762			$this->compress = false;
1763		}
1764	}
1765
1766	function SetTitle($title)
1767	{
1768		// Title of document // Arrives as UTF-8
1769		$this->title = $title;
1770	}
1771
1772	function SetSubject($subject)
1773	{
1774		// Subject of document
1775		$this->subject = $subject;
1776	}
1777
1778	function SetAuthor($author)
1779	{
1780		// Author of document
1781		$this->author = $author;
1782	}
1783
1784	function SetKeywords($keywords)
1785	{
1786		// Keywords of document
1787		$this->keywords = $keywords;
1788	}
1789
1790	function SetCreator($creator)
1791	{
1792		// Creator of document
1793		$this->creator = $creator;
1794	}
1795
1796	function AddCustomProperty($key, $value)
1797	{
1798		$this->customProperties[$key] = $value;
1799	}
1800
1801	/**
1802	 * Set one or multiple associated file ("/AF" as required by PDF/A-3)
1803	 *
1804	 * param $files is an array of hash containing:
1805	 *   path: file path on FS
1806	 *   content: file content
1807	 *   name: file name (not necessarily the same as the file on FS)
1808	 *   mime (optional): file mime type (will show up as /Subtype in the PDF)
1809	 *   description (optional): file description
1810	 *   AFRelationship (optional): PDF/A-3 AFRelationship (e.g. "Alternative")
1811	 *
1812	 * e.g. to associate 1 file:
1813	 *     [[
1814	 *         'path' => 'tmp/1234.xml',
1815	 *         'content' => 'file content',
1816	 *         'name' => 'public_name.xml',
1817	 *         'mime' => 'text/xml',
1818	 *         'description' => 'foo',
1819	 *         'AFRelationship' => 'Alternative',
1820	 *     ]]
1821	 *
1822	 * @param mixed[] $files Array of arrays of associated files. See above
1823	 */
1824	function SetAssociatedFiles(array $files)
1825	{
1826		$this->associatedFiles = $files;
1827	}
1828
1829	function SetAdditionalXmpRdf($s)
1830	{
1831		$this->additionalXmpRdf = $s;
1832	}
1833
1834	function SetAnchor2Bookmark($x)
1835	{
1836		$this->anchor2Bookmark = $x;
1837	}
1838
1839	public function AliasNbPages($alias = '{nb}')
1840	{
1841		// Define an alias for total number of pages
1842		$this->aliasNbPg = $alias;
1843	}
1844
1845	public function AliasNbPageGroups($alias = '{nbpg}')
1846	{
1847		// Define an alias for total number of pages in a group
1848		$this->aliasNbPgGp = $alias;
1849	}
1850
1851	function SetAlpha($alpha, $bm = 'Normal', $return = false, $mode = 'B')
1852	{
1853		// alpha: real value from 0 (transparent) to 1 (opaque)
1854		// bm:    blend mode, one of the following:
1855		//          Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn,
1856		//          HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
1857		// set alpha for stroking (CA) and non-stroking (ca) operations
1858		// mode determines F (fill) S (stroke) B (both)
1859		if (($this->PDFA || $this->PDFX) && $alpha != 1) {
1860			if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
1861				$this->PDFAXwarnings[] = "Image opacity must be 100% (Opacity changed to 100%)";
1862			}
1863			$alpha = 1;
1864		}
1865		$a = ['BM' => '/' . $bm];
1866		if ($mode == 'F' || $mode == 'B') {
1867			$a['ca'] = $alpha; // mPDF 5.7.2
1868		}
1869		if ($mode == 'S' || $mode == 'B') {
1870			$a['CA'] = $alpha; // mPDF 5.7.2
1871		}
1872		$gs = $this->AddExtGState($a);
1873		if ($return) {
1874			return sprintf('/GS%d gs', $gs);
1875		} else {
1876			$this->_out(sprintf('/GS%d gs', $gs));
1877		}
1878	}
1879
1880	function AddExtGState($parms)
1881	{
1882		$n = count($this->extgstates);
1883		// check if graphics state already exists
1884		for ($i = 1; $i <= $n; $i++) {
1885			if (count($this->extgstates[$i]['parms']) == count($parms)) {
1886				$same = true;
1887				foreach ($this->extgstates[$i]['parms'] as $k => $v) {
1888					if (!isset($parms[$k]) || $parms[$k] != $v) {
1889						$same = false;
1890						break;
1891					}
1892				}
1893				if ($same) {
1894					return $i;
1895				}
1896			}
1897		}
1898		$n++;
1899		$this->extgstates[$n]['parms'] = $parms;
1900		return $n;
1901	}
1902
1903	function SetVisibility($v)
1904	{
1905		if (($this->PDFA || $this->PDFX) && $this->visibility != 'visible') {
1906			$this->PDFAXwarnings[] = "Cannot set visibility to anything other than full when using PDFA or PDFX";
1907			return '';
1908		} elseif (!$this->PDFA && !$this->PDFX) {
1909			$this->pdf_version = '1.5';
1910		}
1911		if ($this->visibility != 'visible') {
1912			$this->_out('EMC');
1913			$this->hasOC = intval($this->hasOC);
1914		}
1915		if ($v == 'printonly') {
1916			$this->_out('/OC /OC1 BDC');
1917			$this->hasOC = ($this->hasOC | 1);
1918		} elseif ($v == 'screenonly') {
1919			$this->_out('/OC /OC2 BDC');
1920			$this->hasOC = ($this->hasOC | 2);
1921		} elseif ($v == 'hidden') {
1922			$this->_out('/OC /OC3 BDC');
1923			$this->hasOC = ($this->hasOC | 4);
1924		} elseif ($v != 'visible') {
1925			throw new \Mpdf\MpdfException('Incorrect visibility: ' . $v);
1926		}
1927		$this->visibility = $v;
1928	}
1929
1930	function Open()
1931	{
1932		// Begin document
1933		if ($this->state == 0) {
1934			// Was is function _begindoc()
1935			// Start document
1936			$this->state = 1;
1937			$this->_out('%PDF-' . $this->pdf_version);
1938			$this->_out('%' . chr(226) . chr(227) . chr(207) . chr(211)); // 4 chars > 128 to show binary file
1939		}
1940	}
1941
1942	function Close()
1943	{
1944		// @log Closing last page
1945
1946		// Terminate document
1947		if ($this->state == 3) {
1948			return;
1949		}
1950		if ($this->page == 0) {
1951			$this->AddPage($this->CurOrientation);
1952		}
1953		if (count($this->cellBorderBuffer)) {
1954			$this->printcellbuffer();
1955		} // *TABLES*
1956		if ($this->tablebuffer) {
1957			$this->printtablebuffer();
1958		} // *TABLES*
1959		/* -- COLUMNS -- */
1960
1961		if ($this->ColActive) {
1962			$this->SetColumns(0);
1963			$this->ColActive = 0;
1964			if (count($this->columnbuffer)) {
1965				$this->printcolumnbuffer();
1966			}
1967		}
1968		/* -- END COLUMNS -- */
1969
1970		// BODY Backgrounds
1971		$s = '';
1972
1973		$s .= $this->PrintBodyBackgrounds();
1974		$s .= $this->PrintPageBackgrounds();
1975
1976		$this->pages[$this->page] = preg_replace(
1977			'/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/',
1978			"\n" . $s . "\n" . '\\1',
1979			$this->pages[$this->page]
1980		);
1981
1982		$this->pageBackgrounds = [];
1983
1984		if ($this->visibility != 'visible') {
1985			$this->SetVisibility('visible');
1986		}
1987
1988		$this->EndLayer();
1989
1990		if (!$this->tableOfContents->TOCmark) { // Page footer
1991			$this->InFooter = true;
1992			$this->Footer();
1993			$this->InFooter = false;
1994		}
1995
1996		if ($this->tableOfContents->TOCmark || count($this->tableOfContents->m_TOC)) {
1997			$this->tableOfContents->insertTOC();
1998		}
1999
2000		// *TOC*
2001		// Close page
2002		$this->_endpage();
2003
2004		// Close document
2005		$this->_enddoc();
2006	}
2007
2008	/* -- BACKGROUNDS -- */
2009
2010	function _resizeBackgroundImage($imw, $imh, $cw, $ch, $resize, $repx, $repy, $pba = [], $size = [])
2011	{
2012		// pba is background positioning area (from CSS background-origin) may not always be set [x,y,w,h]
2013		// size is from CSS3 background-size - takes precendence over old resize
2014		// $w - absolute length or % or auto or cover | contain
2015		// $h - absolute length or % or auto or cover | contain
2016		if (isset($pba['w'])) {
2017			$cw = $pba['w'];
2018		}
2019		if (isset($pba['h'])) {
2020			$ch = $pba['h'];
2021		}
2022
2023		$cw = $cw * Mpdf::SCALE;
2024		$ch = $ch * Mpdf::SCALE;
2025		if (empty($size) && !$resize) {
2026			return [$imw, $imh, $repx, $repy];
2027		}
2028
2029		if (isset($size['w']) && $size['w']) {
2030			if ($size['w'] == 'contain') {
2031				// Scale the image, while preserving its intrinsic aspect ratio (if any),
2032				// to the largest size such that both its width and its height can fit inside the background positioning area.
2033				// Same as resize==3
2034				$h = $imh * $cw / $imw;
2035				$w = $cw;
2036				if ($h > $ch) {
2037					$w = $w * $ch / $h;
2038					$h = $ch;
2039				}
2040			} elseif ($size['w'] == 'cover') {
2041				// Scale the image, while preserving its intrinsic aspect ratio (if any),
2042				// to the smallest size such that both its width and its height can completely cover the background positioning area.
2043				$h = $imh * $cw / $imw;
2044				$w = $cw;
2045				if ($h < $ch) {
2046					$w = $w * $h / $ch;
2047					$h = $ch;
2048				}
2049			} else {
2050				if (stristr($size['w'], '%')) {
2051					$size['w'] = (float) $size['w'];
2052					$size['w'] /= 100;
2053					$size['w'] = ($cw * $size['w']);
2054				}
2055				if (stristr($size['h'], '%')) {
2056					$size['h'] = (float) $size['h'];
2057					$size['h'] /= 100;
2058					$size['h'] = ($ch * $size['h']);
2059				}
2060				if ($size['w'] == 'auto' && $size['h'] == 'auto') {
2061					$w = $imw;
2062					$h = $imh;
2063				} elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
2064					$w = $imw * $size['h'] / $imh;
2065					$h = $size['h'];
2066				} elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
2067					$h = $imh * $size['w'] / $imw;
2068					$w = $size['w'];
2069				} else {
2070					$w = $size['w'];
2071					$h = $size['h'];
2072				}
2073			}
2074			return [$w, $h, $repx, $repy];
2075		} elseif ($resize == 1 && $imw > $cw) {
2076			$h = $imh * $cw / $imw;
2077			return [$cw, $h, $repx, $repy];
2078		} elseif ($resize == 2 && $imh > $ch) {
2079			$w = $imw * $ch / $imh;
2080			return [$w, $ch, $repx, $repy];
2081		} elseif ($resize == 3) {
2082			$w = $imw;
2083			$h = $imh;
2084			if ($w > $cw) {
2085				$h = $h * $cw / $w;
2086				$w = $cw;
2087			}
2088			if ($h > $ch) {
2089				$w = $w * $ch / $h;
2090				$h = $ch;
2091			}
2092			return [$w, $h, $repx, $repy];
2093		} elseif ($resize == 4) {
2094			$h = $imh * $cw / $imw;
2095			return [$cw, $h, $repx, $repy];
2096		} elseif ($resize == 5) {
2097			$w = $imw * $ch / $imh;
2098			return [$w, $ch, $repx, $repy];
2099		} elseif ($resize == 6) {
2100			return [$cw, $ch, $repx, $repy];
2101		}
2102		return [$imw, $imh, $repx, $repy];
2103	}
2104
2105	function SetBackground(&$properties, &$maxwidth)
2106	{
2107		if (isset($properties['BACKGROUND-ORIGIN']) && ($properties['BACKGROUND-ORIGIN'] == 'border-box' || $properties['BACKGROUND-ORIGIN'] == 'content-box')) {
2108			$origin = $properties['BACKGROUND-ORIGIN'];
2109		} else {
2110			$origin = 'padding-box';
2111		}
2112
2113		if (isset($properties['BACKGROUND-SIZE'])) {
2114			if (stristr($properties['BACKGROUND-SIZE'], 'contain')) {
2115				$bsw = $bsh = 'contain';
2116			} elseif (stristr($properties['BACKGROUND-SIZE'], 'cover')) {
2117				$bsw = $bsh = 'cover';
2118			} else {
2119				$bsw = $bsh = 'auto';
2120				$sz = preg_split('/\s+/', trim($properties['BACKGROUND-SIZE']));
2121				if (count($sz) == 2) {
2122					$bsw = $sz[0];
2123					$bsh = $sz[1];
2124				} else {
2125					$bsw = $sz[0];
2126				}
2127				if (!stristr($bsw, '%') && !stristr($bsw, 'auto')) {
2128					$bsw = $this->sizeConverter->convert($bsw, $maxwidth, $this->FontSize);
2129				}
2130				if (!stristr($bsh, '%') && !stristr($bsh, 'auto')) {
2131					$bsh = $this->sizeConverter->convert($bsh, $maxwidth, $this->FontSize);
2132				}
2133			}
2134			$size = ['w' => $bsw, 'h' => $bsh];
2135		} else {
2136			$size = false;
2137		} // mPDF 6
2138		if (preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $properties['BACKGROUND-IMAGE'])) {
2139			return ['gradient' => $properties['BACKGROUND-IMAGE'], 'origin' => $origin, 'size' => $size];
2140		} else {
2141			$file = $properties['BACKGROUND-IMAGE'];
2142			$sizesarray = $this->Image($file, 0, 0, 0, 0, '', '', false, false, false, false, true);
2143			if (isset($sizesarray['IMAGE_ID'])) {
2144				$image_id = $sizesarray['IMAGE_ID'];
2145				$orig_w = $sizesarray['WIDTH'] * Mpdf::SCALE;  // in user units i.e. mm
2146				$orig_h = $sizesarray['HEIGHT'] * Mpdf::SCALE;  // (using $this->img_dpi)
2147				if (isset($properties['BACKGROUND-IMAGE-RESOLUTION'])) {
2148					if (preg_match('/from-image/i', $properties['BACKGROUND-IMAGE-RESOLUTION']) && isset($sizesarray['set-dpi']) && $sizesarray['set-dpi'] > 0) {
2149						$orig_w *= $this->img_dpi / $sizesarray['set-dpi'];
2150						$orig_h *= $this->img_dpi / $sizesarray['set-dpi'];
2151					} elseif (preg_match('/(\d+)dpi/i', $properties['BACKGROUND-IMAGE-RESOLUTION'], $m)) {
2152						$dpi = $m[1];
2153						if ($dpi > 0) {
2154							$orig_w *= $this->img_dpi / $dpi;
2155							$orig_h *= $this->img_dpi / $dpi;
2156						}
2157					}
2158				}
2159				$x_repeat = true;
2160				$y_repeat = true;
2161				if (isset($properties['BACKGROUND-REPEAT'])) {
2162					if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-x') {
2163						$y_repeat = false;
2164					}
2165					if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-y') {
2166						$x_repeat = false;
2167					}
2168				}
2169				$x_pos = 0;
2170				$y_pos = 0;
2171				if (isset($properties['BACKGROUND-POSITION'])) {
2172					$ppos = preg_split('/\s+/', $properties['BACKGROUND-POSITION']);
2173					$x_pos = $ppos[0];
2174					$y_pos = $ppos[1];
2175					if (!stristr($x_pos, '%')) {
2176						$x_pos = $this->sizeConverter->convert($x_pos, $maxwidth, $this->FontSize);
2177					}
2178					if (!stristr($y_pos, '%')) {
2179						$y_pos = $this->sizeConverter->convert($y_pos, $maxwidth, $this->FontSize);
2180					}
2181				}
2182				if (isset($properties['BACKGROUND-IMAGE-RESIZE'])) {
2183					$resize = $properties['BACKGROUND-IMAGE-RESIZE'];
2184				} else {
2185					$resize = 0;
2186				}
2187				if (isset($properties['BACKGROUND-IMAGE-OPACITY'])) {
2188					$opacity = $properties['BACKGROUND-IMAGE-OPACITY'];
2189				} else {
2190					$opacity = 1;
2191				}
2192				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];
2193			}
2194		}
2195		return false;
2196	}
2197
2198	/* -- END BACKGROUNDS -- */
2199
2200	function PrintBodyBackgrounds()
2201	{
2202		$s = '';
2203		$clx = 0;
2204		$cly = 0;
2205		$clw = $this->w;
2206		$clh = $this->h;
2207		// If using bleed and trim margins in paged media
2208		if ($this->pageDim[$this->page]['outer_width_LR'] || $this->pageDim[$this->page]['outer_width_TB']) {
2209			$clx = $this->pageDim[$this->page]['outer_width_LR'] - $this->pageDim[$this->page]['bleedMargin'];
2210			$cly = $this->pageDim[$this->page]['outer_width_TB'] - $this->pageDim[$this->page]['bleedMargin'];
2211			$clw = $this->w - 2 * $clx;
2212			$clh = $this->h - 2 * $cly;
2213		}
2214
2215		if ($this->bodyBackgroundColor) {
2216			$s .= 'q ' . $this->SetFColor($this->bodyBackgroundColor, true) . "\n";
2217			if ($this->bodyBackgroundColor{0} == 5) { // RGBa
2218				$s .= $this->SetAlpha(ord($this->bodyBackgroundColor{4}) / 100, 'Normal', true, 'F') . "\n";
2219			} elseif ($this->bodyBackgroundColor{0} == 6) { // CMYKa
2220				$s .= $this->SetAlpha(ord($this->bodyBackgroundColor{5}) / 100, 'Normal', true, 'F') . "\n";
2221			}
2222			$s .= sprintf('%.3F %.3F %.3F %.3F re f Q', ($clx * Mpdf::SCALE), ($cly * Mpdf::SCALE), $clw * Mpdf::SCALE, $clh * Mpdf::SCALE) . "\n";
2223		}
2224
2225		/* -- BACKGROUNDS -- */
2226		if ($this->bodyBackgroundGradient) {
2227			$g = $this->gradient->parseBackgroundGradient($this->bodyBackgroundGradient);
2228			if ($g) {
2229				$s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, (isset($g['gradtype']) ? $g['gradtype'] : null), $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);
2230			}
2231		}
2232		if ($this->bodyBackgroundImage) {
2233			if (isset($this->bodyBackgroundImage['gradient']) && $this->bodyBackgroundImage['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->bodyBackgroundImage['gradient'])) {
2234				$g = $this->gradient->parseMozGradient($this->bodyBackgroundImage['gradient']);
2235				if ($g) {
2236					$s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);
2237				}
2238			} elseif ($this->bodyBackgroundImage['image_id']) { // Background pattern
2239				$n = count($this->patterns) + 1;
2240				// If using resize, uses TrimBox (not including the bleed)
2241				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']);
2242
2243				$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']];
2244				if (($this->bodyBackgroundImage['opacity'] > 0 || $this->bodyBackgroundImage['opacity'] === '0') && $this->bodyBackgroundImage['opacity'] < 1) {
2245					$opac = $this->SetAlpha($this->bodyBackgroundImage['opacity'], 'Normal', true);
2246				} else {
2247					$opac = '';
2248				}
2249				$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";
2250			}
2251		}
2252		/* -- END BACKGROUNDS -- */
2253		return $s;
2254	}
2255
2256	function _setClippingPath($clx, $cly, $clw, $clh)
2257	{
2258		$s = ' q 0 w '; // Line width=0
2259		$s .= sprintf('%.3F %.3F m ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // start point TL before the arc
2260		$s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BL
2261		$s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BR
2262		$s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TR
2263		$s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TL
2264		$s .= ' W n '; // Ends path no-op & Sets the clipping path
2265		return $s;
2266	}
2267
2268	function PrintPageBackgrounds($adjustmenty = 0)
2269	{
2270		$s = '';
2271
2272		ksort($this->pageBackgrounds);
2273		foreach ($this->pageBackgrounds as $bl => $pbs) {
2274			foreach ($pbs as $pb) {
2275				if ((!isset($pb['image_id']) && !isset($pb['gradient'])) || isset($pb['shadowonly'])) { // Background colour or boxshadow
2276					if ($pb['z-index'] > 0) {
2277						$this->current_layer = $pb['z-index'];
2278						$s .= "\n" . '/OCBZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n";
2279					}
2280
2281					if ($pb['visibility'] != 'visible') {
2282						if ($pb['visibility'] == 'printonly') {
2283							$s .= '/OC /OC1 BDC' . "\n";
2284						} elseif ($pb['visibility'] == 'screenonly') {
2285							$s .= '/OC /OC2 BDC' . "\n";
2286						} elseif ($pb['visibility'] == 'hidden') {
2287							$s .= '/OC /OC3 BDC' . "\n";
2288						}
2289					}
2290					// Box shadow
2291					if (isset($pb['shadow']) && $pb['shadow']) {
2292						$s .= $pb['shadow'] . "\n";
2293					}
2294					if (isset($pb['clippath']) && $pb['clippath']) {
2295						$s .= $pb['clippath'] . "\n";
2296					}
2297					$s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n";
2298					if ($pb['col']{0} == 5) { // RGBa
2299						$s .= $this->SetAlpha(ord($pb['col']{4}) / 100, 'Normal', true, 'F') . "\n";
2300					} elseif ($pb['col']{0} == 6) { // CMYKa
2301						$s .= $this->SetAlpha(ord($pb['col']{5}) / 100, 'Normal', true, 'F') . "\n";
2302					}
2303					$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";
2304					if (isset($pb['clippath']) && $pb['clippath']) {
2305						$s .= 'Q' . "\n";
2306					}
2307					if ($pb['visibility'] != 'visible') {
2308						$s .= 'EMC' . "\n";
2309					}
2310
2311					if ($pb['z-index'] > 0) {
2312						$s .= "\n" . 'EMCBZ-index' . "\n";
2313						$this->current_layer = 0;
2314					}
2315				}
2316			}
2317			/* -- BACKGROUNDS -- */
2318			foreach ($pbs as $pb) {
2319				if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {
2320					if ($pb['z-index'] > 0) {
2321						$this->current_layer = $pb['z-index'];
2322						$s .= "\n" . '/OCGZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n";
2323					}
2324					if ($pb['visibility'] != 'visible') {
2325						if ($pb['visibility'] == 'printonly') {
2326							$s .= '/OC /OC1 BDC' . "\n";
2327						} elseif ($pb['visibility'] == 'screenonly') {
2328							$s .= '/OC /OC2 BDC' . "\n";
2329						} elseif ($pb['visibility'] == 'hidden') {
2330							$s .= '/OC /OC3 BDC' . "\n";
2331						}
2332					}
2333				}
2334				if (isset($pb['gradient']) && $pb['gradient']) {
2335					if (isset($pb['clippath']) && $pb['clippath']) {
2336						$s .= $pb['clippath'] . "\n";
2337					}
2338					$s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);
2339					if (isset($pb['clippath']) && $pb['clippath']) {
2340						$s .= 'Q' . "\n";
2341					}
2342				} elseif (isset($pb['image_id']) && $pb['image_id']) { // Background Image
2343					$pb['y'] -= $adjustmenty;
2344					$pb['h'] += $adjustmenty;
2345					$n = count($this->patterns) + 1;
2346					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']);
2347					$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']];
2348					$x = $pb['x'] * Mpdf::SCALE;
2349					$y = ($this->h - $pb['y']) * Mpdf::SCALE;
2350					$w = $pb['w'] * Mpdf::SCALE;
2351					$h = -$pb['h'] * Mpdf::SCALE;
2352					if (isset($pb['clippath']) && $pb['clippath']) {
2353						$s .= $pb['clippath'] . "\n";
2354					}
2355					if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern
2356						$iw = $pb['orig_w'] / Mpdf::SCALE;
2357						$ih = $pb['orig_h'] / Mpdf::SCALE;
2358
2359						$w = $pb['w'];
2360						$h = $pb['h'];
2361						$x0 = $pb['x'];
2362						$y0 = $pb['y'];
2363
2364						if (isset($pb['bpa']) && $pb['bpa']) {
2365							$w = $pb['bpa']['w'];
2366							$h = $pb['bpa']['h'];
2367							$x0 = $pb['bpa']['x'];
2368							$y0 = $pb['bpa']['y'];
2369						}
2370
2371						if (isset($pb['size']['w']) && $pb['size']['w']) {
2372							$size = $pb['size'];
2373
2374							if ($size['w'] == 'contain') {
2375								// Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest
2376								// size such that both its width and its height can fit inside the background positioning area.
2377								// Same as resize==3
2378								$ih = $ih * $pb['bpa']['w'] / $iw;
2379								$iw = $pb['bpa']['w'];
2380								if ($ih > $pb['bpa']['h']) {
2381									$iw = $iw * $pb['bpa']['h'] / $ih;
2382									$ih = $pb['bpa']['h'];
2383								}
2384							} elseif ($size['w'] == 'cover') {
2385								// Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest
2386								// size such that both its width and its height can completely cover the background positioning area.
2387								$ih = $ih * $pb['bpa']['w'] / $iw;
2388								$iw = $pb['bpa']['w'];
2389								if ($ih < $pb['bpa']['h']) {
2390									$iw = $iw * $ih / $pb['bpa']['h'];
2391									$ih = $pb['bpa']['h'];
2392								}
2393							} else {
2394								if (NumericString::containsPercentChar($size['w'])) {
2395									$size['w'] = NumericString::removePercentChar($size['w']);
2396									$size['w'] /= 100;
2397									$size['w'] = ($pb['bpa']['w'] * $size['w']);
2398								}
2399								if (NumericString::containsPercentChar($size['h'])) {
2400									$size['h'] = NumericString::removePercentChar($size['h']);
2401									$size['h'] /= 100;
2402									$size['h'] = ($pb['bpa']['h'] * $size['h']);
2403								}
2404								if ($size['w'] == 'auto' && $size['h'] == 'auto') {
2405									$iw = $iw;
2406									$ih = $ih;
2407								} elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
2408									$iw = $iw * $size['h'] / $ih;
2409									$ih = $size['h'];
2410								} elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
2411									$ih = $ih * $size['w'] / $iw;
2412									$iw = $size['w'];
2413								} else {
2414									$iw = $size['w'];
2415									$ih = $size['h'];
2416								}
2417							}
2418						}
2419
2420						// Number to repeat
2421						if ($pb['x_repeat']) {
2422							$nx = ceil($pb['w'] / $iw) + 1;
2423						} else {
2424							$nx = 1;
2425						}
2426
2427						if ($pb['y_repeat']) {
2428							$ny = ceil($pb['h'] / $ih) + 1;
2429						} else {
2430							$ny = 1;
2431						}
2432
2433						$x_pos = $pb['x_pos'];
2434						if (stristr($x_pos, '%')) {
2435							$x_pos = (float) $x_pos;
2436							$x_pos /= 100;
2437							$x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
2438						}
2439
2440						$y_pos = $pb['y_pos'];
2441						if (stristr($y_pos, '%')) {
2442							$y_pos = (float) $y_pos;
2443							$y_pos /= 100;
2444							$y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
2445						}
2446						if ($nx > 1) {
2447							while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {
2448								$x_pos -= $iw;
2449							}
2450						}
2451
2452						if ($ny > 1) {
2453							while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {
2454								$y_pos -= $ih;
2455							}
2456						}
2457
2458						for ($xi = 0; $xi < $nx; $xi++) {
2459							for ($yi = 0; $yi < $ny; $yi++) {
2460								$x = $x0 + $x_pos + ($iw * $xi);
2461								$y = $y0 + $y_pos + ($ih * $yi);
2462								if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {
2463									$opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
2464								} else {
2465									$opac = '';
2466								}
2467								$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";
2468							}
2469						}
2470					} else {
2471						if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {
2472							$opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
2473						} else {
2474							$opac = '';
2475						}
2476						$s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n";
2477					}
2478
2479					if (isset($pb['clippath']) && $pb['clippath']) {
2480						$s .= 'Q' . "\n";
2481					}
2482				}
2483
2484				if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {
2485					if ($pb['visibility'] != 'visible') {
2486						$s .= 'EMC' . "\n";
2487					}
2488
2489					if ($pb['z-index'] > 0) {
2490						$s .= "\n" . 'EMCGZ-index' . "\n";
2491						$this->current_layer = 0;
2492					}
2493				}
2494			}
2495			/* -- END BACKGROUNDS -- */
2496		}
2497		return $s;
2498	}
2499
2500	function PrintTableBackgrounds($adjustmenty = 0)
2501	{
2502		$s = '';
2503		/* -- BACKGROUNDS -- */
2504		ksort($this->tableBackgrounds);
2505		foreach ($this->tableBackgrounds as $bl => $pbs) {
2506			foreach ($pbs as $pb) {
2507				if ((!isset($pb['gradient']) || !$pb['gradient']) && (!isset($pb['image_id']) || !$pb['image_id'])) {
2508					$s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n";
2509					if ($pb['col']{0} == 5) { // RGBa
2510						$s .= $this->SetAlpha(ord($pb['col']{4}) / 100, 'Normal', true, 'F') . "\n";
2511					} elseif ($pb['col']{0} == 6) { // CMYKa
2512						$s .= $this->SetAlpha(ord($pb['col']{5}) / 100, 'Normal', true, 'F') . "\n";
2513					}
2514					$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";
2515				}
2516				if (isset($pb['gradient']) && $pb['gradient']) {
2517					if (isset($pb['clippath']) && $pb['clippath']) {
2518						$s .= $pb['clippath'] . "\n";
2519					}
2520					$s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);
2521					if (isset($pb['clippath']) && $pb['clippath']) {
2522						$s .= 'Q' . "\n";
2523					}
2524				}
2525				if (isset($pb['image_id']) && $pb['image_id']) { // Background pattern
2526					$pb['y'] -= $adjustmenty;
2527					$pb['h'] += $adjustmenty;
2528					$n = count($this->patterns) + 1;
2529					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']);
2530					$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']];
2531					$x = $pb['x'] * Mpdf::SCALE;
2532					$y = ($this->h - $pb['y']) * Mpdf::SCALE;
2533					$w = $pb['w'] * Mpdf::SCALE;
2534					$h = -$pb['h'] * Mpdf::SCALE;
2535
2536					// mPDF 5.7.3
2537					if (($this->writingHTMLfooter || $this->writingHTMLheader) && (!isset($pb['clippath']) || $pb['clippath'] == '')) {
2538						// Set clipping path
2539						$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);
2540					}
2541
2542					if (isset($pb['clippath']) && $pb['clippath']) {
2543						$s .= $pb['clippath'] . "\n";
2544					}
2545
2546					// mPDF 5.7.3
2547					if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern
2548						$iw = $pb['orig_w'] / Mpdf::SCALE;
2549						$ih = $pb['orig_h'] / Mpdf::SCALE;
2550
2551						$w = $pb['w'];
2552						$h = $pb['h'];
2553						$x0 = $pb['x'];
2554						$y0 = $pb['y'];
2555
2556						if (isset($pb['bpa']) && $pb['bpa']) {
2557							$w = $pb['bpa']['w'];
2558							$h = $pb['bpa']['h'];
2559							$x0 = $pb['bpa']['x'];
2560							$y0 = $pb['bpa']['y'];
2561						} // At present 'bpa' (background page area) is not set for tablebackgrounds - only pagebackgrounds
2562						// For now, just set it as:
2563						else {
2564							$pb['bpa'] = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h];
2565						}
2566
2567						if (isset($pb['size']['w']) && $pb['size']['w']) {
2568							$size = $pb['size'];
2569
2570							if ($size['w'] == 'contain') {
2571								// 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.
2572								// Same as resize==3
2573								$ih = $ih * $pb['bpa']['w'] / $iw;
2574								$iw = $pb['bpa']['w'];
2575								if ($ih > $pb['bpa']['h']) {
2576									$iw = $iw * $pb['bpa']['h'] / $ih;
2577									$ih = $pb['bpa']['h'];
2578								}
2579							} elseif ($size['w'] == 'cover') {
2580								// 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.
2581								$ih = $ih * $pb['bpa']['w'] / $iw;
2582								$iw = $pb['bpa']['w'];
2583								if ($ih < $pb['bpa']['h']) {
2584									$iw = $iw * $ih / $pb['bpa']['h'];
2585									$ih = $pb['bpa']['h'];
2586								}
2587							} else {
2588								if (NumericString::containsPercentChar($size['w'])) {
2589									$size['w'] = NumericString::removePercentChar($size['w']);
2590									$size['w'] /= 100;
2591									$size['w'] = ($pb['bpa']['w'] * $size['w']);
2592								}
2593								if (NumericString::containsPercentChar($size['h'])) {
2594									$size['h'] = NumericString::removePercentChar($size['h']);
2595									$size['h'] /= 100;
2596									$size['h'] = ($pb['bpa']['h'] * $size['h']);
2597								}
2598								if ($size['w'] == 'auto' && $size['h'] == 'auto') {
2599									$iw = $iw;
2600									$ih = $ih;
2601								} elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
2602									$iw = $iw * $size['h'] / $ih;
2603									$ih = $size['h'];
2604								} elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
2605									$ih = $ih * $size['w'] / $iw;
2606									$iw = $size['w'];
2607								} else {
2608									$iw = $size['w'];
2609									$ih = $size['h'];
2610								}
2611							}
2612						}
2613
2614						// Number to repeat
2615						if (isset($pb['x_repeat']) && $pb['x_repeat']) {
2616							$nx = ceil($pb['w'] / $iw) + 1;
2617						} else {
2618							$nx = 1;
2619						}
2620						if (isset($pb['y_repeat']) && $pb['y_repeat']) {
2621							$ny = ceil($pb['h'] / $ih) + 1;
2622						} else {
2623							$ny = 1;
2624						}
2625
2626						$x_pos = $pb['x_pos'];
2627						if (NumericString::containsPercentChar($x_pos)) {
2628							$x_pos = NumericString::removePercentChar($x_pos);
2629							$x_pos /= 100;
2630							$x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
2631						}
2632						$y_pos = $pb['y_pos'];
2633						if (NumericString::containsPercentChar($y_pos)) {
2634							$y_pos = NumericString::removePercentChar($y_pos);
2635							$y_pos /= 100;
2636							$y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
2637						}
2638						if ($nx > 1) {
2639							while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {
2640								$x_pos -= $iw;
2641							}
2642						}
2643						if ($ny > 1) {
2644							while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {
2645								$y_pos -= $ih;
2646							}
2647						}
2648						for ($xi = 0; $xi < $nx; $xi++) {
2649							for ($yi = 0; $yi < $ny; $yi++) {
2650								$x = $x0 + $x_pos + ($iw * $xi);
2651								$y = $y0 + $y_pos + ($ih * $yi);
2652								if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {
2653									$opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
2654								} else {
2655									$opac = '';
2656								}
2657								$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";
2658							}
2659						}
2660					} else {
2661						if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {
2662							$opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
2663						} else {
2664							$opac = '';
2665						}
2666						$s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n";
2667					}
2668
2669					if (isset($pb['clippath']) && $pb['clippath']) {
2670						$s .= 'Q' . "\n";
2671					}
2672				}
2673			}
2674		}
2675		/* -- END BACKGROUNDS -- */
2676		return $s;
2677	}
2678
2679	function BeginLayer($id)
2680	{
2681		if ($this->current_layer > 0) {
2682			$this->EndLayer();
2683		}
2684		if ($id < 1) {
2685			return false;
2686		}
2687		if (!isset($this->layers[$id])) {
2688			$this->layers[$id] = ['name' => 'Layer ' . ($id)];
2689			if (($this->PDFA || $this->PDFX)) {
2690				$this->PDFAXwarnings[] = "Cannot use layers when using PDFA or PDFX";
2691				return '';
2692			} elseif (!$this->PDFA && !$this->PDFX) {
2693				$this->pdf_version = '1.5';
2694			}
2695		}
2696		$this->current_layer = $id;
2697		$this->_out('/OCZ-index /ZI' . $id . ' BDC');
2698
2699		$this->pageoutput[$this->page] = [];
2700	}
2701
2702	function EndLayer()
2703	{
2704		if ($this->current_layer > 0) {
2705			$this->_out('EMCZ-index');
2706			$this->current_layer = 0;
2707		}
2708	}
2709
2710	function AddPageByArray($a)
2711	{
2712		if (!is_array($a)) {
2713			$a = [];
2714		}
2715
2716		$orientation = (isset($a['orientation']) ? $a['orientation'] : '');
2717		$condition = (isset($a['condition']) ? $a['condition'] : (isset($a['type']) ? $a['type'] : ''));
2718		$resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');
2719		$pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');
2720		$suppress = (isset($a['suppress']) ? $a['suppress'] : '');
2721		$mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));
2722		$mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));
2723		$mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));
2724		$mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));
2725		$mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));
2726		$mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));
2727		$ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));
2728		$ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));
2729		$ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));
2730		$efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));
2731		$ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));
2732		$ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));
2733		$ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));
2734		$efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));
2735		$pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));
2736		$newformat = (isset($a['newformat']) ? $a['newformat'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));
2737
2738		$this->AddPage($orientation, $condition, $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);
2739	}
2740
2741	// mPDF 6 pagebreaktype
2742	function _preForcedPagebreak($pagebreaktype)
2743	{
2744		if ($pagebreaktype == 'cloneall') {
2745			// Close any open block tags
2746			$arr = [];
2747			$ai = 0;
2748			for ($b = $this->blklvl; $b > 0; $b--) {
2749				$this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
2750			}
2751			if ($this->blklvl == 0 && !empty($this->textbuffer)) { // Output previously buffered content
2752				$this->printbuffer($this->textbuffer, 1);
2753				$this->textbuffer = [];
2754			}
2755		} elseif ($pagebreaktype == 'clonebycss') {
2756			// Close open block tags whilst box-decoration-break==clone
2757			$arr = [];
2758			$ai = 0;
2759			for ($b = $this->blklvl; $b > 0; $b--) {
2760				if (isset($this->blk[$b]['box_decoration_break']) && $this->blk[$b]['box_decoration_break'] == 'clone') {
2761					$this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
2762				} else {
2763					if ($b == $this->blklvl && !empty($this->textbuffer)) { // Output previously buffered content
2764						$this->printbuffer($this->textbuffer, 1);
2765						$this->textbuffer = [];
2766					}
2767					break;
2768				}
2769			}
2770		} elseif (!empty($this->textbuffer)) { // Output previously buffered content
2771			$this->printbuffer($this->textbuffer, 1);
2772			$this->textbuffer = [];
2773		}
2774	}
2775
2776	// mPDF 6 pagebreaktype
2777	function _postForcedPagebreak($pagebreaktype, $startpage, $save_blk, $save_blklvl)
2778	{
2779		if ($pagebreaktype == 'cloneall') {
2780			$this->blk = [];
2781			$this->blk[0] = $save_blk[0];
2782			// Re-open block tags
2783			$this->blklvl = 0;
2784			$arr = [];
2785			$i = 0;
2786			for ($b = 1; $b <= $save_blklvl; $b++) {
2787				$this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);
2788			}
2789		} elseif ($pagebreaktype == 'clonebycss') {
2790			$this->blk = [];
2791			$this->blk[0] = $save_blk[0];
2792			// Don't re-open tags for lowest level elements - so need to do some adjustments
2793			for ($b = 1; $b <= $this->blklvl; $b++) {
2794				$this->blk[$b] = $save_blk[$b];
2795				$this->blk[$b]['startpage'] = 0;
2796				$this->blk[$b]['y0'] = $this->y; // ?? $this->tMargin
2797				if (($this->page - $startpage) % 2) {
2798					if (isset($this->blk[$b]['x0'])) {
2799						$this->blk[$b]['x0'] += $this->MarginCorrection;
2800					} else {
2801						$this->blk[$b]['x0'] = $this->MarginCorrection;
2802					}
2803				}
2804				// for Float DIV
2805				$this->blk[$b]['marginCorrected'][$this->page] = true;
2806			}
2807
2808			// Re-open block tags for any that have box_decoration_break==clone
2809			$arr = [];
2810			$i = 0;
2811			for ($b = $this->blklvl + 1; $b <= $save_blklvl; $b++) {
2812				if ($b < $this->blklvl) {
2813					$this->lastblocklevelchange = -1;
2814				}
2815				$this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);
2816			}
2817			if ($this->blk[$this->blklvl]['box_decoration_break'] != 'clone') {
2818				$this->lastblocklevelchange = -1;
2819			}
2820		} else {
2821			$this->lastblocklevelchange = -1;
2822		}
2823	}
2824
2825	function AddPage(
2826		$orientation = '',
2827		$condition = '',
2828		$resetpagenum = '',
2829		$pagenumstyle = '',
2830		$suppress = '',
2831		$mgl = '',
2832		$mgr = '',
2833		$mgt = '',
2834		$mgb = '',
2835		$mgh = '',
2836		$mgf = '',
2837		$ohname = '',
2838		$ehname = '',
2839		$ofname = '',
2840		$efname = '',
2841		$ohvalue = 0,
2842		$ehvalue = 0,
2843		$ofvalue = 0,
2844		$efvalue = 0,
2845		$pagesel = '',
2846		$newformat = ''
2847	) {
2848		/* -- CSS-FLOAT -- */
2849		// Float DIV
2850		// Cannot do with columns on, or if any change in page orientation/margins etc.
2851		// If next page already exists - i.e background /headers and footers already written
2852		if ($this->state > 0 && $this->page < count($this->pages)) {
2853			$bak_cml = $this->cMarginL;
2854			$bak_cmr = $this->cMarginR;
2855			$bak_dw = $this->divwidth;
2856			// Paint Div Border if necessary
2857			if ($this->blklvl > 0) {
2858				$save_tr = $this->table_rotate; // *TABLES*
2859				$this->table_rotate = 0; // *TABLES*
2860				if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0']) {
2861					$this->blk[$this->blklvl]['startpage'] ++;
2862				}
2863				if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table']) {
2864					$toplvl = $this->blklvl;
2865				} else {
2866					$toplvl = $this->blklvl - 1;
2867				}
2868				$sy = $this->y;
2869				for ($bl = 1; $bl <= $toplvl; $bl++) {
2870					$this->PaintDivBB('pagebottom', 0, $bl);
2871				}
2872				$this->y = $sy;
2873				$this->table_rotate = $save_tr; // *TABLES*
2874			}
2875			$s = $this->PrintPageBackgrounds();
2876
2877			// Writes after the marker so not overwritten later by page background etc.
2878			$this->pages[$this->page] = preg_replace(
2879				'/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/',
2880				'\\1' . "\n" . $s . "\n",
2881				$this->pages[$this->page]
2882			);
2883
2884			$this->pageBackgrounds = [];
2885			$family = $this->FontFamily;
2886			$style = $this->FontStyle;
2887			$size = $this->FontSizePt;
2888			$lw = $this->LineWidth;
2889			$dc = $this->DrawColor;
2890			$fc = $this->FillColor;
2891			$tc = $this->TextColor;
2892			$cf = $this->ColorFlag;
2893
2894			$this->printfloatbuffer();
2895
2896			// Move to next page
2897			$this->page++;
2898
2899			$this->ResetMargins();
2900			$this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
2901			$this->x = $this->lMargin;
2902			$this->y = $this->tMargin;
2903			$this->FontFamily = '';
2904			$this->_out('2 J');
2905			$this->LineWidth = $lw;
2906			$this->_out(sprintf('%.3F w', $lw * Mpdf::SCALE));
2907
2908			if ($family) {
2909				$this->SetFont($family, $style, $size, true, true);
2910			}
2911
2912			$this->DrawColor = $dc;
2913
2914			if ($dc != $this->defDrawColor) {
2915				$this->_out($dc);
2916			}
2917
2918			$this->FillColor = $fc;
2919
2920			if ($fc != $this->defFillColor) {
2921				$this->_out($fc);
2922			}
2923
2924			$this->TextColor = $tc;
2925			$this->ColorFlag = $cf;
2926
2927			for ($bl = 1; $bl <= $this->blklvl; $bl++) {
2928				$this->blk[$bl]['y0'] = $this->y;
2929				// Don't correct more than once for background DIV containing a Float
2930				if (!isset($this->blk[$bl]['marginCorrected'][$this->page])) {
2931					if (isset($this->blk[$bl]['x0'])) {
2932						$this->blk[$bl]['x0'] += $this->MarginCorrection;
2933					} else {
2934						$this->blk[$bl]['x0'] = $this->MarginCorrection;
2935					}
2936				}
2937				$this->blk[$bl]['marginCorrected'][$this->page] = true;
2938			}
2939
2940			$this->cMarginL = $bak_cml;
2941			$this->cMarginR = $bak_cmr;
2942			$this->divwidth = $bak_dw;
2943
2944			return '';
2945		}
2946		/* -- END CSS-FLOAT -- */
2947
2948		// Start a new page
2949		if ($this->state == 0) {
2950			$this->Open();
2951		}
2952
2953		$bak_cml = $this->cMarginL;
2954		$bak_cmr = $this->cMarginR;
2955		$bak_dw = $this->divwidth;
2956
2957		$bak_lh = $this->lineheight;
2958
2959		$orientation = substr(strtoupper($orientation), 0, 1);
2960		$condition = strtoupper($condition);
2961
2962
2963		if ($condition == 'E') { // only adds new page if needed to create an Even page
2964			if (!$this->mirrorMargins || ($this->page) % 2 == 0) {
2965				return false;
2966			}
2967		} elseif ($condition == 'O') { // only adds new page if needed to create an Odd page
2968			if (!$this->mirrorMargins || ($this->page) % 2 == 1) {
2969				return false;
2970			}
2971		} elseif ($condition == 'NEXT-EVEN') { // always adds at least one new page to create an Even page
2972			if (!$this->mirrorMargins) {
2973				$condition = '';
2974			} else {
2975				if ($pagesel) {
2976					$pbch = $pagesel;
2977					$pagesel = '';
2978				} // *CSS-PAGE*
2979				else {
2980					$pbch = false;
2981				} // *CSS-PAGE*
2982				$this->AddPage($this->CurOrientation, 'O');
2983				$this->extrapagebreak = true; // mPDF 6 pagebreaktype
2984				if ($pbch) {
2985					$pagesel = $pbch;
2986				} // *CSS-PAGE*
2987				$condition = '';
2988			}
2989		} elseif ($condition == 'NEXT-ODD') { // always adds at least one new page to create an Odd page
2990			if (!$this->mirrorMargins) {
2991				$condition = '';
2992			} else {
2993				if ($pagesel) {
2994					$pbch = $pagesel;
2995					$pagesel = '';
2996				} // *CSS-PAGE*
2997				else {
2998					$pbch = false;
2999				} // *CSS-PAGE*
3000				$this->AddPage($this->CurOrientation, 'E');
3001				$this->extrapagebreak = true; // mPDF 6 pagebreaktype
3002				if ($pbch) {
3003					$pagesel = $pbch;
3004				} // *CSS-PAGE*
3005				$condition = '';
3006			}
3007		}
3008
3009		if ($resetpagenum || $pagenumstyle || $suppress) {
3010			$this->PageNumSubstitutions[] = ['from' => ($this->page + 1), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
3011		}
3012
3013		$save_tr = $this->table_rotate; // *TABLES*
3014		$this->table_rotate = 0; // *TABLES*
3015		$save_kwt = $this->kwt;
3016		$this->kwt = 0;
3017		$save_layer = $this->current_layer;
3018		$save_vis = $this->visibility;
3019
3020		if ($this->visibility != 'visible') {
3021			$this->SetVisibility('visible');
3022		}
3023
3024		$this->EndLayer();
3025
3026		// Paint Div Border if necessary
3027		// PAINTS BACKGROUND COLOUR OR BORDERS for DIV - DISABLED FOR COLUMNS (cf. AcceptPageBreak) AT PRESENT in ->PaintDivBB
3028		if (!$this->ColActive && $this->blklvl > 0) {
3029			if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0'] && !$this->extrapagebreak) { // mPDF 6 pagebreaktype
3030				if (isset($this->blk[$this->blklvl]['startpage'])) {
3031					$this->blk[$this->blklvl]['startpage'] ++;
3032				} else {
3033					$this->blk[$this->blklvl]['startpage'] = 1;
3034				}
3035			}
3036			if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table'] || $this->extrapagebreak) {
3037				$toplvl = $this->blklvl;
3038			} // mPDF 6 pagebreaktype
3039			else {
3040				$toplvl = $this->blklvl - 1;
3041			}
3042			$sy = $this->y;
3043			for ($bl = 1; $bl <= $toplvl; $bl++) {
3044				if (isset($this->blk[$bl]['z-index']) && $this->blk[$bl]['z-index'] > 0) {
3045					$this->BeginLayer($this->blk[$bl]['z-index']);
3046				}
3047				if (isset($this->blk[$bl]['visibility']) && $this->blk[$bl]['visibility'] && $this->blk[$bl]['visibility'] != 'visible') {
3048					$this->SetVisibility($this->blk[$bl]['visibility']);
3049				}
3050				$this->PaintDivBB('pagebottom', 0, $bl);
3051			}
3052			$this->y = $sy;
3053			// RESET block y0 and x0 - see below
3054		}
3055		$this->extrapagebreak = false; // mPDF 6 pagebreaktype
3056
3057		if ($this->visibility != 'visible') {
3058			$this->SetVisibility('visible');
3059		}
3060
3061		$this->EndLayer();
3062
3063		// BODY Backgrounds
3064		if ($this->page > 0) {
3065			$s = '';
3066			$s .= $this->PrintBodyBackgrounds();
3067
3068			$s .= $this->PrintPageBackgrounds();
3069			$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $s . "\n" . '\\1', $this->pages[$this->page]);
3070			$this->pageBackgrounds = [];
3071		}
3072
3073		$save_kt = $this->keep_block_together;
3074		$this->keep_block_together = 0;
3075
3076		$save_cols = false;
3077
3078		/* -- COLUMNS -- */
3079		if ($this->ColActive) {
3080			$save_cols = true;
3081			$save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off
3082			$this->SetColumns(0);
3083		}
3084		/* -- END COLUMNS -- */
3085
3086		$family = $this->FontFamily;
3087		$style = $this->FontStyle;
3088		$size = $this->FontSizePt;
3089		$this->ColumnAdjust = true; // enables column height adjustment for the page
3090		$lw = $this->LineWidth;
3091		$dc = $this->DrawColor;
3092		$fc = $this->FillColor;
3093		$tc = $this->TextColor;
3094		$cf = $this->ColorFlag;
3095		if ($this->page > 0) {
3096			// Page footer
3097			$this->InFooter = true;
3098
3099			$this->Reset();
3100			$this->pageoutput[$this->page] = [];
3101
3102			$this->Footer();
3103			// Close page
3104			$this->_endpage();
3105		}
3106
3107		// Start new page
3108		$this->_beginpage($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);
3109
3110		if ($this->docTemplate) {
3111			$pagecount = $this->SetSourceFile($this->docTemplate);
3112			if (($this->page - $this->docTemplateStart) > $pagecount) {
3113				if ($this->docTemplateContinue) {
3114					$tplIdx = $this->ImportPage($pagecount);
3115					$this->UseTemplate($tplIdx);
3116				}
3117			} else {
3118				$tplIdx = $this->ImportPage(($this->page - $this->docTemplateStart));
3119				$this->UseTemplate($tplIdx);
3120			}
3121		}
3122
3123		if ($this->pageTemplate) {
3124			$this->UseTemplate($this->pageTemplate);
3125		}
3126
3127		// Tiling Patterns
3128		$this->_out('___PAGE___START' . $this->uniqstr);
3129		$this->_out('___BACKGROUND___PATTERNS' . $this->uniqstr);
3130		$this->_out('___HEADER___MARKER' . $this->uniqstr);
3131		$this->pageBackgrounds = [];
3132
3133		// Set line cap style to square
3134		$this->SetLineCap(2);
3135		// Set line width
3136		$this->LineWidth = $lw;
3137		$this->_out(sprintf('%.3F w', $lw * Mpdf::SCALE));
3138		// Set font
3139		if ($family) {
3140			$this->SetFont($family, $style, $size, true, true); // forces write
3141		}
3142
3143		// Set colors
3144		$this->DrawColor = $dc;
3145		if ($dc != $this->defDrawColor) {
3146			$this->_out($dc);
3147		}
3148		$this->FillColor = $fc;
3149		if ($fc != $this->defFillColor) {
3150			$this->_out($fc);
3151		}
3152		$this->TextColor = $tc;
3153		$this->ColorFlag = $cf;
3154
3155		// Page header
3156		$this->Header();
3157
3158		// Restore line width
3159		if ($this->LineWidth != $lw) {
3160			$this->LineWidth = $lw;
3161			$this->_out(sprintf('%.3F w', $lw * Mpdf::SCALE));
3162		}
3163		// Restore font
3164		if ($family) {
3165			$this->SetFont($family, $style, $size, true, true); // forces write
3166		}
3167
3168		// Restore colors
3169		if ($this->DrawColor != $dc) {
3170			$this->DrawColor = $dc;
3171			$this->_out($dc);
3172		}
3173		if ($this->FillColor != $fc) {
3174			$this->FillColor = $fc;
3175			$this->_out($fc);
3176		}
3177		$this->TextColor = $tc;
3178		$this->ColorFlag = $cf;
3179		$this->InFooter = false;
3180
3181		if ($save_layer > 0) {
3182			$this->BeginLayer($save_layer);
3183		}
3184
3185		if ($save_vis != 'visible') {
3186			$this->SetVisibility($save_vis);
3187		}
3188
3189		/* -- COLUMNS -- */
3190		if ($save_cols) {
3191			// Restore columns
3192			$this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);
3193		}
3194		if ($this->ColActive) {
3195			$this->SetCol(0);
3196		}
3197		/* -- END COLUMNS -- */
3198
3199
3200		// RESET BLOCK BORDER TOP
3201		if (!$this->ColActive) {
3202			for ($bl = 1; $bl <= $this->blklvl; $bl++) {
3203				$this->blk[$bl]['y0'] = $this->y;
3204				if (isset($this->blk[$bl]['x0'])) {
3205					$this->blk[$bl]['x0'] += $this->MarginCorrection;
3206				} else {
3207					$this->blk[$bl]['x0'] = $this->MarginCorrection;
3208				}
3209				// Added mPDF 3.0 Float DIV
3210				$this->blk[$bl]['marginCorrected'][$this->page] = true;
3211			}
3212		}
3213
3214
3215		$this->table_rotate = $save_tr; // *TABLES*
3216		$this->kwt = $save_kwt;
3217
3218		$this->keep_block_together = $save_kt;
3219
3220		$this->cMarginL = $bak_cml;
3221		$this->cMarginR = $bak_cmr;
3222		$this->divwidth = $bak_dw;
3223
3224		$this->lineheight = $bak_lh;
3225	}
3226
3227	/**
3228	 * Get current page number
3229	 *
3230	 * @return int
3231	 */
3232	function PageNo()
3233	{
3234		return $this->page;
3235	}
3236
3237	function AddSpotColorsFromFile($file)
3238	{
3239		$colors = @file($file);
3240		if (!$colors) {
3241			throw new \Mpdf\MpdfException("Cannot load spot colors file - " . $file);
3242		}
3243		foreach ($colors as $sc) {
3244			list($name, $c, $m, $y, $k) = preg_split("/\t/", $sc);
3245			$c = intval($c);
3246			$m = intval($m);
3247			$y = intval($y);
3248			$k = intval($k);
3249			$this->AddSpotColor($name, $c, $m, $y, $k);
3250		}
3251	}
3252
3253	function AddSpotColor($name, $c, $m, $y, $k)
3254	{
3255		$name = strtoupper(trim($name));
3256		if (!isset($this->spotColors[$name])) {
3257			$i = count($this->spotColors) + 1;
3258			$this->spotColors[$name] = ['i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k];
3259			$this->spotColorIDs[$i] = $name;
3260		}
3261	}
3262
3263	function SetColor($col, $type = '')
3264	{
3265		$out = '';
3266		if (!$col) {
3267			return '';
3268		} // mPDF 6
3269		if ($col{0} == 3 || $col{0} == 5) { // RGB / RGBa
3270			$out = sprintf('%.3F %.3F %.3F rg', ord($col{1}) / 255, ord($col{2}) / 255, ord($col{3}) / 255);
3271		} elseif ($col{0} == 1) { // GRAYSCALE
3272			$out = sprintf('%.3F g', ord($col{1}) / 255);
3273		} elseif ($col{0} == 2) { // SPOT COLOR
3274			$out = sprintf('/CS%d cs %.3F scn', ord($col{1}), ord($col{2}) / 100);
3275		} elseif ($col{0} == 4 || $col{0} == 6) { // CMYK / CMYKa
3276			$out = sprintf('%.3F %.3F %.3F %.3F k', ord($col{1}) / 100, ord($col{2}) / 100, ord($col{3}) / 100, ord($col{4}) / 100);
3277		}
3278		if ($type == 'Draw') {
3279			$out = strtoupper($out);
3280		} // e.g. rg => RG
3281		elseif ($type == 'CodeOnly') {
3282			$out = preg_replace('/\s(rg|g|k)/', '', $out);
3283		}
3284		return $out;
3285	}
3286
3287	function SetDColor($col, $return = false)
3288	{
3289		$out = $this->SetColor($col, 'Draw');
3290		if ($return) {
3291			return $out;
3292		}
3293		if ($out == '') {
3294			return '';
3295		}
3296		$this->DrawColor = $out;
3297		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['DrawColor']) && $this->pageoutput[$this->page]['DrawColor'] != $this->DrawColor) || !isset($this->pageoutput[$this->page]['DrawColor']))) {
3298			$this->_out($this->DrawColor);
3299		}
3300		$this->pageoutput[$this->page]['DrawColor'] = $this->DrawColor;
3301	}
3302
3303	function SetFColor($col, $return = false)
3304	{
3305		$out = $this->SetColor($col, 'Fill');
3306		if ($return) {
3307			return $out;
3308		}
3309		if ($out == '') {
3310			return '';
3311		}
3312		$this->FillColor = $out;
3313		$this->ColorFlag = ($out != $this->TextColor);
3314		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor']))) {
3315			$this->_out($this->FillColor);
3316		}
3317		$this->pageoutput[$this->page]['FillColor'] = $this->FillColor;
3318	}
3319
3320	function SetTColor($col, $return = false)
3321	{
3322		$out = $this->SetColor($col, 'Text');
3323		if ($return) {
3324			return $out;
3325		}
3326		if ($out == '') {
3327			return '';
3328		}
3329		$this->TextColor = $out;
3330		$this->ColorFlag = ($this->FillColor != $out);
3331	}
3332
3333	function SetDrawColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
3334	{
3335		// Set color for all stroking operations
3336		$col = [];
3337		if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
3338			$col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
3339		} elseif ($col4 == -1) {
3340			$col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
3341		} else {
3342			$col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
3343		}
3344		$out = $this->SetDColor($col, $return);
3345		return $out;
3346	}
3347
3348	function SetFillColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
3349	{
3350		// Set color for all filling operations
3351		$col = [];
3352		if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
3353			$col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
3354		} elseif ($col4 == -1) {
3355			$col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
3356		} else {
3357			$col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
3358		}
3359		$out = $this->SetFColor($col, $return);
3360		return $out;
3361	}
3362
3363	function SetTextColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
3364	{
3365		// Set color for text
3366		$col = [];
3367		if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
3368			$col = $this->colorConverter->convert($r, $this->PDFAXwarnings);
3369		} elseif ($col4 == -1) {
3370			$col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);
3371		} else {
3372			$col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);
3373		}
3374		$out = $this->SetTColor($col, $return);
3375		return $out;
3376	}
3377
3378	function _getCharWidth(&$cw, $u, $isdef = true)
3379	{
3380		$w = 0;
3381
3382		if ($u == 0) {
3383			$w = false;
3384		} elseif (isset($cw[$u * 2 + 1])) {
3385			$w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);
3386		}
3387
3388		if ($w == 65535) {
3389			return 0;
3390		} elseif ($w) {
3391			return $w;
3392		} elseif ($isdef) {
3393			return false;
3394		} else {
3395			return 0;
3396		}
3397	}
3398
3399	function _charDefined(&$cw, $u)
3400	{
3401		$w = 0;
3402		if ($u == 0) {
3403			return false;
3404		}
3405		if (isset($cw[$u * 2 + 1])) {
3406			$w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);
3407		}
3408		if ($w) {
3409			return true;
3410		} else {
3411			return false;
3412		}
3413	}
3414
3415	function GetCharWidthCore($c)
3416	{
3417		// Get width of a single character in the current Core font
3418		$c = (string) $c;
3419		$w = 0;
3420		// Soft Hyphens chr(173)
3421		if ($c == chr(173) && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
3422			return 0;
3423		} elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($c)])) {  // mPDF 5.7.1
3424			$charw = $this->CurrentFont['cw'][chr($this->upperCase[ord($c)])];
3425			if ($charw !== false) {
3426				$charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
3427				$w+=$charw;
3428			}
3429		} elseif (isset($this->CurrentFont['cw'][$c])) {
3430			$w += $this->CurrentFont['cw'][$c];
3431		} elseif (isset($this->CurrentFont['cw'][ord($c)])) {
3432			$w += $this->CurrentFont['cw'][ord($c)];
3433		}
3434		$w *= ($this->FontSize / 1000);
3435		if ($this->minwSpacing || $this->fixedlSpacing) {
3436			if ($c == ' ') {
3437				$nb_spaces = 1;
3438			} else {
3439				$nb_spaces = 0;
3440			}
3441			$w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);
3442		}
3443		return ($w);
3444	}
3445
3446	function GetCharWidthNonCore($c, $addSubset = true)
3447	{
3448		// Get width of a single character in the current Non-Core font
3449		$c = (string) $c;
3450		$w = 0;
3451		$unicode = $this->UTF8StringToArray($c, $addSubset);
3452		$char = $unicode[0];
3453		/* -- CJK-FONTS -- */
3454		if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts
3455			if ($char == 173) {
3456				return 0;
3457			} // Soft Hyphens
3458			elseif (isset($this->CurrentFont['cw'][$char])) {
3459				$w+=$this->CurrentFont['cw'][$char];
3460			} elseif (isset($this->CurrentFont['MissingWidth'])) {
3461				$w += $this->CurrentFont['MissingWidth'];
3462			} else {
3463				$w += 500;
3464			}
3465		} else {
3466			/* -- END CJK-FONTS -- */
3467			if ($char == 173) {
3468				return 0;
3469			} // Soft Hyphens
3470			elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) { // mPDF 5.7.1
3471				$charw = $this->_getCharWidth($this->CurrentFont['cw'], $this->upperCase[$char]);
3472				if ($charw !== false) {
3473					$charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
3474					$w+=$charw;
3475				} elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
3476					$w += $this->CurrentFont['desc']['MissingWidth'];
3477				} elseif (isset($this->CurrentFont['MissingWidth'])) {
3478					$w += $this->CurrentFont['MissingWidth'];
3479				} else {
3480					$w += 500;
3481				}
3482			} else {
3483				$charw = $this->_getCharWidth($this->CurrentFont['cw'], $char);
3484				if ($charw !== false) {
3485					$w+=$charw;
3486				} elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
3487					$w += $this->CurrentFont['desc']['MissingWidth'];
3488				} elseif (isset($this->CurrentFont['MissingWidth'])) {
3489					$w += $this->CurrentFont['MissingWidth'];
3490				} else {
3491					$w += 500;
3492				}
3493			}
3494		} // *CJK-FONTS*
3495		$w *= ($this->FontSize / 1000);
3496		if ($this->minwSpacing || $this->fixedlSpacing) {
3497			if ($c == ' ') {
3498				$nb_spaces = 1;
3499			} else {
3500				$nb_spaces = 0;
3501			}
3502			$w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);
3503		}
3504		return ($w);
3505	}
3506
3507	function GetCharWidth($c, $addSubset = true)
3508	{
3509		if (!$this->usingCoreFont) {
3510			return $this->GetCharWidthNonCore($c, $addSubset);
3511		} else {
3512			return $this->GetCharWidthCore($c);
3513		}
3514	}
3515
3516	function GetStringWidth($s, $addSubset = true, $OTLdata = false, $textvar = 0, $includeKashida = false)
3517	{
3518	// mPDF 5.7.1
3519		// Get width of a string in the current font
3520		$s = (string) $s;
3521		$cw = &$this->CurrentFont['cw'];
3522		$w = 0;
3523		$kerning = 0;
3524		$lastchar = 0;
3525		$nb_carac = 0;
3526		$nb_spaces = 0;
3527		$kashida = 0;
3528		// mPDF ITERATION
3529		if ($this->iterationCounter) {
3530			$s = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $s);
3531		}
3532		if (!$this->usingCoreFont) {
3533			$discards = substr_count($s, "\xc2\xad"); // mPDF 6 soft hyphens [U+00AD]
3534			$unicode = $this->UTF8StringToArray($s, $addSubset);
3535			if ($this->minwSpacing || $this->fixedlSpacing) {
3536				$nb_spaces = mb_substr_count($s, ' ', $this->mb_enc);
3537				$nb_carac = count($unicode) - $discards; // mPDF 6
3538				// mPDF 5.7.1
3539				// Use GPOS OTL
3540				if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
3541					if (isset($OTLdata['group']) && $OTLdata['group']) {
3542						$nb_carac -= substr_count($OTLdata['group'], 'M');
3543					}
3544				}
3545			}
3546			/* -- CJK-FONTS -- */
3547			if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts
3548				foreach ($unicode as $char) {
3549					if ($char == 0x00AD) {
3550						continue;
3551					} // mPDF 6 soft hyphens [U+00AD]
3552					if (isset($cw[$char])) {
3553						$w+=$cw[$char];
3554					} elseif (isset($this->CurrentFont['MissingWidth'])) {
3555						$w += $this->CurrentFont['MissingWidth'];
3556					} else {
3557						$w += 500;
3558					}
3559				}
3560			} else {
3561				/* -- END CJK-FONTS -- */
3562				foreach ($unicode as $i => $char) {
3563					if ($char == 0x00AD) {
3564						continue;
3565					} // mPDF 6 soft hyphens [U+00AD]
3566					if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) {
3567						$charw = $this->_getCharWidth($cw, $this->upperCase[$char]);
3568						if ($charw !== false) {
3569							$charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
3570							$w+=$charw;
3571						} elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
3572							$w += $this->CurrentFont['desc']['MissingWidth'];
3573						} elseif (isset($this->CurrentFont['MissingWidth'])) {
3574							$w += $this->CurrentFont['MissingWidth'];
3575						} else {
3576							$w += 500;
3577						}
3578					} else {
3579						$charw = $this->_getCharWidth($cw, $char);
3580						if ($charw !== false) {
3581							$w+=$charw;
3582						} elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
3583							$w += $this->CurrentFont['desc']['MissingWidth'];
3584						} elseif (isset($this->CurrentFont['MissingWidth'])) {
3585							$w += $this->CurrentFont['MissingWidth'];
3586						} else {
3587							$w += 500;
3588						}
3589						// mPDF 5.7.1
3590						// Use GPOS OTL
3591						// ...GetStringWidth...
3592						if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata)) {
3593							if (isset($OTLdata['GPOSinfo'][$i]['wDir']) && $OTLdata['GPOSinfo'][$i]['wDir'] == 'RTL') {
3594								if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceR']) && $OTLdata['GPOSinfo'][$i]['XAdvanceR']) {
3595									$w += $OTLdata['GPOSinfo'][$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
3596								}
3597							} else {
3598								if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceL']) && $OTLdata['GPOSinfo'][$i]['XAdvanceL']) {
3599									$w += $OTLdata['GPOSinfo'][$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
3600								}
3601							}
3602							// Kashida from GPOS
3603							// Kashida is set as an absolute length value (already set as a proportion based on useKashida %)
3604							if ($includeKashida && isset($OTLdata['GPOSinfo'][$i]['kashida_space']) && $OTLdata['GPOSinfo'][$i]['kashida_space']) {
3605								$kashida += $OTLdata['GPOSinfo'][$i]['kashida_space'];
3606							}
3607						}
3608						if (($textvar & TextVars::FC_KERNING) && $lastchar) {
3609							if (isset($this->CurrentFont['kerninfo'][$lastchar][$char])) {
3610								$kerning += $this->CurrentFont['kerninfo'][$lastchar][$char];
3611							}
3612						}
3613						$lastchar = $char;
3614					}
3615				}
3616			} // *CJK-FONTS*
3617		} else {
3618			if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
3619				$s = str_replace(chr(173), '', $s);
3620			}
3621			$nb_carac = $l = strlen($s);
3622			if ($this->minwSpacing || $this->fixedlSpacing) {
3623				$nb_spaces = substr_count($s, ' ');
3624			}
3625			for ($i = 0; $i < $l; $i++) {
3626				if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($s[$i])])) {  // mPDF 5.7.1
3627					$charw = $cw[chr($this->upperCase[ord($s[$i])])];
3628					if ($charw !== false) {
3629						$charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
3630						$w+=$charw;
3631					}
3632				} elseif (isset($cw[$s[$i]])) {
3633					$w += $cw[$s[$i]];
3634				} elseif (isset($cw[ord($s[$i])])) {
3635					$w += $cw[ord($s[$i])];
3636				}
3637				if (($textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
3638					if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]])) {
3639						$kerning += $this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]];
3640					}
3641				}
3642			}
3643		}
3644		unset($cw);
3645		if ($textvar & TextVars::FC_KERNING) {
3646			$w += $kerning;
3647		} // mPDF 5.7.1
3648		$w *= ($this->FontSize / 1000);
3649		$w += (($nb_carac + $nb_spaces) * $this->fixedlSpacing) + ($nb_spaces * $this->minwSpacing);
3650		$w += $kashida / Mpdf::SCALE;
3651
3652		return ($w);
3653	}
3654
3655	function SetLineWidth($width)
3656	{
3657		// Set line width
3658		$this->LineWidth = $width;
3659		$lwout = (sprintf('%.3F w', $width * Mpdf::SCALE));
3660		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineWidth']) && $this->pageoutput[$this->page]['LineWidth'] != $lwout) || !isset($this->pageoutput[$this->page]['LineWidth']))) {
3661			$this->_out($lwout);
3662		}
3663		$this->pageoutput[$this->page]['LineWidth'] = $lwout;
3664	}
3665
3666	function Line($x1, $y1, $x2, $y2)
3667	{
3668		// Draw a line
3669		$this->_out(sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($this->h - $y2) * Mpdf::SCALE));
3670	}
3671
3672	function Arrow($x1, $y1, $x2, $y2, $headsize = 3, $fill = 'B', $angle = 25)
3673	{
3674		// F == fill // S == stroke // B == stroke and fill
3675		// angle = splay of arrowhead - 1 - 89 degrees
3676		if ($fill == 'F') {
3677			$fill = 'f';
3678		} elseif ($fill == 'FD' or $fill == 'DF' or $fill == 'B') {
3679			$fill = 'B';
3680		} else {
3681			$fill = 'S';
3682		}
3683		$a = atan2(($y2 - $y1), ($x2 - $x1));
3684		$b = $a + deg2rad($angle);
3685		$c = $a - deg2rad($angle);
3686		$x3 = $x2 - ($headsize * cos($b));
3687		$y3 = $this->h - ($y2 - ($headsize * sin($b)));
3688		$x4 = $x2 - ($headsize * cos($c));
3689		$y4 = $this->h - ($y2 - ($headsize * sin($c)));
3690
3691		$x5 = $x3 - ($x3 - $x4) / 2; // mid point of base of arrowhead - to join arrow line to
3692		$y5 = $y3 - ($y3 - $y4) / 2;
3693
3694		$s = '';
3695		$s.=sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE);
3696		$this->_out($s);
3697
3698		$s = '';
3699		$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);
3700		$s.=$fill;
3701		$this->_out($s);
3702	}
3703
3704	function Rect($x, $y, $w, $h, $style = '')
3705	{
3706		// Draw a rectangle
3707		if ($style == 'F') {
3708			$op = 'f';
3709		} elseif ($style == 'FD' or $style == 'DF') {
3710			$op = 'B';
3711		} else {
3712			$op = 'S';
3713		}
3714		$this->_out(sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$h * Mpdf::SCALE, $op));
3715	}
3716
3717	function AddFontDirectory($directory)
3718	{
3719		$this->fontDir[] = $directory;
3720		$this->fontFileFinder->setDirectories($this->fontDir);
3721	}
3722
3723	function AddFont($family, $style = '')
3724	{
3725		if (empty($family)) {
3726			return;
3727		}
3728
3729		$family = strtolower($family);
3730		$style = strtoupper($style);
3731		$style = str_replace('U', '', $style);
3732
3733		if ($style == 'IB') {
3734			$style = 'BI';
3735		}
3736
3737		$fontkey = $family . $style;
3738
3739		// check if the font has been already added
3740		if (isset($this->fonts[$fontkey])) {
3741			return;
3742		}
3743
3744		/* -- CJK-FONTS -- */
3745		if (in_array($family, $this->available_CJK_fonts)) {
3746			if (empty($this->Big5_widths)) {
3747				require __DIR__ . '/../data/CJKdata.php';
3748			}
3749			$this->AddCJKFont($family); // don't need to add style
3750			return;
3751		}
3752		/* -- END CJK-FONTS -- */
3753
3754		if ($this->usingCoreFont) {
3755			throw new \Mpdf\MpdfException("mPDF Error - problem with Font management");
3756		}
3757
3758		$stylekey = $style;
3759		if (!$style) {
3760			$stylekey = 'R';
3761		}
3762
3763		if (!isset($this->fontdata[$family][$stylekey]) || !$this->fontdata[$family][$stylekey]) {
3764			throw new \Mpdf\MpdfException(sprintf('Font "%s%s%s" is not supported', $family, $style ? ' - ' : '', $style));
3765		}
3766
3767		$name = '';
3768		$cw = '';
3769		$glyphIDtoUni = '';
3770		$originalsize = 0;
3771		$sip = false;
3772		$smp = false;
3773		$useOTL = 0; // mPDF 5.7.1
3774		$fontmetrics = ''; // mPDF 6
3775		$haskerninfo = false;
3776		$haskernGPOS = false;
3777		$hassmallcapsGSUB = false;
3778		$BMPselected = false;
3779		$GSUBScriptLang = [];
3780		$GSUBFeatures = [];
3781		$GSUBLookups = [];
3782		$GPOSScriptLang = [];
3783		$GPOSFeatures = [];
3784		$GPOSLookups = [];
3785
3786		if ($this->fontCache->has($fontkey . '.mtx.php')) {
3787			require $this->fontCache->tempFilename($fontkey . '.mtx.php');
3788		}
3789
3790		$ttffile = $this->fontFileFinder->findFontFile($this->fontdata[$family][$stylekey]);
3791		$ttfstat = stat($ttffile);
3792
3793		if (isset($this->fontdata[$family]['TTCfontID'][$stylekey])) {
3794			$TTCfontID = $this->fontdata[$family]['TTCfontID'][$stylekey];
3795		} else {
3796			$TTCfontID = 0;
3797		}
3798
3799		$fontUseOTL = isset($this->fontdata[$family]['useOTL']) ? $this->fontdata[$family]['useOTL'] : false;
3800
3801		$BMPonly = false;
3802		if (in_array($family, $this->BMPonly)) {
3803			$BMPonly = true;
3804		}
3805
3806		$regenerate = false;
3807		if ($BMPonly && !$BMPselected) {
3808			$regenerate = true;
3809		} elseif (!$BMPonly && $BMPselected) {
3810			$regenerate = true;
3811		}
3812
3813		// mPDF 5.7.1
3814		if ($fontUseOTL && $useOTL != $fontUseOTL) {
3815			$regenerate = true;
3816			$useOTL = $fontUseOTL;
3817		} elseif (!$fontUseOTL && $useOTL) {
3818			$regenerate = true;
3819			$useOTL = 0;
3820		}
3821
3822		if ($this->fontDescriptor != $fontmetrics) {
3823			$regenerate = true;
3824		} // mPDF 6
3825
3826		if (empty($name) || $originalsize != $ttfstat['size'] || $regenerate) {
3827			$generator = new MetricsGenerator($this->fontCache, $this->fontDescriptor);
3828
3829			$generator->generateMetrics(
3830				$ttffile,
3831				$ttfstat,
3832				$fontkey,
3833				$TTCfontID,
3834				$this->debugfonts,
3835				$BMPonly,
3836				$useOTL,
3837				$fontUseOTL
3838			);
3839
3840			require $this->fontCache->tempFilename($fontkey . '.mtx.php');
3841			$cw = $this->fontCache->load($fontkey . '.cw.dat');
3842			$glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat');
3843		} else {
3844			if ($this->fontCache->has($fontkey . '.cw.dat')) {
3845				$cw = $this->fontCache->load($fontkey . '.cw.dat');
3846			}
3847
3848			if ($this->fontCache->has($fontkey . '.gid.dat')) {
3849				$glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat');
3850			}
3851		}
3852
3853		if (isset($this->fontdata[$family]['sip-ext']) && $this->fontdata[$family]['sip-ext']) {
3854			$sipext = $this->fontdata[$family]['sip-ext'];
3855		} else {
3856			$sipext = '';
3857		}
3858
3859		// Override with values from config_font.php
3860		if (isset($this->fontdata[$family]['Ascent']) && $this->fontdata[$family]['Ascent']) {
3861			$desc['Ascent'] = $this->fontdata[$family]['Ascent'];
3862		}
3863		if (isset($this->fontdata[$family]['Descent']) && $this->fontdata[$family]['Descent']) {
3864			$desc['Descent'] = $this->fontdata[$family]['Descent'];
3865		}
3866		if (isset($this->fontdata[$family]['Leading']) && $this->fontdata[$family]['Leading']) {
3867			$desc['Leading'] = $this->fontdata[$family]['Leading'];
3868		}
3869
3870		$i = count($this->fonts) + $this->extraFontSubsets + 1;
3871		if ($sip || $smp) {
3872			$this->fonts[$fontkey] = [
3873				'i' => $i,
3874				'type' => $type,
3875				'name' => $name,
3876				'desc' => $desc,
3877				'panose' => $panose,
3878				'unitsPerEm' => $unitsPerEm,
3879				'up' => $up,
3880				'ut' => $ut,
3881				'strs' => $strs,
3882				'strp' => $strp,
3883				'cw' => $cw,
3884				'ttffile' => $ttffile,
3885				'fontkey' => $fontkey,
3886				'subsets' => [0 => range(0, 127)],
3887				'subsetfontids' => [$i],
3888				'used' => false,
3889				'sip' => $sip,
3890				'sipext' => $sipext,
3891				'smp' => $smp,
3892				'TTCfontID' => $TTCfontID,
3893				'useOTL' => $fontUseOTL,
3894				'useKashida' => (isset($this->fontdata[$family]['useKashida']) ? $this->fontdata[$family]['useKashida'] : false),
3895				'GSUBScriptLang' => $GSUBScriptLang,
3896				'GSUBFeatures' => $GSUBFeatures,
3897				'GSUBLookups' => $GSUBLookups,
3898				'GPOSScriptLang' => $GPOSScriptLang,
3899				'GPOSFeatures' => $GPOSFeatures,
3900				'GPOSLookups' => $GPOSLookups,
3901				'rtlPUAstr' => $rtlPUAstr,
3902				'glyphIDtoUni' => $glyphIDtoUni,
3903				'haskerninfo' => $haskerninfo,
3904				'haskernGPOS' => $haskernGPOS,
3905				'hassmallcapsGSUB' => $hassmallcapsGSUB]; // mPDF 5.7.1	// mPDF 6
3906		} else {
3907			$ss = [];
3908			for ($s = 32; $s < 128; $s++) {
3909				$ss[$s] = $s;
3910			}
3911			$this->fonts[$fontkey] = [
3912				'i' => $i,
3913				'type' => $type,
3914				'name' => $name,
3915				'desc' => $desc,
3916				'panose' => $panose,
3917				'unitsPerEm' => $unitsPerEm,
3918				'up' => $up,
3919				'ut' => $ut,
3920				'strs' => $strs,
3921				'strp' => $strp,
3922				'cw' => $cw,
3923				'ttffile' => $ttffile,
3924				'fontkey' => $fontkey,
3925				'subset' => $ss,
3926				'used' => false,
3927				'sip' => $sip,
3928				'sipext' => $sipext,
3929				'smp' => $smp,
3930				'TTCfontID' => $TTCfontID,
3931				'useOTL' => $fontUseOTL,
3932				'useKashida' => (isset($this->fontdata[$family]['useKashida']) ? $this->fontdata[$family]['useKashida'] : false),
3933				'GSUBScriptLang' => $GSUBScriptLang,
3934				'GSUBFeatures' => $GSUBFeatures,
3935				'GSUBLookups' => $GSUBLookups,
3936				'GPOSScriptLang' => $GPOSScriptLang,
3937				'GPOSFeatures' => $GPOSFeatures,
3938				'GPOSLookups' => $GPOSLookups,
3939				'rtlPUAstr' => $rtlPUAstr,
3940				'glyphIDtoUni' => $glyphIDtoUni,
3941				'haskerninfo' => $haskerninfo,
3942				'haskernGPOS' => $haskernGPOS,
3943				'hassmallcapsGSUB' => $hassmallcapsGSUB
3944			];
3945		}
3946
3947		if ($haskerninfo) {
3948			$this->fonts[$fontkey]['kerninfo'] = $kerninfo;
3949		}
3950
3951		$this->FontFiles[$fontkey] = [
3952			'length1' => $originalsize,
3953			'type' => 'TTF',
3954			'ttffile' => $ttffile,
3955			'sip' => $sip,
3956			'smp' => $smp
3957		];
3958
3959		unset($cw);
3960	}
3961
3962	function SetFont($family, $style = '', $size = 0, $write = true, $forcewrite = false)
3963	{
3964		$family = strtolower($family);
3965
3966		if (!$this->onlyCoreFonts) {
3967			if ($family == 'sans' || $family == 'sans-serif') {
3968				$family = $this->sans_fonts[0];
3969			}
3970			if ($family == 'serif') {
3971				$family = $this->serif_fonts[0];
3972			}
3973			if ($family == 'mono' || $family == 'monospace') {
3974				$family = $this->mono_fonts[0];
3975			}
3976		}
3977
3978		if (isset($this->fonttrans[$family]) && $this->fonttrans[$family]) {
3979			$family = $this->fonttrans[$family];
3980		}
3981
3982		if ($family == '') {
3983			if ($this->FontFamily) {
3984				$family = $this->FontFamily;
3985			} elseif ($this->default_font) {
3986				$family = $this->default_font;
3987			} else {
3988				throw new \Mpdf\MpdfException("No font or default font set!");
3989			}
3990		}
3991
3992		$this->ReqFontStyle = $style; // required or requested style - used later for artificial bold/italic
3993
3994		if (($family == 'csymbol') || ($family == 'czapfdingbats') || ($family == 'ctimes') || ($family == 'ccourier') || ($family == 'chelvetica')) {
3995			if ($this->PDFA || $this->PDFX) {
3996				if ($family == 'csymbol' || $family == 'czapfdingbats') {
3997					throw new \Mpdf\MpdfException("Symbol and Zapfdingbats cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a).");
3998				}
3999				if ($family == 'ctimes' || $family == 'ccourier' || $family == 'chelvetica') {
4000					if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
4001						$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.)";
4002					}
4003					if ($family == 'chelvetica') {
4004						$family = 'sans';
4005					}
4006					if ($family == 'ctimes') {
4007						$family = 'serif';
4008					}
4009					if ($family == 'ccourier') {
4010						$family = 'mono';
4011					}
4012				}
4013				$this->usingCoreFont = false;
4014			} else {
4015				$this->usingCoreFont = true;
4016			}
4017			if ($family == 'csymbol' || $family == 'czapfdingbats') {
4018				$style = '';
4019			}
4020		} else {
4021			$this->usingCoreFont = false;
4022		}
4023
4024		// mPDF 5.7.1
4025		if ($style) {
4026			$style = strtoupper($style);
4027			if ($style == 'IB') {
4028				$style = 'BI';
4029			}
4030		}
4031		if ($size == 0) {
4032			$size = $this->FontSizePt;
4033		}
4034
4035		$fontkey = $family . $style;
4036
4037		$stylekey = $style;
4038		if (!$stylekey) {
4039			$stylekey = "R";
4040		}
4041
4042		if (!$this->onlyCoreFonts && !$this->usingCoreFont) {
4043			if (!isset($this->fonts[$fontkey]) || count($this->default_available_fonts) != count($this->available_unifonts)) { // not already added
4044
4045				/* -- CJK-FONTS -- */
4046				if (in_array($fontkey, $this->available_CJK_fonts)) {
4047					if (!isset($this->fonts[$fontkey])) { // already added
4048						if (empty($this->Big5_widths)) {
4049							require __DIR__ . '/../data/CJKdata.php';
4050						}
4051						$this->AddCJKFont($family); // don't need to add style
4052					}
4053				} else { // Test to see if requested font/style is available - or substitute /* -- END CJK-FONTS -- */
4054					if (!in_array($fontkey, $this->available_unifonts)) {
4055						// If font[nostyle] exists - set it
4056						if (in_array($family, $this->available_unifonts)) {
4057							$style = '';
4058						} // elseif only one font available - set it (assumes if only one font available it will not have a style)
4059						elseif (count($this->available_unifonts) == 1) {
4060							$family = $this->available_unifonts[0];
4061							$style = '';
4062						} else {
4063							$found = 0;
4064							// else substitute font of similar type
4065							if (in_array($family, $this->sans_fonts)) {
4066								$i = array_intersect($this->sans_fonts, $this->available_unifonts);
4067								if (count($i)) {
4068									$i = array_values($i);
4069									// with requested style if possible
4070									if (!in_array(($i[0] . $style), $this->available_unifonts)) {
4071										$style = '';
4072									}
4073									$family = $i[0];
4074									$found = 1;
4075								}
4076							} elseif (in_array($family, $this->serif_fonts)) {
4077								$i = array_intersect($this->serif_fonts, $this->available_unifonts);
4078								if (count($i)) {
4079									$i = array_values($i);
4080									// with requested style if possible
4081									if (!in_array(($i[0] . $style), $this->available_unifonts)) {
4082										$style = '';
4083									}
4084									$family = $i[0];
4085									$found = 1;
4086								}
4087							} elseif (in_array($family, $this->mono_fonts)) {
4088								$i = array_intersect($this->mono_fonts, $this->available_unifonts);
4089								if (count($i)) {
4090									$i = array_values($i);
4091									// with requested style if possible
4092									if (!in_array(($i[0] . $style), $this->available_unifonts)) {
4093										$style = '';
4094									}
4095									$family = $i[0];
4096									$found = 1;
4097								}
4098							}
4099
4100							if (!$found) {
4101								// set first available font
4102								$fs = $this->available_unifonts[0];
4103								preg_match('/^([a-z_0-9\-]+)([BI]{0,2})$/', $fs, $fas); // Allow "-"
4104								// with requested style if possible
4105								$ws = $fas[1] . $style;
4106								if (in_array($ws, $this->available_unifonts)) {
4107									$family = $fas[1]; // leave $style as is
4108								} elseif (in_array($fas[1], $this->available_unifonts)) {
4109									// or without style
4110									$family = $fas[1];
4111									$style = '';
4112								} else {
4113									// or with the style specified
4114									$family = $fas[1];
4115									$style = $fas[2];
4116								}
4117							}
4118						}
4119						$fontkey = $family . $style;
4120					}
4121				}
4122			}
4123
4124			// try to add font (if not already added)
4125			$this->AddFont($family, $style);
4126
4127			// Test if font is already selected
4128			if ($this->FontFamily == $family && $this->FontFamily == $this->currentfontfamily && $this->FontStyle == $style && $this->FontStyle == $this->currentfontstyle && $this->FontSizePt == $size && $this->FontSizePt == $this->currentfontsize && !$forcewrite) {
4129				return $family;
4130			}
4131
4132			$fontkey = $family . $style;
4133
4134			// Select it
4135			$this->FontFamily = $family;
4136			$this->FontStyle = $style;
4137			$this->FontSizePt = $size;
4138			$this->FontSize = $size / Mpdf::SCALE;
4139			$this->CurrentFont = &$this->fonts[$fontkey];
4140			if ($write) {
4141				$fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4142				if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
4143					$this->_out($fontout);
4144				}
4145				$this->pageoutput[$this->page]['Font'] = $fontout;
4146			}
4147
4148			// Added - currentfont (lowercase) used in HTML2PDF
4149			$this->currentfontfamily = $family;
4150			$this->currentfontsize = $size;
4151			$this->currentfontstyle = $style;
4152			$this->setMBencoding('UTF-8');
4153		} else {  // if using core fonts
4154			if ($this->PDFA || $this->PDFX) {
4155				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.');
4156			}
4157			$this->setMBencoding('windows-1252');
4158
4159			// Test if font is already selected
4160			if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) {
4161				return $family;
4162			}
4163
4164			if (!isset($this->CoreFonts[$fontkey])) {
4165				if (in_array($family, $this->serif_fonts)) {
4166					$family = 'ctimes';
4167				} elseif (in_array($family, $this->mono_fonts)) {
4168					$family = 'ccourier';
4169				} else {
4170					$family = 'chelvetica';
4171				}
4172				$this->usingCoreFont = true;
4173				$fontkey = $family . $style;
4174			}
4175
4176			if (!isset($this->fonts[$fontkey])) {
4177				// STANDARD CORE FONTS
4178				if (isset($this->CoreFonts[$fontkey])) {
4179					// Load metric file
4180					$file = $family;
4181					if ($family == 'ctimes' || $family == 'chelvetica' || $family == 'ccourier') {
4182						$file .= strtolower($style);
4183					}
4184					require __DIR__ . '/../data/font/' . $file . '.php';
4185					if (!isset($cw)) {
4186						throw new \Mpdf\MpdfException(sprintf('Could not include font metric file "%s"', $file));
4187					}
4188					$i = count($this->fonts) + $this->extraFontSubsets + 1;
4189					$this->fonts[$fontkey] = ['i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw];
4190					if ($this->useKerning && isset($kerninfo)) {
4191						$this->fonts[$fontkey]['kerninfo'] = $kerninfo;
4192					}
4193				} else {
4194					throw new \Mpdf\MpdfException(sprintf('Font %s not defined', $fontkey));
4195				}
4196			}
4197
4198			// Test if font is already selected
4199			if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) {
4200				return $family;
4201			}
4202			// Select it
4203			$this->FontFamily = $family;
4204			$this->FontStyle = $style;
4205			$this->FontSizePt = $size;
4206			$this->FontSize = $size / Mpdf::SCALE;
4207			$this->CurrentFont = &$this->fonts[$fontkey];
4208			if ($write) {
4209				$fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4210				if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
4211					$this->_out($fontout);
4212				}
4213				$this->pageoutput[$this->page]['Font'] = $fontout;
4214			}
4215			// Added - currentfont (lowercase) used in HTML2PDF
4216			$this->currentfontfamily = $family;
4217			$this->currentfontsize = $size;
4218			$this->currentfontstyle = $style;
4219		}
4220
4221		return $family;
4222	}
4223
4224	function SetFontSize($size, $write = true)
4225	{
4226		// Set font size in points
4227		if ($this->FontSizePt == $size) {
4228			return;
4229		}
4230		$this->FontSizePt = $size;
4231		$this->FontSize = $size / Mpdf::SCALE;
4232		$this->currentfontsize = $size;
4233		if ($write) {
4234			$fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4235			// Edited mPDF 3.0
4236			if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
4237				$this->_out($fontout);
4238			}
4239			$this->pageoutput[$this->page]['Font'] = $fontout;
4240		}
4241	}
4242
4243	function AddLink()
4244	{
4245		// Create a new internal link
4246		$n = count($this->links) + 1;
4247		$this->links[$n] = [0, 0];
4248		return $n;
4249	}
4250
4251	function SetLink($link, $y = 0, $page = -1)
4252	{
4253		// Set destination of internal link
4254		if ($y == -1) {
4255			$y = $this->y;
4256		}
4257		if ($page == -1) {
4258			$page = $this->page;
4259		}
4260		$this->links[$link] = [$page, $y];
4261	}
4262
4263	function Link($x, $y, $w, $h, $link)
4264	{
4265		$l = [$x * Mpdf::SCALE, $this->hPt - $y * Mpdf::SCALE, $w * Mpdf::SCALE, $h * Mpdf::SCALE, $link];
4266		if ($this->keep_block_together) { // don't write yet
4267			return;
4268		} elseif ($this->table_rotate) { // *TABLES*
4269			$this->tbrot_Links[$this->page][] = $l; // *TABLES*
4270			return; // *TABLES*
4271		} // *TABLES*
4272		elseif ($this->kwt) {
4273			$this->kwt_Links[$this->page][] = $l;
4274			return;
4275		}
4276
4277		if ($this->writingHTMLheader || $this->writingHTMLfooter) {
4278			$this->HTMLheaderPageLinks[] = $l;
4279			return;
4280		}
4281		// Put a link on the page
4282		$this->PageLinks[$this->page][] = $l;
4283		// Save cross-reference to Column buffer
4284		$ref = count($this->PageLinks[$this->page]) - 1; // *COLUMNS*
4285		$this->columnLinks[$this->CurrCol][(int) $this->x][(int) $this->y] = $ref; // *COLUMNS*
4286	}
4287
4288	function Text($x, $y, $txt, $OTLdata = [], $textvar = 0, $aixextra = '', $coordsys = '', $return = false)
4289	{
4290		// Output (or return) a string
4291		// Called (internally) by Watermark() & _tableWrite() [rotated cells] & TableHeaderFooter() & WriteText()
4292		// Called also from classes/svg.php
4293		// Expects Font to be set
4294		// Expects input to be mb_encoded if necessary and RTL reversed & OTL processed
4295		// ARTIFICIAL BOLD AND ITALIC
4296		$s = 'q ';
4297		if ($this->falseBoldWeight && strpos($this->ReqFontStyle, "B") !== false && strpos($this->FontStyle, "B") === false) {
4298			$s .= '2 Tr 1 J 1 j ';
4299			$s .= sprintf('%.3F w ', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight);
4300			$tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
4301			if ($this->FillColor != $tc) {
4302				$s .= $tc . ' ';
4303			}  // stroke (outline) = same colour as text(fill)
4304		}
4305		if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) {
4306			$aix = '1 0 0.261799 1 %.3F %.3F Tm';
4307		} else {
4308			$aix = '%.3F %.3F Td';
4309		}
4310
4311		$aix = $aixextra . $aix;
4312
4313		if ($this->ColorFlag) {
4314			$s.=$this->TextColor . ' ';
4315		}
4316
4317		$this->CurrentFont['used'] = true;
4318
4319		if ($this->usingCoreFont) {
4320			$txt2 = str_replace(chr(160), chr(32), $txt);
4321		} else {
4322			$txt2 = str_replace(chr(194) . chr(160), chr(32), $txt);
4323		}
4324
4325		$px = $x;
4326		$py = $y;
4327		if ($coordsys != 'SVG') {
4328			$px = $x * Mpdf::SCALE;
4329			$py = ($this->h - $y) * Mpdf::SCALE;
4330		}
4331
4332
4333		/** ************** SIMILAR TO Cell() ************************ */
4334
4335		// IF corefonts AND NOT SmCaps AND NOT Kerning
4336		// Just output text
4337		if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) {
4338			$txt2 = $this->_escape($txt2);
4339			$s .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
4340		} // IF NOT corefonts [AND NO wordspacing] AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL
4341		// Just output text
4342		elseif (!$this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {
4343			// IF SIP/SMP
4344			if ($this->CurrentFont['sip'] || $this->CurrentFont['smp']) {
4345				$txt2 = $this->UTF8toSubset($txt2);
4346				$s .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);
4347			} // NOT SIP/SMP
4348			else {
4349				$txt2 = $this->UTF8ToUTF16BE($txt2, false);
4350				$txt2 = $this->_escape($txt2);
4351				$s .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
4352			}
4353		} // IF NOT corefonts [AND IS wordspacing] AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL
4354		// Not required here (cf. Cell() )
4355		// ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]
4356		else {
4357			$s .= $this->applyGPOSpdf($txt2, $aix, $px, $py, $OTLdata, $textvar);
4358		}
4359		/*         * ************** END ************************ */
4360
4361		$s .= ' ';
4362
4363		if (($textvar & TextVars::FD_UNDERLINE) && $txt != '') { // mPDF 5.7.1
4364			$c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
4365			if ($this->FillColor != $c) {
4366				$s.= ' ' . $c . ' ';
4367			}
4368			if (isset($this->CurrentFont['up']) && $this->CurrentFont['up']) {
4369				$up = $this->CurrentFont['up'];
4370			} else {
4371				$up = -100;
4372			}
4373			$adjusty = (-$up / 1000 * $this->FontSize);
4374			if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {
4375				$ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;
4376			} else {
4377				$ut = 60 / 1000 * $this->FontSize;
4378			}
4379			$olw = $this->LineWidth;
4380			$s.=' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE));
4381			$s.=' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);
4382			$s.=' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE));
4383			if ($this->FillColor != $c) {
4384				$s.= ' ' . $this->FillColor . ' ';
4385			}
4386		}
4387		// STRIKETHROUGH
4388		if (($textvar & TextVars::FD_LINETHROUGH) && $txt != '') { // mPDF 5.7.1
4389			$c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
4390			if ($this->FillColor != $c) {
4391				$s.= ' ' . $c . ' ';
4392			}
4393			// Superscript and Subscript Y coordinate adjustment (now for striked-through texts)
4394			if (isset($this->CurrentFont['desc']['CapHeight']) && $this->CurrentFont['desc']['CapHeight']) {
4395				$ch = $this->CurrentFont['desc']['CapHeight'];
4396			} else {
4397				$ch = 700;
4398			}
4399			$adjusty = (-$ch / 1000 * $this->FontSize) * 0.35;
4400			if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {
4401				$ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;
4402			} else {
4403				$ut = 60 / 1000 * $this->FontSize;
4404			}
4405			$olw = $this->LineWidth;
4406			$s.=' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE));
4407			$s.=' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);
4408			$s.=' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE));
4409			if ($this->FillColor != $c) {
4410				$s.= ' ' . $this->FillColor . ' ';
4411			}
4412		}
4413		$s .= 'Q';
4414
4415		if ($return) {
4416			return $s . " \n";
4417		}
4418		$this->_out($s);
4419	}
4420
4421	/* -- DIRECTW -- */
4422
4423	function WriteText($x, $y, $txt)
4424	{
4425		// Output a string using Text() but does encoding and text reversing of RTL
4426		$txt = $this->purify_utf8_text($txt);
4427		if ($this->text_input_as_HTML) {
4428			$txt = $this->all_entities_to_utf8($txt);
4429		}
4430		if ($this->usingCoreFont) {
4431			$txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
4432		}
4433
4434		// DIRECTIONALITY
4435		if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
4436			$this->biDirectional = true;
4437		} // *OTL*
4438
4439		$textvar = 0;
4440		$save_OTLtags = $this->OTLtags;
4441		$this->OTLtags = [];
4442		if ($this->useKerning) {
4443			if ($this->CurrentFont['haskernGPOS']) {
4444				$this->OTLtags['Plus'] .= ' kern';
4445			} else {
4446				$textvar = ($textvar | TextVars::FC_KERNING);
4447			}
4448		}
4449
4450		/* -- OTL -- */
4451		// Use OTL OpenType Table Layout - GSUB & GPOS
4452		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
4453			$txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
4454			$OTLdata = $this->otl->OTLdata;
4455		}
4456		/* -- END OTL -- */
4457		$this->OTLtags = $save_OTLtags;
4458
4459		$this->magic_reverse_dir($txt, $this->directionality, $OTLdata);
4460
4461		$this->Text($x, $y, $txt, $OTLdata, $textvar);
4462	}
4463
4464	function WriteCell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0)
4465	{
4466		// Output a cell using Cell() but does encoding and text reversing of RTL
4467		$txt = $this->purify_utf8_text($txt);
4468		if ($this->text_input_as_HTML) {
4469			$txt = $this->all_entities_to_utf8($txt);
4470		}
4471		if ($this->usingCoreFont) {
4472			$txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
4473		}
4474		// DIRECTIONALITY
4475		if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
4476			$this->biDirectional = true;
4477		} // *OTL*
4478
4479		$textvar = 0;
4480		$save_OTLtags = $this->OTLtags;
4481		$this->OTLtags = [];
4482		if ($this->useKerning) {
4483			if ($this->CurrentFont['haskernGPOS']) {
4484				$this->OTLtags['Plus'] .= ' kern';
4485			} else {
4486				$textvar = ($textvar | TextVars::FC_KERNING);
4487			}
4488		}
4489
4490		/* -- OTL -- */
4491		// Use OTL OpenType Table Layout - GSUB & GPOS
4492		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
4493			$txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
4494			$OTLdata = $this->otl->OTLdata;
4495		}
4496		/* -- END OTL -- */
4497		$this->OTLtags = $save_OTLtags;
4498
4499		$this->magic_reverse_dir($txt, $this->directionality, $OTLdata);
4500
4501		$this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link, $currentx, 0, 0, 'M', 0, false, $OTLdata, $textvar);
4502	}
4503
4504	/* -- END DIRECTW -- */
4505
4506	function ResetSpacing()
4507	{
4508		if ($this->ws != 0) {
4509			$this->_out('BT 0 Tw ET');
4510		}
4511		$this->ws = 0;
4512		if ($this->charspacing != 0) {
4513			$this->_out('BT 0 Tc ET');
4514		}
4515		$this->charspacing = 0;
4516	}
4517
4518	function SetSpacing($cs, $ws)
4519	{
4520		if (intval($cs * 1000) == 0) {
4521			$cs = 0;
4522		}
4523		if ($cs) {
4524			$this->_out(sprintf('BT %.3F Tc ET', $cs));
4525		} elseif ($this->charspacing != 0) {
4526			$this->_out('BT 0 Tc ET');
4527		}
4528		$this->charspacing = $cs;
4529		if (intval($ws * 1000) == 0) {
4530			$ws = 0;
4531		}
4532		if ($ws) {
4533			$this->_out(sprintf('BT %.3F Tw ET', $ws));
4534		} elseif ($this->ws != 0) {
4535			$this->_out('BT 0 Tw ET');
4536		}
4537		$this->ws = $ws;
4538	}
4539
4540	// WORD SPACING
4541	function GetJspacing($nc, $ns, $w, $inclCursive, &$cOTLdata)
4542	{
4543		$kashida_present = false;
4544		$kashida_space = 0;
4545		if ($w > 0 && $inclCursive && isset($this->CurrentFont['useKashida']) && $this->CurrentFont['useKashida'] && !empty($cOTLdata)) {
4546			for ($c = 0; $c < count($cOTLdata); $c++) {
4547				for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
4548					if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
4549						$kashida_present = true;
4550						break 2;
4551					}
4552				}
4553			}
4554		}
4555
4556		if ($kashida_present) {
4557			$k_ctr = 0;  // Number of kashida points
4558			$k_total = 0;  // Total of kashida values (priority)
4559			// Reset word
4560			$max_kashida_in_word = 0;
4561			$last_kashida_in_word = -1;
4562
4563			for ($c = 0; $c < count($cOTLdata); $c++) {
4564				for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
4565					if ($cOTLdata[$c]['group']{$i} == 'S') {
4566						// Save from last word
4567						if ($max_kashida_in_word) {
4568							$k_ctr++;
4569							$k_total = $max_kashida_in_word;
4570						}
4571						// Reset word
4572						$max_kashida_in_word = 0;
4573						$last_kashida_in_word = -1;
4574					}
4575
4576					if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
4577						if ($max_kashida_in_word) {
4578							if ($cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > $max_kashida_in_word) {
4579								$max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];
4580								$cOTLdata[$c]['GPOSinfo'][$last_kashida_in_word]['kashida'] = 0;
4581								$last_kashida_in_word = $i;
4582							} else {
4583								$cOTLdata[$c]['GPOSinfo'][$i]['kashida'] = 0;
4584							}
4585						} else {
4586							$max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];
4587							$last_kashida_in_word = $i;
4588						}
4589					}
4590				}
4591			}
4592			// Save from last word
4593			if ($max_kashida_in_word) {
4594				$k_ctr++;
4595				$k_total = $max_kashida_in_word;
4596			}
4597
4598			// Number of kashida points = $k_ctr
4599			// $useKashida is a % value from CurrentFont/config_fonts.php
4600			// % ratio divided between word-spacing and kashida-spacing
4601			$kashida_space_ratio = intval($this->CurrentFont['useKashida']) / 100;
4602
4603
4604			$kashida_space = $w * $kashida_space_ratio;
4605
4606			$tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);
4607			// Only use kashida if each allocated kashida width is > 0.01 x width of a tatweel
4608			// Otherwise fontstretch is too small and errors
4609			// If not just leave to adjust word-spacing
4610			if ($tatw && (($kashida_space / $k_ctr) / $tatw) > 0.01) {
4611				for ($c = 0; $c < count($cOTLdata); $c++) {
4612					for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
4613						if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
4614							// At this point kashida is a number representing priority (higher number - higher priority)
4615							// We are now going to set it as an actual length
4616							// This shares it equally amongst words:
4617							$cOTLdata[$c]['GPOSinfo'][$i]['kashida_space'] = (1 / $k_ctr) * $kashida_space;
4618						}
4619					}
4620				}
4621				$w -= $kashida_space;
4622			}
4623		}
4624
4625		$ws = 0;
4626		$charspacing = 0;
4627		$ww = $this->jSWord;
4628		$ncx = $nc - 1;
4629		if ($nc == 0) {
4630			return [0, 0, 0];
4631		} // Only word spacing allowed / possible
4632		elseif ($this->fixedlSpacing !== false || $inclCursive) {
4633			if ($ns) {
4634				$ws = $w / $ns;
4635			}
4636		} elseif ($nc == 1) {
4637			$charspacing = $w;
4638		} elseif (!$ns) {
4639			$charspacing = $w / ($ncx );
4640			if (($this->jSmaxChar > 0) && ($charspacing > $this->jSmaxChar)) {
4641				$charspacing = $this->jSmaxChar;
4642			}
4643		} elseif ($ns == ($ncx )) {
4644			$charspacing = $w / $ns;
4645		} else {
4646			if ($this->usingCoreFont) {
4647				$cs = ($w * (1 - $this->jSWord)) / ($ncx );
4648				if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {
4649					$cs = $this->jSmaxChar;
4650					$ww = 1 - (($cs * ($ncx )) / $w);
4651				}
4652				$charspacing = $cs;
4653				$ws = ($w * ($ww) ) / $ns;
4654			} else {
4655				$cs = ($w * (1 - $this->jSWord)) / ($ncx - $ns);
4656				if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {
4657					$cs = $this->jSmaxChar;
4658					$ww = 1 - (($cs * ($ncx - $ns)) / $w);
4659				}
4660				$charspacing = $cs;
4661				$ws = (($w * ($ww) ) / $ns) - $charspacing;
4662			}
4663		}
4664		return [$charspacing, $ws, $kashida_space];
4665	}
4666
4667	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)
4668	{
4669	// mPDF 5.7.1
4670		// Output a cell
4671		// Expects input to be mb_encoded if necessary and RTL reversed
4672		// NON_BREAKING SPACE
4673		if ($this->usingCoreFont) {
4674			$txt = str_replace(chr(160), chr(32), $txt);
4675		} else {
4676			$txt = str_replace(chr(194) . chr(160), chr(32), $txt);
4677		}
4678
4679		$oldcolumn = $this->CurrCol;
4680		// Automatic page break
4681		// Allows PAGE-BREAK-AFTER = avoid to work
4682		if (isset($this->blk[$this->blklvl])) {
4683			$bottom = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['margin_bottom'];
4684		} else {
4685			$bottom = 0;
4686		}
4687		if (!$this->tableLevel && (($this->y + $this->divheight > $this->PageBreakTrigger) || ($this->y + $h > $this->PageBreakTrigger) ||
4688			($this->y + ($h * 2) + $bottom > $this->PageBreakTrigger && $this->blk[$this->blklvl]['page_break_after_avoid'])) and ! $this->InFooter and $this->AcceptPageBreak()) { // mPDF 5.7.2
4689			$x = $this->x; // Current X position
4690			// WORD SPACING
4691			$ws = $this->ws; // Word Spacing
4692			$charspacing = $this->charspacing; // Character Spacing
4693			$this->ResetSpacing();
4694
4695			$this->AddPage($this->CurOrientation);
4696			// Added to correct for OddEven Margins
4697			$x += $this->MarginCorrection;
4698			if ($currentx) {
4699				$currentx += $this->MarginCorrection;
4700			}
4701			$this->x = $x;
4702			// WORD SPACING
4703			$this->SetSpacing($charspacing, $ws);
4704		}
4705
4706		// Test: to put line through centre of cell: $this->Line($this->x,$this->y+($h/2),$this->x+50,$this->y+($h/2));
4707		// Test: to put border around cell as it is specified: $border='LRTB';
4708
4709
4710		/* -- COLUMNS -- */
4711		// COLS
4712		// COLUMN CHANGE
4713		if ($this->CurrCol != $oldcolumn) {
4714			if ($currentx) {
4715				$currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
4716			}
4717			$this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
4718		}
4719
4720		// COLUMNS Update/overwrite the lowest bottom of printing y value for a column
4721		if ($this->ColActive) {
4722			if ($h) {
4723				$this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
4724			} else {
4725				$this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $this->divheight;
4726			}
4727		}
4728		/* -- END COLUMNS -- */
4729
4730
4731		if ($w == 0) {
4732			$w = $this->w - $this->rMargin - $this->x;
4733		}
4734		$s = '';
4735		if ($fill == 1 && $this->FillColor) {
4736			if ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor'])) {
4737				$s .= $this->FillColor . ' ';
4738			}
4739			$this->pageoutput[$this->page]['FillColor'] = $this->FillColor;
4740		}
4741
4742
4743		if ($lineBox && isset($lineBox['boxtop']) && $txt) { // i.e. always from WriteFlowingBlock/finishFlowingBlock (but not objects -
4744			// which only have $lineBox['top'] set)
4745			$boxtop = $this->y + $lineBox['boxtop'];
4746			$boxbottom = $this->y + $lineBox['boxbottom'];
4747			$glyphYorigin = $lineBox['glyphYorigin'];
4748			$baseline_shift = $lineBox['baseline-shift'];
4749			$bord_boxtop = $bg_boxtop = $boxtop = $boxtop - $baseline_shift;
4750			$bord_boxbottom = $bg_boxbottom = $boxbottom = $boxbottom - $baseline_shift;
4751			$bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;
4752
4753			// If inline element BACKGROUND has bounding box set by parent element:
4754			if (isset($lineBox['background-boxtop'])) {
4755				$bg_boxtop = $this->y + $lineBox['background-boxtop'] - $lineBox['background-baseline-shift'];
4756				$bg_boxbottom = $this->y + $lineBox['background-boxbottom'] - $lineBox['background-baseline-shift'];
4757				$bg_boxheight = $bg_boxbottom - $bg_boxtop;
4758			}
4759			// If inline element BORDER has bounding box set by parent element:
4760			if (isset($lineBox['border-boxtop'])) {
4761				$bord_boxtop = $this->y + $lineBox['border-boxtop'] - $lineBox['border-baseline-shift'];
4762				$bord_boxbottom = $this->y + $lineBox['border-boxbottom'] - $lineBox['border-baseline-shift'];
4763				$bord_boxheight = $bord_boxbottom - $bord_boxtop;
4764			}
4765		} else {
4766			$boxtop = $this->y;
4767			$boxheight = $h;
4768			$boxbottom = $this->y + $h;
4769			$baseline_shift = 0;
4770			if ($txt != '') {
4771				// FONT SIZE - this determines the baseline caculation
4772				$bfs = $this->FontSize;
4773				// Calculate baseline Superscript and Subscript Y coordinate adjustment
4774				$bfx = $this->baselineC;
4775				$baseline = $bfx * $bfs;
4776
4777				if ($textvar & TextVars::FA_SUPERSCRIPT) {
4778					$baseline_shift = $this->textparam['text-baseline'];
4779				} // mPDF 5.7.1	// mPDF 6
4780				elseif ($textvar & TextVars::FA_SUBSCRIPT) {
4781					$baseline_shift = $this->textparam['text-baseline'];
4782				} // mPDF 5.7.1	// mPDF 6
4783				elseif ($this->bullet) {
4784					$baseline += ($bfx - 0.7) * $this->FontSize;
4785				}
4786
4787				// Vertical align (for Images)
4788				if ($valign == 'T') {
4789					$va = (0.5 * $bfs * $this->normalLineheight);
4790				} elseif ($valign == 'B') {
4791					$va = $h - (0.5 * $bfs * $this->normalLineheight);
4792				} else {
4793					$va = 0.5 * $h;
4794				} // Middle
4795				// ONLY SET THESE IF WANT TO CONFINE BORDER +/- FILL TO FIT FONTSIZE - NOT FULL CELL AS IS ORIGINAL FUNCTION
4796				// spanfill or spanborder are set in FlowingBlock functions
4797				if ($spanfill || !empty($this->spanborddet) || $link != '') {
4798					$exth = 0.2; // Add to fontsize to increase height of background / link / border
4799					$boxtop = $this->y + $baseline + $va - ($this->FontSize * (1 + $exth / 2) * (0.5 + $bfx));
4800					$boxheight = $this->FontSize * (1 + $exth);
4801					$boxbottom = $boxtop + $boxheight;
4802				}
4803				$glyphYorigin = $baseline + $va;
4804			}
4805			$boxtop -= $baseline_shift;
4806			$boxbottom -= $baseline_shift;
4807			$bord_boxtop = $bg_boxtop = $boxtop;
4808			$bord_boxbottom = $bg_boxbottom = $boxbottom;
4809			$bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;
4810		}
4811
4812
4813		$bbw = $tbw = $lbw = $rbw = 0; // Border widths
4814		if (!empty($this->spanborddet)) {
4815			if (!isset($this->spanborddet['B'])) {
4816				$this->spanborddet['B'] = ['s' => 0, 'style' => '', 'w' => 0];
4817			}
4818			if (!isset($this->spanborddet['T'])) {
4819				$this->spanborddet['T'] = ['s' => 0, 'style' => '', 'w' => 0];
4820			}
4821			if (!isset($this->spanborddet['L'])) {
4822				$this->spanborddet['L'] = ['s' => 0, 'style' => '', 'w' => 0];
4823			}
4824			if (!isset($this->spanborddet['R'])) {
4825				$this->spanborddet['R'] = ['s' => 0, 'style' => '', 'w' => 0];
4826			}
4827			$bbw = $this->spanborddet['B']['w'];
4828			$tbw = $this->spanborddet['T']['w'];
4829			$lbw = $this->spanborddet['L']['w'];
4830			$rbw = $this->spanborddet['R']['w'];
4831		}
4832		if ($fill == 1 || $border == 1 || !empty($this->spanborddet)) {
4833			if (!empty($this->spanborddet)) {
4834				if ($fill == 1) {
4835					$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);
4836				}
4837				$s.= ' q ';
4838				$dashon = 3;
4839				$dashoff = 3.5;
4840				$dot = 2.5;
4841				if ($tbw) {
4842					$short = 0;
4843					if ($this->spanborddet['T']['style'] == 'dashed') {
4844						$s.=sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $tbw * $dashon * Mpdf::SCALE, $tbw * $dashoff * Mpdf::SCALE);
4845					} elseif ($this->spanborddet['T']['style'] == 'dotted') {
4846						$s.=sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $tbw * $dot * Mpdf::SCALE, -$tbw / 2 * Mpdf::SCALE);
4847						$short = $tbw / 2;
4848					} else {
4849						$s.=' 0 j 0 J [] 0 d ';
4850					}
4851					if ($this->spanborddet['T']['style'] != 'dotted') {
4852						$s .= 'q ';
4853						$s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
4854						$s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
4855						$s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
4856						$s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
4857						$s .= ' h W n '; // Ends path no-op & Sets the clipping path
4858					}
4859					$c = $this->SetDColor($this->spanborddet['T']['c'], true);
4860					if ($this->spanborddet['T']['style'] == 'double') {
4861						$s.=sprintf(' %s %.3F w ', $c, $tbw / 3 * Mpdf::SCALE);
4862						$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);
4863						$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);
4864					} elseif ($this->spanborddet['T']['style'] == 'dotted') {
4865						$s.=sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE);
4866						$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);
4867					} else {
4868						$s.=sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE);
4869						$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);
4870					}
4871					if ($this->spanborddet['T']['style'] != 'dotted') {
4872						$s .= ' Q ';
4873					}
4874				}
4875				if ($bbw) {
4876					$short = 0;
4877					if ($this->spanborddet['B']['style'] == 'dashed') {
4878						$s.=sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $bbw * $dashon * Mpdf::SCALE, $bbw * $dashoff * Mpdf::SCALE);
4879					} elseif ($this->spanborddet['B']['style'] == 'dotted') {
4880						$s.=sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $bbw * $dot * Mpdf::SCALE, -$bbw / 2 * Mpdf::SCALE);
4881						$short = $bbw / 2;
4882					} else {
4883						$s.=' 0 j 0 J [] 0 d ';
4884					}
4885					if ($this->spanborddet['B']['style'] != 'dotted') {
4886						$s .= 'q ';
4887						$s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
4888						$s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
4889						$s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
4890						$s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
4891						$s .= ' h W n '; // Ends path no-op & Sets the clipping path
4892					}
4893					$c = $this->SetDColor($this->spanborddet['B']['c'], true);
4894					if ($this->spanborddet['B']['style'] == 'double') {
4895						$s.=sprintf(' %s %.3F w ', $c, $bbw / 3 * Mpdf::SCALE);
4896						$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);
4897						$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);
4898					} elseif ($this->spanborddet['B']['style'] == 'dotted') {
4899						$s.=sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE);
4900						$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);
4901					} else {
4902						$s.=sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE);
4903						$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);
4904					}
4905					if ($this->spanborddet['B']['style'] != 'dotted') {
4906						$s .= ' Q ';
4907					}
4908				}
4909				if ($lbw) {
4910					$short = 0;
4911					if ($this->spanborddet['L']['style'] == 'dashed') {
4912						$s.=sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $lbw * $dashon * Mpdf::SCALE, $lbw * $dashoff * Mpdf::SCALE);
4913					} elseif ($this->spanborddet['L']['style'] == 'dotted') {
4914						$s.=sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $lbw * $dot * Mpdf::SCALE, -$lbw / 2 * Mpdf::SCALE);
4915						$short = $lbw / 2;
4916					} else {
4917						$s.=' 0 j 0 J [] 0 d ';
4918					}
4919					if ($this->spanborddet['L']['style'] != 'dotted') {
4920						$s .= 'q ';
4921						$s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
4922						$s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
4923						$s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
4924						$s .= sprintf('%.3F %.3F l ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
4925						$s .= ' h W n '; // Ends path no-op & Sets the clipping path
4926					}
4927					$c = $this->SetDColor($this->spanborddet['L']['c'], true);
4928					if ($this->spanborddet['L']['style'] == 'double') {
4929						$s.=sprintf(' %s %.3F w ', $c, $lbw / 3 * Mpdf::SCALE);
4930						$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);
4931						$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);
4932					} elseif ($this->spanborddet['L']['style'] == 'dotted') {
4933						$s.=sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE);
4934						$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);
4935					} else {
4936						$s.=sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE);
4937						$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);
4938					}
4939					if ($this->spanborddet['L']['style'] != 'dotted') {
4940						$s .= ' Q ';
4941					}
4942				}
4943				if ($rbw) {
4944					$short = 0;
4945					if ($this->spanborddet['R']['style'] == 'dashed') {
4946						$s.=sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $rbw * $dashon * Mpdf::SCALE, $rbw * $dashoff * Mpdf::SCALE);
4947					} elseif ($this->spanborddet['R']['style'] == 'dotted') {
4948						$s.=sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $rbw * $dot * Mpdf::SCALE, -$rbw / 2 * Mpdf::SCALE);
4949						$short = $rbw / 2;
4950					} else {
4951						$s.=' 0 j 0 J [] 0 d ';
4952					}
4953					if ($this->spanborddet['R']['style'] != 'dotted') {
4954						$s .= 'q ';
4955						$s .= sprintf('%.3F %.3F m ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);
4956						$s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);
4957						$s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);
4958						$s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);
4959						$s .= ' h W n '; // Ends path no-op & Sets the clipping path
4960					}
4961					$c = $this->SetDColor($this->spanborddet['R']['c'], true);
4962					if ($this->spanborddet['R']['style'] == 'double') {
4963						$s.=sprintf(' %s %.3F w ', $c, $rbw / 3 * Mpdf::SCALE);
4964						$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);
4965						$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);
4966					} elseif ($this->spanborddet['R']['style'] == 'dotted') {
4967						$s.=sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE);
4968						$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);
4969					} else {
4970						$s.=sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE);
4971						$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);
4972					}
4973					if ($this->spanborddet['R']['style'] != 'dotted') {
4974						$s .= ' Q ';
4975					}
4976				}
4977				$s.= ' Q ';
4978			} else { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock
4979				if ($fill == 1) {
4980					$op = ($border == 1) ? 'B' : 'f';
4981				} else {
4982					$op = 'S';
4983				}
4984				$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);
4985			}
4986		}
4987
4988		if (is_string($border)) { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock
4989			$x = $this->x;
4990			$y = $this->y;
4991			if (is_int(strpos($border, 'L'))) {
4992				$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);
4993			}
4994			if (is_int(strpos($border, 'T'))) {
4995				$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);
4996			}
4997			if (is_int(strpos($border, 'R'))) {
4998				$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);
4999			}
5000			if (is_int(strpos($border, 'B'))) {
5001				$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);
5002			}
5003		}
5004
5005		if ($txt != '') {
5006			if ($exactWidth) {
5007				$stringWidth = $w;
5008			} else {
5009				$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 );
5010			}
5011
5012			// Set x OFFSET FOR PRINTING
5013			if ($align == 'R') {
5014				$dx = $w - $this->cMarginR - $stringWidth - $lcpaddingR;
5015			} elseif ($align == 'C') {
5016				$dx = (($w - $stringWidth ) / 2);
5017			} elseif ($align == 'L' or $align == 'J') {
5018				$dx = $this->cMarginL + $lcpaddingL;
5019			} else {
5020				$dx = 0;
5021			}
5022
5023			if ($this->ColorFlag) {
5024				$s .='q ' . $this->TextColor . ' ';
5025			}
5026
5027			// OUTLINE
5028			if (isset($this->textparam['outline-s']) && $this->textparam['outline-s'] && !($textvar & TextVars::FC_SMALLCAPS)) { // mPDF 5.7.1
5029				$s .=' ' . sprintf('%.3F w', $this->LineWidth * Mpdf::SCALE) . ' ';
5030				$s .=" $this->DrawColor ";
5031				$s .=" 2 Tr ";
5032			} 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 ???
5033				$s .= ' 2 Tr 1 J 1 j ';
5034				$s .= ' ' . sprintf('%.3F w', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight) . ' ';
5035				$tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
5036				if ($this->FillColor != $tc) {
5037					$s .= ' ' . $tc . ' ';
5038				}  // stroke (outline) = same colour as text(fill)
5039			} else {
5040				$s .=" 0 Tr ";
5041			}
5042
5043			if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) { // Artificial italic
5044				$aix = '1 0 0.261799 1 %.3F %.3F Tm ';
5045			} else {
5046				$aix = '%.3F %.3F Td ';
5047			}
5048
5049			$px = ($this->x + $dx) * Mpdf::SCALE;
5050			$py = ($this->h - ($this->y + $glyphYorigin - $baseline_shift)) * Mpdf::SCALE;
5051
5052			// THE TEXT
5053			$txt2 = $txt;
5054			$sub = '';
5055			$this->CurrentFont['used'] = true;
5056
5057			/*             * ************** SIMILAR TO Text() ************************ */
5058
5059			// IF corefonts AND NOT SmCaps AND NOT Kerning
5060			// Just output text; charspacing and wordspacing already set by charspacing (Tc) and ws (Tw)
5061			if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) {
5062				$txt2 = $this->_escape($txt2);
5063				$sub .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
5064			} // IF NOT corefonts AND NO wordspacing AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL
5065			// Just output text
5066			elseif (!$this->usingCoreFont && !$this->ws && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {
5067				// IF SIP/SMP
5068				if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {
5069					$txt2 = $this->UTF8toSubset($txt2);
5070					$sub .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);
5071				} // NOT SIP/SMP
5072				else {
5073					$txt2 = $this->UTF8ToUTF16BE($txt2, false);
5074					$txt2 = $this->_escape($txt2);
5075					$sub .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
5076				}
5077			} // IF NOT corefonts AND IS wordspacing AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL
5078			// Output text word by word with an adjustment to the intercharacter spacing for SPACEs to form word spacing
5079			// IF multibyte - Tw has no effect - need to do word spacing using an adjustment before each space
5080			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)) )) {
5081				$space = " ";
5082				$space = $this->UTF8ToUTF16BE($space, false);
5083				$space = $this->_escape($space);
5084				$sub .=sprintf('BT ' . $aix . ' %.3F Tc [', $px, $py, $this->charspacing);
5085				$t = explode(' ', $txt2);
5086				$numt = count($t);
5087				for ($i = 0; $i < $numt; $i++) {
5088					$tx = $t[$i];
5089					$tx = $this->UTF8ToUTF16BE($tx, false);
5090					$tx = $this->_escape($tx);
5091					$sub .=sprintf('(%s) ', $tx);
5092					if (($i + 1) < $numt) {
5093						$adj = -($this->ws) * 1000 / $this->FontSizePt;
5094						$sub .=sprintf('%d(%s) ', $adj, $space);
5095					}
5096				}
5097				$sub .='] TJ ';
5098				$sub .=' ET';
5099			} // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]
5100			else {
5101				$sub = $this->applyGPOSpdf($txt, $aix, $px, $py, $OTLdata, $textvar);
5102			}
5103
5104			/** ************** END SIMILAR TO Text() ************************ */
5105
5106			if ($this->shrin_k > 1) {
5107				$shrin_k = $this->shrin_k;
5108			} else {
5109				$shrin_k = 1;
5110			}
5111
5112			// UNDERLINE
5113			if ($textvar & TextVars::FD_UNDERLINE) { // mPDF 5.7.1	// mPDF 6
5114
5115				// mPDF 5.7.3  inline text-decoration parameters
5116
5117				$c = isset($this->textparam['u-decoration']['color']) ? $this->textparam['u-decoration']['color'] : '';
5118				if ($this->FillColor != $c) {
5119					$sub .= ' ' . $c . ' ';
5120				}
5121
5122				// mPDF 5.7.3  inline text-decoration parameters
5123				$decorationfontkey = isset($this->textparam['u-decoration']['fontkey']) ? $this->textparam['u-decoration']['fontkey'] : '';
5124				$decorationfontsize = isset($this->textparam['u-decoration']['fontsize']) ? $this->textparam['u-decoration']['fontsize'] / $shrin_k : 0;
5125
5126				if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
5127					$ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
5128				} else {
5129					$ut = 60 / 1000 * $decorationfontsize;
5130				}
5131
5132				if (isset($this->fonts[$decorationfontkey]['up']) && $this->fonts[$decorationfontkey]['up']) {
5133					$up = $this->fonts[$decorationfontkey]['up'];
5134				} else {
5135					$up = -100;
5136				}
5137
5138				$adjusty = (-$up / 1000 * $decorationfontsize) + $ut / 2;
5139				$ubaseline = isset($this->textparam['u-decoration']['baseline'])
5140					? $glyphYorigin - $this->textparam['u-decoration']['baseline'] / $shrin_k
5141					: $glyphYorigin;
5142
5143				$olw = $this->LineWidth;
5144
5145				$sub .= ' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
5146				$sub .= ' ' . $this->_dounderline($this->x + $dx, $this->y + $ubaseline + $adjusty, $txt, $OTLdata, $textvar);
5147				$sub .= ' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
5148
5149				if ($this->FillColor != $c) {
5150					$sub .= ' ' . $this->FillColor . ' ';
5151				}
5152			}
5153
5154			// STRIKETHROUGH
5155			if ($textvar & TextVars::FD_LINETHROUGH) { // mPDF 5.7.1	// mPDF 6
5156
5157				// mPDF 5.7.3  inline text-decoration parameters
5158				$c = $this->textparam['s-decoration']['color'];
5159
5160				if ($this->FillColor != $c) {
5161					$sub .= ' ' . $c . ' ';
5162				}
5163
5164				// mPDF 5.7.3  inline text-decoration parameters
5165				$decorationfontkey = $this->textparam['s-decoration']['fontkey'];
5166				$decorationfontsize = $this->textparam['s-decoration']['fontsize'] / $shrin_k;
5167
5168				// Use yStrikeoutSize from OS/2 if available
5169				if (isset($this->fonts[$decorationfontkey]['strs']) && $this->fonts[$decorationfontkey]['strs']) {
5170					$ut = $this->fonts[$decorationfontkey]['strs'] / 1000 * $decorationfontsize;
5171				} // else use underlineThickness from post if available
5172				elseif (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
5173					$ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
5174				} else {
5175					$ut = 50 / 1000 * $decorationfontsize;
5176				}
5177
5178				// Use yStrikeoutPosition from OS/2 if available
5179				if (isset($this->fonts[$decorationfontkey]['strp']) && $this->fonts[$decorationfontkey]['strp']) {
5180					$up = $this->fonts[$decorationfontkey]['strp'];
5181					$adjusty = (-$up / 1000 * $decorationfontsize);
5182				} // else use a fraction ($this->baselineS) of CapHeight
5183				else {
5184					if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {
5185						$ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];
5186					} else {
5187						$ch = 700;
5188					}
5189					$adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineS;
5190				}
5191
5192				$sbaseline = $glyphYorigin - $this->textparam['s-decoration']['baseline'] / $shrin_k;
5193
5194				$olw = $this->LineWidth;
5195
5196				$sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
5197				$sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $sbaseline + $adjusty, $txt, $OTLdata, $textvar);
5198				$sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
5199
5200				if ($this->FillColor != $c) {
5201					$sub .= ' ' . $this->FillColor . ' ';
5202				}
5203			}
5204
5205			// mPDF 5.7.3  inline text-decoration parameters
5206			// OVERLINE
5207			if ($textvar & TextVars::FD_OVERLINE) { // mPDF 5.7.1	// mPDF 6
5208				// mPDF 5.7.3  inline text-decoration parameters
5209				$c = $this->textparam['o-decoration']['color'];
5210				if ($this->FillColor != $c) {
5211					$sub .= ' ' . $c . ' ';
5212				}
5213
5214				// mPDF 5.7.3  inline text-decoration parameters
5215				$decorationfontkey = (int) (((float) $this->textparam['o-decoration']['fontkey']) / $shrin_k);
5216				$decorationfontsize = $this->textparam['o-decoration']['fontsize'];
5217
5218				if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
5219					$ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
5220				} else {
5221					$ut = 60 / 1000 * $decorationfontsize;
5222				}
5223				if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {
5224					$ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];
5225				} else {
5226					$ch = 700;
5227				}
5228				$adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineO;
5229				$obaseline = $glyphYorigin - $this->textparam['o-decoration']['baseline'] / $shrin_k;
5230				$olw = $this->LineWidth;
5231				$sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));
5232				$sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $obaseline + $adjusty, $txt, $OTLdata, $textvar);
5233				$sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));
5234				if ($this->FillColor != $c) {
5235					$sub .= ' ' . $this->FillColor . ' ';
5236				}
5237			}
5238
5239			// TEXT SHADOW
5240			if ($this->textshadow) {  // First to process is last in CSS comma separated shadows
5241				foreach ($this->textshadow as $ts) {
5242					$s .= ' q ';
5243					$s .= $this->SetTColor($ts['col'], true) . "\n";
5244					if ($ts['col']{0} == 5 && ord($ts['col']{4}) < 100) { // RGBa
5245						$s .= $this->SetAlpha(ord($ts['col']{4}) / 100, 'Normal', true, 'F') . "\n";
5246					} elseif ($ts['col']{0} == 6 && ord($ts['col']{5}) < 100) { // CMYKa
5247						$s .= $this->SetAlpha(ord($ts['col']{5}) / 100, 'Normal', true, 'F') . "\n";
5248					} elseif ($ts['col']{0} == 1 && $ts['col']{2} == 1 && ord($ts['col']{3}) < 100) { // Gray
5249						$s .= $this->SetAlpha(ord($ts['col']{3}) / 100, 'Normal', true, 'F') . "\n";
5250					}
5251					$s .= sprintf(' 1 0 0 1 %.4F %.4F cm', $ts['x'] * Mpdf::SCALE, -$ts['y'] * Mpdf::SCALE) . "\n";
5252					$s .= $sub;
5253					$s .= ' Q ';
5254				}
5255			}
5256
5257			$s .= $sub;
5258
5259			// COLOR
5260			if ($this->ColorFlag) {
5261				$s .=' Q';
5262			}
5263
5264			// LINK
5265			if ($link != '') {
5266				$this->Link($this->x, $boxtop, $w, $boxheight, $link);
5267			}
5268		}
5269		if ($s) {
5270			$this->_out($s);
5271		}
5272
5273		// WORD SPACING
5274		if ($this->ws && !$this->usingCoreFont) {
5275			$this->_out(sprintf('BT %.3F Tc ET', $this->charspacing));
5276		}
5277		$this->lasth = $h;
5278		if (strpos($txt, "\n") !== false) {
5279			$ln = 1; // cell recognizes \n from <BR> tag
5280		}
5281		if ($ln > 0) {
5282			// Go to next line
5283			$this->y += $h;
5284			if ($ln == 1) {
5285				// Move to next line
5286				if ($currentx != 0) {
5287					$this->x = $currentx;
5288				} else {
5289					$this->x = $this->lMargin;
5290				}
5291			}
5292		} else {
5293			$this->x+=$w;
5294		}
5295	}
5296
5297	function applyGPOSpdf($txt, $aix, $x, $y, $OTLdata, $textvar = 0)
5298	{
5299		// Generate PDF string
5300		// ==============================
5301		if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {
5302			$sipset = true;
5303		} else {
5304			$sipset = false;
5305		}
5306
5307		if ($textvar & TextVars::FC_SMALLCAPS) {
5308			$smcaps = true;
5309		} // IF SmallCaps using transformation, NOT OTL
5310		else {
5311			$smcaps = false;
5312		}
5313
5314		if ($sipset) {
5315			$fontid = $last_fontid = $original_fontid = $this->CurrentFont['subsetfontids'][0];
5316		} else {
5317			$fontid = $last_fontid = $original_fontid = $this->CurrentFont['i'];
5318		}
5319		$SmallCapsON = false;  // state: uppercase/not
5320		$lastSmallCapsON = false; // state: uppercase/not
5321		$last_fontsize = $fontsize = $this->FontSizePt;
5322		$last_fontstretch = $fontstretch = 100;
5323		$groupBreak = false;
5324
5325		$unicode = $this->UTF8StringToArray($txt);
5326
5327		$GPOSinfo = (isset($OTLdata['GPOSinfo']) ? $OTLdata['GPOSinfo'] : []);
5328		$charspacing = ($this->charspacing * 1000 / $this->FontSizePt);
5329		$wordspacing = ($this->ws * 1000 / $this->FontSizePt);
5330
5331		$XshiftBefore = 0;
5332		$XshiftAfter = 0;
5333		$lastYPlacement = 0;
5334
5335		if ($sipset) {
5336			// mPDF 6  DELETED ********
5337			// 	$txt= preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $txt);	// ? Need to adjust OTL info
5338			// 	$txt= preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $txt);	// ? Need to adjust OTL info
5339			$tj = '<';
5340		} else {
5341			$tj = '(';
5342		}
5343
5344		for ($i = 0; $i < count($unicode); $i++) {
5345			$c = $unicode[$i];
5346			$tx = '';
5347			$XshiftBefore = $XshiftAfter;
5348			$XshiftAfter = 0;
5349			$YPlacement = 0;
5350			$groupBreak = false;
5351			$kashida = 0;
5352			if (!empty($OTLdata)) {
5353				// YPlacement from GPOS
5354				if (isset($GPOSinfo[$i]['YPlacement']) && $GPOSinfo[$i]['YPlacement']) {
5355					$YPlacement = $GPOSinfo[$i]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];
5356					$groupBreak = true;
5357				}
5358				// XPlacement from GPOS
5359				if (isset($GPOSinfo[$i]['XPlacement']) && $GPOSinfo[$i]['XPlacement']) {
5360					if (!isset($GPOSinfo[$i]['wDir']) || $GPOSinfo[$i]['wDir'] != 'RTL') {
5361						if (isset($GPOSinfo[$i]['BaseWidth'])) {
5362							$GPOSinfo[$i]['XPlacement'] -= $GPOSinfo[$i]['BaseWidth'];
5363						}
5364					}
5365
5366					// Convert to PDF Text space (thousandths of a unit );
5367					$XshiftBefore += $GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
5368					$XshiftAfter += -$GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
5369				}
5370
5371				// Kashida from GPOS
5372				// Kashida is set as an absolute length value, but to adjust text needs to be converted to
5373				// font-related size
5374				if (isset($GPOSinfo[$i]['kashida_space']) && $GPOSinfo[$i]['kashida_space']) {
5375					$kashida = $GPOSinfo[$i]['kashida_space'];
5376				}
5377
5378				if ($c == 32) { // word spacing
5379					$XshiftAfter += $wordspacing;
5380				}
5381
5382				if (substr($OTLdata['group'], ($i + 1), 1) != 'M') { // Don't add inter-character spacing before Marks
5383					$XshiftAfter += $charspacing;
5384				}
5385
5386				// ...applyGPOSpdf...
5387				// XAdvance from GPOS - Convert to PDF Text space (thousandths of a unit );
5388				if (((isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] != 'RTL') || !isset($GPOSinfo[$i]['wDir'])) && isset($GPOSinfo[$i]['XAdvanceL']) && $GPOSinfo[$i]['XAdvanceL']) {
5389					$XshiftAfter += $GPOSinfo[$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
5390				} elseif (isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] == 'RTL' && isset($GPOSinfo[$i]['XAdvanceR']) && $GPOSinfo[$i]['XAdvanceR']) {
5391					$XshiftAfter += $GPOSinfo[$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
5392				}
5393			} // Character & Word spacing - if NOT OTL
5394			else {
5395				$XshiftAfter += $charspacing;
5396				if ($c == 32) {
5397					$XshiftAfter += $wordspacing;
5398				}
5399			}
5400
5401			// IF Kerning done using pairs rather than OTL
5402			if ($textvar & TextVars::FC_KERNING) {
5403				if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {
5404					$XshiftBefore += $this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];
5405				}
5406			}
5407
5408			if ($YPlacement != $lastYPlacement) {
5409				$groupBreak = true;
5410			}
5411
5412			if ($XshiftBefore) {  // +ve value in PDF moves to the left
5413				// If Fontstretch is ongoing, need to adjust X adjustments because these will be stretched out.
5414				$XshiftBefore *= 100 / $last_fontstretch;
5415				if ($sipset) {
5416					$tj .= sprintf('>%d<', (-$XshiftBefore));
5417				} else {
5418					$tj .= sprintf(')%d(', (-$XshiftBefore));
5419				}
5420			}
5421
5422			// Small-Caps
5423			if ($smcaps) {
5424				if (isset($this->upperCase[$c])) {
5425					$c = $this->upperCase[$c];
5426					// $this->CurrentFont['subset'][$this->upperCase[$c]] = $this->upperCase[$c];	// add the CAP to subset
5427					$SmallCapsON = true;
5428					// For $sipset
5429					if (!$lastSmallCapsON) {   // Turn ON SmallCaps
5430						$groupBreak = true;
5431						$fontstretch = $this->smCapsStretch;
5432						$fontsize = $this->FontSizePt * $this->smCapsScale;
5433					}
5434				} else {
5435					$SmallCapsON = false;
5436					if ($lastSmallCapsON) {  // Turn OFF SmallCaps
5437						$groupBreak = true;
5438						$fontstretch = 100;
5439						$fontsize = $this->FontSizePt;
5440					}
5441				}
5442			}
5443
5444			// Prepare Text and Select Font ID
5445			if ($sipset) {
5446				// mPDF 6  DELETED ********
5447				// if ($c == 7 || $c == 8) {
5448				// if ($original_fontid != $last_fontid) {
5449				// 	$groupBreak = true;
5450				// 	$fontid = $original_fontid;
5451				// }
5452				// if ($c == 7) { $tj .= $this->aliasNbPgHex; }
5453				// else { $tj .= $this->aliasNbPgGpHex; }
5454				// continue;
5455				// }
5456				for ($j = 0; $j < 99; $j++) {
5457					$init = array_search($c, $this->CurrentFont['subsets'][$j]);
5458					if ($init !== false) {
5459						if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
5460							$groupBreak = true;
5461							$fontid = $this->CurrentFont['subsetfontids'][$j];
5462						}
5463						$tx = sprintf("%02s", strtoupper(dechex($init)));
5464						break;
5465					} elseif (count($this->CurrentFont['subsets'][$j]) < 255) {
5466						$n = count($this->CurrentFont['subsets'][$j]);
5467						$this->CurrentFont['subsets'][$j][$n] = $c;
5468						if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
5469							$groupBreak = true;
5470							$fontid = $this->CurrentFont['subsetfontids'][$j];
5471						}
5472						$tx = sprintf("%02s", strtoupper(dechex($n)));
5473						break;
5474					} elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) {
5475						$this->CurrentFont['subsets'][($j + 1)] = [0 => 0];
5476						$this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;
5477						$this->extraFontSubsets++;
5478					}
5479				}
5480			} else {
5481				$tx = UtfString::code2utf($c);
5482				if ($this->usingCoreFont) {
5483					$tx = utf8_decode($tx);
5484				} else {
5485					$tx = $this->UTF8ToUTF16BE($tx, false);
5486				}
5487				$tx = $this->_escape($tx);
5488			}
5489
5490			// If any settings require a new Text Group
5491			if ($groupBreak || $fontstretch != $last_fontstretch) {
5492				if ($sipset) {
5493					$tj .= '>] TJ ';
5494				} else {
5495					$tj .= ')] TJ ';
5496				}
5497				if ($fontid != $last_fontid || $fontsize != $last_fontsize) {
5498					$tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);
5499				}
5500				if ($fontstretch != $last_fontstretch) {
5501					$tj .= sprintf('%d Tz ', $fontstretch);
5502				}
5503				if ($YPlacement != $lastYPlacement) {
5504					$tj .= sprintf('%.3F Ts ', $YPlacement);
5505				}
5506				if ($sipset) {
5507					$tj .= '[<';
5508				} else {
5509					$tj .= '[(';
5510				}
5511			}
5512
5513			// Output the code for the txt character
5514			$tj .= $tx;
5515			$lastSmallCapsON = $SmallCapsON;
5516			$last_fontid = $fontid;
5517			$last_fontsize = $fontsize;
5518			$last_fontstretch = $fontstretch;
5519
5520			// Kashida
5521			if ($kashida) {
5522				$c = 0x0640; // add the Tatweel U+0640
5523				if (isset($this->CurrentFont['subset'])) {
5524					$this->CurrentFont['subset'][$c] = $c;
5525				}
5526				$kashida *= 1000 / $this->FontSizePt;
5527				$tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);
5528
5529				// Get YPlacement from next Base character
5530				$nextbase = $i + 1;
5531				while ($OTLdata['group']{$nextbase} != 'C') {
5532					$nextbase++;
5533				}
5534				if (isset($GPOSinfo[$nextbase]) && isset($GPOSinfo[$nextbase]['YPlacement']) && $GPOSinfo[$nextbase]['YPlacement']) {
5535					$YPlacement = $GPOSinfo[$nextbase]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];
5536				}
5537
5538				// Prepare Text and Select Font ID
5539				if ($sipset) {
5540					for ($j = 0; $j < 99; $j++) {
5541						$init = array_search($c, $this->CurrentFont['subsets'][$j]);
5542						if ($init !== false) {
5543							if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
5544								$fontid = $this->CurrentFont['subsetfontids'][$j];
5545							}
5546							$tx = sprintf("%02s", strtoupper(dechex($init)));
5547							break;
5548						} elseif (count($this->CurrentFont['subsets'][$j]) < 255) {
5549							$n = count($this->CurrentFont['subsets'][$j]);
5550							$this->CurrentFont['subsets'][$j][$n] = $c;
5551							if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
5552								$fontid = $this->CurrentFont['subsetfontids'][$j];
5553							}
5554							$tx = sprintf("%02s", strtoupper(dechex($n)));
5555							break;
5556						} elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) {
5557							$this->CurrentFont['subsets'][($j + 1)] = [0 => 0];
5558							$this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;
5559							$this->extraFontSubsets++;
5560						}
5561					}
5562				} else {
5563					$tx = UtfString::code2utf($c);
5564					$tx = $this->UTF8ToUTF16BE($tx, false);
5565					$tx = $this->_escape($tx);
5566				}
5567
5568				if ($kashida > $tatw) {
5569					// Insert multiple tatweel characters, repositioning the last one to give correct total length
5570					$fontstretch = 100;
5571					$nt = intval($kashida / $tatw);
5572					$nudgeback = (($nt + 1) * $tatw) - $kashida;
5573					$optx = str_repeat($tx, $nt);
5574					if ($sipset) {
5575						$optx .= sprintf('>%d<', ($nudgeback));
5576					} else {
5577						$optx .= sprintf(')%d(', ($nudgeback));
5578					}
5579					$optx .= $tx; // #last
5580				} else {
5581					// Insert single tatweel character and use fontstretch to get correct length
5582					$fontstretch = ($kashida / $tatw) * 100;
5583					$optx = $tx;
5584				}
5585
5586				if ($sipset) {
5587					$tj .= '>] TJ ';
5588				} else {
5589					$tj .= ')] TJ ';
5590				}
5591				if ($fontid != $last_fontid || $fontsize != $last_fontsize) {
5592					$tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);
5593				}
5594				if ($fontstretch != $last_fontstretch) {
5595					$tj .= sprintf('%d Tz ', $fontstretch);
5596				}
5597				$tj .= sprintf('%.3F Ts ', $YPlacement);
5598				if ($sipset) {
5599					$tj .= '[<';
5600				} else {
5601					$tj .= '[(';
5602				}
5603
5604				// Output the code for the txt character(s)
5605				$tj .= $optx;
5606				$last_fontid = $fontid;
5607				$last_fontstretch = $fontstretch;
5608				$fontstretch = 100;
5609			}
5610
5611			$lastYPlacement = $YPlacement;
5612		}
5613
5614
5615		// Finish up
5616		if ($sipset) {
5617			$tj .= '>';
5618			if ($XshiftAfter) {
5619				$tj .= sprintf('%d', (-$XshiftAfter));
5620			}
5621			if ($last_fontid != $original_fontid) {
5622				$tj .= '] TJ ';
5623				$tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);
5624				$tj .= '[';
5625			}
5626			$tj = preg_replace('/([^\\\])<>/', '\\1 ', $tj);
5627		} else {
5628			$tj .= ')';
5629			if ($XshiftAfter) {
5630				$tj .= sprintf('%d', (-$XshiftAfter));
5631			}
5632			if ($last_fontid != $original_fontid) {
5633				$tj .= '] TJ ';
5634				$tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);
5635				$tj .= '[';
5636			}
5637			$tj = preg_replace('/([^\\\])\(\)/', '\\1 ', $tj);
5638		}
5639
5640		$s = sprintf(' BT ' . $aix . ' 0 Tc 0 Tw [%s] TJ ET ', $x, $y, $tj);
5641
5642		// echo $s."\n\n"; // exit;
5643
5644		return $s;
5645	}
5646
5647	function _kern($txt, $mode, $aix, $x, $y)
5648	{
5649		if ($mode == 'MBTw') { // Multibyte requiring word spacing
5650			$space = ' ';
5651			// Convert string to UTF-16BE without BOM
5652			$space = $this->UTF8ToUTF16BE($space, false);
5653			$space = $this->_escape($space);
5654			$s = sprintf(' BT ' . $aix, $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE);
5655			$t = explode(' ', $txt);
5656			for ($i = 0; $i < count($t); $i++) {
5657				$tx = $t[$i];
5658
5659				$tj = '(';
5660				$unicode = $this->UTF8StringToArray($tx);
5661				for ($ti = 0; $ti < count($unicode); $ti++) {
5662					if ($ti > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]])) {
5663						$kern = -$this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]];
5664						$tj .= sprintf(')%d(', $kern);
5665					}
5666					$tc = UtfString::code2utf($unicode[$ti]);
5667					$tc = $this->UTF8ToUTF16BE($tc, false);
5668					$tj .= $this->_escape($tc);
5669				}
5670				$tj .= ')';
5671				$s.=sprintf(' %.3F Tc [%s] TJ', $this->charspacing, $tj);
5672
5673
5674				if (($i + 1) < count($t)) {
5675					$s.=sprintf(' %.3F Tc (%s) Tj', $this->ws + $this->charspacing, $space);
5676				}
5677			}
5678			$s.=' ET ';
5679		} elseif (!$this->usingCoreFont) {
5680			$s = '';
5681			$tj = '(';
5682			$unicode = $this->UTF8StringToArray($txt);
5683			for ($i = 0; $i < count($unicode); $i++) {
5684				if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {
5685					$kern = -$this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];
5686					$tj .= sprintf(')%d(', $kern);
5687				}
5688				$tx = UtfString::code2utf($unicode[$i]);
5689				$tx = $this->UTF8ToUTF16BE($tx, false);
5690				$tj .= $this->_escape($tx);
5691			}
5692			$tj .= ')';
5693			$s.=sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj);
5694		} else { // CORE Font
5695			$s = '';
5696			$tj = '(';
5697			$l = strlen($txt);
5698			for ($i = 0; $i < $l; $i++) {
5699				if ($i > 0 && isset($this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]])) {
5700					$kern = -$this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]];
5701					$tj .= sprintf(')%d(', $kern);
5702				}
5703				$tj .= $this->_escape($txt[$i]);
5704			}
5705			$tj .= ')';
5706			$s.=sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj);
5707		}
5708
5709		return $s;
5710	}
5711
5712	function MultiCell($w, $h, $txt, $border = 0, $align = '', $fill = 0, $link = '', $directionality = 'ltr', $encoded = false, $OTLdata = false, $maxrows = false)
5713	{
5714		// maxrows is called from mpdfform->TEXTAREA
5715		// Parameter (pre-)encoded - When called internally from form::textarea - mb_encoding already done and OTL - but not reverse RTL
5716		if (!$encoded) {
5717			$txt = $this->purify_utf8_text($txt);
5718			if ($this->text_input_as_HTML) {
5719				$txt = $this->all_entities_to_utf8($txt);
5720			}
5721			if ($this->usingCoreFont) {
5722				$txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
5723			}
5724			if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
5725				$this->biDirectional = true;
5726			} // *OTL*
5727			/* -- OTL -- */
5728			$OTLdata = [];
5729			// Use OTL OpenType Table Layout - GSUB & GPOS
5730			if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
5731				$txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
5732				$OTLdata = $this->otl->OTLdata;
5733			}
5734			if ($directionality == 'rtl' || $this->biDirectional) {
5735				if (!isset($OTLdata)) {
5736					$unicode = $this->UTF8StringToArray($txt, false);
5737					$is_strong = false;
5738					$this->getBasicOTLdata($OTLdata, $unicode, $is_strong);
5739				}
5740			}
5741			/* -- END OTL -- */
5742		}
5743		if (!$align) {
5744			$align = $this->defaultAlign;
5745		}
5746
5747		// Output text with automatic or explicit line breaks
5748		$cw = &$this->CurrentFont['cw'];
5749		if ($w == 0) {
5750			$w = $this->w - $this->rMargin - $this->x;
5751		}
5752
5753		$wmax = ($w - ($this->cMarginL + $this->cMarginR));
5754		if ($this->usingCoreFont) {
5755			$s = str_replace("\r", '', $txt);
5756			$nb = strlen($s);
5757			while ($nb > 0 and $s[$nb - 1] == "\n") {
5758				$nb--;
5759			}
5760		} else {
5761			$s = str_replace("\r", '', $txt);
5762			$nb = mb_strlen($s, $this->mb_enc);
5763			while ($nb > 0 and mb_substr($s, $nb - 1, 1, $this->mb_enc) == "\n") {
5764				$nb--;
5765			}
5766		}
5767		$b = 0;
5768		if ($border) {
5769			if ($border == 1) {
5770				$border = 'LTRB';
5771				$b = 'LRT';
5772				$b2 = 'LR';
5773			} else {
5774				$b2 = '';
5775				if (is_int(strpos($border, 'L'))) {
5776					$b2.='L';
5777				}
5778				if (is_int(strpos($border, 'R'))) {
5779					$b2.='R';
5780				}
5781				$b = is_int(strpos($border, 'T')) ? $b2 . 'T' : $b2;
5782			}
5783		}
5784		$sep = -1;
5785		$i = 0;
5786		$j = 0;
5787		$l = 0;
5788		$ns = 0;
5789		$nl = 1;
5790
5791		$rows = 0;
5792		$start_y = $this->y;
5793
5794		if (!$this->usingCoreFont) {
5795			$inclCursive = false;
5796			if (preg_match("/([" . $this->pregCURSchars . "])/u", $s)) {
5797				$inclCursive = true;
5798			}
5799			while ($i < $nb) {
5800				// Get next character
5801				$c = mb_substr($s, $i, 1, $this->mb_enc);
5802				if ($c == "\n") {
5803					// Explicit line break
5804					// WORD SPACING
5805					$this->ResetSpacing();
5806					$tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
5807					$tmpOTLdata = false;
5808					/* -- OTL -- */
5809					if (isset($OTLdata)) {
5810						$tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
5811						$this->otl->trimOTLdata($tmpOTLdata, false, true);
5812						$this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
5813					}
5814					/* -- END OTL -- */
5815					$this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
5816					if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
5817						return false;
5818					}
5819					$i++;
5820					$sep = -1;
5821					$j = $i;
5822					$l = 0;
5823					$ns = 0;
5824					$nl++;
5825					if ($border and $nl == 2) {
5826						$b = $b2;
5827					}
5828					continue;
5829				}
5830				if ($c == " ") {
5831					$sep = $i;
5832					$ls = $l;
5833					$ns++;
5834				}
5835
5836				$l += $this->GetCharWidthNonCore($c);
5837
5838				if ($l > $wmax) {
5839					// Automatic line break
5840					if ($sep == -1) { // Only one word
5841						if ($i == $j) {
5842							$i++;
5843						}
5844						// WORD SPACING
5845						$this->ResetSpacing();
5846						$tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
5847						$tmpOTLdata = false;
5848						/* -- OTL -- */
5849						if (isset($OTLdata)) {
5850							$tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
5851							$this->otl->trimOTLdata($tmpOTLdata, false, true);
5852							$this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
5853						}
5854						/* -- END OTL -- */
5855						$this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
5856					} else {
5857						$tmp = rtrim(mb_substr($s, $j, $sep - $j, $this->mb_enc));
5858						$tmpOTLdata = false;
5859						/* -- OTL -- */
5860						if (isset($OTLdata)) {
5861							$tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $sep - $j);
5862							$this->otl->trimOTLdata($tmpOTLdata, false, true);
5863						}
5864						/* -- END OTL -- */
5865						if ($align == 'J') {
5866							//////////////////////////////////////////
5867							// JUSTIFY J using Unicode fonts (Word spacing doesn't work)
5868							// WORD SPACING UNICODE
5869							// Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
5870							$tmp = str_replace(chr(194) . chr(160), chr(32), $tmp);
5871							$len_ligne = $this->GetStringWidth($tmp, false, $tmpOTLdata);
5872							$nb_carac = mb_strlen($tmp, $this->mb_enc);
5873							$nb_spaces = mb_substr_count($tmp, ' ', $this->mb_enc);
5874							// Take off number of Marks
5875							// Use GPOS OTL
5876							if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'])) {
5877								if (isset($tmpOTLdata['group']) && $tmpOTLdata['group']) {
5878									$nb_carac -= substr_count($tmpOTLdata['group'], 'M');
5879								}
5880							}
5881
5882							list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), $inclCursive, $tmpOTLdata);
5883							$this->SetSpacing($charspacing, $ws);
5884							//////////////////////////////////////////
5885						}
5886						if (isset($OTLdata)) {
5887							$this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
5888						}
5889						$this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
5890						$i = $sep + 1;
5891					}
5892					if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
5893						return false;
5894					}
5895					$sep = -1;
5896					$j = $i;
5897					$l = 0;
5898					$ns = 0;
5899					$nl++;
5900					if ($border and $nl == 2) {
5901						$b = $b2;
5902					}
5903				} else {
5904					$i++;
5905				}
5906			}
5907			// Last chunk
5908			// WORD SPACING
5909
5910			$this->ResetSpacing();
5911		} else {
5912			while ($i < $nb) {
5913				// Get next character
5914				$c = $s[$i];
5915				if ($c == "\n") {
5916					// Explicit line break
5917					// WORD SPACING
5918					$this->ResetSpacing();
5919					$this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
5920					if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
5921						return false;
5922					}
5923					$i++;
5924					$sep = -1;
5925					$j = $i;
5926					$l = 0;
5927					$ns = 0;
5928					$nl++;
5929					if ($border and $nl == 2) {
5930						$b = $b2;
5931					}
5932					continue;
5933				}
5934				if ($c == " ") {
5935					$sep = $i;
5936					$ls = $l;
5937					$ns++;
5938				}
5939
5940				$l += $this->GetCharWidthCore($c);
5941				if ($l > $wmax) {
5942					// Automatic line break
5943					if ($sep == -1) {
5944						if ($i == $j) {
5945							$i++;
5946						}
5947						// WORD SPACING
5948						$this->ResetSpacing();
5949						$this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
5950					} else {
5951						if ($align == 'J') {
5952							$tmp = rtrim(substr($s, $j, $sep - $j));
5953							//////////////////////////////////////////
5954							// JUSTIFY J using Unicode fonts (Word spacing doesn't work)
5955							// WORD SPACING NON_UNICODE/CJK
5956							// Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
5957							$tmp = str_replace(chr(160), chr(32), $tmp);
5958							$len_ligne = $this->GetStringWidth($tmp);
5959							$nb_carac = strlen($tmp);
5960							$nb_spaces = substr_count($tmp, ' ');
5961							$tmpOTLdata = [];
5962							list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), false, $tmpOTLdata);
5963							$this->SetSpacing($charspacing, $ws);
5964							//////////////////////////////////////////
5965						}
5966						$this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill, $link);
5967						$i = $sep + 1;
5968					}
5969					if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {
5970						return false;
5971					}
5972					$sep = -1;
5973					$j = $i;
5974					$l = 0;
5975					$ns = 0;
5976					$nl++;
5977					if ($border and $nl == 2) {
5978						$b = $b2;
5979					}
5980				} else {
5981					$i++;
5982				}
5983			}
5984			// Last chunk
5985			// WORD SPACING
5986
5987			$this->ResetSpacing();
5988		}
5989		// Last chunk
5990		if ($border and is_int(strpos($border, 'B'))) {
5991			$b.='B';
5992		}
5993		if (!$this->usingCoreFont) {
5994			$tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
5995			$tmpOTLdata = false;
5996			/* -- OTL -- */
5997			if (isset($OTLdata)) {
5998				$tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
5999				$this->otl->trimOTLdata($tmpOTLdata, false, true);
6000				$this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
6001			}
6002			/* -- END OTL -- */
6003			$this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
6004		} else {
6005			$this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
6006		}
6007		$this->x = $this->lMargin;
6008	}
6009
6010	/* -- DIRECTW -- */
6011
6012	function Write($h, $txt, $currentx = 0, $link = '', $directionality = 'ltr', $align = '', $fill = 0)
6013	{
6014		if (empty($this->directWrite)) {
6015			$this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
6016		}
6017
6018		$this->directWrite->Write($h, $txt, $currentx, $link, $directionality, $align, $fill);
6019	}
6020
6021	/* -- END DIRECTW -- */
6022
6023
6024	/* -- HTML-CSS -- */
6025
6026	function saveInlineProperties()
6027	{
6028		$saved = [];
6029		$saved['family'] = $this->FontFamily;
6030		$saved['style'] = $this->FontStyle;
6031		$saved['sizePt'] = $this->FontSizePt;
6032		$saved['size'] = $this->FontSize;
6033		$saved['HREF'] = $this->HREF;
6034		$saved['textvar'] = $this->textvar; // mPDF 5.7.1
6035		$saved['OTLtags'] = $this->OTLtags; // mPDF 5.7.1
6036		$saved['textshadow'] = $this->textshadow;
6037		$saved['linewidth'] = $this->LineWidth;
6038		$saved['drawcolor'] = $this->DrawColor;
6039		$saved['textparam'] = $this->textparam;
6040		$saved['lSpacingCSS'] = $this->lSpacingCSS;
6041		$saved['wSpacingCSS'] = $this->wSpacingCSS;
6042		$saved['I'] = $this->I;
6043		$saved['B'] = $this->B;
6044		$saved['colorarray'] = $this->colorarray;
6045		$saved['bgcolorarray'] = $this->spanbgcolorarray;
6046		$saved['border'] = $this->spanborddet;
6047		$saved['color'] = $this->TextColor;
6048		$saved['bgcolor'] = $this->FillColor;
6049		$saved['lang'] = $this->currentLang;
6050		$saved['fontLanguageOverride'] = $this->fontLanguageOverride; // mPDF 5.7.1
6051		$saved['display_off'] = $this->inlineDisplayOff;
6052
6053		return $saved;
6054	}
6055
6056	function restoreInlineProperties(&$saved)
6057	{
6058		$FontFamily = $saved['family'];
6059		$this->FontStyle = $saved['style'];
6060		$this->FontSizePt = $saved['sizePt'];
6061		$this->FontSize = $saved['size'];
6062
6063		$this->currentLang = $saved['lang'];
6064		$this->fontLanguageOverride = $saved['fontLanguageOverride']; // mPDF 5.7.1
6065
6066		$this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well
6067
6068		$this->HREF = $saved['HREF'];
6069		$this->textvar = $saved['textvar']; // mPDF 5.7.1
6070		$this->OTLtags = $saved['OTLtags']; // mPDF 5.7.1
6071		$this->textshadow = $saved['textshadow'];
6072		$this->LineWidth = $saved['linewidth'];
6073		$this->DrawColor = $saved['drawcolor'];
6074		$this->textparam = $saved['textparam'];
6075		$this->inlineDisplayOff = $saved['display_off'];
6076
6077		$this->lSpacingCSS = $saved['lSpacingCSS'];
6078		if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
6079			$this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize);
6080		} else {
6081			$this->fixedlSpacing = false;
6082		}
6083		$this->wSpacingCSS = $saved['wSpacingCSS'];
6084		if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
6085			$this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize);
6086		} else {
6087			$this->minwSpacing = 0;
6088		}
6089
6090		$this->SetFont($FontFamily, $saved['style'], $saved['sizePt'], false);
6091
6092		$this->currentfontstyle = $saved['style'];
6093		$this->currentfontsize = $saved['sizePt'];
6094		$this->SetStylesArray(['B' => $saved['B'], 'I' => $saved['I']]); // mPDF 5.7.1
6095
6096		$this->TextColor = $saved['color'];
6097		$this->FillColor = $saved['bgcolor'];
6098		$this->colorarray = $saved['colorarray'];
6099		$cor = $saved['colorarray'];
6100		if ($cor) {
6101			$this->SetTColor($cor);
6102		}
6103		$this->spanbgcolorarray = $saved['bgcolorarray'];
6104		$cor = $saved['bgcolorarray'];
6105		if ($cor) {
6106			$this->SetFColor($cor);
6107		}
6108		$this->spanborddet = $saved['border'];
6109	}
6110
6111	// Used when ColActive for tables - updated to return first block with background fill OR borders
6112	function GetFirstBlockFill()
6113	{
6114		// Returns the first blocklevel that uses a bgcolor fill
6115		$startfill = 0;
6116		for ($i = 1; $i <= $this->blklvl; $i++) {
6117			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']) {
6118				$startfill = $i;
6119				break;
6120			}
6121		}
6122		return $startfill;
6123	}
6124
6125	// -------------------------FLOWING BLOCK------------------------------------//
6126	// The following functions were originally written by Damon Kohler           //
6127	// --------------------------------------------------------------------------//
6128
6129	function saveFont()
6130	{
6131		$saved = [];
6132		$saved['family'] = $this->FontFamily;
6133		$saved['style'] = $this->FontStyle;
6134		$saved['sizePt'] = $this->FontSizePt;
6135		$saved['size'] = $this->FontSize;
6136		$saved['curr'] = &$this->CurrentFont;
6137		$saved['lang'] = $this->currentLang; // mPDF 6
6138		$saved['color'] = $this->TextColor;
6139		$saved['spanbgcolor'] = $this->spanbgcolor;
6140		$saved['spanbgcolorarray'] = $this->spanbgcolorarray;
6141		$saved['bord'] = $this->spanborder;
6142		$saved['border'] = $this->spanborddet;
6143		$saved['HREF'] = $this->HREF;
6144		$saved['textvar'] = $this->textvar; // mPDF 5.7.1
6145		$saved['textshadow'] = $this->textshadow;
6146		$saved['linewidth'] = $this->LineWidth;
6147		$saved['drawcolor'] = $this->DrawColor;
6148		$saved['textparam'] = $this->textparam;
6149		$saved['ReqFontStyle'] = $this->ReqFontStyle;
6150		$saved['fixedlSpacing'] = $this->fixedlSpacing;
6151		$saved['minwSpacing'] = $this->minwSpacing;
6152		return $saved;
6153	}
6154
6155	function restoreFont(&$saved, $write = true)
6156	{
6157		if (!isset($saved) || empty($saved)) {
6158			return;
6159		}
6160
6161		$this->FontFamily = $saved['family'];
6162		$this->FontStyle = $saved['style'];
6163		$this->FontSizePt = $saved['sizePt'];
6164		$this->FontSize = $saved['size'];
6165		$this->CurrentFont = &$saved['curr'];
6166		$this->currentLang = $saved['lang']; // mPDF 6
6167		$this->TextColor = $saved['color'];
6168		$this->spanbgcolor = $saved['spanbgcolor'];
6169		$this->spanbgcolorarray = $saved['spanbgcolorarray'];
6170		$this->spanborder = $saved['bord'];
6171		$this->spanborddet = $saved['border'];
6172		$this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well
6173		$this->HREF = $saved['HREF'];
6174		$this->fixedlSpacing = $saved['fixedlSpacing'];
6175		$this->minwSpacing = $saved['minwSpacing'];
6176		$this->textvar = $saved['textvar'];  // mPDF 5.7.1
6177		$this->textshadow = $saved['textshadow'];
6178		$this->LineWidth = $saved['linewidth'];
6179		$this->DrawColor = $saved['drawcolor'];
6180		$this->textparam = $saved['textparam'];
6181		if ($write) {
6182			$this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], true, true); // force output
6183			$fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
6184			if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
6185				$this->_out($fontout);
6186			}
6187			$this->pageoutput[$this->page]['Font'] = $fontout;
6188		} else {
6189			$this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], false);
6190		}
6191		$this->ReqFontStyle = $saved['ReqFontStyle'];
6192	}
6193
6194	function newFlowingBlock($w, $h, $a = '', $is_table = false, $blockstate = 0, $newblock = true, $blockdir = 'ltr', $table_draft = false)
6195	{
6196		if (!$a) {
6197			if ($blockdir == 'rtl') {
6198				$a = 'R';
6199			} else {
6200				$a = 'L';
6201			}
6202		}
6203		$this->flowingBlockAttr['width'] = ($w * Mpdf::SCALE);
6204		// line height in user units
6205		$this->flowingBlockAttr['is_table'] = $is_table;
6206		$this->flowingBlockAttr['table_draft'] = $table_draft;
6207		$this->flowingBlockAttr['height'] = $h;
6208		$this->flowingBlockAttr['lineCount'] = 0;
6209		$this->flowingBlockAttr['align'] = $a;
6210		$this->flowingBlockAttr['font'] = [];
6211		$this->flowingBlockAttr['content'] = [];
6212		$this->flowingBlockAttr['contentB'] = [];
6213		$this->flowingBlockAttr['contentWidth'] = 0;
6214		$this->flowingBlockAttr['blockstate'] = $blockstate;
6215
6216		$this->flowingBlockAttr['newblock'] = $newblock;
6217		$this->flowingBlockAttr['valign'] = 'M';
6218		$this->flowingBlockAttr['blockdir'] = $blockdir;
6219		$this->flowingBlockAttr['cOTLdata'] = []; // mPDF 5.7.1
6220		$this->flowingBlockAttr['lastBidiText'] = ''; // mPDF 5.7.1
6221		if (!empty($this->otl)) {
6222			$this->otl->lastBidiStrongType = '';
6223		} // *OTL*
6224	}
6225
6226	function finishFlowingBlock($endofblock = false, $next = '')
6227	{
6228		$currentx = $this->x;
6229		// prints out the last chunk
6230		$is_table = $this->flowingBlockAttr['is_table'];
6231		$table_draft = $this->flowingBlockAttr['table_draft'];
6232		$maxWidth = & $this->flowingBlockAttr['width'];
6233		$stackHeight = & $this->flowingBlockAttr['height'];
6234		$align = & $this->flowingBlockAttr['align'];
6235		$content = & $this->flowingBlockAttr['content'];
6236		$contentB = & $this->flowingBlockAttr['contentB'];
6237		$font = & $this->flowingBlockAttr['font'];
6238		$contentWidth = & $this->flowingBlockAttr['contentWidth'];
6239		$lineCount = & $this->flowingBlockAttr['lineCount'];
6240		$valign = & $this->flowingBlockAttr['valign'];
6241		$blockstate = $this->flowingBlockAttr['blockstate'];
6242
6243		$cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1
6244		$newblock = $this->flowingBlockAttr['newblock'];
6245		$blockdir = $this->flowingBlockAttr['blockdir'];
6246
6247		// *********** BLOCK BACKGROUND COLOR *****************//
6248		if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {
6249			$fill = 0;
6250		} else {
6251			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
6252			$fill = 0;
6253		}
6254
6255		$hanger = '';
6256		// Always right trim!
6257		// Right trim last content and adjust width if needed to justify (later)
6258		if (isset($content[count($content) - 1]) && preg_match('/[ ]+$/', $content[count($content) - 1], $m)) {
6259			$strip = strlen($m[0]);
6260			$content[count($content) - 1] = substr($content[count($content) - 1], 0, (strlen($content[count($content) - 1]) - $strip));
6261			/* -- OTL -- */
6262			if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
6263				$this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true);
6264			}
6265			/* -- END OTL -- */
6266		}
6267
6268		// the amount of space taken up so far in user units
6269		$usedWidth = 0;
6270
6271		// COLS
6272		$oldcolumn = $this->CurrCol;
6273
6274		if ($this->ColActive && !$is_table) {
6275			$this->breakpoints[$this->CurrCol][] = $this->y;
6276		} // *COLUMNS*
6277		// Print out each chunk
6278
6279		/* -- TABLES -- */
6280		if ($is_table) {
6281			$ipaddingL = 0;
6282			$ipaddingR = 0;
6283			$paddingL = 0;
6284			$paddingR = 0;
6285		} else {
6286			/* -- END TABLES -- */
6287			$ipaddingL = $this->blk[$this->blklvl]['padding_left'];
6288			$ipaddingR = $this->blk[$this->blklvl]['padding_right'];
6289			$paddingL = ($ipaddingL * Mpdf::SCALE);
6290			$paddingR = ($ipaddingR * Mpdf::SCALE);
6291			$this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];
6292			$this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];
6293
6294			// Added mPDF 3.0 Float DIV
6295			$fpaddingR = 0;
6296			$fpaddingL = 0;
6297			/* -- CSS-FLOAT -- */
6298			if (count($this->floatDivs)) {
6299				list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
6300				if ($r_exists) {
6301					$fpaddingR = $r_width;
6302				}
6303				if ($l_exists) {
6304					$fpaddingL = $l_width;
6305				}
6306			}
6307			/* -- END CSS-FLOAT -- */
6308
6309			$usey = $this->y + 0.002;
6310			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
6311				$usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
6312			}
6313			/* -- CSS-IMAGE-FLOAT -- */
6314			// If float exists at this level
6315			if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
6316				$fpaddingR += $this->floatmargins['R']['w'];
6317			}
6318			if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
6319				$fpaddingL += $this->floatmargins['L']['w'];
6320			}
6321			/* -- END CSS-IMAGE-FLOAT -- */
6322		} // *TABLES*
6323
6324
6325		$lineBox = [];
6326
6327		$this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);
6328
6329		if ($is_table && count($content) == 0) {
6330			$stackHeight = 0;
6331		}
6332
6333		if ($table_draft) {
6334			$this->y += $stackHeight;
6335			$this->objectbuffer = [];
6336			return 0;
6337		}
6338
6339		// While we're at it, check if contains cursive text
6340		// Change NBSP to SPACE.
6341		// Re-calculate contentWidth
6342		$contentWidth = 0;
6343
6344		foreach ($content as $k => $chunk) {
6345			$this->restoreFont($font[$k], false);
6346			if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
6347				// Soft Hyphens chr(173)
6348				if (!$this->usingCoreFont) {
6349					/* -- OTL -- */
6350					// mPDF 5.7.1
6351					if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
6352						$this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad");
6353						$this->otl->replaceSpace($chunk, $cOTLdata[$k]);
6354						$content[$k] = $chunk;
6355					} /* -- END OTL -- */ else {  // *OTL*
6356						$content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk);
6357						$content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);
6358					} // *OTL*
6359				} elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
6360					$content[$k] = $chunk = str_replace(chr(173), '', $chunk);
6361					$content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);
6362				}
6363				$contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE;
6364			} elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
6365				// LIST MARKERS	// mPDF 6  Lists
6366				if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
6367					// do nothing
6368				} else {
6369					$contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
6370				}
6371			}
6372		}
6373
6374		if (isset($font[count($font) - 1])) {
6375			$lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');
6376			$lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');
6377		} else {
6378			$lastfontreqstyle = null;
6379			$lastfontstyle = null;
6380		}
6381		if ($blockdir == 'ltr' && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic
6382			$lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE;
6383		} else {
6384			$lastitalic = 0;
6385		}
6386
6387		// Get PAGEBREAK TO TEST for height including the bottom border/padding
6388		$check_h = max($this->divheight, $stackHeight);
6389
6390		// This fixes a proven bug...
6391		if ($endofblock && $newblock && $blockstate == 0 && !$content) {
6392			$check_h = 0;
6393		}
6394		// but ? needs to fix potentially more widespread...
6395		// if (!$content) {  $check_h = 0; }
6396
6397		if ($this->blklvl > 0 && !$is_table) {
6398			if ($endofblock && $blockstate > 1) {
6399				if ($this->blk[$this->blklvl]['page_break_after_avoid']) {
6400					$check_h += $stackHeight;
6401				}
6402				$check_h += ($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);
6403			}
6404			if (($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0) || ($endofblock && $blockstate == 3 && $lineCount == 0)) {
6405				$check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);
6406			}
6407		}
6408
6409		// Force PAGE break if column height cannot take check-height
6410		if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {
6411			$this->SetCol($this->NbCol - 1);
6412		}
6413
6414		// Avoid just border/background-color moved on to next page
6415		if ($endofblock && $blockstate > 1 && !$content) {
6416			$buff = $this->margBuffer;
6417		} else {
6418			$buff = 0;
6419		}
6420
6421
6422		// PAGEBREAK
6423		if (!$is_table && ($this->y + $check_h) > ($this->PageBreakTrigger + $buff) and ! $this->InFooter and $this->AcceptPageBreak()) {
6424			$bak_x = $this->x; // Current X position
6425			// WORD SPACING
6426			$ws = $this->ws; // Word Spacing
6427			$charspacing = $this->charspacing; // Character Spacing
6428			$this->ResetSpacing();
6429
6430			$this->AddPage($this->CurOrientation);
6431
6432			$this->x = $bak_x;
6433			// Added to correct for OddEven Margins
6434			$currentx += $this->MarginCorrection;
6435			$this->x += $this->MarginCorrection;
6436
6437			// WORD SPACING
6438			$this->SetSpacing($charspacing, $ws);
6439		}
6440
6441
6442		/* -- COLUMNS -- */
6443		// COLS
6444		// COLUMN CHANGE
6445		if ($this->CurrCol != $oldcolumn) {
6446			$currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
6447			$this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
6448			$oldcolumn = $this->CurrCol;
6449		}
6450
6451
6452		if ($this->ColActive && !$is_table) {
6453			$this->breakpoints[$this->CurrCol][] = $this->y;
6454		}
6455		/* -- END COLUMNS -- */
6456
6457		// TOP MARGIN
6458		if ($newblock && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && $lineCount == 0 && !$is_table) {
6459			$this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
6460			if ($this->ColActive) {
6461				$this->breakpoints[$this->CurrCol][] = $this->y;
6462			} // *COLUMNS*
6463		}
6464
6465		if ($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0 && !$is_table) {
6466			$this->blk[$this->blklvl]['y0'] = $this->y;
6467			$this->blk[$this->blklvl]['startpage'] = $this->page;
6468			if ($this->blk[$this->blklvl]['float']) {
6469				$this->blk[$this->blklvl]['float_start_y'] = $this->y;
6470			}
6471			if ($this->ColActive) {
6472				$this->breakpoints[$this->CurrCol][] = $this->y;
6473			} // *COLUMNS*
6474		}
6475
6476		// Paragraph INDENT
6477		$WidthCorrection = 0;
6478		if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
6479			$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
6480			$WidthCorrection = ($ti * Mpdf::SCALE);
6481		}
6482
6483
6484		// PADDING and BORDER spacing/fill
6485		if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 0) && (!$is_table)) {
6486			// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
6487			$this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);
6488			if ($this->ColActive) {
6489				$this->breakpoints[$this->CurrCol][] = $this->y;
6490			} // *COLUMNS*
6491			$this->x = $currentx;
6492		}
6493
6494
6495		// Added mPDF 3.0 Float DIV
6496		$fpaddingR = 0;
6497		$fpaddingL = 0;
6498		/* -- CSS-FLOAT -- */
6499		if (count($this->floatDivs)) {
6500			list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
6501			if ($r_exists) {
6502				$fpaddingR = $r_width;
6503			}
6504			if ($l_exists) {
6505				$fpaddingL = $l_width;
6506			}
6507		}
6508		/* -- END CSS-FLOAT -- */
6509
6510		$usey = $this->y + 0.002;
6511		if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
6512			$usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
6513		}
6514		/* -- CSS-IMAGE-FLOAT -- */
6515		// If float exists at this level
6516		if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
6517			$fpaddingR += $this->floatmargins['R']['w'];
6518		}
6519		if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
6520			$fpaddingL += $this->floatmargins['L']['w'];
6521		}
6522		/* -- END CSS-IMAGE-FLOAT -- */
6523
6524
6525		if ($content) {
6526			// In FinishFlowing Block no lines are justified as it is always last line
6527			// but if CJKorphan has allowed content width to go over max width, use J charspacing to compress line
6528			// JUSTIFICATION J - NOT!
6529			$nb_carac = 0;
6530			$nb_spaces = 0;
6531			$jcharspacing = 0;
6532			$jkashida = 0;
6533			$jws = 0;
6534			$inclCursive = false;
6535			$dottab = false;
6536			foreach ($content as $k => $chunk) {
6537				if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
6538					$nb_carac += mb_strlen($chunk, $this->mb_enc);
6539					$nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);
6540					// mPDF 6
6541					// Use GPOS OTL
6542					$this->restoreFont($font[$k], false);
6543					if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
6544						if (isset($cOTLdata[$k]['group']) && $cOTLdata[$k]['group']) {
6545							$nb_marks = substr_count($cOTLdata[$k]['group'], 'M');
6546							$nb_carac -= $nb_marks;
6547						}
6548						if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) {
6549							$inclCursive = true;
6550						}
6551					}
6552				} else {
6553					$nb_carac ++;  // mPDF 6 allow spacing for inline object
6554					if ($this->objectbuffer[$k]['type'] == 'dottab') {
6555						$dottab = $this->objectbuffer[$k]['outdent'];
6556					}
6557				}
6558			}
6559
6560			// DIRECTIONALITY RTL
6561			$chunkorder = range(0, count($content) - 1); // mPDF 6
6562			/* -- OTL -- */
6563			// mPDF 6
6564			if ($blockdir == 'rtl' || $this->biDirectional) {
6565				$this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);
6566				// From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to
6567				// $this->objectbuffer and $font ($chunkorder contains the mapping)
6568			}
6569			/* -- END OTL -- */
6570
6571			// Remove any XAdvance from OTL data at end of line
6572			// And correct for XPlacement on last character
6573			// BIDI is applied
6574			foreach ($chunkorder as $aord => $k) {
6575				if (count($cOTLdata)) {
6576					$this->restoreFont($font[$k], false);
6577					// ...FinishFlowingBlock...
6578					if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line
6579						$nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character
6580						if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {
6581							if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {
6582								$w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
6583							} else {
6584								$w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
6585							}
6586							$w *= ($this->FontSize / 1000);
6587							$contentWidth -= $w * Mpdf::SCALE;
6588							$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;
6589							$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;
6590						}
6591
6592						// If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it
6593						if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {
6594							$w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
6595							$w *= ($this->FontSize / 1000);
6596							$contentWidth -= $w * Mpdf::SCALE;
6597							$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
6598							$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
6599						}
6600					}
6601				}
6602			}
6603
6604			// 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)
6605			// If "orphans" in fact is just a final space - ignore this
6606			$lastchar = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);
6607			if (preg_match("/[" . $this->CJKoverflow . "]/u", $lastchar)) {
6608				$CJKoverflow = true;
6609			} else {
6610				$CJKoverflow = false;
6611			}
6612			if ((((($contentWidth + $lastitalic) > $maxWidth) && ($content[(count($chunkorder) - 1)] != ' ') ) ||
6613				(!$endofblock && $align == 'J' && ($next == 'image' || $next == 'select' || $next == 'input' || $next == 'textarea' || ($next == 'br' && $this->justifyB4br)))) && !($CJKoverflow && $this->allowCJKoverflow)) {
6614				// WORD SPACING
6615				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);
6616			} /* -- CJK-FONTS -- */ elseif ($this->checkCJK && $align == 'J' && $CJKoverflow && $this->allowCJKoverflow && $this->CJKforceend) {
6617				// force-end overhang
6618				$hanger = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);
6619				if (preg_match("/[" . $this->CJKoverflow . "]/u", $hanger)) {
6620					$content[(count($chunkorder) - 1)] = mb_substr($content[(count($chunkorder) - 1)], 0, mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, $this->mb_enc);
6621					$this->restoreFont($font[$chunkorder[count($chunkorder) - 1]], false);
6622					$contentWidth -= $this->GetStringWidth($hanger) * Mpdf::SCALE;
6623					$nb_carac -= 1;
6624					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);
6625				}
6626			} /* -- END CJK-FONTS -- */
6627
6628			// Check if will fit at word/char spacing of previous line - if so continue it
6629			// but only allow a maximum of $this->jSmaxWordLast and $this->jSmaxCharLast
6630			elseif ($contentWidth < ($maxWidth - $lastitalic - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE))) && !$this->fixedlSpacing) {
6631				if ($this->ws > $this->jSmaxWordLast) {
6632					$jws = $this->jSmaxWordLast;
6633				}
6634				if ($this->charspacing > $this->jSmaxCharLast) {
6635					$jcharspacing = $this->jSmaxCharLast;
6636				}
6637				$check = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) - ( $jcharspacing * $nb_carac) - ( $jws * $nb_spaces);
6638				if ($check <= 0) {
6639					$jcharspacing = 0;
6640					$jws = 0;
6641				}
6642			}
6643
6644			$empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) );
6645
6646
6647			$empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1
6648			$empty -= ($jws * $nb_spaces);
6649			$empty -= ($jkashida);
6650
6651			$empty /= Mpdf::SCALE;
6652
6653			if (!$is_table) {
6654				$this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin'] - $empty));
6655				$this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $empty));
6656			}
6657
6658			$arraysize = count($chunkorder);
6659
6660			$margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );
6661
6662			if (!$is_table) {
6663				$this->DivLn($stackHeight, $this->blklvl, false);
6664			} // false -> don't advance y
6665
6666			$this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;
6667			if ($dottab !== false && $blockdir == 'rtl') {
6668				$this->x -= $dottab;
6669			} elseif ($align == 'R') {
6670				$this->x += $empty;
6671			} elseif ($align == 'J' && $blockdir == 'rtl') {
6672				$this->x += $empty;
6673			} elseif ($align == 'C') {
6674				$this->x += ($empty / 2);
6675			}
6676
6677			// Paragraph INDENT
6678			$WidthCorrection = 0;
6679			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
6680				$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
6681				if ($blockdir != 'rtl') {
6682					$this->x += $ti;
6683				} // mPDF 6
6684			}
6685
6686			foreach ($chunkorder as $aord => $k) { // mPDF 5.7
6687				$chunk = $content[$aord];
6688				if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
6689					$xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];
6690					$this->objectbuffer[$k]['OUTER-X'] += $xadj;
6691					$this->objectbuffer[$k]['BORDER-X'] += $xadj;
6692					$this->objectbuffer[$k]['INNER-X'] += $xadj;
6693
6694					if ($this->objectbuffer[$k]['type'] == 'listmarker') {
6695						$this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin
6696					}
6697					$yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];
6698					if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
6699						$this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin
6700					}
6701					if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB
6702						$yadj += $lineBox[$k]['top'];
6703					}
6704					$this->objectbuffer[$k]['OUTER-Y'] += $yadj;
6705					$this->objectbuffer[$k]['BORDER-Y'] += $yadj;
6706					$this->objectbuffer[$k]['INNER-Y'] += $yadj;
6707				}
6708
6709				$this->restoreFont($font[$k]);  // mPDF 5.7
6710
6711				if ($is_table && substr($align, 0, 1) == 'D' && $aord == 0) {
6712					$dp = $this->decimal_align[substr($align, 0, 2)];
6713					$s = preg_split('/' . preg_quote($dp, '/') . '/', $content[0], 2);  // ? needs to be /u if not core
6714					$s0 = $this->GetStringWidth($s[0], false);
6715					$this->x += ($this->decimal_offset - $s0);
6716				}
6717
6718				$this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws);
6719				$this->fixedlSpacing = false;
6720				$this->minwSpacing = 0;
6721
6722				$save_vis = $this->visibility;
6723				if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {
6724					$this->SetVisibility($this->textparam['visibility']);
6725				}
6726
6727				// *********** SPAN BACKGROUND COLOR ***************** //
6728				if (isset($this->spanbgcolor) && $this->spanbgcolor) {
6729					$cor = $this->spanbgcolorarray;
6730					$this->SetFColor($cor);
6731					$save_fill = $fill;
6732					$spanfill = 1;
6733					$fill = 1;
6734				}
6735				if (!empty($this->spanborddet)) {
6736					if (strpos($contentB[$k], 'L') !== false && isset($this->spanborddet['L'])) {
6737						$this->x += $this->spanborddet['L']['w'];
6738					}
6739					if (strpos($contentB[$k], 'L') === false) {
6740						$this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;
6741					}
6742					if (strpos($contentB[$k], 'R') === false) {
6743						$this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;
6744					}
6745				}
6746				// WORD SPACING
6747				// mPDF 5.7.1
6748				$stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar);
6749				$nch = mb_strlen($chunk, $this->mb_enc);
6750				// Use GPOS OTL
6751				if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
6752					if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
6753						$nch -= substr_count($cOTLdata[$aord]['group'], 'M');
6754					}
6755				}
6756				$stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE );
6757
6758				$stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE );
6759
6760				if (isset($this->objectbuffer[$k])) {
6761					if ($this->objectbuffer[$k]['type'] == 'dottab') {
6762						$this->objectbuffer[$k]['OUTER-WIDTH'] +=$empty;
6763						$this->objectbuffer[$k]['OUTER-WIDTH'] +=$this->objectbuffer[$k]['outdent'];
6764					}
6765					// LIST MARKERS	// mPDF 6  Lists
6766					if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
6767						// do nothing
6768					} else {
6769						$stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];
6770					}
6771				}
6772
6773				if ($stringWidth == 0) {
6774					$stringWidth = 0.000001;
6775				}
6776				if ($aord == $arraysize - 1) { // mPDF 5.7
6777					// mPDF 5.7.1
6778					if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {
6779						// force-end overhang
6780						$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
6781						$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
6782					} else {
6783						$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
6784					}
6785				} else {
6786					$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
6787				}
6788
6789
6790				if (!empty($this->spanborddet)) {
6791					if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) {
6792						$this->x += $this->spanborddet['R']['w'];
6793					}
6794				}
6795				// *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //
6796				if (isset($spanfill) && $spanfill) {
6797					$fill = $save_fill;
6798					$spanfill = 0;
6799					if ($fill) {
6800						$this->SetFColor($bcor);
6801					}
6802				}
6803				if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {
6804					$this->SetVisibility($save_vis);
6805				}
6806			}
6807
6808			$this->printobjectbuffer($is_table, $blockdir);
6809			$this->objectbuffer = [];
6810			$this->ResetSpacing();
6811		} // END IF CONTENT
6812
6813		/* -- CSS-IMAGE-FLOAT -- */
6814		// Update values if set to skipline
6815		if ($this->floatmargins) {
6816			$this->_advanceFloatMargins();
6817		}
6818
6819
6820		if ($endofblock && $blockstate > 1) {
6821			// If float exists at this level
6822			if (isset($this->floatmargins['R']['y1'])) {
6823				$fry1 = $this->floatmargins['R']['y1'];
6824			} else {
6825				$fry1 = 0;
6826			}
6827			if (isset($this->floatmargins['L']['y1'])) {
6828				$fly1 = $this->floatmargins['L']['y1'];
6829			} else {
6830				$fly1 = 0;
6831			}
6832			if ($this->y < $fry1 || $this->y < $fly1) {
6833				$drop = max($fry1, $fly1) - $this->y;
6834				$this->DivLn($drop);
6835				$this->x = $currentx;
6836			}
6837		}
6838		/* -- END CSS-IMAGE-FLOAT -- */
6839
6840
6841		// PADDING and BORDER spacing/fill
6842		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)) {
6843			// If CSS height set, extend bottom - if on same page as block started, and CSS HEIGHT > actual height,
6844			// and does not force pagebreak
6845			$extra = 0;
6846			if (isset($this->blk[$this->blklvl]['css_set_height']) && $this->blk[$this->blklvl]['css_set_height'] && $this->blk[$this->blklvl]['startpage'] == $this->page) {
6847				// predicted height
6848				$h1 = ($this->y - $this->blk[$this->blklvl]['y0']) + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];
6849				if ($h1 < ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top'])) {
6850					$extra = ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top']) - $h1;
6851				}
6852				if ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra > $this->PageBreakTrigger) {
6853					$extra = $this->PageBreakTrigger - ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);
6854				}
6855			}
6856
6857			// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
6858			$this->DivLn($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra, -3, true, false, 2);
6859			$this->x = $currentx;
6860
6861			if ($this->ColActive) {
6862				$this->breakpoints[$this->CurrCol][] = $this->y;
6863			} // *COLUMNS*
6864		}
6865
6866		// SET Bottom y1 of block (used for painting borders)
6867		if (($endofblock) && ($blockstate > 1) && (!$is_table)) {
6868			$this->blk[$this->blklvl]['y1'] = $this->y;
6869		}
6870
6871		// BOTTOM MARGIN
6872		if (($endofblock) && ($blockstate > 1) && ($this->blk[$this->blklvl]['margin_bottom']) && (!$is_table)) {
6873			if ($this->y + $this->blk[$this->blklvl]['margin_bottom'] < $this->PageBreakTrigger and ! $this->InFooter) {
6874				$this->DivLn($this->blk[$this->blklvl]['margin_bottom'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
6875				if ($this->ColActive) {
6876					$this->breakpoints[$this->CurrCol][] = $this->y;
6877				} // *COLUMNS*
6878			}
6879		}
6880
6881		// Reset lineheight
6882		$stackHeight = $this->divheight;
6883	}
6884
6885	function printobjectbuffer($is_table = false, $blockdir = false)
6886	{
6887		if (!$blockdir) {
6888			$blockdir = $this->directionality;
6889		}
6890
6891		if ($is_table && $this->shrin_k > 1) {
6892			$k = $this->shrin_k;
6893		} else {
6894			$k = 1;
6895		}
6896
6897		$save_y = $this->y;
6898		$save_x = $this->x;
6899
6900		$save_currentfontfamily = $this->FontFamily;
6901		$save_currentfontsize = $this->FontSizePt;
6902		$save_currentfontstyle = $this->FontStyle;
6903
6904		if ($blockdir == 'rtl') {
6905			$rtlalign = 'R';
6906		} else {
6907			$rtlalign = 'L';
6908		}
6909
6910		foreach ($this->objectbuffer as $ib => $objattr) {
6911
6912			if ($objattr['type'] == 'bookmark' || $objattr['type'] == 'indexentry' || $objattr['type'] == 'toc') {
6913				$x = $objattr['OUTER-X'];
6914				$y = $objattr['OUTER-Y'];
6915				$this->y = $y - $this->FontSize / 2;
6916				$this->x = $x;
6917				if ($objattr['type'] == 'bookmark') {
6918					$this->Bookmark($objattr['CONTENT'], $objattr['bklevel'], $y - $this->FontSize);
6919				} // *BOOKMARKS*
6920				if ($objattr['type'] == 'indexentry') {
6921					$this->IndexEntry($objattr['CONTENT']);
6922				} // *INDEX*
6923				if ($objattr['type'] == 'toc') {
6924					$this->TOC_Entry($objattr['CONTENT'], $objattr['toclevel'], (isset($objattr['toc_id']) ? $objattr['toc_id'] : ''));
6925				} // *TOC*
6926			} /* -- ANNOTATIONS -- */ elseif ($objattr['type'] == 'annot') {
6927				if ($objattr['POS-X']) {
6928					$x = $objattr['POS-X'];
6929				} elseif ($this->annotMargin <> 0) {
6930					$x = -$objattr['OUTER-X'];
6931				} else {
6932					$x = $objattr['OUTER-X'];
6933				}
6934				if ($objattr['POS-Y']) {
6935					$y = $objattr['POS-Y'];
6936				} else {
6937					$y = $objattr['OUTER-Y'] - $this->FontSize / 2;
6938				}
6939				// Create a dummy entry in the _out/columnBuffer with position sensitive data,
6940				// linking $y-1 in the Columnbuffer with entry in $this->columnAnnots
6941				// and when columns are split in length will not break annotation from current line
6942				$this->y = $y - 1;
6943				$this->x = $x - 1;
6944				$this->Line($x - 1, $y - 1, $x - 1, $y - 1);
6945				$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'] : ''));
6946			} /* -- END ANNOTATIONS -- */ else {
6947				$y = $objattr['OUTER-Y'];
6948				$x = $objattr['OUTER-X'];
6949				$w = $objattr['OUTER-WIDTH'];
6950				$h = $objattr['OUTER-HEIGHT'];
6951				if (isset($objattr['text'])) {
6952					$texto = $objattr['text'];
6953				}
6954				$this->y = $y;
6955				$this->x = $x;
6956				if (isset($objattr['fontfamily'])) {
6957					$this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);
6958				}
6959			}
6960
6961			// HR
6962			if ($objattr['type'] == 'hr') {
6963				$this->SetDColor($objattr['color']);
6964				switch ($objattr['align']) {
6965					case 'C':
6966						$empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];
6967						$empty /= 2;
6968						$x += $empty;
6969						break;
6970					case 'R':
6971						$empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];
6972						$x += $empty;
6973						break;
6974				}
6975				$oldlinewidth = $this->LineWidth;
6976				$this->SetLineWidth($objattr['linewidth'] / $k);
6977				$this->y += ($objattr['linewidth'] / 2) + $objattr['margin_top'] / $k;
6978				$this->Line($x, $this->y, $x + $objattr['INNER-WIDTH'], $this->y);
6979				$this->SetLineWidth($oldlinewidth);
6980				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
6981			}
6982			// IMAGE
6983			if ($objattr['type'] == 'image') {
6984				// mPDF 5.7.3 TRANSFORMS
6985				if (isset($objattr['transform'])) {
6986					$this->_out("\n" . '% BTR'); // Begin Transform
6987				}
6988				if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {
6989					$this->BeginLayer($objattr['z-index']);
6990				}
6991				if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {
6992					$this->SetVisibility($objattr['visibility']);
6993				}
6994				if (isset($objattr['opacity'])) {
6995					$this->SetAlpha($objattr['opacity']);
6996				}
6997
6998				$obiw = $objattr['INNER-WIDTH'];
6999				$obih = $objattr['INNER-HEIGHT'];
7000
7001				$sx = $objattr['orig_w'] ? ($objattr['INNER-WIDTH'] * Mpdf::SCALE / $objattr['orig_w']) : INF;
7002				$sy = $objattr['orig_h'] ? ($objattr['INNER-HEIGHT'] * Mpdf::SCALE / $objattr['orig_h']) : INF;
7003
7004				$rotate = 0;
7005				if (isset($objattr['ROTATE'])) {
7006					$rotate = $objattr['ROTATE'];
7007				}
7008
7009				if ($rotate == 90) {
7010					// Clockwise
7011					$obiw = $objattr['INNER-HEIGHT'];
7012					$obih = $objattr['INNER-WIDTH'];
7013					$tr = $this->transformTranslate(0, -$objattr['INNER-WIDTH'], true);
7014					$tr .= ' ' . $this->transformRotate(90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);
7015					$sx = $obiw * Mpdf::SCALE / $objattr['orig_h'];
7016					$sy = $obih * Mpdf::SCALE / $objattr['orig_w'];
7017				} elseif ($rotate == -90 || $rotate == 270) {
7018					// AntiClockwise
7019					$obiw = $objattr['INNER-HEIGHT'];
7020					$obih = $objattr['INNER-WIDTH'];
7021					$tr = $this->transformTranslate($objattr['INNER-WIDTH'], ($objattr['INNER-HEIGHT'] - $objattr['INNER-WIDTH']), true);
7022					$tr .= ' ' . $this->transformRotate(-90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);
7023					$sx = $obiw * Mpdf::SCALE / $objattr['orig_h'];
7024					$sy = $obih * Mpdf::SCALE / $objattr['orig_w'];
7025				} elseif ($rotate == 180) {
7026					// Mirror
7027					$tr = $this->transformTranslate($objattr['INNER-WIDTH'], -$objattr['INNER-HEIGHT'], true);
7028					$tr .= ' ' . $this->transformRotate(180, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-HEIGHT']), true);
7029				} else {
7030					$tr = '';
7031				}
7032				$tr = trim($tr);
7033				if ($tr) {
7034					$tr .= ' ';
7035				}
7036				$gradmask = '';
7037
7038				// mPDF 5.7.3 TRANSFORMS
7039				$tr2 = '';
7040				if (isset($objattr['transform'])) {
7041					$maxsize_x = $w;
7042					$maxsize_y = $h;
7043					$cx = $x + $w / 2;
7044					$cy = $y + $h / 2;
7045					preg_match_all('/(translatex|translatey|translate|scalex|scaley|scale|rotate|skewX|skewY|skew)\((.*?)\)/is', $objattr['transform'], $m);
7046					if (count($m[0])) {
7047						for ($i = 0; $i < count($m[0]); $i++) {
7048							$c = strtolower($m[1][$i]);
7049							$v = trim($m[2][$i]);
7050							$vv = preg_split('/[ ,]+/', $v);
7051							if ($c == 'translate' && count($vv)) {
7052								$translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false);
7053								if (count($vv) == 2) {
7054									$translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false);
7055								} else {
7056									$translate_y = 0;
7057								}
7058								$tr2 .= $this->transformTranslate($translate_x, $translate_y, true) . ' ';
7059							} elseif ($c == 'translatex' && count($vv)) {
7060								$translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false);
7061								$tr2 .= $this->transformTranslate($translate_x, 0, true) . ' ';
7062							} elseif ($c == 'translatey' && count($vv)) {
7063								$translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false);
7064								$tr2 .= $this->transformTranslate(0, $translate_y, true) . ' ';
7065							} elseif ($c == 'scale' && count($vv)) {
7066								$scale_x = $vv[0] * 100;
7067								if (count($vv) == 2) {
7068									$scale_y = $vv[1] * 100;
7069								} else {
7070									$scale_y = $scale_x;
7071								}
7072								$tr2 .= $this->transformScale($scale_x, $scale_y, $cx, $cy, true) . ' ';
7073							} elseif ($c == 'scalex' && count($vv)) {
7074								$scale_x = $vv[0] * 100;
7075								$tr2 .= $this->transformScale($scale_x, 0, $cx, $cy, true) . ' ';
7076							} elseif ($c == 'scaley' && count($vv)) {
7077								$scale_y = $vv[1] * 100;
7078								$tr2 .= $this->transformScale(0, $scale_y, $cx, $cy, true) . ' ';
7079							} elseif ($c == 'skew' && count($vv)) {
7080								$angle_x = $this->ConvertAngle($vv[0], false);
7081								if (count($vv) == 2) {
7082									$angle_y = $this->ConvertAngle($vv[1], false);
7083								} else {
7084									$angle_y = 0;
7085								}
7086								$tr2 .= $this->transformSkew($angle_x, $angle_y, $cx, $cy, true) . ' ';
7087							} elseif ($c == 'skewx' && count($vv)) {
7088								$angle = $this->ConvertAngle($vv[0], false);
7089								$tr2 .= $this->transformSkew($angle, 0, $cx, $cy, true) . ' ';
7090							} elseif ($c == 'skewy' && count($vv)) {
7091								$angle = $this->ConvertAngle($vv[0], false);
7092								$tr2 .= $this->transformSkew(0, $angle, $cx, $cy, true) . ' ';
7093							} elseif ($c == 'rotate' && count($vv)) {
7094								$angle = $this->ConvertAngle($vv[0]);
7095								$tr2 .= $this->transformRotate($angle, $cx, $cy, true) . ' ';
7096							}
7097						}
7098					}
7099				}
7100
7101				// LIST MARKERS (Images)	// mPDF 6  Lists
7102				if (isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {
7103					$mw = $objattr['OUTER-WIDTH'];
7104					// NB If change marker-offset, also need to alter in function _getListMarkerWidth
7105					$adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
7106					if ($objattr['dir'] == 'rtl') {
7107						$objattr['INNER-X'] += $adjx;
7108					} else {
7109						$objattr['INNER-X'] -= $adjx;
7110						$objattr['INNER-X'] -= $mw;
7111					}
7112				}
7113				// mPDF 5.7.3 TRANSFORMS / BACKGROUND COLOR
7114				// Transform also affects image background
7115				if ($tr2) {
7116					$this->_out('q ' . $tr2 . ' ');
7117				}
7118				if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
7119					$bgcol = $objattr['bgcolor'];
7120					$this->SetFColor($bgcol);
7121					$this->Rect($x, $y, $w, $h, 'F');
7122					$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
7123				}
7124				if ($tr2) {
7125					$this->_out('Q');
7126				}
7127
7128				/* -- BACKGROUNDS -- */
7129				if (isset($objattr['GRADIENT-MASK'])) {
7130					$g = $this->gradient->parseMozGradient($objattr['GRADIENT-MASK']);
7131					if ($g) {
7132						$dummy = $this->gradient->Gradient($objattr['INNER-X'], $objattr['INNER-Y'], $obiw, $obih, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true, true);
7133						$gradmask = '/TGS' . count($this->gradients) . ' gs ';
7134					}
7135				}
7136				/* -- END BACKGROUNDS -- */
7137				/* -- IMAGES-WMF -- */
7138				if (isset($objattr['itype']) && $objattr['itype'] == 'wmf') {
7139					$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
7140				} else { 				/* -- END IMAGES-WMF -- */
7141					if (isset($objattr['itype']) && $objattr['itype'] == 'svg') {
7142						$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
7143					} else {
7144						$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
7145					}
7146				}
7147				$this->_out($outstring);
7148				// LINK
7149				if (isset($objattr['link'])) {
7150					$this->Link($objattr['INNER-X'], $objattr['INNER-Y'], $objattr['INNER-WIDTH'], $objattr['INNER-HEIGHT'], $objattr['link']);
7151				}
7152				if (isset($objattr['opacity'])) {
7153					$this->SetAlpha(1);
7154				}
7155
7156				// mPDF 5.7.3 TRANSFORMS
7157				// Transform also affects image borders
7158				if ($tr2) {
7159					$this->_out('q ' . $tr2 . ' ');
7160				}
7161				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)) {
7162					$this->PaintImgBorder($objattr, $is_table);
7163				}
7164				if ($tr2) {
7165					$this->_out('Q');
7166				}
7167
7168				if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {
7169					$this->SetVisibility('visible');
7170				}
7171				if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {
7172					$this->EndLayer();
7173				}
7174				// mPDF 5.7.3 TRANSFORMS
7175				if (isset($objattr['transform'])) {
7176					$this->_out("\n" . '% ETR'); // End Transform
7177				}
7178			}
7179
7180			if ($objattr['type'] === 'barcode') {
7181
7182				$bgcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
7183
7184				if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
7185					$bgcol = $objattr['bgcolor'];
7186				}
7187
7188				$col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
7189
7190				if (isset($objattr['color']) && $objattr['color']) {
7191					$col = $objattr['color'];
7192				}
7193
7194				$this->SetFColor($bgcol);
7195				$this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');
7196				$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
7197
7198				if (isset($objattr['BORDER-WIDTH'])) {
7199					$this->PaintImgBorder($objattr, $is_table);
7200				}
7201
7202				$barcodeTypes = ['EAN13', 'ISBN', 'ISSN', 'UPCA', 'UPCE', 'EAN8'];
7203				if (in_array($objattr['btype'], $barcodeTypes, true)) {
7204
7205					$this->WriteBarcode(
7206						$objattr['code'],
7207						$objattr['showtext'],
7208						$objattr['INNER-X'],
7209						$objattr['INNER-Y'],
7210						$objattr['bsize'],
7211						0,
7212						0,
7213						0,
7214						0,
7215						0,
7216						$objattr['bheight'],
7217						$bgcol,
7218						$col,
7219						$objattr['btype'],
7220						$objattr['bsupp'],
7221						(isset($objattr['bsupp_code']) ? $objattr['bsupp_code'] : ''),
7222						$k
7223					);
7224
7225				} elseif ($objattr['btype'] === 'QR') {
7226
7227					$barcodeContent = str_replace('\r\n', "\r\n", $objattr['code']);
7228					$barcodeContent = str_replace('\n', "\n", $barcodeContent);
7229
7230					$this->qrcode = new QrCode\QrCode($barcodeContent, $objattr['errorlevel']);
7231					if ($objattr['disableborder']) {
7232						$this->qrcode->disableBorder();
7233					}
7234
7235					$bgColor = [255, 255, 255];
7236					if ($objattr['bgcolor']) {
7237						$bgColor = array_map(
7238							function ($col) {
7239								return intval(255 * floatval($col));
7240							},
7241							explode(" ", $this->SetColor($objattr['bgcolor'], 'CodeOnly'))
7242						);
7243					}
7244					$color = [0, 0, 0];
7245					if ($objattr['color']) {
7246						$color = array_map(
7247							function ($col) {
7248								return intval(255 * floatval($col));
7249							},
7250							explode(" ", $this->SetColor($objattr['color'], 'CodeOnly'))
7251						);
7252					}
7253
7254					$this->qrcode->displayFPDF(
7255						$this,
7256						$objattr['INNER-X'],
7257						$objattr['INNER-Y'],
7258						$objattr['bsize'] * 25,
7259						$bgColor,
7260						$color
7261					);
7262
7263				} else {
7264
7265					$this->WriteBarcode2(
7266						$objattr['code'],
7267						$objattr['INNER-X'],
7268						$objattr['INNER-Y'],
7269						$objattr['bsize'],
7270						$objattr['bheight'],
7271						$bgcol,
7272						$col,
7273						$objattr['btype'],
7274						$objattr['pr_ratio'],
7275						$k
7276					);
7277
7278				}
7279			}
7280
7281			// TEXT CIRCLE
7282			if ($objattr['type'] == 'textcircle') {
7283				$bgcol = '';
7284				if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
7285					$bgcol = $objattr['bgcolor'];
7286				}
7287				$col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
7288				if (isset($objattr['color']) && $objattr['color']) {
7289					$col = $objattr['color'];
7290				}
7291				$this->SetTColor($col);
7292				$this->SetFColor($bgcol);
7293				if ($bgcol) {
7294					$this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');
7295				}
7296				$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
7297				if (isset($objattr['BORDER-WIDTH'])) {
7298					$this->PaintImgBorder($objattr, $is_table);
7299				}
7300				if (empty($this->directWrite)) {
7301					$this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
7302				}
7303				if (isset($objattr['top-text'])) {
7304					$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'] : ''));
7305				}
7306				if (isset($objattr['bottom-text'])) {
7307					$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'] : ''));
7308				}
7309			}
7310
7311			$this->ResetSpacing();
7312
7313			// LIST MARKERS (Text or bullets)	// mPDF 6  Lists
7314			if ($objattr['type'] == 'listmarker') {
7315				if (isset($objattr['fontfamily'])) {
7316					$this->SetFont($objattr['fontfamily'], $objattr['fontstyle'], $objattr['fontsizept']);
7317				}
7318				$col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
7319				if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {
7320					$col = $objattr['colorarray'];
7321				}
7322
7323				if (isset($objattr['bullet']) && $objattr['bullet']) { // Used for position "outside" only
7324					$type = $objattr['bullet'];
7325					$size = $objattr['size'];
7326
7327					if ($objattr['listmarkerposition'] == 'inside') {
7328						$adjx = $size / 2;
7329						if ($objattr['dir'] == 'rtl') {
7330							$adjx += $objattr['offset'];
7331						}
7332						$this->x += $adjx;
7333					} else {
7334						$adjx = $objattr['offset'];
7335						$adjx += $size / 2;
7336						if ($objattr['dir'] == 'rtl') {
7337							$this->x += $adjx;
7338						} else {
7339							$this->x -= $adjx;
7340						}
7341					}
7342
7343					$yadj = $objattr['lineBox']['glyphYorigin'];
7344					if (isset($this->CurrentFont['desc']['XHeight']) && $this->CurrentFont['desc']['XHeight']) {
7345						$xh = $this->CurrentFont['desc']['XHeight'];
7346					} else {
7347						$xh = 500;
7348					}
7349					$yadj -= ($this->FontSize * $xh / 1000) * 0.625; // Vertical height of bullet (centre) from baseline= XHeight * 0.625
7350					$this->y += $yadj;
7351
7352					$this->_printListBullet($this->x, $this->y, $size, $type, $col);
7353				} else {
7354					$this->SetTColor($col);
7355					$w = $this->GetStringWidth($texto);
7356					// NB If change marker-offset, also need to alter in function _getListMarkerWidth
7357					$adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
7358					if ($objattr['dir'] == 'rtl') {
7359						$align = 'L';
7360						$this->x += $adjx;
7361					} else {
7362						// Use these lines to set as marker-offset, right-aligned - default
7363						$align = 'R';
7364						$this->x -= $adjx;
7365						$this->x -= $w;
7366					}
7367					$this->Cell($w, $this->FontSize, $texto, 0, 0, $align, 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']);
7368					$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
7369				}
7370			}
7371
7372			// DOT-TAB
7373			if ($objattr['type'] == 'dottab') {
7374				if (isset($objattr['fontfamily'])) {
7375					$this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);
7376				}
7377				$sp = $this->GetStringWidth(' ');
7378				$nb = floor(($w - 2 * $sp) / $this->GetStringWidth('.'));
7379				if ($nb > 0) {
7380					$dots = ' ' . str_repeat('.', $nb) . ' ';
7381				} else {
7382					$dots = ' ';
7383				}
7384				$col = $this->colorConverter->convert(0, $this->PDFAXwarnings);
7385				if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {
7386					$col = $objattr['colorarray'];
7387				}
7388				$this->SetTColor($col);
7389				$save_dh = $this->divheight;
7390				$save_sbd = $this->spanborddet;
7391				$save_textvar = $this->textvar; // mPDF 5.7.1
7392				$this->spanborddet = '';
7393				$this->divheight = 0;
7394				$this->textvar = 0x00; // mPDF 5.7.1
7395
7396				$this->Cell($w, $h, $dots, 0, 0, 'C', 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']); // mPDF 6 DOTTAB
7397				$this->spanborddet = $save_sbd;
7398				$this->textvar = $save_textvar; // mPDF 5.7.1
7399				$this->divheight = $save_dh;
7400				$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
7401			}
7402
7403			/* -- FORMS -- */
7404			// TEXT/PASSWORD INPUT
7405			if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD')) {
7406				$this->form->print_ob_text($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
7407			}
7408
7409			// TEXTAREA
7410			if ($objattr['type'] == 'textarea') {
7411				$this->form->print_ob_textarea($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
7412			}
7413
7414			// SELECT
7415			if ($objattr['type'] == 'select') {
7416				$this->form->print_ob_select($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
7417			}
7418
7419
7420			// INPUT/BUTTON as IMAGE
7421			if ($objattr['type'] == 'input' && $objattr['subtype'] == 'IMAGE') {
7422				$this->form->print_ob_imageinput($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $is_table);
7423			}
7424
7425			// BUTTON
7426			if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'SUBMIT' || $objattr['subtype'] == 'RESET' || $objattr['subtype'] == 'BUTTON')) {
7427				$this->form->print_ob_button($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
7428			}
7429
7430			// CHECKBOX
7431			if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'CHECKBOX')) {
7432				$this->form->print_ob_checkbox($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);
7433			}
7434			// RADIO
7435			if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'RADIO')) {
7436				$this->form->print_ob_radio($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);
7437			}
7438			/* -- END FORMS -- */
7439		}
7440
7441		$this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);
7442
7443		$this->y = $save_y;
7444		$this->x = $save_x;
7445
7446		unset($content);
7447	}
7448
7449	function _printListBullet($x, $y, $size, $type, $color)
7450	{
7451		// x and y are the centre of the bullet; size is the width and/or height in mm
7452		$fcol = $this->SetTColor($color, true);
7453		$lcol = strtoupper($fcol); // change 0 0 0 rg to 0 0 0 RG
7454		$this->_out(sprintf('q %s %s', $lcol, $fcol));
7455		$this->_out('0 j 0 J [] 0 d');
7456		if ($type == 'square') {
7457			$size *= 0.85; // Smaller to appear the same size as circle/disc
7458			$this->_out(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));
7459		} elseif ($type == 'disc') {
7460			$this->Circle($x, $y, $size / 2, 'F'); // Fill
7461		} elseif ($type == 'circle') {
7462			$lw = $size / 12; // Line width
7463			$this->_out(sprintf('%.3F w ', $lw * Mpdf::SCALE));
7464			$this->Circle($x, $y, $size / 2 - $lw / 2, 'S'); // Stroke
7465		}
7466		$this->_out('Q');
7467	}
7468
7469	// mPDF 6
7470	// Get previous character and move pointers
7471	function _moveToPrevChar(&$contentctr, &$charctr, $content)
7472	{
7473		$lastchar = false;
7474		$charctr--;
7475		while ($charctr < 0) { // go back to previous $content[]
7476			$contentctr--;
7477			if ($contentctr < 0) {
7478				return false;
7479			}
7480			if ($this->usingCoreFont) {
7481				$charctr = strlen($content[$contentctr]) - 1;
7482			} else {
7483				$charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;
7484			}
7485		}
7486		if ($this->usingCoreFont) {
7487			$lastchar = $content[$contentctr][$charctr];
7488		} else {
7489			$lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);
7490		}
7491		return $lastchar;
7492	}
7493
7494	// Get previous character
7495	function _getPrevChar($contentctr, $charctr, $content)
7496	{
7497		$lastchar = false;
7498		$charctr--;
7499		while ($charctr < 0) { // go back to previous $content[]
7500			$contentctr--;
7501			if ($contentctr < 0) {
7502				return false;
7503			}
7504			if ($this->usingCoreFont) {
7505				$charctr = strlen($content[$contentctr]) - 1;
7506			} else {
7507				$charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;
7508			}
7509		}
7510		if ($this->usingCoreFont) {
7511			$lastchar = $content[$contentctr][$charctr];
7512		} else {
7513			$lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);
7514		}
7515		return $lastchar;
7516	}
7517
7518	function WriteFlowingBlock($s, $sOTLdata)
7519	{
7520	// mPDF 5.7.1
7521		$currentx = $this->x;
7522		$is_table = $this->flowingBlockAttr['is_table'];
7523		$table_draft = $this->flowingBlockAttr['table_draft'];
7524		// width of all the content so far in points
7525		$contentWidth = & $this->flowingBlockAttr['contentWidth'];
7526		// cell width in points
7527		$maxWidth = & $this->flowingBlockAttr['width'];
7528		$lineCount = & $this->flowingBlockAttr['lineCount'];
7529		// line height in user units
7530		$stackHeight = & $this->flowingBlockAttr['height'];
7531		$align = & $this->flowingBlockAttr['align'];
7532		$content = & $this->flowingBlockAttr['content'];
7533		$contentB = & $this->flowingBlockAttr['contentB'];
7534		$font = & $this->flowingBlockAttr['font'];
7535		$valign = & $this->flowingBlockAttr['valign'];
7536		$blockstate = $this->flowingBlockAttr['blockstate'];
7537		$cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1
7538
7539		$newblock = $this->flowingBlockAttr['newblock'];
7540		$blockdir = $this->flowingBlockAttr['blockdir'];
7541
7542		// *********** BLOCK BACKGROUND COLOR ***************** //
7543		if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {
7544			$fill = 0;
7545		} else {
7546			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
7547			$fill = 0;
7548		}
7549		$font[] = $this->saveFont();
7550		$content[] = '';
7551		$contentB[] = '';
7552		$cOTLdata[] = $sOTLdata; // mPDF 5.7.1
7553		$currContent = & $content[count($content) - 1];
7554
7555		$CJKoverflow = false;
7556		$Oikomi = false; // mPDF 6
7557		$hanger = '';
7558
7559		// COLS
7560		$oldcolumn = $this->CurrCol;
7561		if ($this->ColActive && !$is_table) {
7562			$this->breakpoints[$this->CurrCol][] = $this->y;
7563		} // *COLUMNS*
7564
7565		/* -- TABLES -- */
7566		if ($is_table) {
7567			$ipaddingL = 0;
7568			$ipaddingR = 0;
7569			$paddingL = 0;
7570			$paddingR = 0;
7571			$cpaddingadjustL = 0;
7572			$cpaddingadjustR = 0;
7573			// Added mPDF 3.0
7574			$fpaddingR = 0;
7575			$fpaddingL = 0;
7576		} else {
7577			/* -- END TABLES -- */
7578			$ipaddingL = $this->blk[$this->blklvl]['padding_left'];
7579			$ipaddingR = $this->blk[$this->blklvl]['padding_right'];
7580			$paddingL = ($ipaddingL * Mpdf::SCALE);
7581			$paddingR = ($ipaddingR * Mpdf::SCALE);
7582			$this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];
7583			$cpaddingadjustL = -$this->cMarginL;
7584			$this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];
7585			$cpaddingadjustR = -$this->cMarginR;
7586			// Added mPDF 3.0 Float DIV
7587			$fpaddingR = 0;
7588			$fpaddingL = 0;
7589			/* -- CSS-FLOAT -- */
7590			if (count($this->floatDivs)) {
7591				list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
7592				if ($r_exists) {
7593					$fpaddingR = $r_width;
7594				}
7595				if ($l_exists) {
7596					$fpaddingL = $l_width;
7597				}
7598			}
7599			/* -- END CSS-FLOAT -- */
7600
7601			$usey = $this->y + 0.002;
7602			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
7603				$usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
7604			}
7605			/* -- CSS-IMAGE-FLOAT -- */
7606			// If float exists at this level
7607			if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
7608				$fpaddingR += $this->floatmargins['R']['w'];
7609			}
7610			if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
7611				$fpaddingL += $this->floatmargins['L']['w'];
7612			}
7613			/* -- END CSS-IMAGE-FLOAT -- */
7614		} // *TABLES*
7615		// OBJECTS - IMAGES & FORM Elements (NB has already skipped line/page if required - in printbuffer)
7616		if (substr($s, 0, 3) == "\xbb\xa4\xac") { // identifier has been identified!
7617			$objattr = $this->_getObjAttr($s);
7618			$h_corr = 0;
7619			if ($is_table) { // *TABLES*
7620				$maximumW = ($maxWidth / Mpdf::SCALE) - ($this->cellPaddingL + $this->cMarginL + $this->cellPaddingR + $this->cMarginR);  // *TABLES*
7621			} // *TABLES*
7622			else { // *TABLES*
7623				if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0) && (!$is_table)) {
7624					$h_corr = $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
7625				}
7626				$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 );
7627			} // *TABLES*
7628			$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);
7629
7630			// SET LINEHEIGHT for this line ================ RESET AT END
7631			$stackHeight = max($stackHeight, $objattr['OUTER-HEIGHT']);
7632			$this->objectbuffer[count($content) - 1] = $objattr;
7633			// if (isset($objattr['vertical-align'])) { $valign = $objattr['vertical-align']; }
7634			// else { $valign = ''; }
7635			// LIST MARKERS	// mPDF 6  Lists
7636			if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {
7637				// do nothing
7638			} else {
7639				$contentWidth += ($objattr['OUTER-WIDTH'] * Mpdf::SCALE);
7640			}
7641			return;
7642		}
7643
7644		$lbw = $rbw = 0; // Border widths
7645		if (!empty($this->spanborddet)) {
7646			if (isset($this->spanborddet['L'])) {
7647				$lbw = $this->spanborddet['L']['w'];
7648			}
7649			if (isset($this->spanborddet['R'])) {
7650				$rbw = $this->spanborddet['R']['w'];
7651			}
7652		}
7653
7654		if ($this->usingCoreFont) {
7655			$clen = strlen($s);
7656		} else {
7657			$clen = mb_strlen($s, $this->mb_enc);
7658		}
7659
7660		// for every character in the string
7661		for ($i = 0; $i < $clen; $i++) {
7662			// extract the current character
7663			// get the width of the character in points
7664			if ($this->usingCoreFont) {
7665				$c = $s[$i];
7666				// Soft Hyphens chr(173)
7667				$cw = ($this->GetCharWidthCore($c) * Mpdf::SCALE);
7668				if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
7669					if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c])) {
7670						$cw += ($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c] * $this->FontSizePt / 1000 );
7671					}
7672				}
7673			} else {
7674				$c = mb_substr($s, $i, 1, $this->mb_enc);
7675				$cw = ($this->GetCharWidthNonCore($c, false) * Mpdf::SCALE);
7676				// mPDF 5.7.1
7677				// Use OTL GPOS
7678				if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
7679					// ...WriteFlowingBlock...
7680					// Only  add XAdvanceL (not sure at present whether RTL or LTR writing direction)
7681					// At this point, XAdvanceL and XAdvanceR will balance
7682					if (isset($sOTLdata['GPOSinfo'][$i]['XAdvanceL'])) {
7683						$cw += $sOTLdata['GPOSinfo'][$i]['XAdvanceL'] * (1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000) * Mpdf::SCALE;
7684					}
7685				}
7686				if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1
7687					$lastc = mb_substr($s, ($i - 1), 1, $this->mb_enc);
7688					$ulastc = $this->UTF8StringToArray($lastc, false);
7689					$uc = $this->UTF8StringToArray($c, false);
7690					if (isset($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]])) {
7691						$cw += ($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]] * $this->FontSizePt / 1000 );
7692					}
7693				}
7694			}
7695
7696			if ($i == 0) {
7697				$cw += $lbw * Mpdf::SCALE;
7698				$contentB[(count($contentB) - 1)] .= 'L';
7699			}
7700			if ($i == ($clen - 1)) {
7701				$cw += $rbw * Mpdf::SCALE;
7702				$contentB[(count($contentB) - 1)] .= 'R';
7703			}
7704			if ($c == ' ') {
7705				$currContent .= $c;
7706				$contentWidth += $cw;
7707				continue;
7708			}
7709
7710			// Paragraph INDENT
7711			$WidthCorrection = 0;
7712			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
7713				$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
7714				$WidthCorrection = ($ti * Mpdf::SCALE);
7715			}
7716			// OUTDENT
7717			foreach ($this->objectbuffer as $k => $objattr) {   // mPDF 6 DOTTAB
7718				if ($objattr['type'] == 'dottab') {
7719					$WidthCorrection -= ($objattr['outdent'] * Mpdf::SCALE);
7720					break;
7721				}
7722			}
7723
7724
7725			// Added mPDF 3.0 Float DIV
7726			$fpaddingR = 0;
7727			$fpaddingL = 0;
7728			/* -- CSS-FLOAT -- */
7729			if (count($this->floatDivs)) {
7730				list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
7731				if ($r_exists) {
7732					$fpaddingR = $r_width;
7733				}
7734				if ($l_exists) {
7735					$fpaddingL = $l_width;
7736				}
7737			}
7738			/* -- END CSS-FLOAT -- */
7739
7740			$usey = $this->y + 0.002;
7741			if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
7742				$usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
7743			}
7744
7745			/* -- CSS-IMAGE-FLOAT -- */
7746			// If float exists at this level
7747			if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
7748				$fpaddingR += $this->floatmargins['R']['w'];
7749			}
7750			if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
7751				$fpaddingL += $this->floatmargins['L']['w'];
7752			}
7753			/* -- END CSS-IMAGE-FLOAT -- */
7754
7755
7756			// try adding another char
7757			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
7758				// it won't fit, output what we already have
7759				$lineCount++;
7760
7761				// contains any content that didn't make it into this print
7762				$savedContent = '';
7763				$savedContentB = '';
7764				$savedOTLdata = []; // mPDF 5.7.1
7765				$savedFont = [];
7766				$savedObj = [];
7767				$savedPreOTLdata = []; // mPDF 5.7.1
7768				$savedPreContent = [];
7769				$savedPreContentB = [];
7770				$savedPreFont = [];
7771
7772				// mPDF 6
7773				// New line-breaking algorithm
7774				/////////////////////
7775				// LINE BREAKING
7776				/////////////////////
7777				$breakfound = false;
7778				$contentctr = count($content) - 1;
7779				if ($this->usingCoreFont) {
7780					$charctr = strlen($currContent);
7781				} else {
7782					$charctr = mb_strlen($currContent, $this->mb_enc);
7783				}
7784				$checkchar = $c;
7785				$prevchar = $this->_getPrevChar($contentctr, $charctr, $content);
7786
7787				/* -- CJK-FONTS -- */
7788				// 1) CJK Overflowing a) punctuation or b) Oikomi
7789				// Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi
7790				if ($CJKoverflow || $Oikomi) { // If flag already set
7791					$CJKoverflow = false;
7792					$Oikomi = false;
7793					$breakfound = true;
7794				}
7795				if (!$this->usingCoreFont && !$breakfound && $this->checkCJK) {
7796
7797					// Get next/following character (in this chunk)
7798					$followingchar = '';
7799					if ($i < ($clen - 1)) {
7800						if ($this->usingCoreFont) {
7801							$followingchar = $s[$i + 1];
7802						} else {
7803							$followingchar = mb_substr($s, $i + 1, 1, $this->mb_enc);
7804						}
7805					}
7806
7807					// 1a) Overflow punctuation
7808					if (preg_match("/[" . $this->pregCJKchars . "]/u", $prevchar) && preg_match("/[" . $this->CJKoverflow . "]/u", $checkchar) && $this->allowCJKorphans) {
7809						// add character onto this line
7810						$currContent .= $c;
7811						$contentWidth += $cw;
7812						$CJKoverflow = true; // Set flag
7813						continue;
7814					} elseif (preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) && $this->allowCJKorphans &&
7815							(preg_match("/[" . $this->CJKleading . "]/u", $followingchar) || preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar)) &&
7816							!preg_match("/[" . $this->CJKleading . "]/u", $checkchar) && !preg_match("/[" . $this->CJKfollowing . "]/u", $followingchar) &&
7817							!(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $followingchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) {
7818						// 1b) Try squeezing another character(s) onto this line = Oikomi, if character cannot end line
7819						// or next character cannot start line (and not splitting CJK numerals)
7820						// NB otherwise it move lastchar(s) to next line to keep $c company = Oidashi, which is done below in standard way
7821						// add character onto this line
7822						$currContent .= $c;
7823						$contentWidth += $cw;
7824						$Oikomi = true; // Set flag
7825						continue;
7826					}
7827				}
7828				/* -- END CJK-FONTS -- */
7829				/* -- HYPHENATION -- */
7830
7831				// AUTOMATIC HYPHENATION
7832				// 2) Automatic hyphen in current word (does not cross tags)
7833				if (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] == 1) {
7834					$currWord = '';
7835					// Look back and ahead to get current word
7836					for ($ac = $charctr - 1; $ac >= 0; $ac--) {
7837						if ($this->usingCoreFont) {
7838							$addc = substr($currContent, $ac, 1);
7839						} else {
7840							$addc = mb_substr($currContent, $ac, 1, $this->mb_enc);
7841						}
7842						if ($addc == ' ') {
7843							break;
7844						}
7845						$currWord = $addc . $currWord;
7846					}
7847					$start = $ac + 1;
7848					for ($ac = $i; $ac < ($clen - 1); $ac++) {
7849						if ($this->usingCoreFont) {
7850							$addc = substr($s, $ac, 1);
7851						} else {
7852							$addc = mb_substr($s, $ac, 1, $this->mb_enc);
7853						}
7854						if ($addc == ' ') {
7855							break;
7856						}
7857						$currWord .= $addc;
7858					}
7859					$ptr = $this->hyphenator->hyphenateWord($currWord, $charctr - $start);
7860					if ($ptr > -1) {
7861						$breakfound = [$contentctr, $start + $ptr, $contentctr, $start + $ptr, 'hyphen'];
7862					}
7863				}
7864				/* -- END HYPHENATION -- */
7865
7866				// Search backwards to find first line-break opportunity
7867				while ($breakfound == false && $prevchar !== false) {
7868					$cutcontentctr = $contentctr;
7869					$cutcharctr = $charctr;
7870					$prevchar = $this->_moveToPrevChar($contentctr, $charctr, $content);
7871					/////////////////////
7872					// 3) Break at SPACE
7873					/////////////////////
7874					if ($prevchar == ' ') {
7875						$breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard'];
7876					} /////////////////////
7877					// 4) Break at U+200B in current word (Khmer, Lao & Thai Invisible word boundary, and Tibetan)
7878					/////////////////////
7879					elseif ($prevchar == "\xe2\x80\x8b") { // U+200B Zero-width Word Break
7880						$breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard'];
7881					} /////////////////////
7882					// 5) Break at Hard HYPHEN '-' or U+2010
7883					/////////////////////
7884					elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && ($prevchar == '-' || $prevchar == "\xe2\x80\x90")) {
7885						// Don't break a URL
7886						// Look back to get first part of current word
7887						$checkw = '';
7888						for ($ac = $charctr - 1; $ac >= 0; $ac--) {
7889							if ($this->usingCoreFont) {
7890								$addc = substr($currContent, $ac, 1);
7891							} else {
7892								$addc = mb_substr($currContent, $ac, 1, $this->mb_enc);
7893							}
7894							if ($addc == ' ') {
7895								break;
7896							}
7897							$checkw = $addc . $checkw;
7898						}
7899						// Don't break if HyphenMinus AND (a URL or before a numeral or before a >)
7900						if ((!preg_match('/(http:|ftp:|https:|www\.)/', $checkw) && $checkchar != '>' && !preg_match('/[0-9]/', $checkchar)) || $prevchar == "\xe2\x80\x90") {
7901							$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
7902						}
7903					} /////////////////////
7904					// 6) Break at Soft HYPHEN (replace with hard hyphen)
7905					/////////////////////
7906					elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && !$this->usingCoreFont && $prevchar == "\xc2\xad") {
7907						$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
7908						$content[$contentctr] = mb_substr($content[$contentctr], 0, $charctr, $this->mb_enc) . '-' . mb_substr($content[$contentctr], $charctr + 1, mb_strlen($content[$contentctr]), $this->mb_enc);
7909						if (!empty($cOTLdata[$contentctr])) {
7910							$cOTLdata[$contentctr]['char_data'][$charctr] = ['bidi_class' => 9, 'uni' => 45];
7911							$cOTLdata[$contentctr]['group'][$charctr] = 'C';
7912						}
7913					} elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && $prevchar == chr(173)) {
7914						$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
7915						$content[$contentctr] = substr($content[$contentctr], 0, $charctr) . '-' . substr($content[$contentctr], $charctr + 1);
7916					} /* -- CJK-FONTS -- */
7917					/////////////////////
7918					// 7) Break at CJK characters (unless forbidden characters to end or start line)
7919					// CJK Avoiding line break in the middle of numerals
7920					/////////////////////
7921					elseif (!$this->usingCoreFont && $this->checkCJK && preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) &&
7922						!preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar) && !preg_match("/[" . $this->CJKleading . "]/u", $prevchar) &&
7923						!(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $prevchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) {
7924						$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
7925					}
7926					/* -- END CJK-FONTS -- */
7927					/////////////////////
7928					// 8) Break at OBJECT (Break before all objects here - selected objects are moved forward to next line below e.g. dottab)
7929					/////////////////////
7930					if (isset($this->objectbuffer[$contentctr])) {
7931						$breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];
7932					}
7933
7934
7935					$checkchar = $prevchar;
7936				}
7937
7938				// If a line-break opportunity found:
7939				if (is_array($breakfound)) {
7940					$contentctr = $breakfound[0];
7941					$charctr = $breakfound[1];
7942					$cutcontentctr = $breakfound[2];
7943					$cutcharctr = $breakfound[3];
7944					$type = $breakfound[4];
7945					// Cache chunks which are already processed, but now need to be passed on to the new line
7946					for ($ix = count($content) - 1; $ix > $cutcontentctr; $ix--) {
7947						// save and crop off any subsequent chunks
7948						/* -- OTL -- */
7949						if (!empty($sOTLdata)) {
7950							$tmpOTL = array_pop($cOTLdata);
7951							$savedPreOTLdata[] = $tmpOTL;
7952						}
7953						/* -- END OTL -- */
7954						$savedPreContent[] = array_pop($content);
7955						$savedPreContentB[] = array_pop($contentB);
7956						$savedPreFont[] = array_pop($font);
7957					}
7958
7959					// Next cache the part which will start the next line
7960					if ($this->usingCoreFont) {
7961						$savedPreContent[] = substr($content[$cutcontentctr], $cutcharctr);
7962					} else {
7963						$savedPreContent[] = mb_substr($content[$cutcontentctr], $cutcharctr, mb_strlen($content[$cutcontentctr]), $this->mb_enc);
7964					}
7965					$savedPreContentB[] = preg_replace('/L/', '', $contentB[$cutcontentctr]);
7966					$savedPreFont[] = $font[$cutcontentctr];
7967					/* -- OTL -- */
7968					if (!empty($sOTLdata)) {
7969						$savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[$cutcontentctr], $cutcharctr, $cutcharctr);
7970					}
7971					/* -- END OTL -- */
7972
7973
7974					// Finally adjust the Current content which ends this line
7975					if ($cutcharctr == 0 && $type == 'discard') {
7976						array_pop($content);
7977						array_pop($contentB);
7978						array_pop($font);
7979						array_pop($cOTLdata);
7980					}
7981
7982					$currContent = & $content[count($content) - 1];
7983					if ($this->usingCoreFont) {
7984						$currContent = substr($currContent, 0, $charctr);
7985					} else {
7986						$currContent = mb_substr($currContent, 0, $charctr, $this->mb_enc);
7987					}
7988
7989					if (!empty($sOTLdata)) {
7990						$savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));
7991					}
7992
7993					if (strpos($contentB[(count($contentB) - 1)], 'R') !== false) {   // ???
7994						$contentB[count($content) - 1] = preg_replace('/R/', '', $contentB[count($content) - 1]); // ???
7995					}
7996
7997					if ($type == 'hyphen') {
7998						$currContent .= '-';
7999						if (!empty($cOTLdata[(count($cOTLdata) - 1)])) {
8000							$cOTLdata[(count($cOTLdata) - 1)]['char_data'][] = ['bidi_class' => 9, 'uni' => 45];
8001							$cOTLdata[(count($cOTLdata) - 1)]['group'] .= 'C';
8002						}
8003					}
8004
8005					$savedContent = '';
8006					$savedContentB = '';
8007					$savedFont = [];
8008					$savedOTLdata = [];
8009				}
8010				// If no line-break opportunity found - split at current position
8011				// or - Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi, as set above by:
8012				// 1) CJK Overflowing a) punctuation or b) Oikomi
8013				// in which case $breakfound==1 and NOT array
8014
8015				if (!is_array($breakfound)) {
8016					$savedFont = $this->saveFont();
8017					if (!empty($sOTLdata)) {
8018						$savedOTLdata = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));
8019					}
8020				}
8021
8022				if ($content[count($content) - 1] == '' && !isset($this->objectbuffer[count($content) - 1])) {
8023					array_pop($content);
8024					array_pop($contentB);
8025					array_pop($font);
8026					array_pop($cOTLdata);
8027					$currContent = & $content[count($content) - 1];
8028				}
8029
8030				// Right Trim current content - including CJK space, and for OTLdata
8031				// incl. CJK - strip CJK space at end of line &#x3000; = \xe3\x80\x80 = CJK space
8032				$currContent = rtrim($currContent);
8033				if ($this->checkCJK) {
8034					$currContent = preg_replace("/\xe3\x80\x80$/", '', $currContent);
8035				} // *CJK-FONTS*
8036				/* -- OTL -- */
8037				if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
8038					$this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true); // NB also does U+3000
8039				}
8040				/* -- END OTL -- */
8041
8042
8043				// Selected OBJECTS are moved forward to next line, unless they come before a space or U+200B (type='discard')
8044				if (isset($this->objectbuffer[(count($content) - 1)]) && (!isset($type) || $type != 'discard')) {
8045					$objtype = $this->objectbuffer[(count($content) - 1)]['type'];
8046					if ($objtype == 'dottab' || $objtype == 'bookmark' || $objtype == 'indexentry' || $objtype == 'toc' || $objtype == 'annot') {
8047						$savedObj = array_pop($this->objectbuffer);
8048					}
8049				}
8050
8051
8052				// Decimal alignment (cancel if wraps to > 1 line)
8053				if ($is_table && substr($align, 0, 1) == 'D') {
8054					$align = substr($align, 2, 1);
8055				}
8056
8057				$lineBox = [];
8058
8059				$this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);
8060
8061				// update $contentWidth since it has changed with cropping
8062				$contentWidth = 0;
8063
8064				$inclCursive = false;
8065				foreach ($content as $k => $chunk) {
8066					if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
8067						// LIST MARKERS
8068						if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker']) {
8069							if ($this->objectbuffer[$k]['listmarkerposition'] != 'outside') {
8070								$contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
8071							}
8072						} else {
8073							$contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;
8074						}
8075					} elseif (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
8076						$this->restoreFont($font[$k], false);
8077						if ($this->checkCJK && $k == count($content) - 1 && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $this->CJKforceend) {
8078							// force-end overhang
8079							$hanger = mb_substr($chunk, mb_strlen($chunk, $this->mb_enc) - 1, 1, $this->mb_enc);
8080							// Probably ought to do something with char_data and GPOS in cOTLdata...
8081							$content[$k] = $chunk = mb_substr($chunk, 0, mb_strlen($chunk, $this->mb_enc) - 1, $this->mb_enc);
8082						}
8083
8084						// Soft Hyphens chr(173) + Replace NBSP with SPACE + Set inclcursive if includes CURSIVE TEXT
8085						if (!$this->usingCoreFont) {
8086							/* -- OTL -- */
8087							if ((isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) || !empty($sOTLdata)) {
8088								$this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad");
8089								$this->otl->replaceSpace($chunk, $cOTLdata[$k]); // NBSP -> space
8090								if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) {
8091									$inclCursive = true;
8092								}
8093								$content[$k] = $chunk;
8094							} /* -- END OTL -- */ else {  // *OTL*
8095								$content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk);
8096								$content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);
8097							} // *OTL*
8098						} elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
8099							$content[$k] = $chunk = str_replace(chr(173), '', $chunk);
8100							$content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);
8101						}
8102
8103						$contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE;  // mPDF 5.7.1
8104						if (!empty($this->spanborddet)) {
8105							if (isset($this->spanborddet['L']['w']) && strpos($contentB[$k], 'L') !== false) {
8106								$contentWidth += $this->spanborddet['L']['w'] * Mpdf::SCALE;
8107							}
8108							if (isset($this->spanborddet['R']['w']) && strpos($contentB[$k], 'R') !== false) {
8109								$contentWidth += $this->spanborddet['R']['w'] * Mpdf::SCALE;
8110							}
8111						}
8112					}
8113				}
8114
8115				$lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');
8116				$lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');
8117				if ($blockdir == 'ltr' && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic
8118					$lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE;
8119				} else {
8120					$lastitalic = 0;
8121				}
8122
8123
8124
8125
8126				// NOW FORMAT THE LINE TO OUTPUT
8127				if (!$table_draft) {
8128					// DIRECTIONALITY RTL
8129					$chunkorder = range(0, count($content) - 1); // mPDF 5.7
8130					/* -- OTL -- */
8131					// mPDF 6
8132					if ($blockdir == 'rtl' || $this->biDirectional) {
8133						$this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);
8134						// From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to
8135						// $this->objectbuffer and $font ($chunkorder contains the mapping)
8136					}
8137
8138					/* -- END OTL -- */
8139					// Remove any XAdvance from OTL data at end of line
8140					foreach ($chunkorder as $aord => $k) {
8141						if (count($cOTLdata)) {
8142							$this->restoreFont($font[$k], false);
8143							// ...WriteFlowingBlock...
8144							if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line
8145								$nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character
8146								if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {
8147									if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {
8148										$w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
8149									} else {
8150										$w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
8151									}
8152									$w *= ($this->FontSize / 1000);
8153									$contentWidth -= $w * Mpdf::SCALE;
8154									$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;
8155									$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;
8156								}
8157
8158								// If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it
8159								if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {
8160									$w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
8161									$w *= ($this->FontSize / 1000);
8162									$contentWidth -= $w * Mpdf::SCALE;
8163									$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
8164									$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
8165								}
8166							}
8167						}
8168					}
8169
8170					// JUSTIFICATION J
8171					$jcharspacing = 0;
8172					$jws = 0;
8173					$nb_carac = 0;
8174					$nb_spaces = 0;
8175					$jkashida = 0;
8176					// if it's justified, we need to find the char/word spacing (or if hanger $this->CJKforceend)
8177					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
8178						// JUSTIFY J (Use character spacing)
8179						// WORD SPACING
8180						// mPDF 5.7
8181						foreach ($chunkorder as $aord => $k) {
8182							$chunk = isset($content[$aord]) ? $content[$aord] : '';
8183							if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
8184								$nb_carac += mb_strlen($chunk, $this->mb_enc);
8185								$nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);
8186								// Use GPOS OTL
8187								if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
8188									if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
8189										$nb_carac -= substr_count($cOTLdata[$aord]['group'], 'M');
8190									}
8191								}
8192							} else {
8193								$nb_carac ++;
8194							} // mPDF 6 allow spacing for inline object
8195						}
8196						// GetJSpacing adds kashida spacing to GPOSinfo if appropriate for Font
8197						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);
8198					}
8199
8200					// WORD SPACING
8201					$empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) );
8202
8203					$empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1
8204					$empty -= ($jws * $nb_spaces);
8205					$empty -= ($jkashida);
8206					$empty /= Mpdf::SCALE;
8207
8208					$b = ''; // do not use borders
8209					// Get PAGEBREAK TO TEST for height including the top border/padding
8210					$check_h = max($this->divheight, $stackHeight);
8211					if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blklvl > 0) && ($lineCount == 1) && (!$is_table)) {
8212						$check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);
8213					}
8214
8215					if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {
8216						$this->SetCol($this->NbCol - 1);
8217					}
8218
8219					// PAGEBREAK
8220					// 'If' below used in order to fix "first-line of other page with justify on" bug
8221					if (!$is_table && ($this->y + $check_h) > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {
8222						$bak_x = $this->x; // Current X position
8223						// WORD SPACING
8224						$ws = $this->ws; // Word Spacing
8225						$charspacing = $this->charspacing; // Character Spacing
8226						$this->ResetSpacing();
8227
8228						$this->AddPage($this->CurOrientation);
8229
8230						$this->x = $bak_x;
8231						// Added to correct for OddEven Margins
8232						$currentx += $this->MarginCorrection;
8233						$this->x += $this->MarginCorrection;
8234
8235						// WORD SPACING
8236						$this->SetSpacing($charspacing, $ws);
8237					}
8238
8239					if ($this->kwt && !$is_table) { // mPDF 5.7+
8240						$this->printkwtbuffer();
8241						$this->kwt = false;
8242					}
8243
8244
8245					/* -- COLUMNS -- */
8246					// COLS
8247					// COLUMN CHANGE
8248					if ($this->CurrCol != $oldcolumn) {
8249						$currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
8250						$this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
8251						$oldcolumn = $this->CurrCol;
8252					}
8253
8254					if ($this->ColActive && !$is_table) {
8255						$this->breakpoints[$this->CurrCol][] = $this->y;
8256					} // *COLUMNS*
8257					/* -- END COLUMNS -- */
8258
8259					// TOP MARGIN
8260					if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && ($lineCount == 1) && (!$is_table)) {
8261						$this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
8262						if ($this->ColActive) {
8263							$this->breakpoints[$this->CurrCol][] = $this->y;
8264						} // *COLUMNS*
8265					}
8266
8267
8268					// Update y0 for top of block (used to paint border)
8269					if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table)) {
8270						$this->blk[$this->blklvl]['y0'] = $this->y;
8271						$this->blk[$this->blklvl]['startpage'] = $this->page;
8272						if ($this->blk[$this->blklvl]['float']) {
8273							$this->blk[$this->blklvl]['float_start_y'] = $this->y;
8274						}
8275					}
8276
8277					// TOP PADDING and BORDER spacing/fill
8278					if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 1) && (!$is_table)) {
8279						// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
8280						$this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);
8281						if ($this->ColActive) {
8282							$this->breakpoints[$this->CurrCol][] = $this->y;
8283						} // *COLUMNS*
8284					}
8285
8286					$arraysize = count($chunkorder);
8287
8288					$margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );
8289
8290					// PAINT BACKGROUND FOR THIS LINE
8291					if (!$is_table) {
8292						$this->DivLn($stackHeight, $this->blklvl, false);
8293					} // false -> don't advance y
8294
8295					$this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;
8296					if ($align == 'R') {
8297						$this->x += $empty;
8298					} elseif ($align == 'C') {
8299						$this->x += ($empty / 2);
8300					}
8301
8302					// Paragraph INDENT
8303					if (isset($this->blk[$this->blklvl]['text_indent']) && ($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table) && ($blockdir != 'rtl') && ($align != 'C')) {
8304						$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
8305						$this->x += $ti;
8306					}
8307
8308					// BIDI magic_reverse moved upwards from here
8309					foreach ($chunkorder as $aord => $k) { // mPDF 5.7
8310
8311						$chunk = isset($content[$aord]) ? $content[$aord] : '';
8312
8313						if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
8314							$xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];
8315							$this->objectbuffer[$k]['OUTER-X'] += $xadj;
8316							$this->objectbuffer[$k]['BORDER-X'] += $xadj;
8317							$this->objectbuffer[$k]['INNER-X'] += $xadj;
8318
8319							if ($this->objectbuffer[$k]['type'] == 'listmarker') {
8320								$this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin
8321							}
8322							$yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];
8323							if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
8324								$this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin
8325							}
8326							if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB
8327								$yadj += $lineBox[$k]['top'];
8328							}
8329							$this->objectbuffer[$k]['OUTER-Y'] += $yadj;
8330							$this->objectbuffer[$k]['BORDER-Y'] += $yadj;
8331							$this->objectbuffer[$k]['INNER-Y'] += $yadj;
8332						}
8333
8334						$this->restoreFont($font[$k]);  // mPDF 5.7
8335
8336						$this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws);
8337						// Now unset these values so they don't influence GetStringwidth below or in fn. Cell
8338						$this->fixedlSpacing = false;
8339						$this->minwSpacing = 0;
8340
8341						$save_vis = $this->visibility;
8342						if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {
8343							$this->SetVisibility($this->textparam['visibility']);
8344						}
8345						// *********** SPAN BACKGROUND COLOR ***************** //
8346						if ($this->spanbgcolor) {
8347							$cor = $this->spanbgcolorarray;
8348							$this->SetFColor($cor);
8349							$save_fill = $fill;
8350							$spanfill = 1;
8351							$fill = 1;
8352						}
8353						if (!empty($this->spanborddet)) {
8354							if (strpos($contentB[$k], 'L') !== false) {
8355								$this->x += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
8356							}
8357							if (strpos($contentB[$k], 'L') === false) {
8358								$this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;
8359							}
8360							if (strpos($contentB[$k], 'R') === false) {
8361								$this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;
8362							}
8363						}
8364
8365						// WORD SPACING
8366						// StringWidth this time includes any kashida spacing
8367						$stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, true);
8368
8369						$nch = mb_strlen($chunk, $this->mb_enc);
8370						// Use GPOS OTL
8371						if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
8372							if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
8373								$nch -= substr_count($cOTLdata[$aord]['group'], 'M');
8374							}
8375						}
8376						$stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE );
8377
8378						$stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE );
8379
8380						if (isset($this->objectbuffer[$k])) {
8381							// LIST MARKERS	// mPDF 6  Lists
8382							if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
8383								$stringWidth = 0;
8384							} else {
8385								$stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];
8386							}
8387						}
8388
8389						if ($stringWidth == 0) {
8390							$stringWidth = 0.000001;
8391						}
8392
8393						if ($aord == $arraysize - 1) {
8394							$stringWidth -= ( $this->charspacing / Mpdf::SCALE );
8395							if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {
8396								// force-end overhang
8397								$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));
8398								$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));
8399							} else {
8400								$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)
8401							}
8402						} else {
8403							$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
8404						}
8405
8406						if (!empty($this->spanborddet)) {
8407							if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) {
8408								$this->x += $this->spanborddet['R']['w'];
8409							}
8410						}
8411						// *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //
8412						if (isset($spanfill) && $spanfill) {
8413							$fill = $save_fill;
8414							$spanfill = 0;
8415							if ($fill) {
8416								$this->SetFColor($bcor);
8417							}
8418						}
8419						if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {
8420							$this->SetVisibility($save_vis);
8421						}
8422					}
8423				} elseif ($table_draft) {
8424					$this->y += $stackHeight;
8425				}
8426
8427				if (!$is_table) {
8428					$this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin']));
8429					$this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin']));
8430				}
8431
8432				// move on to the next line, reset variables, tack on saved content and current char
8433
8434				if (!$table_draft) {
8435					$this->printobjectbuffer($is_table, $blockdir);
8436				}
8437				$this->objectbuffer = [];
8438
8439
8440				/* -- CSS-IMAGE-FLOAT -- */
8441				// Update values if set to skipline
8442				if ($this->floatmargins) {
8443					$this->_advanceFloatMargins();
8444				}
8445				/* -- END CSS-IMAGE-FLOAT -- */
8446
8447				// Reset lineheight
8448				$stackHeight = $this->divheight;
8449				$valign = 'M';
8450
8451				$font = [];
8452				$content = [];
8453				$contentB = [];
8454				$cOTLdata = []; // mPDF 5.7.1
8455				$contentWidth = 0;
8456				if (!empty($savedObj)) {
8457					$this->objectbuffer[] = $savedObj;
8458					$font[] = $savedFont;
8459					$content[] = '';
8460					$contentB[] = '';
8461					$cOTLdata[] = []; // mPDF 5.7.1
8462					$contentWidth += $savedObj['OUTER-WIDTH'] * Mpdf::SCALE;
8463				}
8464				if (count($savedPreContent) > 0) {
8465					for ($ix = count($savedPreContent) - 1; $ix >= 0; $ix--) {
8466						$font[] = $savedPreFont[$ix];
8467						$content[] = $savedPreContent[$ix];
8468						$contentB[] = $savedPreContentB[$ix];
8469						if (!empty($sOTLdata)) {
8470							$cOTLdata[] = $savedPreOTLdata[$ix];
8471						}
8472						$this->restoreFont($savedPreFont[$ix]);
8473						$lbw = $rbw = 0; // Border widths
8474						if (!empty($this->spanborddet)) {
8475							$lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
8476							$rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
8477						}
8478						if ($ix > 0) {
8479							$contentWidth += $this->GetStringWidth($savedPreContent[$ix], true, (isset($savedPreOTLdata[$ix]) ? $savedPreOTLdata[$ix] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1
8480							if (strpos($savedPreContentB[$ix], 'L') !== false) {
8481								$contentWidth += $lbw;
8482							}
8483							if (strpos($savedPreContentB[$ix], 'R') !== false) {
8484								$contentWidth += $rbw;
8485							}
8486						}
8487					}
8488					$savedPreContent = [];
8489					$savedPreContentB = [];
8490					$savedPreOTLdata = []; // mPDF 5.7.1
8491					$savedPreFont = [];
8492					$content[(count($content) - 1)] .= $c;
8493				} else {
8494					$font[] = $savedFont;
8495					$content[] = $savedContent . $c;
8496					$contentB[] = $savedContentB;
8497					$cOTLdata[] = $savedOTLdata; // mPDF 5.7.1
8498				}
8499
8500				$currContent = & $content[(count($content) - 1)];
8501				$this->restoreFont($font[(count($font) - 1)]); // mPDF 6.0
8502
8503				/* -- CJK-FONTS -- */
8504				// CJK - strip CJK space at start of line
8505				// &#x3000; = \xe3\x80\x80 = CJK space
8506				if ($this->checkCJK && $currContent == "\xe3\x80\x80") {
8507					$currContent = '';
8508					if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
8509						$this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], true, false); // left trim U+3000
8510					}
8511				}
8512				/* -- END CJK-FONTS -- */
8513
8514				$lbw = $rbw = 0; // Border widths
8515				if (!empty($this->spanborddet)) {
8516					$lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
8517					$rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
8518				}
8519
8520				$contentWidth += $this->GetStringWidth($currContent, false, (isset($cOTLdata[(count($cOTLdata) - 1)]) ? $cOTLdata[(count($cOTLdata) - 1)] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1
8521				if (strpos($savedContentB, 'L') !== false) {
8522					$contentWidth += $lbw;
8523				}
8524				$CJKoverflow = false;
8525				$hanger = '';
8526			} // another character will fit, so add it on
8527			else {
8528				$contentWidth += $cw;
8529				$currContent .= $c;
8530			}
8531		}
8532
8533		unset($content);
8534		unset($contentB);
8535	}
8536
8537	// ----------------------END OF FLOWING BLOCK------------------------------------//
8538
8539
8540	/* -- CSS-IMAGE-FLOAT -- */
8541	// Update values if set to skipline
8542	function _advanceFloatMargins()
8543	{
8544		// Update floatmargins - L
8545		if (isset($this->floatmargins['L']) && $this->floatmargins['L']['skipline'] && $this->floatmargins['L']['y0'] != $this->y) {
8546			$yadj = $this->y - $this->floatmargins['L']['y0'];
8547			$this->floatmargins['L']['y0'] = $this->y;
8548			$this->floatmargins['L']['y1'] += $yadj;
8549
8550			// Update objattr in floatbuffer
8551			if ($this->floatbuffer[$this->floatmargins['L']['id']]['border_left']['w']) {
8552				$this->floatbuffer[$this->floatmargins['L']['id']]['BORDER-Y'] += $yadj;
8553			}
8554			$this->floatbuffer[$this->floatmargins['L']['id']]['INNER-Y'] += $yadj;
8555			$this->floatbuffer[$this->floatmargins['L']['id']]['OUTER-Y'] += $yadj;
8556
8557			// Unset values
8558			$this->floatbuffer[$this->floatmargins['L']['id']]['skipline'] = false;
8559			$this->floatmargins['L']['skipline'] = false;
8560			$this->floatmargins['L']['id'] = '';
8561		}
8562		// Update floatmargins - R
8563		if (isset($this->floatmargins['R']) && $this->floatmargins['R']['skipline'] && $this->floatmargins['R']['y0'] != $this->y) {
8564			$yadj = $this->y - $this->floatmargins['R']['y0'];
8565			$this->floatmargins['R']['y0'] = $this->y;
8566			$this->floatmargins['R']['y1'] += $yadj;
8567
8568			// Update objattr in floatbuffer
8569			if ($this->floatbuffer[$this->floatmargins['R']['id']]['border_left']['w']) {
8570				$this->floatbuffer[$this->floatmargins['R']['id']]['BORDER-Y'] += $yadj;
8571			}
8572			$this->floatbuffer[$this->floatmargins['R']['id']]['INNER-Y'] += $yadj;
8573			$this->floatbuffer[$this->floatmargins['R']['id']]['OUTER-Y'] += $yadj;
8574
8575			// Unset values
8576			$this->floatbuffer[$this->floatmargins['R']['id']]['skipline'] = false;
8577			$this->floatmargins['R']['skipline'] = false;
8578			$this->floatmargins['R']['id'] = '';
8579		}
8580	}
8581
8582	/* -- END CSS-IMAGE-FLOAT -- */
8583
8584
8585
8586	/* -- END HTML-CSS -- */
8587
8588	function _SetTextRendering($mode)
8589	{
8590		if (!(($mode == 0) || ($mode == 1) || ($mode == 2))) {
8591			throw new \Mpdf\MpdfException("Text rendering mode should be 0, 1 or 2 (value : $mode)");
8592		}
8593		$tr = ($mode . ' Tr');
8594		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
8595			$this->_out($tr);
8596		}
8597		$this->pageoutput[$this->page]['TextRendering'] = $tr;
8598	}
8599
8600	function SetTextOutline($params = [])
8601	{
8602		if (isset($params['outline-s']) && $params['outline-s']) {
8603			$this->SetLineWidth($params['outline-WIDTH']);
8604			$this->SetDColor($params['outline-COLOR']);
8605			$tr = ('2 Tr');
8606			if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
8607				$this->_out($tr);
8608			}
8609			$this->pageoutput[$this->page]['TextRendering'] = $tr;
8610		} else { // Now resets all values
8611			$this->SetLineWidth(0.2);
8612			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
8613			$this->_SetTextRendering(0);
8614			$tr = ('0 Tr');
8615			if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
8616				$this->_out($tr);
8617			}
8618			$this->pageoutput[$this->page]['TextRendering'] = $tr;
8619		}
8620	}
8621
8622	function Image($file, $x, $y, $w = 0, $h = 0, $type = '', $link = '', $paint = true, $constrain = true, $watermark = false, $shownoimg = true, $allowvector = true)
8623	{
8624		$orig_srcpath = $file;
8625		$this->GetFullPath($file);
8626
8627		$info = $this->imageProcessor->getImage($file, true, $allowvector, $orig_srcpath);
8628		if (!$info && $paint) {
8629			$info = $this->imageProcessor->getImage($this->noImageFile);
8630			if ($info) {
8631				$file = $this->noImageFile;
8632				$w = ($info['w'] * (25.4 / $this->dpi));  // 14 x 16px
8633				$h = ($info['h'] * (25.4 / $this->dpi));  // 14 x 16px
8634			}
8635		}
8636		if (!$info) {
8637			return false;
8638		}
8639		// Automatic width and height calculation if needed
8640		if ($w == 0 and $h == 0) {
8641			/* -- IMAGES-WMF -- */
8642			if ($info['type'] == 'wmf') {
8643				// WMF units are twips (1/20pt)
8644				// divide by 20 to get points
8645				// divide by k to get user units
8646				$w = abs($info['w']) / (20 * Mpdf::SCALE);
8647				$h = abs($info['h']) / (20 * Mpdf::SCALE);
8648			} else { 			/* -- END IMAGES-WMF -- */
8649				if ($info['type'] == 'svg') {
8650					// returned SVG units are pts
8651					// divide by k to get user units (mm)
8652					$w = abs($info['w']) / Mpdf::SCALE;
8653					$h = abs($info['h']) / Mpdf::SCALE;
8654				} else {
8655					// Put image at default image dpi
8656					$w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi);
8657					$h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi);
8658				}
8659			}
8660		}
8661		if ($w == 0) {
8662			$w = abs($h * $info['w'] / $info['h']);
8663		}
8664		if ($h == 0) {
8665			$h = abs($w * $info['h'] / $info['w']);
8666		}
8667
8668		/* -- WATERMARK -- */
8669		if ($watermark) {
8670			$maxw = $this->w;
8671			$maxh = $this->h;
8672			// Size = D PF or array
8673			if (is_array($this->watermark_size)) {
8674				$w = $this->watermark_size[0];
8675				$h = $this->watermark_size[1];
8676			} elseif (!is_string($this->watermark_size)) {
8677				$maxw -= $this->watermark_size * 2;
8678				$maxh -= $this->watermark_size * 2;
8679				$w = $maxw;
8680				$h = abs($w * $info['h'] / $info['w']);
8681				if ($h > $maxh) {
8682					$h = $maxh;
8683					$w = abs($h * $info['w'] / $info['h']);
8684				}
8685			} elseif ($this->watermark_size == 'F') {
8686				if ($this->ColActive) {
8687					$maxw = $this->w - ($this->DeflMargin + $this->DefrMargin);
8688				} else {
8689					$maxw = $this->pgwidth;
8690				}
8691				$maxh = $this->h - ($this->tMargin + $this->bMargin);
8692				$w = $maxw;
8693				$h = abs($w * $info['h'] / $info['w']);
8694				if ($h > $maxh) {
8695					$h = $maxh;
8696					$w = abs($h * $info['w'] / $info['h']);
8697				}
8698			} elseif ($this->watermark_size == 'P') { // Default P
8699				$w = $maxw;
8700				$h = abs($w * $info['h'] / $info['w']);
8701				if ($h > $maxh) {
8702					$h = $maxh;
8703					$w = abs($h * $info['w'] / $info['h']);
8704				}
8705			}
8706			// Automatically resize to maximum dimensions of page if too large
8707			if ($w > $maxw) {
8708				$w = $maxw;
8709				$h = abs($w * $info['h'] / $info['w']);
8710			}
8711			if ($h > $maxh) {
8712				$h = $maxh;
8713				$w = abs($h * $info['w'] / $info['h']);
8714			}
8715			// Position
8716			if (is_array($this->watermark_pos)) {
8717				$x = $this->watermark_pos[0];
8718				$y = $this->watermark_pos[1];
8719			} elseif ($this->watermark_pos == 'F') { // centred on printable area
8720				if ($this->ColActive) { // *COLUMNS*
8721					if (($this->mirrorMargins) && (($this->page) % 2 == 0)) {
8722						$xadj = $this->DeflMargin - $this->DefrMargin;
8723					} // *COLUMNS*
8724					else {
8725						$xadj = 0;
8726					} // *COLUMNS*
8727					$x = ($this->DeflMargin - $xadj + ($this->w - ($this->DeflMargin + $this->DefrMargin)) / 2) - ($w / 2); // *COLUMNS*
8728				} // *COLUMNS*
8729				else {  // *COLUMNS*
8730					$x = ($this->lMargin + ($this->pgwidth) / 2) - ($w / 2);
8731				} // *COLUMNS*
8732				$y = ($this->tMargin + ($this->h - ($this->tMargin + $this->bMargin)) / 2) - ($h / 2);
8733			} else { // default P - centred on whole page
8734				$x = ($this->w / 2) - ($w / 2);
8735				$y = ($this->h / 2) - ($h / 2);
8736			}
8737			/* -- IMAGES-WMF -- */
8738			if ($info['type'] == 'wmf') {
8739				$sx = $w * Mpdf::SCALE / $info['w'];
8740				$sy = -$h * Mpdf::SCALE / $info['h'];
8741				$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']);
8742			} else { 			/* -- END IMAGES-WMF -- */
8743				if ($info['type'] == 'svg') {
8744					$sx = $w * Mpdf::SCALE / $info['w'];
8745					$sy = -$h * Mpdf::SCALE / $info['h'];
8746					$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']);
8747				} else {
8748					$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']);
8749				}
8750			}
8751
8752			if ($this->watermarkImgBehind) {
8753				$outstring = $this->watermarkImgAlpha . "\n" . $outstring . "\n" . $this->SetAlpha(1, 'Normal', true) . "\n";
8754				$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $outstring . "\n" . '\\1', $this->pages[$this->page]);
8755			} else {
8756				$this->_out($outstring);
8757			}
8758
8759			return 0;
8760		} // end of IF watermark
8761		/* -- END WATERMARK -- */
8762
8763		if ($constrain) {
8764			// Automatically resize to maximum dimensions of page if too large
8765			if (isset($this->blk[$this->blklvl]['inner_width']) && $this->blk[$this->blklvl]['inner_width']) {
8766				$maxw = $this->blk[$this->blklvl]['inner_width'];
8767			} else {
8768				$maxw = $this->pgwidth;
8769			}
8770			if ($w > $maxw) {
8771				$w = $maxw;
8772				$h = abs($w * $info['h'] / $info['w']);
8773			}
8774			if ($h > $this->h - ($this->tMargin + $this->bMargin + 1)) {  // see below - +10 to avoid drawing too close to border of page
8775				$h = $this->h - ($this->tMargin + $this->bMargin + 1);
8776				if ($this->fullImageHeight) {
8777					$h = $this->fullImageHeight;
8778				}
8779				$w = abs($h * $info['w'] / $info['h']);
8780			}
8781
8782
8783			// Avoid drawing out of the paper(exceeding width limits).
8784			// if ( ($x + $w) > $this->fw ) {
8785			if (($x + $w) > $this->w) {
8786				$x = $this->lMargin;
8787				$y += 5;
8788			}
8789
8790			$changedpage = false;
8791			$oldcolumn = $this->CurrCol;
8792			// Avoid drawing out of the page.
8793			if ($y + $h > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {
8794				$this->AddPage($this->CurOrientation);
8795				// Added to correct for OddEven Margins
8796				$x = $x + $this->MarginCorrection;
8797				$y = $this->tMargin; // mPDF 5.7.3
8798				$changedpage = true;
8799			}
8800			/* -- COLUMNS -- */
8801			// COLS
8802			// COLUMN CHANGE
8803			if ($this->CurrCol != $oldcolumn) {
8804				$y = $this->y0;
8805				$x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
8806				$this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
8807			}
8808			/* -- END COLUMNS -- */
8809		} // end of IF constrain
8810
8811		/* -- IMAGES-WMF -- */
8812		if ($info['type'] == 'wmf') {
8813			$sx = $w * Mpdf::SCALE / $info['w'];
8814			$sy = -$h * Mpdf::SCALE / $info['h'];
8815			$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']);
8816		} else { 		/* -- END IMAGES-WMF -- */
8817			if ($info['type'] == 'svg') {
8818				$sx = $w * Mpdf::SCALE / $info['w'];
8819				$sy = -$h * Mpdf::SCALE / $info['h'];
8820				$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']);
8821			} else {
8822				$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']);
8823			}
8824		}
8825
8826		if ($paint) {
8827			$this->_out($outstring);
8828			if ($link) {
8829				$this->Link($x, $y, $w, $h, $link);
8830			}
8831
8832			// Avoid writing text on top of the image. // THIS WAS OUTSIDE THE if ($paint) bit!!!!!!!!!!!!!!!!
8833			$this->y = $y + $h;
8834		}
8835
8836		// Return width-height array
8837		$sizesarray['WIDTH'] = $w;
8838		$sizesarray['HEIGHT'] = $h;
8839		$sizesarray['X'] = $x; // Position before painting image
8840		$sizesarray['Y'] = $y; // Position before painting image
8841		$sizesarray['OUTPUT'] = $outstring;
8842
8843		$sizesarray['IMAGE_ID'] = $info['i'];
8844		$sizesarray['itype'] = $info['type'];
8845		$sizesarray['set-dpi'] = (isset($info['set-dpi']) ? $info['set-dpi'] : 0);
8846		return $sizesarray;
8847	}
8848
8849	// =============================================================
8850	// =============================================================
8851	// =============================================================
8852	// =============================================================
8853	// =============================================================
8854	/* -- HTML-CSS -- */
8855
8856	function _getObjAttr($t)
8857	{
8858		$c = explode("\xbb\xa4\xac", $t, 2);
8859		$c = explode(",", $c[1], 2);
8860		foreach ($c as $v) {
8861			$v = explode("=", $v, 2);
8862			$sp[$v[0]] = $v[1];
8863		}
8864		return (unserialize($sp['objattr']));
8865	}
8866
8867	function inlineObject($type, $x, $y, $objattr, $Lmargin, $widthUsed, $maxWidth, $lineHeight, $paint = false, $is_table = false)
8868	{
8869		if ($is_table) {
8870			$k = $this->shrin_k;
8871		} else {
8872			$k = 1;
8873		}
8874
8875		// NB $x is only used when paint=true
8876		// Lmargin not used
8877		$w = 0;
8878		if (isset($objattr['width'])) {
8879			$w = $objattr['width'] / $k;
8880		}
8881		$h = 0;
8882		if (isset($objattr['height'])) {
8883			$h = abs($objattr['height'] / $k);
8884		}
8885		$widthLeft = $maxWidth - $widthUsed;
8886		$maxHeight = $this->h - ($this->tMargin + $this->bMargin + 10);
8887		if ($this->fullImageHeight) {
8888			$maxHeight = $this->fullImageHeight;
8889		}
8890		// For Images
8891		if (isset($objattr['border_left'])) {
8892			$extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']) / $k;
8893			$extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']) / $k;
8894
8895			if ($type == 'image' || $type == 'barcode' || $type == 'textcircle') {
8896				$extraWidth += ($objattr['padding_left'] + $objattr['padding_right']) / $k;
8897				$extraHeight += ($objattr['padding_top'] + $objattr['padding_bottom']) / $k;
8898			}
8899		}
8900
8901		if (!isset($objattr['vertical-align'])) {
8902			if ($objattr['type'] == 'select') {
8903				$objattr['vertical-align'] = 'M';
8904			} else {
8905				$objattr['vertical-align'] = 'BS';
8906			}
8907		} // mPDF 6
8908
8909		if ($type == 'image' || (isset($objattr['subtype']) && $objattr['subtype'] == 'IMAGE')) {
8910			if (isset($objattr['itype']) && ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg')) {
8911				$file = $objattr['file'];
8912				$info = $this->formobjects[$file];
8913			} elseif (isset($objattr['file'])) {
8914				$file = $objattr['file'];
8915				$info = $this->images[$file];
8916			}
8917		}
8918		if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {
8919			$w = 0.00001;
8920			$h = 0.00001;
8921		}
8922
8923		// TEST whether need to skipline
8924		if (!$paint) {
8925			if ($type == 'hr') { // always force new line
8926				if (($y + $h + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {
8927					return [-2, $w, $h];
8928				} // New page + new line
8929				else {
8930					return [1, $w, $h];
8931				} // new line
8932			} else {
8933				// LIST MARKERS	// mPDF 6  Lists
8934				$displayheight = $h;
8935				$displaywidth = $w;
8936				if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker']) {
8937					$displayheight = 0;
8938					if ($objattr['listmarkerposition'] == 'outside') {
8939						$displaywidth = 0;
8940					}
8941				}
8942
8943				if ($widthUsed > 0 && $displaywidth > $widthLeft && (!$is_table || $type != 'image')) {  // New line needed
8944					// mPDF 6  Lists
8945					if (($y + $displayheight + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter) {
8946						return [-2, $w, $h];
8947					} // New page + new line
8948					return [1, $w, $h]; // new line
8949				} elseif ($widthUsed > 0 && $displaywidth > $widthLeft && $is_table) {  // New line needed in TABLE
8950					return [1, $w, $h]; // new line
8951				} // Will fit on line but NEW PAGE REQUIRED
8952				elseif (($y + $displayheight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {
8953					return [-1, $w, $h];
8954				} // mPDF 6  Lists
8955				else {
8956					return [0, $w, $h];
8957				}
8958			}
8959		}
8960
8961		if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {
8962			$w = 0.00001;
8963			$h = 0.00001;
8964			$objattr['BORDER-WIDTH'] = 0;
8965			$objattr['BORDER-HEIGHT'] = 0;
8966			$objattr['BORDER-X'] = $x;
8967			$objattr['BORDER-Y'] = $y;
8968			$objattr['INNER-WIDTH'] = 0;
8969			$objattr['INNER-HEIGHT'] = 0;
8970			$objattr['INNER-X'] = $x;
8971			$objattr['INNER-Y'] = $y;
8972		}
8973
8974		if ($type == 'image') {
8975			// Automatically resize to width remaining
8976			if ($w > ($widthLeft + 0.0001) && !$is_table) { // mPDF 5.7.4  0.0001 to allow for rounding errors when w==maxWidth
8977				$w = $widthLeft;
8978				$h = abs($w * $info['h'] / $info['w']);
8979			}
8980			$img_w = $w - $extraWidth;
8981			$img_h = $h - $extraHeight;
8982
8983			$objattr['BORDER-WIDTH'] = $img_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
8984			$objattr['BORDER-HEIGHT'] = $img_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
8985			$objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
8986			$objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
8987			$objattr['INNER-WIDTH'] = $img_w;
8988			$objattr['INNER-HEIGHT'] = $img_h;
8989			$objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
8990			$objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
8991			$objattr['ID'] = $info['i'];
8992		}
8993
8994		if ($type == 'input' && $objattr['subtype'] == 'IMAGE') {
8995			$img_w = $w - $extraWidth;
8996			$img_h = $h - $extraHeight;
8997			$objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
8998			$objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
8999			$objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
9000			$objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
9001			$objattr['INNER-WIDTH'] = $img_w;
9002			$objattr['INNER-HEIGHT'] = $img_h;
9003			$objattr['INNER-X'] = $x + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
9004			$objattr['INNER-Y'] = $y + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
9005			$objattr['ID'] = $info['i'];
9006		}
9007
9008		if ($type == 'barcode' || $type == 'textcircle') {
9009			$b_w = $w - $extraWidth;
9010			$b_h = $h - $extraHeight;
9011			$objattr['BORDER-WIDTH'] = $b_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
9012			$objattr['BORDER-HEIGHT'] = $b_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
9013			$objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
9014			$objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
9015			$objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
9016			$objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
9017			$objattr['INNER-WIDTH'] = $b_w;
9018			$objattr['INNER-HEIGHT'] = $b_h;
9019		}
9020
9021
9022		if ($type == 'textarea') {
9023			// Automatically resize to width remaining
9024			if ($w > $widthLeft && !$is_table) {
9025				$w = $widthLeft;
9026			}
9027			// This used to resize height to maximum remaining on page ? why. Causes problems when in table and causing a new column
9028			// if (($y + $h > $this->PageBreakTrigger) && !$this->InFooter) {
9029			// 	$h=$this->h - $y - $this->bMargin;
9030			// }
9031		}
9032
9033		if ($type == 'hr') {
9034			if ($is_table) {
9035				$objattr['INNER-WIDTH'] = $maxWidth * $objattr['W-PERCENT'] / 100;
9036				$objattr['width'] = $objattr['INNER-WIDTH'];
9037				$w = $maxWidth;
9038			} else {
9039				if ($w > $maxWidth) {
9040					$w = $maxWidth;
9041				}
9042				$objattr['INNER-WIDTH'] = $w;
9043				$w = $maxWidth;
9044			}
9045		}
9046
9047
9048
9049		if (($type == 'select') || ($type == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD'))) {
9050			// Automatically resize to width remaining
9051			if ($w > $widthLeft && !$is_table) {
9052				$w = $widthLeft;
9053			}
9054		}
9055
9056		if ($type == 'textarea' || $type == 'select' || $type == 'input') {
9057			if (isset($objattr['fontsize'])) {
9058				$objattr['fontsize'] /= $k;
9059			}
9060			if (isset($objattr['linewidth'])) {
9061				$objattr['linewidth'] /= $k;
9062			}
9063		}
9064
9065		if (!isset($objattr['BORDER-Y'])) {
9066			$objattr['BORDER-Y'] = 0;
9067		}
9068		if (!isset($objattr['BORDER-X'])) {
9069			$objattr['BORDER-X'] = 0;
9070		}
9071		if (!isset($objattr['INNER-Y'])) {
9072			$objattr['INNER-Y'] = 0;
9073		}
9074		if (!isset($objattr['INNER-X'])) {
9075			$objattr['INNER-X'] = 0;
9076		}
9077
9078		// Return width-height array
9079		$objattr['OUTER-WIDTH'] = $w;
9080		$objattr['OUTER-HEIGHT'] = $h;
9081		$objattr['OUTER-X'] = $x;
9082		$objattr['OUTER-Y'] = $y;
9083		return $objattr;
9084	}
9085
9086	/* -- END HTML-CSS -- */
9087
9088	// =============================================================
9089	// =============================================================
9090	// =============================================================
9091	// =============================================================
9092	// =============================================================
9093
9094	function SetLineJoin($mode = 0)
9095	{
9096		$s = sprintf('%d j', $mode);
9097		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineJoin']) && $this->pageoutput[$this->page]['LineJoin'] != $s) || !isset($this->pageoutput[$this->page]['LineJoin']))) {
9098			$this->_out($s);
9099		}
9100		$this->pageoutput[$this->page]['LineJoin'] = $s;
9101	}
9102
9103	function SetLineCap($mode = 2)
9104	{
9105		$s = sprintf('%d J', $mode);
9106		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineCap']) && $this->pageoutput[$this->page]['LineCap'] != $s) || !isset($this->pageoutput[$this->page]['LineCap']))) {
9107			$this->_out($s);
9108		}
9109		$this->pageoutput[$this->page]['LineCap'] = $s;
9110	}
9111
9112	function SetDash($black = false, $white = false)
9113	{
9114		if ($black and $white) {
9115			$s = sprintf('[%.3F %.3F] 0 d', $black * Mpdf::SCALE, $white * Mpdf::SCALE);
9116		} else {
9117			$s = '[] 0 d';
9118		}
9119		if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Dash']) && $this->pageoutput[$this->page]['Dash'] != $s) || !isset($this->pageoutput[$this->page]['Dash']))) {
9120			$this->_out($s);
9121		}
9122		$this->pageoutput[$this->page]['Dash'] = $s;
9123	}
9124
9125	function SetDisplayPreferences($preferences)
9126	{
9127		// String containing any or none of /HideMenubar/HideToolbar/HideWindowUI/DisplayDocTitle/CenterWindow/FitWindow
9128		$this->DisplayPreferences .= $preferences;
9129	}
9130
9131	function Ln($h = '', $collapsible = 0)
9132	{
9133		// Added collapsible to allow collapsible top-margin on new page
9134		// Line feed; default value is last cell height
9135		$this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'];
9136		if ($collapsible && ($this->y == $this->tMargin) && (!$this->ColActive)) {
9137			$h = 0;
9138		}
9139		if (is_string($h)) {
9140			$this->y+=$this->lasth;
9141		} else {
9142			$this->y+=$h;
9143		}
9144	}
9145
9146	/* -- HTML-CSS -- */
9147
9148	function DivLn($h, $level = -3, $move_y = true, $collapsible = false, $state = 0)
9149	{
9150		// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
9151		// Used in Columns and keep-with-table i.e. "kwt"
9152		// writes background block by block so it can be repositioned
9153		// and also used in writingFlowingBlock at top and bottom of blocks to move y (not to draw/paint anything)
9154		// adds lines (y) where DIV bgcolors are filled in
9155		// this->x is returned as it was
9156		// allows .00001 as nominal height used for bookmarks/annotations etc.
9157		if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)) && (!$this->ColActive)) {
9158			return;
9159		}
9160
9161		// mPDF 6 Columns
9162		//   if ($collapsible && (sprintf("%0.4f", $this->y)==sprintf("%0.4f", $this->y0)) && ($this->ColActive) && $this->CurrCol == 0) { return; }	// *COLUMNS*
9163		if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->y0)) && ($this->ColActive)) {
9164			return;
9165		} // *COLUMNS*
9166		// Still use this method if columns or keep-with-table, as it allows repositioning later
9167		// otherwise, now uses PaintDivBB()
9168		if (!$this->ColActive && !$this->kwt) {
9169			if ($move_y && !$this->ColActive) {
9170				$this->y += $h;
9171			}
9172			return;
9173		}
9174
9175		if ($level == -3) {
9176			$level = $this->blklvl;
9177		}
9178		$firstblockfill = $this->GetFirstBlockFill();
9179		if ($firstblockfill && $this->blklvl > 0 && $this->blklvl >= $firstblockfill) {
9180			$last_x = 0;
9181			$last_w = 0;
9182			$last_fc = $this->FillColor;
9183			$bak_x = $this->x;
9184			$bak_h = $this->divheight;
9185			$this->divheight = 0; // Temporarily turn off divheight - as Cell() uses it to check for PageBreak
9186			for ($blvl = $firstblockfill; $blvl <= $level; $blvl++) {
9187				$this->x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];
9188				// mPDF 6
9189				if ($this->blk[$blvl]['bgcolor']) {
9190					$this->SetFColor($this->blk[$blvl]['bgcolorarray']);
9191				}
9192				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'])) {
9193					$x = $this->x;
9194					$this->Cell(($this->blk[$blvl]['width']), $h, '', '', 0, '', 1);
9195					$this->x = $x;
9196					if (!$this->keep_block_together && !$this->writingHTMLheader && !$this->writingHTMLfooter) {
9197						// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
9198						if ($blvl == $this->blklvl) {
9199							$this->PaintDivLnBorder($state, $blvl, $h);
9200						} else {
9201							$this->PaintDivLnBorder(0, $blvl, $h);
9202						}
9203					}
9204				}
9205				$last_x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];
9206				$last_w = $this->blk[$blvl]['width'];
9207				$last_fc = $this->FillColor;
9208			}
9209			// Reset current block fill
9210			if (isset($this->blk[$this->blklvl]['bgcolorarray'])) {
9211				$bcor = $this->blk[$this->blklvl]['bgcolorarray'];
9212				$this->SetFColor($bcor);
9213			}
9214			$this->x = $bak_x;
9215			$this->divheight = $bak_h;
9216		}
9217		if ($move_y) {
9218			$this->y += $h;
9219		}
9220	}
9221
9222	/* -- END HTML-CSS -- */
9223
9224	function SetX($x)
9225	{
9226		// Set x position
9227		if ($x >= 0) {
9228			$this->x = $x;
9229		} else {
9230			$this->x = $this->w + $x;
9231		}
9232	}
9233
9234	function SetY($y)
9235	{
9236		// Set y position and reset x
9237		$this->x = $this->lMargin;
9238		if ($y >= 0) {
9239			$this->y = $y;
9240		} else {
9241			$this->y = $this->h + $y;
9242		}
9243	}
9244
9245	function SetXY($x, $y)
9246	{
9247		// Set x and y positions
9248		$this->SetY($y);
9249		$this->SetX($x);
9250	}
9251
9252	function Output($name = '', $dest = '')
9253	{
9254		$this->logger->debug(sprintf('PDF generated in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]);
9255
9256		// Finish document if necessary
9257		if ($this->state < 3) {
9258			$this->Close();
9259		}
9260
9261		if ($this->debug && error_get_last()) {
9262			$e = error_get_last();
9263			if (($e['type'] < 2048 && $e['type'] != 8) || (intval($e['type']) & intval(ini_get("error_reporting")))) {
9264				throw new \Mpdf\MpdfException(
9265					sprintf('Error detected. PDF file generation aborted: %s', $e['message']),
9266					$e['type'],
9267					1,
9268					$e['file'],
9269					$e['line']
9270				);
9271			}
9272		}
9273
9274		if (($this->PDFA || $this->PDFX) && $this->encrypted) {
9275			throw new \Mpdf\MpdfException('PDF/A1-b or PDF/X1-a does not permit encryption of documents.');
9276		}
9277
9278		if (count($this->PDFAXwarnings) && (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto))) {
9279			if ($this->PDFA) {
9280				$standard = 'PDFA/1-b';
9281				$option = '$mpdf->PDFAauto';
9282			} else {
9283				$standard = 'PDFX/1-a ';
9284				$option = '$mpdf->PDFXauto';
9285			}
9286
9287			$this->logger->warning(sprintf('PDF could not be generated as it stands as a %s compliant file.', $standard), ['context' => LogContext::PDFA_PDFX]);
9288			$this->logger->warning(sprintf('These issues can be automatically fixed by mPDF using %s = true;', $option), ['context' => LogContext::PDFA_PDFX]);
9289			$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]);
9290
9291			$this->PDFAXwarnings = array_unique($this->PDFAXwarnings);
9292			foreach ($this->PDFAXwarnings as $w) {
9293				$this->logger->warning($w, ['context' => LogContext::PDFA_PDFX]);
9294			}
9295
9296			throw new \Mpdf\MpdfException('PDFA/PDFX warnings generated. See log for further details');
9297		}
9298
9299		$this->logger->debug(sprintf('Compiled in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]);
9300		$this->logger->debug(sprintf('Peak Memory usage %s MB', number_format(memory_get_peak_usage(true) / (1024 * 1024), 2)), ['context' => LogContext::STATISTICS]);
9301		$this->logger->debug(sprintf('PDF file size %s kB', number_format(strlen($this->buffer) / 1024)), ['context' => LogContext::STATISTICS]);
9302		$this->logger->debug(sprintf('%d fonts used', count($this->fonts)), ['context' => LogContext::STATISTICS]);
9303
9304		if (is_bool($dest)) {
9305			$dest = $dest ? Destination::DOWNLOAD : Destination::FILE;
9306		}
9307
9308		$dest = strtoupper($dest);
9309		if (empty($dest)) {
9310			if (empty($name)) {
9311				$name = 'mpdf.pdf';
9312				$dest = Destination::INLINE;
9313			} else {
9314				$dest = Destination::FILE;
9315			}
9316		}
9317
9318		switch ($dest) {
9319
9320			case Destination::INLINE:
9321
9322				if (headers_sent($filename, $line)) {
9323					throw new \Mpdf\MpdfException(
9324						sprintf('Data has already been sent to output (%s at line %s), unable to output PDF file', $filename, $line)
9325					);
9326				}
9327
9328				if ($this->debug && !$this->allow_output_buffering && ob_get_contents()) {
9329					throw new \Mpdf\MpdfException('Output has already been sent from the script - PDF file generation aborted.');
9330				}
9331
9332				// We send to a browser
9333				if (PHP_SAPI !== 'cli') {
9334					header('Content-Type: application/pdf');
9335
9336					if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
9337						// don't use length if server using compression
9338						header('Content-Length: ' . strlen($this->buffer));
9339					}
9340
9341					header('Content-disposition: inline; filename="' . $name . '"');
9342					header('Cache-Control: public, must-revalidate, max-age=0');
9343					header('Pragma: public');
9344					header('X-Generator: mPDF ' . static::VERSION);
9345					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
9346					header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
9347				}
9348
9349				echo $this->buffer;
9350
9351				break;
9352
9353			case Destination::DOWNLOAD:
9354
9355				if (headers_sent()) {
9356					throw new \Mpdf\MpdfException('Data has already been sent to output, unable to output PDF file');
9357				}
9358
9359				header('Content-Description: File Transfer');
9360				header('Content-Transfer-Encoding: binary');
9361				header('Cache-Control: public, must-revalidate, max-age=0');
9362				header('Pragma: public');
9363				header('X-Generator: mPDF ' . static::VERSION);
9364				header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
9365				header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
9366				header('Content-Type: application/pdf');
9367
9368				if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
9369					// don't use length if server using compression
9370					header('Content-Length: ' . strlen($this->buffer));
9371				}
9372
9373				header('Content-Disposition: attachment; filename="' . $name . '"');
9374
9375				echo $this->buffer;
9376
9377				break;
9378
9379			case Destination::FILE:
9380				$f = fopen($name, 'wb');
9381
9382				if (!$f) {
9383					throw new \Mpdf\MpdfException(sprintf('Unable to create output file %s', $name));
9384				}
9385
9386				fwrite($f, $this->buffer, strlen($this->buffer));
9387				fclose($f);
9388
9389				break;
9390
9391			case Destination::STRING_RETURN:
9392				$this->cache->clearOld();
9393				return $this->buffer;
9394
9395			default:
9396				throw new \Mpdf\MpdfException(sprintf('Incorrect output destination %s', $dest));
9397		}
9398
9399		$this->cache->clearOld();
9400	}
9401
9402	// *****************************************************************************
9403	//                                                                             *
9404	//                             Protected methods                               *
9405	//                                                                             *
9406	// *****************************************************************************
9407	function _dochecks()
9408	{
9409		// Check for locale-related bug
9410		if (1.1 == 1) {
9411			throw new \Mpdf\MpdfException('Do not alter the locale before including mPDF');
9412		}
9413
9414		// Check for decimal separator
9415		if (sprintf('%.1f', 1.0) != '1.0') {
9416			setlocale(LC_NUMERIC, 'C');
9417		}
9418
9419		if (ini_get('mbstring.func_overload')) {
9420			throw new \Mpdf\MpdfException('Mpdf cannot function properly with mbstring.func_overload enabled');
9421		}
9422
9423		if (!function_exists('mb_substr')) {
9424			throw new \Mpdf\MpdfException('mbstring extension must be loaded in order to run mPDF');
9425		}
9426	}
9427
9428	function _puthtmlheaders()
9429	{
9430		$this->state = 2;
9431		$nb = $this->page;
9432		for ($n = 1; $n <= $nb; $n++) {
9433			if ($this->mirrorMargins && $n % 2 == 0) {
9434				$OE = 'E';
9435			} // EVEN
9436			else {
9437				$OE = 'O';
9438			}
9439			$this->page = $n;
9440			$pn = $this->docPageNum($n);
9441			if ($pn) {
9442				$pnstr = $this->pagenumPrefix . $pn . $this->pagenumSuffix;
9443			} else {
9444				$pnstr = '';
9445			}
9446
9447			$pnt = $this->docPageNumTotal($n);
9448
9449			if ($pnt) {
9450				$pntstr = $this->nbpgPrefix . $pnt . $this->nbpgSuffix;
9451			} else {
9452				$pntstr = '';
9453			}
9454
9455			if (isset($this->saveHTMLHeader[$n][$OE])) {
9456				$html = isset($this->saveHTMLHeader[$n][$OE]['html']) ? $this->saveHTMLHeader[$n][$OE]['html'] : '';
9457				$this->lMargin = $this->saveHTMLHeader[$n][$OE]['ml'];
9458				$this->rMargin = $this->saveHTMLHeader[$n][$OE]['mr'];
9459				$this->tMargin = $this->saveHTMLHeader[$n][$OE]['mh'];
9460				$this->bMargin = $this->saveHTMLHeader[$n][$OE]['mf'];
9461				$this->margin_header = $this->saveHTMLHeader[$n][$OE]['mh'];
9462				$this->margin_footer = $this->saveHTMLHeader[$n][$OE]['mf'];
9463				$this->w = $this->saveHTMLHeader[$n][$OE]['pw'];
9464				$this->h = $this->saveHTMLHeader[$n][$OE]['ph'];
9465				$rotate = (isset($this->saveHTMLHeader[$n][$OE]['rotate']) ? $this->saveHTMLHeader[$n][$OE]['rotate'] : null);
9466				$this->Reset();
9467				$this->pageoutput[$n] = [];
9468				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
9469				$this->x = $this->lMargin;
9470				$this->y = $this->margin_header;
9471				$html = str_replace('{PAGENO}', $pnstr, $html);
9472				$html = str_replace($this->aliasNbPgGp, $pntstr, $html); // {nbpg}
9473				$html = str_replace($this->aliasNbPg, $nb, $html); // {nb}
9474				$html = preg_replace_callback('/\{DATE\s+(.*?)\}/', [$this, 'date_callback'], $html); // mPDF 5.7
9475
9476				$this->HTMLheaderPageLinks = [];
9477				$this->HTMLheaderPageAnnots = [];
9478				$this->HTMLheaderPageForms = [];
9479				$this->pageBackgrounds = [];
9480
9481				$this->writingHTMLheader = true;
9482				$this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
9483				$this->writingHTMLheader = false;
9484				$this->Reset();
9485				$this->pageoutput[$n] = [];
9486
9487				$s = $this->PrintPageBackgrounds();
9488				$this->headerbuffer = $s . $this->headerbuffer;
9489				$os = '';
9490				if ($rotate) {
9491					$os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE));
9492					// To rotate the other way i.e. Header to left of page:
9493					// $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE));
9494				}
9495				$os .= $this->headerbuffer;
9496				if ($rotate) {
9497					$os .= ' Q' . "\n";
9498				}
9499
9500				// Writes over the page background but behind any other output on page
9501				$os = preg_replace(['/\\\\/', '/\$/'], ['\\\\\\\\', '\\\\$'], $os);
9502
9503				$this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]);
9504
9505				$lks = $this->HTMLheaderPageLinks;
9506				foreach ($lks as $lk) {
9507					if ($rotate) {
9508						$lw = $lk[2];
9509						$lh = $lk[3];
9510						$lk[2] = $lh;
9511						$lk[3] = $lw; // swap width and height
9512						$ax = $lk[0] / Mpdf::SCALE;
9513						$ay = $lk[1] / Mpdf::SCALE;
9514						$bx = $ay - ($lh / Mpdf::SCALE);
9515						$by = $this->w - $ax;
9516						$lk[0] = $bx * Mpdf::SCALE;
9517						$lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw;
9518					}
9519					$this->PageLinks[$n][] = $lk;
9520				}
9521				/* -- FORMS -- */
9522				foreach ($this->HTMLheaderPageForms as $f) {
9523					$this->form->forms[$f['n']] = $f;
9524				}
9525				/* -- END FORMS -- */
9526			}
9527
9528			if (isset($this->saveHTMLFooter[$n][$OE])) {
9529
9530				$html = $this->saveHTMLFooter[$this->page][$OE]['html'];
9531
9532				$this->lMargin = $this->saveHTMLFooter[$n][$OE]['ml'];
9533				$this->rMargin = $this->saveHTMLFooter[$n][$OE]['mr'];
9534				$this->tMargin = $this->saveHTMLFooter[$n][$OE]['mh'];
9535				$this->bMargin = $this->saveHTMLFooter[$n][$OE]['mf'];
9536				$this->margin_header = $this->saveHTMLFooter[$n][$OE]['mh'];
9537				$this->margin_footer = $this->saveHTMLFooter[$n][$OE]['mf'];
9538				$this->w = $this->saveHTMLFooter[$n][$OE]['pw'];
9539				$this->h = $this->saveHTMLFooter[$n][$OE]['ph'];
9540				$rotate = (isset($this->saveHTMLFooter[$n][$OE]['rotate']) ? $this->saveHTMLFooter[$n][$OE]['rotate'] : null);
9541				$this->Reset();
9542				$this->pageoutput[$n] = [];
9543				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
9544				$this->x = $this->lMargin;
9545				$top_y = $this->y = $this->h - $this->margin_footer;
9546
9547				// if bottom-margin==0, corrects to avoid division by zero
9548				if ($this->y == $this->h) {
9549					$top_y = $this->y = ($this->h + 0.01);
9550				}
9551
9552				$html = str_replace('{PAGENO}', $pnstr, $html);
9553				$html = str_replace($this->aliasNbPgGp, $pntstr, $html); // {nbpg}
9554				$html = str_replace($this->aliasNbPg, $nb, $html); // {nb}
9555				$html = preg_replace_callback('/\{DATE\s+(.*?)\}/', [$this, 'date_callback'], $html); // mPDF 5.7
9556
9557
9558				$this->HTMLheaderPageLinks = [];
9559				$this->HTMLheaderPageAnnots = [];
9560				$this->HTMLheaderPageForms = [];
9561				$this->pageBackgrounds = [];
9562
9563				$this->writingHTMLfooter = true;
9564				$this->InFooter = true;
9565				$this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
9566				$this->InFooter = false;
9567				$this->Reset();
9568				$this->pageoutput[$n] = [];
9569
9570				$fheight = $this->y - $top_y;
9571				$adj = -$fheight;
9572
9573				$s = $this->PrintPageBackgrounds(-$adj);
9574				$this->headerbuffer = $s . $this->headerbuffer;
9575				$this->writingHTMLfooter = false; // mPDF 5.7.3  (moved after PrintPageBackgrounds so can adjust position of images in footer)
9576
9577				$os = '';
9578				$os .= $this->StartTransform(true) . "\n";
9579
9580				if ($rotate) {
9581					$os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE));
9582					// To rotate the other way i.e. Header to left of page:
9583					// $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE));
9584				}
9585
9586				$os .= $this->transformTranslate(0, $adj, true) . "\n";
9587				$os .= $this->headerbuffer;
9588
9589				if ($rotate) {
9590					$os .= ' Q' . "\n";
9591				}
9592
9593				$os .= $this->StopTransform(true) . "\n";
9594
9595				// Writes over the page background but behind any other output on page
9596				$os = preg_replace(['/\\\\/', '/\$/'], ['\\\\\\\\', '\\\\$'], $os);
9597
9598				$this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]);
9599
9600				$lks = $this->HTMLheaderPageLinks;
9601
9602				foreach ($lks as $lk) {
9603
9604					$lk[1] -= $adj * Mpdf::SCALE;
9605
9606					if ($rotate) {
9607						$lw = $lk[2];
9608						$lh = $lk[3];
9609						$lk[2] = $lh;
9610						$lk[3] = $lw; // swap width and height
9611
9612						$ax = $lk[0] / Mpdf::SCALE;
9613						$ay = $lk[1] / Mpdf::SCALE;
9614						$bx = $ay - ($lh / Mpdf::SCALE);
9615						$by = $this->w - $ax;
9616						$lk[0] = $bx * Mpdf::SCALE;
9617						$lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw;
9618					}
9619
9620					$this->PageLinks[$n][] = $lk;
9621				}
9622
9623				/* -- FORMS -- */
9624				foreach ($this->HTMLheaderPageForms as $f) {
9625					$f['y'] += $adj;
9626					$this->form->forms[$f['n']] = $f;
9627				}
9628				/* -- END FORMS -- */
9629			}
9630		}
9631
9632		$this->page = $nb;
9633		$this->state = 1;
9634	}
9635
9636	function _putpages()
9637	{
9638		$nb = $this->page;
9639		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9640
9641		if ($this->DefOrientation == 'P') {
9642			$defwPt = $this->fwPt;
9643			$defhPt = $this->fhPt;
9644		} else {
9645			$defwPt = $this->fhPt;
9646			$defhPt = $this->fwPt;
9647		}
9648		$annotid = (3 + 2 * $nb);
9649
9650		// Active Forms
9651		$totaladdnum = 0;
9652		for ($n = 1; $n <= $nb; $n++) {
9653			if (isset($this->PageLinks[$n])) {
9654				$totaladdnum += count($this->PageLinks[$n]);
9655			}
9656			/* -- ANNOTATIONS -- */
9657			if (isset($this->PageAnnots[$n])) {
9658				foreach ($this->PageAnnots[$n] as $k => $pl) {
9659					if (!empty($pl['opt']['popup']) || !empty($pl['opt']['file'])) {
9660						$totaladdnum += 2;
9661					} else {
9662						$totaladdnum++;
9663					}
9664				}
9665			}
9666			/* -- END ANNOTATIONS -- */
9667
9668			/* -- FORMS -- */
9669			if (count($this->form->forms) > 0) {
9670				$this->form->countPageForms($n, $totaladdnum);
9671			}
9672			/* -- END FORMS -- */
9673		}
9674		/* -- FORMS -- */
9675		// Make a note in the radio button group of the obj_id it will have
9676		$ctr = 0;
9677		if (count($this->form->form_radio_groups)) {
9678			foreach ($this->form->form_radio_groups as $name => $frg) {
9679				$this->form->form_radio_groups[$name]['obj_id'] = $annotid + $totaladdnum + $ctr;
9680				$ctr++;
9681			}
9682		}
9683		/* -- END FORMS -- */
9684
9685		// Select unused fonts (usually default font)
9686		$unused = [];
9687		foreach ($this->fonts as $fk => $font) {
9688			if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {
9689				$unused[] = $fk;
9690			}
9691		}
9692
9693
9694		for ($n = 1; $n <= $nb; $n++) {
9695			$thispage = $this->pages[$n];
9696			if (isset($this->OrientationChanges[$n])) {
9697				$hPt = $this->pageDim[$n]['w'] * Mpdf::SCALE;
9698				$wPt = $this->pageDim[$n]['h'] * Mpdf::SCALE;
9699				$owidthPt_LR = $this->pageDim[$n]['outer_width_TB'] * Mpdf::SCALE;
9700				$owidthPt_TB = $this->pageDim[$n]['outer_width_LR'] * Mpdf::SCALE;
9701			} else {
9702				$wPt = $this->pageDim[$n]['w'] * Mpdf::SCALE;
9703				$hPt = $this->pageDim[$n]['h'] * Mpdf::SCALE;
9704				$owidthPt_LR = $this->pageDim[$n]['outer_width_LR'] * Mpdf::SCALE;
9705				$owidthPt_TB = $this->pageDim[$n]['outer_width_TB'] * Mpdf::SCALE;
9706			}
9707			// Remove references to unused fonts (usually default font)
9708			foreach ($unused as $fk) {
9709				if ($this->fonts[$fk]['sip'] || $this->fonts[$fk]['smp']) {
9710					foreach ($this->fonts[$fk]['subsetfontids'] as $k => $fid) {
9711						$thispage = preg_replace('/\s\/F' . $fid . ' \d[\d.]* Tf\s/is', ' ', $thispage);
9712					}
9713				} else {
9714					$thispage = preg_replace('/\s\/F' . $this->fonts[$fk]['i'] . ' \d[\d.]* Tf\s/is', ' ', $thispage);
9715				}
9716			}
9717			// Clean up repeated /GS1 gs statements
9718			// For some reason using + for repetition instead of {2,20} crashes PHP Script Interpreter ???
9719			$thispage = preg_replace('/(\/GS1 gs\n){2,20}/', "/GS1 gs\n", $thispage);
9720
9721			$thispage = preg_replace('/(\s*___BACKGROUND___PATTERNS' . $this->uniqstr . '\s*)/', " ", $thispage);
9722			$thispage = preg_replace('/(\s*___HEADER___MARKER' . $this->uniqstr . '\s*)/', " ", $thispage);
9723			$thispage = preg_replace('/(\s*___PAGE___START' . $this->uniqstr . '\s*)/', " ", $thispage);
9724			$thispage = preg_replace('/(\s*___TABLE___BACKGROUNDS' . $this->uniqstr . '\s*)/', " ", $thispage);
9725			// mPDF 5.7.3 TRANSFORMS
9726			while (preg_match('/(\% BTR(.*?)\% ETR)/is', $thispage, $m)) {
9727				$thispage = preg_replace('/(\% BTR.*?\% ETR)/is', '', $thispage, 1) . "\n" . $m[2];
9728			}
9729
9730			// Page
9731			$this->_newobj();
9732			$this->_out('<</Type /Page');
9733			$this->_out('/Parent 1 0 R');
9734			if (isset($this->OrientationChanges[$n])) {
9735				$this->_out(sprintf('/MediaBox [0 0 %.3F %.3F]', $hPt, $wPt));
9736				// If BleedBox is defined, it must be larger than the TrimBox, but smaller than the MediaBox
9737				$bleedMargin = $this->pageDim[$n]['bleedMargin'] * Mpdf::SCALE;
9738				if ($bleedMargin && ($owidthPt_TB || $owidthPt_LR)) {
9739					$x0 = $owidthPt_TB - $bleedMargin;
9740					$y0 = $owidthPt_LR - $bleedMargin;
9741					$x1 = $hPt - $owidthPt_TB + $bleedMargin;
9742					$y1 = $wPt - $owidthPt_LR + $bleedMargin;
9743					$this->_out(sprintf('/BleedBox [%.3F %.3F %.3F %.3F]', $x0, $y0, $x1, $y1));
9744				}
9745				$this->_out(sprintf('/TrimBox [%.3F %.3F %.3F %.3F]', $owidthPt_TB, $owidthPt_LR, ($hPt - $owidthPt_TB), ($wPt - $owidthPt_LR)));
9746				if (isset($this->OrientationChanges[$n]) && $this->displayDefaultOrientation) {
9747					if ($this->DefOrientation == 'P') {
9748						$this->_out('/Rotate 270');
9749					} else {
9750						$this->_out('/Rotate 90');
9751					}
9752				}
9753			} // elseif($wPt != $defwPt || $hPt != $defhPt) {
9754			else {
9755				$this->_out(sprintf('/MediaBox [0 0 %.3F %.3F]', $wPt, $hPt));
9756				$bleedMargin = $this->pageDim[$n]['bleedMargin'] * Mpdf::SCALE;
9757				if ($bleedMargin && ($owidthPt_TB || $owidthPt_LR)) {
9758					$x0 = $owidthPt_LR - $bleedMargin;
9759					$y0 = $owidthPt_TB - $bleedMargin;
9760					$x1 = $wPt - $owidthPt_LR + $bleedMargin;
9761					$y1 = $hPt - $owidthPt_TB + $bleedMargin;
9762					$this->_out(sprintf('/BleedBox [%.3F %.3F %.3F %.3F]', $x0, $y0, $x1, $y1));
9763				}
9764				$this->_out(sprintf('/TrimBox [%.3F %.3F %.3F %.3F]', $owidthPt_LR, $owidthPt_TB, ($wPt - $owidthPt_LR), ($hPt - $owidthPt_TB)));
9765			}
9766			$this->_out('/Resources 2 0 R');
9767
9768			// Important to keep in RGB colorSpace when using transparency
9769			if (!$this->PDFA && !$this->PDFX) {
9770				if ($this->restrictColorSpace == 3) {
9771					$this->_out('/Group << /Type /Group /S /Transparency /CS /DeviceCMYK >> ');
9772				} elseif ($this->restrictColorSpace == 1) {
9773					$this->_out('/Group << /Type /Group /S /Transparency /CS /DeviceGray >> ');
9774				} else {
9775					$this->_out('/Group << /Type /Group /S /Transparency /CS /DeviceRGB >> ');
9776				}
9777			}
9778
9779			$annotsnum = 0;
9780			$embeddedfiles = []; // mPDF 5.7.2 /EmbeddedFiles
9781
9782			if (isset($this->PageLinks[$n])) {
9783				$annotsnum += count($this->PageLinks[$n]);
9784			}
9785			/* -- ANNOTATIONS -- */
9786			if (isset($this->PageAnnots[$n])) {
9787				foreach ($this->PageAnnots[$n] as $k => $pl) {
9788					if (!empty($pl['opt']['file'])) {
9789						$embeddedfiles[$annotsnum + 1] = true;
9790					} // mPDF 5.7.2 /EmbeddedFiles
9791					if (!empty($pl['opt']['popup']) || !empty($pl['opt']['file'])) {
9792						$annotsnum += 2;
9793					} else {
9794						$annotsnum++;
9795					}
9796					$this->PageAnnots[$n][$k]['pageobj'] = $this->n;
9797				}
9798			}
9799			/* -- END ANNOTATIONS -- */
9800
9801			/* -- FORMS -- */
9802			// Active Forms
9803			$formsnum = 0;
9804			if (count($this->form->forms) > 0) {
9805				foreach ($this->form->forms as $val) {
9806					if ($val['page'] == $n) {
9807						$formsnum++;
9808					}
9809				}
9810			}
9811			/* -- END FORMS -- */
9812			if ($annotsnum || $formsnum) {
9813				$s = '/Annots [ ';
9814				for ($i = 0; $i < $annotsnum; $i++) {
9815					if (!isset($embeddedfiles[$i])) {
9816						$s .= ($annotid + $i) . ' 0 R ';
9817					} // mPDF 5.7.2 /EmbeddedFiles
9818				}
9819				$annotid += $annotsnum;
9820				/* -- FORMS -- */
9821				if (count($this->form->forms) > 0) {
9822					$this->form->addFormIds($n, $s, $annotid);
9823				}
9824				/* -- END FORMS -- */
9825				$s .= '] ';
9826				$this->_out($s);
9827			}
9828
9829			$this->_out('/Contents ' . ($this->n + 1) . ' 0 R>>');
9830			$this->_out('endobj');
9831
9832			// Page content
9833			$this->_newobj();
9834			$p = ($this->compress) ? gzcompress($thispage) : $thispage;
9835			$this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
9836			$this->_putstream($p);
9837			$this->_out('endobj');
9838		}
9839		$this->_putannots(); // mPDF 5.7.2
9840		// Pages root
9841		$this->offsets[1] = strlen($this->buffer);
9842		$this->_out('1 0 obj');
9843		$this->_out('<</Type /Pages');
9844		$kids = '/Kids [';
9845		for ($i = 0; $i < $nb; $i++) {
9846			$kids.=(3 + 2 * $i) . ' 0 R ';
9847		}
9848		$this->_out($kids . ']');
9849		$this->_out('/Count ' . $nb);
9850		$this->_out(sprintf('/MediaBox [0 0 %.3F %.3F]', $defwPt, $defhPt));
9851		$this->_out('>>');
9852		$this->_out('endobj');
9853	}
9854
9855	/**
9856	 * @since 5.7.2
9857	 */
9858	function _putannots()
9859	{
9860		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9861
9862		$nb = $this->page;
9863
9864		for ($n = 1; $n <= $nb; $n++) {
9865
9866			$annotobjs = [];
9867
9868			if (isset($this->PageLinks[$n]) || isset($this->PageAnnots[$n]) || count($this->form->forms) > 0) {
9869
9870				$wPt = $this->pageDim[$n]['w'] * Mpdf::SCALE;
9871				$hPt = $this->pageDim[$n]['h'] * Mpdf::SCALE;
9872
9873				// Links
9874				if (isset($this->PageLinks[$n])) {
9875
9876					foreach ($this->PageLinks[$n] as $key => $pl) {
9877
9878						$this->_newobj();
9879						$annot = '';
9880
9881						$rect = sprintf('%.3F %.3F %.3F %.3F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
9882
9883						$annot .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . ']';
9884						// Removed as causing undesired effects in Chrome PDF viewer https://github.com/mpdf/mpdf/issues/283
9885						// $annot .= ' /Contents ' . $this->_UTF16BEtextstring($pl[4]);
9886						$annot .= ' /NM ' . $this->_textstring(sprintf('%04u-%04u', $n, $key));
9887						$annot .= ' /M ' . $this->_textstring('D:' . date('YmdHis'));
9888
9889						$annot .= ' /Border [0 0 0]';
9890
9891						// Use this (instead of /Border) to specify border around link
9892
9893						// $annot .= ' /BS <</W 1';	// Width on points; 0 = no line
9894						// $annot .= ' /S /D';		// style - [S]olid, [D]ashed, [B]eveled, [I]nset, [U]nderline
9895						// $annot .= ' /D [3 2]';		// Dash array - if dashed
9896						// $annot .= ' >>';
9897						// $annot .= ' /C [1 0 0]';	// Color RGB
9898
9899						if ($this->PDFA || $this->PDFX) {
9900							$annot .= ' /F 28';
9901						}
9902
9903						if (strpos($pl[4], '@') === 0) {
9904
9905							$p = substr($pl[4], 1);
9906							// $h=isset($this->OrientationChanges[$p]) ? $wPt : $hPt;
9907							$htarg = $this->pageDim[$p]['h'] * Mpdf::SCALE;
9908							$annot .= sprintf(' /Dest [%d 0 R /XYZ 0 %.3F null]>>', 1 + 2 * $p, $htarg);
9909
9910						} elseif (is_string($pl[4])) {
9911
9912							$annot .= ' /A <</S /URI /URI ' . $this->_textstring($pl[4]) . '>> >>';
9913
9914						} else {
9915
9916							$l = $this->links[$pl[4]];
9917							// may not be set if #link points to non-existent target
9918							if (isset($this->pageDim[$l[0]]['h'])) {
9919								$htarg = $this->pageDim[$l[0]]['h'] * Mpdf::SCALE;
9920							} else {
9921								$htarg = $this->h * Mpdf::SCALE;
9922							} // doesn't really matter
9923
9924							$annot .= sprintf(' /Dest [%d 0 R /XYZ 0 %.3F null]>>', 1 + 2 * $l[0], $htarg - $l[1] * Mpdf::SCALE);
9925						}
9926
9927						$this->_out($annot);
9928						$this->_out('endobj');
9929
9930					}
9931				}
9932
9933				/* -- ANNOTATIONS -- */
9934				if (isset($this->PageAnnots[$n])) {
9935
9936					foreach ($this->PageAnnots[$n] as $key => $pl) {
9937
9938						$fileAttachment = (bool) $pl['opt']['file'];
9939
9940						if ($fileAttachment && !$this->allowAnnotationFiles) {
9941							$this->logger->warning('Embedded files for annotations have to be allowed explicitly with "allowAnnotationFiles" config key');
9942							$fileAttachment = false;
9943						}
9944
9945						$this->_newobj();
9946						$annot = '';
9947						$pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
9948						$x = $pl['x'];
9949
9950						if ($this->annotMargin <> 0 || $x == 0 || $x < 0) { // Odd page
9951							$x = ($wPt / Mpdf::SCALE) - $this->annotMargin;
9952						}
9953
9954						$w = $h = 0;
9955						$a = $x * Mpdf::SCALE;
9956						$b = $hPt - ($pl['y'] * Mpdf::SCALE);
9957
9958						$annot .= '<</Type /Annot ';
9959
9960						if ($fileAttachment) {
9961							$annot .= '/Subtype /FileAttachment ';
9962							// Need to set a size for FileAttachment icons
9963							if ($pl['opt']['icon'] == 'Paperclip') {
9964								$w = 8.235;
9965								$h = 20;
9966							} elseif ($pl['opt']['icon'] == 'Tag') {
9967								$w = 20;
9968								$h = 16;
9969							} elseif ($pl['opt']['icon'] == 'Graph') {
9970								$w = 20;
9971								$h = 20;
9972							} else {
9973								$w = 14;
9974								$h = 20;
9975							}
9976
9977							// PushPin
9978							$f = $pl['opt']['file'];
9979							$f = preg_replace('/^.*\//', '', $f);
9980							$f = preg_replace('/[^a-zA-Z0-9._]/', '', $f);
9981
9982							$annot .= '/FS <</Type /Filespec /F (' . $f . ')';
9983							$annot .= '/EF <</F ' . ($this->n + 1) . ' 0 R>>';
9984							$annot .= '>>';
9985
9986						} else {
9987							$annot .= '/Subtype /Text';
9988							$w = 20;
9989							$h = 20;  // mPDF 6
9990						}
9991
9992						$rect = sprintf('%.3F %.3F %.3F %.3F', $a, $b - $h, $a + $w, $b);
9993						$annot .= ' /Rect [' . $rect . ']';
9994
9995						// contents = description of file in free text
9996						$annot .= ' /Contents ' . $this->_UTF16BEtextstring($pl['txt']);
9997
9998						$annot .= ' /NM ' . $this->_textstring(sprintf('%04u-%04u', $n, (2000 + $key)));
9999						$annot .= ' /M ' . $this->_textstring('D:' . date('YmdHis'));
10000						$annot .= ' /CreationDate ' . $this->_textstring('D:' . date('YmdHis'));
10001						$annot .= ' /Border [0 0 0]';
10002
10003						if ($this->PDFA || $this->PDFX) {
10004							$annot .= ' /F 28';
10005							$annot .= ' /CA 1';
10006						} elseif ($pl['opt']['ca'] > 0) {
10007							$annot .= ' /CA ' . $pl['opt']['ca'];
10008						}
10009
10010						$annotcolor = ' /C [';
10011						if (isset($pl['opt']['c']) and $pl['opt']['c']) {
10012							$col = $pl['opt']['c'];
10013							if ($col{0} == 3 || $col{0} == 5) {
10014								$annotcolor .= sprintf("%.3F %.3F %.3F", ord($col{1}) / 255, ord($col{2}) / 255, ord($col{3}) / 255);
10015							} elseif ($col{0} == 1) {
10016								$annotcolor .= sprintf("%.3F", ord($col{1}) / 255);
10017							} elseif ($col{0} == 4 || $col{0} == 6) {
10018								$annotcolor .= sprintf("%.3F %.3F %.3F %.3F", ord($col{1}) / 100, ord($col{2}) / 100, ord($col{3}) / 100, ord($col{4}) / 100);
10019							} else {
10020								$annotcolor .= '1 1 0';
10021							}
10022						} else {
10023							$annotcolor .= '1 1 0';
10024						}
10025						$annotcolor .= ']';
10026						$annot .= $annotcolor;
10027
10028						// Usually Author
10029						// Use as Title for fileattachment
10030						if (isset($pl['opt']['t']) and is_string($pl['opt']['t'])) {
10031							$annot .= ' /T ' . $this->_UTF16BEtextstring($pl['opt']['t']);
10032						}
10033
10034						if ($fileAttachment) {
10035							$iconsapp = ['Paperclip', 'Graph', 'PushPin', 'Tag'];
10036						} else {
10037							$iconsapp = ['Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph'];
10038						}
10039
10040						if (isset($pl['opt']['icon']) and in_array($pl['opt']['icon'], $iconsapp)) {
10041							$annot .= ' /Name /' . $pl['opt']['icon'];
10042						} elseif ($fileAttachment) {
10043							$annot .= ' /Name /PushPin';
10044						} else {
10045							$annot .= ' /Name /Note';
10046						}
10047
10048						if (!$fileAttachment) {
10049							///Subj is PDF 1.5 spec.
10050							if (isset($pl['opt']['subj']) && !$this->PDFA && !$this->PDFX) {
10051								$annot .= ' /Subj ' . $this->_UTF16BEtextstring($pl['opt']['subj']);
10052							}
10053							if (!empty($pl['opt']['popup'])) {
10054								$annot .= ' /Open true';
10055								$annot .= ' /Popup ' . ($this->n + 1) . ' 0 R';
10056							} else {
10057								$annot .= ' /Open false';
10058							}
10059						}
10060
10061						$annot .= ' /P ' . $pl['pageobj'] . ' 0 R';
10062						$annot .= '>>';
10063						$this->_out($annot);
10064						$this->_out('endobj');
10065
10066						if ($fileAttachment) {
10067							$file = @file_get_contents($pl['opt']['file']);
10068							if (!$file) {
10069								throw new \Mpdf\MpdfException('mPDF Error: Cannot access file attachment - ' . $pl['opt']['file']);
10070							}
10071							$filestream = gzcompress($file);
10072							$this->_newobj();
10073							$this->_out('<</Type /EmbeddedFile');
10074							$this->_out('/Length ' . strlen($filestream));
10075							$this->_out('/Filter /FlateDecode');
10076							$this->_out('>>');
10077							$this->_putstream($filestream);
10078							$this->_out('endobj');
10079						} elseif (!empty($pl['opt']['popup'])) {
10080							$this->_newobj();
10081							$annot = '';
10082							if (is_array($pl['opt']['popup']) && isset($pl['opt']['popup'][0])) {
10083								$x = $pl['opt']['popup'][0] * Mpdf::SCALE;
10084							} else {
10085								$x = $pl['x'] * Mpdf::SCALE;
10086							}
10087							if (is_array($pl['opt']['popup']) && isset($pl['opt']['popup'][1])) {
10088								$y = $hPt - ($pl['opt']['popup'][1] * Mpdf::SCALE);
10089							} else {
10090								$y = $hPt - ($pl['y'] * Mpdf::SCALE);
10091							}
10092							if (is_array($pl['opt']['popup']) && isset($pl['opt']['popup'][2])) {
10093								$w = $pl['opt']['popup'][2] * Mpdf::SCALE;
10094							} else {
10095								$w = 180;
10096							}
10097							if (is_array($pl['opt']['popup']) && isset($pl['opt']['popup'][3])) {
10098								$h = $pl['opt']['popup'][3] * Mpdf::SCALE;
10099							} else {
10100								$h = 120;
10101							}
10102							$rect = sprintf('%.3F %.3F %.3F %.3F', $x, $y - $h, $x + $w, $y);
10103							$annot .= '<</Type /Annot /Subtype /Popup /Rect [' . $rect . ']';
10104							$annot .= ' /M ' . $this->_textstring('D:' . date('YmdHis'));
10105							if ($this->PDFA || $this->PDFX) {
10106								$annot .= ' /F 28';
10107							}
10108							$annot .= ' /Parent ' . ($this->n - 1) . ' 0 R';
10109							$annot .= '>>';
10110							$this->_out($annot);
10111							$this->_out('endobj');
10112						}
10113					}
10114				}
10115
10116				/* -- END ANNOTATIONS -- */
10117
10118				/* -- FORMS -- */
10119				// Active Forms
10120				if (count($this->form->forms) > 0) {
10121					$this->form->_putFormItems($n, $hPt);
10122				}
10123				/* -- END FORMS -- */
10124			}
10125		}
10126
10127		/* -- FORMS -- */
10128		// Active Forms - Radio Button Group entries
10129		// Output Radio Button Group form entries (radio_on_obj_id already determined)
10130		if (count($this->form->form_radio_groups)) {
10131			$this->form->_putRadioItems($n);
10132		}
10133		/* -- END FORMS -- */
10134	}
10135
10136	/* -- ANNOTATIONS -- */
10137
10138	function Annotation($text, $x = 0, $y = 0, $icon = 'Note', $author = '', $subject = '', $opacity = 0, $colarray = false, $popup = '', $file = '')
10139	{
10140		if (is_array($colarray) && count($colarray) == 3) {
10141			$colarray = $this->colorConverter->convert('rgb(' . $colarray[0] . ',' . $colarray[1] . ',' . $colarray[2] . ')', $this->PDFAXwarnings);
10142		}
10143		if ($colarray === false) {
10144			$colarray = $this->colorConverter->convert('yellow', $this->PDFAXwarnings);
10145		}
10146		if ($x == 0) {
10147			$x = $this->x;
10148		}
10149		if ($y == 0) {
10150			$y = $this->y;
10151		}
10152		$page = $this->page;
10153		if ($page < 1) { // Document has not been started - assume it's for first page
10154			$page = 1;
10155			if ($x == 0) {
10156				$x = $this->lMargin;
10157			}
10158			if ($y == 0) {
10159				$y = $this->tMargin;
10160			}
10161		}
10162
10163		if ($this->PDFA || $this->PDFX) {
10164			if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
10165				$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)";
10166			}
10167			$x = ($this->w) - $this->rMargin * 0.66;
10168		}
10169		if (!$this->annotMargin) {
10170			$y -= $this->FontSize / 2;
10171		}
10172
10173		if (!$opacity && $this->annotMargin) {
10174			$opacity = 1;
10175		} elseif (!$opacity) {
10176			$opacity = $this->annotOpacity;
10177		}
10178
10179		$an = ['txt' => $text, 'x' => $x, 'y' => $y, 'opt' => ['Icon' => $icon, 'T' => $author, 'Subj' => $subject, 'C' => $colarray, 'CA' => $opacity, 'popup' => $popup, 'file' => $file]];
10180
10181		if ($this->keep_block_together) { // don't write yet
10182			return;
10183		} elseif ($this->table_rotate) {
10184			$this->tbrot_Annots[$this->page][] = $an;
10185			return;
10186		} elseif ($this->kwt) {
10187			$this->kwt_Annots[$this->page][] = $an;
10188			return;
10189		}
10190		if ($this->writingHTMLheader || $this->writingHTMLfooter) {
10191			$this->HTMLheaderPageAnnots[] = $an;
10192			return;
10193		}
10194		// Put an Annotation on the page
10195		$this->PageAnnots[$page][] = $an;
10196		/* -- COLUMNS -- */
10197		// Save cross-reference to Column buffer
10198		$ref = count($this->PageAnnots[$this->page]) - 1;
10199		$this->columnAnnots[$this->CurrCol][intval($this->x)][intval($this->y)] = $ref;
10200		/* -- END COLUMNS -- */
10201	}
10202
10203	/* -- END ANNOTATIONS -- */
10204
10205	function _putfonts()
10206	{
10207		$nf = $this->n;
10208		foreach ($this->FontFiles as $fontkey => $info) {
10209			// TrueType embedded
10210			if (isset($info['type']) && $info['type'] == 'TTF' && !$info['sip'] && !$info['smp']) {
10211				$used = true;
10212				$asSubset = false;
10213				foreach ($this->fonts as $k => $f) {
10214					if (isset($f['fontkey']) && $f['fontkey'] == $fontkey && $f['type'] == 'TTF') {
10215						$used = $f['used'];
10216						if ($used) {
10217							$nChars = (ord($f['cw'][0]) << 8) + ord($f['cw'][1]);
10218							$usage = intval(count($f['subset']) * 100 / $nChars);
10219							$fsize = $info['length1'];
10220							// Always subset the very large TTF files
10221							if ($fsize > ($this->maxTTFFilesize * 1024)) {
10222								$asSubset = true;
10223							} elseif ($usage < $this->percentSubset) {
10224								$asSubset = true;
10225							}
10226						}
10227						if ($this->PDFA || $this->PDFX) {
10228							$asSubset = false;
10229						}
10230						$this->fonts[$k]['asSubset'] = $asSubset;
10231						break;
10232					}
10233				}
10234				if ($used && !$asSubset) {
10235					// Font file embedding
10236					$this->_newobj();
10237					$this->FontFiles[$fontkey]['n'] = $this->n;
10238					$originalsize = $info['length1'];
10239					if ($this->repackageTTF || $this->fonts[$fontkey]['TTCfontID'] > 0 || $this->fonts[$fontkey]['useOTL'] > 0) { // mPDF 5.7.1
10240						// First see if there is a cached compressed file
10241						if ($this->fontCache->has($fontkey . '.ps.z')) {
10242							$font = $this->fontCache->load($fontkey . '.ps.z');
10243							include $this->fontCache->tempFilename($fontkey . '.ps.php'); // sets $originalsize (of repackaged font)
10244						} else {
10245							$ttf = new TTFontFile($this->fontCache, $this->fontDescriptor);
10246							$font = $ttf->repackageTTF($this->FontFiles[$fontkey]['ttffile'], $this->fonts[$fontkey]['TTCfontID'], $this->debugfonts, $this->fonts[$fontkey]['useOTL']); // mPDF 5.7.1
10247
10248							$originalsize = strlen($font);
10249							$font = gzcompress($font);
10250							unset($ttf);
10251
10252							$len = "<?php \n";
10253							$len .= '$originalsize=' . $originalsize . ";\n";
10254
10255							$this->fontCache->binaryWrite($fontkey . '.ps.z', $font);
10256							$this->fontCache->write($fontkey . '.ps.php', $len);
10257						}
10258					} else {
10259						// First see if there is a cached compressed file
10260						if ($this->fontCache->has($fontkey . '.z')) {
10261							$font = $this->fontCache->load($fontkey . '.z', 'rb');
10262						} else {
10263							$font = file_get_contents($this->FontFiles[$fontkey]['ttffile']);
10264							$font = gzcompress($font);
10265							$this->fontCache->binaryWrite($fontkey . '.z', $font);
10266						}
10267					}
10268
10269					$this->_out('<</Length ' . strlen($font));
10270					$this->_out('/Filter /FlateDecode');
10271					$this->_out('/Length1 ' . $originalsize);
10272					$this->_out('>>');
10273					$this->_putstream($font);
10274					$this->_out('endobj');
10275				}
10276			}
10277		}
10278
10279		$nfonts = count($this->fonts);
10280		$fctr = 1;
10281		foreach ($this->fonts as $k => $font) {
10282			// Font objects
10283			$type = $font['type'];
10284			$name = $font['name'];
10285			if ((!isset($font['used']) || !$font['used']) && $type == 'TTF') {
10286				continue;
10287			}
10288
10289			// @log Writing fonts
10290
10291			if (isset($font['asSubset'])) {
10292				$asSubset = $font['asSubset'];
10293			} else {
10294				$asSubset = '';
10295			}
10296			/* -- CJK-FONTS -- */
10297			if ($type == 'Type0') {  // = Adobe CJK Fonts
10298				$this->fonts[$k]['n'] = $this->n + 1;
10299				$this->_newobj();
10300				$this->_out('<</Type /Font');
10301				$this->_putType0($font);
10302			} else { 			/* -- END CJK-FONTS -- */
10303				if ($type == 'core') {
10304					// Standard font
10305					$this->fonts[$k]['n'] = $this->n + 1;
10306					if ($this->PDFA || $this->PDFX) {
10307						throw new \Mpdf\MpdfException('Core fonts are not allowed in PDF/A1-b or PDFX/1-a files (Times, Helvetica, Courier etc.)');
10308					}
10309					$this->_newobj();
10310					$this->_out('<</Type /Font');
10311					$this->_out('/BaseFont /' . $name);
10312					$this->_out('/Subtype /Type1');
10313					if ($name != 'Symbol' && $name != 'ZapfDingbats') {
10314						$this->_out('/Encoding /WinAnsiEncoding');
10315					}
10316					$this->_out('>>');
10317					$this->_out('endobj');
10318				} // TrueType embedded SUBSETS for SIP (CJK extB containing Supplementary Ideographic Plane 2)
10319			// Or Unicode Plane 1 - Supplementary Multilingual Plane
10320				elseif ($type == 'TTF' && ($font['sip'] || $font['smp'])) {
10321					if (!$font['used']) {
10322						continue;
10323					}
10324					$ssfaid = "AA";
10325					$ttf = new TTFontFile($this->fontCache, $this->fontDescriptor);
10326					for ($sfid = 0; $sfid < count($font['subsetfontids']); $sfid++) {
10327						$this->fonts[$k]['n'][$sfid] = $this->n + 1;  // NB an array for subset
10328						$subsetname = 'MPDF' . $ssfaid . '+' . $font['name'];
10329						$ssfaid++;
10330
10331						/* For some strange reason a subset ($sfid > 0) containing less than 97 characters causes an error
10332						  so fill up the array */
10333						for ($j = count($font['subsets'][$sfid]); $j < 98; $j++) {
10334							$font['subsets'][$sfid][$j] = 0;
10335						}
10336
10337						$subset = $font['subsets'][$sfid];
10338						unset($subset[0]);
10339						$ttfontstream = $ttf->makeSubsetSIP($font['ttffile'], $subset, $font['TTCfontID'], $this->debugfonts, $font['useOTL']); // mPDF 5.7.1
10340						$ttfontsize = strlen($ttfontstream);
10341						$fontstream = gzcompress($ttfontstream);
10342						$widthstring = '';
10343						$toUnistring = '';
10344
10345
10346						foreach ($font['subsets'][$sfid] as $cp => $u) {
10347							$w = $this->_getCharWidth($font['cw'], $u);
10348							if ($w !== false) {
10349								$widthstring .= $w . ' ';
10350							} else {
10351								$widthstring .= round($ttf->defaultWidth) . ' ';
10352							}
10353							if ($u > 65535) {
10354								$utf8 = chr(($u >> 18) + 240) . chr((($u >> 12) & 63) + 128) . chr((($u >> 6) & 63) + 128) . chr(($u & 63) + 128);
10355								$utf16 = mb_convert_encoding($utf8, 'UTF-16BE', 'UTF-8');
10356								$l1 = ord($utf16[0]);
10357								$h1 = ord($utf16[1]);
10358								$l2 = ord($utf16[2]);
10359								$h2 = ord($utf16[3]);
10360								$toUnistring .= sprintf("<%02s> <%02s%02s%02s%02s>\n", strtoupper(dechex($cp)), strtoupper(dechex($l1)), strtoupper(dechex($h1)), strtoupper(dechex($l2)), strtoupper(dechex($h2)));
10361							} else {
10362								$toUnistring .= sprintf("<%02s> <%04s>\n", strtoupper(dechex($cp)), strtoupper(dechex($u)));
10363							}
10364						}
10365
10366						// Additional Type1 or TrueType font
10367						$this->_newobj();
10368						$this->_out('<</Type /Font');
10369						$this->_out('/BaseFont /' . $subsetname);
10370						$this->_out('/Subtype /TrueType');
10371						$this->_out('/FirstChar 0 /LastChar ' . (count($font['subsets'][$sfid]) - 1));
10372						$this->_out('/Widths ' . ($this->n + 1) . ' 0 R');
10373						$this->_out('/FontDescriptor ' . ($this->n + 2) . ' 0 R');
10374						$this->_out('/ToUnicode ' . ($this->n + 3) . ' 0 R');
10375						$this->_out('>>');
10376						$this->_out('endobj');
10377
10378						// Widths
10379						$this->_newobj();
10380						$this->_out('[' . $widthstring . ']');
10381						$this->_out('endobj');
10382
10383						// Descriptor
10384						$this->_newobj();
10385						$s = '<</Type /FontDescriptor /FontName /' . $subsetname . "\n";
10386						foreach ($font['desc'] as $kd => $v) {
10387							if ($kd == 'Flags') {
10388								$v = $v | 4;
10389								$v = $v & ~32;
10390							} // SYMBOLIC font flag
10391							$s.=' /' . $kd . ' ' . $v . "\n";
10392						}
10393						$s.='/FontFile2 ' . ($this->n + 2) . ' 0 R';
10394						$this->_out($s . '>>');
10395						$this->_out('endobj');
10396
10397						// ToUnicode
10398						$this->_newobj();
10399						$toUni = "/CIDInit /ProcSet findresource begin\n";
10400						$toUni .= "12 dict begin\n";
10401						$toUni .= "begincmap\n";
10402						$toUni .= "/CIDSystemInfo\n";
10403						$toUni .= "<</Registry (Adobe)\n";
10404						$toUni .= "/Ordering (UCS)\n";
10405						$toUni .= "/Supplement 0\n";
10406						$toUni .= ">> def\n";
10407						$toUni .= "/CMapName /Adobe-Identity-UCS def\n";
10408						$toUni .= "/CMapType 2 def\n";
10409						$toUni .= "1 begincodespacerange\n";
10410						$toUni .= "<00> <FF>\n";
10411						// $toUni .= sprintf("<00> <%02s>\n", strtoupper(dechex(count($font['subsets'][$sfid])-1)));
10412						$toUni .= "endcodespacerange\n";
10413						$toUni .= count($font['subsets'][$sfid]) . " beginbfchar\n";
10414						$toUni .= $toUnistring;
10415						$toUni .= "endbfchar\n";
10416						$toUni .= "endcmap\n";
10417						$toUni .= "CMapName currentdict /CMap defineresource pop\n";
10418						$toUni .= "end\n";
10419						$toUni .= "end\n";
10420						$this->_out('<</Length ' . (strlen($toUni)) . '>>');
10421						$this->_putstream($toUni);
10422						$this->_out('endobj');
10423
10424						// Font file
10425						$this->_newobj();
10426						$this->_out('<</Length ' . strlen($fontstream));
10427						$this->_out('/Filter /FlateDecode');
10428						$this->_out('/Length1 ' . $ttfontsize);
10429						$this->_out('>>');
10430						$this->_putstream($fontstream);
10431						$this->_out('endobj');
10432					} // foreach subset
10433					unset($ttf);
10434				} // TrueType embedded SUBSETS or FULL
10435				elseif ($type == 'TTF') {
10436					$this->fonts[$k]['n'] = $this->n + 1;
10437					if ($asSubset) {
10438						$ssfaid = "A";
10439						$ttf = new TTFontFile($this->fontCache, $this->fontDescriptor);
10440						$fontname = 'MPDFA' . $ssfaid . '+' . $font['name'];
10441						$subset = $font['subset'];
10442						unset($subset[0]);
10443						$ttfontstream = $ttf->makeSubset($font['ttffile'], $subset, $font['TTCfontID'], $this->debugfonts, $font['useOTL']);
10444						$ttfontsize = strlen($ttfontstream);
10445						$fontstream = gzcompress($ttfontstream);
10446						$codeToGlyph = $ttf->codeToGlyph;
10447						unset($codeToGlyph[0]);
10448					} else {
10449						$fontname = $font['name'];
10450					}
10451					// Type0 Font
10452					// A composite font - a font composed of other fonts, organized hierarchically
10453					$this->_newobj();
10454					$this->_out('<</Type /Font');
10455					$this->_out('/Subtype /Type0');
10456					$this->_out('/BaseFont /' . $fontname . '');
10457					$this->_out('/Encoding /Identity-H');
10458					$this->_out('/DescendantFonts [' . ($this->n + 1) . ' 0 R]');
10459					$this->_out('/ToUnicode ' . ($this->n + 2) . ' 0 R');
10460					$this->_out('>>');
10461					$this->_out('endobj');
10462
10463					// CIDFontType2
10464					// A CIDFont whose glyph descriptions are based on TrueType font technology
10465					$this->_newobj();
10466					$this->_out('<</Type /Font');
10467					$this->_out('/Subtype /CIDFontType2');
10468					$this->_out('/BaseFont /' . $fontname . '');
10469					$this->_out('/CIDSystemInfo ' . ($this->n + 2) . ' 0 R');
10470					$this->_out('/FontDescriptor ' . ($this->n + 3) . ' 0 R');
10471					if (isset($font['desc']['MissingWidth'])) {
10472						$this->_out('/DW ' . $font['desc']['MissingWidth'] . '');
10473					}
10474
10475					if (!$asSubset && $this->fontCache->has($font['fontkey'] . '.cw')) {
10476						$w = $this->fontCache->load($font['fontkey'] . '.cw');
10477						$this->_out($w);
10478					} else {
10479						$this->_putTTfontwidths($font, $asSubset, ($asSubset ? $ttf->maxUni : 0));
10480					}
10481
10482					$this->_out('/CIDToGIDMap ' . ($this->n + 4) . ' 0 R');
10483					$this->_out('>>');
10484					$this->_out('endobj');
10485
10486					// ToUnicode
10487					$this->_newobj();
10488					$toUni = "/CIDInit /ProcSet findresource begin\n";
10489					$toUni .= "12 dict begin\n";
10490					$toUni .= "begincmap\n";
10491					$toUni .= "/CIDSystemInfo\n";
10492					$toUni .= "<</Registry (Adobe)\n";
10493					$toUni .= "/Ordering (UCS)\n";
10494					$toUni .= "/Supplement 0\n";
10495					$toUni .= ">> def\n";
10496					$toUni .= "/CMapName /Adobe-Identity-UCS def\n";
10497					$toUni .= "/CMapType 2 def\n";
10498					$toUni .= "1 begincodespacerange\n";
10499					$toUni .= "<0000> <FFFF>\n";
10500					$toUni .= "endcodespacerange\n";
10501					$toUni .= "1 beginbfrange\n";
10502					$toUni .= "<0000> <FFFF> <0000>\n";
10503					$toUni .= "endbfrange\n";
10504					$toUni .= "endcmap\n";
10505					$toUni .= "CMapName currentdict /CMap defineresource pop\n";
10506					$toUni .= "end\n";
10507					$toUni .= "end\n";
10508					$this->_out('<</Length ' . (strlen($toUni)) . '>>');
10509					$this->_putstream($toUni);
10510					$this->_out('endobj');
10511
10512
10513					// CIDSystemInfo dictionary
10514					$this->_newobj();
10515					$this->_out('<</Registry (Adobe)');
10516					$this->_out('/Ordering (UCS)');
10517					$this->_out('/Supplement 0');
10518					$this->_out('>>');
10519					$this->_out('endobj');
10520
10521					// Font descriptor
10522					$this->_newobj();
10523					$this->_out('<</Type /FontDescriptor');
10524					$this->_out('/FontName /' . $fontname);
10525					foreach ($font['desc'] as $kd => $v) {
10526						if ($asSubset && $kd == 'Flags') {
10527							$v = $v | 4;
10528							$v = $v & ~32;
10529						} // SYMBOLIC font flag
10530						$this->_out(' /' . $kd . ' ' . $v);
10531					}
10532					if ($font['panose']) {
10533						$this->_out(' /Style << /Panose <' . $font['panose'] . '> >>');
10534					}
10535					if ($asSubset) {
10536						$this->_out('/FontFile2 ' . ($this->n + 2) . ' 0 R');
10537					} elseif ($font['fontkey']) {
10538						// obj ID of a stream containing a TrueType font program
10539						$this->_out('/FontFile2 ' . $this->FontFiles[$font['fontkey']]['n'] . ' 0 R');
10540					}
10541					$this->_out('>>');
10542					$this->_out('endobj');
10543
10544					// Embed CIDToGIDMap
10545					// A specification of the mapping from CIDs to glyph indices
10546					if ($asSubset) {
10547						$cidtogidmap = '';
10548						$cidtogidmap = str_pad('', 256 * 256 * 2, "\x00");
10549						foreach ($codeToGlyph as $cc => $glyph) {
10550							$cidtogidmap[$cc * 2] = chr($glyph >> 8);
10551							$cidtogidmap[$cc * 2 + 1] = chr($glyph & 0xFF);
10552						}
10553						$cidtogidmap = gzcompress($cidtogidmap);
10554					} else {
10555						// First see if there is a cached CIDToGIDMapfile
10556						$cidtogidmap = '';
10557						if ($this->fontCache->has($font['fontkey'] . '.cgm')) {
10558							$cidtogidmap = $this->fontCache->load($font['fontkey'] . '.cgm');
10559						} else {
10560							$ttf = new TTFontFile($this->fontCache, $this->fontDescriptor);
10561							$charToGlyph = $ttf->getCTG($font['ttffile'], $font['TTCfontID'], $this->debugfonts, $font['useOTL']);
10562							$cidtogidmap = str_pad('', 256 * 256 * 2, "\x00");
10563							foreach ($charToGlyph as $cc => $glyph) {
10564								$cidtogidmap[$cc * 2] = chr($glyph >> 8);
10565								$cidtogidmap[$cc * 2 + 1] = chr($glyph & 0xFF);
10566							}
10567							unset($ttf);
10568							$cidtogidmap = gzcompress($cidtogidmap);
10569							$this->fontCache->binaryWrite($font['fontkey'] . '.cgm', $cidtogidmap);
10570						}
10571					}
10572					$this->_newobj();
10573					$this->_out('<</Length ' . strlen($cidtogidmap) . '');
10574					$this->_out('/Filter /FlateDecode');
10575					$this->_out('>>');
10576					$this->_putstream($cidtogidmap);
10577					$this->_out('endobj');
10578
10579					// Font file
10580					if ($asSubset) {
10581						$this->_newobj();
10582						$this->_out('<</Length ' . strlen($fontstream));
10583						$this->_out('/Filter /FlateDecode');
10584						$this->_out('/Length1 ' . $ttfontsize);
10585						$this->_out('>>');
10586						$this->_putstream($fontstream);
10587						$this->_out('endobj');
10588						unset($ttf);
10589					}
10590				} else {
10591					throw new \Mpdf\MpdfException('Unsupported font type: ' . $type . ' (' . $name . ')');
10592				}
10593			}
10594		}
10595	}
10596
10597	function _putTTfontwidths(&$font, $asSubset, $maxUni)
10598	{
10599		if ($asSubset && $this->fontCache->has($font['fontkey'] . '.cw127.php')) {
10600			include $this->fontCache->tempFilename($font['fontkey'] . '.cw127.php');
10601			$startcid = 128;
10602		} else {
10603			$rangeid = 0;
10604			$range = [];
10605			$prevcid = -2;
10606			$prevwidth = -1;
10607			$interval = false;
10608			$startcid = 1;
10609		}
10610		if ($asSubset) {
10611			$cwlen = $maxUni + 1;
10612		} else {
10613			$cwlen = (strlen($font['cw']) / 2);
10614		}
10615
10616		// for each character
10617		for ($cid = $startcid; $cid < $cwlen; $cid++) {
10618			if ($cid == 128 && $asSubset && (!$this->fontCache->has($font['fontkey'] . '.cw127.php'))) {
10619				$cw127 = '<?php' . "\n";
10620				$cw127 .= '$rangeid=' . $rangeid . ";\n";
10621				$cw127 .= '$prevcid=' . $prevcid . ";\n";
10622				$cw127 .= '$prevwidth=' . $prevwidth . ";\n";
10623				if ($interval) {
10624					$cw127 .= '$interval=true' . ";\n";
10625				} else {
10626					$cw127 .= '$interval=false' . ";\n";
10627				}
10628				$cw127 .= '$range=' . var_export($range, true) . ";\n";
10629				$this->fontCache->write($font['fontkey'] . '.cw127.php', $cw127);
10630			}
10631
10632			$character1 = isset($font['cw'][$cid * 2]) ? $font['cw'][$cid * 2] : '';
10633			$character2 = isset($font['cw'][$cid * 2 + 1]) ? $font['cw'][$cid * 2 + 1] : '';
10634
10635			if ($character1 == "\00" && $character2 == "\00") {
10636				continue;
10637			}
10638
10639			$width = (ord($character1) << 8) + ord($character2);
10640
10641			if ($width == 65535) {
10642				$width = 0;
10643			}
10644
10645			if ($asSubset && $cid > 255 && (!isset($font['subset'][$cid]) || !$font['subset'][$cid])) {
10646				continue;
10647			}
10648
10649			if ($asSubset && $cid > 0xFFFF) {
10650				continue;
10651			} // mPDF 6
10652
10653			if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) {
10654				if ($cid == ($prevcid + 1)) {
10655					// consecutive CID
10656					if ($width == $prevwidth) {
10657						if ($width == $range[$rangeid][0]) {
10658							$range[$rangeid][] = $width;
10659						} else {
10660							array_pop($range[$rangeid]);
10661							// new range
10662							$rangeid = $prevcid;
10663							$range[$rangeid] = [];
10664							$range[$rangeid][] = $prevwidth;
10665							$range[$rangeid][] = $width;
10666						}
10667						$interval = true;
10668						$range[$rangeid]['interval'] = true;
10669					} else {
10670						if ($interval) {
10671							// new range
10672							$rangeid = $cid;
10673							$range[$rangeid] = [];
10674							$range[$rangeid][] = $width;
10675						} else {
10676							$range[$rangeid][] = $width;
10677						}
10678						$interval = false;
10679					}
10680				} else {
10681					// new range
10682					$rangeid = $cid;
10683					$range[$rangeid] = [];
10684					$range[$rangeid][] = $width;
10685					$interval = false;
10686				}
10687				$prevcid = $cid;
10688				$prevwidth = $width;
10689			}
10690		}
10691		$w = $this->_putfontranges($range);
10692		$this->_out($w);
10693		if (!$asSubset) {
10694			$this->fontCache->binaryWrite($font['fontkey'] . '.cw', $w);
10695		}
10696	}
10697
10698	function _putfontranges(&$range)
10699	{
10700		// optimize ranges
10701		$prevk = -1;
10702		$nextk = -1;
10703		$prevint = false;
10704		foreach ($range as $k => $ws) {
10705			$cws = count($ws);
10706			if (($k == $nextk) and ( !$prevint) and ( (!isset($ws['interval'])) or ( $cws < 4))) {
10707				if (isset($range[$k]['interval'])) {
10708					unset($range[$k]['interval']);
10709				}
10710				$range[$prevk] = array_merge($range[$prevk], $range[$k]);
10711				unset($range[$k]);
10712			} else {
10713				$prevk = $k;
10714			}
10715			$nextk = $k + $cws;
10716			if (isset($ws['interval'])) {
10717				if ($cws > 3) {
10718					$prevint = true;
10719				} else {
10720					$prevint = false;
10721				}
10722				unset($range[$k]['interval']);
10723				--$nextk;
10724			} else {
10725				$prevint = false;
10726			}
10727		}
10728		// output data
10729		$w = '';
10730		foreach ($range as $k => $ws) {
10731			if (count(array_count_values($ws)) == 1) {
10732				// interval mode is more compact
10733				$w .= ' ' . $k . ' ' . ($k + count($ws) - 1) . ' ' . $ws[0];
10734			} else {
10735				// range mode
10736				$w .= ' ' . $k . ' [ ' . implode(' ', $ws) . ' ]' . "\n";
10737			}
10738		}
10739		return '/W [' . $w . ' ]';
10740	}
10741
10742	function _putfontwidths(&$font, $cidoffset = 0)
10743	{
10744		ksort($font['cw']);
10745		unset($font['cw'][65535]);
10746		$rangeid = 0;
10747		$range = [];
10748		$prevcid = -2;
10749		$prevwidth = -1;
10750		$interval = false;
10751		// for each character
10752		foreach ($font['cw'] as $cid => $width) {
10753			$cid -= $cidoffset;
10754			if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) {
10755				if ($cid == ($prevcid + 1)) {
10756					// consecutive CID
10757					if ($width == $prevwidth) {
10758						if ($width == $range[$rangeid][0]) {
10759							$range[$rangeid][] = $width;
10760						} else {
10761							array_pop($range[$rangeid]);
10762							// new range
10763							$rangeid = $prevcid;
10764							$range[$rangeid] = [];
10765							$range[$rangeid][] = $prevwidth;
10766							$range[$rangeid][] = $width;
10767						}
10768						$interval = true;
10769						$range[$rangeid]['interval'] = true;
10770					} else {
10771						if ($interval) {
10772							// new range
10773							$rangeid = $cid;
10774							$range[$rangeid] = [];
10775							$range[$rangeid][] = $width;
10776						} else {
10777							$range[$rangeid][] = $width;
10778						}
10779						$interval = false;
10780					}
10781				} else {
10782					// new range
10783					$rangeid = $cid;
10784					$range[$rangeid] = [];
10785					$range[$rangeid][] = $width;
10786					$interval = false;
10787				}
10788				$prevcid = $cid;
10789				$prevwidth = $width;
10790			}
10791		}
10792		$this->_out($this->_putfontranges($range));
10793	}
10794
10795	/* -- CJK-FONTS -- */
10796
10797	// from class PDF_Chinese CJK EXTENSIONS
10798	function _putType0(&$font)
10799	{
10800		// Type0
10801		$this->_out('/Subtype /Type0');
10802		$this->_out('/BaseFont /' . $font['name'] . '-' . $font['CMap']);
10803		$this->_out('/Encoding /' . $font['CMap']);
10804		$this->_out('/DescendantFonts [' . ($this->n + 1) . ' 0 R]');
10805		$this->_out('>>');
10806		$this->_out('endobj');
10807		// CIDFont
10808		$this->_newobj();
10809		$this->_out('<</Type /Font');
10810		$this->_out('/Subtype /CIDFontType0');
10811		$this->_out('/BaseFont /' . $font['name']);
10812
10813		$cidinfo = '/Registry ' . $this->_textstring('Adobe');
10814		$cidinfo .= ' /Ordering ' . $this->_textstring($font['registry']['ordering']);
10815		$cidinfo .= ' /Supplement ' . $font['registry']['supplement'];
10816		$this->_out('/CIDSystemInfo <<' . $cidinfo . '>>');
10817
10818		$this->_out('/FontDescriptor ' . ($this->n + 1) . ' 0 R');
10819		if (isset($font['MissingWidth'])) {
10820			$this->_out('/DW ' . $font['MissingWidth'] . '');
10821		}
10822		$this->_putfontwidths($font, 31);
10823		$this->_out('>>');
10824		$this->_out('endobj');
10825
10826		// Font descriptor
10827		$this->_newobj();
10828		$s = '<</Type /FontDescriptor /FontName /' . $font['name'];
10829		foreach ($font['desc'] as $k => $v) {
10830			if ($k != 'Style') {
10831				$s .= ' /' . $k . ' ' . $v . '';
10832			}
10833		}
10834		$this->_out($s . '>>');
10835		$this->_out('endobj');
10836	}
10837
10838	/* -- END CJK-FONTS -- */
10839
10840	function _putimages()
10841	{
10842		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
10843
10844		foreach ($this->images as $file => $info) {
10845
10846			$this->_newobj();
10847
10848			$this->images[$file]['n'] = $this->n;
10849
10850			$this->_out('<</Type /XObject');
10851			$this->_out('/Subtype /Image');
10852			$this->_out('/Width ' . $info['w']);
10853			$this->_out('/Height ' . $info['h']);
10854
10855			if (isset($info['interpolation']) && $info['interpolation']) {
10856				$this->_out('/Interpolate true'); // mPDF 6 - image interpolation shall be performed by a conforming reader
10857			}
10858
10859			if (isset($info['masked'])) {
10860				$this->_out('/SMask ' . ($this->n - 1) . ' 0 R');
10861			}
10862
10863			// set color space
10864			$icc = false;
10865			if (isset($info['icc']) and ( $info['icc'] !== false)) {
10866				// ICC Colour Space
10867				$icc = true;
10868				$this->_out('/ColorSpace [/ICCBased ' . ($this->n + 1) . ' 0 R]');
10869			} elseif ($info['cs'] == 'Indexed') {
10870				if ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3)) {
10871					throw new \Mpdf\MpdfException("PDFA1-b and PDFX/1-a files do not permit using mixed colour space (" . $file . ").");
10872				}
10873				$this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal']) / 3 - 1) . ' ' . ($this->n + 1) . ' 0 R]');
10874			} else {
10875				$this->_out('/ColorSpace /' . $info['cs']);
10876				if ($info['cs'] == 'DeviceCMYK') {
10877					if ($this->PDFA && $this->restrictColorSpace != 3) {
10878						throw new \Mpdf\MpdfException("PDFA1-b does not permit Images using mixed colour space (" . $file . ").");
10879					}
10880					if ($info['type'] == 'jpg') {
10881						$this->_out('/Decode [1 0 1 0 1 0 1 0]');
10882					}
10883				} elseif ($info['cs'] == 'DeviceRGB' && ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3))) {
10884					throw new \Mpdf\MpdfException("PDFA1-b and PDFX/1-a files do not permit using mixed colour space (" . $file . ").");
10885				}
10886			}
10887
10888			$this->_out('/BitsPerComponent ' . $info['bpc']);
10889
10890			if (isset($info['f']) && $info['f']) {
10891				$this->_out('/Filter /' . $info['f']);
10892			}
10893
10894			if (isset($info['parms'])) {
10895				$this->_out($info['parms']);
10896			}
10897
10898			if (isset($info['trns']) and is_array($info['trns'])) {
10899				$trns = '';
10900				for ($i = 0; $i < count($info['trns']); $i++) {
10901					$trns.=$info['trns'][$i] . ' ' . $info['trns'][$i] . ' ';
10902				}
10903				$this->_out('/Mask [' . $trns . ']');
10904			}
10905
10906			$this->_out('/Length ' . strlen($info['data']) . '>>');
10907			$this->_putstream($info['data']);
10908
10909			unset($this->images[$file]['data']);
10910
10911			$this->_out('endobj');
10912
10913			if ($icc) { // ICC colour profile
10914				$this->_newobj();
10915				$icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
10916				$this->_out('<</N ' . $info['ch'] . ' ' . $filter . '/Length ' . strlen($icc) . '>>');
10917				$this->_putstream($icc);
10918				$this->_out('endobj');
10919			} elseif ($info['cs'] == 'Indexed') { // Palette
10920				$this->_newobj();
10921				$pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
10922				$this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>');
10923				$this->_putstream($pal);
10924				$this->_out('endobj');
10925			}
10926		}
10927	}
10928
10929	private function getVersionString()
10930	{
10931		$return = self::VERSION;
10932		$headFile = __DIR__ . '/../.git/HEAD';
10933		if (file_exists($headFile)) {
10934			$ref = file($headFile);
10935			$path = explode('/', $ref[0], 3);
10936			$branch = isset($path[2]) ? trim($path[2]) : '';
10937			$revFile = __DIR__ . '/../.git/refs/heads/' . $branch;
10938			if ($branch && file_exists($revFile)) {
10939				$rev = file($revFile);
10940				$rev = substr($rev[0], 0, 7);
10941				$return .= ' (' . $rev . ')';
10942			}
10943		}
10944
10945		return $return;
10946	}
10947
10948	function _putinfo()
10949	{
10950		$this->_out('/Producer ' . $this->_UTF16BEtextstring('mPDF ' . $this->getVersionString()));
10951		if (!empty($this->title)) {
10952			$this->_out('/Title ' . $this->_UTF16BEtextstring($this->title));
10953		}
10954		if (!empty($this->subject)) {
10955			$this->_out('/Subject ' . $this->_UTF16BEtextstring($this->subject));
10956		}
10957		if (!empty($this->author)) {
10958			$this->_out('/Author ' . $this->_UTF16BEtextstring($this->author));
10959		}
10960		if (!empty($this->keywords)) {
10961			$this->_out('/Keywords ' . $this->_UTF16BEtextstring($this->keywords));
10962		}
10963		if (!empty($this->creator)) {
10964			$this->_out('/Creator ' . $this->_UTF16BEtextstring($this->creator));
10965		}
10966		foreach ($this->customProperties as $key => $value) {
10967			$this->_out('/' . $key . ' ' . $this->_UTF16BEtextstring($value));
10968		}
10969
10970		$z = date('O'); // +0200
10971		$offset = substr($z, 0, 3) . "'" . substr($z, 3, 2) . "'";
10972		$this->_out('/CreationDate ' . $this->_textstring(date('YmdHis') . $offset));
10973		$this->_out('/ModDate ' . $this->_textstring(date('YmdHis') . $offset));
10974		if ($this->PDFX) {
10975			$this->_out('/Trapped/False');
10976			$this->_out('/GTS_PDFXVersion(PDF/X-1a:2003)');
10977		}
10978	}
10979
10980	function _putmetadata()
10981	{
10982		$this->_newobj();
10983		$this->MetadataRoot = $this->n;
10984		$Producer = 'mPDF ' . self::VERSION;
10985		$z = date('O'); // +0200
10986		$offset = substr($z, 0, 3) . ':' . substr($z, 3, 2);
10987		$CreationDate = date('Y-m-d\TH:i:s') . $offset; // 2006-03-10T10:47:26-05:00 2006-06-19T09:05:17Z
10988		$uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0x0fff) | 0x4000, random_int(0, 0x3fff) | 0x8000, random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff));
10989
10990
10991		$m = '<?xpacket begin="' . chr(239) . chr(187) . chr(191) . '" id="W5M0MpCehiHzreSzNTczkc9d"?>' . "\n"; // begin = FEFF BOM
10992		$m .= ' <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="3.1-701">' . "\n";
10993		$m .= '  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' . "\n";
10994		$m .= '   <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">' . "\n";
10995		$m .= '    <pdf:Producer>' . $Producer . '</pdf:Producer>' . "\n";
10996		if (!empty($this->keywords)) {
10997			$m .= '    <pdf:Keywords>' . $this->keywords . '</pdf:Keywords>' . "\n";
10998		}
10999		$m .= '   </rdf:Description>' . "\n";
11000
11001		$m .= '   <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:xmp="http://ns.adobe.com/xap/1.0/">' . "\n";
11002		$m .= '    <xmp:CreateDate>' . $CreationDate . '</xmp:CreateDate>' . "\n";
11003		$m .= '    <xmp:ModifyDate>' . $CreationDate . '</xmp:ModifyDate>' . "\n";
11004		$m .= '    <xmp:MetadataDate>' . $CreationDate . '</xmp:MetadataDate>' . "\n";
11005		if (!empty($this->creator)) {
11006			$m .= '    <xmp:CreatorTool>' . $this->creator . '</xmp:CreatorTool>' . "\n";
11007		}
11008		$m .= '   </rdf:Description>' . "\n";
11009
11010		// DC elements
11011		$m .= '   <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:dc="http://purl.org/dc/elements/1.1/">' . "\n";
11012		$m .= '    <dc:format>application/pdf</dc:format>' . "\n";
11013		if (!empty($this->title)) {
11014			$m .= '    <dc:title>
11015	 <rdf:Alt>
11016	  <rdf:li xml:lang="x-default">' . $this->title . '</rdf:li>
11017	 </rdf:Alt>
11018	</dc:title>' . "\n";
11019		}
11020		if (!empty($this->keywords)) {
11021			$m .= '    <dc:subject>
11022	 <rdf:Bag>
11023	  <rdf:li>' . $this->keywords . '</rdf:li>
11024	 </rdf:Bag>
11025	</dc:subject>' . "\n";
11026		}
11027		if (!empty($this->subject)) {
11028			$m .= '    <dc:description>
11029	 <rdf:Alt>
11030	  <rdf:li xml:lang="x-default">' . $this->subject . '</rdf:li>
11031	 </rdf:Alt>
11032	</dc:description>' . "\n";
11033		}
11034		if (!empty($this->author)) {
11035			$m .= '    <dc:creator>
11036	 <rdf:Seq>
11037	  <rdf:li>' . $this->author . '</rdf:li>
11038	 </rdf:Seq>
11039	</dc:creator>' . "\n";
11040		}
11041		$m .= '   </rdf:Description>' . "\n";
11042
11043		if (!empty($this->additionalXmpRdf)) {
11044			$m .= $this->additionalXmpRdf;
11045		}
11046
11047		// This bit is specific to PDFX-1a
11048		if ($this->PDFX) {
11049			$m .= '   <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/" pdfx:Apag_PDFX_Checkup="1.3" pdfx:GTS_PDFXConformance="PDF/X-1a:2003" pdfx:GTS_PDFXVersion="PDF/X-1:2003"/>' . "\n";
11050		} // This bit is specific to PDFA-1b
11051		elseif ($this->PDFA) {
11052
11053			if (strpos($this->PDFAversion, '-') === false) {
11054				throw new \Mpdf\MpdfException(sprintf('PDFA version (%s) is not valid. (Use: 1-B, 3-B, etc.)', $this->PDFAversion));
11055			}
11056
11057			list($part, $conformance) = explode('-', strtoupper($this->PDFAversion));
11058			$m .= '   <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/" >' . "\n";
11059			$m .= '    <pdfaid:part>' . $part . '</pdfaid:part>' . "\n";
11060			$m .= '    <pdfaid:conformance>' . $conformance . '</pdfaid:conformance>' . "\n";
11061			if ($part === '1' && $conformance === 'B') {
11062				$m .= '    <pdfaid:amd>2005</pdfaid:amd>' . "\n";
11063			}
11064			$m .= '   </rdf:Description>' . "\n";
11065		}
11066
11067		$m .= '   <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">' . "\n";
11068		$m .= '    <xmpMM:DocumentID>uuid:' . $uuid . '</xmpMM:DocumentID>' . "\n";
11069		$m .= '   </rdf:Description>' . "\n";
11070		$m .= '  </rdf:RDF>' . "\n";
11071		$m .= ' </x:xmpmeta>' . "\n";
11072		$m .= str_repeat(str_repeat(' ', 100) . "\n", 20); // 2-4kB whitespace padding required
11073		$m .= '<?xpacket end="w"?>'; // "r" read only
11074		$this->_out('<</Type/Metadata/Subtype/XML/Length ' . strlen($m) . '>>');
11075		$this->_putstream($m);
11076		$this->_out('endobj');
11077	}
11078
11079	function _putoutputintent()
11080	{
11081		$this->_newobj();
11082		$this->OutputIntentRoot = $this->n;
11083		$this->_out('<</Type /OutputIntent');
11084
11085		$ICCProfile = preg_replace('/_/', ' ', basename($this->ICCProfile, '.icc'));
11086
11087		if ($this->PDFA) {
11088			$this->_out('/S /GTS_PDFA1');
11089			if ($this->ICCProfile) {
11090				$this->_out('/Info (' . $ICCProfile . ')');
11091				$this->_out('/OutputConditionIdentifier (Custom)');
11092				$this->_out('/OutputCondition ()');
11093			} else {
11094				$this->_out('/Info (sRGB IEC61966-2.1)');
11095				$this->_out('/OutputConditionIdentifier (sRGB IEC61966-2.1)');
11096				$this->_out('/OutputCondition ()');
11097			}
11098			$this->_out('/DestOutputProfile ' . ($this->n + 1) . ' 0 R');
11099		} elseif ($this->PDFX) { // always a CMYK profile
11100			$this->_out('/S /GTS_PDFX');
11101			if ($this->ICCProfile) {
11102				$this->_out('/Info (' . $ICCProfile . ')');
11103				$this->_out('/OutputConditionIdentifier (Custom)');
11104				$this->_out('/OutputCondition ()');
11105				$this->_out('/DestOutputProfile ' . ($this->n + 1) . ' 0 R');
11106			} else {
11107				$this->_out('/Info (CGATS TR 001)');
11108				$this->_out('/OutputConditionIdentifier (CGATS TR 001)');
11109				$this->_out('/OutputCondition (CGATS TR 001 (SWOP))');
11110				$this->_out('/RegistryName (http://www.color.org)');
11111			}
11112		}
11113		$this->_out('>>');
11114		$this->_out('endobj');
11115
11116		if ($this->PDFX && !$this->ICCProfile) {
11117			return;
11118		}
11119
11120		$this->_newobj();
11121
11122		if ($this->ICCProfile) {
11123			if (!file_exists($this->ICCProfile)) {
11124				throw new \Mpdf\MpdfException(sprintf('Unable to find ICC profile "%s"', $this->ICCProfile));
11125			}
11126			$s = file_get_contents($this->ICCProfile);
11127		} else {
11128			$s = file_get_contents(__DIR__ . '/../data/iccprofiles/sRGB_IEC61966-2-1.icc');
11129		}
11130
11131		if ($this->compress) {
11132			$s = gzcompress($s);
11133		}
11134
11135		$this->_out('<<');
11136
11137		if ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3)) {
11138			$this->_out('/N 4');
11139		} else {
11140			$this->_out('/N 3');
11141		}
11142
11143		if ($this->compress) {
11144			$this->_out('/Filter /FlateDecode ');
11145		}
11146
11147		$this->_out('/Length ' . strlen($s) . '>>');
11148		$this->_putstream($s);
11149		$this->_out('endobj');
11150	}
11151
11152	function _putAssociatedFiles()
11153	{
11154		if (!function_exists('gzcompress')) {
11155			throw new \Mpdf\MpdfException('ext-zlib is required for compression of associated files');
11156		}
11157
11158		// for each file, we create the spec object + the stream object
11159		foreach ($this->associatedFiles as $k => $file) {
11160			// spec
11161			$this->_newobj();
11162			$this->associatedFiles[$k]['_root'] = $this->n; // we store the root ref of object for future reference (e.g. /EmbeddedFiles catalog)
11163			$this->_out('<</F ' . $this->_textstring($file['name']));
11164			if ($file['description']) {
11165				$this->_out('/Desc ' . $this->_textstring($file['description']));
11166			}
11167			$this->_out('/Type /Filespec');
11168			$this->_out('/EF <<');
11169			$this->_out('/F ' . ($this->n + 1) . ' 0 R');
11170			$this->_out('/UF ' . ($this->n + 1) . ' 0 R');
11171			$this->_out('>>');
11172			if ($file['AFRelationship']) {
11173				$this->_out('/AFRelationship /' . $file['AFRelationship']);
11174			}
11175			$this->_out('/UF ' . $this->_textstring($file['name']));
11176			$this->_out('>>');
11177			$this->_out('endobj');
11178
11179			$fileContent = null;
11180			if (isset($file['path'])) {
11181				$fileContent = @file_get_contents($file['path']);
11182			} elseif (isset($file['content'])) {
11183				$fileContent = $file['content'];
11184			}
11185
11186			if (!$fileContent) {
11187				throw new \Mpdf\MpdfException(sprintf('Cannot access associated file - %s', $file['path']));
11188			}
11189
11190			$filestream = gzcompress($fileContent);
11191			$this->_newobj();
11192			$this->_out('<</Type /EmbeddedFile');
11193			if ($file['mime']) {
11194				$this->_out('/Subtype /' . $this->_escapeName($file['mime']));
11195			}
11196			$this->_out('/Length '.strlen($filestream));
11197			$this->_out('/Filter /FlateDecode');
11198			if (isset($file['path'])) {
11199				$this->_out('/Params <</ModDate '.$this->_textstring('D:'.PdfDate::format(filemtime($file['path']))).' >>');
11200			} else {
11201				$this->_out('/Params <</ModDate '.$this->_textstring('D:'.PdfDate::format(time())).' >>');
11202			}
11203
11204			$this->_out('>>');
11205			$this->_putstream($filestream);
11206			$this->_out('endobj');
11207		}
11208
11209		// AF array
11210		$this->_newobj();
11211		$refs = [];
11212		foreach ($this->associatedFiles as $file) {
11213			array_push($refs, '' . $file['_root'] . ' 0 R');
11214		}
11215		$this->_out('[' . join(' ', $refs) . ']');
11216		$this->_out('endobj');
11217		$this->associatedFilesRoot = $this->n;
11218	}
11219
11220	function _putcatalog()
11221	{
11222		$this->_out('/Type /Catalog');
11223		$this->_out('/Pages 1 0 R');
11224		if ($this->ZoomMode == 'fullpage') {
11225			$this->_out('/OpenAction [3 0 R /Fit]');
11226		} elseif ($this->ZoomMode == 'fullwidth') {
11227			$this->_out('/OpenAction [3 0 R /FitH null]');
11228		} elseif ($this->ZoomMode == 'real') {
11229			$this->_out('/OpenAction [3 0 R /XYZ null null 1]');
11230		} elseif (!is_string($this->ZoomMode)) {
11231			$this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->ZoomMode / 100) . ']');
11232		} else {
11233			$this->_out('/OpenAction [3 0 R /XYZ null null null]');
11234		}
11235		if ($this->LayoutMode == 'single') {
11236			$this->_out('/PageLayout /SinglePage');
11237		} elseif ($this->LayoutMode == 'continuous') {
11238			$this->_out('/PageLayout /OneColumn');
11239		} elseif ($this->LayoutMode == 'twoleft') {
11240			$this->_out('/PageLayout /TwoColumnLeft');
11241		} elseif ($this->LayoutMode == 'tworight') {
11242			$this->_out('/PageLayout /TwoColumnRight');
11243		} elseif ($this->LayoutMode == 'two') {
11244			if ($this->mirrorMargins) {
11245				$this->_out('/PageLayout /TwoColumnRight');
11246			} else {
11247				$this->_out('/PageLayout /TwoColumnLeft');
11248			}
11249		}
11250
11251		// Bookmarks
11252		if (count($this->BMoutlines) > 0) {
11253			$this->_out('/Outlines ' . $this->OutlineRoot . ' 0 R');
11254			$this->_out('/PageMode /UseOutlines');
11255		}
11256
11257		// Fullscreen
11258		if (is_int(strpos($this->DisplayPreferences, 'FullScreen'))) {
11259			$this->_out('/PageMode /FullScreen');
11260		}
11261
11262		// Metadata
11263		if ($this->PDFA || $this->PDFX) {
11264			$this->_out('/Metadata ' . $this->MetadataRoot . ' 0 R');
11265		}
11266
11267		// OutputIntents
11268		if ($this->PDFA || $this->PDFX || $this->ICCProfile) {
11269			$this->_out('/OutputIntents [' . $this->OutputIntentRoot . ' 0 R]');
11270		}
11271
11272		// Associated files
11273		if ($this->associatedFilesRoot) {
11274			$this->_out('/AF '. $this->associatedFilesRoot .' 0 R');
11275
11276			$names = [];
11277			foreach ($this->associatedFiles as $file) {
11278				array_push($names, $this->_textstring($file['name']) . ' ' . $file['_root'] . ' 0 R');
11279			}
11280			$this->_out('/Names << /EmbeddedFiles << /Names [' . join(' ', $names) .  '] >> >>');
11281		}
11282
11283		// Forms
11284		if (count($this->form->forms) > 0) {
11285			$this->form->_putFormsCatalog();
11286		}
11287
11288		if (isset($this->js)) {
11289			$this->_out('/Names << /JavaScript ' . ($this->n_js) . ' 0 R >> ');
11290		}
11291
11292		if ($this->DisplayPreferences || $this->directionality == 'rtl' || $this->mirrorMargins) {
11293			$this->_out('/ViewerPreferences<<');
11294			if (is_int(strpos($this->DisplayPreferences, 'HideMenubar'))) {
11295				$this->_out('/HideMenubar true');
11296			}
11297			if (is_int(strpos($this->DisplayPreferences, 'HideToolbar'))) {
11298				$this->_out('/HideToolbar true');
11299			}
11300			if (is_int(strpos($this->DisplayPreferences, 'HideWindowUI'))) {
11301				$this->_out('/HideWindowUI true');
11302			}
11303			if (is_int(strpos($this->DisplayPreferences, 'DisplayDocTitle'))) {
11304				$this->_out('/DisplayDocTitle true');
11305			}
11306			if (is_int(strpos($this->DisplayPreferences, 'CenterWindow'))) {
11307				$this->_out('/CenterWindow true');
11308			}
11309			if (is_int(strpos($this->DisplayPreferences, 'FitWindow'))) {
11310				$this->_out('/FitWindow true');
11311			}
11312			///PrintScaling is PDF 1.6 spec.
11313			if (is_int(strpos($this->DisplayPreferences, 'NoPrintScaling')) && !$this->PDFA && !$this->PDFX) {
11314				$this->_out('/PrintScaling /None');
11315			}
11316			if ($this->directionality == 'rtl') {
11317				$this->_out('/Direction /R2L');
11318			}
11319			///Duplex is PDF 1.7 spec.
11320			if ($this->mirrorMargins && !$this->PDFA && !$this->PDFX) {
11321				// if ($this->DefOrientation=='P') $this->_out('/Duplex /DuplexFlipShortEdge');
11322				$this->_out('/Duplex /DuplexFlipLongEdge'); // PDF v1.7+
11323			}
11324			$this->_out('>>');
11325		}
11326
11327		if ($this->open_layer_pane && ($this->hasOC || count($this->layers))) {
11328			$this->_out('/PageMode /UseOC');
11329		}
11330
11331		if ($this->hasOC || count($this->layers)) {
11332			$p = $v = $h = $l = $loff = $lall = $as = '';
11333			if ($this->hasOC) {
11334				if (($this->hasOC & 1) == 1) {
11335					$p = $this->n_ocg_print . ' 0 R';
11336				}
11337				if (($this->hasOC & 2) == 2) {
11338					$v = $this->n_ocg_view . ' 0 R';
11339				}
11340				if (($this->hasOC & 4) == 4) {
11341					$h = $this->n_ocg_hidden . ' 0 R';
11342				}
11343				$as = "<</Event /Print /OCGs [$p $v $h] /Category [/Print]>> <</Event /View /OCGs [$p $v $h] /Category [/View]>>";
11344			}
11345
11346			if (count($this->layers)) {
11347				foreach ($this->layers as $k => $layer) {
11348					if (strtolower($this->layerDetails[$k]['state']) == 'hidden') {
11349						$loff .= $layer['n'] . ' 0 R ';
11350					} else {
11351						$l .= $layer['n'] . ' 0 R ';
11352					}
11353					$lall .= $layer['n'] . ' 0 R ';
11354				}
11355			}
11356			$this->_out("/OCProperties <</OCGs [$p $v $h $lall] /D <</ON [$p $l] /OFF [$v $h $loff] ");
11357			$this->_out("/Order [$v $p $h $lall] ");
11358			if ($as) {
11359				$this->_out("/AS [$as] ");
11360			}
11361			$this->_out(">>>>");
11362		}
11363	}
11364
11365	function _enddoc()
11366	{
11367		// @log Writing Headers & Footers
11368
11369		$this->_puthtmlheaders();
11370
11371		// @log Writing Pages
11372
11373		// Remove references to unused fonts (usually default font)
11374		foreach ($this->fonts as $fk => $font) {
11375			if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {
11376				if ($font['sip'] || $font['smp']) {
11377					foreach ($font['subsetfontids'] as $k => $fid) {
11378						foreach ($this->pages as $pn => $page) {
11379							$this->pages[$pn] = preg_replace('/\s\/F' . $fid . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]);
11380						}
11381					}
11382				} else {
11383					foreach ($this->pages as $pn => $page) {
11384						$this->pages[$pn] = preg_replace('/\s\/F' . $font['i'] . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]);
11385					}
11386				}
11387			}
11388		}
11389
11390		if (count($this->layers)) {
11391			foreach ($this->pages as $pn => $page) {
11392				preg_match_all('/\/OCZ-index \/ZI(\d+) BDC(.*?)(EMCZ)-index/is', $this->pages[$pn], $m1);
11393				preg_match_all('/\/OCBZ-index \/ZI(\d+) BDC(.*?)(EMCBZ)-index/is', $this->pages[$pn], $m2);
11394				preg_match_all('/\/OCGZ-index \/ZI(\d+) BDC(.*?)(EMCGZ)-index/is', $this->pages[$pn], $m3);
11395				$m = [];
11396				for ($i = 0; $i < 4; $i++) {
11397					$m[$i] = array_merge($m1[$i], $m2[$i], $m3[$i]);
11398				}
11399				if (count($m[0])) {
11400					$sortarr = [];
11401					for ($i = 0; $i < count($m[0]); $i++) {
11402						$key = $m[1][$i] * 2;
11403						if ($m[3][$i] == 'EMCZ') {
11404							$key +=2; // background first then gradient then normal
11405						} elseif ($m[3][$i] == 'EMCGZ') {
11406							$key +=1;
11407						}
11408						$sortarr[$i] = $key;
11409					}
11410					asort($sortarr);
11411					foreach ($sortarr as $i => $k) {
11412						$this->pages[$pn] = str_replace($m[0][$i], '', $this->pages[$pn]);
11413						$this->pages[$pn] .= "\n" . $m[0][$i] . "\n";
11414					}
11415					$this->pages[$pn] = preg_replace('/\/OC[BG]{0,1}Z-index \/ZI(\d+) BDC/is', '/OC /ZI\\1 BDC ', $this->pages[$pn]);
11416					$this->pages[$pn] = preg_replace('/EMC[BG]{0,1}Z-index/is', 'EMC', $this->pages[$pn]);
11417				}
11418			}
11419		}
11420
11421		$this->_putpages();
11422
11423		// @log Writing document resources
11424
11425		$this->_putresources();
11426		// Info
11427		$this->_newobj();
11428		$this->InfoRoot = $this->n;
11429		$this->_out('<<');
11430
11431		// @log Writing document info
11432
11433		$this->_putinfo();
11434		$this->_out('>>');
11435		$this->_out('endobj');
11436
11437		// METADATA
11438		if ($this->PDFA || $this->PDFX) {
11439			$this->_putmetadata();
11440		}
11441
11442		// OUTPUTINTENT
11443		if ($this->PDFA || $this->PDFX || $this->ICCProfile) {
11444			$this->_putoutputintent();
11445		}
11446
11447		// Associated files
11448		if ($this->associatedFiles) {
11449			$this->_putAssociatedFiles();
11450		}
11451
11452		// Catalog
11453		$this->_newobj();
11454		$this->_out('<<');
11455
11456		// @log Writing document catalog
11457
11458		$this->_putcatalog();
11459		$this->_out('>>');
11460		$this->_out('endobj');
11461
11462		// Cross-ref
11463		$o = strlen($this->buffer);
11464		$this->_out('xref');
11465		$this->_out('0 ' . ($this->n + 1));
11466		$this->_out('0000000000 65535 f ');
11467
11468		for ($i = 1; $i <= $this->n; $i++) {
11469			$this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
11470		}
11471
11472		// Trailer
11473		$this->_out('trailer');
11474		$this->_out('<<');
11475		$this->_puttrailer();
11476		$this->_out('>>');
11477		$this->_out('startxref');
11478		$this->_out($o);
11479
11480		$this->buffer .= '%%EOF';
11481		$this->state = 3;
11482
11483		// Imports
11484		if ($this->enableImports && count($this->parsers) > 0) {
11485			foreach ($this->parsers as $k => $_) {
11486				$this->parsers[$k]->closeFile();
11487				$this->parsers[$k] = null;
11488				unset($this->parsers[$k]);
11489			}
11490		}
11491	}
11492
11493	function _beginpage(
11494		$orientation,
11495		$mgl = '',
11496		$mgr = '',
11497		$mgt = '',
11498		$mgb = '',
11499		$mgh = '',
11500		$mgf = '',
11501		$ohname = '',
11502		$ehname = '',
11503		$ofname = '',
11504		$efname = '',
11505		$ohvalue = 0,
11506		$ehvalue = 0,
11507		$ofvalue = 0,
11508		$efvalue = 0,
11509		$pagesel = '',
11510		$newformat = ''
11511	) {
11512		if (!($pagesel && $this->page == 1 && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)))) {
11513			$this->page++;
11514			$this->pages[$this->page] = '';
11515		}
11516		$this->state = 2;
11517		$resetHTMLHeadersrequired = false;
11518
11519		if ($newformat) {
11520			$this->_setPageSize($newformat, $orientation);
11521		}
11522
11523		/* -- CSS-PAGE -- */
11524		// Paged media (page-box)
11525		if ($pagesel || (isset($this->page_box['using']) && $this->page_box['using'])) {
11526
11527			if ($pagesel || $this->page == 1) {
11528				$first = true;
11529			} else {
11530				$first = false;
11531			}
11532
11533			if ($this->mirrorMargins && ($this->page % 2 == 0)) {
11534				$oddEven = 'E';
11535			} else {
11536				$oddEven = 'O';
11537			}
11538
11539			if ($pagesel) {
11540				$psel = $pagesel;
11541			} elseif ($this->page_box['current']) {
11542				$psel = $this->page_box['current'];
11543			} else {
11544				$psel = '';
11545			}
11546
11547			list($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS($psel, $first, $oddEven);
11548
11549			if ($this->mirrorMargins && ($this->page % 2 == 0)) {
11550
11551				if ($hname) {
11552					$ehvalue = 1;
11553					$ehname = $hname;
11554				} else {
11555					$ehvalue = -1;
11556				}
11557
11558				if ($fname) {
11559					$efvalue = 1;
11560					$efname = $fname;
11561				} else {
11562					$efvalue = -1;
11563				}
11564
11565			} else {
11566
11567				if ($hname) {
11568					$ohvalue = 1;
11569					$ohname = $hname;
11570				} else {
11571					$ohvalue = -1;
11572				}
11573
11574				if ($fname) {
11575					$ofvalue = 1;
11576					$ofname = $fname;
11577				} else {
11578					$ofvalue = -1;
11579				}
11580			}
11581
11582			if ($resetpagenum || $pagenumstyle || $suppress) {
11583				$this->PageNumSubstitutions[] = ['from' => ($this->page), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
11584			}
11585
11586			// PAGED MEDIA - CROP / CROSS MARKS from @PAGE
11587			$this->show_marks = $marks;
11588
11589			// Background color
11590			if (isset($bg['BACKGROUND-COLOR'])) {
11591				$cor = $this->colorConverter->convert($bg['BACKGROUND-COLOR'], $this->PDFAXwarnings);
11592				if ($cor) {
11593					$this->bodyBackgroundColor = $cor;
11594				}
11595			} else {
11596				$this->bodyBackgroundColor = false;
11597			}
11598
11599			/* -- BACKGROUNDS -- */
11600			if (isset($bg['BACKGROUND-GRADIENT'])) {
11601				$this->bodyBackgroundGradient = $bg['BACKGROUND-GRADIENT'];
11602			} else {
11603				$this->bodyBackgroundGradient = false;
11604			}
11605
11606			// Tiling Patterns
11607			if (isset($bg['BACKGROUND-IMAGE']) && $bg['BACKGROUND-IMAGE']) {
11608				$ret = $this->SetBackground($bg, $this->pgwidth);
11609				if ($ret) {
11610					$this->bodyBackgroundImage = $ret;
11611				}
11612			} else {
11613				$this->bodyBackgroundImage = false;
11614			}
11615			/* -- END BACKGROUNDS -- */
11616
11617			$this->page_box['current'] = $psel;
11618			$this->page_box['using'] = true;
11619		}
11620		/* -- END CSS-PAGE -- */
11621
11622		// Page orientation
11623		if (!$orientation) {
11624			$orientation = $this->DefOrientation;
11625		} else {
11626			$orientation = strtoupper(substr($orientation, 0, 1));
11627			if ($orientation != $this->DefOrientation) {
11628				$this->OrientationChanges[$this->page] = true;
11629			}
11630		}
11631
11632		if ($orientation != $this->CurOrientation || $newformat) {
11633
11634			// Change orientation
11635			if ($orientation == 'P') {
11636				$this->wPt = $this->fwPt;
11637				$this->hPt = $this->fhPt;
11638				$this->w = $this->fw;
11639				$this->h = $this->fh;
11640				if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') {
11641					$this->tMargin = $this->orig_tMargin;
11642					$this->bMargin = $this->orig_bMargin;
11643					$this->DeflMargin = $this->orig_lMargin;
11644					$this->DefrMargin = $this->orig_rMargin;
11645					$this->margin_header = $this->orig_hMargin;
11646					$this->margin_footer = $this->orig_fMargin;
11647				} else {
11648					$resetHTMLHeadersrequired = true;
11649				}
11650			} else {
11651				$this->wPt = $this->fhPt;
11652				$this->hPt = $this->fwPt;
11653				$this->w = $this->fh;
11654				$this->h = $this->fw;
11655
11656				if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') {
11657					$this->tMargin = $this->orig_lMargin;
11658					$this->bMargin = $this->orig_rMargin;
11659					$this->DeflMargin = $this->orig_bMargin;
11660					$this->DefrMargin = $this->orig_tMargin;
11661					$this->margin_header = $this->orig_hMargin;
11662					$this->margin_footer = $this->orig_fMargin;
11663				} else {
11664					$resetHTMLHeadersrequired = true;
11665				}
11666			}
11667
11668			$this->CurOrientation = $orientation;
11669			$this->ResetMargins();
11670			$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
11671			$this->PageBreakTrigger = $this->h - $this->bMargin;
11672		}
11673
11674		$this->pageDim[$this->page]['w'] = $this->w;
11675		$this->pageDim[$this->page]['h'] = $this->h;
11676
11677		$this->pageDim[$this->page]['outer_width_LR'] = isset($this->page_box['outer_width_LR']) ? $this->page_box['outer_width_LR'] : 0;
11678		$this->pageDim[$this->page]['outer_width_TB'] = isset($this->page_box['outer_width_TB']) ? $this->page_box['outer_width_TB'] : 0;
11679
11680		if (!isset($this->page_box['outer_width_LR']) && !isset($this->page_box['outer_width_TB'])) {
11681			$this->pageDim[$this->page]['bleedMargin'] = 0;
11682		} elseif ($this->bleedMargin <= $this->page_box['outer_width_LR'] && $this->bleedMargin <= $this->page_box['outer_width_TB']) {
11683			$this->pageDim[$this->page]['bleedMargin'] = $this->bleedMargin;
11684		} else {
11685			$this->pageDim[$this->page]['bleedMargin'] = min($this->page_box['outer_width_LR'], $this->page_box['outer_width_TB']) - 0.01;
11686		}
11687
11688		// If Page Margins are re-defined
11689		// strlen()>0 is used to pick up (integer) 0, (string) '0', or set value
11690		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)) {
11691
11692			if (strlen($mgl) > 0) {
11693				$this->DeflMargin = $mgl;
11694			}
11695
11696			if (strlen($mgr) > 0) {
11697				$this->DefrMargin = $mgr;
11698			}
11699
11700			if (strlen($mgt) > 0) {
11701				$this->tMargin = $mgt;
11702			}
11703
11704			if (strlen($mgb) > 0) {
11705				$this->bMargin = $mgb;
11706			}
11707
11708			if (strlen($mgh) > 0) {
11709				$this->margin_header = $mgh;
11710			}
11711
11712			if (strlen($mgf) > 0) {
11713				$this->margin_footer = $mgf;
11714			}
11715
11716			$this->ResetMargins();
11717			$this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
11718
11719			$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
11720			$resetHTMLHeadersrequired = true;
11721		}
11722
11723		$this->ResetMargins();
11724		$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
11725		$this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
11726
11727		// Reset column top margin
11728		$this->y0 = $this->tMargin;
11729
11730		$this->x = $this->lMargin;
11731		$this->y = $this->tMargin;
11732		$this->FontFamily = '';
11733
11734		// HEADERS AND FOOTERS	// mPDF 6
11735		if ($ohvalue < 0 || strtoupper($ohvalue) == 'OFF') {
11736			$this->HTMLHeader = '';
11737			$resetHTMLHeadersrequired = true;
11738		} elseif ($ohname && $ohvalue > 0) {
11739			if (preg_match('/^html_(.*)$/i', $ohname, $n)) {
11740				$name = $n[1];
11741			} else {
11742				$name = $ohname;
11743			}
11744			if (isset($this->pageHTMLheaders[$name])) {
11745				$this->HTMLHeader = $this->pageHTMLheaders[$name];
11746			} else {
11747				$this->HTMLHeader = '';
11748			}
11749			$resetHTMLHeadersrequired = true;
11750		}
11751
11752		if ($ehvalue < 0 || strtoupper($ehvalue) == 'OFF') {
11753			$this->HTMLHeaderE = '';
11754			$resetHTMLHeadersrequired = true;
11755		} elseif ($ehname && $ehvalue > 0) {
11756			if (preg_match('/^html_(.*)$/i', $ehname, $n)) {
11757				$name = $n[1];
11758			} else {
11759				$name = $ehname;
11760			}
11761			if (isset($this->pageHTMLheaders[$name])) {
11762				$this->HTMLHeaderE = $this->pageHTMLheaders[$name];
11763			} else {
11764				$this->HTMLHeaderE = '';
11765			}
11766			$resetHTMLHeadersrequired = true;
11767		}
11768
11769		if ($ofvalue < 0 || strtoupper($ofvalue) == 'OFF') {
11770			$this->HTMLFooter = '';
11771			$resetHTMLHeadersrequired = true;
11772		} elseif ($ofname && $ofvalue > 0) {
11773			if (preg_match('/^html_(.*)$/i', $ofname, $n)) {
11774				$name = $n[1];
11775			} else {
11776				$name = $ofname;
11777			}
11778			if (isset($this->pageHTMLfooters[$name])) {
11779				$this->HTMLFooter = $this->pageHTMLfooters[$name];
11780			} else {
11781				$this->HTMLFooter = '';
11782			}
11783			$resetHTMLHeadersrequired = true;
11784		}
11785
11786		if ($efvalue < 0 || strtoupper($efvalue) == 'OFF') {
11787			$this->HTMLFooterE = '';
11788			$resetHTMLHeadersrequired = true;
11789		} elseif ($efname && $efvalue > 0) {
11790			if (preg_match('/^html_(.*)$/i', $efname, $n)) {
11791				$name = $n[1];
11792			} else {
11793				$name = $efname;
11794			}
11795			if (isset($this->pageHTMLfooters[$name])) {
11796				$this->HTMLFooterE = $this->pageHTMLfooters[$name];
11797			} else {
11798				$this->HTMLFooterE = '';
11799			}
11800			$resetHTMLHeadersrequired = true;
11801		}
11802
11803		if ($resetHTMLHeadersrequired) {
11804			$this->SetHTMLHeader($this->HTMLHeader);
11805			$this->SetHTMLHeader($this->HTMLHeaderE, 'E');
11806			$this->SetHTMLFooter($this->HTMLFooter);
11807			$this->SetHTMLFooter($this->HTMLFooterE, 'E');
11808		}
11809
11810
11811		if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
11812			$this->_setAutoHeaderHeight($this->HTMLHeaderE);
11813			$this->_setAutoFooterHeight($this->HTMLFooterE);
11814		} else { // ODD or DEFAULT
11815			$this->_setAutoHeaderHeight($this->HTMLHeader);
11816			$this->_setAutoFooterHeight($this->HTMLFooter);
11817		}
11818
11819		// Reset column top margin
11820		$this->y0 = $this->tMargin;
11821
11822		$this->x = $this->lMargin;
11823		$this->y = $this->tMargin;
11824	}
11825
11826	// mPDF 6
11827	function _setAutoHeaderHeight(&$htmlh)
11828	{
11829		/* When the setAutoTopMargin option is set to pad/stretch, only apply auto header height when a header exists */
11830		if ($this->HTMLHeader === '' && $this->HTMLHeaderE === '') {
11831			return;
11832		}
11833
11834		if ($this->setAutoTopMargin == 'pad') {
11835			if (isset($htmlh['h']) && $htmlh['h']) {
11836				$h = $htmlh['h'];
11837			} // 5.7.3
11838			else {
11839				$h = 0;
11840			}
11841			$this->tMargin = $this->margin_header + $h + $this->orig_tMargin;
11842		} elseif ($this->setAutoTopMargin == 'stretch') {
11843			if (isset($htmlh['h']) && $htmlh['h']) {
11844				$h = $htmlh['h'];
11845			} // 5.7.3
11846			else {
11847				$h = 0;
11848			}
11849			$this->tMargin = max($this->orig_tMargin, $this->margin_header + $h + $this->autoMarginPadding);
11850		}
11851	}
11852
11853	// mPDF 6
11854	function _setAutoFooterHeight(&$htmlf)
11855	{
11856		/* When the setAutoTopMargin option is set to pad/stretch, only apply auto footer height when a footer exists */
11857		if ($this->HTMLFooter === '' && $this->HTMLFooterE === '') {
11858			return;
11859		}
11860
11861		if ($this->setAutoBottomMargin == 'pad') {
11862			if (isset($htmlf['h']) && $htmlf['h']) {
11863				$h = $htmlf['h'];
11864			} // 5.7.3
11865			else {
11866				$h = 0;
11867			}
11868			$this->bMargin = $this->margin_footer + $h + $this->orig_bMargin;
11869			$this->PageBreakTrigger = $this->h - $this->bMargin;
11870		} elseif ($this->setAutoBottomMargin == 'stretch') {
11871			if (isset($htmlf['h']) && $htmlf['h']) {
11872				$h = $htmlf['h'];
11873			} // 5.7.3
11874			else {
11875				$h = 0;
11876			}
11877			$this->bMargin = max($this->orig_bMargin, $this->margin_footer + $h + $this->autoMarginPadding);
11878			$this->PageBreakTrigger = $this->h - $this->bMargin;
11879		}
11880	}
11881
11882	function _endpage()
11883	{
11884		/* -- CSS-IMAGE-FLOAT -- */
11885		$this->printfloatbuffer();
11886		/* -- END CSS-IMAGE-FLOAT -- */
11887
11888		if ($this->visibility != 'visible') {
11889			$this->SetVisibility('visible');
11890		}
11891		$this->EndLayer();
11892		// End of page contents
11893		$this->state = 1;
11894	}
11895
11896	function _newobj($obj_id = false, $onlynewobj = false)
11897	{
11898		if (!$obj_id) {
11899			$obj_id = ++$this->n;
11900		}
11901		// Begin a new object
11902		if (!$onlynewobj) {
11903			$this->offsets[$obj_id] = strlen($this->buffer);
11904			$this->_out($obj_id . ' 0 obj');
11905			$this->_current_obj_id = $obj_id; // for later use with encryption
11906		}
11907	}
11908
11909	function _dounderline($x, $y, $txt, $OTLdata = false, $textvar = 0)
11910	{
11911		// Now print line exactly where $y secifies - called from Text() and Cell() - adjust  position there
11912		// WORD SPACING
11913		$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));
11914		// Draw a line
11915		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);
11916	}
11917
11918	function getFileContentsByCurl($url, &$data)
11919	{
11920		$this->logger->debug(sprintf('Fetching (cURL) content of remote URL "%s"', $url), ['context' => LogContext::REMOTE_CONTENT]);
11921
11922		$ch = curl_init($url);
11923
11924		curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1'); // mPDF 5.7.4
11925		curl_setopt($ch, CURLOPT_HEADER, 0);
11926		curl_setopt($ch, CURLOPT_NOBODY, 0);
11927		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
11928		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->curlTimeout);
11929
11930		if ($this->curlFollowLocation) {
11931			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
11932		}
11933
11934		if ($this->curlAllowUnsafeSslRequests) {
11935			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
11936			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
11937		}
11938
11939		$data = curl_exec($ch);
11940		curl_close($ch);
11941	}
11942
11943	function getFileContentsBySocket($url, &$data)
11944	{
11945		$this->logger->debug(sprintf('Fetching (socket) content of remote URL "%s"', $url), ['context' => LogContext::REMOTE_CONTENT]);
11946		// mPDF 5.7.3
11947
11948		$timeout = 1;
11949		$p = parse_url($url);
11950		$file = $p['path'];
11951		if ($p['scheme'] == 'https') {
11952			$prefix = 'ssl://';
11953			$port = ($p['port'] ? $p['port'] : 443);
11954		} else {
11955			$prefix = '';
11956			$port = ($p['port'] ? $p['port'] : 80);
11957		}
11958		if ($p['query']) {
11959			$file .= '?' . $p['query'];
11960		}
11961		if (!($fh = @fsockopen($prefix . $p['host'], $port, $errno, $errstr, $timeout))) {
11962			return false;
11963		}
11964
11965		$getstring = "GET " . $file . " HTTP/1.0 \r\n" .
11966			"Host: " . $p['host'] . " \r\n" .
11967			"Connection: close\r\n\r\n";
11968
11969		fwrite($fh, $getstring);
11970
11971		// Get rid of HTTP header
11972		$s = fgets($fh, 1024);
11973		if (!$s) {
11974			return false;
11975		}
11976		while (!feof($fh)) {
11977			$s = fgets($fh, 1024);
11978			if ($s == "\r\n") {
11979				break;
11980			}
11981		}
11982		$data = '';
11983
11984		while (!feof($fh)) {
11985			$data .= fgets($fh, 1024);
11986		}
11987
11988		fclose($fh);
11989	}
11990
11991	// ==============================================================
11992	// Moved outside WMF as also needed for SVG
11993	function _putformobjects()
11994	{
11995		foreach ($this->formobjects as $file => $info) {
11996
11997			$this->_newobj();
11998
11999			$this->formobjects[$file]['n'] = $this->n;
12000
12001			$this->_out('<</Type /XObject');
12002			$this->_out('/Subtype /Form');
12003			$this->_out('/Group ' . ($this->n + 1) . ' 0 R');
12004			$this->_out('/BBox [' . $info['x'] . ' ' . $info['y'] . ' ' . ($info['w'] + $info['x']) . ' ' . ($info['h'] + $info['y']) . ']');
12005
12006			if ($this->compress) {
12007				$this->_out('/Filter /FlateDecode');
12008			}
12009
12010			$data = ($this->compress) ? gzcompress($info['data']) : $info['data'];
12011			$this->_out('/Length ' . strlen($data) . '>>');
12012			$this->_putstream($data);
12013
12014			unset($this->formobjects[$file]['data']);
12015
12016			$this->_out('endobj');
12017
12018			// Required for SVG transparency (opacity) to work
12019			$this->_newobj();
12020			$this->_out('<</Type /Group');
12021			$this->_out('/S /Transparency');
12022			$this->_out('>>');
12023			$this->_out('endobj');
12024		}
12025	}
12026
12027	function _freadint($f)
12028	{
12029		$i = ord(fread($f, 1)) << 24;
12030		$i += ord(fread($f, 1)) << 16;
12031		$i += ord(fread($f, 1)) << 8;
12032		$i += ord(fread($f, 1));
12033
12034		return $i;
12035	}
12036
12037	function _UTF16BEtextstring($s)
12038	{
12039		$s = $this->UTF8ToUTF16BE($s, true);
12040		if ($this->encrypted) {
12041			$s = $this->protection->rc4($this->protection->objectKey($this->_current_obj_id), $s);
12042		}
12043
12044		return '(' . $this->_escape($s) . ')';
12045	}
12046
12047	function _textstring($s)
12048	{
12049		if ($this->encrypted) {
12050			$s = $this->protection->rc4($this->protection->objectKey($this->_current_obj_id), $s);
12051		}
12052
12053		return '(' . $this->_escape($s) . ')';
12054	}
12055
12056	function _escape($s)
12057	{
12058		return strtr($s, [')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r']);
12059	}
12060
12061	function _escapeName($s)
12062	{
12063		return strtr($s, array('/' => '#2F'));
12064	}
12065
12066	function _putstream($s)
12067	{
12068		if ($this->encrypted) {
12069			$s = $this->protection->rc4($this->protection->objectKey($this->_current_obj_id), $s);
12070		}
12071
12072		$this->_out('stream');
12073		$this->_out($s);
12074		$this->_out('endstream');
12075	}
12076
12077	function _out($s, $ln = true)
12078	{
12079		if ($this->state == 2) {
12080			if ($this->bufferoutput) {
12081				$this->headerbuffer.= $s . "\n";
12082			} /* -- COLUMNS -- */ elseif (($this->ColActive) && !$this->processingHeader && !$this->processingFooter) {
12083				// Captures everything in buffer for columns; Almost everything is sent from fn. Cell() except:
12084				// Images sent from Image() or
12085				// later sent as _out($textto) in printbuffer
12086				// Line()
12087				if (preg_match('/q \d+\.\d\d+ 0 0 (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ cm \/(I|FO)\d+ Do Q/', $s, $m)) { // Image data
12088					$h = ($m[1] / Mpdf::SCALE);
12089					// Update/overwrite the lowest bottom of printing y value for a column
12090					$this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
12091				} /* -- TABLES -- */ elseif (preg_match('/\d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ ([\-]{0,1}\d+\.\d\d+) re/', $s, $m) && $this->tableLevel > 0) { // Rect in table
12092					$h = ($m[1] / Mpdf::SCALE);
12093					// Update/overwrite the lowest bottom of printing y value for a column
12094					$this->ColDetails[$this->CurrCol]['bottom_margin'] = max($this->ColDetails[$this->CurrCol]['bottom_margin'], ($this->y + $h));
12095				} /* -- END TABLES -- */ else {  // Td Text Set in Cell()
12096					if (isset($this->ColDetails[$this->CurrCol]['bottom_margin'])) {
12097						$h = $this->ColDetails[$this->CurrCol]['bottom_margin'] - $this->y;
12098					} else {
12099						$h = 0;
12100					}
12101				}
12102				if ($h < 0) {
12103					$h = -$h;
12104				}
12105				$this->columnbuffer[] = [
12106					's' => $s, // Text string to output
12107					'col' => $this->CurrCol, // Column when printed
12108					'x' => $this->x, // x when printed
12109					'y' => $this->y, // this->y when printed (after column break)
12110					'h' => $h        // actual y at bottom when printed = y+h
12111				];
12112			} /* -- END COLUMNS -- */
12113			/* -- TABLES -- */ elseif ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) {
12114				// Captures eveything in buffer for rotated tables;
12115				$this->tablebuffer .= $s . "\n";
12116			} /* -- END TABLES -- */ elseif ($this->kwt && !$this->processingHeader && !$this->processingFooter) {
12117				// Captures eveything in buffer for keep-with-table (h1-6);
12118				$this->kwt_buffer[] = [
12119					's' => $s, // Text string to output
12120					'x' => $this->x, // x when printed
12121					'y' => $this->y, // y when printed
12122				];
12123			} elseif (($this->keep_block_together) && !$this->processingHeader && !$this->processingFooter) {
12124				// do nothing
12125			} else {
12126				$this->pages[$this->page] .= $s . ($ln == true ? "\n" : '');
12127			}
12128		} else {
12129			$this->buffer .= $s . ($ln == true ? "\n" : '');
12130		}
12131	}
12132
12133	/* -- WATERMARK -- */
12134
12135	// add a watermark
12136	function watermark($texte, $angle = 45, $fontsize = 96, $alpha = 0.2)
12137	{
12138		if ($this->PDFA || $this->PDFX) {
12139			throw new \Mpdf\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');
12140		}
12141
12142		if (!$this->watermark_font) {
12143			$this->watermark_font = $this->default_font;
12144		}
12145
12146		$this->SetFont($this->watermark_font, "B", $fontsize, false); // Don't output
12147		$texte = $this->purify_utf8_text($texte);
12148
12149		if ($this->text_input_as_HTML) {
12150			$texte = $this->all_entities_to_utf8($texte);
12151		}
12152
12153		if ($this->usingCoreFont) {
12154			$texte = mb_convert_encoding($texte, $this->mb_enc, 'UTF-8');
12155		}
12156
12157		// DIRECTIONALITY
12158		if (preg_match("/([" . $this->pregRTLchars . "])/u", $texte)) {
12159			$this->biDirectional = true;
12160		} // *OTL*
12161
12162		$textvar = 0;
12163		$save_OTLtags = $this->OTLtags;
12164		$this->OTLtags = [];
12165		if ($this->useKerning) {
12166			if ($this->CurrentFont['haskernGPOS']) {
12167				$this->OTLtags['Plus'] .= ' kern';
12168			} else {
12169				$textvar = ($textvar | TextVars::FC_KERNING);
12170			}
12171		}
12172
12173		/* -- OTL -- */
12174		// Use OTL OpenType Table Layout - GSUB & GPOS
12175		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
12176			$texte = $this->otl->applyOTL($texte, $this->CurrentFont['useOTL']);
12177			$OTLdata = $this->otl->OTLdata;
12178		}
12179		/* -- END OTL -- */
12180		$this->OTLtags = $save_OTLtags;
12181
12182		$this->magic_reverse_dir($texte, $this->directionality, $OTLdata);
12183
12184		$this->SetAlpha($alpha);
12185
12186		$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
12187
12188		$szfont = $fontsize;
12189		$loop = 0;
12190		$maxlen = (min($this->w, $this->h) ); // sets max length of text as 7/8 width/height of page
12191
12192		while ($loop == 0) {
12193			$this->SetFont($this->watermark_font, "B", $szfont, false); // Don't output
12194			$offset = ((sin(deg2rad($angle))) * ($szfont / Mpdf::SCALE));
12195
12196			$strlen = $this->GetStringWidth($texte, true, $OTLdata, $textvar);
12197			if ($strlen > $maxlen - $offset) {
12198				$szfont --;
12199			} else {
12200				$loop ++;
12201			}
12202		}
12203
12204		$this->SetFont($this->watermark_font, "B", $szfont - 0.1, true, true); // Output The -0.1 is because SetFont above is not written to PDF
12205
12206		// Repeating it will not output anything as mPDF thinks it is set
12207		$adj = ((cos(deg2rad($angle))) * ($strlen / 2));
12208		$opp = ((sin(deg2rad($angle))) * ($strlen / 2));
12209
12210		$wx = ($this->w / 2) - $adj + $offset / 3;
12211		$wy = ($this->h / 2) + $opp;
12212
12213		$this->Rotate($angle, $wx, $wy);
12214		$this->Text($wx, $wy, $texte, $OTLdata, $textvar);
12215		$this->Rotate(0);
12216		$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
12217
12218		$this->SetAlpha(1);
12219	}
12220
12221	function watermarkImg($src, $alpha = 0.2)
12222	{
12223		if ($this->PDFA || $this->PDFX) {
12224			throw new \Mpdf\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');
12225		}
12226
12227		if ($this->watermarkImgBehind) {
12228			$this->watermarkImgAlpha = $this->SetAlpha($alpha, 'Normal', true);
12229		} else {
12230			$this->SetAlpha($alpha, $this->watermarkImgAlphaBlend);
12231		}
12232
12233		$this->Image($src, 0, 0, 0, 0, '', '', true, true, true);
12234
12235		if (!$this->watermarkImgBehind) {
12236			$this->SetAlpha(1);
12237		}
12238	}
12239
12240	/* -- END WATERMARK -- */
12241
12242	function Rotate($angle, $x = -1, $y = -1)
12243	{
12244		if ($x == -1) {
12245			$x = $this->x;
12246		}
12247		if ($y == -1) {
12248			$y = $this->y;
12249		}
12250		if ($this->angle != 0) {
12251			$this->_out('Q');
12252		}
12253		$this->angle = $angle;
12254		if ($angle != 0) {
12255			$angle*=M_PI / 180;
12256			$c = cos($angle);
12257			$s = sin($angle);
12258			$cx = $x * Mpdf::SCALE;
12259			$cy = ($this->h - $y) * Mpdf::SCALE;
12260			$this->_out(sprintf('q %.5F %.5F %.5F %.5F %.3F %.3F cm 1 0 0 1 %.3F %.3F cm', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy));
12261		}
12262	}
12263
12264	function CircularText($x, $y, $r, $text, $align = 'top', $fontfamily = '', $fontsize = 0, $fontstyle = '', $kerning = 120, $fontwidth = 100, $divider = '')
12265	{
12266		if (empty($this->directWrite)) {
12267			$this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
12268		}
12269
12270		$this->directWrite->CircularText($x, $y, $r, $text, $align, $fontfamily, $fontsize, $fontstyle, $kerning, $fontwidth, $divider);
12271	}
12272
12273	// From Invoice
12274	function RoundedRect($x, $y, $w, $h, $r, $style = '')
12275	{
12276		$hp = $this->h;
12277
12278		if ($style == 'F') {
12279			$op = 'f';
12280		} elseif ($style == 'FD' or $style == 'DF') {
12281			$op = 'B';
12282		} else {
12283			$op = 'S';
12284		}
12285
12286		$MyArc = 4 / 3 * (sqrt(2) - 1);
12287		$this->_out(sprintf('%.3F %.3F m', ($x + $r) * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE));
12288		$xc = $x + $w - $r;
12289		$yc = $y + $r;
12290		$this->_out(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE));
12291
12292		$this->_Arc($xc + $r * $MyArc, $yc - $r, $xc + $r, $yc - $r * $MyArc, $xc + $r, $yc);
12293		$xc = $x + $w - $r;
12294		$yc = $y + $h - $r;
12295		$this->_out(sprintf('%.3F %.3F l', ($x + $w) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE));
12296
12297		$this->_Arc($xc + $r, $yc + $r * $MyArc, $xc + $r * $MyArc, $yc + $r, $xc, $yc + $r);
12298		$xc = $x + $r;
12299		$yc = $y + $h - $r;
12300		$this->_out(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - ($y + $h)) * Mpdf::SCALE));
12301
12302		$this->_Arc($xc - $r * $MyArc, $yc + $r, $xc - $r, $yc + $r * $MyArc, $xc - $r, $yc);
12303		$xc = $x + $r;
12304		$yc = $y + $r;
12305		$this->_out(sprintf('%.3F %.3F l', ($x) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE));
12306
12307		$this->_Arc($xc - $r, $yc - $r * $MyArc, $xc - $r * $MyArc, $yc - $r, $xc, $yc - $r);
12308		$this->_out($op);
12309	}
12310
12311	function _Arc($x1, $y1, $x2, $y2, $x3, $y3)
12312	{
12313		$h = $this->h;
12314		$this->_out(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));
12315	}
12316
12317	// ====================================================
12318
12319
12320
12321	/* -- DIRECTW -- */
12322	function Shaded_box($text, $font = '', $fontstyle = 'B', $szfont = '', $width = '70%', $style = 'DF', $radius = 2.5, $fill = '#FFFFFF', $color = '#000000', $pad = 2)
12323	{
12324		// F (shading - no line),S (line, no shading),DF (both)
12325		if (empty($this->directWrite)) {
12326			$this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);
12327		}
12328		$this->directWrite->Shaded_box($text, $font, $fontstyle, $szfont, $width, $style, $radius, $fill, $color, $pad);
12329	}
12330
12331	/* -- END DIRECTW -- */
12332
12333	function UTF8StringToArray($str, $addSubset = true)
12334	{
12335		$out = [];
12336		$len = strlen($str);
12337		for ($i = 0; $i < $len; $i++) {
12338			$uni = -1;
12339			$h = ord($str[$i]);
12340			if ($h <= 0x7F) {
12341				$uni = $h;
12342			} elseif ($h >= 0xC2) {
12343				if (($h <= 0xDF) && ($i < $len - 1)) {
12344					$uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
12345				} elseif (($h <= 0xEF) && ($i < $len - 2)) {
12346					$uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
12347				} elseif (($h <= 0xF4) && ($i < $len - 3)) {
12348					$uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
12349				}
12350			}
12351			if ($uni >= 0) {
12352				$out[] = $uni;
12353				if ($addSubset && isset($this->CurrentFont['subset'])) {
12354					$this->CurrentFont['subset'][$uni] = $uni;
12355				}
12356			}
12357		}
12358		return $out;
12359	}
12360
12361	// Convert utf-8 string to <HHHHHH> for Font Subsets
12362	function UTF8toSubset($str)
12363	{
12364		$ret = '<';
12365		// $str = preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $str );	// mPDF 6 deleted
12366		// $str = preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $str );	// mPDF 6 deleted
12367		$unicode = $this->UTF8StringToArray($str);
12368		$orig_fid = $this->CurrentFont['subsetfontids'][0];
12369		$last_fid = $this->CurrentFont['subsetfontids'][0];
12370		foreach ($unicode as $c) {
12371			/* 	// mPDF 6 deleted
12372			  if ($c == 7 || $c == 8) {
12373			  if ($orig_fid != $last_fid) {
12374			  $ret .= '> Tj /F'.$orig_fid.' '.$this->FontSizePt.' Tf <';
12375			  $last_fid = $orig_fid;
12376			  }
12377			  if ($c == 7) { $ret .= $this->aliasNbPgHex; }
12378			  else { $ret .= $this->aliasNbPgGpHex; }
12379			  continue;
12380			  }
12381			 */
12382			if (!$this->_charDefined($this->CurrentFont['cw'], $c)) {
12383				$c = 0;
12384			} // mPDF 6
12385			for ($i = 0; $i < 99; $i++) {
12386				// return c as decimal char
12387				$init = array_search($c, $this->CurrentFont['subsets'][$i]);
12388				if ($init !== false) {
12389					if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {
12390						$ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';
12391						$last_fid = $this->CurrentFont['subsetfontids'][$i];
12392					}
12393					$ret .= sprintf("%02s", strtoupper(dechex($init)));
12394					break;
12395				} // TrueType embedded SUBSETS
12396				elseif (count($this->CurrentFont['subsets'][$i]) < 255) {
12397					$n = count($this->CurrentFont['subsets'][$i]);
12398					$this->CurrentFont['subsets'][$i][$n] = $c;
12399					if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {
12400						$ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';
12401						$last_fid = $this->CurrentFont['subsetfontids'][$i];
12402					}
12403					$ret .= sprintf("%02s", strtoupper(dechex($n)));
12404					break;
12405				} elseif (!isset($this->CurrentFont['subsets'][($i + 1)])) {
12406					// TrueType embedded SUBSETS
12407					$this->CurrentFont['subsets'][($i + 1)] = [0 => 0];
12408					$new_fid = count($this->fonts) + $this->extraFontSubsets + 1;
12409					$this->CurrentFont['subsetfontids'][($i + 1)] = $new_fid;
12410					$this->extraFontSubsets++;
12411				}
12412			}
12413		}
12414		$ret .= '>';
12415		if ($last_fid != $orig_fid) {
12416			$ret .= ' Tj /F' . $orig_fid . ' ' . $this->FontSizePt . ' Tf <> ';
12417		}
12418		return $ret;
12419	}
12420
12421	// Converts UTF-8 strings to UTF16-BE.
12422	function UTF8ToUTF16BE($str, $setbom = true)
12423	{
12424		if ($this->checkSIP && preg_match("/([\x{20000}-\x{2FFFF}])/u", $str)) {
12425			if (!in_array($this->currentfontfamily, ['gb', 'big5', 'sjis', 'uhc', 'gbB', 'big5B', 'sjisB', 'uhcB', 'gbI', 'big5I', 'sjisI', 'uhcI',
12426					'gbBI', 'big5BI', 'sjisBI', 'uhcBI'])) {
12427				$str = preg_replace("/[\x{20000}-\x{2FFFF}]/u", chr(0), $str);
12428			}
12429		}
12430		if ($this->checkSMP && preg_match("/([\x{10000}-\x{1FFFF}])/u", $str)) {
12431			$str = preg_replace("/[\x{10000}-\x{1FFFF}]/u", chr(0), $str);
12432		}
12433		$outstr = ""; // string to be returned
12434		if ($setbom) {
12435			$outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
12436		}
12437		$outstr .= mb_convert_encoding($str, 'UTF-16BE', 'UTF-8');
12438		return $outstr;
12439	}
12440
12441	/* -- CJK-FONTS -- */
12442
12443	// from class PDF_Chinese CJK EXTENSIONS
12444	function AddCIDFont($family, $style, $name, &$cw, $CMap, $registry, $desc)
12445	{
12446		$fontkey = strtolower($family) . strtoupper($style);
12447		if (isset($this->fonts[$fontkey])) {
12448			throw new \Mpdf\MpdfException("Font already added: $family $style");
12449		}
12450		$i = count($this->fonts) + $this->extraFontSubsets + 1;
12451		$name = str_replace(' ', '', $name);
12452		if ($family == 'sjis') {
12453			$up = -120;
12454		} else {
12455			$up = -130;
12456		}
12457		// ? 'up' and 'ut' do not seem to be referenced anywhere
12458		$this->fonts[$fontkey] = ['i' => $i, 'type' => 'Type0', 'name' => $name, 'up' => $up, 'ut' => 40, 'cw' => $cw, 'CMap' => $CMap, 'registry' => $registry, 'MissingWidth' => 1000, 'desc' => $desc];
12459	}
12460
12461	function AddCJKFont($family)
12462	{
12463
12464		if ($this->PDFA || $this->PDFX) {
12465			throw new \Mpdf\MpdfException("Adobe CJK fonts cannot be embedded in mPDF (required for PDFA1-b and PDFX/1-a).");
12466		}
12467		if ($family == 'big5') {
12468			$this->AddBig5Font();
12469		} elseif ($family == 'gb') {
12470			$this->AddGBFont();
12471		} elseif ($family == 'sjis') {
12472			$this->AddSJISFont();
12473		} elseif ($family == 'uhc') {
12474			$this->AddUHCFont();
12475		}
12476	}
12477
12478	function AddBig5Font()
12479	{
12480		// Add Big5 font with proportional Latin
12481		$family = 'big5';
12482		$name = 'MSungStd-Light-Acro';
12483		$cw = $this->Big5_widths;
12484		$CMap = 'UniCNS-UTF16-H';
12485		$registry = ['ordering' => 'CNS1', 'supplement' => 4];
12486		$desc = [
12487			'Ascent' => 880,
12488			'Descent' => -120,
12489			'CapHeight' => 880,
12490			'Flags' => 6,
12491			'FontBBox' => '[-160 -249 1015 1071]',
12492			'ItalicAngle' => 0,
12493			'StemV' => 93,
12494		];
12495		$this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
12496		$this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
12497		$this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
12498		$this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
12499	}
12500
12501	function AddGBFont()
12502	{
12503		// Add GB font with proportional Latin
12504		$family = 'gb';
12505		$name = 'STSongStd-Light-Acro';
12506		$cw = $this->GB_widths;
12507		$CMap = 'UniGB-UTF16-H';
12508		$registry = ['ordering' => 'GB1', 'supplement' => 4];
12509		$desc = [
12510			'Ascent' => 880,
12511			'Descent' => -120,
12512			'CapHeight' => 737,
12513			'Flags' => 6,
12514			'FontBBox' => '[-25 -254 1000 880]',
12515			'ItalicAngle' => 0,
12516			'StemV' => 58,
12517			'Style' => '<< /Panose <000000000400000000000000> >>',
12518		];
12519		$this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
12520		$this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
12521		$this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
12522		$this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
12523	}
12524
12525	function AddSJISFont()
12526	{
12527		// Add SJIS font with proportional Latin
12528		$family = 'sjis';
12529		$name = 'KozMinPro-Regular-Acro';
12530		$cw = $this->SJIS_widths;
12531		$CMap = 'UniJIS-UTF16-H';
12532		$registry = ['ordering' => 'Japan1', 'supplement' => 5];
12533		$desc = [
12534			'Ascent' => 880,
12535			'Descent' => -120,
12536			'CapHeight' => 740,
12537			'Flags' => 6,
12538			'FontBBox' => '[-195 -272 1110 1075]',
12539			'ItalicAngle' => 0,
12540			'StemV' => 86,
12541			'XHeight' => 502,
12542		];
12543		$this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
12544		$this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
12545		$this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
12546		$this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
12547	}
12548
12549	function AddUHCFont()
12550	{
12551		// Add UHC font with proportional Latin
12552		$family = 'uhc';
12553		$name = 'HYSMyeongJoStd-Medium-Acro';
12554		$cw = $this->UHC_widths;
12555		$CMap = 'UniKS-UTF16-H';
12556		$registry = ['ordering' => 'Korea1', 'supplement' => 2];
12557		$desc = [
12558			'Ascent' => 880,
12559			'Descent' => -120,
12560			'CapHeight' => 720,
12561			'Flags' => 6,
12562			'FontBBox' => '[-28 -148 1001 880]',
12563			'ItalicAngle' => 0,
12564			'StemV' => 60,
12565			'Style' => '<< /Panose <000000000600000000000000> >>',
12566		];
12567		$this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
12568		$this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
12569		$this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
12570		$this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
12571	}
12572
12573	/* -- END CJK-FONTS -- */
12574
12575	//////////////////////////////////////////////////////////////////////////////
12576	//////////////////////////////////////////////////////////////////////////////
12577	//////////////////////////////////////////////////////////////////////////////
12578	//////////////////////////////////////////////////////////////////////////////
12579	//////////////////////////////////////////////////////////////////////////////
12580	//////////////////////////////////////////////////////////////////////////////
12581	//////////////////////////////////////////////////////////////////////////////
12582
12583	function SetDefaultFont($font)
12584	{
12585		// Disallow embedded fonts to be used as defaults in PDFA
12586		if ($this->PDFA || $this->PDFX) {
12587			if (strtolower($font) == 'ctimes') {
12588				$font = 'serif';
12589			}
12590			if (strtolower($font) == 'ccourier') {
12591				$font = 'monospace';
12592			}
12593			if (strtolower($font) == 'chelvetica') {
12594				$font = 'sans-serif';
12595			}
12596		}
12597		$font = $this->SetFont($font); // returns substituted font if necessary
12598		$this->default_font = $font;
12599		$this->original_default_font = $font;
12600		if (!$this->watermark_font) {
12601			$this->watermark_font = $font;
12602		} // *WATERMARK*
12603		$this->defaultCSS['BODY']['FONT-FAMILY'] = $font;
12604		$this->cssManager->CSS['BODY']['FONT-FAMILY'] = $font;
12605	}
12606
12607	function SetDefaultFontSize($fontsize)
12608	{
12609		$this->default_font_size = $fontsize;
12610		$this->original_default_font_size = $fontsize;
12611		$this->SetFontSize($fontsize);
12612		$this->defaultCSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';
12613		$this->cssManager->CSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';
12614	}
12615
12616	function SetDefaultBodyCSS($prop, $val)
12617	{
12618		if ($prop) {
12619			$this->defaultCSS['BODY'][strtoupper($prop)] = $val;
12620			$this->cssManager->CSS['BODY'][strtoupper($prop)] = $val;
12621		}
12622	}
12623
12624	function SetDirectionality($dir = 'ltr')
12625	{
12626		/* -- OTL -- */
12627		if (strtolower($dir) == 'rtl') {
12628			if ($this->directionality != 'rtl') {
12629				// Swop L/R Margins so page 1 RTL is an 'even' page
12630				$tmp = $this->DeflMargin;
12631				$this->DeflMargin = $this->DefrMargin;
12632				$this->DefrMargin = $tmp;
12633				$this->orig_lMargin = $this->DeflMargin;
12634				$this->orig_rMargin = $this->DefrMargin;
12635
12636				$this->SetMargins($this->DeflMargin, $this->DefrMargin, $this->tMargin);
12637			}
12638			$this->directionality = 'rtl';
12639			$this->defaultAlign = 'R';
12640			$this->defaultTableAlign = 'R';
12641		} else {
12642			/* -- END OTL -- */
12643			$this->directionality = 'ltr';
12644			$this->defaultAlign = 'L';
12645			$this->defaultTableAlign = 'L';
12646		} // *OTL*
12647		$this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality;
12648	}
12649
12650	// Return either a number (factor) - based on current set fontsize (if % or em) - or exact lineheight (with 'mm' after it)
12651	function fixLineheight($v)
12652	{
12653		$lh = false;
12654		if (preg_match('/^[0-9\.,]*$/', $v) && $v >= 0) {
12655			return ($v + 0);
12656		} elseif (strtoupper($v) == 'NORMAL' || $v == 'N') {
12657			return 'N';  // mPDF 6
12658		} else {
12659			$tlh = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize, true);
12660			if ($tlh) {
12661				return ($tlh . 'mm');
12662			}
12663		}
12664		return $this->normalLineheight;
12665	}
12666
12667	function _getNormalLineheight($desc = false)
12668	{
12669		if (!$desc) {
12670			$desc = $this->CurrentFont['desc'];
12671		}
12672		if (!isset($desc['Leading'])) {
12673			$desc['Leading'] = 0;
12674		}
12675		if ($this->useFixedNormalLineHeight) {
12676			$lh = $this->normalLineheight;
12677		} elseif (isset($desc['Ascent']) && $desc['Ascent']) {
12678			$lh = ($this->adjustFontDescLineheight * ($desc['Ascent'] - $desc['Descent'] + $desc['Leading']) / 1000);
12679		} else {
12680			$lh = $this->normalLineheight;
12681		}
12682		return $lh;
12683	}
12684
12685	// Set a (fixed) lineheight to an actual value - either to named fontsize(pts) or default
12686	function SetLineHeight($FontPt = '', $lh = '')
12687	{
12688		if (!$FontPt) {
12689			$FontPt = $this->FontSizePt;
12690		}
12691		$fs = $FontPt / Mpdf::SCALE;
12692		$this->lineheight = $this->_computeLineheight($lh, $fs);
12693	}
12694
12695	function _computeLineheight($lh, $fs = '')
12696	{
12697		if ($this->shrin_k > 1) {
12698			$k = $this->shrin_k;
12699		} else {
12700			$k = 1;
12701		}
12702		if (!$fs) {
12703			$fs = $this->FontSize;
12704		}
12705		if ($lh == 'N') {
12706			$lh = $this->_getNormalLineheight();
12707		}
12708		if (preg_match('/mm/', $lh)) {
12709			return (((float) $lh) / $k); // convert to number
12710		} elseif ($lh > 0) {
12711			return ($fs * $lh);
12712		}
12713		return ($fs * $this->normalLineheight);
12714	}
12715
12716	function _setLineYpos(&$fontsize, &$fontdesc, &$CSSlineheight, $blockYpos = false)
12717	{
12718		$ypos['glyphYorigin'] = 0;
12719		$ypos['baseline-shift'] = 0;
12720		$linegap = 0;
12721		$leading = 0;
12722
12723		if (isset($fontdesc['Ascent']) && $fontdesc['Ascent'] && !$this->useFixedTextBaseline) {
12724			// Fontsize uses font metrics - this method seems to produce results compatible with browsers (except IE9)
12725			$ypos['boxtop'] = $fontdesc['Ascent'] / 1000 * $fontsize;
12726			$ypos['boxbottom'] = $fontdesc['Descent'] / 1000 * $fontsize;
12727			if (isset($fontdesc['Leading'])) {
12728				$linegap = $fontdesc['Leading'] / 1000 * $fontsize;
12729			}
12730		} // Default if not set - uses baselineC
12731		else {
12732			$ypos['boxtop'] = (0.5 + $this->baselineC) * $fontsize;
12733			$ypos['boxbottom'] = -(0.5 - $this->baselineC) * $fontsize;
12734		}
12735		$fontheight = $ypos['boxtop'] - $ypos['boxbottom'];
12736
12737		if ($this->shrin_k > 1) {
12738			$shrin_k = $this->shrin_k;
12739		} else {
12740			$shrin_k = 1;
12741		}
12742
12743		$leading = 0;
12744		if ($CSSlineheight == 'N') {
12745			$lh = $this->_getNormalLineheight($fontdesc);
12746			$lineheight = ($fontsize * $lh);
12747			$leading += $linegap; // specified in hhea or sTypo in OpenType tables
12748		} elseif (preg_match('/mm/', $CSSlineheight)) {
12749			$lineheight = (((float) $CSSlineheight) / $shrin_k); // convert to number
12750		} // ??? If lineheight is a factor e.g. 1.3  ?? use factor x 1em or ? use 'normal' lineheight * factor
12751		// Could depend on value for $text_height - a draft CSS value as set above for now
12752		elseif ($CSSlineheight > 0) {
12753			$lineheight = ($fontsize * $CSSlineheight);
12754		} else {
12755			$lineheight = ($fontsize * $this->normalLineheight);
12756		}
12757
12758		// In general, calculate the "leading" - the difference between the fontheight and the lineheight
12759		// and add half to the top and half to the bottom. BUT
12760		// If an inline element has a font-size less than the block element, and the line-height is set as an em or % value
12761		// it will add too much leading below the font and expand the height of the line - so just use the block element exttop/extbottom:
12762		if (preg_match('/mm/', $CSSlineheight) && $ypos['boxtop'] < $blockYpos['boxtop'] && $ypos['boxbottom'] > $blockYpos['boxbottom']) {
12763			$ypos['exttop'] = $blockYpos['exttop'];
12764			$ypos['extbottom'] = $blockYpos['extbottom'];
12765		} else {
12766			$leading += ($lineheight - $fontheight);
12767
12768			$ypos['exttop'] = $ypos['boxtop'] + $leading / 2;
12769			$ypos['extbottom'] = $ypos['boxbottom'] - $leading / 2;
12770		}
12771
12772
12773		// TEMP ONLY FOR DEBUGGING *********************************
12774		// $ypos['lineheight'] = $lineheight;
12775		// $ypos['fontheight'] = $fontheight;
12776		// $ypos['leading'] = $leading;
12777
12778		return $ypos;
12779	}
12780
12781	/* Called from WriteFlowingBlock() and finishFlowingBlock()
12782	  Determines the line hieght and glyph/writing position
12783	  for each element in the line to be written */
12784
12785	function _setInlineBlockHeights(&$lineBox, &$stackHeight, &$content, &$font, $is_table)
12786	{
12787		if ($this->shrin_k > 1) {
12788			$shrin_k = $this->shrin_k;
12789		} else {
12790			$shrin_k = 1;
12791		}
12792
12793		$ypos = [];
12794		$bordypos = [];
12795		$bgypos = [];
12796
12797		if ($is_table) {
12798			// FOR TABLE
12799			$fontsize = $this->FontSize;
12800			$fontkey = $this->FontFamily . $this->FontStyle;
12801			$fontdesc = $this->fonts[$fontkey]['desc'];
12802			$CSSlineheight = $this->cellLineHeight;
12803			$line_stacking_strategy = $this->cellLineStackingStrategy; // inline-line-height [default] | block-line-height | max-height | grid-height
12804			$line_stacking_shift = $this->cellLineStackingShift;  // consider-shifts [default] | disregard-shifts
12805		} else {
12806			// FOR BLOCK FONT
12807			$fontsize = $this->blk[$this->blklvl]['InlineProperties']['size'];
12808			$fontkey = $this->blk[$this->blklvl]['InlineProperties']['family'] . $this->blk[$this->blklvl]['InlineProperties']['style'];
12809			$fontdesc = $this->fonts[$fontkey]['desc'];
12810			$CSSlineheight = $this->blk[$this->blklvl]['line_height'];
12811			// inline-line-height | block-line-height | max-height | grid-height
12812			$line_stacking_strategy = (isset($this->blk[$this->blklvl]['line_stacking_strategy']) ? $this->blk[$this->blklvl]['line_stacking_strategy'] : 'inline-line-height');
12813			// consider-shifts | disregard-shifts
12814			$line_stacking_shift = (isset($this->blk[$this->blklvl]['line_stacking_shift']) ? $this->blk[$this->blklvl]['line_stacking_shift'] : 'consider-shifts');
12815		}
12816		$boxLineHeight = $this->_computeLineheight($CSSlineheight, $fontsize);
12817
12818
12819		// First, set a "strut" using block font at index $lineBox[-1]
12820		$ypos[-1] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight);
12821
12822		// for the block element - always taking the block EXTENDED progression including leading - which may be negative
12823		if ($line_stacking_strategy == 'block-line-height') {
12824			$topy = $ypos[-1]['exttop'];
12825			$bottomy = $ypos[-1]['extbottom'];
12826		} else {
12827			$topy = 0;
12828			$bottomy = 0;
12829		}
12830
12831		// Get text-middle for aligning images/objects
12832		$midpoint = $ypos[-1]['boxtop'] - (($ypos[-1]['boxtop'] - $ypos[-1]['boxbottom']) / 2);
12833
12834		// for images / inline objects / replaced elements
12835		$mta = 0; // Maximum top-aligned
12836		$mba = 0; // Maximum bottom-aligned
12837		foreach ($content as $k => $chunk) {
12838			if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'listmarker') {
12839				$ypos[$k] = $ypos[-1];
12840				// UPDATE Maximums
12841				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
12842					if ($ypos[$k]['boxtop'] > $topy) {
12843						$topy = $ypos[$k]['boxtop'];
12844					}
12845					if ($ypos[$k]['boxbottom'] < $bottomy) {
12846						$bottomy = $ypos[$k]['boxbottom'];
12847					}
12848				} else {
12849					if ($ypos[$k]['exttop'] > $topy) {
12850						$topy = $ypos[$k]['exttop'];
12851					}
12852					if ($ypos[$k]['extbottom'] < $bottomy) {
12853						$bottomy = $ypos[$k]['extbottom'];
12854					}
12855				}
12856			} elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
12857				$fontsize = $font[$k]['size'];
12858				$fontdesc = $font[$k]['curr']['desc'];
12859				$lh = 1;
12860				$ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $lh, $ypos[-1]); // Lineheight=1 fixed
12861			} elseif (isset($this->objectbuffer[$k])) {
12862				$oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];
12863				$va = $this->objectbuffer[$k]['vertical-align'];
12864
12865				if ($va == 'BS') { //  (BASELINE default)
12866					if ($oh > $topy) {
12867						$topy = $oh;
12868					}
12869				} elseif ($va == 'M') {
12870					if (($midpoint + $oh / 2) > $topy) {
12871						$topy = $midpoint + $oh / 2;
12872					}
12873					if (($midpoint - $oh / 2) < $bottomy) {
12874						$bottomy = $midpoint - $oh / 2;
12875					}
12876				} elseif ($va == 'TT') {
12877					if (($ypos[-1]['boxtop'] - $oh) < $bottomy) {
12878						$bottomy = $ypos[-1]['boxtop'] - $oh;
12879						$topy = max($topy, $ypos[-1]['boxtop']);
12880					}
12881				} elseif ($va == 'TB') {
12882					if (($ypos[-1]['boxbottom'] + $oh) > $topy) {
12883						$topy = $ypos[-1]['boxbottom'] + $oh;
12884						$bottomy = min($bottomy, $ypos[-1]['boxbottom']);
12885					}
12886				} elseif ($va == 'T') {
12887					if ($oh > $mta) {
12888						$mta = $oh;
12889					}
12890				} elseif ($va == 'B') {
12891					if ($oh > $mba) {
12892						$mba = $oh;
12893					}
12894				}
12895			} elseif ($content[$k] || $content[$k] === '0') {
12896				// FOR FLOWING BLOCK
12897				$fontsize = $font[$k]['size'];
12898				$fontdesc = $font[$k]['curr']['desc'];
12899				// In future could set CSS line-height from inline elements; for now, use block level:
12900				$ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight, $ypos[-1]);
12901
12902				if (isset($font[$k]['textparam']['text-baseline']) && $font[$k]['textparam']['text-baseline'] != 0) {
12903					$ypos[$k]['baseline-shift'] = $font[$k]['textparam']['text-baseline'];
12904				}
12905
12906				// DO ALIGNMENT FOR BASELINES *******************
12907				// Until most fonts have OpenType BASE tables, this won't work
12908				// $ypos[$k] compared to $ypos[-1] or $ypos[$k-1] using $dominant_baseline and $baseline_table
12909				// UPDATE Maximums
12910				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
12911					if ($line_stacking_shift == 'disregard-shifts') {
12912						if ($ypos[$k]['boxtop'] > $topy) {
12913							$topy = $ypos[$k]['boxtop'];
12914						}
12915						if ($ypos[$k]['boxbottom'] < $bottomy) {
12916							$bottomy = $ypos[$k]['boxbottom'];
12917						}
12918					} else {
12919						if (($ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift']) > $topy) {
12920							$topy = $ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift'];
12921						}
12922						if (($ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) {
12923							$bottomy = $ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift'];
12924						}
12925					}
12926				} else {
12927					if ($line_stacking_shift == 'disregard-shifts') {
12928						if ($ypos[$k]['exttop'] > $topy) {
12929							$topy = $ypos[$k]['exttop'];
12930						}
12931						if ($ypos[$k]['extbottom'] < $bottomy) {
12932							$bottomy = $ypos[$k]['extbottom'];
12933						}
12934					} else {
12935						if (($ypos[$k]['exttop'] + $ypos[$k]['baseline-shift']) > $topy) {
12936							$topy = $ypos[$k]['exttop'] + $ypos[$k]['baseline-shift'];
12937						}
12938						if (($ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) {
12939							$bottomy = $ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift'];
12940						}
12941					}
12942				}
12943
12944				// If BORDER set on inline element
12945				if (isset($font[$k]['bord']) && $font[$k]['bord']) {
12946					$bordfontsize = $font[$k]['textparam']['bord-decoration']['fontsize'] / $shrin_k;
12947					$bordfontkey = $font[$k]['textparam']['bord-decoration']['fontkey'];
12948					if ($bordfontkey != $fontkey || $bordfontsize != $fontsize || isset($font[$k]['textparam']['bord-decoration']['baseline'])) {
12949						$bordfontdesc = $this->fonts[$bordfontkey]['desc'];
12950						$bordypos[$k] = $this->_setLineYpos($bordfontsize, $bordfontdesc, $CSSlineheight, $ypos[-1]);
12951						if (isset($font[$k]['textparam']['bord-decoration']['baseline']) && $font[$k]['textparam']['bord-decoration']['baseline'] != 0) {
12952							$bordypos[$k]['baseline-shift'] = $font[$k]['textparam']['bord-decoration']['baseline'] / $shrin_k;
12953						}
12954					}
12955				}
12956				// If BACKGROUND set on inline element
12957				if (isset($font[$k]['spanbgcolor']) && $font[$k]['spanbgcolor']) {
12958					$bgfontsize = $font[$k]['textparam']['bg-decoration']['fontsize'] / $shrin_k;
12959					$bgfontkey = $font[$k]['textparam']['bg-decoration']['fontkey'];
12960					if ($bgfontkey != $fontkey || $bgfontsize != $fontsize || isset($font[$k]['textparam']['bg-decoration']['baseline'])) {
12961						$bgfontdesc = $this->fonts[$bgfontkey]['desc'];
12962						$bgypos[$k] = $this->_setLineYpos($bgfontsize, $bgfontdesc, $CSSlineheight, $ypos[-1]);
12963						if (isset($font[$k]['textparam']['bg-decoration']['baseline']) && $font[$k]['textparam']['bg-decoration']['baseline'] != 0) {
12964							$bgypos[$k]['baseline-shift'] = $font[$k]['textparam']['bg-decoration']['baseline'] / $shrin_k;
12965						}
12966					}
12967				}
12968			}
12969		}
12970
12971
12972		// TOP or BOTTOM aligned images
12973		if ($mta > ($topy - $bottomy)) {
12974			if (($topy - $mta) < $bottomy) {
12975				$bottomy = $topy - $mta;
12976			}
12977		}
12978		if ($mba > ($topy - $bottomy)) {
12979			if (($bottomy + $mba) > $topy) {
12980				$topy = $bottomy + $mba;
12981			}
12982		}
12983
12984		if ($line_stacking_strategy == 'block-line-height') { // fixed height set by block element (whether present or not)
12985			$topy = $ypos[-1]['exttop'];
12986			$bottomy = $ypos[-1]['extbottom'];
12987		}
12988
12989		$inclusiveHeight = $topy - $bottomy;
12990
12991		// SET $stackHeight taking note of line_stacking_strategy
12992		// NB inclusive height already takes account of need to consider block progression height (excludes leading set by lineheight)
12993		// or extended block progression height (includes leading set by lineheight)
12994		if ($line_stacking_strategy == 'block-line-height') { // fixed = extended block progression height of block element
12995			$stackHeight = $boxLineHeight;
12996		} elseif ($line_stacking_strategy == 'max-height') { // smallest height which includes extended block progression height of block element
12997			// and block progression heights of inline elements (NOT extended)
12998			$stackHeight = $inclusiveHeight;
12999		} elseif ($line_stacking_strategy == 'grid-height') { // smallest multiple of block element lineheight to include
13000			// block progression heights of inline elements (NOT extended)
13001			$stackHeight = $boxLineHeight;
13002			while ($stackHeight < $inclusiveHeight) {
13003				$stackHeight += $boxLineHeight;
13004			}
13005		} else { // 'inline-line-height' = default		// smallest height which includes extended block progression height of block element
13006			// AND extended block progression heights of inline elements
13007			$stackHeight = $inclusiveHeight;
13008		}
13009
13010		$diff = $stackHeight - $inclusiveHeight;
13011		$topy += $diff / 2;
13012		$bottomy -= $diff / 2;
13013
13014		// ADJUST $ypos => lineBox using $stackHeight; lineBox are all offsets from the top of stackHeight in mm
13015		// and SET IMAGE OFFSETS
13016		$lineBox[-1]['boxtop'] = $topy - $ypos[-1]['boxtop'];
13017		$lineBox[-1]['boxbottom'] = $topy - $ypos[-1]['boxbottom'];
13018		// $lineBox[-1]['exttop'] = $topy - $ypos[-1]['exttop'];
13019		// $lineBox[-1]['extbottom'] = $topy - $ypos[-1]['extbottom'];
13020		$lineBox[-1]['glyphYorigin'] = $topy - $ypos[-1]['glyphYorigin'];
13021		$lineBox[-1]['baseline-shift'] = $ypos[-1]['baseline-shift'];
13022
13023		$midpoint = $lineBox[-1]['boxbottom'] - (($lineBox[-1]['boxbottom'] - $lineBox[-1]['boxtop']) / 2);
13024
13025		foreach ($content as $k => $chunk) {
13026			if (isset($this->objectbuffer[$k])) {
13027				$oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];
13028				// LIST MARKERS
13029				if ($this->objectbuffer[$k]['type'] == 'listmarker') {
13030					$oh = $fontsize;
13031				} elseif ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
13032					$oh = $font[$k]['size']; // == $this->objectbuffer[$k]['fontsize']/Mpdf::SCALE;
13033					$lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];
13034					$lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];
13035					$lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];
13036					$lineBox[$k]['baseline-shift'] = 0;
13037					// continue;
13038				}
13039				$va = $this->objectbuffer[$k]['vertical-align']; // = $objattr['vertical-align'] = set as M,T,B,S
13040
13041				if ($va == 'BS') { //  (BASELINE default)
13042					$lineBox[$k]['top'] = $lineBox[-1]['glyphYorigin'] - $oh;
13043				} elseif ($va == 'M') {
13044					$lineBox[$k]['top'] = $midpoint - $oh / 2;
13045				} elseif ($va == 'TT') {
13046					$lineBox[$k]['top'] = $lineBox[-1]['boxtop'];
13047				} elseif ($va == 'TB') {
13048					$lineBox[$k]['top'] = $lineBox[-1]['boxbottom'] - $oh;
13049				} elseif ($va == 'T') {
13050					$lineBox[$k]['top'] = 0;
13051				} elseif ($va == 'B') {
13052					$lineBox[$k]['top'] = $stackHeight - $oh;
13053				}
13054			} elseif ($content[$k] || $content[$k] === '0') {
13055				$lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];
13056				$lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];
13057				// $lineBox[$k]['exttop'] = $topy - $ypos[$k]['exttop'];
13058				// $lineBox[$k]['extbottom'] = $topy - $ypos[$k]['extbottom'];
13059				$lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];
13060				$lineBox[$k]['baseline-shift'] = $ypos[$k]['baseline-shift'];
13061				if (isset($bordypos[$k]['boxtop'])) {
13062					$lineBox[$k]['border-boxtop'] = $topy - $bordypos[$k]['boxtop'];
13063					$lineBox[$k]['border-boxbottom'] = $topy - $bordypos[$k]['boxbottom'];
13064					$lineBox[$k]['border-baseline-shift'] = $bordypos[$k]['baseline-shift'];
13065				}
13066				if (isset($bgypos[$k]['boxtop'])) {
13067					$lineBox[$k]['background-boxtop'] = $topy - $bgypos[$k]['boxtop'];
13068					$lineBox[$k]['background-boxbottom'] = $topy - $bgypos[$k]['boxbottom'];
13069					$lineBox[$k]['background-baseline-shift'] = $bgypos[$k]['baseline-shift'];
13070				}
13071			}
13072		}
13073	}
13074
13075	function SetBasePath($str = '')
13076	{
13077		if (isset($_SERVER['HTTP_HOST'])) {
13078			$host = $_SERVER['HTTP_HOST'];
13079		} elseif (isset($_SERVER['SERVER_NAME'])) {
13080			$host = $_SERVER['SERVER_NAME'];
13081		} else {
13082			$host = '';
13083		}
13084		if (!$str) {
13085			if ($_SERVER['SCRIPT_NAME']) {
13086				$currentPath = dirname($_SERVER['SCRIPT_NAME']);
13087			} else {
13088				$currentPath = dirname($_SERVER['PHP_SELF']);
13089			}
13090			$currentPath = str_replace("\\", "/", $currentPath);
13091			if ($currentPath == '/') {
13092				$currentPath = '';
13093			}
13094			if ($host) {  // mPDF 6
13095				if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') {
13096					$currpath = 'https://' . $host . $currentPath . '/';
13097				} else {
13098					$currpath = 'http://' . $host . $currentPath . '/';
13099				}
13100			} else {
13101				$currpath = '';
13102			}
13103			$this->basepath = $currpath;
13104			$this->basepathIsLocal = true;
13105			return;
13106		}
13107		$str = preg_replace('/\?.*/', '', $str);
13108		if (!preg_match('/(http|https|ftp):\/\/.*\//i', $str)) {
13109			$str .= '/';
13110		}
13111		$str .= 'xxx'; // in case $str ends in / e.g. http://www.bbc.co.uk/
13112		$this->basepath = dirname($str) . "/"; // returns e.g. e.g. http://www.google.com/dir1/dir2/dir3/
13113		$this->basepath = str_replace("\\", "/", $this->basepath); // If on Windows
13114		$tr = parse_url($this->basepath);
13115		if (isset($tr['host']) && ($tr['host'] == $host)) {
13116			$this->basepathIsLocal = true;
13117		} else {
13118			$this->basepathIsLocal = false;
13119		}
13120	}
13121
13122	public function GetFullPath(&$path, $basepath = '')
13123	{
13124		// When parsing CSS need to pass temporary basepath - so links are relative to current stylesheet
13125		if (!$basepath) {
13126			$basepath = $this->basepath;
13127		}
13128
13129		// Fix path value
13130		$path = str_replace("\\", '/', $path); // If on Windows
13131
13132		// mPDF 5.7.2
13133		if (substr($path, 0, 2) === '//') {
13134			$scheme = parse_url($basepath, PHP_URL_SCHEME);
13135			$scheme = $scheme ?: 'http';
13136			$path = $scheme . ':' . $path;
13137		}
13138
13139		$path = preg_replace('|^./|', '', $path); // Inadvertently corrects "./path/etc" and "//www.domain.com/etc"
13140
13141		if (substr($path, 0, 1) == '#') {
13142			return;
13143		}
13144
13145		if (preg_match('@^(mailto|tel|fax):.*@i', $path)) {
13146			return;
13147		}
13148
13149		if (substr($path, 0, 3) == "../") { // It is a relative link
13150
13151			$backtrackamount = substr_count($path, "../");
13152			$maxbacktrack = substr_count($basepath, "/") - 3;
13153			$filepath = str_replace("../", '', $path);
13154			$path = $basepath;
13155
13156			// If it is an invalid relative link, then make it go to directory root
13157			if ($backtrackamount > $maxbacktrack) {
13158				$backtrackamount = $maxbacktrack;
13159			}
13160
13161			// Backtrack some directories
13162			for ($i = 0; $i < $backtrackamount + 1; $i++) {
13163				$path = substr($path, 0, strrpos($path, "/"));
13164			}
13165
13166			$path = $path . "/" . $filepath; // Make it an absolute path
13167
13168		} elseif ((strpos($path, ":/") === false || strpos($path, ":/") > 10) && !is_file($path)) { // It is a local link
13169
13170			if (substr($path, 0, 1) == "/") {
13171
13172				$tr = parse_url($basepath);
13173
13174				// mPDF 5.7.2
13175				$root = '';
13176				if (!empty($tr['scheme'])) {
13177					$root .= $tr['scheme'] . '://';
13178				}
13179
13180				$root .= isset($tr['host']) ? $tr['host'] : '';
13181				$root .= ((isset($tr['port']) && $tr['port']) ? (':' . $tr['port']) : ''); // mPDF 5.7.3
13182
13183				$path = $root . $path;
13184
13185			} else {
13186				$path = $basepath . $path;
13187			}
13188		}
13189		// Do nothing if it is an Absolute Link
13190	}
13191
13192	function docPageNum($num = 0, $extras = false)
13193	{
13194		if ($num < 1) {
13195			$num = $this->page;
13196		}
13197
13198		$type = $this->defaultPageNumStyle; // set default Page Number Style
13199		$ppgno = $num;
13200		$suppress = 0;
13201		$offset = 0;
13202		$lastreset = 0;
13203
13204		foreach ($this->PageNumSubstitutions as $psarr) {
13205
13206			if ($num >= $psarr['from']) {
13207
13208				if ($psarr['reset']) {
13209					if ($psarr['reset'] > 1) {
13210						$offset = $psarr['reset'] - 1;
13211					}
13212					$ppgno = $num - $psarr['from'] + 1 + $offset;
13213					$lastreset = $psarr['from'];
13214				}
13215
13216				if ($psarr['type']) {
13217					$type = $psarr['type'];
13218				}
13219
13220				if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
13221					$suppress = 1;
13222				} elseif (strtoupper($psarr['suppress']) == 'OFF') {
13223					$suppress = 0;
13224				}
13225			}
13226		}
13227
13228		if ($suppress) {
13229			return '';
13230		}
13231
13232		$ppgno = $this->_getStyledNumber($ppgno, $type);
13233
13234		if ($extras) {
13235			$ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;
13236		}
13237
13238		return $ppgno;
13239	}
13240
13241	function docPageNumTotal($num = 0, $extras = false)
13242	{
13243		if ($num < 1) {
13244			$num = $this->page;
13245		}
13246
13247		$type = $this->defaultPageNumStyle; // set default Page Number Style
13248		$ppgstart = 1;
13249		$ppgend = count($this->pages) + 1;
13250		$suppress = 0;
13251		$offset = 0;
13252
13253		foreach ($this->PageNumSubstitutions as $psarr) {
13254			if ($num >= $psarr['from']) {
13255				if ($psarr['reset']) {
13256					if ($psarr['reset'] > 1) {
13257						$offset = $psarr['reset'] - 1;
13258					}
13259					$ppgstart = $psarr['from'] + $offset;
13260					$ppgend = count($this->pages) + 1 + $offset;
13261				}
13262				if ($psarr['type']) {
13263					$type = $psarr['type'];
13264				}
13265				if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
13266					$suppress = 1;
13267				} elseif (strtoupper($psarr['suppress']) == 'OFF') {
13268					$suppress = 0;
13269				}
13270			}
13271			if ($num < $psarr['from']) {
13272				if ($psarr['reset']) {
13273					$ppgend = $psarr['from'] + $offset;
13274					break;
13275				}
13276			}
13277		}
13278
13279		if ($suppress) {
13280			return '';
13281		}
13282
13283		$ppgno = $ppgend - $ppgstart + $offset;
13284		$ppgno = $this->_getStyledNumber($ppgno, $type);
13285
13286		if ($extras) {
13287			$ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;
13288		}
13289
13290		return $ppgno;
13291	}
13292
13293	// mPDF 6
13294	function _getStyledNumber($ppgno, $type, $listmarker = false)
13295	{
13296		if ($listmarker) {
13297			$reverse = true; // Reverse RTL numerals (Hebrew) when using for list
13298			$checkfont = true; // Using list - font is set, so check if character is available
13299		} else {
13300			$reverse = false; // For pagenumbers, RTL numerals (Hebrew) will get reversed later by bidi
13301			$checkfont = false; // For pagenumbers - font is not set, so no check
13302		}
13303
13304		$decToAlpha = new Conversion\DecToAlpha();
13305		$decToCjk = new Conversion\DecToCjk();
13306		$decToHebrew = new Conversion\DecToHebrew();
13307		$decToRoman = new Conversion\DecToRoman();
13308		$decToOther = new Conversion\DecToOther($this);
13309
13310		$lowertype = strtolower($type);
13311
13312		if ($lowertype == 'upper-latin' || $lowertype == 'upper-alpha' || $type == 'A') {
13313
13314			$ppgno = $decToAlpha->convert($ppgno, true);
13315
13316		} elseif ($lowertype == 'lower-latin' || $lowertype == 'lower-alpha' || $type == 'a') {
13317
13318			$ppgno = $decToAlpha->convert($ppgno, false);
13319
13320		} elseif ($lowertype == 'upper-roman' || $type == 'I') {
13321
13322			$ppgno = $decToRoman->convert($ppgno, true);
13323
13324		} elseif ($lowertype == 'lower-roman' || $type == 'i') {
13325
13326			$ppgno = $decToRoman->convert($ppgno, false);
13327
13328		} elseif ($lowertype == 'hebrew') {
13329
13330			$ppgno = $decToHebrew->convert($ppgno, $reverse);
13331
13332		} elseif (preg_match('/(arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao)/i', $lowertype, $m)) {
13333
13334			$cp = $decToOther->getCodePage($m[1]);
13335			$ppgno = $decToOther->convert($ppgno, $cp, $checkfont);
13336
13337		} elseif ($lowertype == 'cjk-decimal') {
13338
13339			$ppgno = $decToCjk->convert($ppgno);
13340
13341		}
13342
13343		return $ppgno;
13344	}
13345
13346	function docPageSettings($num = 0)
13347	{
13348		// Returns current type (numberstyle), suppression state for this page number;
13349		// reset is only returned if set for this page number
13350		if ($num < 1) {
13351			$num = $this->page;
13352		}
13353
13354		$type = $this->defaultPageNumStyle; // set default Page Number Style
13355		$ppgno = $num;
13356		$suppress = 0;
13357		$offset = 0;
13358		$reset = '';
13359
13360		foreach ($this->PageNumSubstitutions as $psarr) {
13361			if ($num >= $psarr['from']) {
13362				if ($psarr['reset']) {
13363					if ($psarr['reset'] > 1) {
13364						$offset = $psarr['reset'] - 1;
13365					}
13366					$ppgno = $num - $psarr['from'] + 1 + $offset;
13367				}
13368				if ($psarr['type']) {
13369					$type = $psarr['type'];
13370				}
13371				if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
13372					$suppress = 1;
13373				} elseif (strtoupper($psarr['suppress']) == 'OFF') {
13374					$suppress = 0;
13375				}
13376			}
13377			if ($num == $psarr['from']) {
13378				$reset = $psarr['reset'];
13379			}
13380		}
13381
13382		if ($suppress) {
13383			$suppress = 'on';
13384		} else {
13385			$suppress = 'off';
13386		}
13387
13388		return [$type, $suppress, $reset];
13389	}
13390
13391	function RestartDocTemplate()
13392	{
13393		$this->docTemplateStart = $this->page;
13394	}
13395
13396	// Page header
13397	function Header($content = '')
13398	{
13399
13400		$this->cMarginL = 0;
13401		$this->cMarginR = 0;
13402
13403
13404		if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLHeaderE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLHeader) || (!$this->mirrorMargins && $this->HTMLHeader)) {
13405			$this->writeHTMLHeaders();
13406			return;
13407		}
13408	}
13409
13410	/* -- TABLES -- */
13411	function TableHeaderFooter($content = '', $tablestartpage = '', $tablestartcolumn = '', $horf = 'H', $level = 0, $firstSpread = true, $finalSpread = true)
13412	{
13413		if (($horf == 'H' || $horf == 'F') && !empty($content)) { // mPDF 5.7.2
13414			$table = &$this->table[1][1];
13415
13416			// mPDF 5.7.2
13417			if ($horf == 'F') { // Table Footer
13418				$firstrow = count($table['cells']) - $table['footernrows'];
13419				$lastrow = count($table['cells']) - 1;
13420			} else {  // Table Header
13421				$firstrow = 0;
13422				$lastrow = $table['headernrows'] - 1;
13423			}
13424			if (empty($content[$firstrow])) {
13425				if ($this->debug) {
13426					throw new \Mpdf\MpdfException("<tfoot> must precede <tbody> in a table");
13427				} else {
13428					return;
13429				}
13430			}
13431
13432
13433			// Advance down page by half width of top border
13434			if ($horf == 'H') { // Only if header
13435				if ($table['borders_separate']) {
13436					$adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];
13437				} else {
13438					$adv = $table['max_cell_border_width']['T'] / 2;
13439				}
13440				if ($adv) {
13441					if ($this->table_rotate) {
13442						$this->y += ($adv);
13443					} else {
13444						$this->DivLn($adv, $this->blklvl, true);
13445					}
13446				}
13447			}
13448
13449			$topy = $content[$firstrow][0]['y'] - $this->y;
13450
13451			for ($i = $firstrow; $i <= $lastrow; $i++) {
13452				$y = $this->y;
13453
13454				/* -- COLUMNS -- */
13455				// If outside columns, this is done in PaintDivBB
13456				if ($this->ColActive) {
13457					// OUTER FILL BGCOLOR of DIVS
13458					if ($this->blklvl > 0) {
13459						$firstblockfill = $this->GetFirstBlockFill();
13460						if ($firstblockfill && $this->blklvl >= $firstblockfill) {
13461							$divh = $content[$i][0]['h'];
13462							$bak_x = $this->x;
13463							$this->DivLn($divh, -3, false);
13464							// Reset current block fill
13465							$bcor = $this->blk[$this->blklvl]['bgcolorarray'];
13466							$this->SetFColor($bcor);
13467							$this->x = $bak_x;
13468						}
13469					}
13470				}
13471				/* -- END COLUMNS -- */
13472
13473				$colctr = 0;
13474				foreach ($content[$i] as $tablehf) {
13475					$colctr++;
13476					$y = Arrays::get($tablehf, 'y', null) - $topy;
13477					$this->y = $y;
13478					// Set some cell values
13479					$x = Arrays::get($tablehf, 'x', null);
13480					if (($this->mirrorMargins) && ($tablestartpage == 'ODD') && (($this->page) % 2 == 0)) { // EVEN
13481						$x = $x + $this->MarginCorrection;
13482					} elseif (($this->mirrorMargins) && ($tablestartpage == 'EVEN') && (($this->page) % 2 == 1)) { // ODD
13483						$x = $x + $this->MarginCorrection;
13484					}
13485					/* -- COLUMNS -- */
13486					// Added to correct for Columns
13487					if ($this->ColActive) {
13488						if ($this->directionality == 'rtl') { // *OTL*
13489							$x -= ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
13490						} // *OTL*
13491						else { // *OTL*
13492							$x += ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap);
13493						} // *OTL*
13494					}
13495					/* -- END COLUMNS -- */
13496
13497					if ($colctr == 1) {
13498						$x0 = $x;
13499					}
13500
13501					// mPDF ITERATION
13502					if ($this->iterationCounter) {
13503						foreach ($tablehf['textbuffer'] as $k => $t) {
13504							if (!is_array($t[0]) && preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {
13505								$vname = '__' . $m[1] . '_';
13506								if (!isset($this->$vname)) {
13507									$this->$vname = 1;
13508								} else {
13509									$this->$vname++;
13510								}
13511								$tablehf['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $tablehf['textbuffer'][$k][0]);
13512							}
13513						}
13514					}
13515
13516					$w = Arrays::get($tablehf, 'w', null);
13517					$h = Arrays::get($tablehf, 'h', null);
13518					$va = Arrays::get($tablehf, 'va', null);
13519					$R = Arrays::get($tablehf, 'R', null);
13520					$direction = Arrays::get($tablehf, 'direction', null);
13521					$mih = Arrays::get($tablehf, 'mih', null);
13522					$border = Arrays::get($tablehf, 'border', null);
13523					$border_details = Arrays::get($tablehf, 'border_details', null);
13524					$padding = Arrays::get($tablehf, 'padding', null);
13525					$this->tabletheadjustfinished = true;
13526
13527					$textbuffer = Arrays::get($tablehf, 'textbuffer', null);
13528
13529					// Align
13530					$align = Arrays::get($tablehf, 'a', null);
13531					$this->cellTextAlign = $align;
13532
13533					$this->cellLineHeight = Arrays::get($tablehf, 'cellLineHeight', null);
13534					$this->cellLineStackingStrategy = Arrays::get($tablehf, 'cellLineStackingStrategy', null);
13535					$this->cellLineStackingShift = Arrays::get($tablehf, 'cellLineStackingShift', null);
13536
13537					$this->x = $x;
13538
13539					if ($this->ColActive) {
13540						if ($table['borders_separate']) {
13541							$tablefill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;
13542							if ($tablefill) {
13543								$color = $this->colorConverter->convert($tablefill, $this->PDFAXwarnings);
13544								if ($color) {
13545									$xadj = ($table['border_spacing_H'] / 2);
13546									$yadj = ($table['border_spacing_V'] / 2);
13547									$wadj = $table['border_spacing_H'];
13548									$hadj = $table['border_spacing_V'];
13549									if ($i == $firstrow && $horf == 'H') {  // Top
13550										$yadj += $table['padding']['T'] + $table['border_details']['T']['w'];
13551										$hadj += $table['padding']['T'] + $table['border_details']['T']['w'];
13552									}
13553									if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1)) || (!isset($tablehf['rowspan']) && ($i + 1) == ($lastrow + 1))) && $horf == 'F') { // Bottom
13554										$hadj += $table['padding']['B'] + $table['border_details']['B']['w'];
13555									}
13556									if ($colctr == 1) {  // Left
13557										$xadj += $table['padding']['L'] + $table['border_details']['L']['w'];
13558										$wadj += $table['padding']['L'] + $table['border_details']['L']['w'];
13559									}
13560									if ($colctr == count($content[$i])) { // Right
13561										$wadj += $table['padding']['R'] + $table['border_details']['R']['w'];
13562									}
13563									$this->SetFColor($color);
13564									$this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');
13565								}
13566							}
13567						}
13568					}
13569
13570					if ($table['empty_cells'] != 'hide' || !empty($textbuffer) || !$table['borders_separate']) {
13571						$paintcell = true;
13572					} else {
13573						$paintcell = false;
13574					}
13575
13576					// Vertical align
13577					if ($R && intval($R) > 0 && isset($va) && $va != 'B') {
13578						$va = 'B';
13579					}
13580
13581					if (!isset($va) || empty($va) || $va == 'M') {
13582						$this->y += ($h - $mih) / 2;
13583					} elseif (isset($va) && $va == 'B') {
13584						$this->y += $h - $mih;
13585					}
13586
13587
13588					// TABLE ROW OR CELL FILL BGCOLOR
13589					$fill = 0;
13590					if (isset($tablehf['bgcolor']) && $tablehf['bgcolor'] && $tablehf['bgcolor'] != 'transparent') {
13591						$fill = $tablehf['bgcolor'];
13592						$leveladj = 6;
13593					} elseif (isset($content[$i][0]['trbgcolor']) && $content[$i][0]['trbgcolor'] && $content[$i][0]['trbgcolor'] != 'transparent') { // Row color
13594						$fill = $content[$i][0]['trbgcolor'];
13595						$leveladj = 3;
13596					}
13597					if ($fill && $paintcell) {
13598						$color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
13599						if ($color) {
13600							if ($table['borders_separate']) {
13601								if ($this->ColActive) {
13602									$this->SetFColor($color);
13603									$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');
13604								} else {
13605									$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];
13606								}
13607							} else {
13608								if ($this->ColActive) {
13609									$this->SetFColor($color);
13610									$this->Rect($x, $y, $w, $h, 'F');
13611								} else {
13612									$this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color];
13613								}
13614							}
13615						}
13616					}
13617
13618
13619					/* -- BACKGROUNDS -- */
13620					if (isset($tablehf['gradient']) && $tablehf['gradient'] && $paintcell) {
13621						$g = $this->gradient->parseBackgroundGradient($tablehf['gradient']);
13622						if ($g) {
13623							if ($table['borders_separate']) {
13624								$px = $x + ($table['border_spacing_H'] / 2);
13625								$py = $y + ($table['border_spacing_V'] / 2);
13626								$pw = $w - $table['border_spacing_H'];
13627								$ph = $h - $table['border_spacing_V'];
13628							} else {
13629								$px = $x;
13630								$py = $y;
13631								$pw = $w;
13632								$ph = $h;
13633							}
13634							if ($this->ColActive) {
13635								$this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
13636							} else {
13637								$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' => ''];
13638							}
13639						}
13640					}
13641
13642					if (isset($tablehf['background-image']) && $paintcell) {
13643						if ($tablehf['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $tablehf['background-image']['gradient'])) {
13644							$g = $this->gradient->parseMozGradient($tablehf['background-image']['gradient']);
13645							if ($g) {
13646								if ($table['borders_separate']) {
13647									$px = $x + ($table['border_spacing_H'] / 2);
13648									$py = $y + ($table['border_spacing_V'] / 2);
13649									$pw = $w - $table['border_spacing_H'];
13650									$ph = $h - $table['border_spacing_V'];
13651								} else {
13652									$px = $x;
13653									$py = $y;
13654									$pw = $w;
13655									$ph = $h;
13656								}
13657								if ($this->ColActive) {
13658									$this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
13659								} else {
13660									$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' => ''];
13661								}
13662							}
13663						} elseif ($tablehf['background-image']['image_id']) { // Background pattern
13664							$n = count($this->patterns) + 1;
13665							if ($table['borders_separate']) {
13666								$px = $x + ($table['border_spacing_H'] / 2);
13667								$py = $y + ($table['border_spacing_V'] / 2);
13668								$pw = $w - $table['border_spacing_H'];
13669								$ph = $h - $table['border_spacing_V'];
13670							} else {
13671								$px = $x;
13672								$py = $y;
13673								$pw = $w;
13674								$ph = $h;
13675							}
13676							if ($this->ColActive) {
13677								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']);
13678								$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']];
13679								if ($tablehf['background-image']['opacity'] > 0 && $tablehf['background-image']['opacity'] < 1) {
13680									$opac = $this->SetAlpha($tablehf['background-image']['opacity'], 'Normal', true);
13681								} else {
13682									$opac = '';
13683								}
13684								$this->_out(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));
13685							} else {
13686								$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']];
13687							}
13688						}
13689					}
13690					/* -- END BACKGROUNDS -- */
13691
13692					// Cell Border
13693					if ($table['borders_separate'] && $paintcell && $border) {
13694						$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']);
13695					} elseif ($paintcell && $border) {
13696						$this->_tableRect($x, $y, $w, $h, $border, $border_details, true, $table['borders_separate']);   // true causes buffer
13697					}
13698
13699					// Print cell content
13700					if (!empty($textbuffer)) {
13701						if ($horf == 'F' && preg_match('/{colsum([0-9]*)[_]*}/', $textbuffer[0][0], $m)) {
13702							$rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$colctr - 1]);
13703							$textbuffer[0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $textbuffer[0][0]);
13704						}
13705
13706						if ($R) {
13707							$cellPtSize = $textbuffer[0][11] / $this->shrin_k;
13708							if (!$cellPtSize) {
13709								$cellPtSize = $this->default_font_size;
13710							}
13711							$cellFontHeight = ($cellPtSize / Mpdf::SCALE);
13712							$opx = $this->x;
13713							$opy = $this->y;
13714							$angle = intval($R);
13715
13716							// Only allow 45 - 90 degrees (when bottom-aligned) or -90
13717							if ($angle > 90) {
13718								$angle = 90;
13719							} elseif ($angle > 0 && (isset($va) && $va != 'B')) {
13720								$angle = 90;
13721							} elseif ($angle > 0 && $angle < 45) {
13722								$angle = 45;
13723							} elseif ($angle < 0) {
13724								$angle = -90;
13725							}
13726
13727							$offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);
13728							if (isset($align) && $align == 'R') {
13729								$this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($padding['R'] + $border_details['R']['w']);
13730							} elseif (!isset($align) || $align == 'C') {
13731								$this->x += ($w / 2) + ($offset);
13732							} else {
13733								$this->x += ($offset) + ($cellFontHeight / 3) + ($padding['L'] + $border_details['L']['w']);
13734							}
13735							$str = '';
13736							foreach ($tablehf['textbuffer'] as $t) {
13737								$str .= $t[0] . ' ';
13738							}
13739							$str = rtrim($str);
13740
13741							if (!isset($va) || $va == 'M') {
13742								$this->y -= ($h - $mih) / 2; // Undo what was added earlier VERTICAL ALIGN
13743								if ($angle > 0) {
13744									$this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']) + ($mih - ($padding['T'] + $border_details['T']['w'] + $border_details['B']['w'] + $padding['B']));
13745								} elseif ($angle < 0) {
13746									$this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']);
13747								}
13748							} elseif (isset($va) && $va == 'B') {
13749								$this->y -= $h - $mih; // Undo what was added earlier VERTICAL ALIGN
13750								if ($angle > 0) {
13751									$this->y += $h - ($border_details['B']['w'] + $padding['B']);
13752								} elseif ($angle < 0) {
13753									$this->y += $h - $mih + ($padding['T'] + $border_details['T']['w']);
13754								}
13755							} elseif (isset($va) && $va == 'T') {
13756								if ($angle > 0) {
13757									$this->y += $mih - ($border_details['B']['w'] + $padding['B']);
13758								} elseif ($angle < 0) {
13759									$this->y += ($padding['T'] + $border_details['T']['w']);
13760								}
13761							}
13762
13763							$this->Rotate($angle, $this->x, $this->y);
13764							$s_fs = $this->FontSizePt;
13765							$s_f = $this->FontFamily;
13766							$s_st = $this->FontStyle;
13767							if (!empty($textbuffer[0][3])) { // Font Color
13768								$cor = $textbuffer[0][3];
13769								$this->SetTColor($cor);
13770							}
13771							$this->SetFont($textbuffer[0][4], $textbuffer[0][2], $cellPtSize, true, true);
13772
13773							$this->magic_reverse_dir($str, $this->directionality, $textbuffer[0][18]);
13774							$this->Text($this->x, $this->y, $str, $textbuffer[0][18], $textbuffer[0][8]); // textvar
13775							$this->Rotate(0);
13776							$this->SetFont($s_f, $s_st, $s_fs, true, true);
13777							$this->SetTColor(0);
13778							$this->x = $opx;
13779							$this->y = $opy;
13780						} else {
13781							if ($table['borders_separate']) { // NB twice border width
13782								$xadj = $border_details['L']['w'] + $padding['L'] + ($table['border_spacing_H'] / 2);
13783								$wadj = $border_details['L']['w'] + $border_details['R']['w'] + $padding['L'] + $padding['R'] + $table['border_spacing_H'];
13784								$yadj = $border_details['T']['w'] + $padding['T'] + ($table['border_spacing_H'] / 2);
13785							} else {
13786								$xadj = $border_details['L']['w'] / 2 + $padding['L'];
13787								$wadj = ($border_details['L']['w'] + $border_details['R']['w']) / 2 + $padding['L'] + $padding['R'];
13788								$yadj = $border_details['T']['w'] / 2 + $padding['T'];
13789							}
13790
13791							$this->divwidth = $w - ($wadj);
13792							$this->x += $xadj;
13793							$this->y += $yadj;
13794							$this->printbuffer($textbuffer, '', true, false, $direction);
13795						}
13796					}
13797					$textbuffer = [];
13798
13799					/* -- BACKGROUNDS -- */
13800					if (!$this->ColActive) {
13801						if (isset($content[$i][0]['trgradients']) && ($colctr == 1 || $table['borders_separate'])) {
13802							$g = $this->gradient->parseBackgroundGradient($content[$i][0]['trgradients']);
13803							if ($g) {
13804								$gx = $x0;
13805								$gy = $y;
13806								$gh = $h;
13807								$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
13808								if ($table['borders_separate']) {
13809									$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
13810									$clx = $x + ($table['border_spacing_H'] / 2);
13811									$cly = $y + ($table['border_spacing_V'] / 2);
13812									$clw = $w - $table['border_spacing_H'];
13813									$clh = $h - $table['border_spacing_V'];
13814									// Set clipping path
13815									$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
13816									$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];
13817								} else {
13818									$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' => ''];
13819								}
13820							}
13821						}
13822
13823						if (isset($content[$i][0]['trbackground-images']) && ($colctr == 1 || $table['borders_separate'])) {
13824							if ($content[$i][0]['trbackground-images']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $content[$i][0]['trbackground-images']['gradient'])) {
13825								$g = $this->gradient->parseMozGradient($content[$i][0]['trbackground-images']['gradient']);
13826								if ($g) {
13827									$gx = $x0;
13828									$gy = $y;
13829									$gh = $h;
13830									$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
13831									if ($table['borders_separate']) {
13832										$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
13833										$clx = $x + ($table['border_spacing_H'] / 2);
13834										$cly = $y + ($table['border_spacing_V'] / 2);
13835										$clw = $w - $table['border_spacing_H'];
13836										$clh = $h - $table['border_spacing_V'];
13837										// Set clipping path
13838										$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
13839										$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];
13840									} else {
13841										$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' => ''];
13842									}
13843								}
13844							} else {
13845								$image_id = $content[$i][0]['trbackground-images']['image_id'];
13846								$orig_w = $content[$i][0]['trbackground-images']['orig_w'];
13847								$orig_h = $content[$i][0]['trbackground-images']['orig_h'];
13848								$x_pos = $content[$i][0]['trbackground-images']['x_pos'];
13849								$y_pos = $content[$i][0]['trbackground-images']['y_pos'];
13850								$x_repeat = $content[$i][0]['trbackground-images']['x_repeat'];
13851								$y_repeat = $content[$i][0]['trbackground-images']['y_repeat'];
13852								$resize = $content[$i][0]['trbackground-images']['resize'];
13853								$opacity = $content[$i][0]['trbackground-images']['opacity'];
13854								$itype = $content[$i][0]['trbackground-images']['itype'];
13855
13856								$clippath = '';
13857								$gx = $x0;
13858								$gy = $y;
13859								$gh = $h;
13860								$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
13861								if ($table['borders_separate']) {
13862									$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
13863									$clx = $x + ($table['border_spacing_H'] / 2);
13864									$cly = $y + ($table['border_spacing_V'] / 2);
13865									$clw = $w - $table['border_spacing_H'];
13866									$clh = $h - $table['border_spacing_V'];
13867									// Set clipping path
13868									$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
13869									$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];
13870								} else {
13871									$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];
13872								}
13873							}
13874						}
13875					}
13876					/* -- END BACKGROUNDS -- */
13877
13878					// TABLE BORDER - if separate OR collapsed and only table border
13879					if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {
13880						$halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);
13881						$halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);
13882						$halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);
13883						$halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);
13884						$tbx = $x;
13885						$tby = $y;
13886						$tbw = $w;
13887						$tbh = $h;
13888						$tab_bord = 0;
13889						$corner = '';
13890						if ($i == $firstrow && $horf == 'H') {  // Top
13891							$tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);
13892							$tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);
13893							$this->setBorder($tab_bord, Border::TOP);
13894							$corner .= 'T';
13895						}
13896						if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1))) && $horf == 'F') { // Bottom
13897							$tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);
13898							$this->setBorder($tab_bord, Border::BOTTOM);
13899							$corner .= 'B';
13900						}
13901						if ($colctr == 1 && $firstSpread) { // Left
13902							$tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);
13903							$tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);
13904							$this->setBorder($tab_bord, Border::LEFT);
13905							$corner .= 'L';
13906						}
13907						if ($colctr == count($content[$i]) && $finalSpread) { // Right
13908							$tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);
13909							$this->setBorder($tab_bord, Border::RIGHT);
13910							$corner .= 'R';
13911						}
13912						$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']);
13913					}
13914				}// end column $content
13915				$this->y = $y + $h; // Update y coordinate
13916			}// end row $i
13917			unset($table);
13918			$this->colsums = [];
13919		}
13920	}
13921
13922	/* -- END TABLES -- */
13923
13924	function SetHTMLHeader($header = '', $OE = '', $write = false)
13925	{
13926
13927		$height = 0;
13928		if (is_array($header) && isset($header['html']) && $header['html']) {
13929			$Hhtml = $header['html'];
13930			if ($this->setAutoTopMargin) {
13931				if (isset($header['h'])) {
13932					$height = $header['h'];
13933				} else {
13934					$height = $this->_getHtmlHeight($Hhtml);
13935				}
13936			}
13937		} elseif (!is_array($header) && $header) {
13938			$Hhtml = $header;
13939			if ($this->setAutoTopMargin) {
13940				$height = $this->_getHtmlHeight($Hhtml);
13941			}
13942		} else {
13943			$Hhtml = '';
13944		}
13945
13946		if ($OE !== 'E') {
13947			$OE = 'O';
13948		}
13949
13950		if ($OE === 'E') {
13951			if ($Hhtml) {
13952				$this->HTMLHeaderE = [];
13953				$this->HTMLHeaderE['html'] = $Hhtml;
13954				$this->HTMLHeaderE['h'] = $height;
13955			} else {
13956				$this->HTMLHeaderE = '';
13957			}
13958		} else {
13959			if ($Hhtml) {
13960				$this->HTMLHeader = [];
13961				$this->HTMLHeader['html'] = $Hhtml;
13962				$this->HTMLHeader['h'] = $height;
13963			} else {
13964				$this->HTMLHeader = '';
13965			}
13966		}
13967
13968		if (!$this->mirrorMargins && $OE == 'E') {
13969			return;
13970		}
13971		if ($Hhtml == '') {
13972			return;
13973		}
13974
13975		if ($this->setAutoTopMargin == 'pad') {
13976			$this->tMargin = $this->margin_header + $height + $this->orig_tMargin;
13977			if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {
13978				$this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;
13979			}
13980		} elseif ($this->setAutoTopMargin == 'stretch') {
13981			$this->tMargin = max($this->orig_tMargin, $this->margin_header + $height + $this->autoMarginPadding);
13982			if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {
13983				$this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;
13984			}
13985		}
13986		if ($write && $this->state != 0 && (($this->mirrorMargins && $OE == 'E' && ($this->page) % 2 == 0) || ($this->mirrorMargins && $OE != 'E' && ($this->page) % 2 == 1) || !$this->mirrorMargins)) {
13987			$this->writeHTMLHeaders();
13988		}
13989	}
13990
13991	function SetHTMLFooter($footer = '', $OE = '')
13992	{
13993		$height = 0;
13994		if (is_array($footer) && isset($footer['html']) && $footer['html']) {
13995			$Fhtml = $footer['html'];
13996			if ($this->setAutoBottomMargin) {
13997				if (isset($footer['h'])) {
13998					$height = $footer['h'];
13999				} else {
14000					$height = $this->_getHtmlHeight($Fhtml);
14001				}
14002			}
14003		} elseif (!is_array($footer) && $footer) {
14004			$Fhtml = $footer;
14005			if ($this->setAutoBottomMargin) {
14006				$height = $this->_getHtmlHeight($Fhtml);
14007			}
14008		} else {
14009			$Fhtml = '';
14010		}
14011
14012		if ($OE !== 'E') {
14013			$OE = 'O';
14014		}
14015
14016		if ($OE === 'E') {
14017			if ($Fhtml) {
14018				$this->HTMLFooterE = [];
14019				$this->HTMLFooterE['html'] = $Fhtml;
14020				$this->HTMLFooterE['h'] = $height;
14021			} else {
14022				$this->HTMLFooterE = '';
14023			}
14024		} else {
14025			if ($Fhtml) {
14026				$this->HTMLFooter = [];
14027				$this->HTMLFooter['html'] = $Fhtml;
14028				$this->HTMLFooter['h'] = $height;
14029			} else {
14030				$this->HTMLFooter = '';
14031			}
14032		}
14033
14034		if (!$this->mirrorMargins && $OE == 'E') {
14035			return;
14036		}
14037
14038		if ($Fhtml == '') {
14039			return false;
14040		}
14041
14042		if ($this->setAutoBottomMargin == 'pad') {
14043			$this->bMargin = $this->margin_footer + $height + $this->orig_bMargin;
14044			$this->PageBreakTrigger = $this->h - $this->bMargin;
14045			if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {
14046				$this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;
14047			}
14048		} elseif ($this->setAutoBottomMargin == 'stretch') {
14049			$this->bMargin = max($this->orig_bMargin, $this->margin_footer + $height + $this->autoMarginPadding);
14050			$this->PageBreakTrigger = $this->h - $this->bMargin;
14051			if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {
14052				$this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;
14053			}
14054		}
14055	}
14056
14057	function _getHtmlHeight($html)
14058	{
14059		$save_state = $this->state;
14060		if ($this->state == 0) {
14061			$this->AddPage($this->CurOrientation);
14062		}
14063		$this->state = 2;
14064		$this->Reset();
14065		$this->pageoutput[$this->page] = [];
14066		$save_x = $this->x;
14067		$save_y = $this->y;
14068		$this->x = $this->lMargin;
14069		$this->y = $this->margin_header;
14070		$html = str_replace('{PAGENO}', $this->pagenumPrefix . $this->docPageNum($this->page) . $this->pagenumSuffix, $html);
14071		$html = str_replace($this->aliasNbPgGp, $this->nbpgPrefix . $this->docPageNumTotal($this->page) . $this->nbpgSuffix, $html);
14072		$html = str_replace($this->aliasNbPg, $this->page, $html);
14073		$html = preg_replace_callback('/\{DATE\s+(.*?)\}/', [$this, 'date_callback'], $html); // mPDF 5.7
14074		$this->HTMLheaderPageLinks = [];
14075		$this->HTMLheaderPageAnnots = [];
14076		$this->HTMLheaderPageForms = [];
14077		$savepb = $this->pageBackgrounds;
14078		$this->writingHTMLheader = true;
14079		$this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
14080		$this->writingHTMLheader = false;
14081		$h = ($this->y - $this->margin_header);
14082		$this->Reset();
14083		// mPDF 5.7.2 - Clear in case Float used in Header/Footer
14084		$this->blk[0]['blockContext'] = 0;
14085		$this->blk[0]['float_endpos'] = 0;
14086
14087		$this->pageoutput[$this->page] = [];
14088		$this->headerbuffer = '';
14089		$this->pageBackgrounds = $savepb;
14090		$this->x = $save_x;
14091		$this->y = $save_y;
14092		$this->state = $save_state;
14093		if ($save_state == 0) {
14094			unset($this->pages[1]);
14095			$this->page = 0;
14096		}
14097		return $h;
14098	}
14099
14100	// Called internally from Header
14101	function writeHTMLHeaders()
14102	{
14103
14104		if ($this->mirrorMargins && ($this->page) % 2 == 0) {
14105			$OE = 'E';
14106		} else {
14107			$OE = 'O';
14108		}
14109
14110		if ($OE === 'E') {
14111			$this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeaderE['html'];
14112		} else {
14113			$this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeader['html'];
14114		}
14115
14116		if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {
14117			$this->saveHTMLHeader[$this->page][$OE]['rotate'] = true;
14118			$this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->tMargin;
14119			$this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->bMargin;
14120			$this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;
14121			$this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;
14122			$this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->h;
14123			$this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->w;
14124		} else {
14125			$this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->lMargin;
14126			$this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->rMargin;
14127			$this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;
14128			$this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;
14129			$this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->w;
14130			$this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->h;
14131		}
14132	}
14133
14134	function writeHTMLFooters()
14135	{
14136
14137		if ($this->mirrorMargins && ($this->page) % 2 == 0) {
14138			$OE = 'E';
14139		} else {
14140			$OE = 'O';
14141		}
14142
14143		if ($OE === 'E') {
14144			$this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooterE['html'];
14145		} else {
14146			$this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooter['html'];
14147		}
14148
14149		if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {
14150			$this->saveHTMLFooter[$this->page][$OE]['rotate'] = true;
14151			$this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->tMargin;
14152			$this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->bMargin;
14153			$this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->rMargin;
14154			$this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->lMargin;
14155			$this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;
14156			$this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;
14157			$this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->h;
14158			$this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->w;
14159		} else {
14160			$this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->lMargin;
14161			$this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->rMargin;
14162			$this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->tMargin;
14163			$this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->bMargin;
14164			$this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;
14165			$this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;
14166			$this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->w;
14167			$this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->h;
14168		}
14169	}
14170
14171	// mPDF 6
14172	function _shareHeaderFooterWidth($cl, $cc, $cr)
14173	{
14174	// mPDF 6
14175		$l = mb_strlen($cl, 'UTF-8');
14176		$c = mb_strlen($cc, 'UTF-8');
14177		$r = mb_strlen($cr, 'UTF-8');
14178		$s = max($l, $r);
14179		$tw = $c + 2 * $s;
14180		if ($tw > 0) {
14181			return [intval($s * 100 / $tw), intval($c * 100 / $tw), intval($s * 100 / $tw)];
14182		} else {
14183			return [33, 33, 33];
14184		}
14185	}
14186
14187	// mPDF 6
14188	// Create an HTML header/footer from array (non-HTML header/footer)
14189	function _createHTMLheaderFooter($arr, $hf)
14190	{
14191		$lContent = (isset($arr['L']['content']) ? $arr['L']['content'] : '');
14192		$cContent = (isset($arr['C']['content']) ? $arr['C']['content'] : '');
14193		$rContent = (isset($arr['R']['content']) ? $arr['R']['content'] : '');
14194		list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($lContent, $cContent, $rContent);
14195		if ($hf == 'H') {
14196			$valign = 'bottom';
14197			$vpadding = '0 0 ' . $this->header_line_spacing . 'em 0';
14198		} else {
14199			$valign = 'top';
14200			$vpadding = '' . $this->footer_line_spacing . 'em 0 0 0';
14201		}
14202		if ($this->directionality == 'rtl') { // table columns get reversed so need different text-alignment
14203			$talignL = 'right';
14204			$talignR = 'left';
14205		} else {
14206			$talignL = 'left';
14207			$talignR = 'right';
14208		}
14209		$html = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: ' . $valign . '; color: #000000; ';
14210		if (isset($arr['line']) && $arr['line']) {
14211			$html .= ' border-' . $valign . ': 0.1mm solid #000000;';
14212		}
14213		$html .= '">';
14214		$html .= '<tr>';
14215		$html .= '<td width="' . $lw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignL . '; ';
14216		if (isset($arr['L']['font-family'])) {
14217			$html .= ' font-family: ' . $arr['L']['font-family'] . ';';
14218		}
14219		if (isset($arr['L']['color'])) {
14220			$html .= ' color: ' . $arr['L']['color'] . ';';
14221		}
14222		if (isset($arr['L']['font-size'])) {
14223			$html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;';
14224		}
14225		if (isset($arr['L']['font-style'])) {
14226			if ($arr['L']['font-style'] == 'B' || $arr['L']['font-style'] == 'BI') {
14227				$html .= ' font-weight: bold;';
14228			}
14229			if ($arr['L']['font-style'] == 'I' || $arr['L']['font-style'] == 'BI') {
14230				$html .= ' font-style: italic;';
14231			}
14232		}
14233		$html .= '">' . $lContent . '</td>';
14234		$html .= '<td width="' . $cw . '%" style="padding: ' . $vpadding . '; text-align: center; ';
14235		if (isset($arr['C']['font-family'])) {
14236			$html .= ' font-family: ' . $arr['C']['font-family'] . ';';
14237		}
14238		if (isset($arr['C']['color'])) {
14239			$html .= ' color: ' . $arr['C']['color'] . ';';
14240		}
14241		if (isset($arr['C']['font-size'])) {
14242			$html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;';
14243		}
14244		if (isset($arr['C']['font-style'])) {
14245			if ($arr['C']['font-style'] == 'B' || $arr['C']['font-style'] == 'BI') {
14246				$html .= ' font-weight: bold;';
14247			}
14248			if ($arr['C']['font-style'] == 'I' || $arr['C']['font-style'] == 'BI') {
14249				$html .= ' font-style: italic;';
14250			}
14251		}
14252		$html .= '">' . $cContent . '</td>';
14253		$html .= '<td width="' . $rw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignR . '; ';
14254		if (isset($arr['R']['font-family'])) {
14255			$html .= ' font-family: ' . $arr['R']['font-family'] . ';';
14256		}
14257		if (isset($arr['R']['color'])) {
14258			$html .= ' color: ' . $arr['R']['color'] . ';';
14259		}
14260		if (isset($arr['R']['font-size'])) {
14261			$html .= ' font-size: ' . $arr['R']['font-size'] . 'pt;';
14262		}
14263		if (isset($arr['R']['font-style'])) {
14264			if ($arr['R']['font-style'] == 'B' || $arr['R']['font-style'] == 'BI') {
14265				$html .= ' font-weight: bold;';
14266			}
14267			if ($arr['R']['font-style'] == 'I' || $arr['R']['font-style'] == 'BI') {
14268				$html .= ' font-style: italic;';
14269			}
14270		}
14271		$html .= '">' . $rContent . '</td>';
14272		$html .= '</tr></table>';
14273		return $html;
14274	}
14275
14276	function DefHeaderByName($name, $arr)
14277	{
14278		if (!$name) {
14279			$name = '_nonhtmldefault';
14280		}
14281		$html = $this->_createHTMLheaderFooter($arr, 'H');
14282
14283		$this->pageHTMLheaders[$name]['html'] = $html;
14284		$this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html);
14285	}
14286
14287	function DefFooterByName($name, $arr)
14288	{
14289		if (!$name) {
14290			$name = '_nonhtmldefault';
14291		}
14292		$html = $this->_createHTMLheaderFooter($arr, 'F');
14293
14294		$this->pageHTMLfooters[$name]['html'] = $html;
14295		$this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html);
14296	}
14297
14298	function SetHeaderByName($name, $side = 'O', $write = false)
14299	{
14300		if (!$name) {
14301			$name = '_nonhtmldefault';
14302		}
14303		$this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);
14304	}
14305
14306	function SetFooterByName($name, $side = 'O')
14307	{
14308		if (!$name) {
14309			$name = '_nonhtmldefault';
14310		}
14311		$this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);
14312	}
14313
14314	function DefHTMLHeaderByName($name, $html)
14315	{
14316		if (!$name) {
14317			$name = '_default';
14318		}
14319
14320		$this->pageHTMLheaders[$name]['html'] = $html;
14321		$this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html);
14322	}
14323
14324	function DefHTMLFooterByName($name, $html)
14325	{
14326		if (!$name) {
14327			$name = '_default';
14328		}
14329
14330		$this->pageHTMLfooters[$name]['html'] = $html;
14331		$this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html);
14332	}
14333
14334	function SetHTMLHeaderByName($name, $side = 'O', $write = false)
14335	{
14336		if (!$name) {
14337			$name = '_default';
14338		}
14339		$this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);
14340	}
14341
14342	function SetHTMLFooterByName($name, $side = 'O')
14343	{
14344		if (!$name) {
14345			$name = '_default';
14346		}
14347		$this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);
14348	}
14349
14350	function SetHeader($Harray = [], $side = '', $write = false)
14351	{
14352		$oddhtml = '';
14353		$evenhtml = '';
14354		if (is_string($Harray)) {
14355			if (strlen($Harray) == 0) {
14356				$oddhtml = '';
14357				$evenhtml = '';
14358			} elseif (strpos($Harray, '|') !== false) {
14359				$hdet = explode('|', $Harray);
14360				list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($hdet[0], $hdet[1], $hdet[2]);
14361				$oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';
14362				if ($this->defaultheaderfontsize) {
14363					$oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
14364				}
14365				if ($this->defaultheaderfontstyle) {
14366					if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
14367						$oddhtml .= ' font-weight: bold;';
14368					}
14369					if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
14370						$oddhtml .= ' font-style: italic;';
14371					}
14372				}
14373				if ($this->defaultheaderline) {
14374					$oddhtml .= ' border-bottom: 0.1mm solid #000000;';
14375				}
14376				$oddhtml .= '">';
14377				$oddhtml .= '<tr>';
14378				$oddhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[0] . '</td>';
14379				$oddhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>';
14380				$oddhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[2] . '</td>';
14381				$oddhtml .= '</tr></table>';
14382
14383				$evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';
14384				if ($this->defaultheaderfontsize) {
14385					$evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
14386				}
14387				if ($this->defaultheaderfontstyle) {
14388					if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
14389						$evenhtml .= ' font-weight: bold;';
14390					}
14391					if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
14392						$evenhtml .= ' font-style: italic;';
14393					}
14394				}
14395				if ($this->defaultheaderline) {
14396					$evenhtml .= ' border-bottom: 0.1mm solid #000000;';
14397				}
14398				$evenhtml .= '">';
14399				$evenhtml .= '<tr>';
14400				$evenhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[2] . '</td>';
14401				$evenhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>';
14402				$evenhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[0] . '</td>';
14403				$evenhtml .= '</tr></table>';
14404			} else {
14405				$oddhtml = '<div style="margin: 0; color: #000000; ';
14406				if ($this->defaultheaderfontsize) {
14407					$oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
14408				}
14409				if ($this->defaultheaderfontstyle) {
14410					if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
14411						$oddhtml .= ' font-weight: bold;';
14412					}
14413					if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
14414						$oddhtml .= ' font-style: italic;';
14415					}
14416				}
14417				if ($this->defaultheaderline) {
14418					$oddhtml .= ' border-bottom: 0.1mm solid #000000;';
14419				}
14420				$oddhtml .= 'text-align: right; ">' . $Harray . '</div>';
14421
14422				$evenhtml = '<div style="margin: 0; color: #000000; ';
14423				if ($this->defaultheaderfontsize) {
14424					$evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
14425				}
14426				if ($this->defaultheaderfontstyle) {
14427					if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
14428						$evenhtml .= ' font-weight: bold;';
14429					}
14430					if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
14431						$evenhtml .= ' font-style: italic;';
14432					}
14433				}
14434				if ($this->defaultheaderline) {
14435					$evenhtml .= ' border-bottom: 0.1mm solid #000000;';
14436				}
14437				$evenhtml .= 'text-align: left; ">' . $Harray . '</div>';
14438			}
14439		} elseif (is_array($Harray) && !empty($Harray)) {
14440			if ($side == 'O') {
14441				$odd = $Harray;
14442			} elseif ($side == 'E') {
14443				$even = $Harray;
14444			} else {
14445				$odd = $Harray['odd'];
14446				$even = $Harray['even'];
14447			}
14448			$oddhtml = $this->_createHTMLheaderFooter($odd, 'H');
14449
14450			$evenhtml = $this->_createHTMLheaderFooter($even, 'H');
14451		}
14452
14453		if ($side == 'E') {
14454			$this->SetHTMLHeader($evenhtml, 'E', $write);
14455		} elseif ($side == 'O') {
14456			$this->SetHTMLHeader($oddhtml, 'O', $write);
14457		} else {
14458			$this->SetHTMLHeader($oddhtml, 'O', $write);
14459			$this->SetHTMLHeader($evenhtml, 'E', $write);
14460		}
14461	}
14462
14463	function SetFooter($Farray = [], $side = '')
14464	{
14465		$oddhtml = '';
14466		$evenhtml = '';
14467		if (is_string($Farray)) {
14468			if (strlen($Farray) == 0) {
14469				$oddhtml = '';
14470				$evenhtml = '';
14471			} elseif (strpos($Farray, '|') !== false) {
14472				$hdet = explode('|', $Farray);
14473				$oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';
14474				if ($this->defaultfooterfontsize) {
14475					$oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
14476				}
14477				if ($this->defaultfooterfontstyle) {
14478					if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
14479						$oddhtml .= ' font-weight: bold;';
14480					}
14481					if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
14482						$oddhtml .= ' font-style: italic;';
14483					}
14484				}
14485				if ($this->defaultfooterline) {
14486					$oddhtml .= ' border-top: 0.1mm solid #000000;';
14487				}
14488				$oddhtml .= '">';
14489				$oddhtml .= '<tr>';
14490				$oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[0] . '</td>';
14491				$oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>';
14492				$oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[2] . '</td>';
14493				$oddhtml .= '</tr></table>';
14494
14495				$evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';
14496				if ($this->defaultfooterfontsize) {
14497					$evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
14498				}
14499				if ($this->defaultfooterfontstyle) {
14500					if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
14501						$evenhtml .= ' font-weight: bold;';
14502					}
14503					if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
14504						$evenhtml .= ' font-style: italic;';
14505					}
14506				}
14507				if ($this->defaultfooterline) {
14508					$evenhtml .= ' border-top: 0.1mm solid #000000;';
14509				}
14510				$evenhtml .= '">';
14511				$evenhtml .= '<tr>';
14512				$evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[2] . '</td>';
14513				$evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>';
14514				$evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[0] . '</td>';
14515				$evenhtml .= '</tr></table>';
14516			} else {
14517				$oddhtml = '<div style="margin: 0; color: #000000; ';
14518				if ($this->defaultfooterfontsize) {
14519					$oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
14520				}
14521				if ($this->defaultfooterfontstyle) {
14522					if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
14523						$oddhtml .= ' font-weight: bold;';
14524					}
14525					if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
14526						$oddhtml .= ' font-style: italic;';
14527					}
14528				}
14529				if ($this->defaultfooterline) {
14530					$oddhtml .= ' border-top: 0.1mm solid #000000;';
14531				}
14532				$oddhtml .= 'text-align: right; ">' . $Farray . '</div>';
14533
14534				$evenhtml = '<div style="margin: 0; color: #000000; ';
14535				if ($this->defaultfooterfontsize) {
14536					$evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
14537				}
14538				if ($this->defaultfooterfontstyle) {
14539					if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
14540						$evenhtml .= ' font-weight: bold;';
14541					}
14542					if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
14543						$evenhtml .= ' font-style: italic;';
14544					}
14545				}
14546				if ($this->defaultfooterline) {
14547					$evenhtml .= ' border-top: 0.1mm solid #000000;';
14548				}
14549				$evenhtml .= 'text-align: left; ">' . $Farray . '</div>';
14550			}
14551		} elseif (is_array($Farray)) {
14552			if ($side == 'O') {
14553				$odd = $Farray;
14554			} elseif ($side == 'E') {
14555				$even = $Farray;
14556			} else {
14557				if (isset($Farray['odd'])) {
14558					$odd = $Farray['odd'];
14559				}
14560				if (isset($Farray['even'])) {
14561					$even = $Farray['even'];
14562				}
14563			}
14564
14565			if (isset($odd)) {
14566				$oddhtml = $this->_createHTMLheaderFooter($odd, 'F');
14567			}
14568
14569			if (isset($even)) {
14570				$evenhtml = $this->_createHTMLheaderFooter($even, 'F');
14571			}
14572		}
14573		/* -- HTMLfooterS-FOOTERS -- */
14574		if ($side == 'E') {
14575			$this->SetHTMLFooter($evenhtml, 'E');
14576		} elseif ($side == 'O') {
14577			$this->SetHTMLFooter($oddhtml, 'O');
14578		} else {
14579			$this->SetHTMLFooter($oddhtml, 'O');
14580			$this->SetHTMLFooter($evenhtml, 'E');
14581		}
14582		/* -- END HTMLfooterS-FOOTERS -- */
14583	}
14584
14585	/* -- WATERMARK -- */
14586
14587	function SetWatermarkText($txt = '', $alpha = -1)
14588	{
14589		if ($alpha >= 0) {
14590			$this->watermarkTextAlpha = $alpha;
14591		}
14592		$this->watermarkText = $txt;
14593	}
14594
14595	function SetWatermarkImage($src, $alpha = -1, $size = 'D', $pos = 'F')
14596	{
14597		if ($alpha >= 0) {
14598			$this->watermarkImageAlpha = $alpha;
14599		}
14600		$this->watermarkImage = $src;
14601		$this->watermark_size = $size;
14602		$this->watermark_pos = $pos;
14603	}
14604
14605	/* -- END WATERMARK -- */
14606
14607	// Page footer
14608	function Footer()
14609	{
14610		/* -- CSS-PAGE -- */
14611		// PAGED MEDIA - CROP / CROSS MARKS from @PAGE
14612		if ($this->show_marks == 'CROP' || $this->show_marks == 'CROPCROSS') {
14613			// Show TICK MARKS
14614			$this->SetLineWidth(0.1); // = 0.1 mm
14615			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
14616			$l = $this->cropMarkLength;
14617			$m = $this->cropMarkMargin; // Distance of crop mark from margin
14618			$b = $this->nonPrintMargin; // Non-printable border at edge of paper sheet
14619			$ax1 = $b;
14620			$bx = $this->page_box['outer_width_LR'] - $m;
14621			$ax = max($ax1, $bx - $l);
14622			$cx1 = $this->w - $b;
14623			$dx = $this->w - $this->page_box['outer_width_LR'] + $m;
14624			$cx = min($cx1, $dx + $l);
14625			$ay1 = $b;
14626			$by = $this->page_box['outer_width_TB'] - $m;
14627			$ay = max($ay1, $by - $l);
14628			$cy1 = $this->h - $b;
14629			$dy = $this->h - $this->page_box['outer_width_TB'] + $m;
14630			$cy = min($cy1, $dy + $l);
14631
14632			$this->Line($ax, $this->page_box['outer_width_TB'], $bx, $this->page_box['outer_width_TB']);
14633			$this->Line($cx, $this->page_box['outer_width_TB'], $dx, $this->page_box['outer_width_TB']);
14634			$this->Line($ax, $this->h - $this->page_box['outer_width_TB'], $bx, $this->h - $this->page_box['outer_width_TB']);
14635			$this->Line($cx, $this->h - $this->page_box['outer_width_TB'], $dx, $this->h - $this->page_box['outer_width_TB']);
14636			$this->Line($this->page_box['outer_width_LR'], $ay, $this->page_box['outer_width_LR'], $by);
14637			$this->Line($this->page_box['outer_width_LR'], $cy, $this->page_box['outer_width_LR'], $dy);
14638			$this->Line($this->w - $this->page_box['outer_width_LR'], $ay, $this->w - $this->page_box['outer_width_LR'], $by);
14639			$this->Line($this->w - $this->page_box['outer_width_LR'], $cy, $this->w - $this->page_box['outer_width_LR'], $dy);
14640
14641			if ($this->printers_info) {
14642				$hd = date('Y-m-d H:i') . '  Page ' . $this->page . ' of {nb}';
14643				$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
14644				$this->SetFont('arial', '', 7.5, true, true);
14645				$this->x = $this->page_box['outer_width_LR'] + 1.5;
14646				$this->y = 1;
14647				$this->Cell($headerpgwidth, $this->FontSize, $hd, 0, 0, 'L', 0, '', 0, 0, 0, 'M');
14648				$this->SetFont($this->default_font, '', $this->original_default_font_size);
14649			}
14650		}
14651		if ($this->show_marks == 'CROSS' || $this->show_marks == 'CROPCROSS') {
14652			$this->SetLineWidth(0.1); // = 0.1 mm
14653			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
14654			$l = 14 / 2; // longer length of the cross line (half)
14655			$w = 6 / 2; // shorter width of the cross line (half)
14656			$r = 1.2; // radius of circle
14657			$m = $this->crossMarkMargin; // Distance of cross mark from margin
14658			$x1 = $this->page_box['outer_width_LR'] - $m;
14659			$x2 = $this->w - $this->page_box['outer_width_LR'] + $m;
14660			$y1 = $this->page_box['outer_width_TB'] - $m;
14661			$y2 = $this->h - $this->page_box['outer_width_TB'] + $m;
14662			// Left
14663			$this->Circle($x1, $this->h / 2, $r, 'S');
14664			$this->Line($x1 - $w, $this->h / 2, $x1 + $w, $this->h / 2);
14665			$this->Line($x1, $this->h / 2 - $l, $x1, $this->h / 2 + $l);
14666			// Right
14667			$this->Circle($x2, $this->h / 2, $r, 'S');
14668			$this->Line($x2 - $w, $this->h / 2, $x2 + $w, $this->h / 2);
14669			$this->Line($x2, $this->h / 2 - $l, $x2, $this->h / 2 + $l);
14670			// Top
14671			$this->Circle($this->w / 2, $y1, $r, 'S');
14672			$this->Line($this->w / 2, $y1 - $w, $this->w / 2, $y1 + $w);
14673			$this->Line($this->w / 2 - $l, $y1, $this->w / 2 + $l, $y1);
14674			// Bottom
14675			$this->Circle($this->w / 2, $y2, $r, 'S');
14676			$this->Line($this->w / 2, $y2 - $w, $this->w / 2, $y2 + $w);
14677			$this->Line($this->w / 2 - $l, $y2, $this->w / 2 + $l, $y2);
14678		}
14679
14680		/* -- END CSS-PAGE -- */
14681
14682		// mPDF 6
14683		// If @page set non-HTML headers/footers named, they were not read until later in the HTML code - so now set them
14684		if ($this->page == 1) {
14685			if ($this->firstPageBoxHeader) {
14686				if (isset($this->pageHTMLheaders[$this->firstPageBoxHeader])) {
14687					$this->HTMLHeader = $this->pageHTMLheaders[$this->firstPageBoxHeader];
14688				}
14689				$this->Header();
14690			}
14691			if ($this->firstPageBoxFooter) {
14692				if (isset($this->pageHTMLfooters[$this->firstPageBoxFooter])) {
14693					$this->HTMLFooter = $this->pageHTMLfooters[$this->firstPageBoxFooter];
14694				}
14695			}
14696			$this->firstPageBoxHeader = '';
14697			$this->firstPageBoxFooter = '';
14698		}
14699
14700
14701		if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLFooterE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLFooter) || (!$this->mirrorMargins && $this->HTMLFooter)) {
14702			$this->writeHTMLFooters();
14703		}
14704
14705		/* -- WATERMARK -- */
14706		if (($this->watermarkText) && ($this->showWatermarkText)) {
14707			$this->watermark($this->watermarkText, $this->watermarkAngle, 120, $this->watermarkTextAlpha); // Watermark text
14708		}
14709		if (($this->watermarkImage) && ($this->showWatermarkImage)) {
14710			$this->watermarkImg($this->watermarkImage, $this->watermarkImageAlpha); // Watermark image
14711		}
14712		/* -- END WATERMARK -- */
14713	}
14714
14715	/* -- HTML-CSS -- */
14716
14717	/**
14718	 * HTML parser
14719	 *
14720	 * @param string $html
14721	 * @param int $sub 0 = default;
14722	 *                 1 = headerCSS only
14723	 *                 2 = HTML body (parts) only;
14724	 *                 3 = HTML parses only
14725	 *                 4 = writes HTML headers/Fixed pos DIVs - stores in buffer - for single page only
14726	 * @param bool $init Clears and sets buffers to Top level block etc.
14727	 * @param bool $close If false leaves buffers etc. in current state, so that it can continue a block etc.
14728	 */
14729	function WriteHTML($html, $sub = 0, $init = true, $close = true)
14730	{
14731		/* Check $html is an integer, float, string, boolean or class with __toString(), otherwise throw exception */
14732		if (is_scalar($html) === false) {
14733			if (!is_object($html) || ! method_exists($html, '__toString')) {
14734				throw new \Mpdf\MpdfException('WriteHTML() requires $html be an integer, float, string, boolean or an object with the __toString() magic method.');
14735			}
14736		}
14737
14738		/* Cast $html as a string */
14739		$html = (string) $html;
14740
14741		// @log Parsing CSS & Headers
14742
14743		if ($init) {
14744			$this->headerbuffer = '';
14745			$this->textbuffer = [];
14746			$this->fixedPosBlockSave = [];
14747		}
14748		if ($sub == 1) {
14749			$html = '<style> ' . $html . ' </style>';
14750		} // stylesheet only
14751
14752		if ($this->allow_charset_conversion) {
14753			if ($sub < 1) {
14754				$this->ReadCharset($html);
14755			}
14756			if ($this->charset_in && $sub != 4) {
14757				$success = iconv($this->charset_in, 'UTF-8//TRANSLIT', $html);
14758				if ($success) {
14759					$html = $success;
14760				}
14761			}
14762		}
14763
14764		$html = $this->purify_utf8($html, false);
14765		if ($init) {
14766			$this->blklvl = 0;
14767			$this->lastblocklevelchange = 0;
14768			$this->blk = [];
14769			$this->initialiseBlock($this->blk[0]);
14770			$this->blk[0]['width'] = & $this->pgwidth;
14771			$this->blk[0]['inner_width'] = & $this->pgwidth;
14772			$this->blk[0]['blockContext'] = $this->blockContext;
14773		}
14774
14775		$zproperties = [];
14776		if ($sub < 2) {
14777			$this->ReadMetaTags($html);
14778
14779			if (preg_match('/<base[^>]*href=["\']([^"\'>]*)["\']/i', $html, $m)) {
14780				$this->SetBasePath($m[1]);
14781			}
14782
14783			$html = $this->cssManager->ReadCSS($html);
14784
14785			if ($this->autoLangToFont && !$this->usingCoreFont && preg_match('/<html [^>]*lang=[\'\"](.*?)[\'\"]/ism', $html, $m)) {
14786				$html_lang = $m[1];
14787			}
14788
14789			if (preg_match('/<html [^>]*dir=[\'\"]\s*rtl\s*[\'\"]/ism', $html)) {
14790				$zproperties['DIRECTION'] = 'rtl';
14791			}
14792
14793			// allow in-line CSS for body tag to be parsed // Get <body> tag inline CSS
14794			if (preg_match('/<body([^>]*)>(.*?)<\/body>/ism', $html, $m) || preg_match('/<body([^>]*)>(.*)$/ism', $html, $m)) {
14795				$html = $m[2];
14796				// Changed to allow style="background: url('bg.jpg')"
14797				if (preg_match('/style=[\"](.*?)[\"]/ism', $m[1], $mm) || preg_match('/style=[\'](.*?)[\']/ism', $m[1], $mm)) {
14798					$zproperties = $this->cssManager->readInlineCSS($mm[1]);
14799				}
14800				if (preg_match('/dir=[\'\"]\s*rtl\s*[\'\"]/ism', $m[1])) {
14801					$zproperties['DIRECTION'] = 'rtl';
14802				}
14803				if (isset($html_lang) && $html_lang) {
14804					$zproperties['LANG'] = $html_lang;
14805				}
14806				if ($this->autoLangToFont && !$this->onlyCoreFonts && preg_match('/lang=[\'\"](.*?)[\'\"]/ism', $m[1], $mm)) {
14807					$zproperties['LANG'] = $mm[1];
14808				}
14809			}
14810		}
14811		$properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
14812		if ($zproperties) {
14813			$properties = $this->cssManager->array_merge_recursive_unique($properties, $zproperties);
14814		}
14815
14816		if (isset($properties['DIRECTION']) && $properties['DIRECTION']) {
14817			$this->cssManager->CSS['BODY']['DIRECTION'] = $properties['DIRECTION'];
14818		}
14819		if (!isset($this->cssManager->CSS['BODY']['DIRECTION'])) {
14820			$this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality;
14821		} else {
14822			$this->SetDirectionality($this->cssManager->CSS['BODY']['DIRECTION']);
14823		}
14824
14825		$this->setCSS($properties, '', 'BODY');
14826
14827		$this->blk[0]['InlineProperties'] = $this->saveInlineProperties();
14828
14829		if ($sub == 1) {
14830			return '';
14831		}
14832		if (!isset($this->cssManager->CSS['BODY'])) {
14833			$this->cssManager->CSS['BODY'] = [];
14834		}
14835
14836		/* -- BACKGROUNDS -- */
14837		if (isset($properties['BACKGROUND-GRADIENT'])) {
14838			$this->bodyBackgroundGradient = $properties['BACKGROUND-GRADIENT'];
14839		}
14840
14841		if (isset($properties['BACKGROUND-IMAGE']) && $properties['BACKGROUND-IMAGE']) {
14842			$ret = $this->SetBackground($properties, $this->pgwidth);
14843			if ($ret) {
14844				$this->bodyBackgroundImage = $ret;
14845			}
14846		}
14847		/* -- END BACKGROUNDS -- */
14848
14849		/* -- CSS-PAGE -- */
14850		// If page-box is set
14851		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
14852			$this->page_box['current'] = '';
14853			$this->page_box['using'] = true;
14854			list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', false, 'O');
14855			$this->DefOrientation = $this->CurOrientation = $pborientation;
14856			$this->orig_lMargin = $this->DeflMargin = $pbmgl;
14857			$this->orig_rMargin = $this->DefrMargin = $pbmgr;
14858			$this->orig_tMargin = $this->tMargin = $pbmgt;
14859			$this->orig_bMargin = $this->bMargin = $pbmgb;
14860			$this->orig_hMargin = $this->margin_header = $pbmgh;
14861			$this->orig_fMargin = $this->margin_footer = $pbmgf;
14862			list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', true, 'O'); // first page
14863			$this->show_marks = $marks;
14864			if ($hname) {
14865				$this->firstPageBoxHeader = $hname;
14866			}
14867			if ($fname) {
14868				$this->firstPageBoxFooter = $fname;
14869			}
14870		}
14871		/* -- END CSS-PAGE -- */
14872
14873		$parseonly = false;
14874		$this->bufferoutput = false;
14875		if ($sub == 3) {
14876			$parseonly = true;
14877			// Close any open block tags
14878			$arr = [];
14879			$ai = 0;
14880			for ($b = $this->blklvl; $b > 0; $b--) {
14881				$this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
14882			}
14883			// Output any text left in buffer
14884			if (count($this->textbuffer)) {
14885				$this->printbuffer($this->textbuffer);
14886			}
14887			$this->textbuffer = [];
14888		} elseif ($sub == 4) {
14889			// Close any open block tags
14890			$arr = [];
14891			$ai = 0;
14892			for ($b = $this->blklvl; $b > 0; $b--) {
14893				$this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
14894			}
14895			// Output any text left in buffer
14896			if (count($this->textbuffer)) {
14897				$this->printbuffer($this->textbuffer);
14898			}
14899			$this->bufferoutput = true;
14900			$this->textbuffer = [];
14901			$this->headerbuffer = '';
14902			$properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
14903			$this->setCSS($properties, '', 'BODY');
14904		}
14905
14906		mb_internal_encoding('UTF-8');
14907
14908		$html = $this->AdjustHTML($html, $this->tabSpaces); // Try to make HTML look more like XHTML
14909
14910		if ($this->autoScriptToLang) {
14911			$html = $this->markScriptToLang($html);
14912		}
14913
14914		preg_match_all('/<htmlpageheader([^>]*)>(.*?)<\/htmlpageheader>/si', $html, $h);
14915		for ($i = 0; $i < count($h[1]); $i++) {
14916			if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $h[1][$i], $n)) {
14917				$this->pageHTMLheaders[$n[1]]['html'] = $h[2][$i];
14918				$this->pageHTMLheaders[$n[1]]['h'] = $this->_getHtmlHeight($h[2][$i]);
14919			}
14920		}
14921		preg_match_all('/<htmlpagefooter([^>]*)>(.*?)<\/htmlpagefooter>/si', $html, $f);
14922		for ($i = 0; $i < count($f[1]); $i++) {
14923			if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $f[1][$i], $n)) {
14924				$this->pageHTMLfooters[$n[1]]['html'] = $f[2][$i];
14925				$this->pageHTMLfooters[$n[1]]['h'] = $this->_getHtmlHeight($f[2][$i]);
14926			}
14927		}
14928
14929		$html = preg_replace('/<htmlpageheader.*?<\/htmlpageheader>/si', '', $html);
14930		$html = preg_replace('/<htmlpagefooter.*?<\/htmlpagefooter>/si', '', $html);
14931
14932		if ($this->state == 0 && $sub != 1 && $sub != 3 && $sub != 4) {
14933			$this->AddPage($this->CurOrientation);
14934		}
14935
14936
14937		if (isset($hname) && preg_match('/^html_(.*)$/i', $hname, $n)) {
14938			$this->SetHTMLHeader($this->pageHTMLheaders[$n[1]], 'O', true);
14939		}
14940		if (isset($fname) && preg_match('/^html_(.*)$/i', $fname, $n)) {
14941			$this->SetHTMLFooter($this->pageHTMLfooters[$n[1]], 'O');
14942		}
14943
14944
14945
14946		$html = str_replace('<?', '< ', $html); // Fix '<?XML' bug from HTML code generated by MS Word
14947
14948		$this->checkSIP = false;
14949		$this->checkSMP = false;
14950		$this->checkCJK = false;
14951		if ($this->onlyCoreFonts) {
14952			$html = $this->SubstituteChars($html);
14953		} else {
14954			if (preg_match("/([" . $this->pregRTLchars . "])/u", $html)) {
14955				$this->biDirectional = true;
14956			} // *OTL*
14957			if (preg_match("/([\x{20000}-\x{2FFFF}])/u", $html)) {
14958				$this->checkSIP = true;
14959			}
14960			if (preg_match("/([\x{10000}-\x{1FFFF}])/u", $html)) {
14961				$this->checkSMP = true;
14962			}
14963			/* -- CJK-FONTS -- */
14964			if (preg_match("/([" . $this->pregCJKchars . "])/u", $html)) {
14965				$this->checkCJK = true;
14966			}
14967			/* -- END CJK-FONTS -- */
14968		}
14969
14970		// Don't allow non-breaking spaces that are converted to substituted chars or will break anyway and mess up table width calc.
14971		$html = str_replace('<tta>160</tta>', chr(32), $html);
14972		$html = str_replace('</tta><tta>', '|', $html);
14973		$html = str_replace('</tts><tts>', '|', $html);
14974		$html = str_replace('</ttz><ttz>', '|', $html);
14975
14976		// Add new supported tags in the DisableTags function
14977		$html = strip_tags($html, $this->enabledtags); // remove all unsupported tags, but the ones inside the 'enabledtags' string
14978		// Explode the string in order to parse the HTML code
14979		$a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
14980		// ? more accurate regexp that allows e.g. <a name="Silly <name>">
14981		// if changing - also change in fn.SubstituteChars()
14982		// $a = preg_split ('/<((?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
14983
14984		if ($this->mb_enc) {
14985			mb_internal_encoding($this->mb_enc);
14986		}
14987		$pbc = 0;
14988		$this->subPos = -1;
14989		$cnt = count($a);
14990		for ($i = 0; $i < $cnt; $i++) {
14991			$e = $a[$i];
14992			if ($i % 2 == 0) {
14993				// TEXT
14994				if ($this->blk[$this->blklvl]['hide']) {
14995					continue;
14996				}
14997				if ($this->inlineDisplayOff) {
14998					continue;
14999				}
15000				if ($this->inMeter) {
15001					continue;
15002				}
15003
15004				if ($this->inFixedPosBlock) {
15005					$this->fixedPosBlock .= $e;
15006					continue;
15007				} // *CSS-POSITION*
15008				if (strlen($e) == 0) {
15009					continue;
15010				}
15011
15012				if ($this->ignorefollowingspaces && !$this->ispre) {
15013					if (strlen(ltrim($e)) == 0) {
15014						continue;
15015					}
15016					if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && substr($e, 0, 1) == ' ') {
15017						$this->ignorefollowingspaces = false;
15018						$e = ltrim($e);
15019					}
15020				}
15021
15022				$this->OTLdata = null;  // mPDF 5.7.1
15023
15024				$e = UtfString::strcode2utf($e);
15025				$e = $this->lesser_entity_decode($e);
15026
15027				if ($this->usingCoreFont) {
15028					// If core font is selected in document which is not onlyCoreFonts - substitute with non-core font
15029					if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->subPos < $i && !$this->specialcontent) {
15030						$cnt += $this->SubstituteCharsNonCore($a, $i, $e);
15031					}
15032					// CONVERT ENCODING
15033					$e = mb_convert_encoding($e, $this->mb_enc, 'UTF-8');
15034					if ($this->textvar & TextVars::FT_UPPERCASE) {
15035						$e = mb_strtoupper($e, $this->mb_enc);
15036					} // mPDF 5.7.1
15037					elseif ($this->textvar & TextVars::FT_LOWERCASE) {
15038						$e = mb_strtolower($e, $this->mb_enc);
15039					} // mPDF 5.7.1
15040					elseif ($this->textvar & TextVars::FT_CAPITALIZE) {
15041						$e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8");
15042					} // mPDF 5.7.1
15043				} else {
15044					if ($this->checkSIP && $this->CurrentFont['sipext'] && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {
15045						$cnt += $this->SubstituteCharsSIP($a, $i, $e);
15046					}
15047
15048					if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->CurrentFont['type'] != 'Type0' && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {
15049						$cnt += $this->SubstituteCharsMB($a, $i, $e);
15050					}
15051
15052					if ($this->textvar & TextVars::FT_UPPERCASE) {
15053						$e = mb_strtoupper($e, $this->mb_enc);
15054					} elseif ($this->textvar & TextVars::FT_LOWERCASE) {
15055						$e = mb_strtolower($e, $this->mb_enc);
15056					} elseif ($this->textvar & TextVars::FT_CAPITALIZE) {
15057						$e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8");
15058					}
15059
15060					/* -- OTL -- */
15061					// Use OTL OpenType Table Layout - GSUB & GPOS
15062					if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL'] && (!$this->specialcontent || !$this->useActiveForms)) {
15063						if (!$this->otl) {
15064							$this->otl = new Otl($this, $this->fontCache);
15065						}
15066						$e = $this->otl->applyOTL($e, $this->CurrentFont['useOTL']);
15067						$this->OTLdata = $this->otl->OTLdata;
15068						$this->otl->removeChar($e, $this->OTLdata, "\xef\xbb\xbf"); // Remove ZWNBSP (also Byte order mark FEFF)
15069					} /* -- END OTL -- */
15070					else {
15071						// removes U+200E/U+200F LTR and RTL mark and U+200C/U+200D Zero-width Joiner and Non-joiner
15072						$e = preg_replace("/[\xe2\x80\x8c\xe2\x80\x8d\xe2\x80\x8e\xe2\x80\x8f]/u", '', $e);
15073						$e = preg_replace("/[\xef\xbb\xbf]/u", '', $e); // Remove ZWNBSP (also Byte order mark FEFF)
15074					}
15075				}
15076
15077				if (($this->tts) || ($this->ttz) || ($this->tta)) {
15078					$es = explode('|', $e);
15079					$e = '';
15080					foreach ($es as $val) {
15081						$e .= chr($val);
15082					}
15083				}
15084
15085				//  FORM ELEMENTS
15086				if ($this->specialcontent) {
15087					/* -- FORMS -- */
15088					// SELECT tag (form element)
15089					if ($this->specialcontent == "type=select") {
15090						$e = ltrim($e);
15091						if (!empty($this->OTLdata)) {
15092							$this->otl->trimOTLdata($this->OTLdata, true, false);
15093						} // *OTL*
15094						$stringwidth = $this->GetStringWidth($e);
15095						if (!isset($this->selectoption['MAXWIDTH']) || $stringwidth > $this->selectoption['MAXWIDTH']) {
15096							$this->selectoption['MAXWIDTH'] = $stringwidth;
15097						}
15098						if (!isset($this->selectoption['SELECTED']) || $this->selectoption['SELECTED'] == '') {
15099							$this->selectoption['SELECTED'] = $e;
15100							if (!empty($this->OTLdata)) {
15101								$this->selectoption['SELECTED-OTLDATA'] = $this->OTLdata;
15102							} // *OTL*
15103						}
15104						// Active Forms
15105						if (isset($this->selectoption['ACTIVE']) && $this->selectoption['ACTIVE']) {
15106							$this->selectoption['ITEMS'][] = ['exportValue' => $this->selectoption['currentVAL'], 'content' => $e, 'selected' => $this->selectoption['currentSEL']];
15107						}
15108						$this->OTLdata = [];
15109					} // TEXTAREA
15110					else {
15111						$objattr = unserialize($this->specialcontent);
15112						$objattr['text'] = $e;
15113						$objattr['OTLdata'] = $this->OTLdata;
15114						$this->OTLdata = [];
15115						$te = "\xbb\xa4\xactype=textarea,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
15116						if ($this->tdbegin) {
15117							$this->_saveCellTextBuffer($te, $this->HREF);
15118						} else {
15119							$this->_saveTextBuffer($te, $this->HREF);
15120						}
15121					}
15122					/* -- END FORMS -- */
15123				} // TABLE
15124				elseif ($this->tableLevel) {
15125					/* -- TABLES -- */
15126					if ($this->tdbegin) {
15127						if (($this->ignorefollowingspaces) && !$this->ispre) {
15128							$e = ltrim($e);
15129							if (!empty($this->OTLdata)) {
15130								$this->otl->trimOTLdata($this->OTLdata, true, false);
15131							} // *OTL*
15132						}
15133						if ($e || $e === '0') {
15134							if ($this->blockjustfinished && $this->cell[$this->row][$this->col]['s'] > 0) {
15135								$this->_saveCellTextBuffer("\n");
15136								if (!isset($this->cell[$this->row][$this->col]['maxs'])) {
15137									$this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];
15138								} elseif ($this->cell[$this->row][$this->col]['maxs'] < $this->cell[$this->row][$this->col]['s']) {
15139									$this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];
15140								}
15141								$this->cell[$this->row][$this->col]['s'] = 0; // reset
15142							}
15143							$this->blockjustfinished = false;
15144
15145							if (!isset($this->cell[$this->row][$this->col]['R']) || !$this->cell[$this->row][$this->col]['R']) {
15146								if (isset($this->cell[$this->row][$this->col]['s'])) {
15147									$this->cell[$this->row][$this->col]['s'] += $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);
15148								} else {
15149									$this->cell[$this->row][$this->col]['s'] = $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);
15150								}
15151								if (!empty($this->spanborddet)) {
15152									$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);
15153								}
15154							}
15155							$this->_saveCellTextBuffer($e, $this->HREF);
15156							if (substr($this->cell[$this->row][$this->col]['a'], 0, 1) == 'D') {
15157								$dp = $this->decimal_align[substr($this->cell[$this->row][$this->col]['a'], 0, 2)];
15158								$s = preg_split('/' . preg_quote($dp, '/') . '/', $e, 2);  // ? needs to be /u if not core
15159								$s0 = $this->GetStringWidth($s[0], false);
15160								if (isset($s[1]) && $s[1]) {
15161									$s1 = $this->GetStringWidth(($s[1] . $dp), false);
15162								} else {
15163									$s1 = 0;
15164								}
15165								if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'])) {
15166									$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = $s0;
15167								} else {
15168									$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']);
15169								}
15170								if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'])) {
15171									$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = $s1;
15172								} else {
15173									$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']);
15174								}
15175							}
15176
15177							$this->nestedtablejustfinished = false;
15178							$this->linebreakjustfinished = false;
15179						}
15180					}
15181					/* -- END TABLES -- */
15182				} // ALL ELSE
15183				else {
15184					if ($this->ignorefollowingspaces && !$this->ispre) {
15185						$e = ltrim($e);
15186						if (!empty($this->OTLdata)) {
15187							$this->otl->trimOTLdata($this->OTLdata, true, false);
15188						} // *OTL*
15189					}
15190					if ($e || $e === '0') {
15191						$this->_saveTextBuffer($e, $this->HREF);
15192					}
15193				}
15194				if ($e || $e === '0') {
15195					$this->ignorefollowingspaces = false; // mPDF 6
15196				}
15197				if (substr($e, -1, 1) == ' ' && !$this->ispre && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
15198					$this->ignorefollowingspaces = true;
15199				}
15200			} else { // TAG **
15201				if (isset($e[0]) && $e[0] == '/') {
15202					$endtag = trim(strtoupper(substr($e, 1)));
15203
15204					/* -- CSS-POSITION -- */
15205					// mPDF 6
15206					if ($this->inFixedPosBlock) {
15207						if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {
15208							$this->fixedPosBlockDepth--;
15209						}
15210						if ($this->fixedPosBlockDepth == 0) {
15211							$this->fixedPosBlockSave[] = [$this->fixedPosBlock, $this->fixedPosBlockBBox, $this->page];
15212							$this->fixedPosBlock = '';
15213							$this->inFixedPosBlock = false;
15214							continue;
15215						}
15216						$this->fixedPosBlock .= '<' . $e . '>';
15217						continue;
15218					}
15219					/* -- END CSS-POSITION -- */
15220
15221					// mPDF 6
15222					// Correct for tags where HTML5 specifies optional end tags (see also OpenTag() )
15223					if ($this->allow_html_optional_endtags && !$parseonly) {
15224						if (isset($this->blk[$this->blklvl]['tag'])) {
15225							$closed = false;
15226							// li end tag may be omitted if there is no more content in the parent element
15227							if (!$closed && $this->blk[$this->blklvl]['tag'] == 'LI' && $endtag != 'LI' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
15228								$this->tag->CloseTag('LI', $a, $i);
15229								$closed = true;
15230							}
15231							// dd end tag may be omitted if there is no more content in the parent element
15232							if (!$closed && $this->blk[$this->blklvl]['tag'] == 'DD' && $endtag != 'DD' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
15233								$this->tag->CloseTag('DD', $a, $i);
15234								$closed = true;
15235							}
15236							// 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 [??????]
15237							if (!$closed && $this->blk[$this->blklvl]['tag'] == 'P' && $endtag != 'P' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
15238								$this->tag->CloseTag('P', $a, $i);
15239								$closed = true;
15240							}
15241							// option end tag may be omitted if there is no more content in the parent element
15242							if (!$closed && $this->blk[$this->blklvl]['tag'] == 'OPTION' && $endtag != 'OPTION' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
15243								$this->tag->CloseTag('OPTION', $a, $i);
15244								$closed = true;
15245							}
15246						}
15247						/* -- TABLES -- */
15248						// Check for Table tags where HTML specifies optional end tags,
15249						if ($endtag == 'TABLE') {
15250							if ($this->lastoptionaltag == 'THEAD' || $this->lastoptionaltag == 'TBODY' || $this->lastoptionaltag == 'TFOOT') {
15251								$this->tag->CloseTag($this->lastoptionaltag, $a, $i);
15252							}
15253							if ($this->lastoptionaltag == 'TR') {
15254								$this->tag->CloseTag('TR', $a, $i);
15255							}
15256							if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
15257								$this->tag->CloseTag($this->lastoptionaltag, $a, $i);
15258								$this->tag->CloseTag('TR', $a, $i);
15259							}
15260						}
15261						if ($endtag == 'THEAD' || $endtag == 'TBODY' || $endtag == 'TFOOT') {
15262							if ($this->lastoptionaltag == 'TR') {
15263								$this->tag->CloseTag('TR', $a, $i);
15264							}
15265							if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
15266								$this->tag->CloseTag($this->lastoptionaltag, $a, $i);
15267								$this->tag->CloseTag('TR', $a, $i);
15268							}
15269						}
15270						if ($endtag == 'TR') {
15271							if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
15272								$this->tag->CloseTag($this->lastoptionaltag, $a, $i);
15273							}
15274						}
15275						/* -- END TABLES -- */
15276					}
15277
15278
15279					// mPDF 6
15280					if ($this->blk[$this->blklvl]['hide']) {
15281						if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {
15282							unset($this->blk[$this->blklvl]);
15283							$this->blklvl--;
15284						}
15285						continue;
15286					}
15287
15288					// mPDF 6
15289					$this->tag->CloseTag($endtag, $a, $i); // mPDF 6
15290				} else { // OPENING TAG
15291					if ($this->blk[$this->blklvl]['hide']) {
15292						if (strpos($e, ' ')) {
15293							$te = strtoupper(substr($e, 0, strpos($e, ' ')));
15294						} else {
15295							$te = strtoupper($e);
15296						}
15297						// mPDF 6
15298						if ($te == 'THEAD' || $te == 'TBODY' || $te == 'TFOOT' || $te == 'TR' || $te == 'TD' || $te == 'TH') {
15299							$this->lastoptionaltag = $te;
15300						}
15301						if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {
15302							$this->blklvl++;
15303							$this->blk[$this->blklvl]['hide'] = true;
15304							$this->blk[$this->blklvl]['tag'] = $te; // mPDF 6
15305						}
15306						continue;
15307					}
15308
15309					/* -- CSS-POSITION -- */
15310					if ($this->inFixedPosBlock) {
15311						if (strpos($e, ' ')) {
15312							$te = strtoupper(substr($e, 0, strpos($e, ' ')));
15313						} else {
15314							$te = strtoupper($e);
15315						}
15316						$this->fixedPosBlock .= '<' . $e . '>';
15317						if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {
15318							$this->fixedPosBlockDepth++;
15319						}
15320						continue;
15321					}
15322					/* -- END CSS-POSITION -- */
15323					$regexp = '|=\'(.*?)\'|s'; // eliminate single quotes, if any
15324					$e = preg_replace($regexp, "=\"\$1\"", $e);
15325					// changes anykey=anyvalue to anykey="anyvalue" (only do this inside [some] tags)
15326					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)
15327						$regexp = '| (\\w+?)=([^\\s>"]+)|si';
15328						$e = preg_replace($regexp, " \$1=\"\$2\"", $e);
15329					}
15330
15331					$e = preg_replace('/ (\\S+?)\s*=\s*"/i', " \\1=\"", $e);
15332
15333					// Fix path values, if needed
15334					$orig_srcpath = '';
15335					if ((stristr($e, "href=") !== false) or ( stristr($e, "src=") !== false)) {
15336						$regexp = '/ (href|src)\s*=\s*"(.*?)"/i';
15337						preg_match($regexp, $e, $auxiliararray);
15338						if (isset($auxiliararray[2])) {
15339							$path = $auxiliararray[2];
15340						} else {
15341							$path = '';
15342						}
15343						if (trim($path) != '' && !(stristr($e, "src=") !== false && substr($path, 0, 4) == 'var:') && substr($path, 0, 1) != '@') {
15344							$path = htmlspecialchars_decode($path); // mPDF 5.7.4 URLs
15345							$orig_srcpath = $path;
15346							$this->GetFullPath($path);
15347							$regexp = '/ (href|src)="(.*?)"/i';
15348							$e = preg_replace($regexp, ' \\1="' . $path . '"', $e);
15349						}
15350					}//END of Fix path values
15351					// Extract attributes
15352					$contents = [];
15353					$contents1 = [];
15354					$contents2 = [];
15355					// Changed to allow style="background: url('bg.jpg')"
15356					// Changed to improve performance; maximum length of \S (attribute) = 16
15357					// Increase allowed attribute name to 32 - cutting off "toc-even-header-name" etc.
15358					preg_match_all('/\\S{1,32}=["][^"]*["]/', $e, $contents1);
15359					preg_match_all('/\\S{1,32}=[\'][^\']*[\']/i', $e, $contents2);
15360
15361					$contents = array_merge($contents1, $contents2);
15362					preg_match('/\\S+/', $e, $a2);
15363					$tag = (isset($a2[0]) ? strtoupper($a2[0]) : '');
15364					$attr = [];
15365					if ($orig_srcpath) {
15366						$attr['ORIG_SRC'] = $orig_srcpath;
15367					}
15368					if (!empty($contents)) {
15369						foreach ($contents[0] as $v) {
15370							// Changed to allow style="background: url('bg.jpg')"
15371							if (preg_match('/^([^=]*)=["]?([^"]*)["]?$/', $v, $a3) || preg_match('/^([^=]*)=[\']?([^\']*)[\']?$/', $v, $a3)) {
15372								if (strtoupper($a3[1]) == 'ID' || strtoupper($a3[1]) == 'CLASS') { // 4.2.013 Omits STYLE
15373									$attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));
15374								} // includes header-style-right etc. used for <pageheader>
15375								elseif (preg_match('/^(HEADER|FOOTER)-STYLE/i', $a3[1])) {
15376									$attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));
15377								} else {
15378									$attr[strtoupper($a3[1])] = trim($a3[2]);
15379								}
15380							}
15381						}
15382					}
15383					$this->tag->OpenTag($tag, $attr, $a, $i); // mPDF 6
15384					/* -- CSS-POSITION -- */
15385					if ($this->inFixedPosBlock) {
15386						$this->fixedPosBlockBBox = [$tag, $attr, $this->x, $this->y];
15387						$this->fixedPosBlock = '';
15388						$this->fixedPosBlockDepth = 1;
15389					}
15390					/* -- END CSS-POSITION -- */
15391					if (preg_match('/\/$/', $e)) {
15392						$this->tag->CloseTag($tag, $a, $i);
15393					}
15394				}
15395			} // end TAG
15396		} // end of	foreach($a as $i=>$e)
15397
15398		if ($close) {
15399			// Close any open block tags
15400			for ($b = $this->blklvl; $b > 0; $b--) {
15401				$this->tag->CloseTag($this->blk[$b]['tag'], $a, $i);
15402			}
15403
15404			// Output any text left in buffer
15405			if (count($this->textbuffer) && !$parseonly) {
15406				$this->printbuffer($this->textbuffer);
15407			}
15408			if (!$parseonly) {
15409				$this->textbuffer = [];
15410			}
15411
15412			/* -- CSS-FLOAT -- */
15413			// If ended with a float, need to move to end page
15414			$currpos = $this->page * 1000 + $this->y;
15415			if (isset($this->blk[$this->blklvl]['float_endpos']) && $this->blk[$this->blklvl]['float_endpos'] > $currpos) {
15416				$old_page = $this->page;
15417				$new_page = intval($this->blk[$this->blklvl]['float_endpos'] / 1000);
15418				if ($old_page != $new_page) {
15419					$s = $this->PrintPageBackgrounds();
15420					// Writes after the marker so not overwritten later by page background etc.
15421					$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
15422					$this->pageBackgrounds = [];
15423					$this->page = $new_page;
15424					$this->ResetMargins();
15425					$this->Reset();
15426					$this->pageoutput[$this->page] = [];
15427				}
15428				$this->y = (($this->blk[$this->blklvl]['float_endpos'] * 1000) % 1000000) / 1000; // mod changes operands to integers before processing
15429			}
15430			/* -- END CSS-FLOAT -- */
15431
15432			/* -- CSS-IMAGE-FLOAT -- */
15433			$this->printfloatbuffer();
15434			/* -- END CSS-IMAGE-FLOAT -- */
15435
15436			// Create Internal Links, if needed
15437			if (!empty($this->internallink)) {
15438				foreach ($this->internallink as $k => $v) {
15439					if (strpos($k, "#") !== false) {
15440						continue;
15441					} // ignore
15442					$ypos = $v['Y'];
15443					$pagenum = $v['PAGE'];
15444					$sharp = "#";
15445					while (array_key_exists($sharp . $k, $this->internallink)) {
15446						$internallink = $this->internallink[$sharp . $k];
15447						$this->SetLink($internallink, $ypos, $pagenum);
15448						$sharp .= "#";
15449					}
15450				}
15451			}
15452
15453			$this->bufferoutput = false;
15454
15455			/* -- CSS-POSITION -- */
15456			if (count($this->fixedPosBlockSave)) {
15457				foreach ($this->fixedPosBlockSave as $fpbs) {
15458					$old_page = $this->page;
15459					$this->page = $fpbs[2];
15460					$this->WriteFixedPosHTML($fpbs[0], 0, 0, 100, 100, 'auto', $fpbs[1]);  // 0,0,10,10 are overwritten by bbox
15461					$this->page = $old_page;
15462				}
15463				$this->fixedPosBlockSave = [];
15464			}
15465			/* -- END CSS-POSITION -- */
15466		}
15467	}
15468
15469	/* -- CSS-POSITION -- */
15470
15471	function WriteFixedPosHTML($html, $x, $y, $w, $h, $overflow = 'visible', $bounding = [])
15472	{
15473		// $overflow can be 'hidden', 'visible' or 'auto' - 'auto' causes autofit to size
15474		// Annotations disabled - enabled in mPDF 5.0
15475		// Links do work
15476		// Will always go on current page (or start Page 1 if required)
15477		// Probably INCOMPATIBLE WITH keep with table, columns etc.
15478		// Called externally or interally via <div style="position: [fixed|absolute]">
15479		// When used internally, $x $y $w $h and $overflow are all overridden by $bounding
15480
15481		$overflow = strtolower($overflow);
15482		if ($this->state == 0) {
15483			$this->AddPage($this->CurOrientation);
15484		}
15485		$save_y = $this->y;
15486		$save_x = $this->x;
15487		$this->fullImageHeight = $this->h;
15488		$save_cols = false;
15489		/* -- COLUMNS -- */
15490		if ($this->ColActive) {
15491			$save_cols = true;
15492			$save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off
15493			$this->SetColumns(0);
15494		}
15495		/* -- END COLUMNS -- */
15496		$save_annots = $this->title2annots; // *ANNOTATIONS*
15497		$this->writingHTMLheader = true; // a FIX to stop pagebreaks etc.
15498		$this->writingHTMLfooter = true;
15499		$this->InFooter = true; // suppresses autopagebreaks
15500		$save_bgs = $this->pageBackgrounds;
15501		$checkinnerhtml = preg_replace('/\s/', '', $html);
15502		$rotate = 0;
15503
15504		if ($w > $this->w) {
15505			$x = 0;
15506			$w = $this->w;
15507		}
15508		if ($h > $this->h) {
15509			$y = 0;
15510			$h = $this->h;
15511		}
15512		if ($x > $this->w) {
15513			$x = $this->w - $w;
15514		}
15515		if ($y > $this->h) {
15516			$y = $this->h - $h;
15517		}
15518
15519		if (!empty($bounding)) {
15520			// $cont_ containing block = full physical page (position: absolute) or page inside margins (position: fixed)
15521			// $bbox_ Bounding box is the <div> which is positioned absolutely/fixed
15522			// top/left/right/bottom/width/height/background*/border*/padding*/margin* are taken from bounding
15523			// font*[family/size/style/weight]/line-height/text*[align/decoration/transform/indent]/color are transferred to $inner
15524			// as an enclosing <div> (after having checked ID/CLASS)
15525			// $x, $y, $w, $h are inside of $bbox_ = containing box for $inner_
15526			// $inner_ InnerHTML is the contents of that block to be output
15527			$tag = $bounding[0];
15528			$attr = $bounding[1];
15529			$orig_x0 = $bounding[2];
15530			$orig_y0 = $bounding[3];
15531
15532			// As in WriteHTML() initialising
15533			$this->blklvl = 0;
15534			$this->lastblocklevelchange = 0;
15535			$this->blk = [];
15536			$this->initialiseBlock($this->blk[0]);
15537
15538			$this->blk[0]['width'] = & $this->pgwidth;
15539			$this->blk[0]['inner_width'] = & $this->pgwidth;
15540
15541			$this->blk[0]['blockContext'] = $this->blockContext;
15542
15543			$properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');
15544			$this->setCSS($properties, '', 'BODY');
15545			$this->blklvl = 1;
15546			$this->initialiseBlock($this->blk[1]);
15547			$this->blk[1]['tag'] = $tag;
15548			$this->blk[1]['attr'] = $attr;
15549			$this->Reset();
15550			$p = $this->cssManager->MergeCSS('BLOCK', $tag, $attr);
15551			if (isset($p['ROTATE']) && ($p['ROTATE'] == 90 || $p['ROTATE'] == -90 || $p['ROTATE'] == 180)) {
15552				$rotate = $p['ROTATE'];
15553			} // mPDF 6
15554			if (isset($p['OVERFLOW'])) {
15555				$overflow = strtolower($p['OVERFLOW']);
15556			}
15557			if (strtolower($p['POSITION']) == 'fixed') {
15558				$cont_w = $this->pgwidth; // $this->blk[0]['inner_width'];
15559				$cont_h = $this->h - $this->tMargin - $this->bMargin;
15560				$cont_x = $this->lMargin;
15561				$cont_y = $this->tMargin;
15562			} else {
15563				$cont_w = $this->w; // ABSOLUTE;
15564				$cont_h = $this->h;
15565				$cont_x = 0;
15566				$cont_y = 0;
15567			}
15568
15569			// Pass on in-line properties to the innerhtml
15570			$css = '';
15571			if (isset($p['TEXT-ALIGN'])) {
15572				$css .= 'text-align: ' . strtolower($p['TEXT-ALIGN']) . '; ';
15573			}
15574			if (isset($p['TEXT-TRANSFORM'])) {
15575				$css .= 'text-transform: ' . strtolower($p['TEXT-TRANSFORM']) . '; ';
15576			}
15577			if (isset($p['TEXT-INDENT'])) {
15578				$css .= 'text-indent: ' . strtolower($p['TEXT-INDENT']) . '; ';
15579			}
15580			if (isset($p['TEXT-DECORATION'])) {
15581				$css .= 'text-decoration: ' . strtolower($p['TEXT-DECORATION']) . '; ';
15582			}
15583			if (isset($p['FONT-FAMILY'])) {
15584				$css .= 'font-family: ' . strtolower($p['FONT-FAMILY']) . '; ';
15585			}
15586			if (isset($p['FONT-STYLE'])) {
15587				$css .= 'font-style: ' . strtolower($p['FONT-STYLE']) . '; ';
15588			}
15589			if (isset($p['FONT-WEIGHT'])) {
15590				$css .= 'font-weight: ' . strtolower($p['FONT-WEIGHT']) . '; ';
15591			}
15592			if (isset($p['FONT-SIZE'])) {
15593				$css .= 'font-size: ' . strtolower($p['FONT-SIZE']) . '; ';
15594			}
15595			if (isset($p['LINE-HEIGHT'])) {
15596				$css .= 'line-height: ' . strtolower($p['LINE-HEIGHT']) . '; ';
15597			}
15598			if (isset($p['TEXT-SHADOW'])) {
15599				$css .= 'text-shadow: ' . strtolower($p['TEXT-SHADOW']) . '; ';
15600			}
15601			if (isset($p['LETTER-SPACING'])) {
15602				$css .= 'letter-spacing: ' . strtolower($p['LETTER-SPACING']) . '; ';
15603			}
15604			// mPDF 6
15605			if (isset($p['FONT-VARIANT-POSITION'])) {
15606				$css .= 'font-variant-position: ' . strtolower($p['FONT-VARIANT-POSITION']) . '; ';
15607			}
15608			if (isset($p['FONT-VARIANT-CAPS'])) {
15609				$css .= 'font-variant-caps: ' . strtolower($p['FONT-VARIANT-CAPS']) . '; ';
15610			}
15611			if (isset($p['FONT-VARIANT-LIGATURES'])) {
15612				$css .= 'font-variant-ligatures: ' . strtolower($p['FONT-VARIANT-LIGATURES']) . '; ';
15613			}
15614			if (isset($p['FONT-VARIANT-NUMERIC'])) {
15615				$css .= 'font-variant-numeric: ' . strtolower($p['FONT-VARIANT-NUMERIC']) . '; ';
15616			}
15617			if (isset($p['FONT-VARIANT-ALTERNATES'])) {
15618				$css .= 'font-variant-alternates: ' . strtolower($p['FONT-VARIANT-ALTERNATES']) . '; ';
15619			}
15620			if (isset($p['FONT-FEATURE-SETTINGS'])) {
15621				$css .= 'font-feature-settings: ' . strtolower($p['FONT-FEATURE-SETTINGS']) . '; ';
15622			}
15623			if (isset($p['FONT-LANGUAGE-OVERRIDE'])) {
15624				$css .= 'font-language-override: ' . strtolower($p['FONT-LANGUAGE-OVERRIDE']) . '; ';
15625			}
15626			if (isset($p['FONT-KERNING'])) {
15627				$css .= 'font-kerning: ' . strtolower($p['FONT-KERNING']) . '; ';
15628			}
15629
15630			if (isset($p['COLOR'])) {
15631				$css .= 'color: ' . strtolower($p['COLOR']) . '; ';
15632			}
15633			if (isset($p['Z-INDEX'])) {
15634				$css .= 'z-index: ' . $p['Z-INDEX'] . '; ';
15635			}
15636			if ($css) {
15637				$html = '<div style="' . $css . '">' . $html . '</div>';
15638			}
15639			// Copy over (only) the properties to set for border and background
15640			$pb = [];
15641			$pb['MARGIN-TOP'] = (isset($p['MARGIN-TOP']) ? $p['MARGIN-TOP'] : '');
15642			$pb['MARGIN-RIGHT'] = (isset($p['MARGIN-RIGHT']) ? $p['MARGIN-RIGHT'] : '');
15643			$pb['MARGIN-BOTTOM'] = (isset($p['MARGIN-BOTTOM']) ? $p['MARGIN-BOTTOM'] : '');
15644			$pb['MARGIN-LEFT'] = (isset($p['MARGIN-LEFT']) ? $p['MARGIN-LEFT'] : '');
15645			$pb['PADDING-TOP'] = (isset($p['PADDING-TOP']) ? $p['PADDING-TOP'] : '');
15646			$pb['PADDING-RIGHT'] = (isset($p['PADDING-RIGHT']) ? $p['PADDING-RIGHT'] : '');
15647			$pb['PADDING-BOTTOM'] = (isset($p['PADDING-BOTTOM']) ? $p['PADDING-BOTTOM'] : '');
15648			$pb['PADDING-LEFT'] = (isset($p['PADDING-LEFT']) ? $p['PADDING-LEFT'] : '');
15649			$pb['BORDER-TOP'] = (isset($p['BORDER-TOP']) ? $p['BORDER-TOP'] : '');
15650			$pb['BORDER-RIGHT'] = (isset($p['BORDER-RIGHT']) ? $p['BORDER-RIGHT'] : '');
15651			$pb['BORDER-BOTTOM'] = (isset($p['BORDER-BOTTOM']) ? $p['BORDER-BOTTOM'] : '');
15652			$pb['BORDER-LEFT'] = (isset($p['BORDER-LEFT']) ? $p['BORDER-LEFT'] : '');
15653			if (isset($p['BORDER-TOP-LEFT-RADIUS-H'])) {
15654				$pb['BORDER-TOP-LEFT-RADIUS-H'] = $p['BORDER-TOP-LEFT-RADIUS-H'];
15655			}
15656			if (isset($p['BORDER-TOP-LEFT-RADIUS-V'])) {
15657				$pb['BORDER-TOP-LEFT-RADIUS-V'] = $p['BORDER-TOP-LEFT-RADIUS-V'];
15658			}
15659			if (isset($p['BORDER-TOP-RIGHT-RADIUS-H'])) {
15660				$pb['BORDER-TOP-RIGHT-RADIUS-H'] = $p['BORDER-TOP-RIGHT-RADIUS-H'];
15661			}
15662			if (isset($p['BORDER-TOP-RIGHT-RADIUS-V'])) {
15663				$pb['BORDER-TOP-RIGHT-RADIUS-V'] = $p['BORDER-TOP-RIGHT-RADIUS-V'];
15664			}
15665			if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-H'])) {
15666				$pb['BORDER-BOTTOM-LEFT-RADIUS-H'] = $p['BORDER-BOTTOM-LEFT-RADIUS-H'];
15667			}
15668			if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-V'])) {
15669				$pb['BORDER-BOTTOM-LEFT-RADIUS-V'] = $p['BORDER-BOTTOM-LEFT-RADIUS-V'];
15670			}
15671			if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-H'])) {
15672				$pb['BORDER-BOTTOM-RIGHT-RADIUS-H'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-H'];
15673			}
15674			if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-V'])) {
15675				$pb['BORDER-BOTTOM-RIGHT-RADIUS-V'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-V'];
15676			}
15677			if (isset($p['BACKGROUND-COLOR'])) {
15678				$pb['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];
15679			}
15680			if (isset($p['BOX-SHADOW'])) {
15681				$pb['BOX-SHADOW'] = $p['BOX-SHADOW'];
15682			}
15683			/* -- BACKGROUNDS -- */
15684			if (isset($p['BACKGROUND-IMAGE'])) {
15685				$pb['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];
15686			}
15687			if (isset($p['BACKGROUND-IMAGE-RESIZE'])) {
15688				$pb['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];
15689			}
15690			if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {
15691				$pb['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];
15692			}
15693			if (isset($p['BACKGROUND-REPEAT'])) {
15694				$pb['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];
15695			}
15696			if (isset($p['BACKGROUND-POSITION'])) {
15697				$pb['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];
15698			}
15699			if (isset($p['BACKGROUND-GRADIENT'])) {
15700				$pb['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];
15701			}
15702			if (isset($p['BACKGROUND-SIZE'])) {
15703				$pb['BACKGROUND-SIZE'] = $p['BACKGROUND-SIZE'];
15704			}
15705			if (isset($p['BACKGROUND-ORIGIN'])) {
15706				$pb['BACKGROUND-ORIGIN'] = $p['BACKGROUND-ORIGIN'];
15707			}
15708			if (isset($p['BACKGROUND-CLIP'])) {
15709				$pb['BACKGROUND-CLIP'] = $p['BACKGROUND-CLIP'];
15710			}
15711
15712			/* -- END BACKGROUNDS -- */
15713
15714			$this->setCSS($pb, 'BLOCK', $tag);
15715
15716			// ================================================================
15717			$bbox_br = $this->blk[1]['border_right']['w'];
15718			$bbox_bl = $this->blk[1]['border_left']['w'];
15719			$bbox_bt = $this->blk[1]['border_top']['w'];
15720			$bbox_bb = $this->blk[1]['border_bottom']['w'];
15721			$bbox_pr = $this->blk[1]['padding_right'];
15722			$bbox_pl = $this->blk[1]['padding_left'];
15723			$bbox_pt = $this->blk[1]['padding_top'];
15724			$bbox_pb = $this->blk[1]['padding_bottom'];
15725			$bbox_mr = $this->blk[1]['margin_right'];
15726			if (isset($p['MARGIN-RIGHT']) && strtolower($p['MARGIN-RIGHT']) == 'auto') {
15727				$bbox_mr = 'auto';
15728			}
15729			$bbox_ml = $this->blk[1]['margin_left'];
15730			if (isset($p['MARGIN-LEFT']) && strtolower($p['MARGIN-LEFT']) == 'auto') {
15731				$bbox_ml = 'auto';
15732			}
15733			$bbox_mt = $this->blk[1]['margin_top'];
15734			if (isset($p['MARGIN-TOP']) && strtolower($p['MARGIN-TOP']) == 'auto') {
15735				$bbox_mt = 'auto';
15736			}
15737			$bbox_mb = $this->blk[1]['margin_bottom'];
15738			if (isset($p['MARGIN-BOTTOM']) && strtolower($p['MARGIN-BOTTOM']) == 'auto') {
15739				$bbox_mb = 'auto';
15740			}
15741			if (isset($p['LEFT']) && strtolower($p['LEFT']) != 'auto') {
15742				$bbox_left = $this->sizeConverter->convert($p['LEFT'], $cont_w, $this->FontSize, false);
15743			} else {
15744				$bbox_left = 'auto';
15745			}
15746			if (isset($p['TOP']) && strtolower($p['TOP']) != 'auto') {
15747				$bbox_top = $this->sizeConverter->convert($p['TOP'], $cont_h, $this->FontSize, false);
15748			} else {
15749				$bbox_top = 'auto';
15750			}
15751			if (isset($p['RIGHT']) && strtolower($p['RIGHT']) != 'auto') {
15752				$bbox_right = $this->sizeConverter->convert($p['RIGHT'], $cont_w, $this->FontSize, false);
15753			} else {
15754				$bbox_right = 'auto';
15755			}
15756			if (isset($p['BOTTOM']) && strtolower($p['BOTTOM']) != 'auto') {
15757				$bbox_bottom = $this->sizeConverter->convert($p['BOTTOM'], $cont_h, $this->FontSize, false);
15758			} else {
15759				$bbox_bottom = 'auto';
15760			}
15761			if (isset($p['WIDTH']) && strtolower($p['WIDTH']) != 'auto') {
15762				$inner_w = $this->sizeConverter->convert($p['WIDTH'], $cont_w, $this->FontSize, false);
15763			} else {
15764				$inner_w = 'auto';
15765			}
15766			if (isset($p['HEIGHT']) && strtolower($p['HEIGHT']) != 'auto') {
15767				$inner_h = $this->sizeConverter->convert($p['HEIGHT'], $cont_h, $this->FontSize, false);
15768			} else {
15769				$inner_h = 'auto';
15770			}
15771
15772			// If bottom or right pos are set and not left / top - save this to adjust rotated block later
15773			if ($rotate == 90 || $rotate == -90) { // mPDF 6
15774				if ($bbox_left === 'auto' && $bbox_right !== 'auto') {
15775					$rot_rpos = $bbox_right;
15776				} else {
15777					$rot_rpos = false;
15778				}
15779				if ($bbox_top === 'auto' && $bbox_bottom !== 'auto') {
15780					$rot_bpos = $bbox_bottom;
15781				} else {
15782					$rot_bpos = false;
15783				}
15784			}
15785
15786			// ================================================================
15787			if ($checkinnerhtml == '' && $inner_h === 'auto') {
15788				$inner_h = 0.0001;
15789			}
15790			if ($checkinnerhtml == '' && $inner_w === 'auto') {
15791				$inner_w = 2 * $this->GetCharWidth('W', false);
15792			}
15793			// ================================================================
15794			// Algorithm from CSS2.1  See http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
15795			// mPD 5.3.14
15796			// Special case (not CSS) if all not specified, centre vertically on page
15797			$bbox_top_orig = '';
15798			if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_mt === 'auto' && $bbox_mb === 'auto') {
15799				$bbox_top_orig = $bbox_top;
15800				if ($bbox_mt === 'auto') {
15801					$bbox_mt = 0;
15802				}
15803				if ($bbox_mb === 'auto') {
15804					$bbox_mb = 0;
15805				}
15806				$bbox_top = $orig_y0 - $bbox_mt - $cont_y;
15807				// solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
15808			} // mPD 5.3.14
15809			elseif ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto') {
15810				$bbox_top_orig = $bbox_top = $orig_y0 - $cont_y;
15811				if ($bbox_mt === 'auto') {
15812					$bbox_mt = 0;
15813				}
15814				if ($bbox_mb === 'auto') {
15815					$bbox_mb = 0;
15816				}
15817				// solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
15818			} elseif ($bbox_top !== 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {
15819				if ($bbox_mt === 'auto' && $bbox_mb === 'auto') {
15820					$x = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;
15821					$bbox_mt = $bbox_mb = ($x / 2);
15822				} elseif ($bbox_mt === 'auto') {
15823					$bbox_mt = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
15824				} elseif ($bbox_mb === 'auto') {
15825					$bbox_mb = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;
15826				} else {
15827					$bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
15828				}
15829			} else {
15830				if ($bbox_mt === 'auto') {
15831					$bbox_mt = 0;
15832				}
15833				if ($bbox_mb === 'auto') {
15834					$bbox_mb = 0;
15835				}
15836				if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom !== 'auto') {
15837					// solve for $bbox_top when content_h known - $inner_h=='auto' && $bbox_top =='auto'
15838				} elseif ($bbox_top === 'auto' && $bbox_bottom === 'auto' && $inner_h !== 'auto') {
15839					$bbox_top = $orig_y0 - $bbox_mt - $cont_y;
15840					$bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
15841				} elseif ($inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_top !== 'auto') {
15842					// solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
15843				} elseif ($bbox_top === 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {
15844					$bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;
15845				} elseif ($inner_h === 'auto' && $bbox_top !== 'auto' && $bbox_bottom !== 'auto') {
15846					$inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;
15847				} elseif ($bbox_bottom === 'auto' && $bbox_top !== 'auto' && $inner_h !== 'auto') {
15848					$bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
15849				}
15850			}
15851
15852			// THEN DO SAME FOR WIDTH
15853			// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
15854			if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right === 'auto') {
15855				if ($bbox_ml === 'auto') {
15856					$bbox_ml = 0;
15857				}
15858				if ($bbox_mr === 'auto') {
15859					$bbox_mr = 0;
15860				}
15861				// IF containing element RTL, should set $bbox_right
15862				$bbox_left = $orig_x0 - $bbox_ml - $cont_x;
15863				// solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'
15864			} elseif ($bbox_left !== 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {
15865				if ($bbox_ml === 'auto' && $bbox_mr === 'auto') {
15866					$x = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;
15867					$bbox_ml = $bbox_mr = ($x / 2);
15868				} elseif ($bbox_ml === 'auto') {
15869					$bbox_ml = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;
15870				} elseif ($bbox_mr === 'auto') {
15871					$bbox_mr = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;
15872				} else {
15873					$bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
15874				}
15875			} else {
15876				if ($bbox_ml === 'auto') {
15877					$bbox_ml = 0;
15878				}
15879				if ($bbox_mr === 'auto') {
15880					$bbox_mr = 0;
15881				}
15882				if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right !== 'auto') {
15883					// solve for $bbox_left when content_w known - $inner_w=='auto' && $bbox_left =='auto'
15884				} elseif ($bbox_left === 'auto' && $bbox_right === 'auto' && $inner_w !== 'auto') {
15885					// IF containing element RTL, should set $bbox_right
15886					$bbox_left = $orig_x0 - $bbox_ml - $cont_x;
15887					$bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
15888				} elseif ($inner_w === 'auto' && $bbox_right === 'auto' && $bbox_left !== 'auto') {
15889					// solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'
15890				} elseif ($bbox_left === 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {
15891					$bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
15892				} elseif ($inner_w === 'auto' && $bbox_left !== 'auto' && $bbox_right !== 'auto') {
15893					$inner_w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
15894				} elseif ($bbox_right === 'auto' && $bbox_left !== 'auto' && $inner_w !== 'auto') {
15895					$bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
15896				}
15897			}
15898
15899			// ================================================================
15900			// ================================================================
15901			/* -- BACKGROUNDS -- */
15902			if (isset($pb['BACKGROUND-IMAGE']) && $pb['BACKGROUND-IMAGE']) {
15903				$ret = $this->SetBackground($pb, $this->blk[1]['inner_width']);
15904				if ($ret) {
15905					$this->blk[1]['background-image'] = $ret;
15906				}
15907			}
15908			/* -- END BACKGROUNDS -- */
15909
15910			$bbox_top_auto = $bbox_top === 'auto';
15911			$bbox_left_auto = $bbox_left === 'auto';
15912			$bbox_right_auto = $bbox_right === 'auto';
15913			$bbox_bottom_auto = $bbox_bottom === 'auto';
15914
15915			$bbox_top = is_numeric($bbox_top) ? $bbox_top : 0;
15916			$bbox_left = is_numeric($bbox_left) ? $bbox_left : 0;
15917			$bbox_right = is_numeric($bbox_right) ? $bbox_right : 0;
15918			$bbox_bottom = is_numeric($bbox_bottom) ? $bbox_bottom : 0;
15919
15920			$y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;
15921			$h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
15922
15923			$x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;
15924			$w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;
15925
15926			// Set (temporary) values for x y w h to do first paint, if values are auto
15927			if ($inner_h === 'auto' && $bbox_top_auto) {
15928				$y = $cont_y + $bbox_mt + $bbox_bt + $bbox_pt;
15929				$h = $cont_h - ($bbox_bottom + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);
15930			} elseif ($inner_h === 'auto' && $bbox_bottom_auto) {
15931				$y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;
15932				$h = $cont_h - ($bbox_top + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);
15933			}
15934			if ($inner_w === 'auto' && $bbox_left_auto) {
15935				$x = $cont_x + $bbox_ml + $bbox_bl + $bbox_pl;
15936				$w = $cont_w - ($bbox_right + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);
15937			} elseif ($inner_w === 'auto' && $bbox_right_auto) {
15938				$x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;
15939				$w = $cont_w - ($bbox_left + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);
15940			}
15941
15942			$bbox_y = $cont_y + $bbox_top + $bbox_mt;
15943			$bbox_x = $cont_x + $bbox_left + $bbox_ml;
15944
15945			$saved_block1 = $this->blk[1];
15946
15947			unset($p);
15948			unset($pb);
15949
15950			// ================================================================
15951			if ($inner_w === 'auto') { // do a first write
15952				$this->lMargin = $x;
15953				$this->rMargin = $this->w - $w - $x;
15954
15955				// SET POSITION & FONT VALUES
15956				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
15957				$this->pageoutput[$this->page] = [];
15958				$this->x = $x;
15959				$this->y = $y;
15960				$this->HTMLheaderPageLinks = [];
15961				$this->HTMLheaderPageAnnots = [];
15962				$this->HTMLheaderPageForms = [];
15963				$this->pageBackgrounds = [];
15964				$this->maxPosR = 0;
15965				$this->maxPosL = $this->w; // For RTL
15966				$this->WriteHTML($html, 4);
15967				$inner_w = $this->maxPosR - $this->lMargin;
15968				if ($bbox_right_auto) {
15969					$bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
15970				} elseif ($bbox_left_auto) {
15971					$bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
15972					$bbox_x = $cont_x + $bbox_left + $bbox_ml;
15973					$inner_x = $bbox_x + $bbox_bl + $bbox_pl;
15974					$x = $inner_x;
15975				}
15976
15977				$w = $inner_w;
15978				$bbox_y = $cont_y + $bbox_top + $bbox_mt;
15979				$bbox_x = $cont_x + $bbox_left + $bbox_ml;
15980			}
15981
15982			if ($inner_h === 'auto') { // do a first write
15983
15984				$this->lMargin = $x;
15985				$this->rMargin = $this->w - $w - $x;
15986
15987				// SET POSITION & FONT VALUES
15988				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
15989				$this->pageoutput[$this->page] = [];
15990				$this->x = $x;
15991				$this->y = $y;
15992				$this->HTMLheaderPageLinks = [];
15993				$this->HTMLheaderPageAnnots = [];
15994				$this->HTMLheaderPageForms = [];
15995				$this->pageBackgrounds = [];
15996				$this->WriteHTML($html, 4);
15997				$inner_h = $this->y - $y;
15998
15999				if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
16000					if (($this->y + $bbox_pb + $bbox_bb) > ($cont_y + $cont_h)) {
16001						$adj = ($this->y + $bbox_pb + $bbox_bb) - ($cont_y + $cont_h);
16002						$inner_h -= $adj;
16003					}
16004				}
16005				if ($bbox_bottom_auto && $bbox_top_orig === 'auto') {
16006					$bbox_bottom = $bbox_top = ($cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb) / 2;
16007					if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
16008						if ($bbox_top < 0) {
16009							$bbox_top = 0;
16010							$inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
16011						}
16012					}
16013					$bbox_y = $cont_y + $bbox_top + $bbox_mt;
16014					$inner_y = $bbox_y + $bbox_bt + $bbox_pt;
16015					$y = $inner_y;
16016				} elseif ($bbox_bottom_auto) {
16017					$bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb;
16018				} elseif ($bbox_top_auto) {
16019					$bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
16020					if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
16021						if ($bbox_top < 0) {
16022							$bbox_top = 0;
16023							$inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
16024						}
16025					}
16026					$bbox_y = $cont_y + $bbox_top + $bbox_mt;
16027					$inner_y = $bbox_y + $bbox_bt + $bbox_pt;
16028					$y = $inner_y;
16029				}
16030				$h = $inner_h;
16031				$bbox_y = $cont_y + $bbox_top + $bbox_mt;
16032				$bbox_x = $cont_x + $bbox_left + $bbox_ml;
16033			}
16034
16035			$inner_w = $w;
16036			$inner_h = $h;
16037		}
16038
16039		$this->lMargin = $x;
16040		$this->rMargin = $this->w - $w - $x;
16041
16042		// SET POSITION & FONT VALUES
16043		$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
16044		$this->pageoutput[$this->page] = [];
16045
16046		$this->x = $x;
16047		$this->y = $y;
16048
16049		$this->HTMLheaderPageLinks = [];
16050		$this->HTMLheaderPageAnnots = [];
16051		$this->HTMLheaderPageForms = [];
16052
16053		$this->pageBackgrounds = [];
16054
16055		$this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
16056
16057		$actual_h = $this->y - $y;
16058		$use_w = $w;
16059		$use_h = $h;
16060		$ratio = $actual_h / $use_w;
16061
16062		if ($overflow != 'hidden' && $overflow != 'visible') {
16063			$target = $h / $w;
16064			if (($ratio / $target ) > 1) {
16065				$nl = ceil($actual_h / $this->lineheight);
16066				$l = $use_w * $nl;
16067				$est_w = sqrt(($l * $this->lineheight) / $target) * 0.8;
16068				$use_w += ($est_w - $use_w) - ($w / 100);
16069			}
16070			$bpcstart = ($ratio / $target);
16071			$bpcctr = 1;
16072
16073			while (($ratio / $target ) > 1) {
16074				// @log 'Auto-sizing fixed-position block $bpcctr++
16075
16076				$this->x = $x;
16077				$this->y = $y;
16078
16079				if (($ratio / $target) > 1.5 || ($ratio / $target) < 0.6) {
16080					$use_w += ($w / $this->incrementFPR1);
16081				} elseif (($ratio / $target) > 1.2 || ($ratio / $target) < 0.85) {
16082					$use_w += ($w / $this->incrementFPR2);
16083				} elseif (($ratio / $target) > 1.1 || ($ratio / $target) < 0.91) {
16084					$use_w += ($w / $this->incrementFPR3);
16085				} else {
16086					$use_w += ($w / $this->incrementFPR4);
16087				}
16088
16089				$use_h = $use_w * $target;
16090				$this->rMargin = $this->w - $use_w - $x;
16091				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
16092				$this->HTMLheaderPageLinks = [];
16093				$this->HTMLheaderPageAnnots = [];
16094				$this->HTMLheaderPageForms = [];
16095				$this->pageBackgrounds = [];
16096				$this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
16097				$actual_h = $this->y - $y;
16098				$ratio = $actual_h / $use_w;
16099			}
16100		}
16101
16102		$shrink_f = $w / $use_w;
16103
16104		// ================================================================
16105
16106		$this->pages[$this->page] .= '___BEFORE_BORDERS___';
16107		$block_s = $this->PrintPageBackgrounds(); // Save to print later inside clipping path
16108		$this->pageBackgrounds = [];
16109
16110		// ================================================================
16111
16112		if ($rotate == 90 || $rotate == -90) { // mPDF 6
16113			$prerotw = $bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br;
16114			$preroth = $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;
16115			$rot_start = " q\n";
16116			if ($rotate == 90) {
16117				if ($rot_rpos !== false) {
16118					$adjw = $prerotw;
16119				} // width before rotation
16120				else {
16121					$adjw = $preroth;
16122				} // height before rotation
16123				if ($rot_bpos !== false) {
16124					$adjh = -$prerotw + $preroth;
16125				} else {
16126					$adjh = 0;
16127				}
16128			} else {
16129				if ($rot_rpos !== false) {
16130					$adjw = $prerotw - $preroth;
16131				} else {
16132					$adjw = 0;
16133				}
16134				if ($rot_bpos !== false) {
16135					$adjh = $preroth;
16136				} // height before rotation
16137				else {
16138					$adjh = $prerotw;
16139				} // width before rotation
16140			}
16141			$rot_start .= $this->transformTranslate($adjw, $adjh, true) . "\n";
16142			$rot_start .= $this->transformRotate($rotate, $bbox_x, $bbox_y, true) . "\n";
16143			$rot_end = " Q\n";
16144		} elseif ($rotate == 180) { // mPDF 6
16145			$rot_start = " q\n";
16146			$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";
16147			$rot_start .= $this->transformRotate(180, $bbox_x, $bbox_y, true) . "\n";
16148			$rot_end = " Q\n";
16149		} else {
16150			$rot_start = '';
16151			$rot_end = '';
16152		}
16153
16154		// ================================================================
16155		if (!empty($bounding)) {
16156			// WHEN HEIGHT // BOTTOM EDGE IS KNOWN and $this->y is set to the bottom
16157			// Re-instate saved $this->blk[1]
16158			$this->blk[1] = $saved_block1;
16159
16160			// These are only needed when painting border/background
16161			$this->blk[1]['width'] = $bbox_w = $cont_w - $bbox_left - $bbox_ml - $bbox_mr - $bbox_right;
16162			$this->blk[1]['x0'] = $bbox_x;
16163			$this->blk[1]['y0'] = $bbox_y;
16164			$this->blk[1]['startpage'] = $this->page;
16165			$this->blk[1]['y1'] = $bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;
16166			$this->_out($rot_start);
16167			$this->PaintDivBB('', 0, 1); // Prints borders and sets backgrounds in $this->pageBackgrounds
16168			$this->_out($rot_end);
16169		}
16170
16171		$s = $this->PrintPageBackgrounds();
16172		$s = $rot_start . $s . $rot_end;
16173		$this->pages[$this->page] = preg_replace('/___BEFORE_BORDERS___/', "\n" . $s . "\n", $this->pages[$this->page]);
16174		$this->pageBackgrounds = [];
16175
16176		$this->_out($rot_start);
16177
16178		// Clipping Output
16179		if ($overflow == 'hidden') {
16180			// Bounding rectangle to clip
16181			$clip_y1 = $this->y;
16182			if (!empty($bounding) && ($this->y + $bbox_pb + $bbox_bb) > ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb )) {
16183				$clip_y1 = ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb ) - ($bbox_pb + $bbox_bb);
16184			}
16185			// $op = 'W* n';	// Clipping
16186			$op = 'W n'; // Clipping alternative mode
16187			$this->_out("q");
16188			$ch = $clip_y1 - $y;
16189			$this->_out(sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$ch * Mpdf::SCALE, $op));
16190			if (!empty($block_s)) {
16191				$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);
16192				$tmp .= "\n" . $block_s . "\nQ";
16193				$block_s = $tmp;
16194			}
16195		}
16196
16197
16198		if (!empty($block_s)) {
16199			if ($shrink_f != 1) { // i.e. autofit has resized the box
16200				$tmp = "q\n" . $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y, true);
16201				$tmp .= "\n" . $block_s . "\nQ";
16202				$block_s = $tmp;
16203			}
16204			$this->_out($block_s);
16205		}
16206
16207
16208
16209		if ($shrink_f != 1) { // i.e. autofit has resized the box
16210			$this->StartTransform();
16211			$this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y);
16212		}
16213
16214		$this->_out($this->headerbuffer);
16215
16216		if ($shrink_f != 1) { // i.e. autofit has resized the box
16217			$this->StopTransform();
16218		}
16219
16220		if ($overflow == 'hidden') {
16221			// End clipping
16222			$this->_out("Q");
16223		}
16224
16225		$this->_out($rot_end);
16226
16227
16228		// Page Links
16229		foreach ($this->HTMLheaderPageLinks as $lk) {
16230			if ($rotate) {
16231				$tmp = $lk[2]; // Switch h - w
16232				$lk[2] = $lk[3];
16233				$lk[3] = $tmp;
16234
16235				$lx1 = (($lk[0] / Mpdf::SCALE));
16236				$ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)));
16237				if ($rotate == 90) {
16238					$adjx = -($lx1 - $bbox_x) + ($preroth - ($ly1 - $bbox_y));
16239					$adjy = -($ly1 - $bbox_y) + ($lx1 - $bbox_x);
16240					$lk[2] = -$lk[2];
16241				} elseif ($rotate == -90) {
16242					$adjx = -($lx1 - $bbox_x) + ($ly1 - $bbox_y);
16243					$adjy = -($ly1 - $bbox_y) - ($lx1 - $bbox_x) + $prerotw;
16244					$lk[3] = -$lk[3];
16245				}
16246				if ($rot_rpos !== false) {
16247					$adjx += $prerotw - $preroth;
16248				}
16249				if ($rot_bpos !== false) {
16250					$adjy += $preroth - $prerotw;
16251				}
16252				$lx1 += $adjx;
16253				$ly1 += $adjy;
16254
16255				$lk[0] = $lx1 * Mpdf::SCALE;
16256				$lk[1] = ($this->h - $ly1) * Mpdf::SCALE;
16257			}
16258			if ($shrink_f != 1) {  // i.e. autofit has resized the box
16259				$lx1 = (($lk[0] / Mpdf::SCALE) - $x);
16260				$lx2 = $x + ($lx1 * $shrink_f);
16261				$lk[0] = $lx2 * Mpdf::SCALE;
16262				$ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)) - $y);
16263				$ly2 = $y + ($ly1 * $shrink_f);
16264				$lk[1] = ($this->h - $ly2) * Mpdf::SCALE;
16265				$lk[2] *= $shrink_f; // width
16266				$lk[3] *= $shrink_f; // height
16267			}
16268			$this->PageLinks[$this->page][] = $lk;
16269		}
16270
16271		foreach ($this->HTMLheaderPageForms as $n => $f) {
16272			if ($shrink_f != 1) {  // i.e. autofit has resized the box
16273				$f['x'] = $x + (($f['x'] - $x) * $shrink_f);
16274				$f['y'] = $y + (($f['y'] - $y) * $shrink_f);
16275				$f['w'] *= $shrink_f;
16276				$f['h'] *= $shrink_f;
16277				$f['style']['fontsize'] *= $shrink_f;
16278			}
16279			$this->form->forms[$f['n']] = $f;
16280		}
16281		// Page Annotations
16282		foreach ($this->HTMLheaderPageAnnots as $lk) {
16283			if ($rotate) {
16284				if ($rotate == 90) {
16285					$adjx = -($lk['x'] - $bbox_x) + ($preroth - ($lk['y'] - $bbox_y));
16286					$adjy = -($lk['y'] - $bbox_y) + ($lk['x'] - $bbox_x);
16287				} elseif ($rotate == -90) {
16288					$adjx = -($lk['x'] - $bbox_x) + ($lk['y'] - $bbox_y);
16289					$adjy = -($lk['y'] - $bbox_y) - ($lk['x'] - $bbox_x) + $prerotw;
16290				}
16291				if ($rot_rpos !== false) {
16292					$adjx += $prerotw - $preroth;
16293				}
16294				if ($rot_bpos !== false) {
16295					$adjy += $preroth - $prerotw;
16296				}
16297				$lk['x'] += $adjx;
16298				$lk['y'] += $adjy;
16299			}
16300			if ($shrink_f != 1) {  // i.e. autofit has resized the box
16301				$lk['x'] = $x + (($lk['x'] - $x) * $shrink_f);
16302				$lk['y'] = $y + (($lk['y'] - $y) * $shrink_f);
16303			}
16304			$this->PageAnnots[$this->page][] = $lk;
16305		}
16306
16307		// Restore
16308		$this->headerbuffer = '';
16309		$this->HTMLheaderPageLinks = [];
16310		$this->HTMLheaderPageAnnots = [];
16311		$this->HTMLheaderPageForms = [];
16312		$this->pageBackgrounds = $save_bgs;
16313		$this->writingHTMLheader = false;
16314
16315		$this->writingHTMLfooter = false;
16316		$this->fullImageHeight = false;
16317		$this->ResetMargins();
16318		$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
16319		$this->SetXY($save_x, $save_y);
16320		$this->title2annots = $save_annots; // *ANNOTATIONS*
16321		$this->InFooter = false; // turns back on autopagebreaks
16322		$this->pageoutput[$this->page] = [];
16323		$this->pageoutput[$this->page]['Font'] = '';
16324		/* -- COLUMNS -- */
16325		if ($save_cols) {
16326			$this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);
16327		}
16328		/* -- END COLUMNS -- */
16329	}
16330
16331	/* -- END CSS-POSITION -- */
16332
16333	function initialiseBlock(&$blk)
16334	{
16335		$blk['margin_top'] = 0;
16336		$blk['margin_left'] = 0;
16337		$blk['margin_bottom'] = 0;
16338		$blk['margin_right'] = 0;
16339		$blk['padding_top'] = 0;
16340		$blk['padding_left'] = 0;
16341		$blk['padding_bottom'] = 0;
16342		$blk['padding_right'] = 0;
16343		$blk['border_top']['w'] = 0;
16344		$blk['border_left']['w'] = 0;
16345		$blk['border_bottom']['w'] = 0;
16346		$blk['border_right']['w'] = 0;
16347		$blk['direction'] = 'ltr';
16348		$blk['hide'] = false;
16349		$blk['outer_left_margin'] = 0;
16350		$blk['outer_right_margin'] = 0;
16351		$blk['cascadeCSS'] = [];
16352		$blk['block-align'] = false;
16353		$blk['bgcolor'] = false;
16354		$blk['page_break_after_avoid'] = false;
16355		$blk['keep_block_together'] = false;
16356		$blk['float'] = false;
16357		$blk['line_height'] = '';
16358		$blk['margin_collapse'] = false;
16359	}
16360
16361	function border_details($bd)
16362	{
16363		$prop = preg_split('/\s+/', trim($bd));
16364
16365		if (isset($this->blk[$this->blklvl]['inner_width'])) {
16366			$refw = $this->blk[$this->blklvl]['inner_width'];
16367		} elseif (isset($this->blk[$this->blklvl - 1]['inner_width'])) {
16368			$refw = $this->blk[$this->blklvl - 1]['inner_width'];
16369		} else {
16370			$refw = $this->w;
16371		}
16372		if (count($prop) == 1) {
16373			$bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false);
16374			if ($bsize > 0) {
16375				return ['s' => 1, 'w' => $bsize, 'c' => $this->colorConverter->convert(0, $this->PDFAXwarnings), 'style' => 'solid'];
16376			} else {
16377				return ['w' => 0, 's' => 0];
16378			}
16379		} elseif (count($prop) == 2) {
16380			// 1px solid
16381			if (in_array($prop[1], $this->borderstyles) || $prop[1] == 'none' || $prop[1] == 'hidden') {
16382				$prop[2] = '';
16383			} // solid #000000
16384			elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {
16385				$prop[0] = '';
16386				$prop[1] = $prop[0];
16387				$prop[2] = $prop[1];
16388			} // 1px #000000
16389			else {
16390				$prop[1] = '';
16391				$prop[2] = $prop[1];
16392			}
16393		} elseif (count($prop) == 3) {
16394			// Change #000000 1px solid to 1px solid #000000 (proper)
16395			if (substr($prop[0], 0, 1) == '#') {
16396				$tmp = $prop[0];
16397				$prop[0] = $prop[1];
16398				$prop[1] = $prop[2];
16399				$prop[2] = $tmp;
16400			} // Change solid #000000 1px to 1px solid #000000 (proper)
16401			elseif (substr($prop[0], 1, 1) == '#') {
16402				$tmp = $prop[1];
16403				$prop[0] = $prop[2];
16404				$prop[1] = $prop[0];
16405				$prop[2] = $tmp;
16406			} // Change solid 1px #000000 to 1px solid #000000 (proper)
16407			elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {
16408				$tmp = $prop[0];
16409				$prop[0] = $prop[1];
16410				$prop[1] = $tmp;
16411			}
16412		} else {
16413			return [];
16414		}
16415		// Size
16416		$bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false);
16417		// color
16418		$coul = $this->colorConverter->convert($prop[2], $this->PDFAXwarnings); // returns array
16419		// Style
16420		$prop[1] = strtolower($prop[1]);
16421		if (in_array($prop[1], $this->borderstyles) && $bsize > 0) {
16422			$on = 1;
16423		} elseif ($prop[1] == 'hidden') {
16424			$on = 1;
16425			$bsize = 0;
16426			$coul = '';
16427		} elseif ($prop[1] == 'none') {
16428			$on = 0;
16429			$bsize = 0;
16430			$coul = '';
16431		} else {
16432			$on = 0;
16433			$bsize = 0;
16434			$coul = '';
16435			$prop[1] = '';
16436		}
16437		return ['s' => $on, 'w' => $bsize, 'c' => $coul, 'style' => $prop[1], 'dom' => 0];
16438	}
16439
16440	/* -- END HTML-CSS -- */
16441
16442
16443	/* -- BORDER-RADIUS -- */
16444
16445	function _borderPadding($a, $b, &$px, &$py)
16446	{
16447		// $px and py are padding long axis (x) and short axis (y)
16448		$added = 0; // extra padding
16449
16450		$x = $a - $px;
16451		$y = $b - $py;
16452		// Check if Falls within ellipse of border radius
16453		if (( (($x + $added) * ($x + $added)) / ($a * $a) + (($y + $added) * ($y + $added)) / ($b * $b) ) <= 1) {
16454			return false;
16455		}
16456
16457		$t = atan2($y, $x);
16458
16459		$newx = $b / sqrt((($b * $b) / ($a * $a)) + ( tan($t) * tan($t) ));
16460		$newy = $a / sqrt((($a * $a) / ($b * $b)) + ( (1 / tan($t)) * (1 / tan($t)) ));
16461		$px = max($px, $a - $newx + $added);
16462		$py = max($py, $b - $newy + $added);
16463	}
16464
16465	/* -- END BORDER-RADIUS -- */
16466	/* -- HTML-CSS -- */
16467	/* -- CSS-PAGE -- */
16468
16469	function SetPagedMediaCSS($name, $first, $oddEven)
16470	{
16471		if ($oddEven == 'E') {
16472			if ($this->directionality == 'rtl') {
16473				$side = 'R';
16474			} else {
16475				$side = 'L';
16476			}
16477		} else {
16478			if ($this->directionality == 'rtl') {
16479				$side = 'L';
16480			} else {
16481				$side = 'R';
16482			}
16483		}
16484		$name = strtoupper($name);
16485		$p = [];
16486		$p['SIZE'] = 'AUTO';
16487
16488		// Uses mPDF original margins as default
16489		$p['MARGIN-RIGHT'] = strval($this->orig_rMargin) . 'mm';
16490		$p['MARGIN-LEFT'] = strval($this->orig_lMargin) . 'mm';
16491		$p['MARGIN-TOP'] = strval($this->orig_tMargin) . 'mm';
16492		$p['MARGIN-BOTTOM'] = strval($this->orig_bMargin) . 'mm';
16493		$p['MARGIN-HEADER'] = strval($this->orig_hMargin) . 'mm';
16494		$p['MARGIN-FOOTER'] = strval($this->orig_fMargin) . 'mm';
16495
16496		// Basic page + selector
16497		if (isset($this->cssManager->CSS['@PAGE'])) {
16498			$zp = $this->cssManager->CSS['@PAGE'];
16499		} else {
16500			$zp = [];
16501		}
16502		if (is_array($zp) && !empty($zp)) {
16503			$p = array_merge($p, $zp);
16504		}
16505
16506		if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {
16507			$p['HEADER'] = $p['EVEN-HEADER-NAME'];
16508			unset($p['EVEN-HEADER-NAME']);
16509		}
16510		if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {
16511			$p['HEADER'] = $p['ODD-HEADER-NAME'];
16512			unset($p['ODD-HEADER-NAME']);
16513		}
16514		if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {
16515			$p['FOOTER'] = $p['EVEN-FOOTER-NAME'];
16516			unset($p['EVEN-FOOTER-NAME']);
16517		}
16518		if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {
16519			$p['FOOTER'] = $p['ODD-FOOTER-NAME'];
16520			unset($p['ODD-FOOTER-NAME']);
16521		}
16522
16523		// If right/Odd page
16524		if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT']) && $side == 'R') {
16525			$zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT'];
16526		} else {
16527			$zp = [];
16528		}
16529		if (isset($zp['SIZE'])) {
16530			unset($zp['SIZE']);
16531		}
16532		if (isset($zp['SHEET-SIZE'])) {
16533			unset($zp['SHEET-SIZE']);
16534		}
16535		// Disallow margin-left or -right on :LEFT or :RIGHT
16536		if (isset($zp['MARGIN-LEFT'])) {
16537			unset($zp['MARGIN-LEFT']);
16538		}
16539		if (isset($zp['MARGIN-RIGHT'])) {
16540			unset($zp['MARGIN-RIGHT']);
16541		}
16542		if (is_array($zp) && !empty($zp)) {
16543			$p = array_merge($p, $zp);
16544		}
16545
16546		// If left/Even page
16547		if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT']) && $side == 'L') {
16548			$zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT'];
16549		} else {
16550			$zp = [];
16551		}
16552		if (isset($zp['SIZE'])) {
16553			unset($zp['SIZE']);
16554		}
16555		if (isset($zp['SHEET-SIZE'])) {
16556			unset($zp['SHEET-SIZE']);
16557		}
16558		// Disallow margin-left or -right on :LEFT or :RIGHT
16559		if (isset($zp['MARGIN-LEFT'])) {
16560			unset($zp['MARGIN-LEFT']);
16561		}
16562		if (isset($zp['MARGIN-RIGHT'])) {
16563			unset($zp['MARGIN-RIGHT']);
16564		}
16565		if (is_array($zp) && !empty($zp)) {
16566			$p = array_merge($p, $zp);
16567		}
16568
16569		// If first page
16570		if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']) && $first) {
16571			$zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST'];
16572		} else {
16573			$zp = [];
16574		}
16575		if (isset($zp['SIZE'])) {
16576			unset($zp['SIZE']);
16577		}
16578		if (isset($zp['SHEET-SIZE'])) {
16579			unset($zp['SHEET-SIZE']);
16580		}
16581		// Disallow margin-left or -right on :FIRST	// mPDF 5.7.3
16582		if (isset($zp['MARGIN-LEFT'])) {
16583			unset($zp['MARGIN-LEFT']);
16584		}
16585		if (isset($zp['MARGIN-RIGHT'])) {
16586			unset($zp['MARGIN-RIGHT']);
16587		}
16588		if (is_array($zp) && !empty($zp)) {
16589			$p = array_merge($p, $zp);
16590		}
16591
16592		// If named page
16593		if ($name) {
16594			if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name])) {
16595				$zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name];
16596			} else {
16597				$zp = [];
16598			}
16599			if (is_array($zp) && !empty($zp)) {
16600				$p = array_merge($p, $zp);
16601			}
16602
16603			if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {
16604				$p['HEADER'] = $p['EVEN-HEADER-NAME'];
16605				unset($p['EVEN-HEADER-NAME']);
16606			}
16607			if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {
16608				$p['HEADER'] = $p['ODD-HEADER-NAME'];
16609				unset($p['ODD-HEADER-NAME']);
16610			}
16611			if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {
16612				$p['FOOTER'] = $p['EVEN-FOOTER-NAME'];
16613				unset($p['EVEN-FOOTER-NAME']);
16614			}
16615			if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {
16616				$p['FOOTER'] = $p['ODD-FOOTER-NAME'];
16617				unset($p['ODD-FOOTER-NAME']);
16618			}
16619
16620			// If named right/Odd page
16621			if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT']) && $side == 'R') {
16622				$zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT'];
16623			} else {
16624				$zp = [];
16625			}
16626			if (isset($zp['SIZE'])) {
16627				unset($zp['SIZE']);
16628			}
16629			if (isset($zp['SHEET-SIZE'])) {
16630				unset($zp['SHEET-SIZE']);
16631			}
16632			// Disallow margin-left or -right on :LEFT or :RIGHT
16633			if (isset($zp['MARGIN-LEFT'])) {
16634				unset($zp['MARGIN-LEFT']);
16635			}
16636			if (isset($zp['MARGIN-RIGHT'])) {
16637				unset($zp['MARGIN-RIGHT']);
16638			}
16639			if (is_array($zp) && !empty($zp)) {
16640				$p = array_merge($p, $zp);
16641			}
16642
16643			// If named left/Even page
16644			if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT']) && $side == 'L') {
16645				$zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT'];
16646			} else {
16647				$zp = [];
16648			}
16649			if (isset($zp['SIZE'])) {
16650				unset($zp['SIZE']);
16651			}
16652			if (isset($zp['SHEET-SIZE'])) {
16653				unset($zp['SHEET-SIZE']);
16654			}
16655			// Disallow margin-left or -right on :LEFT or :RIGHT
16656			if (isset($zp['MARGIN-LEFT'])) {
16657				unset($zp['MARGIN-LEFT']);
16658			}
16659			if (isset($zp['MARGIN-RIGHT'])) {
16660				unset($zp['MARGIN-RIGHT']);
16661			}
16662			if (is_array($zp) && !empty($zp)) {
16663				$p = array_merge($p, $zp);
16664			}
16665
16666			// If named first page
16667			if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST']) && $first) {
16668				$zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST'];
16669			} else {
16670				$zp = [];
16671			}
16672			if (isset($zp['SIZE'])) {
16673				unset($zp['SIZE']);
16674			}
16675			if (isset($zp['SHEET-SIZE'])) {
16676				unset($zp['SHEET-SIZE']);
16677			}
16678			// Disallow margin-left or -right on :FIRST	// mPDF 5.7.3
16679			if (isset($zp['MARGIN-LEFT'])) {
16680				unset($zp['MARGIN-LEFT']);
16681			}
16682			if (isset($zp['MARGIN-RIGHT'])) {
16683				unset($zp['MARGIN-RIGHT']);
16684			}
16685			if (is_array($zp) && !empty($zp)) {
16686				$p = array_merge($p, $zp);
16687			}
16688		}
16689
16690		$orientation = $mgl = $mgr = $mgt = $mgb = $mgh = $mgf = '';
16691		$header = $footer = '';
16692		$resetpagenum = $pagenumstyle = $suppress = '';
16693		$marks = '';
16694		$bg = [];
16695
16696		$newformat = '';
16697
16698
16699		if (isset($p['SHEET-SIZE']) && is_array($p['SHEET-SIZE'])) {
16700			$newformat = $p['SHEET-SIZE'];
16701			if ($newformat[0] > $newformat[1]) { // landscape
16702				$newformat = array_reverse($newformat);
16703				$p['ORIENTATION'] = 'L';
16704			} else {
16705				$p['ORIENTATION'] = 'P';
16706			}
16707			$this->_setPageSize($newformat, $p['ORIENTATION']);
16708		}
16709
16710		if (isset($p['SIZE']) && is_array($p['SIZE']) && !$newformat) {
16711			if ($p['SIZE']['W'] > $p['SIZE']['H']) {
16712				$p['ORIENTATION'] = 'L';
16713			} else {
16714				$p['ORIENTATION'] = 'P';
16715			}
16716		}
16717		if (is_array($p['SIZE'])) {
16718			if ($p['SIZE']['W'] > $this->fw) {
16719				$p['SIZE']['W'] = $this->fw;
16720			} // mPD 4.2 use fw not fPt
16721			if ($p['SIZE']['H'] > $this->fh) {
16722				$p['SIZE']['H'] = $this->fh;
16723			}
16724			if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {
16725				$outer_width_LR = ($this->fw - $p['SIZE']['W']) / 2;
16726				$outer_width_TB = ($this->fh - $p['SIZE']['H']) / 2;
16727			} else {
16728				$outer_width_LR = ($this->fh - $p['SIZE']['W']) / 2;
16729				$outer_width_TB = ($this->fw - $p['SIZE']['H']) / 2;
16730			}
16731			$pgw = $p['SIZE']['W'];
16732			$pgh = $p['SIZE']['H'];
16733		} else { // AUTO LANDSCAPE PORTRAIT
16734			$outer_width_LR = 0;
16735			$outer_width_TB = 0;
16736			if (!$newformat) {
16737				if (strtoupper($p['SIZE']) == 'AUTO') {
16738					$p['ORIENTATION'] = $this->DefOrientation;
16739				} elseif (strtoupper($p['SIZE']) == 'LANDSCAPE') {
16740					$p['ORIENTATION'] = 'L';
16741				} else {
16742					$p['ORIENTATION'] = 'P';
16743				}
16744			}
16745			if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {
16746				$pgw = $this->fw;
16747				$pgh = $this->fh;
16748			} else {
16749				$pgw = $this->fh;
16750				$pgh = $this->fw;
16751			}
16752		}
16753
16754		if (isset($p['HEADER']) && $p['HEADER']) {
16755			$header = $p['HEADER'];
16756		}
16757		if (isset($p['FOOTER']) && $p['FOOTER']) {
16758			$footer = $p['FOOTER'];
16759		}
16760		if (isset($p['RESETPAGENUM']) && $p['RESETPAGENUM']) {
16761			$resetpagenum = $p['RESETPAGENUM'];
16762		}
16763		if (isset($p['PAGENUMSTYLE']) && $p['PAGENUMSTYLE']) {
16764			$pagenumstyle = $p['PAGENUMSTYLE'];
16765		}
16766		if (isset($p['SUPPRESS']) && $p['SUPPRESS']) {
16767			$suppress = $p['SUPPRESS'];
16768		}
16769
16770		if (isset($p['MARKS'])) {
16771			if (preg_match('/cross/i', $p['MARKS']) && preg_match('/crop/i', $p['MARKS'])) {
16772				$marks = 'CROPCROSS';
16773			} elseif (strtoupper($p['MARKS']) == 'CROP') {
16774				$marks = 'CROP';
16775			} elseif (strtoupper($p['MARKS']) == 'CROSS') {
16776				$marks = 'CROSS';
16777			}
16778		}
16779
16780		if (isset($p['BACKGROUND-COLOR']) && $p['BACKGROUND-COLOR']) {
16781			$bg['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];
16782		}
16783		/* -- BACKGROUNDS -- */
16784		if (isset($p['BACKGROUND-GRADIENT']) && $p['BACKGROUND-GRADIENT']) {
16785			$bg['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];
16786		}
16787		if (isset($p['BACKGROUND-IMAGE']) && $p['BACKGROUND-IMAGE']) {
16788			$bg['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];
16789		}
16790		if (isset($p['BACKGROUND-REPEAT']) && $p['BACKGROUND-REPEAT']) {
16791			$bg['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];
16792		}
16793		if (isset($p['BACKGROUND-POSITION']) && $p['BACKGROUND-POSITION']) {
16794			$bg['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];
16795		}
16796		if (isset($p['BACKGROUND-IMAGE-RESIZE']) && $p['BACKGROUND-IMAGE-RESIZE']) {
16797			$bg['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];
16798		}
16799		if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {
16800			$bg['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];
16801		}
16802		/* -- END BACKGROUNDS -- */
16803
16804		if (isset($p['MARGIN-LEFT'])) {
16805			$mgl = $this->sizeConverter->convert($p['MARGIN-LEFT'], $pgw) + $outer_width_LR;
16806		}
16807		if (isset($p['MARGIN-RIGHT'])) {
16808			$mgr = $this->sizeConverter->convert($p['MARGIN-RIGHT'], $pgw) + $outer_width_LR;
16809		}
16810		if (isset($p['MARGIN-BOTTOM'])) {
16811			$mgb = $this->sizeConverter->convert($p['MARGIN-BOTTOM'], $pgh) + $outer_width_TB;
16812		}
16813		if (isset($p['MARGIN-TOP'])) {
16814			$mgt = $this->sizeConverter->convert($p['MARGIN-TOP'], $pgh) + $outer_width_TB;
16815		}
16816		if (isset($p['MARGIN-HEADER'])) {
16817			$mgh = $this->sizeConverter->convert($p['MARGIN-HEADER'], $pgh) + $outer_width_TB;
16818		}
16819		if (isset($p['MARGIN-FOOTER'])) {
16820			$mgf = $this->sizeConverter->convert($p['MARGIN-FOOTER'], $pgh) + $outer_width_TB;
16821		}
16822
16823		if (isset($p['ORIENTATION']) && $p['ORIENTATION']) {
16824			$orientation = $p['ORIENTATION'];
16825		}
16826		$this->page_box['outer_width_LR'] = $outer_width_LR; // Used in MARKS:crop etc.
16827		$this->page_box['outer_width_TB'] = $outer_width_TB;
16828
16829		return [$orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $header, $footer, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat];
16830	}
16831
16832	/* -- END CSS-PAGE -- */
16833
16834
16835
16836	/* -- CSS-FLOAT -- */
16837
16838	// Added mPDF 3.0 Float DIV - CLEAR
16839	function ClearFloats($clear, $blklvl = 0)
16840	{
16841		list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($blklvl, true);
16842		$end = $currpos = ($this->page * 1000 + $this->y);
16843		if ($clear == 'BOTH' && ($l_exists || $r_exists)) {
16844			$this->pageoutput[$this->page] = [];
16845			$end = max($l_max, $r_max, $currpos);
16846		} elseif ($clear == 'RIGHT' && $r_exists) {
16847			$this->pageoutput[$this->page] = [];
16848			$end = max($r_max, $currpos);
16849		} elseif ($clear == 'LEFT' && $l_exists) {
16850			$this->pageoutput[$this->page] = [];
16851			$end = max($l_max, $currpos);
16852		} else {
16853			return;
16854		}
16855		$old_page = $this->page;
16856		$new_page = intval($end / 1000);
16857		if ($old_page != $new_page) {
16858			$s = $this->PrintPageBackgrounds();
16859			// Writes after the marker so not overwritten later by page background etc.
16860			$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
16861			$this->pageBackgrounds = [];
16862			$this->page = $new_page;
16863		}
16864		$this->ResetMargins();
16865		$this->pageoutput[$this->page] = [];
16866		$this->y = (($end * 1000) % 1000000) / 1000; // mod changes operands to integers before processing
16867	}
16868
16869	// Added mPDF 3.0 Float DIV
16870	function GetFloatDivInfo($blklvl = 0, $clear = false)
16871	{
16872		// If blklvl specified, only returns floats at that level - for ClearFloats
16873		$l_exists = false;
16874		$r_exists = false;
16875		$l_max = 0;
16876		$r_max = 0;
16877		$l_width = 0;
16878		$r_width = 0;
16879		if (count($this->floatDivs)) {
16880			$currpos = ($this->page * 1000 + $this->y);
16881			foreach ($this->floatDivs as $f) {
16882				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'])) {
16883					if ($f['side'] == 'L') {
16884						$l_exists = true;
16885						$l_max = max($l_max, $f['endpos']);
16886						$l_width = max($l_width, $f['w']);
16887					}
16888					if ($f['side'] == 'R') {
16889						$r_exists = true;
16890						$r_max = max($r_max, $f['endpos']);
16891						$r_width = max($r_width, $f['w']);
16892					}
16893				}
16894			}
16895		}
16896		return [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width];
16897	}
16898
16899	/* -- END CSS-FLOAT -- */
16900
16901	// LIST MARKERS	// mPDF 6  Lists
16902	function _setListMarker($listitemtype, $listitemimage, $listitemposition)
16903	{
16904		// if position:inside (and NOT table) - output now as a textbuffer; (so if next is block, will move to new line)
16905		// elseif position:outside (and NOT table) - output in front of first textbuffer output by setting listitem (cf. _saveTextBuffer)
16906		$e = '';
16907		$this->listitem = '';
16908		$spacer = ' ';
16909		// IMAGE
16910		if ($listitemimage && $listitemimage != 'none') {
16911			$listitemimage = trim(preg_replace('/url\(["\']*(.*?)["\']*\)/', '\\1', $listitemimage));
16912
16913			// ? Restrict maximum height/width of list marker??
16914			$maxWidth = 100;
16915			$maxHeight = 100;
16916
16917			$objattr = [];
16918			$objattr['margin_top'] = 0;
16919			$objattr['margin_bottom'] = 0;
16920			$objattr['margin_left'] = 0;
16921			$objattr['margin_right'] = 0;
16922			$objattr['padding_top'] = 0;
16923			$objattr['padding_bottom'] = 0;
16924			$objattr['padding_left'] = 0;
16925			$objattr['padding_right'] = 0;
16926			$objattr['width'] = 0;
16927			$objattr['height'] = 0;
16928			$objattr['border_top']['w'] = 0;
16929			$objattr['border_bottom']['w'] = 0;
16930			$objattr['border_left']['w'] = 0;
16931			$objattr['border_right']['w'] = 0;
16932			$objattr['visibility'] = 'visible';
16933			$srcpath = $listitemimage;
16934			$orig_srcpath = $listitemimage;
16935
16936			$objattr['vertical-align'] = 'BS'; // vertical alignment of marker (baseline)
16937			$w = 0;
16938			$h = 0;
16939
16940			// Image file
16941			$info = $this->imageProcessor->getImage($srcpath, true, true, $orig_srcpath);
16942			if (!$info) {
16943				return;
16944			}
16945
16946			if ($info['w'] == 0 && $info['h'] == 0) {
16947				$info['h'] = $this->sizeConverter->convert('1em', $this->blk[$this->blklvl]['inner_width'], $this->FontSize, false);
16948			}
16949
16950			$objattr['file'] = $srcpath;
16951
16952			// Default width and height calculation if needed
16953			if ($w == 0 and $h == 0) {
16954				/* -- IMAGES-WMF -- */
16955				if ($info['type'] == 'wmf') {
16956					// WMF units are twips (1/20pt)
16957					// divide by 20 to get points
16958					// divide by k to get user units
16959					$w = abs($info['w']) / (20 * Mpdf::SCALE);
16960					$h = abs($info['h']) / (20 * Mpdf::SCALE);
16961				} else { 				/* -- END IMAGES-WMF -- */
16962					if ($info['type'] == 'svg') {
16963						// SVG units are pixels
16964						$w = abs($info['w']) / Mpdf::SCALE;
16965						$h = abs($info['h']) / Mpdf::SCALE;
16966					} else {
16967						// Put image at default image dpi
16968						$w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi);
16969						$h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi);
16970					}
16971				}
16972			}
16973			// IF WIDTH OR HEIGHT SPECIFIED
16974			if ($w == 0) {
16975				$w = abs($h * $info['w'] / $info['h']);
16976			}
16977			if ($h == 0) {
16978				$h = abs($w * $info['h'] / $info['w']);
16979			}
16980
16981			if ($w > $maxWidth) {
16982				$w = $maxWidth;
16983				$h = abs($w * $info['h'] / $info['w']);
16984			}
16985
16986			if ($h > $maxHeight) {
16987				$h = $maxHeight;
16988				$w = abs($h * $info['w'] / $info['h']);
16989			}
16990
16991			$objattr['type'] = 'image';
16992			$objattr['itype'] = $info['type'];
16993
16994			$objattr['orig_h'] = $info['h'];
16995			$objattr['orig_w'] = $info['w'];
16996
16997			/* -- IMAGES-WMF -- */
16998			if ($info['type'] == 'wmf') {
16999				$objattr['wmf_x'] = $info['x'];
17000				$objattr['wmf_y'] = $info['y'];
17001			} else { 			/* -- END IMAGES-WMF -- */
17002				if ($info['type'] == 'svg') {
17003					$objattr['wmf_x'] = $info['x'];
17004					$objattr['wmf_y'] = $info['y'];
17005				}
17006			}
17007
17008			$objattr['height'] = $h;
17009			$objattr['width'] = $w;
17010			$objattr['image_height'] = $h;
17011			$objattr['image_width'] = $w;
17012
17013			$objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
17014			$objattr['listmarker'] = true;
17015
17016			$objattr['listmarkerposition'] = $listitemposition;
17017
17018			$e = "\xbb\xa4\xactype=image,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
17019			$this->_saveTextBuffer($e);
17020
17021			if ($listitemposition == 'inside') {
17022				$e = $spacer;
17023				$this->_saveTextBuffer($e);
17024			}
17025		} elseif ($listitemtype == 'disc' || $listitemtype == 'circle' || $listitemtype == 'square') { // SYMBOL (needs new font)
17026			$objattr = [];
17027			$objattr['type'] = 'listmarker';
17028			$objattr['listmarkerposition'] = $listitemposition;
17029			$objattr['width'] = 0;
17030			$size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize);
17031			$objattr['size'] = $size;
17032			$objattr['offset'] = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
17033
17034			if ($listitemposition == 'inside') {
17035				$objattr['width'] = $size + $objattr['offset'];
17036			}
17037
17038			$objattr['height'] = $this->FontSize;
17039			$objattr['vertical-align'] = 'T';
17040			$objattr['text'] = '';
17041			$objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
17042			$objattr['bullet'] = $listitemtype;
17043			$objattr['colorarray'] = $this->colorarray;
17044			$objattr['fontfamily'] = $this->FontFamily;
17045			$objattr['fontsize'] = $this->FontSize;
17046			$objattr['fontsizept'] = $this->FontSizePt;
17047			$objattr['fontstyle'] = $this->FontStyle;
17048
17049			$e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
17050			$this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
17051
17052		} elseif (preg_match('/U\+([a-fA-F0-9]+)/i', $listitemtype, $m)) { // SYMBOL 2 (needs new font)
17053
17054			if ($this->_charDefined($this->CurrentFont['cw'], hexdec($m[1]))) {
17055				$list_item_marker = UtfString::codeHex2utf($m[1]);
17056			} else {
17057				$list_item_marker = '-';
17058			}
17059			if (preg_match('/rgb\(.*?\)/', $listitemtype, $m)) {
17060				$list_item_color = $this->colorConverter->convert($m[0], $this->PDFAXwarnings);
17061			} else {
17062				$list_item_color = '';
17063			}
17064
17065			// SAVE then SET COLR
17066			$save_colorarray = $this->colorarray;
17067			if ($list_item_color) {
17068				$this->colorarray = $list_item_color;
17069			}
17070
17071			if ($listitemposition == 'inside') {
17072				$e = $list_item_marker . $spacer;
17073				$this->_saveTextBuffer($e);
17074			} else {
17075				$objattr = [];
17076				$objattr['type'] = 'listmarker';
17077				$objattr['width'] = 0;
17078				$objattr['height'] = $this->FontSize;
17079				$objattr['vertical-align'] = 'T';
17080				$objattr['text'] = $list_item_marker;
17081				$objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
17082				$objattr['colorarray'] = $this->colorarray;
17083				$objattr['fontfamily'] = $this->FontFamily;
17084				$objattr['fontsize'] = $this->FontSize;
17085				$objattr['fontsizept'] = $this->FontSizePt;
17086				$objattr['fontstyle'] = $this->FontStyle;
17087				$e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
17088				$this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
17089			}
17090
17091			// RESET COLOR
17092			$this->colorarray = $save_colorarray;
17093
17094		} else { // TEXT
17095			$counter = $this->listcounter[$this->listlvl];
17096
17097			if ($listitemtype == 'none') {
17098				return;
17099			}
17100
17101			$num = $this->_getStyledNumber($counter, $listitemtype, true);
17102
17103			if ($listitemposition == 'inside') {
17104				$e = $num . $this->list_number_suffix . $spacer;
17105				$this->_saveTextBuffer($e);
17106			} else {
17107				if (isset($this->blk[$this->blklvl]['direction']) && $this->blk[$this->blklvl]['direction'] == 'rtl') {
17108					// REPLACE MIRRORED RTL $this->list_number_suffix  e.g. ) -> (  (NB could use Ucdn::$mirror_pairs)
17109					$m = strtr($this->list_number_suffix, ")]}", "([{") . $num;
17110				} else {
17111					$m = $num . $this->list_number_suffix;
17112				}
17113
17114				$objattr = [];
17115				$objattr['type'] = 'listmarker';
17116				$objattr['width'] = 0;
17117				$objattr['height'] = $this->FontSize;
17118				$objattr['vertical-align'] = 'T';
17119				$objattr['text'] = $m;
17120				$objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
17121				$objattr['colorarray'] = $this->colorarray;
17122				$objattr['fontfamily'] = $this->FontFamily;
17123				$objattr['fontsize'] = $this->FontSize;
17124				$objattr['fontsizept'] = $this->FontSizePt;
17125				$objattr['fontstyle'] = $this->FontStyle;
17126				$e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
17127
17128				$this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
17129			}
17130		}
17131	}
17132
17133	// mPDF Lists
17134	function _getListMarkerWidth(&$currblk, &$a, &$i)
17135	{
17136		$blt_width = 0;
17137
17138		$markeroffset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
17139
17140		// Get Maximum number in the list
17141		$maxnum = $this->listcounter[$this->listlvl];
17142		if ($currblk['list_style_type'] != 'disc' && $currblk['list_style_type'] != 'circle' && $currblk['list_style_type'] != 'square') {
17143			$lvl = 1;
17144			for ($j = $i + 2; $j < count($a); $j+=2) {
17145				$e = $a[$j];
17146				if (!$e) {
17147					continue;
17148				}
17149				if ($e[0] == '/') { // end tag
17150					$e = strtoupper(substr($e, 1));
17151					if ($e == 'OL' || $e == 'UL') {
17152						if ($lvl == 1) {
17153							break;
17154						}
17155						$lvl--;
17156					}
17157				} else { // opening tag
17158					if (strpos($e, ' ')) {
17159						$e = substr($e, 0, strpos($e, ' '));
17160					}
17161					$e = strtoupper($e);
17162					if ($e == 'LI') {
17163						if ($lvl == 1) {
17164							$maxnum++;
17165						}
17166					} elseif ($e == 'OL' || $e == 'UL') {
17167						$lvl++;
17168					}
17169				}
17170			}
17171		}
17172
17173		$decToAlpha = new Conversion\DecToAlpha();
17174		$decToRoman = new Conversion\DecToRoman();
17175		$decToOther = new Conversion\DecToOther($this);
17176
17177		switch ($currblk['list_style_type']) {
17178			case 'decimal':
17179			case '1':
17180				$blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);
17181				break;
17182			case 'none':
17183				$blt_width = 0;
17184				break;
17185			case 'upper-alpha':
17186			case 'upper-latin':
17187			case 'A':
17188				$maxnumA = $decToAlpha->convert($maxnum, true);
17189				if ($maxnum < 13) {
17190					$blt_width = $this->GetStringWidth('D' . $this->list_number_suffix);
17191				} else {
17192					$blt_width = $this->GetStringWidth(str_repeat('W', strlen($maxnumA)) . $this->list_number_suffix);
17193				}
17194				break;
17195			case 'lower-alpha':
17196			case 'lower-latin':
17197			case 'a':
17198				$maxnuma = $decToAlpha->convert($maxnum, false);
17199				if ($maxnum < 13) {
17200					$blt_width = $this->GetStringWidth('b' . $this->list_number_suffix);
17201				} else {
17202					$blt_width = $this->GetStringWidth(str_repeat('m', strlen($maxnuma)) . $this->list_number_suffix);
17203				}
17204				break;
17205			case 'upper-roman':
17206			case 'I':
17207				if ($maxnum > 87) {
17208					$bbit = 87;
17209				} elseif ($maxnum > 86) {
17210					$bbit = 86;
17211				} elseif ($maxnum > 37) {
17212					$bbit = 38;
17213				} elseif ($maxnum > 36) {
17214					$bbit = 37;
17215				} elseif ($maxnum > 27) {
17216					$bbit = 28;
17217				} elseif ($maxnum > 26) {
17218					$bbit = 27;
17219				} elseif ($maxnum > 17) {
17220					$bbit = 18;
17221				} elseif ($maxnum > 16) {
17222					$bbit = 17;
17223				} elseif ($maxnum > 7) {
17224					$bbit = 8;
17225				} elseif ($maxnum > 6) {
17226					$bbit = 7;
17227				} elseif ($maxnum > 3) {
17228					$bbit = 4;
17229				} else {
17230					$bbit = $maxnum;
17231				}
17232
17233				$maxlnum = $decToRoman->convert($bbit, true);
17234				$blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);
17235
17236				break;
17237			case 'lower-roman':
17238			case 'i':
17239				if ($maxnum > 87) {
17240					$bbit = 87;
17241				} elseif ($maxnum > 86) {
17242					$bbit = 86;
17243				} elseif ($maxnum > 37) {
17244					$bbit = 38;
17245				} elseif ($maxnum > 36) {
17246					$bbit = 37;
17247				} elseif ($maxnum > 27) {
17248					$bbit = 28;
17249				} elseif ($maxnum > 26) {
17250					$bbit = 27;
17251				} elseif ($maxnum > 17) {
17252					$bbit = 18;
17253				} elseif ($maxnum > 16) {
17254					$bbit = 17;
17255				} elseif ($maxnum > 7) {
17256					$bbit = 8;
17257				} elseif ($maxnum > 6) {
17258					$bbit = 7;
17259				} elseif ($maxnum > 3) {
17260					$bbit = 4;
17261				} else {
17262					$bbit = $maxnum;
17263				}
17264				$maxlnum = $decToRoman->convert($bbit, false);
17265				$blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);
17266				break;
17267
17268			case 'disc':
17269			case 'circle':
17270			case 'square':
17271				$size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize);
17272				$offset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);
17273				$blt_width = $size + $offset;
17274				break;
17275
17276			case 'arabic-indic':
17277				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0660), strlen($maxnum)) . $this->list_number_suffix);
17278				break;
17279			case 'persian':
17280			case 'urdu':
17281				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x06F0), strlen($maxnum)) . $this->list_number_suffix);
17282				break;
17283			case 'bengali':
17284				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x09E6), strlen($maxnum)) . $this->list_number_suffix);
17285				break;
17286			case 'devanagari':
17287				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0966), strlen($maxnum)) . $this->list_number_suffix);
17288				break;
17289			case 'gujarati':
17290				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0AE6), strlen($maxnum)) . $this->list_number_suffix);
17291				break;
17292			case 'gurmukhi':
17293				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0A66), strlen($maxnum)) . $this->list_number_suffix);
17294				break;
17295			case 'kannada':
17296				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0CE6), strlen($maxnum)) . $this->list_number_suffix);
17297				break;
17298			case 'malayalam':
17299				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(6, 0x0D66), strlen($maxnum)) . $this->list_number_suffix);
17300				break;
17301			case 'oriya':
17302				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0B66), strlen($maxnum)) . $this->list_number_suffix);
17303				break;
17304			case 'telugu':
17305				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0C66), strlen($maxnum)) . $this->list_number_suffix);
17306				break;
17307			case 'tamil':
17308				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(9, 0x0BE6), strlen($maxnum)) . $this->list_number_suffix);
17309				break;
17310			case 'thai':
17311				$blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(5, 0x0E50), strlen($maxnum)) . $this->list_number_suffix);
17312				break;
17313			default:
17314				$blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);
17315				break;
17316		}
17317
17318		return ($blt_width + $markeroffset);
17319	}
17320
17321	/* -- TABLES -- */
17322
17323	// This function determines the shrink factor when resizing tables
17324	// val is the table_height / page_height_available
17325	// returns a scaling factor used as $shrin_k to resize the table
17326	// Overcompensating will be quicker but may unnecessarily shrink table too much
17327	// Undercompensating means it will reiterate more times (taking more processing time)
17328	function tbsqrt($val, $iteration = 3)
17329	{
17330		$k = 4; // Alters number of iterations until it returns $val itself - Must be > 2
17331		// Probably best guess and most accurate
17332		if ($iteration == 1) {
17333			return sqrt($val);
17334		}
17335		// Faster than using sqrt (because it won't undercompensate), and gives reasonable results
17336		// return 1+(($val-1)/2);
17337		$x = 2 - (($iteration - 2) / ($k - 2));
17338		if ($x == 0) {
17339			$ret = $val + 0.00001;
17340		} elseif ($x < 0) {
17341			$ret = 1 + ( pow(2, ($iteration - 2 - $k)) / 1000 );
17342		} else {
17343			$ret = 1 + (($val - 1) / $x);
17344		}
17345		return $ret;
17346	}
17347
17348	/* -- END TABLES -- */
17349
17350	function _saveTextBuffer($t, $link = '', $intlink = '', $return = false)
17351	{
17352	// mPDF 6  Lists
17353		$arr = [];
17354		$arr[0] = $t;
17355		if (isset($link) && $link) {
17356			$arr[1] = $link;
17357		}
17358		$arr[2] = $this->currentfontstyle;
17359		if (isset($this->colorarray) && $this->colorarray) {
17360			$arr[3] = $this->colorarray;
17361		}
17362		$arr[4] = $this->currentfontfamily;
17363		$arr[5] = $this->currentLang; // mPDF 6
17364		if (isset($intlink) && $intlink) {
17365			$arr[7] = $intlink;
17366		}
17367		// mPDF 6
17368		// If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,
17369		// set for kerning via kern table
17370		// e.g. Latin script when useOTL set as 0x80
17371		if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {
17372			$this->textvar = ($this->textvar | TextVars::FC_KERNING);
17373		}
17374		$arr[8] = $this->textvar; // mPDF 5.7.1
17375		if (isset($this->textparam) && $this->textparam) {
17376			$arr[9] = $this->textparam;
17377		}
17378		if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) {
17379			$arr[10] = $this->spanbgcolorarray;
17380		}
17381		$arr[11] = $this->currentfontsize;
17382		if (isset($this->ReqFontStyle) && $this->ReqFontStyle) {
17383			$arr[12] = $this->ReqFontStyle;
17384		}
17385		if (isset($this->lSpacingCSS) && $this->lSpacingCSS) {
17386			$arr[14] = $this->lSpacingCSS;
17387		}
17388		if (isset($this->wSpacingCSS) && $this->wSpacingCSS) {
17389			$arr[15] = $this->wSpacingCSS;
17390		}
17391		if (isset($this->spanborddet) && $this->spanborddet) {
17392			$arr[16] = $this->spanborddet;
17393		}
17394		if (isset($this->textshadow) && $this->textshadow) {
17395			$arr[17] = $this->textshadow;
17396		}
17397		if (isset($this->OTLdata) && $this->OTLdata) {
17398			$arr[18] = $this->OTLdata;
17399			$this->OTLdata = [];
17400		} // mPDF 5.7.1
17401		else {
17402			$arr[18] = null;
17403		}
17404		// mPDF 6  Lists
17405		if ($return) {
17406			return ($arr);
17407		}
17408		if ($this->listitem) {
17409			$this->textbuffer[] = $this->listitem;
17410			$this->listitem = [];
17411		}
17412		$this->textbuffer[] = $arr;
17413	}
17414
17415	function _saveCellTextBuffer($t, $link = '', $intlink = '')
17416	{
17417		$arr = [];
17418		$arr[0] = $t;
17419		if (isset($link) && $link) {
17420			$arr[1] = $link;
17421		}
17422		$arr[2] = $this->currentfontstyle;
17423		if (isset($this->colorarray) && $this->colorarray) {
17424			$arr[3] = $this->colorarray;
17425		}
17426		$arr[4] = $this->currentfontfamily;
17427		if (isset($intlink) && $intlink) {
17428			$arr[7] = $intlink;
17429		}
17430		// mPDF 6
17431		// If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,
17432		// set for kerning via kern table
17433		// e.g. Latin script when useOTL set as 0x80
17434		if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {
17435			$this->textvar = ($this->textvar | TextVars::FC_KERNING);
17436		}
17437		$arr[8] = $this->textvar; // mPDF 5.7.1
17438		if (isset($this->textparam) && $this->textparam) {
17439			$arr[9] = $this->textparam;
17440		}
17441		if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) {
17442			$arr[10] = $this->spanbgcolorarray;
17443		}
17444		$arr[11] = $this->currentfontsize;
17445		if (isset($this->ReqFontStyle) && $this->ReqFontStyle) {
17446			$arr[12] = $this->ReqFontStyle;
17447		}
17448		if (isset($this->lSpacingCSS) && $this->lSpacingCSS) {
17449			$arr[14] = $this->lSpacingCSS;
17450		}
17451		if (isset($this->wSpacingCSS) && $this->wSpacingCSS) {
17452			$arr[15] = $this->wSpacingCSS;
17453		}
17454		if (isset($this->spanborddet) && $this->spanborddet) {
17455			$arr[16] = $this->spanborddet;
17456		}
17457		if (isset($this->textshadow) && $this->textshadow) {
17458			$arr[17] = $this->textshadow;
17459		}
17460		if (isset($this->OTLdata) && $this->OTLdata) {
17461			$arr[18] = $this->OTLdata;
17462			$this->OTLdata = [];
17463		} // mPDF 5.7.1
17464		else {
17465			$arr[18] = null;
17466		}
17467		$this->cell[$this->row][$this->col]['textbuffer'][] = $arr;
17468	}
17469
17470	function printbuffer($arrayaux, $blockstate = 0, $is_table = false, $table_draft = false, $cell_dir = '')
17471	{
17472		// $blockstate = 0;	// NO margins/padding
17473		// $blockstate = 1;	// Top margins/padding only
17474		// $blockstate = 2;	// Bottom margins/padding only
17475		// $blockstate = 3;	// Top & bottom margins/padding
17476		$this->spanbgcolorarray = '';
17477		$this->spanbgcolor = false;
17478		$this->spanborder = false;
17479		$this->spanborddet = [];
17480		$paint_ht_corr = 0;
17481		/* -- CSS-FLOAT -- */
17482		if (count($this->floatDivs)) {
17483			list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
17484			if (($this->blk[$this->blklvl]['inner_width'] - $l_width - $r_width) < (2 * $this->GetCharWidth('W', false))) {
17485				// Too narrow to fit - try to move down past L or R float
17486				if ($l_max < $r_max && ($this->blk[$this->blklvl]['inner_width'] - $r_width) > (2 * $this->GetCharWidth('W', false))) {
17487					$this->ClearFloats('LEFT', $this->blklvl);
17488				} elseif ($r_max < $l_max && ($this->blk[$this->blklvl]['inner_width'] - $l_width) > (2 * $this->GetCharWidth('W', false))) {
17489					$this->ClearFloats('RIGHT', $this->blklvl);
17490				} else {
17491					$this->ClearFloats('BOTH', $this->blklvl);
17492				}
17493			}
17494		}
17495		/* -- END CSS-FLOAT -- */
17496		$bak_y = $this->y;
17497		$bak_x = $this->x;
17498		$align = '';
17499		if (!$is_table) {
17500			if (isset($this->blk[$this->blklvl]['align']) && $this->blk[$this->blklvl]['align']) {
17501				$align = $this->blk[$this->blklvl]['align'];
17502			}
17503			// Block-align is set by e.g. <.. align="center"> Takes priority for this block but not inherited
17504			if (isset($this->blk[$this->blklvl]['block-align']) && $this->blk[$this->blklvl]['block-align']) {
17505				$align = $this->blk[$this->blklvl]['block-align'];
17506			}
17507			if (isset($this->blk[$this->blklvl]['direction'])) {
17508				$blockdir = $this->blk[$this->blklvl]['direction'];
17509			} else {
17510				$blockdir = "";
17511			}
17512			$this->divwidth = $this->blk[$this->blklvl]['width'];
17513		} else {
17514			$align = $this->cellTextAlign;
17515			$blockdir = $cell_dir;
17516		}
17517		$oldpage = $this->page;
17518
17519		// ADDED for Out of Block now done as Flowing Block
17520		if ($this->divwidth == 0) {
17521			$this->divwidth = $this->pgwidth;
17522		}
17523
17524		if (!$is_table) {
17525			$this->SetLineHeight($this->FontSizePt, $this->blk[$this->blklvl]['line_height']);
17526		}
17527		$this->divheight = $this->lineheight;
17528		$old_height = $this->divheight;
17529
17530		// As a failsafe - if font has been set but not output to page
17531		if (!$table_draft) {
17532			$this->SetFont($this->default_font, '', $this->default_font_size, true, true); // force output to page
17533		}
17534
17535		$this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, true, $blockdir, $table_draft);
17536
17537		$array_size = count($arrayaux);
17538
17539		// Added - Otherwise <div><div><p> did not output top margins/padding for 1st/2nd div
17540		if ($array_size == 0) {
17541			$this->finishFlowingBlock(true);
17542		} // true = END of flowing block
17543		// mPDF 6
17544		// ALL the chunks of textbuffer need to have at least basic OTLdata set
17545		// First make sure each element/chunk has the OTLdata for Bidi set.
17546		for ($i = 0; $i < $array_size; $i++) {
17547			if (empty($arrayaux[$i][18])) {
17548				if (substr($arrayaux[$i][0], 0, 3) == "\xbb\xa4\xac") { // object identifier has been identified!
17549					$unicode = [0xFFFC]; // Object replacement character
17550				} else {
17551					$unicode = $this->UTF8StringToArray($arrayaux[$i][0], false);
17552				}
17553				$is_strong = false;
17554				$this->getBasicOTLdata($arrayaux[$i][18], $unicode, $is_strong);
17555			}
17556			// Gets messed up if try and use core fonts inside a paragraph of text which needs to be BiDi re-ordered or OTLdata set
17557			if (($blockdir == 'rtl' || $this->biDirectional) && isset($arrayaux[$i][4]) && in_array($arrayaux[$i][4], ['ccourier', 'ctimes', 'chelvetica', 'csymbol', 'czapfdingbats'])) {
17558				throw new \Mpdf\MpdfException("You cannot use core fonts in a document which contains RTL text.");
17559			}
17560		}
17561		// mPDF 6
17562		// Process bidirectional text ready for bidi-re-ordering (which is done after line-breaks are established in WriteFlowingBlock etc.)
17563		if (($blockdir == 'rtl' || $this->biDirectional) && !$table_draft) {
17564			if (empty($this->otl)) {
17565				$this->otl = new Otl($this, $this->fontCache);
17566			}
17567			$this->otl->bidiPrepare($arrayaux, $blockdir);
17568			$array_size = count($arrayaux);
17569		}
17570
17571
17572		// Remove empty items // mPDF 6
17573		for ($i = $array_size - 1; $i > 0; $i--) {
17574			if (empty($arrayaux[$i][0]) && (isset($arrayaux[$i][16]) && $arrayaux[$i][16] !== '0') && empty($arrayaux[$i][7])) {
17575				unset($arrayaux[$i]);
17576			}
17577		}
17578
17579		// Correct adjoining borders for inline elements
17580		if (isset($arrayaux[0][16])) {
17581			$lastspanborder = $arrayaux[0][16];
17582		} else {
17583			$lastspanborder = false;
17584		}
17585		for ($i = 1; $i < $array_size; $i++) {
17586			if (isset($arrayaux[$i][16]) && $arrayaux[$i][16] == $lastspanborder &&
17587				((!isset($arrayaux[$i][9]['bord-decoration']) && !isset($arrayaux[$i - 1][9]['bord-decoration'])) ||
17588				(isset($arrayaux[$i][9]['bord-decoration']) && isset($arrayaux[$i - 1][9]['bord-decoration']) && $arrayaux[$i][9]['bord-decoration'] == $arrayaux[$i - 1][9]['bord-decoration'])
17589				)
17590			) {
17591				if (isset($arrayaux[$i][16]['R'])) {
17592					$lastspanborder = $arrayaux[$i][16];
17593				} else {
17594					$lastspanborder = false;
17595				}
17596				$arrayaux[$i][16]['L']['s'] = 0;
17597				$arrayaux[$i][16]['L']['w'] = 0;
17598				$arrayaux[$i - 1][16]['R']['s'] = 0;
17599				$arrayaux[$i - 1][16]['R']['w'] = 0;
17600			} else {
17601				if (isset($arrayaux[$i][16]['R'])) {
17602					$lastspanborder = $arrayaux[$i][16];
17603				} else {
17604					$lastspanborder = false;
17605				}
17606			}
17607		}
17608
17609		for ($i = 0; $i < $array_size; $i++) {
17610			// COLS
17611			$oldcolumn = $this->CurrCol;
17612			$vetor = isset($arrayaux[$i]) ? $arrayaux[$i] : null;
17613			if ($i == 0 && $vetor[0] != "\n" && ! $this->ispre) {
17614				$vetor[0] = ltrim($vetor[0]);
17615				if (!empty($vetor[18])) {
17616					$this->otl->trimOTLdata($vetor[18], true, false);
17617				} // *OTL*
17618			}
17619
17620			// FIXED TO ALLOW IT TO SHOW '0'
17621			if (empty($vetor[0]) && !($vetor[0] === '0') && empty($vetor[7])) { // Ignore empty text and not carrying an internal link
17622				// Check if it is the last element. If so then finish printing the block
17623				if ($i == ($array_size - 1)) {
17624					$this->finishFlowingBlock(true);
17625				} // true = END of flowing block
17626				continue;
17627			}
17628
17629
17630			// Activating buffer properties
17631			if (isset($vetor[11]) && $vetor[11] != '') {   // Font Size
17632				if ($is_table && $this->shrin_k) {
17633					$this->SetFontSize($vetor[11] / $this->shrin_k, false);
17634				} else {
17635					$this->SetFontSize($vetor[11], false);
17636				}
17637			}
17638
17639			if (isset($vetor[17]) && !empty($vetor[17])) { // TextShadow
17640				$this->textshadow = $vetor[17];
17641			}
17642			if (isset($vetor[16]) && !empty($vetor[16])) { // Border
17643				$this->spanborddet = $vetor[16];
17644				$this->spanborder = true;
17645			}
17646
17647			if (isset($vetor[15])) {   // Word spacing
17648				$this->wSpacingCSS = $vetor[15];
17649				if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
17650					$this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
17651				}
17652			}
17653			if (isset($vetor[14])) {   // Letter spacing
17654				$this->lSpacingCSS = $vetor[14];
17655				if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
17656					$this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
17657				}
17658			}
17659
17660
17661			if (isset($vetor[10]) and ! empty($vetor[10])) { // Background color
17662				$this->spanbgcolorarray = $vetor[10];
17663				$this->spanbgcolor = true;
17664			}
17665			if (isset($vetor[9]) and ! empty($vetor[9])) { // Text parameters - Outline + hyphens
17666				$this->textparam = $vetor[9];
17667				$this->SetTextOutline($this->textparam);
17668				// mPDF 5.7.3  inline text-decoration parameters
17669				if ($is_table && $this->shrin_k) {
17670					if (isset($this->textparam['text-baseline'])) {
17671						$this->textparam['text-baseline'] /= $this->shrin_k;
17672					}
17673					if (isset($this->textparam['decoration-baseline'])) {
17674						$this->textparam['decoration-baseline'] /= $this->shrin_k;
17675					}
17676					if (isset($this->textparam['decoration-fontsize'])) {
17677						$this->textparam['decoration-fontsize'] /= $this->shrin_k;
17678					}
17679				}
17680			}
17681			if (isset($vetor[8])) {  // mPDF 5.7.1
17682				$this->textvar = $vetor[8];
17683			}
17684			if (isset($vetor[7]) and $vetor[7] != '') { // internal target: <a name="anyvalue">
17685				$ily = $this->y;
17686				if ($this->table_rotate) {
17687					$this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "tbrot" => true];
17688				} elseif ($this->kwt) {
17689					$this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "kwt" => true];
17690				} elseif ($this->ColActive) {
17691					$this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol];
17692				} elseif (!$this->keep_block_together) {
17693					$this->internallink[$vetor[7]] = ["Y" => $ily, "PAGE" => $this->page];
17694				}
17695				if (empty($vetor[0])) { // Ignore empty text
17696					// Check if it is the last element. If so then finish printing the block
17697					if ($i == ($array_size - 1)) {
17698						$this->finishFlowingBlock(true);
17699					} // true = END of flowing block
17700					continue;
17701				}
17702			}
17703			if (isset($vetor[5]) and $vetor[5] != '') {  // Language	// mPDF 6
17704				$this->currentLang = $vetor[5];
17705			}
17706			if (isset($vetor[4]) and $vetor[4] != '') {  // Font Family
17707				$font = $this->SetFont($vetor[4], $this->FontStyle, 0, false);
17708			}
17709			if (!empty($vetor[3])) { // Font Color
17710				$cor = $vetor[3];
17711				$this->SetTColor($cor);
17712			}
17713			if (isset($vetor[2]) and $vetor[2] != '') { // Bold,Italic styles
17714				$this->SetStyles($vetor[2]);
17715			}
17716
17717			if (isset($vetor[12]) and $vetor[12] != '') { // Requested Bold,Italic
17718				$this->ReqFontStyle = $vetor[12];
17719			}
17720			if (isset($vetor[1]) and $vetor[1] != '') { // LINK
17721				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.)
17722					// Repeated reference to same anchor?
17723					while (array_key_exists($vetor[1], $this->internallink)) {
17724						$vetor[1] = "#" . $vetor[1];
17725					}
17726					$this->internallink[$vetor[1]] = $this->AddLink();
17727					$vetor[1] = $this->internallink[$vetor[1]];
17728				}
17729				$this->HREF = $vetor[1];     // HREF link style set here ******
17730			}
17731
17732			// SPECIAL CONTENT - IMAGES & FORM OBJECTS
17733			// Print-out special content
17734
17735			if (substr($vetor[0], 0, 3) == "\xbb\xa4\xac") { // identifier has been identified!
17736				$objattr = $this->_getObjAttr($vetor[0]);
17737
17738				/* -- TABLES -- */
17739				if ($objattr['type'] == 'nestedtable') {
17740					if ($objattr['nestedcontent']) {
17741						$level = $objattr['level'];
17742						$table = &$this->table[$level][$objattr['table']];
17743
17744						if ($table_draft) {
17745							$this->y += $this->table[($level + 1)][$objattr['nestedcontent']]['h']; // nested table height
17746							$this->finishFlowingBlock(false, 'nestedtable');
17747						} else {
17748							$cell = &$table['cells'][$objattr['row']][$objattr['col']];
17749							$this->finishFlowingBlock(false, 'nestedtable');
17750							$save_dw = $this->divwidth;
17751							$save_buffer = $this->cellBorderBuffer;
17752							$this->cellBorderBuffer = [];
17753							$ncx = $this->x;
17754							list($dummyx, $w) = $this->_tableGetWidth($table, $objattr['row'], $objattr['col']);
17755							$ntw = $this->table[($level + 1)][$objattr['nestedcontent']]['w']; // nested table width
17756							if (!$this->simpleTables) {
17757								if ($this->packTableData) {
17758									list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cell['borderbin']);
17759								} else {
17760									$br = $cell['border_details']['R']['w'];
17761									$bl = $cell['border_details']['L']['w'];
17762								}
17763								if ($table['borders_separate']) {
17764									$innerw = $w - $bl - $br - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];
17765								} else {
17766									$innerw = $w - $bl / 2 - $br / 2 - $cell['padding']['L'] - $cell['padding']['R'];
17767								}
17768							} elseif ($this->simpleTables) {
17769								if ($table['borders_separate']) {
17770									$innerw = $w - $table['simple']['border_details']['L']['w'] - $table['simple']['border_details']['R']['w'] - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];
17771								} else {
17772									$innerw = $w - $table['simple']['border_details']['L']['w'] / 2 - $table['simple']['border_details']['R']['w'] / 2 - $cell['padding']['L'] - $cell['padding']['R'];
17773								}
17774							}
17775							if ($cell['a'] == 'C' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'C') {
17776								$ncx += ($innerw - $ntw) / 2;
17777							} elseif ($cell['a'] == 'R' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'R') {
17778								$ncx += $innerw - $ntw;
17779							}
17780							$this->x = $ncx;
17781
17782							$this->_tableWrite($this->table[($level + 1)][$objattr['nestedcontent']]);
17783							$this->cellBorderBuffer = $save_buffer;
17784							$this->x = $bak_x;
17785							$this->divwidth = $save_dw;
17786						}
17787
17788						$this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
17789					}
17790				} else {
17791					/* -- END TABLES -- */
17792					if ($is_table) { // *TABLES*
17793						$maxWidth = $this->divwidth;  // *TABLES*
17794					} // *TABLES*
17795					else { // *TABLES*
17796						$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']);
17797					} // *TABLES*
17798
17799					/* -- CSS-IMAGE-FLOAT -- */
17800					// If float (already) exists at this level
17801					if (isset($this->floatmargins['R']) && $this->y <= $this->floatmargins['R']['y1'] && $this->y >= $this->floatmargins['R']['y0']) {
17802						$maxWidth -= $this->floatmargins['R']['w'];
17803					}
17804					if (isset($this->floatmargins['L']) && $this->y <= $this->floatmargins['L']['y1'] && $this->y >= $this->floatmargins['L']['y0']) {
17805						$maxWidth -= $this->floatmargins['L']['w'];
17806					}
17807					/* -- END CSS-IMAGE-FLOAT -- */
17808
17809					list($skipln) = $this->inlineObject($objattr['type'], '', $this->y, $objattr, $this->lMargin, ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE), $maxWidth, $this->flowingBlockAttr['height'], false, $is_table);
17810					//  1 -> New line needed because of width
17811					// -1 -> Will fit width on line but NEW PAGE REQUIRED because of height
17812					// -2 -> Will not fit on line therefore needs new line but thus NEW PAGE REQUIRED
17813					$iby = $this->y;
17814					$oldpage = $this->page;
17815					$oldcol = $this->CurrCol;
17816					if (($skipln == 1 || $skipln == -2) && !isset($objattr['float'])) {
17817						$this->finishFlowingBlock(false, $objattr['type']);
17818						$this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
17819					}
17820
17821					if (!$table_draft) {
17822						$thispage = $this->page;
17823						if ($this->CurrCol != $oldcol) {
17824							$changedcol = true;
17825						} else {
17826							$changedcol = false;
17827						}
17828
17829						// the previous lines can already have triggered page break or column change
17830						if (!$changedcol && $skipln < 0 && $this->AcceptPageBreak() && $thispage == $oldpage) {
17831							$this->AddPage($this->CurOrientation);
17832
17833							// Added to correct Images already set on line before page advanced
17834							// i.e. if second inline image on line is higher than first and forces new page
17835							if (count($this->objectbuffer)) {
17836								$yadj = $iby - $this->y;
17837								foreach ($this->objectbuffer as $ib => $val) {
17838									if ($this->objectbuffer[$ib]['OUTER-Y']) {
17839										$this->objectbuffer[$ib]['OUTER-Y'] -= $yadj;
17840									}
17841									if ($this->objectbuffer[$ib]['BORDER-Y']) {
17842										$this->objectbuffer[$ib]['BORDER-Y'] -= $yadj;
17843									}
17844									if ($this->objectbuffer[$ib]['INNER-Y']) {
17845										$this->objectbuffer[$ib]['INNER-Y'] -= $yadj;
17846									}
17847								}
17848							}
17849						}
17850
17851						// Added to correct for OddEven Margins
17852						if ($this->page != $oldpage) {
17853							if (($this->page - $oldpage) % 2 == 1) {
17854								$bak_x += $this->MarginCorrection;
17855							}
17856							$oldpage = $this->page;
17857							$y = $this->tMargin - $paint_ht_corr;
17858							$this->oldy = $this->tMargin - $paint_ht_corr;
17859							$old_height = 0;
17860						}
17861						$this->x = $bak_x;
17862						/* -- COLUMNS -- */
17863						// COLS
17864						// OR COLUMN CHANGE
17865						if ($this->CurrCol != $oldcolumn) {
17866							if ($this->directionality == 'rtl') { // *OTL*
17867								$bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
17868							} // *OTL*
17869							else { // *OTL*
17870								$bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
17871							} // *OTL*
17872							$this->x = $bak_x;
17873							$oldcolumn = $this->CurrCol;
17874							$y = $this->y0 - $paint_ht_corr;
17875							$this->oldy = $this->y0 - $paint_ht_corr;
17876							$old_height = 0;
17877						}
17878						/* -- END COLUMNS -- */
17879					}
17880
17881					/* -- CSS-IMAGE-FLOAT -- */
17882					if ($objattr['type'] == 'image' && isset($objattr['float'])) {
17883						$fy = $this->y;
17884
17885						// DIV TOP MARGIN/BORDER/PADDING
17886						if ($this->flowingBlockAttr['newblock'] && ($this->flowingBlockAttr['blockstate'] == 1 || $this->flowingBlockAttr['blockstate'] == 3) && $this->flowingBlockAttr['lineCount'] == 0) {
17887							$fy += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
17888						}
17889
17890						if ($objattr['float'] == 'R') {
17891							$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']);
17892						} elseif ($objattr['float'] == 'L') {
17893							$fx = $this->lMargin + ($this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left']);
17894						}
17895						$w = $objattr['width'];
17896						$h = abs($objattr['height']);
17897
17898						$widthLeft = $maxWidth - ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE);
17899						$maxHeight = $this->h - ($this->tMargin + $this->margin_header + $this->bMargin + 10);
17900						// For Images
17901						$extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']);
17902						$extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']);
17903
17904						if ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg') {
17905							$file = $objattr['file'];
17906							$info = $this->formobjects[$file];
17907						} else {
17908							$file = $objattr['file'];
17909							$info = $this->images[$file];
17910						}
17911						$img_w = $w - $extraWidth;
17912						$img_h = $h - $extraHeight;
17913						if ($objattr['border_left']['w']) {
17914							$objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] + $objattr['border_right']['w']) / 2);
17915							$objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] + $objattr['border_bottom']['w']) / 2);
17916							$objattr['BORDER-X'] = $fx + $objattr['margin_left'] + (($objattr['border_left']['w']) / 2);
17917							$objattr['BORDER-Y'] = $fy + $objattr['margin_top'] + (($objattr['border_top']['w']) / 2);
17918						}
17919						$objattr['INNER-WIDTH'] = $img_w;
17920						$objattr['INNER-HEIGHT'] = $img_h;
17921						$objattr['INNER-X'] = $fx + $objattr['margin_left'] + ($objattr['border_left']['w']);
17922						$objattr['INNER-Y'] = $fy + $objattr['margin_top'] + ($objattr['border_top']['w']);
17923						$objattr['ID'] = $info['i'];
17924						$objattr['OUTER-WIDTH'] = $w;
17925						$objattr['OUTER-HEIGHT'] = $h;
17926						$objattr['OUTER-X'] = $fx;
17927						$objattr['OUTER-Y'] = $fy;
17928						if ($objattr['float'] == 'R') {
17929							// If R float already exists at this level
17930							$this->floatmargins['R']['skipline'] = false;
17931							if (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {
17932								$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
17933							} // If L float already exists at this level
17934							elseif (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {
17935								// Final check distance between floats is not now too narrow to fit text
17936								$mw = 2 * $this->GetCharWidth('W', false);
17937								if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['L']['w']) < $mw) {
17938									$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
17939								} else {
17940									$this->floatmargins['R']['x'] = $fx;
17941									$this->floatmargins['R']['w'] = $w;
17942									$this->floatmargins['R']['y0'] = $fy;
17943									$this->floatmargins['R']['y1'] = $fy + $h;
17944									if ($skipln == 1) {
17945										$this->floatmargins['R']['skipline'] = true;
17946										$this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;
17947										$objattr['skipline'] = true;
17948									}
17949									$this->floatbuffer[] = $objattr;
17950								}
17951							} else {
17952								$this->floatmargins['R']['x'] = $fx;
17953								$this->floatmargins['R']['w'] = $w;
17954								$this->floatmargins['R']['y0'] = $fy;
17955								$this->floatmargins['R']['y1'] = $fy + $h;
17956								if ($skipln == 1) {
17957									$this->floatmargins['R']['skipline'] = true;
17958									$this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;
17959									$objattr['skipline'] = true;
17960								}
17961								$this->floatbuffer[] = $objattr;
17962							}
17963						} elseif ($objattr['float'] == 'L') {
17964							// If L float already exists at this level
17965							$this->floatmargins['L']['skipline'] = false;
17966							if (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {
17967								$this->floatmargins['L']['skipline'] = false;
17968								$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
17969							} // If R float already exists at this level
17970							elseif (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {
17971								// Final check distance between floats is not now too narrow to fit text
17972								$mw = 2 * $this->GetCharWidth('W', false);
17973								if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['R']['w']) < $mw) {
17974									$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
17975								} else {
17976									$this->floatmargins['L']['x'] = $fx + $w;
17977									$this->floatmargins['L']['w'] = $w;
17978									$this->floatmargins['L']['y0'] = $fy;
17979									$this->floatmargins['L']['y1'] = $fy + $h;
17980									if ($skipln == 1) {
17981										$this->floatmargins['L']['skipline'] = true;
17982										$this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;
17983										$objattr['skipline'] = true;
17984									}
17985									$this->floatbuffer[] = $objattr;
17986								}
17987							} else {
17988								$this->floatmargins['L']['x'] = $fx + $w;
17989								$this->floatmargins['L']['w'] = $w;
17990								$this->floatmargins['L']['y0'] = $fy;
17991								$this->floatmargins['L']['y1'] = $fy + $h;
17992								if ($skipln == 1) {
17993									$this->floatmargins['L']['skipline'] = true;
17994									$this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;
17995									$objattr['skipline'] = true;
17996								}
17997								$this->floatbuffer[] = $objattr;
17998							}
17999						}
18000					} else {
18001						/* -- END CSS-IMAGE-FLOAT -- */
18002						$this->WriteFlowingBlock($vetor[0], (isset($vetor[18]) ? $vetor[18] : null));  // mPDF 5.7.1
18003						/* -- CSS-IMAGE-FLOAT -- */
18004					}
18005					/* -- END CSS-IMAGE-FLOAT -- */
18006				} // *TABLES*
18007			} // END If special content
18008			else { // THE text
18009				if ($this->tableLevel) {
18010					$paint_ht_corr = 0;
18011				} // To move the y up when new column/page started if div border needed
18012				else {
18013					$paint_ht_corr = $this->blk[$this->blklvl]['border_top']['w'];
18014				}
18015
18016				if ($vetor[0] == "\n") { // We are reading a <BR> now turned into newline ("\n")
18017					if ($this->flowingBlockAttr['content']) {
18018						$this->finishFlowingBlock(false, 'br');
18019					} elseif ($is_table) {
18020						$this->y+= $this->_computeLineheight($this->cellLineHeight);
18021					} elseif (!$is_table) {
18022						$this->DivLn($this->lineheight);
18023						if ($this->ColActive) {
18024							$this->breakpoints[$this->CurrCol][] = $this->y;
18025						} // *COLUMNS*
18026					}
18027					// Added to correct for OddEven Margins
18028					if ($this->page != $oldpage) {
18029						if (($this->page - $oldpage) % 2 == 1) {
18030							$bak_x += $this->MarginCorrection;
18031						}
18032						$oldpage = $this->page;
18033						$y = $this->tMargin - $paint_ht_corr;
18034						$this->oldy = $this->tMargin - $paint_ht_corr;
18035						$old_height = 0;
18036					}
18037					$this->x = $bak_x;
18038					/* -- COLUMNS -- */
18039					// COLS
18040					// OR COLUMN CHANGE
18041					if ($this->CurrCol != $oldcolumn) {
18042						if ($this->directionality == 'rtl') { // *OTL*
18043							$bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
18044						} // *OTL*
18045						else { // *OTL*
18046							$bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
18047						} // *OTL*
18048						$this->x = $bak_x;
18049						$oldcolumn = $this->CurrCol;
18050						$y = $this->y0 - $paint_ht_corr;
18051						$this->oldy = $this->y0 - $paint_ht_corr;
18052						$old_height = 0;
18053					}
18054					/* -- END COLUMNS -- */
18055					$this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
18056				} else {
18057					$this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1
18058					// Added to correct for OddEven Margins
18059					if ($this->page != $oldpage) {
18060						if (($this->page - $oldpage) % 2 == 1) {
18061							$bak_x += $this->MarginCorrection;
18062							$this->x = $bak_x;
18063						}
18064						$oldpage = $this->page;
18065						$y = $this->tMargin - $paint_ht_corr;
18066						$this->oldy = $this->tMargin - $paint_ht_corr;
18067						$old_height = 0;
18068					}
18069					/* -- COLUMNS -- */
18070					// COLS
18071					// OR COLUMN CHANGE
18072					if ($this->CurrCol != $oldcolumn) {
18073						if ($this->directionality == 'rtl') { // *OTL*
18074							$bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
18075						} // *OTL*
18076						else { // *OTL*
18077							$bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
18078						} // *OTL*
18079						$this->x = $bak_x;
18080						$oldcolumn = $this->CurrCol;
18081						$y = $this->y0 - $paint_ht_corr;
18082						$this->oldy = $this->y0 - $paint_ht_corr;
18083						$old_height = 0;
18084					}
18085					/* -- END COLUMNS -- */
18086				}
18087			}
18088
18089			// Check if it is the last element. If so then finish printing the block
18090			if ($i == ($array_size - 1)) {
18091				$this->finishFlowingBlock(true); // true = END of flowing block
18092				// Added to correct for OddEven Margins
18093				if ($this->page != $oldpage) {
18094					if (($this->page - $oldpage) % 2 == 1) {
18095						$bak_x += $this->MarginCorrection;
18096						$this->x = $bak_x;
18097					}
18098					$oldpage = $this->page;
18099					$y = $this->tMargin - $paint_ht_corr;
18100					$this->oldy = $this->tMargin - $paint_ht_corr;
18101					$old_height = 0;
18102				}
18103
18104				/* -- COLUMNS -- */
18105				// COLS
18106				// OR COLUMN CHANGE
18107				if ($this->CurrCol != $oldcolumn) {
18108					if ($this->directionality == 'rtl') { // *OTL*
18109						$bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
18110					} // *OTL*
18111					else { // *OTL*
18112						$bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
18113					} // *OTL*
18114					$this->x = $bak_x;
18115					$oldcolumn = $this->CurrCol;
18116					$y = $this->y0 - $paint_ht_corr;
18117					$this->oldy = $this->y0 - $paint_ht_corr;
18118					$old_height = 0;
18119				}
18120				/* -- END COLUMNS -- */
18121			}
18122
18123			// RESETTING VALUES
18124			$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18125			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18126			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
18127			$this->colorarray = '';
18128			$this->spanbgcolorarray = '';
18129			$this->spanbgcolor = false;
18130			$this->spanborder = false;
18131			$this->spanborddet = [];
18132			$this->HREF = '';
18133			$this->textparam = [];
18134			$this->SetTextOutline();
18135
18136			$this->textvar = 0x00; // mPDF 5.7.1
18137			$this->OTLtags = [];
18138			$this->textshadow = '';
18139
18140			$this->currentfontfamily = '';
18141			$this->currentfontsize = '';
18142			$this->currentfontstyle = '';
18143			$this->currentLang = $this->default_lang;  // mPDF 6
18144			$this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6
18145			/* -- TABLES -- */
18146			if ($this->tableLevel) {
18147				$this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES*
18148			} else { 			/* -- END TABLES -- */
18149				if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {
18150					$this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height
18151				}
18152			}
18153			$this->ResetStyles();
18154			$this->lSpacingCSS = '';
18155			$this->wSpacingCSS = '';
18156			$this->fixedlSpacing = false;
18157			$this->minwSpacing = 0;
18158			$this->SetDash();
18159			$this->dash_on = false;
18160			$this->dotted_on = false;
18161		}//end of for(i=0;i<arraysize;i++)
18162
18163		$this->Reset(); // mPDF 6
18164		// PAINT DIV BORDER	// DISABLED IN COLUMNS AS DOESN'T WORK WHEN BROKEN ACROSS COLS??
18165		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)) {
18166			$bottom_y = $this->y; // Does not include Bottom Margin
18167			if (isset($this->blk[$this->blklvl]['startpage']) && $this->blk[$this->blklvl]['startpage'] != $this->page && $blockstate != 1) {
18168				$this->PaintDivBB('pagetop', $blockstate);
18169			} elseif ($blockstate != 1) {
18170				$this->PaintDivBB('', $blockstate);
18171			}
18172			$this->y = $bottom_y;
18173			$this->x = $bak_x;
18174		}
18175
18176		// Reset Font
18177		$this->SetFontSize($this->default_font_size, false);
18178		if ($table_draft) {
18179			$ch = $this->y - $bak_y;
18180			$this->y = $bak_y;
18181			$this->x = $bak_x;
18182			return $ch;
18183		}
18184	}
18185
18186	function _setDashBorder($style, $div, $cp, $side)
18187	{
18188		if ($style == 'dashed' && (($side == 'L' || $side == 'R') || ($side == 'T' && $div != 'pagetop' && !$cp) || ($side == 'B' && $div != 'pagebottom') )) {
18189			$dashsize = 2; // final dash will be this + 1*linewidth
18190			$dashsizek = 1.5; // ratio of Dash/Blank
18191			$this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));
18192		} elseif ($style == 'dotted' || ($side == 'T' && ($div == 'pagetop' || $cp)) || ($side == 'B' && $div == 'pagebottom')) {
18193			// Round join and cap
18194			$this->SetLineJoin(1);
18195			$this->SetLineCap(1);
18196			$this->SetDash(0.001, ($this->LineWidth * 3));
18197		}
18198	}
18199
18200	function _setBorderLine($b, $k = 1)
18201	{
18202		$this->SetLineWidth($b['w'] / $k);
18203		$this->SetDColor($b['c']);
18204		if ($b['c'][0] == 5) { // RGBa
18205			$this->SetAlpha(ord($b['c'][4]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2
18206		} elseif ($b['c'][0] == 6) { // CMYKa
18207			$this->SetAlpha(ord($b['c'][5]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2
18208		}
18209	}
18210
18211	function PaintDivBB($divider = '', $blockstate = 0, $blvl = 0)
18212	{
18213		// Borders & backgrounds are done elsewhere for columns - messes up the repositioning in printcolumnbuffer
18214		if ($this->ColActive) {
18215			return;
18216		} // *COLUMNS*
18217		if ($this->keep_block_together) {
18218			return;
18219		} // mPDF 6
18220		$save_y = $this->y;
18221		if (!$blvl) {
18222			$blvl = $this->blklvl;
18223		}
18224		$x0 = $x1 = $y0 = $y1 = 0;
18225
18226		// Added mPDF 3.0 Float DIV
18227		if (isset($this->blk[$blvl]['bb_painted'][$this->page]) && $this->blk[$blvl]['bb_painted'][$this->page]) {
18228			return;
18229		} // *CSS-FLOAT*
18230
18231		if (isset($this->blk[$blvl]['x0'])) {
18232			$x0 = $this->blk[$blvl]['x0'];
18233		} // left
18234		if (isset($this->blk[$blvl]['y1'])) {
18235			$y1 = $this->blk[$blvl]['y1'];
18236		} // bottom
18237		// Added mPDF 3.0 Float DIV - ensures backgrounds/borders are drawn to bottom of page
18238		if ($y1 == 0) {
18239			if ($divider == 'pagebottom') {
18240				$y1 = $this->h - $this->bMargin;
18241			} else {
18242				$y1 = $this->y;
18243			}
18244		}
18245
18246		if (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page) {
18247			$continuingpage = true;
18248		} else {
18249			$continuingpage = false;
18250		}
18251
18252		if (isset($this->blk[$blvl]['y0'])) {
18253			$y0 = $this->blk[$blvl]['y0'];
18254		}
18255		$h = $y1 - $y0;
18256		$w = $this->blk[$blvl]['width'];
18257		$x1 = $x0 + $w;
18258
18259		// Set border-widths as used here
18260		$border_top = $this->blk[$blvl]['border_top']['w'];
18261		$border_bottom = $this->blk[$blvl]['border_bottom']['w'];
18262		$border_left = $this->blk[$blvl]['border_left']['w'];
18263		$border_right = $this->blk[$blvl]['border_right']['w'];
18264		if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {
18265			$border_top = 0;
18266		}
18267		if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {
18268			$border_bottom = 0;
18269		}
18270
18271		$brTL_H = 0;
18272		$brTL_V = 0;
18273		$brTR_H = 0;
18274		$brTR_V = 0;
18275		$brBL_H = 0;
18276		$brBL_V = 0;
18277		$brBR_H = 0;
18278		$brBR_V = 0;
18279
18280		$brset = false;
18281		/* -- BORDER-RADIUS -- */
18282		if (isset($this->blk[$blvl]['border_radius_TL_H'])) {
18283			$brTL_H = $this->blk[$blvl]['border_radius_TL_H'];
18284			$brset = true;
18285		}
18286		if (isset($this->blk[$blvl]['border_radius_TL_V'])) {
18287			$brTL_V = $this->blk[$blvl]['border_radius_TL_V'];
18288			$brset = true;
18289		}
18290		if (isset($this->blk[$blvl]['border_radius_TR_H'])) {
18291			$brTR_H = $this->blk[$blvl]['border_radius_TR_H'];
18292			$brset = true;
18293		}
18294		if (isset($this->blk[$blvl]['border_radius_TR_V'])) {
18295			$brTR_V = $this->blk[$blvl]['border_radius_TR_V'];
18296			$brset = true;
18297		}
18298		if (isset($this->blk[$blvl]['border_radius_BR_H'])) {
18299			$brBR_H = $this->blk[$blvl]['border_radius_BR_H'];
18300			$brset = true;
18301		}
18302		if (isset($this->blk[$blvl]['border_radius_BR_V'])) {
18303			$brBR_V = $this->blk[$blvl]['border_radius_BR_V'];
18304			$brset = true;
18305		}
18306		if (isset($this->blk[$blvl]['border_radius_BL_H'])) {
18307			$brBL_H = $this->blk[$blvl]['border_radius_BL_H'];
18308			$brset = true;
18309		}
18310		if (isset($this->blk[$blvl]['border_radius_BL_V'])) {
18311			$brBL_V = $this->blk[$blvl]['border_radius_BL_V'];
18312			$brset = true;
18313		}
18314
18315		if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {
18316			$brTL_H = 0;
18317			$brTL_V = 0;
18318			$brTR_H = 0;
18319			$brTR_V = 0;
18320		}
18321		if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {
18322			$brBL_H = 0;
18323			$brBL_V = 0;
18324			$brBR_H = 0;
18325			$brBR_V = 0;
18326		}
18327
18328		// Disallow border-radius if it is smaller than the border width.
18329		if ($brTL_H < min($border_left, $border_top)) {
18330			$brTL_H = $brTL_V = 0;
18331		}
18332		if ($brTL_V < min($border_left, $border_top)) {
18333			$brTL_V = $brTL_H = 0;
18334		}
18335		if ($brTR_H < min($border_right, $border_top)) {
18336			$brTR_H = $brTR_V = 0;
18337		}
18338		if ($brTR_V < min($border_right, $border_top)) {
18339			$brTR_V = $brTR_H = 0;
18340		}
18341		if ($brBL_H < min($border_left, $border_bottom)) {
18342			$brBL_H = $brBL_V = 0;
18343		}
18344		if ($brBL_V < min($border_left, $border_bottom)) {
18345			$brBL_V = $brBL_H = 0;
18346		}
18347		if ($brBR_H < min($border_right, $border_bottom)) {
18348			$brBR_H = $brBR_V = 0;
18349		}
18350		if ($brBR_V < min($border_right, $border_bottom)) {
18351			$brBR_V = $brBR_H = 0;
18352		}
18353
18354		// CHECK FOR radii that sum to > width or height of div ********
18355		$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));
18356		if ($f < 1) {
18357			$brTL_H *= $f;
18358			$brTL_V *= $f;
18359			$brTR_H *= $f;
18360			$brTR_V *= $f;
18361			$brBL_H *= $f;
18362			$brBL_V *= $f;
18363			$brBR_H *= $f;
18364			$brBR_V *= $f;
18365		}
18366		/* -- END BORDER-RADIUS -- */
18367
18368		$tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
18369		for ($l = 0; $l <= $blvl; $l++) {
18370			if ($this->blk[$l]['bgcolor']) {
18371				$tbcol = $this->blk[$l]['bgcolorarray'];
18372			}
18373		}
18374
18375		// BORDERS
18376		if (isset($this->blk[$blvl]['y0']) && $this->blk[$blvl]['y0']) {
18377			$y0 = $this->blk[$blvl]['y0'];
18378		}
18379		$h = $y1 - $y0;
18380		$w = $this->blk[$blvl]['width'];
18381
18382		if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
18383			$tbd = $this->blk[$blvl]['border_top'];
18384
18385			$legend = '';
18386			$legbreakL = 0;
18387			$legbreakR = 0;
18388			// BORDER LEGEND
18389			if (isset($this->blk[$blvl]['border_legend']) && $this->blk[$blvl]['border_legend']) {
18390				$legend = $this->blk[$blvl]['border_legend']; // Same structure array as textbuffer
18391				$txt = $legend[0] = ltrim($legend[0]);
18392				if (!empty($legend[18])) {
18393					$this->otl->trimOTLdata($legend[18], true, false);
18394				} // *OTL*
18395				// Set font, size, style, color
18396				$this->SetFont($legend[4], $legend[2], $legend[11]);
18397				if (isset($legend[3]) && $legend[3]) {
18398					$cor = $legend[3];
18399					$this->SetTColor($cor);
18400				}
18401				$stringWidth = $this->GetStringWidth($txt, true, $legend[18], $legend[8]);
18402				$save_x = $this->x;
18403				$save_y = $this->y;
18404				$save_currentfontfamily = $this->FontFamily;
18405				$save_currentfontsize = $this->FontSizePt;
18406				$save_currentfontstyle = $this->FontStyle;
18407				$this->y = $y0 - $this->FontSize / 2 + $this->blk[$blvl]['border_top']['w'] / 2;
18408				$this->x = $x0 + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_left']['w'];
18409
18410				// Set the distance from the border line to the text ? make configurable variable
18411				$gap = 0.2 * $this->FontSize;
18412				$legbreakL = $this->x - $gap;
18413				$legbreakR = $this->x + $stringWidth + $gap;
18414				$this->magic_reverse_dir($txt, $this->blk[$blvl]['direction'], $legend[18]);
18415				$fill = '';
18416				$this->Cell($stringWidth, $this->FontSize, $txt, '', 0, 'C', $fill, '', 0, 0, 0, 'M', $fill, false, $legend[18], $legend[8]);
18417				// Reset
18418				$this->x = $save_x;
18419				$this->y = $save_y;
18420				$this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);
18421				$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18422			}
18423
18424			if (isset($tbd['s']) && $tbd['s']) {
18425				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
18426					$this->_out('q');
18427					$this->SetLineWidth(0);
18428					$this->_out(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
18429					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
18430					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
18431					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
18432					$this->_out(' h W n '); // Ends path no-op & Sets the clipping path
18433				}
18434
18435				$this->_setBorderLine($tbd);
18436				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
18437					$legbreakL -= $border_top / 2; // because line cap different
18438					$legbreakR += $border_top / 2;
18439					$this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'T');
18440				} /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brTR_V && $brTR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18441					$this->SetLineJoin(0);
18442					$this->SetLineCap(0);
18443				}
18444				$s = '';
18445				if ($brTR_H && $brTR_V) {
18446					$s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_top / 2, $brTR_V - $border_top / 2, 1, 2, true)) . "\n";
18447				} else { 				/* -- END BORDER-RADIUS -- */
18448					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18449						$s .= (sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18450					} else {
18451						$s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18452					}
18453				}
18454				/* -- BORDER-RADIUS -- */
18455				if ($brTL_H && $brTL_V) {
18456					if ($legend) {
18457						if ($legbreakR < ($x0 + $w - $brTR_H)) {
18458							$s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18459						}
18460						if ($legbreakL > ($x0 + $brTL_H )) {
18461							$s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18462							$s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE) . "\n");
18463						} else {
18464							$s .= (sprintf('%.3F %.3F m ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18465						}
18466					} else {
18467						$s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18468					}
18469					$s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_top / 2, $brTL_V - $border_top / 2, 2, 1)) . "\n";
18470				} else {
18471					/* -- END BORDER-RADIUS -- */
18472					if ($legend) {
18473						if ($legbreakR < ($x0 + $w)) {
18474							$s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18475						}
18476						if ($legbreakL > ($x0)) {
18477							$s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18478							if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18479								$s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18480							} else {
18481								$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18482							}
18483						} elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18484							$s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18485						} else {
18486							$s .= (sprintf('%.3F %.3F m ', ($x0 + $border_top / 2) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18487						}
18488					} elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18489						$s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18490					} else {
18491						$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . "\n";
18492					}
18493					/* -- BORDER-RADIUS -- */
18494				}
18495				/* -- END BORDER-RADIUS -- */
18496				$s .= 'S' . "\n";
18497				$this->_out($s);
18498
18499				if ($tbd['style'] == 'double') {
18500					$this->SetLineWidth($tbd['w'] / 3);
18501					$this->SetDColor($tbcol);
18502					$this->_out($s);
18503				}
18504				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
18505					$this->_out('Q');
18506				}
18507
18508				// Reset Corners and Dash off
18509				$this->SetLineWidth(0.1);
18510				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18511				$this->SetLineJoin(2);
18512				$this->SetLineCap(2);
18513				$this->SetDash();
18514			}
18515		}
18516		// Reinstate line above for dotted line divider when block border crosses a page
18517		// elseif ($divider == 'pagetop' || $continuingpage) {
18518
18519		if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
18520			$tbd = $this->blk[$blvl]['border_bottom'];
18521			if (isset($tbd['s']) && $tbd['s']) {
18522				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
18523					$this->_out('q');
18524					$this->SetLineWidth(0);
18525					$this->_out(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
18526					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
18527					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
18528					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
18529					$this->_out(' h W n '); // Ends path no-op & Sets the clipping path
18530				}
18531
18532				$this->_setBorderLine($tbd);
18533				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
18534					$this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'B');
18535				} /* -- BORDER-RADIUS -- */ elseif (($brBL_V && $brBL_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18536					$this->SetLineJoin(0);
18537					$this->SetLineCap(0);
18538				}
18539				$s = '';
18540				if ($brBL_H && $brBL_V) {
18541					$s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_bottom / 2, $brBL_V - $border_bottom / 2, 3, 2, true)) . "\n";
18542				} else { 				/* -- END BORDER-RADIUS -- */
18543					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18544						$s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
18545					} else {
18546						$s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
18547					}
18548				}
18549				/* -- BORDER-RADIUS -- */
18550				if ($brBR_H && $brBR_V) {
18551					$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2) - $brBR_H ) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
18552					$s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_bottom / 2, $brBR_V - $border_bottom / 2, 4, 1)) . "\n";
18553				} else { 				/* -- END BORDER-RADIUS -- */
18554					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18555						$s .= (sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
18556					} else {
18557						$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . "\n";
18558					}
18559				}
18560				$s .= 'S' . "\n";
18561				$this->_out($s);
18562
18563				if ($tbd['style'] == 'double') {
18564					$this->SetLineWidth($tbd['w'] / 3);
18565					$this->SetDColor($tbcol);
18566					$this->_out($s);
18567				}
18568				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
18569					$this->_out('Q');
18570				}
18571
18572				// Reset Corners and Dash off
18573				$this->SetLineWidth(0.1);
18574				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18575				$this->SetLineJoin(2);
18576				$this->SetLineCap(2);
18577				$this->SetDash();
18578			}
18579		}
18580		// Reinstate line below for dotted line divider when block border crosses a page
18581		// elseif ($blockstate == 1 || $divider == 'pagebottom') {
18582
18583		if ($this->blk[$blvl]['border_left']) {
18584			$tbd = $this->blk[$blvl]['border_left'];
18585			if (isset($tbd['s']) && $tbd['s']) {
18586				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
18587					$this->_out('q');
18588					$this->SetLineWidth(0);
18589					$this->_out(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
18590					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
18591					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
18592					$this->_out(sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
18593					$this->_out(' h W n '); // Ends path no-op & Sets the clipping path
18594				}
18595
18596				$this->_setBorderLine($tbd);
18597				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
18598					$this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'L');
18599				} /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brBL_V && $brBL_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18600					$this->SetLineJoin(0);
18601					$this->SetLineCap(0);
18602				}
18603				$s = '';
18604				if ($brTL_V && $brTL_H) {
18605					$s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_left / 2, $brTL_V - $border_left / 2, 2, 2, true)) . "\n";
18606				} else { 				/* -- END BORDER-RADIUS -- */
18607					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18608						$s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)) . "\n";
18609					} else {
18610						$s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_left / 2))) * Mpdf::SCALE)) . "\n";
18611					}
18612				}
18613				/* -- BORDER-RADIUS -- */
18614				if ($brBL_V && $brBL_H) {
18615					$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2) - $brBL_V) ) * Mpdf::SCALE)) . "\n";
18616					$s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_left / 2, $brBL_V - $border_left / 2, 3, 1)) . "\n";
18617				} else { 				/* -- END BORDER-RADIUS -- */
18618					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18619						$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h) ) * Mpdf::SCALE)) . "\n";
18620					} else {
18621						$s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2)) ) * Mpdf::SCALE)) . "\n";
18622					}
18623				}
18624				$s .= 'S' . "\n";
18625				$this->_out($s);
18626
18627				if ($tbd['style'] == 'double') {
18628					$this->SetLineWidth($tbd['w'] / 3);
18629					$this->SetDColor($tbcol);
18630					$this->_out($s);
18631				}
18632				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
18633					$this->_out('Q');
18634				}
18635
18636				// Reset Corners and Dash off
18637				$this->SetLineWidth(0.1);
18638				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18639				$this->SetLineJoin(2);
18640				$this->SetLineCap(2);
18641				$this->SetDash();
18642			}
18643		}
18644		if ($this->blk[$blvl]['border_right']) {
18645			$tbd = $this->blk[$blvl]['border_right'];
18646			if (isset($tbd['s']) && $tbd['s']) {
18647				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
18648					$this->_out('q');
18649					$this->SetLineWidth(0);
18650					$this->_out(sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));
18651					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));
18652					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));
18653					$this->_out(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));
18654					$this->_out(' h W n '); // Ends path no-op & Sets the clipping path
18655				}
18656
18657				$this->_setBorderLine($tbd);
18658				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
18659					$this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'R');
18660				} /* -- BORDER-RADIUS -- */ elseif (($brTR_V && $brTR_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18661					$this->SetLineJoin(0);
18662					$this->SetLineCap(0);
18663				}
18664				$s = '';
18665				if ($brBR_V && $brBR_H) {
18666					$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";
18667				} else { 				/* -- END BORDER-RADIUS -- */
18668					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18669						$s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)) . "\n";
18670					} else {
18671						$s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_right / 2))) * Mpdf::SCALE)) . "\n";
18672					}
18673				}
18674				/* -- BORDER-RADIUS -- */
18675				if ($brTR_V && $brTR_H) {
18676					$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2) + $brTR_V) ) * Mpdf::SCALE)) . "\n";
18677					$s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_right / 2, $brTR_V - $border_right / 2, 1, 1)) . "\n";
18678				} else { 				/* -- END BORDER-RADIUS -- */
18679					if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
18680						$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0) ) * Mpdf::SCALE)) . "\n";
18681					} else {
18682						$s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2)) ) * Mpdf::SCALE)) . "\n";
18683					}
18684				}
18685				$s .= 'S' . "\n";
18686				$this->_out($s);
18687
18688				if ($tbd['style'] == 'double') {
18689					$this->SetLineWidth($tbd['w'] / 3);
18690					$this->SetDColor($tbcol);
18691					$this->_out($s);
18692				}
18693				if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
18694					$this->_out('Q');
18695				}
18696
18697				// Reset Corners and Dash off
18698				$this->SetLineWidth(0.1);
18699				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
18700				$this->SetLineJoin(2);
18701				$this->SetLineCap(2);
18702				$this->SetDash();
18703			}
18704		}
18705
18706
18707		$this->SetDash();
18708		$this->y = $save_y;
18709
18710
18711		// BACKGROUNDS are disabled in columns/kbt/headers - messes up the repositioning in printcolumnbuffer
18712		if ($this->ColActive || $this->kwt || $this->keep_block_together) {
18713			return;
18714		}
18715
18716
18717		$bgx0 = $x0;
18718		$bgx1 = $x1;
18719		$bgy0 = $y0;
18720		$bgy1 = $y1;
18721
18722		// Defined br values represent the radius of the outer curve - need to take border-width/2 from each radius for drawing the borders
18723		if (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'padding-box') {
18724			$brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w']);
18725			$brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w']);
18726			$brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w']);
18727			$brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w']);
18728			$brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w']);
18729			$brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w']);
18730			$brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w']);
18731			$brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w']);
18732			$bgx0 += $this->blk[$blvl]['border_left']['w'];
18733			$bgx1 -= $this->blk[$blvl]['border_right']['w'];
18734			if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
18735				$bgy0 += $this->blk[$blvl]['border_top']['w'];
18736			}
18737			if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
18738				$bgy1 -= $this->blk[$blvl]['border_bottom']['w'];
18739			}
18740		} elseif (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'content-box') {
18741			$brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);
18742			$brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);
18743			$brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);
18744			$brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);
18745			$brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);
18746			$brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);
18747			$brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);
18748			$brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);
18749			$bgx0 += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
18750			$bgx1 -= $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right'];
18751			if (($this->blk[$blvl]['border_top']['w'] || $this->blk[$blvl]['padding_top']) && $divider != 'pagetop' && !$continuingpage) {
18752				$bgy0 += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
18753			}
18754			if (($this->blk[$blvl]['border_bottom']['w'] || $this->blk[$blvl]['padding_bottom']) && $blockstate != 1 && $divider != 'pagebottom') {
18755				$bgy1 -= $this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom'];
18756			}
18757		} else {
18758			$brbgTL_H = $brTL_H;
18759			$brbgTL_V = $brTL_V;
18760			$brbgTR_H = $brTR_H;
18761			$brbgTR_V = $brTR_V;
18762			$brbgBL_H = $brBL_H;
18763			$brbgBL_V = $brBL_V;
18764			$brbgBR_H = $brBR_H;
18765			$brbgBR_V = $brBR_V;
18766		}
18767
18768		// Set clipping path
18769		$s = ' q 0 w '; // Line width=0
18770		$s .= sprintf('%.3F %.3F m ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // start point TL before the arc
18771		/* -- BORDER-RADIUS -- */
18772		if ($brbgTL_H || $brbgTL_V) {
18773			$s .= $this->_EllipseArc($bgx0 + $brbgTL_H, $bgy0 + $brbgTL_V, $brbgTL_H, $brbgTL_V, 2); // segment 2 TL
18774		}
18775		/* -- END BORDER-RADIUS -- */
18776		$s .= sprintf('%.3F %.3F l ', ($bgx0) * Mpdf::SCALE, ($this->h - ($bgy1 - $brbgBL_V )) * Mpdf::SCALE); // line to BL
18777		/* -- BORDER-RADIUS -- */
18778		if ($brbgBL_H || $brbgBL_V) {
18779			$s .= $this->_EllipseArc($bgx0 + $brbgBL_H, $bgy1 - $brbgBL_V, $brbgBL_H, $brbgBL_V, 3); // segment 3 BL
18780		}
18781		/* -- END BORDER-RADIUS -- */
18782		$s .= sprintf('%.3F %.3F l ', ($bgx1 - $brbgBR_H ) * Mpdf::SCALE, ($this->h - ($bgy1)) * Mpdf::SCALE); // line to BR
18783		/* -- BORDER-RADIUS -- */
18784		if ($brbgBR_H || $brbgBR_V) {
18785			$s .= $this->_EllipseArc($bgx1 - $brbgBR_H, $bgy1 - $brbgBR_V, $brbgBR_H, $brbgBR_V, 4); // segment 4 BR
18786		}
18787		/* -- END BORDER-RADIUS -- */
18788		$s .= sprintf('%.3F %.3F l ', ($bgx1) * Mpdf::SCALE, ($this->h - ($bgy0 + $brbgTR_V)) * Mpdf::SCALE); // line to TR
18789		/* -- BORDER-RADIUS -- */
18790		if ($brbgTR_H || $brbgTR_V) {
18791			$s .= $this->_EllipseArc($bgx1 - $brbgTR_H, $bgy0 + $brbgTR_V, $brbgTR_H, $brbgTR_V, 1); // segment 1 TR
18792		}
18793		/* -- END BORDER-RADIUS -- */
18794		$s .= sprintf('%.3F %.3F l ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // line to TL
18795		// Box Shadow
18796		$shadow = '';
18797		if (isset($this->blk[$blvl]['box_shadow']) && $this->blk[$blvl]['box_shadow'] && $h > 0) {
18798			foreach ($this->blk[$blvl]['box_shadow'] as $sh) {
18799				// Colors
18800				if ($sh['col']{0} == 1) {
18801					$colspace = 'Gray';
18802					if ($sh['col']{2} == 1) {
18803						$col1 = '1' . $sh['col'][1] . '1' . $sh['col'][3];
18804					} else {
18805						$col1 = '1' . $sh['col'][1] . '1' . chr(100);
18806					}
18807					$col2 = '1' . $sh['col'][1] . '1' . chr(0);
18808				} elseif ($sh['col']{0} == 4) { // CMYK
18809					$colspace = 'CMYK';
18810					$col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(100);
18811					$col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);
18812				} elseif ($sh['col']{0} == 5) { // RGBa
18813					$colspace = 'RGB';
18814					$col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4];
18815					$col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);
18816				} elseif ($sh['col']{0} == 6) { // CMYKa
18817					$colspace = 'CMYK';
18818					$col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . $sh['col'][5];
18819					$col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);
18820				} else {
18821					$colspace = 'RGB';
18822					$col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(100);
18823					$col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);
18824				}
18825
18826				// Use clipping path as set above (and rectangle around page) to clip area outside box
18827				$shadow .= $s; // Use the clipping path with W*
18828				$shadow .= sprintf('0 %.3F m %.3F %.3F l ', $this->h * Mpdf::SCALE, $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE);
18829				$shadow .= sprintf('%.3F 0 l 0 0 l 0 %.3F l ', $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE);
18830				$shadow .= 'W n' . "\n";
18831
18832				$sh['blur'] = abs($sh['blur']); // cannot have negative blur value
18833				// Ensure spread/blur do not make effective shadow width/height < 0
18834				// Could do more complex things but this just adjusts spread value
18835				if (-$sh['spread'] + $sh['blur'] / 2 > min($w / 2, $h / 2)) {
18836					$sh['spread'] = $sh['blur'] / 2 - min($w / 2, $h / 2) + 0.01;
18837				}
18838				// Shadow Offset
18839				if ($sh['x'] || $sh['y']) {
18840					$shadow .= sprintf(' q 1 0 0 1 %.4F %.4F cm', $sh['x'] * Mpdf::SCALE, -$sh['y'] * Mpdf::SCALE) . "\n";
18841				}
18842
18843				// Set path for INNER shadow
18844				$shadow .= ' q 0 w ';
18845				$shadow .= $this->SetFColor($col1, true) . "\n";
18846				if ($col1{0} == 5 && ord($col1{4}) < 100) { // RGBa
18847					$shadow .= $this->SetAlpha(ord($col1{4}) / 100, 'Normal', true, 'F') . "\n";
18848				} elseif ($col1{0} == 6 && ord($col1{5}) < 100) { // CMYKa
18849					$shadow .= $this->SetAlpha(ord($col1{5}) / 100, 'Normal', true, 'F') . "\n";
18850				} elseif ($col1{0} == 1 && $col1{2} == 1 && ord($col1{3}) < 100) { // Gray
18851					$shadow .= $this->SetAlpha(ord($col1{3}) / 100, 'Normal', true, 'F') . "\n";
18852				}
18853
18854				// Blur edges
18855				$mag = 0.551784; // Bezier Control magic number for 4-part spline for circle/ellipse
18856				$mag2 = 0.551784; // Bezier Control magic number to fill in edge of blurred rectangle
18857				$d1 = $sh['spread'] + $sh['blur'] / 2;
18858				$d2 = $sh['spread'] - $sh['blur'] / 2;
18859				$bl = $sh['blur'];
18860				$x00 = $x0 - $d1;
18861				$y00 = $y0 - $d1;
18862				$w00 = $w + $d1 * 2;
18863				$h00 = $h + $d1 * 2;
18864
18865				// If any border-radius is greater width-negative spread(inner edge), ignore radii for shadow or screws up
18866				$flatten = false;
18867				if (max($brbgTR_H, $brbgTL_H, $brbgBR_H, $brbgBL_H) >= $w + $d2) {
18868					$flatten = true;
18869				}
18870				if (max($brbgTR_V, $brbgTL_V, $brbgBR_V, $brbgBL_V) >= $h + $d2) {
18871					$flatten = true;
18872				}
18873
18874
18875				// TOP RIGHT corner
18876				$p1x = $x00 + $w00 - $d1 - $brbgTR_H;
18877				$p1c2x = $p1x + ($d2 + $brbgTR_H) * $mag;
18878				$p1y = $y00 + $bl;
18879				$p2x = $x00 + $w00 - $d1 - $brbgTR_H;
18880				$p2c2x = $p2x + ($d1 + $brbgTR_H) * $mag;
18881				$p2y = $y00;
18882				$p2c1y = $p2y + $bl / 2;
18883				$p3x = $x00 + $w00;
18884				$p3c2x = $p3x - $bl / 2;
18885				$p3y = $y00 + $d1 + $brbgTR_V;
18886				$p3c1y = $p3y - ($d1 + $brbgTR_V) * $mag;
18887				$p4x = $x00 + $w00 - $bl;
18888				$p4y = $y00 + $d1 + $brbgTR_V;
18889				$p4c2y = $p4y - ($d2 + $brbgTR_V) * $mag;
18890				if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
18891					$p1x = $x00 + $w00 - $bl;
18892					$p1c2x = $p1x;
18893					$p2x = $x00 + $w00 - $bl;
18894					$p2c2x = $p2x + $bl * $mag2;
18895					$p3y = $y00 + $bl;
18896					$p3c1y = $p3y - $bl * $mag2;
18897					$p4y = $y00 + $bl;
18898					$p4c2y = $p4y;
18899				}
18900
18901				$shadow .= sprintf('%.3F %.3F m ', ($p1x ) * Mpdf::SCALE, ($this->h - ($p1y )) * Mpdf::SCALE);
18902				$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);
18903				$patch_array[0]['f'] = 0;
18904				$patch_array[0]['points'] = [$p1x, $p1y, $p1x, $p1y,
18905					$p2x, $p2c1y, $p2x, $p2y, $p2c2x, $p2y,
18906					$p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,
18907					$p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,
18908					$p1c2x, $p1y];
18909				$patch_array[0]['colors'] = [$col1, $col2, $col2, $col1];
18910
18911
18912				// RIGHT
18913				$p1x = $x00 + $w00; // control point only matches p3 preceding
18914				$p1y = $y00 + $d1 + $brbgTR_V;
18915				$p2x = $x00 + $w00 - $bl; // control point only matches p4 preceding
18916				$p2y = $y00 + $d1 + $brbgTR_V;
18917				$p3x = $x00 + $w00 - $bl;
18918				$p3y = $y00 + $h00 - $d1 - $brbgBR_V;
18919				$p4x = $x00 + $w00;
18920				$p4c1x = $p4x - $bl / 2;
18921				$p4y = $y00 + $h00 - $d1 - $brbgBR_V;
18922				if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
18923					$p1y = $y00 + $bl;
18924					$p2y = $y00 + $bl;
18925				}
18926				if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
18927					$p3y = $y00 + $h00 - $bl;
18928					$p4y = $y00 + $h00 - $bl;
18929				}
18930
18931				$shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
18932				$patch_array[1]['f'] = 2;
18933				$patch_array[1]['points'] = [$p2x, $p2y,
18934					$p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
18935					$p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,
18936					$p1x, $p1y];
18937				$patch_array[1]['colors'] = [$col1, $col2];
18938
18939
18940				// BOTTOM RIGHT corner
18941				$p1x = $x00 + $w00 - $bl;  // control points only matches p3 preceding
18942				$p1y = $y00 + $h00 - $d1 - $brbgBR_V;
18943				$p1c2y = $p1y + ($d2 + $brbgBR_V) * $mag;
18944				$p2x = $x00 + $w00;     // control point only matches p4 preceding
18945				$p2y = $y00 + $h00 - $d1 - $brbgBR_V;
18946				$p2c2y = $p2y + ($d1 + $brbgBR_V) * $mag;
18947				$p3x = $x00 + $w00 - $d1 - $brbgBR_H;
18948				$p3c1x = $p3x + ($d1 + $brbgBR_H) * $mag;
18949				$p3y = $y00 + $h00;
18950				$p3c2y = $p3y - $bl / 2;
18951				$p4x = $x00 + $w00 - $d1 - $brbgBR_H;
18952				$p4c2x = $p4x + ($d2 + $brbgBR_H) * $mag;
18953				$p4y = $y00 + $h00 - $bl;
18954
18955				if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
18956					$p1y = $y00 + $h00 - $bl;
18957					$p1c2y = $p1y;
18958					$p2y = $y00 + $h00 - $bl;
18959					$p2c2y = $p2y + $bl * $mag2;
18960					$p3x = $x00 + $w00 - $bl;
18961					$p3c1x = $p3x + $bl * $mag2;
18962					$p4x = $x00 + $w00 - $bl;
18963					$p4c2x = $p4x;
18964				}
18965
18966				$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);
18967				$patch_array[2]['f'] = 2;
18968				$patch_array[2]['points'] = [$p2x, $p2c2y,
18969					$p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,
18970					$p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,
18971					$p1x, $p1c2y];
18972				$patch_array[2]['colors'] = [$col2, $col1];
18973
18974
18975
18976				// BOTTOM
18977				$p1x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p3 preceding
18978				$p1y = $y00 + $h00;
18979				$p2x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p4 preceding
18980				$p2y = $y00 + $h00 - $bl;
18981				$p3x = $x00 + $d1 + $brbgBL_H;
18982				$p3y = $y00 + $h00 - $bl;
18983				$p4x = $x00 + $d1 + $brbgBL_H;
18984				$p4y = $y00 + $h00;
18985				$p4c1y = $p4y - $bl / 2;
18986
18987				if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
18988					$p1x = $x00 + $w00 - $bl;
18989					$p2x = $x00 + $w00 - $bl;
18990				}
18991				if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
18992					$p3x = $x00 + $bl;
18993					$p4x = $x00 + $bl;
18994				}
18995
18996				$shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
18997				$patch_array[3]['f'] = 2;
18998				$patch_array[3]['points'] = [$p2x, $p2y,
18999					$p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
19000					$p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,
19001					$p1x, $p1y];
19002				$patch_array[3]['colors'] = [$col1, $col2];
19003
19004				// BOTTOM LEFT corner
19005				$p1x = $x00 + $d1 + $brbgBL_H;
19006				$p1c2x = $p1x - ($d2 + $brbgBL_H) * $mag; // control points only matches p3 preceding
19007				$p1y = $y00 + $h00 - $bl;
19008				$p2x = $x00 + $d1 + $brbgBL_H;
19009				$p2c2x = $p2x - ($d1 + $brbgBL_H) * $mag; // control point only matches p4 preceding
19010				$p2y = $y00 + $h00;
19011				$p3x = $x00;
19012				$p3c2x = $p3x + $bl / 2;
19013				$p3y = $y00 + $h00 - $d1 - $brbgBL_V;
19014				$p3c1y = $p3y + ($d1 + $brbgBL_V) * $mag;
19015				$p4x = $x00 + $bl;
19016				$p4y = $y00 + $h00 - $d1 - $brbgBL_V;
19017				$p4c2y = $p4y + ($d2 + $brbgBL_V) * $mag;
19018				if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
19019					$p1x = $x00 + $bl;
19020					$p1c2x = $p1x;
19021					$p2x = $x00 + $bl;
19022					$p2c2x = $p2x - $bl * $mag2;
19023					$p3y = $y00 + $h00 - $bl;
19024					$p3c1y = $p3y + $bl * $mag2;
19025					$p4y = $y00 + $h00 - $bl;
19026					$p4c2y = $p4y;
19027				}
19028
19029				$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);
19030				$patch_array[4]['f'] = 2;
19031				$patch_array[4]['points'] = [$p2c2x, $p2y,
19032					$p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,
19033					$p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,
19034					$p1c2x, $p1y];
19035				$patch_array[4]['colors'] = [$col2, $col1];
19036
19037
19038				// LEFT - joins on the right (C3-C4 of previous): f = 2
19039				$p1x = $x00; // control point only matches p3 preceding
19040				$p1y = $y00 + $h00 - $d1 - $brbgBL_V;
19041				$p2x = $x00 + $bl; // control point only matches p4 preceding
19042				$p2y = $y00 + $h00 - $d1 - $brbgBL_V;
19043				$p3x = $x00 + $bl;
19044				$p3y = $y00 + $d1 + $brbgTL_V;
19045				$p4x = $x00;
19046				$p4c1x = $p4x + $bl / 2;
19047				$p4y = $y00 + $d1 + $brbgTL_V;
19048				if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
19049					$p1y = $y00 + $h00 - $bl;
19050					$p2y = $y00 + $h00 - $bl;
19051				}
19052				if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
19053					$p3y = $y00 + $bl;
19054					$p4y = $y00 + $bl;
19055				}
19056
19057				$shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
19058				$patch_array[5]['f'] = 2;
19059				$patch_array[5]['points'] = [$p2x, $p2y,
19060					$p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
19061					$p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,
19062					$p1x, $p1y];
19063				$patch_array[5]['colors'] = [$col1, $col2];
19064
19065				// TOP LEFT corner
19066				$p1x = $x00 + $bl;  // control points only matches p3 preceding
19067				$p1y = $y00 + $d1 + $brbgTL_V;
19068				$p1c2y = $p1y - ($d2 + $brbgTL_V) * $mag;
19069				$p2x = $x00;   // control point only matches p4 preceding
19070				$p2y = $y00 + $d1 + $brbgTL_V;
19071				$p2c2y = $p2y - ($d1 + $brbgTL_V) * $mag;
19072				$p3x = $x00 + $d1 + $brbgTL_H;
19073				$p3c1x = $p3x - ($d1 + $brbgTL_H) * $mag;
19074				$p3y = $y00;
19075				$p3c2y = $p3y + $bl / 2;
19076				$p4x = $x00 + $d1 + $brbgTL_H;
19077				$p4c2x = $p4x - ($d2 + $brbgTL_H) * $mag;
19078				$p4y = $y00 + $bl;
19079
19080				if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
19081					$p1y = $y00 + $bl;
19082					$p1c2y = $p1y;
19083					$p2y = $y00 + $bl;
19084					$p2c2y = $p2y - $bl * $mag2;
19085					$p3x = $x00 + $bl;
19086					$p3c1x = $p3x - $bl * $mag2;
19087					$p4x = $x00 + $bl;
19088					$p4c2x = $p4x;
19089				}
19090
19091				$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);
19092				$patch_array[6]['f'] = 2;
19093				$patch_array[6]['points'] = [$p2x, $p2c2y,
19094					$p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,
19095					$p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,
19096					$p1x, $p1c2y];
19097				$patch_array[6]['colors'] = [$col2, $col1];
19098
19099
19100				// TOP - joins on the right (C3-C4 of previous): f = 2
19101				$p1x = $x00 + $d1 + $brbgTL_H; // control point only matches p3 preceding
19102				$p1y = $y00;
19103				$p2x = $x00 + $d1 + $brbgTL_H; // control point only matches p4 preceding
19104				$p2y = $y00 + $bl;
19105				$p3x = $x00 + $w00 - $d1 - $brbgTR_H;
19106				$p3y = $y00 + $bl;
19107				$p4x = $x00 + $w00 - $d1 - $brbgTR_H;
19108				$p4y = $y00;
19109				$p4c1y = $p4y + $bl / 2;
19110				if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
19111					$p1x = $x00 + $bl;
19112					$p2x = $x00 + $bl;
19113				}
19114				if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
19115					$p3x = $x00 + $w00 - $bl;
19116					$p4x = $x00 + $w00 - $bl;
19117				}
19118
19119				$shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);
19120				$patch_array[7]['f'] = 2;
19121				$patch_array[7]['points'] = [$p2x, $p2y,
19122					$p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
19123					$p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,
19124					$p1x, $p1y];
19125				$patch_array[7]['colors'] = [$col1, $col2];
19126
19127				$shadow .= ' h f Q ' . "\n"; // Close path and Fill the inner solid shadow
19128
19129				if ($bl) {
19130					$shadow .= $this->gradient->CoonsPatchMesh($x00, $y00, $w00, $h00, $patch_array, $x00, $x00 + $w00, $y00, $y00 + $h00, $colspace, true);
19131				}
19132
19133				if ($sh['x'] || $sh['y']) {
19134					$shadow .= ' Q' . "\n";  // Shadow Offset
19135				}
19136				$shadow .= ' Q' . "\n"; // Ends path no-op & Sets the clipping path
19137			}
19138		}
19139
19140		$s .= ' W n '; // Ends path no-op & Sets the clipping path
19141
19142		if ($this->blk[$blvl]['bgcolor']) {
19143			$this->pageBackgrounds[$blvl][] = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h, 'col' => $this->blk[$blvl]['bgcolorarray'], 'clippath' => $s, 'visibility' => $this->visibility, 'shadow' => $shadow, 'z-index' => $this->current_layer];
19144		} elseif ($shadow) {
19145			$this->pageBackgrounds[$blvl][] = ['shadowonly' => true, 'col' => '', 'clippath' => '', 'visibility' => $this->visibility, 'shadow' => $shadow, 'z-index' => $this->current_layer];
19146		}
19147
19148		/* -- BACKGROUNDS -- */
19149		if (isset($this->blk[$blvl]['gradient'])) {
19150			$g = $this->gradient->parseBackgroundGradient($this->blk[$blvl]['gradient']);
19151			if ($g) {
19152				$gx = $x0;
19153				$gy = $y0;
19154				$this->pageBackgrounds[$blvl][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $w, 'h' => $h, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s, 'visibility' => $this->visibility, 'z-index' => $this->current_layer];
19155			}
19156		}
19157		if (isset($this->blk[$blvl]['background-image'])) {
19158			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'])) {
19159				$g = $this->gradient->parseMozGradient($this->blk[$blvl]['background-image']['gradient']);
19160				if ($g) {
19161					$gx = $x0;
19162					$gy = $y0;
19163					// origin specifies the background-positioning-area (bpa)
19164					if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {
19165						$gx += $this->blk[$blvl]['border_left']['w'];
19166						$w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);
19167						if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
19168							$gy += $this->blk[$blvl]['border_top']['w'];
19169						}
19170						if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
19171							$gy1 = $y1 - $this->blk[$blvl]['border_bottom']['w'];
19172						} else {
19173							$gy1 = $y1;
19174						}
19175						$h = $gy1 - $gy;
19176					} elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {
19177						$gx += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
19178						$w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']);
19179						if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
19180							$gy += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
19181						}
19182						if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
19183							$gy1 = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);
19184						} else {
19185							$gy1 = $y1 - $this->blk[$blvl]['padding_bottom'];
19186						}
19187						$h = $gy1 - $gy;
19188					}
19189
19190					if (isset($this->blk[$blvl]['background-image']['size']['w']) && $this->blk[$blvl]['background-image']['size']['w']) {
19191						$size = $this->blk[$blvl]['background-image']['size'];
19192						if ($size['w'] != 'contain' && $size['w'] != 'cover') {
19193							if (stristr($size['w'], '%')) {
19194								$size['w'] = (float) $size['w'];
19195								$size['w'] /= 100;
19196								$w *= $size['w'];
19197							} elseif ($size['w'] != 'auto') {
19198								$w = $size['w'];
19199							}
19200							if (stristr($size['h'], '%')) {
19201								$size['h'] = (float) $size['h'];
19202								$size['h'] /= 100;
19203								$h *= $size['h'];
19204							} elseif ($size['h'] != 'auto') {
19205								$h = $size['h'];
19206							}
19207						}
19208					}
19209					$this->pageBackgrounds[$blvl][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $w, 'h' => $h, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s, 'visibility' => $this->visibility, 'z-index' => $this->current_layer];
19210				}
19211			} else {
19212				$image_id = $this->blk[$blvl]['background-image']['image_id'];
19213				$orig_w = $this->blk[$blvl]['background-image']['orig_w'];
19214				$orig_h = $this->blk[$blvl]['background-image']['orig_h'];
19215				$x_pos = $this->blk[$blvl]['background-image']['x_pos'];
19216				$y_pos = $this->blk[$blvl]['background-image']['y_pos'];
19217				$x_repeat = $this->blk[$blvl]['background-image']['x_repeat'];
19218				$y_repeat = $this->blk[$blvl]['background-image']['y_repeat'];
19219				$resize = $this->blk[$blvl]['background-image']['resize'];
19220				$opacity = $this->blk[$blvl]['background-image']['opacity'];
19221				$itype = $this->blk[$blvl]['background-image']['itype'];
19222				$size = $this->blk[$blvl]['background-image']['size'];
19223				// origin specifies the background-positioning-area (bpa)
19224				$bpa = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h];
19225				if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {
19226					$bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'];
19227					$bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);
19228					if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
19229						$bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'];
19230					} else {
19231						$bpa['y'] = $y0;
19232					}
19233					if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
19234						$bpay = $y1 - $this->blk[$blvl]['border_bottom']['w'];
19235					} else {
19236						$bpay = $y1;
19237					}
19238					$bpa['h'] = $bpay - $bpa['y'];
19239				} elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {
19240					$bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
19241					$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']);
19242					if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
19243						$bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
19244					} else {
19245						$bpa['y'] = $y0 + $this->blk[$blvl]['padding_top'];
19246					}
19247					if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
19248						$bpay = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);
19249					} else {
19250						$bpay = $y1 - $this->blk[$blvl]['padding_bottom'];
19251					}
19252					$bpa['h'] = $bpay - $bpa['y'];
19253				}
19254				$this->pageBackgrounds[$blvl][] = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $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, 'visibility' => $this->visibility, 'z-index' => $this->current_layer, 'size' => $size, 'bpa' => $bpa];
19255			}
19256		}
19257		/* -- END BACKGROUNDS -- */
19258
19259		// Float DIV
19260		$this->blk[$blvl]['bb_painted'][$this->page] = true;
19261	}
19262
19263	/* -- BORDER-RADIUS -- */
19264
19265	function _EllipseArc($x0, $y0, $rx, $ry, $seg = 1, $part = false, $start = false)
19266	{
19267	// Anticlockwise segment 1-4 TR-TL-BL-BR (part=1 or 2)
19268		$s = '';
19269		if ($rx < 0) {
19270			$rx = 0;
19271		}
19272		if ($ry < 0) {
19273			$ry = 0;
19274		}
19275		$rx *= Mpdf::SCALE;
19276		$ry *= Mpdf::SCALE;
19277		$astart = 0;
19278		if ($seg == 1) { // Top Right
19279			$afinish = 90;
19280			$nSeg = 4;
19281		} elseif ($seg == 2) { // Top Left
19282			$afinish = 180;
19283			$nSeg = 8;
19284		} elseif ($seg == 3) { // Bottom Left
19285			$afinish = 270;
19286			$nSeg = 12;
19287		} else {   // Bottom Right
19288			$afinish = 360;
19289			$nSeg = 16;
19290		}
19291		$astart = deg2rad((float) $astart);
19292		$afinish = deg2rad((float) $afinish);
19293		$totalAngle = $afinish - $astart;
19294		$dt = $totalAngle / $nSeg; // segment angle
19295		$dtm = $dt / 3;
19296		$x0 *= Mpdf::SCALE;
19297		$y0 = ($this->h - $y0) * Mpdf::SCALE;
19298		$t1 = $astart;
19299		$a0 = $x0 + ($rx * cos($t1));
19300		$b0 = $y0 + ($ry * sin($t1));
19301		$c0 = -$rx * sin($t1);
19302		$d0 = $ry * cos($t1);
19303		$op = false;
19304		for ($i = 1; $i <= $nSeg; $i++) {
19305			// Draw this bit of the total curve
19306			$t1 = ($i * $dt) + $astart;
19307			$a1 = $x0 + ($rx * cos($t1));
19308			$b1 = $y0 + ($ry * sin($t1));
19309			$c1 = -$rx * sin($t1);
19310			$d1 = $ry * cos($t1);
19311			if ($i > ($nSeg - 4) && (!$part || ($part == 1 && $i <= $nSeg - 2) || ($part == 2 && $i > $nSeg - 2))) {
19312				if ($start && !$op) {
19313					$s .= sprintf('%.3F %.3F m ', $a0, $b0);
19314				}
19315				$s .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($a0 + ($c0 * $dtm)), ($b0 + ($d0 * $dtm)), ($a1 - ($c1 * $dtm)), ($b1 - ($d1 * $dtm)), $a1, $b1);
19316				$op = true;
19317			}
19318			$a0 = $a1;
19319			$b0 = $b1;
19320			$c0 = $c1;
19321			$d0 = $d1;
19322		}
19323		return $s;
19324	}
19325
19326	/* -- END BORDER-RADIUS -- */
19327
19328	function PaintDivLnBorder($state = 0, $blvl = 0, $h = 0)
19329	{
19330		// $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
19331		$this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
19332
19333		$save_y = $this->y;
19334
19335		$w = $this->blk[$blvl]['width'];
19336		$x0 = $this->x;    // left
19337		$y0 = $this->y;    // top
19338		$x1 = $this->x + $w;   // bottom
19339		$y1 = $this->y + $h;   // bottom
19340
19341		if ($this->blk[$blvl]['border_top'] && ($state == 1 || $state == 3)) {
19342			$tbd = $this->blk[$blvl]['border_top'];
19343			if (isset($tbd['s']) && $tbd['s']) {
19344				$this->_setBorderLine($tbd);
19345				$this->y = $y0 + ($tbd['w'] / 2);
19346				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
19347					$this->_setDashBorder($tbd['style'], '', $continuingpage, 'T');
19348					$this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);
19349				} else {
19350					$this->SetLineJoin(0);
19351					$this->SetLineCap(0);
19352					$this->Line($x0, $this->y, $x0 + $w, $this->y);
19353				}
19354				$this->y += $tbd['w'];
19355				// Reset Corners and Dash off
19356				$this->SetLineJoin(2);
19357				$this->SetLineCap(2);
19358				$this->SetDash();
19359			}
19360		}
19361		if ($this->blk[$blvl]['border_left']) {
19362			$tbd = $this->blk[$blvl]['border_left'];
19363			if (isset($tbd['s']) && $tbd['s']) {
19364				$this->_setBorderLine($tbd);
19365				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
19366					$this->y = $y0 + ($tbd['w'] / 2);
19367					$this->_setDashBorder($tbd['style'], '', $continuingpage, 'L');
19368					$this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));
19369				} else {
19370					$this->y = $y0;
19371					$this->SetLineJoin(0);
19372					$this->SetLineCap(0);
19373					$this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h);
19374				}
19375				$this->y += $tbd['w'];
19376				// Reset Corners and Dash off
19377				$this->SetLineJoin(2);
19378				$this->SetLineCap(2);
19379				$this->SetDash();
19380			}
19381		}
19382		if ($this->blk[$blvl]['border_right']) {
19383			$tbd = $this->blk[$blvl]['border_right'];
19384			if (isset($tbd['s']) && $tbd['s']) {
19385				$this->_setBorderLine($tbd);
19386				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
19387					$this->y = $y0 + ($tbd['w'] / 2);
19388					$this->_setDashBorder($tbd['style'], '', $continuingpage, 'R');
19389					$this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));
19390				} else {
19391					$this->y = $y0;
19392					$this->SetLineJoin(0);
19393					$this->SetLineCap(0);
19394					$this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h);
19395				}
19396				$this->y += $tbd['w'];
19397				// Reset Corners and Dash off
19398				$this->SetLineJoin(2);
19399				$this->SetLineCap(2);
19400				$this->SetDash();
19401			}
19402		}
19403		if ($this->blk[$blvl]['border_bottom'] && $state > 1) {
19404			$tbd = $this->blk[$blvl]['border_bottom'];
19405			if (isset($tbd['s']) && $tbd['s']) {
19406				$this->_setBorderLine($tbd);
19407				$this->y = $y0 + $h - ($tbd['w'] / 2);
19408				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
19409					$this->_setDashBorder($tbd['style'], '', $continuingpage, 'B');
19410					$this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);
19411				} else {
19412					$this->SetLineJoin(0);
19413					$this->SetLineCap(0);
19414					$this->Line($x0, $this->y, $x0 + $w, $this->y);
19415				}
19416				$this->y += $tbd['w'];
19417				// Reset Corners and Dash off
19418				$this->SetLineJoin(2);
19419				$this->SetLineCap(2);
19420				$this->SetDash();
19421			}
19422		}
19423		$this->SetDash();
19424		$this->y = $save_y;
19425	}
19426
19427	function PaintImgBorder($objattr, $is_table)
19428	{
19429		// Borders are disabled in columns - messes up the repositioning in printcolumnbuffer
19430		if ($this->ColActive) {
19431			return;
19432		} // *COLUMNS*
19433		if ($is_table) {
19434			$k = $this->shrin_k;
19435		} else {
19436			$k = 1;
19437		}
19438		$h = (isset($objattr['BORDER-HEIGHT']) ? $objattr['BORDER-HEIGHT'] : 0);
19439		$w = (isset($objattr['BORDER-WIDTH']) ? $objattr['BORDER-WIDTH'] : 0);
19440		$x0 = (isset($objattr['BORDER-X']) ? $objattr['BORDER-X'] : 0);
19441		$y0 = (isset($objattr['BORDER-Y']) ? $objattr['BORDER-Y'] : 0);
19442
19443		// BORDERS
19444		if ($objattr['border_top']) {
19445			$tbd = $objattr['border_top'];
19446			if (!empty($tbd['s'])) {
19447				$this->_setBorderLine($tbd, $k);
19448				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
19449					$this->_setDashBorder($tbd['style'], '', '', 'T');
19450				}
19451				$this->Line($x0, $y0, $x0 + $w, $y0);
19452				// Reset Corners and Dash off
19453				$this->SetLineJoin(2);
19454				$this->SetLineCap(2);
19455				$this->SetDash();
19456			}
19457		}
19458		if ($objattr['border_left']) {
19459			$tbd = $objattr['border_left'];
19460			if (!empty($tbd['s'])) {
19461				$this->_setBorderLine($tbd, $k);
19462				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
19463					$this->_setDashBorder($tbd['style'], '', '', 'L');
19464				}
19465				$this->Line($x0, $y0, $x0, $y0 + $h);
19466				// Reset Corners and Dash off
19467				$this->SetLineJoin(2);
19468				$this->SetLineCap(2);
19469				$this->SetDash();
19470			}
19471		}
19472		if ($objattr['border_right']) {
19473			$tbd = $objattr['border_right'];
19474			if (!empty($tbd['s'])) {
19475				$this->_setBorderLine($tbd, $k);
19476				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
19477					$this->_setDashBorder($tbd['style'], '', '', 'R');
19478				}
19479				$this->Line($x0 + $w, $y0, $x0 + $w, $y0 + $h);
19480				// Reset Corners and Dash off
19481				$this->SetLineJoin(2);
19482				$this->SetLineCap(2);
19483				$this->SetDash();
19484			}
19485		}
19486		if ($objattr['border_bottom']) {
19487			$tbd = $objattr['border_bottom'];
19488			if (!empty($tbd['s'])) {
19489				$this->_setBorderLine($tbd, $k);
19490				if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
19491					$this->_setDashBorder($tbd['style'], '', '', 'B');
19492				}
19493				$this->Line($x0, $y0 + $h, $x0 + $w, $y0 + $h);
19494				// Reset Corners and Dash off
19495				$this->SetLineJoin(2);
19496				$this->SetLineCap(2);
19497				$this->SetDash();
19498			}
19499		}
19500		$this->SetDash();
19501		$this->SetAlpha(1);
19502	}
19503
19504	/* -- END HTML-CSS -- */
19505
19506	function Reset()
19507	{
19508		$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
19509		$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
19510		$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
19511		$this->SetAlpha(1);
19512		$this->colorarray = '';
19513
19514		$this->spanbgcolorarray = '';
19515		$this->spanbgcolor = false;
19516		$this->spanborder = false;
19517		$this->spanborddet = [];
19518
19519		$this->ResetStyles();
19520
19521		$this->HREF = '';
19522		$this->textparam = [];
19523		$this->SetTextOutline();
19524
19525		$this->textvar = 0x00; // mPDF 5.7.1
19526		$this->OTLtags = [];
19527		$this->textshadow = '';
19528
19529		$this->currentLang = $this->default_lang;  // mPDF 6
19530		$this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6
19531		$this->SetFont($this->default_font, '', 0, false);
19532		$this->SetFontSize($this->default_font_size, false);
19533
19534		$this->currentfontfamily = '';
19535		$this->currentfontsize = '';
19536		$this->currentfontstyle = '';
19537
19538		/* -- TABLES -- */
19539		if ($this->tableLevel && isset($this->table[1][1]['cellLineHeight'])) {
19540			$this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES*
19541		} else { 		/* -- END TABLES -- */
19542			if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {
19543				$this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height
19544			}
19545		}
19546
19547		$this->lSpacingCSS = '';
19548		$this->wSpacingCSS = '';
19549		$this->fixedlSpacing = false;
19550		$this->minwSpacing = 0;
19551		$this->SetDash(); // restore to no dash
19552		$this->dash_on = false;
19553		$this->dotted_on = false;
19554		$this->divwidth = 0;
19555		$this->divheight = 0;
19556		$this->cellTextAlign = '';
19557		$this->cellLineHeight = '';
19558		$this->cellLineStackingStrategy = '';
19559		$this->cellLineStackingShift = '';
19560		$this->oldy = -1;
19561
19562		$bodystyle = [];
19563		if (isset($this->cssManager->CSS['BODY']['FONT-STYLE'])) {
19564			$bodystyle['FONT-STYLE'] = $this->cssManager->CSS['BODY']['FONT-STYLE'];
19565		}
19566		if (isset($this->cssManager->CSS['BODY']['FONT-WEIGHT'])) {
19567			$bodystyle['FONT-WEIGHT'] = $this->cssManager->CSS['BODY']['FONT-WEIGHT'];
19568		}
19569		if (isset($this->cssManager->CSS['BODY']['COLOR'])) {
19570			$bodystyle['COLOR'] = $this->cssManager->CSS['BODY']['COLOR'];
19571		}
19572		if (isset($bodystyle)) {
19573			$this->setCSS($bodystyle, 'BLOCK', 'BODY');
19574		}
19575	}
19576
19577	/* -- HTML-CSS -- */
19578
19579	function ReadMetaTags($html)
19580	{
19581		// changes anykey=anyvalue to anykey="anyvalue" (only do this when this happens inside tags)
19582		$regexp = '/ (\\w+?)=([^\\s>"]+)/si';
19583		$html = preg_replace($regexp, " \$1=\"\$2\"", $html);
19584		if (preg_match('/<title>(.*?)<\/title>/si', $html, $m)) {
19585			$this->SetTitle($m[1]);
19586		}
19587		preg_match_all('/<meta [^>]*?(name|content)="([^>]*?)" [^>]*?(name|content)="([^>]*?)".*?>/si', $html, $aux);
19588		$firstattr = $aux[1];
19589		$secondattr = $aux[3];
19590		for ($i = 0; $i < count($aux[0]); $i++) {
19591			$name = ( strtoupper($firstattr[$i]) == "NAME" ) ? strtoupper($aux[2][$i]) : strtoupper($aux[4][$i]);
19592			$content = ( strtoupper($firstattr[$i]) == "CONTENT" ) ? $aux[2][$i] : $aux[4][$i];
19593			switch ($name) {
19594				case "KEYWORDS":
19595					$this->SetKeywords($content);
19596					break;
19597				case "AUTHOR":
19598					$this->SetAuthor($content);
19599					break;
19600				case "DESCRIPTION":
19601					$this->SetSubject($content);
19602					break;
19603			}
19604		}
19605	}
19606
19607	function ReadCharset($html)
19608	{
19609		// Charset conversion
19610		if ($this->allow_charset_conversion) {
19611			if (preg_match('/<head.*charset=([^\'\"\s]*).*<\/head>/si', $html, $m)) {
19612				if (strtoupper($m[1]) != 'UTF-8') {
19613					$this->charset_in = strtoupper($m[1]);
19614				}
19615			}
19616		}
19617	}
19618
19619	function setCSS($arrayaux, $type = '', $tag = '')
19620	{
19621	// type= INLINE | BLOCK | TABLECELL // tag= BODY
19622		if (!is_array($arrayaux)) {
19623			return; // Removes PHP Warning
19624		}
19625
19626		// mPDF 5.7.3  inline text-decoration parameters
19627		$preceeding_fontkey = $this->FontFamily . $this->FontStyle;
19628		$preceeding_fontsize = $this->FontSize;
19629		$spanbordset = false;
19630		$spanbgset = false;
19631		// mPDF 6
19632		$prevlevel = (($this->blklvl == 0) ? 0 : $this->blklvl - 1);
19633
19634		// Set font size first so that e.g. MARGIN 0.83em works on font size for this element
19635		if (isset($arrayaux['FONT-SIZE'])) {
19636			$v = $arrayaux['FONT-SIZE'];
19637			if (is_numeric($v[0])) {
19638				if ($type == 'BLOCK' && $this->blklvl > 0 && isset($this->blk[$this->blklvl - 1]['InlineProperties']) && isset($this->blk[$this->blklvl - 1]['InlineProperties']['size'])) {
19639					$mmsize = $this->sizeConverter->convert($v, $this->blk[$this->blklvl - 1]['InlineProperties']['size']);
19640				} elseif ($type == 'TABLECELL') {
19641					$mmsize = $this->sizeConverter->convert($v, $this->default_font_size / Mpdf::SCALE);
19642				} else {
19643					$mmsize = $this->sizeConverter->convert($v, $this->FontSize);
19644				}
19645				$this->SetFontSize($mmsize * (Mpdf::SCALE), false); // Get size in points (pt)
19646			} else {
19647				$v = strtoupper($v);
19648				if (isset($this->fontsizes[$v])) {
19649					$this->SetFontSize($this->fontsizes[$v] * $this->default_font_size, false);
19650				}
19651			}
19652			if ($tag == 'BODY') {
19653				$this->SetDefaultFontSize($this->FontSizePt);
19654			}
19655		}
19656
19657		// mPDF 6
19658		if (isset($arrayaux['LANG']) && $arrayaux['LANG']) {
19659			if ($this->autoLangToFont && !$this->usingCoreFont) {
19660				if ($arrayaux['LANG'] != $this->default_lang && $arrayaux['LANG'] != 'UTF-8') {
19661					list ($coreSuitable, $mpdf_pdf_unifont) = $this->languageToFont->getLanguageOptions($arrayaux['LANG'], $this->useAdobeCJK);
19662					if ($mpdf_pdf_unifont) {
19663						$arrayaux['FONT-FAMILY'] = $mpdf_pdf_unifont;
19664					}
19665					if ($tag == 'BODY') {
19666						$this->default_lang = $arrayaux['LANG'];
19667					}
19668				}
19669			}
19670			$this->currentLang = $arrayaux['LANG'];
19671		}
19672
19673		// FOR INLINE and BLOCK OR 'BODY'
19674		if (isset($arrayaux['FONT-FAMILY'])) {
19675			$v = $arrayaux['FONT-FAMILY'];
19676			// If it is a font list, get all font types
19677			$aux_fontlist = explode(",", $v);
19678			$found = 0;
19679			foreach ($aux_fontlist as $f) {
19680				$fonttype = trim($f);
19681				$fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
19682				$fonttype = preg_replace('/ /', '', $fonttype);
19683				$v = strtolower(trim($fonttype));
19684				if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {
19685					$v = $this->fonttrans[$v];
19686				}
19687				if ((!$this->onlyCoreFonts && in_array($v, $this->available_unifonts)) ||
19688					in_array($v, ['ccourier', 'ctimes', 'chelvetica']) ||
19689					($this->onlyCoreFonts && in_array($v, ['courier', 'times', 'helvetica', 'arial'])) ||
19690					in_array($v, ['sjis', 'uhc', 'big5', 'gb'])) {
19691					$fonttype = $v;
19692					$found = 1;
19693					break;
19694				}
19695			}
19696			if (!$found) {
19697				foreach ($aux_fontlist as $f) {
19698					$fonttype = trim($f);
19699					$fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
19700					$fonttype = preg_replace('/ /', '', $fonttype);
19701					$v = strtolower(trim($fonttype));
19702					if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {
19703						$v = $this->fonttrans[$v];
19704					}
19705					if (in_array($v, $this->sans_fonts) || in_array($v, $this->serif_fonts) || in_array($v, $this->mono_fonts)) {
19706						$fonttype = $v;
19707						break;
19708					}
19709				}
19710			}
19711
19712			if ($tag == 'BODY') {
19713				$this->SetDefaultFont($fonttype);
19714			}
19715			$this->SetFont($fonttype, $this->currentfontstyle, 0, false);
19716		} else {
19717			$this->SetFont($this->currentfontfamily, $this->currentfontstyle, 0, false);
19718		}
19719
19720		foreach ($arrayaux as $k => $v) {
19721			if ($type != 'INLINE' && $tag != 'BODY' && $type != 'TABLECELL') {
19722				switch ($k) {
19723					// BORDERS
19724					case 'BORDER-TOP':
19725						$this->blk[$this->blklvl]['border_top'] = $this->border_details($v);
19726						if ($this->blk[$this->blklvl]['border_top']['s']) {
19727							$this->blk[$this->blklvl]['border'] = 1;
19728						}
19729						break;
19730					case 'BORDER-BOTTOM':
19731						$this->blk[$this->blklvl]['border_bottom'] = $this->border_details($v);
19732						if ($this->blk[$this->blklvl]['border_bottom']['s']) {
19733							$this->blk[$this->blklvl]['border'] = 1;
19734						}
19735						break;
19736					case 'BORDER-LEFT':
19737						$this->blk[$this->blklvl]['border_left'] = $this->border_details($v);
19738						if ($this->blk[$this->blklvl]['border_left']['s']) {
19739							$this->blk[$this->blklvl]['border'] = 1;
19740						}
19741						break;
19742					case 'BORDER-RIGHT':
19743						$this->blk[$this->blklvl]['border_right'] = $this->border_details($v);
19744						if ($this->blk[$this->blklvl]['border_right']['s']) {
19745							$this->blk[$this->blklvl]['border'] = 1;
19746						}
19747						break;
19748
19749					// PADDING
19750					case 'PADDING-TOP':
19751						$this->blk[$this->blklvl]['padding_top'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19752						break;
19753					case 'PADDING-BOTTOM':
19754						$this->blk[$this->blklvl]['padding_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19755						break;
19756					case 'PADDING-LEFT':
19757						if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {
19758							$this->blk[$this->blklvl]['padding_left'] = 'auto';
19759							break;
19760						}
19761						$this->blk[$this->blklvl]['padding_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19762						break;
19763					case 'PADDING-RIGHT':
19764						if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {
19765							$this->blk[$this->blklvl]['padding_right'] = 'auto';
19766							break;
19767						}
19768						$this->blk[$this->blklvl]['padding_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19769						break;
19770
19771					// MARGINS
19772					case 'MARGIN-TOP':
19773						$tmp = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19774						if (isset($this->blk[$this->blklvl]['lastbottommargin'])) {
19775							if ($tmp > $this->blk[$this->blklvl]['lastbottommargin']) {
19776								$tmp -= $this->blk[$this->blklvl]['lastbottommargin'];
19777							} else {
19778								$tmp = 0;
19779							}
19780						}
19781						$this->blk[$this->blklvl]['margin_top'] = $tmp;
19782						break;
19783					case 'MARGIN-BOTTOM':
19784						$this->blk[$this->blklvl]['margin_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19785						break;
19786					case 'MARGIN-LEFT':
19787						$this->blk[$this->blklvl]['margin_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19788						break;
19789					case 'MARGIN-RIGHT':
19790						$this->blk[$this->blklvl]['margin_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19791						break;
19792
19793					/* -- BORDER-RADIUS -- */
19794					case 'BORDER-TOP-LEFT-RADIUS-H':
19795						$this->blk[$this->blklvl]['border_radius_TL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19796						break;
19797					case 'BORDER-TOP-LEFT-RADIUS-V':
19798						$this->blk[$this->blklvl]['border_radius_TL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19799						break;
19800					case 'BORDER-TOP-RIGHT-RADIUS-H':
19801						$this->blk[$this->blklvl]['border_radius_TR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19802						break;
19803					case 'BORDER-TOP-RIGHT-RADIUS-V':
19804						$this->blk[$this->blklvl]['border_radius_TR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19805						break;
19806					case 'BORDER-BOTTOM-LEFT-RADIUS-H':
19807						$this->blk[$this->blklvl]['border_radius_BL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19808						break;
19809					case 'BORDER-BOTTOM-LEFT-RADIUS-V':
19810						$this->blk[$this->blklvl]['border_radius_BL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19811						break;
19812					case 'BORDER-BOTTOM-RIGHT-RADIUS-H':
19813						$this->blk[$this->blklvl]['border_radius_BR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19814						break;
19815					case 'BORDER-BOTTOM-RIGHT-RADIUS-V':
19816						$this->blk[$this->blklvl]['border_radius_BR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19817						break;
19818					/* -- END BORDER-RADIUS -- */
19819
19820					case 'BOX-SHADOW':
19821						$bs = $this->cssManager->setCSSboxshadow($v);
19822						if ($bs) {
19823							$this->blk[$this->blklvl]['box_shadow'] = $bs;
19824						}
19825						break;
19826
19827					case 'BACKGROUND-CLIP':
19828						if (strtoupper($v) == 'PADDING-BOX') {
19829							$this->blk[$this->blklvl]['background_clip'] = 'padding-box';
19830						} elseif (strtoupper($v) == 'CONTENT-BOX') {
19831							$this->blk[$this->blklvl]['background_clip'] = 'content-box';
19832						}
19833						break;
19834
19835					case 'PAGE-BREAK-AFTER':
19836						if (strtoupper($v) == 'AVOID') {
19837							$this->blk[$this->blklvl]['page_break_after_avoid'] = true;
19838						} elseif (strtoupper($v) == 'ALWAYS' || strtoupper($v) == 'LEFT' || strtoupper($v) == 'RIGHT') {
19839							$this->blk[$this->blklvl]['page_break_after'] = strtoupper($v);
19840						}
19841						break;
19842
19843					// mPDF 6 pagebreaktype
19844					case 'BOX-DECORATION-BREAK':
19845						if (strtoupper($v) == 'CLONE') {
19846							$this->blk[$this->blklvl]['box_decoration_break'] = 'clone';
19847						} elseif (strtoupper($v) == 'SLICE') {
19848							$this->blk[$this->blklvl]['box_decoration_break'] = 'slice';
19849						}
19850						break;
19851
19852					case 'WIDTH':
19853						if (strtoupper($v) != 'AUTO') {
19854							$this->blk[$this->blklvl]['css_set_width'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
19855						}
19856						break;
19857
19858					// mPDF 6  Lists
19859					// LISTS
19860					case 'LIST-STYLE-TYPE':
19861						$this->blk[$this->blklvl]['list_style_type'] = strtolower($v);
19862						break;
19863					case 'LIST-STYLE-IMAGE':
19864						$this->blk[$this->blklvl]['list_style_image'] = strtolower($v);
19865						break;
19866					case 'LIST-STYLE-POSITION':
19867						$this->blk[$this->blklvl]['list_style_position'] = strtolower($v);
19868						break;
19869				}//end of switch($k)
19870			}
19871
19872
19873			if ($type != 'INLINE' && $type != 'TABLECELL') { // All block-level, including BODY tag
19874				switch ($k) {
19875					case 'TEXT-INDENT':
19876						// Computed value - to inherit
19877						$this->blk[$this->blklvl]['text_indent'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false) . 'mm';
19878						break;
19879
19880					case 'MARGIN-COLLAPSE': // Custom tag to collapse margins at top and bottom of page
19881						if (strtoupper($v) == 'COLLAPSE') {
19882							$this->blk[$this->blklvl]['margin_collapse'] = true;
19883						}
19884						break;
19885
19886					case 'LINE-HEIGHT':
19887						$this->blk[$this->blklvl]['line_height'] = $this->fixLineheight($v);
19888						if (!$this->blk[$this->blklvl]['line_height']) {
19889							$this->blk[$this->blklvl]['line_height'] = 'N';
19890						} // mPDF 6
19891						break;
19892
19893					// mPDF 6
19894					case 'LINE-STACKING-STRATEGY':
19895						$this->blk[$this->blklvl]['line_stacking_strategy'] = strtolower($v);
19896						break;
19897
19898					case 'LINE-STACKING-SHIFT':
19899						$this->blk[$this->blklvl]['line_stacking_shift'] = strtolower($v);
19900						break;
19901
19902					case 'TEXT-ALIGN': // left right center justify
19903						switch (strtoupper($v)) {
19904							case 'LEFT':
19905								$this->blk[$this->blklvl]['align'] = "L";
19906								break;
19907							case 'CENTER':
19908								$this->blk[$this->blklvl]['align'] = "C";
19909								break;
19910							case 'RIGHT':
19911								$this->blk[$this->blklvl]['align'] = "R";
19912								break;
19913							case 'JUSTIFY':
19914								$this->blk[$this->blklvl]['align'] = "J";
19915								break;
19916						}
19917						break;
19918
19919					/* -- BACKGROUNDS -- */
19920					case 'BACKGROUND-GRADIENT':
19921						if ($type == 'BLOCK') {
19922							$this->blk[$this->blklvl]['gradient'] = $v;
19923						}
19924						break;
19925					/* -- END BACKGROUNDS -- */
19926
19927					case 'DIRECTION':
19928						if ($v) {
19929							$this->blk[$this->blklvl]['direction'] = strtolower($v);
19930						}
19931						break;
19932				}
19933			}
19934
19935			// FOR INLINE ONLY
19936			if ($type == 'INLINE') {
19937				switch ($k) {
19938					case 'DISPLAY':
19939						if (strtoupper($v) == 'NONE') {
19940							$this->inlineDisplayOff = true;
19941						}
19942						break;
19943					case 'DIRECTION':
19944						break;
19945				}
19946			}
19947			// FOR INLINE ONLY
19948			if ($type == 'INLINE') {
19949				switch ($k) {
19950					// BORDERS
19951					case 'BORDER-TOP':
19952						$this->spanborddet['T'] = $this->border_details($v);
19953						$this->spanborder = true;
19954						$spanbordset = true;
19955						break;
19956					case 'BORDER-BOTTOM':
19957						$this->spanborddet['B'] = $this->border_details($v);
19958						$this->spanborder = true;
19959						$spanbordset = true;
19960						break;
19961					case 'BORDER-LEFT':
19962						$this->spanborddet['L'] = $this->border_details($v);
19963						$this->spanborder = true;
19964						$spanbordset = true;
19965						break;
19966					case 'BORDER-RIGHT':
19967						$this->spanborddet['R'] = $this->border_details($v);
19968						$this->spanborder = true;
19969						$spanbordset = true;
19970						break;
19971					case 'VISIBILITY': // block is set in OpenTag
19972						$v = strtolower($v);
19973						if ($v == 'visible' || $v == 'hidden' || $v == 'printonly' || $v == 'screenonly') {
19974							$this->textparam['visibility'] = $v;
19975						}
19976						break;
19977				}//end of switch($k)
19978			}
19979
19980			if ($type != 'TABLECELL') {
19981				// FOR INLINE and BLOCK
19982				switch ($k) {
19983					case 'TEXT-ALIGN': // left right center justify
19984						if (strtoupper($v) == 'NOJUSTIFY' && $this->blk[$this->blklvl]['align'] == "J") {
19985							$this->blk[$this->blklvl]['align'] = "";
19986						}
19987						break;
19988					// bgcolor only - to stay consistent with original html2fpdf
19989					case 'BACKGROUND':
19990					case 'BACKGROUND-COLOR':
19991						$cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
19992						if ($cor) {
19993							if ($tag == 'BODY') {
19994								$this->bodyBackgroundColor = $cor;
19995							} elseif ($type == 'INLINE') {
19996								$this->spanbgcolorarray = $cor;
19997								$this->spanbgcolor = true;
19998								$spanbgset = true;
19999							} else {
20000								$this->blk[$this->blklvl]['bgcolorarray'] = $cor;
20001								$this->blk[$this->blklvl]['bgcolor'] = true;
20002							}
20003						} elseif ($type != 'INLINE') {
20004							if ($this->ColActive) {
20005								$this->blk[$this->blklvl]['bgcolorarray'] = $this->blk[$prevlevel]['bgcolorarray'];
20006								$this->blk[$this->blklvl]['bgcolor'] = $this->blk[$prevlevel]['bgcolor'];
20007							}
20008						}
20009						break;
20010
20011					case 'VERTICAL-ALIGN': // super and sub only dealt with here e.g. <SUB> and <SUP>
20012						switch (strtoupper($v)) {
20013							case 'SUPER':
20014								$this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT); // mPDF 5.7.1
20015								$this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
20016								// mPDF 5.7.3  inline text-decoration parameters
20017								if (isset($this->textparam['text-baseline'])) {
20018									$this->textparam['text-baseline'] += ($this->baselineSup) * $preceeding_fontsize;
20019								} else {
20020									$this->textparam['text-baseline'] = ($this->baselineSup) * $preceeding_fontsize;
20021								}
20022								break;
20023							case 'SUB':
20024								$this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT);
20025								$this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
20026								// mPDF 5.7.3  inline text-decoration parameters
20027								if (isset($this->textparam['text-baseline'])) {
20028									$this->textparam['text-baseline'] += ($this->baselineSub) * $preceeding_fontsize;
20029								} else {
20030									$this->textparam['text-baseline'] = ($this->baselineSub) * $preceeding_fontsize;
20031								}
20032								break;
20033							case 'BASELINE':
20034								$this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
20035								$this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
20036								// mPDF 5.7.3  inline text-decoration parameters
20037								if (isset($this->textparam['text-baseline'])) {
20038									unset($this->textparam['text-baseline']);
20039								}
20040								break;
20041							// mPDF 5.7.3  inline text-decoration parameters
20042							default:
20043								$lh = $this->_computeLineheight($this->blk[$this->blklvl]['line_height']);
20044								$sz = $this->sizeConverter->convert($v, $lh, $this->FontSize, false);
20045								$this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);
20046								$this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);
20047								if ($sz) {
20048									if ($sz > 0) {
20049										$this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT);
20050									} else {
20051										$this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT);
20052									}
20053									if (isset($this->textparam['text-baseline'])) {
20054										$this->textparam['text-baseline'] += $sz;
20055									} else {
20056										$this->textparam['text-baseline'] = $sz;
20057									}
20058								}
20059						}
20060						break;
20061				}//end of switch($k)
20062			}
20063
20064
20065			// FOR ALL
20066			switch ($k) {
20067				case 'LETTER-SPACING':
20068					$this->lSpacingCSS = $v;
20069					if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
20070						$this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize);
20071					}
20072					break;
20073
20074				case 'WORD-SPACING':
20075					$this->wSpacingCSS = $v;
20076					if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
20077						$this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize);
20078					}
20079					break;
20080
20081				case 'FONT-STYLE': // italic normal oblique
20082					switch (strtoupper($v)) {
20083						case 'ITALIC':
20084						case 'OBLIQUE':
20085							$this->SetStyle('I', true);
20086							break;
20087						case 'NORMAL':
20088							$this->SetStyle('I', false);
20089							break;
20090					}
20091					break;
20092
20093				case 'FONT-WEIGHT': // normal bold // Does not support: bolder, lighter, 100..900(step value=100)
20094					switch (strtoupper($v)) {
20095						case 'BOLD':
20096							$this->SetStyle('B', true);
20097							break;
20098						case 'NORMAL':
20099							$this->SetStyle('B', false);
20100							break;
20101					}
20102					break;
20103
20104				case 'FONT-KERNING':
20105					if (strtoupper($v) == 'NORMAL' || (strtoupper($v) == 'AUTO' && $this->useKerning)) {
20106						/* -- OTL -- */
20107						if ($this->CurrentFont['haskernGPOS']) {
20108							if (isset($this->OTLtags['Plus'])) {
20109								$this->OTLtags['Plus'] .= ' kern';
20110							} else {
20111								$this->OTLtags['Plus'] = ' kern';
20112							}
20113						} /* -- END OTL -- */ else {  // *OTL*
20114							$this->textvar = ($this->textvar | TextVars::FC_KERNING);
20115						} // *OTL*
20116					} elseif (strtoupper($v) == 'NONE' || (strtoupper($v) == 'AUTO' && !$this->useKerning)) {
20117						if (isset($this->OTLtags['Plus'])) {
20118							$this->OTLtags['Plus'] = str_replace('kern', '', $this->OTLtags['Plus']); // *OTL*
20119						}
20120						if (isset($this->OTLtags['FFPlus'])) {
20121							$this->OTLtags['FFPlus'] = preg_replace('/kern[\d]*/', '', $this->OTLtags['FFPlus']);
20122						}
20123						$this->textvar = ($this->textvar & ~TextVars::FC_KERNING);
20124					}
20125					break;
20126
20127				/* -- OTL -- */
20128				case 'FONT-LANGUAGE-OVERRIDE':
20129					$v = strtoupper($v);
20130					if (strpos($v, 'NORMAL') !== false) {
20131						$this->fontLanguageOverride = '';
20132					} else {
20133						$this->fontLanguageOverride = trim($v);
20134					}
20135					break;
20136
20137
20138				case 'FONT-VARIANT-POSITION':
20139					if (isset($this->OTLtags['Plus'])) {
20140						$this->OTLtags['Plus'] = str_replace(['sups', 'subs'], '', $this->OTLtags['Plus']);
20141					}
20142					switch (strtoupper($v)) {
20143						case 'SUPER':
20144							$this->OTLtags['Plus'] .= ' sups';
20145							break;
20146						case 'SUB':
20147							$this->OTLtags['Plus'] .= ' subs';
20148							break;
20149						case 'NORMAL':
20150							break;
20151					}
20152					break;
20153
20154				case 'FONT-VARIANT-CAPS':
20155					$v = strtoupper($v);
20156					if (!isset($this->OTLtags['Plus'])) {
20157						$this->OTLtags['Plus'] = '';
20158					}
20159					$this->OTLtags['Plus'] = str_replace(['c2sc', 'smcp', 'c2pc', 'pcap', 'unic', 'titl'], '', $this->OTLtags['Plus']);
20160					$this->textvar = ($this->textvar & ~TextVars::FC_SMALLCAPS);   // ?????????????? <small-caps>
20161					if (strpos($v, 'ALL-SMALL-CAPS') !== false) {
20162						$this->OTLtags['Plus'] .= ' c2sc smcp';
20163					} elseif (strpos($v, 'SMALL-CAPS') !== false) {
20164						if (isset($this->CurrentFont['hassmallcapsGSUB']) && $this->CurrentFont['hassmallcapsGSUB']) {
20165							$this->OTLtags['Plus'] .= ' smcp';
20166						} else {
20167							$this->textvar = ($this->textvar | TextVars::FC_SMALLCAPS);
20168						}
20169					} elseif (strpos($v, 'ALL-PETITE-CAPS') !== false) {
20170						$this->OTLtags['Plus'] .= ' c2pc pcap';
20171					} elseif (strpos($v, 'PETITE-CAPS') !== false) {
20172						$this->OTLtags['Plus'] .= ' pcap';
20173					} elseif (strpos($v, 'UNICASE') !== false) {
20174						$this->OTLtags['Plus'] .= ' unic';
20175					} elseif (strpos($v, 'TITLING-CAPS') !== false) {
20176						$this->OTLtags['Plus'] .= ' titl';
20177					}
20178					break;
20179
20180				case 'FONT-VARIANT-LIGATURES':
20181					$v = strtoupper($v);
20182					if (!isset($this->OTLtags['Plus'])) {
20183						$this->OTLtags['Plus'] = '';
20184					}
20185					if (!isset($this->OTLtags['Minus'])) {
20186						$this->OTLtags['Minus'] = '';
20187					}
20188					if (strpos($v, 'NORMAL') !== false) {
20189						$this->OTLtags['Minus'] = str_replace(['liga', 'clig', 'calt'], '', $this->OTLtags['Minus']);
20190						$this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']);
20191					} elseif (strpos($v, 'NONE') !== false) {
20192						$this->OTLtags['Minus'] .= ' liga clig calt';
20193						$this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']);
20194					}
20195					if (strpos($v, 'NO-COMMON-LIGATURES') !== false) {
20196						$this->OTLtags['Minus'] .= ' liga clig';
20197					} elseif (strpos($v, 'COMMON-LIGATURES') !== false) {
20198						$this->OTLtags['Minus'] = str_replace(['liga', 'clig'], '', $this->OTLtags['Minus']);
20199					}
20200					if (strpos($v, 'NO-CONTEXTUAL') !== false) {
20201						$this->OTLtags['Minus'] .= ' calt';
20202					} elseif (strpos($v, 'CONTEXTUAL') !== false) {
20203						$this->OTLtags['Minus'] = str_replace('calt', '', $this->OTLtags['Minus']);
20204					}
20205					if (strpos($v, 'NO-DISCRETIONARY-LIGATURES') !== false) {
20206						$this->OTLtags['Plus'] = str_replace('dlig', '', $this->OTLtags['Plus']);
20207					} elseif (strpos($v, 'DISCRETIONARY-LIGATURES') !== false) {
20208						$this->OTLtags['Plus'] .= ' dlig';
20209					}
20210					if (strpos($v, 'NO-HISTORICAL-LIGATURES') !== false) {
20211						$this->OTLtags['Plus'] = str_replace('hlig', '', $this->OTLtags['Plus']);
20212					} elseif (strpos($v, 'HISTORICAL-LIGATURES') !== false) {
20213						$this->OTLtags['Plus'] .= ' hlig';
20214					}
20215
20216					break;
20217
20218				case 'FONT-VARIANT-NUMERIC':
20219					$v = strtoupper($v);
20220					if (!isset($this->OTLtags['Plus'])) {
20221						$this->OTLtags['Plus'] = '';
20222					}
20223					if (strpos($v, 'NORMAL') !== false) {
20224						$this->OTLtags['Plus'] = str_replace(['ordn', 'zero', 'lnum', 'onum', 'pnum', 'tnum', 'frac', 'afrc'], '', $this->OTLtags['Plus']);
20225					}
20226					if (strpos($v, 'ORDINAL') !== false) {
20227						$this->OTLtags['Plus'] .= ' ordn';
20228					}
20229					if (strpos($v, 'SLASHED-ZERO') !== false) {
20230						$this->OTLtags['Plus'] .= ' zero';
20231					}
20232					if (strpos($v, 'LINING-NUMS') !== false) {
20233						$this->OTLtags['Plus'] .= ' lnum';
20234						$this->OTLtags['Plus'] = str_replace('onum', '', $this->OTLtags['Plus']);
20235					} elseif (strpos($v, 'OLDSTYLE-NUMS') !== false) {
20236						$this->OTLtags['Plus'] .= ' onum';
20237						$this->OTLtags['Plus'] = str_replace('lnum', '', $this->OTLtags['Plus']);
20238					}
20239					if (strpos($v, 'PROPORTIONAL-NUMS') !== false) {
20240						$this->OTLtags['Plus'] .= ' pnum';
20241						$this->OTLtags['Plus'] = str_replace('tnum', '', $this->OTLtags['Plus']);
20242					} elseif (strpos($v, 'TABULAR-NUMS') !== false) {
20243						$this->OTLtags['Plus'] .= ' tnum';
20244						$this->OTLtags['Plus'] = str_replace('pnum', '', $this->OTLtags['Plus']);
20245					}
20246					if (strpos($v, 'DIAGONAL-FRACTIONS') !== false) {
20247						$this->OTLtags['Plus'] .= ' frac';
20248						$this->OTLtags['Plus'] = str_replace('afrc', '', $this->OTLtags['Plus']);
20249					} elseif (strpos($v, 'STACKED-FRACTIONS') !== false) {
20250						$this->OTLtags['Plus'] .= ' afrc';
20251						$this->OTLtags['Plus'] = str_replace('frac', '', $this->OTLtags['Plus']);
20252					}
20253					break;
20254
20255				case 'FONT-VARIANT-ALTERNATES':  // Only supports historical-forms
20256					$v = strtoupper($v);
20257					if (!isset($this->OTLtags['Plus'])) {
20258						$this->OTLtags['Plus'] = '';
20259					}
20260					if (strpos($v, 'NORMAL') !== false) {
20261						$this->OTLtags['Plus'] = str_replace('hist', '', $this->OTLtags['Plus']);
20262					}
20263					if (strpos($v, 'HISTORICAL-FORMS') !== false) {
20264						$this->OTLtags['Plus'] .= ' hist';
20265					}
20266					break;
20267
20268
20269				case 'FONT-FEATURE-SETTINGS':
20270					$v = strtolower($v);
20271					if (strpos($v, 'normal') !== false) {
20272						$this->OTLtags['FFMinus'] = '';
20273						$this->OTLtags['FFPlus'] = '';
20274					} else {
20275						if (!isset($this->OTLtags['FFPlus'])) {
20276							$this->OTLtags['FFPlus'] = '';
20277						}
20278						if (!isset($this->OTLtags['FFMinus'])) {
20279							$this->OTLtags['FFMinus'] = '';
20280						}
20281						$tags = preg_split('/[,]/', $v);
20282						foreach ($tags as $t) {
20283							if (preg_match('/[\"\']([a-zA-Z0-9]{4})[\"\']\s*(on|off|\d*){0,1}/', $t, $m)) {
20284								if ($m[2] == 'off' || $m[2] === '0') {
20285									if (strpos($this->OTLtags['FFMinus'], $m[1]) === false) {
20286										$this->OTLtags['FFMinus'] .= ' ' . $m[1];
20287									}
20288									$this->OTLtags['FFPlus'] = preg_replace('/' . $m[1] . '[\d]*/', '', $this->OTLtags['FFPlus']);
20289								} else {
20290									if ($m[2] == 'on') {
20291										$m[2] = '1';
20292									}
20293									if (strpos($this->OTLtags['FFPlus'], $m[1]) === false) {
20294										$this->OTLtags['FFPlus'] .= ' ' . $m[1] . $m[2];
20295									}
20296									$this->OTLtags['FFMinus'] = str_replace($m[1], '', $this->OTLtags['FFMinus']);
20297								}
20298							}
20299						}
20300					}
20301					break;
20302				/* -- END OTL -- */
20303
20304
20305				case 'TEXT-TRANSFORM': // none uppercase lowercase // Does support: capitalize
20306					switch (strtoupper($v)) { // Not working 100%
20307						case 'CAPITALIZE':
20308							$this->textvar = ($this->textvar | TextVars::FT_CAPITALIZE); // mPDF 5.7.1
20309							$this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
20310							$this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
20311							break;
20312						case 'UPPERCASE':
20313							$this->textvar = ($this->textvar | TextVars::FT_UPPERCASE); // mPDF 5.7.1
20314							$this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
20315							$this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
20316							break;
20317						case 'LOWERCASE':
20318							$this->textvar = ($this->textvar | TextVars::FT_LOWERCASE); // mPDF 5.7.1
20319							$this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
20320							$this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
20321							break;
20322						case 'NONE':
20323							break;
20324							$this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1
20325							$this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1
20326							$this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1
20327					}
20328					break;
20329
20330				case 'TEXT-SHADOW':
20331					$ts = $this->cssManager->setCSStextshadow($v);
20332					if ($ts) {
20333						$this->textshadow = $ts;
20334					}
20335					break;
20336
20337				case 'HYPHENS':
20338					if (strtoupper($v) == 'NONE') {
20339						$this->textparam['hyphens'] = 2;
20340					} elseif (strtoupper($v) == 'AUTO') {
20341						$this->textparam['hyphens'] = 1;
20342					} elseif (strtoupper($v) == 'MANUAL') {
20343						$this->textparam['hyphens'] = 0;
20344					}
20345					break;
20346
20347				case 'TEXT-OUTLINE':
20348					if (strtoupper($v) == 'NONE') {
20349						$this->textparam['outline-s'] = false;
20350					}
20351					break;
20352
20353				case 'TEXT-OUTLINE-WIDTH':
20354				case 'OUTLINE-WIDTH':
20355					switch (strtoupper($v)) {
20356						case 'THIN':
20357							$v = '0.03em';
20358							break;
20359						case 'MEDIUM':
20360							$v = '0.05em';
20361							break;
20362						case 'THICK':
20363							$v = '0.07em';
20364							break;
20365					}
20366					$w = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize);
20367					if ($w) {
20368						$this->textparam['outline-WIDTH'] = $w;
20369						$this->textparam['outline-s'] = true;
20370					} else {
20371						$this->textparam['outline-s'] = false;
20372					}
20373					break;
20374
20375				case 'TEXT-OUTLINE-COLOR':
20376				case 'OUTLINE-COLOR':
20377					if (strtoupper($v) == 'INVERT') {
20378						if ($this->colorarray) {
20379							$cor = $this->colorarray;
20380							$this->textparam['outline-COLOR'] = $this->colorConverter->invert($cor);
20381						} else {
20382							$this->textparam['outline-COLOR'] = $this->colorConverter->convert(255, $this->PDFAXwarnings);
20383						}
20384					} else {
20385						$cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
20386						if ($cor) {
20387							$this->textparam['outline-COLOR'] = $cor;
20388						}
20389					}
20390					break;
20391
20392				case 'COLOR': // font color
20393					$cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);
20394					if ($cor) {
20395						$this->colorarray = $cor;
20396						$this->SetTColor($cor);
20397					}
20398					break;
20399			}//end of switch($k)
20400		}//end of foreach
20401		// mPDF 5.7.3  inline text-decoration parameters
20402		// Needs to be set at the end - after vertical-align = super/sub, so that textparam['text-baseline'] is set
20403		if (isset($arrayaux['TEXT-DECORATION'])) {
20404			$v = $arrayaux['TEXT-DECORATION']; // none underline line-through (strikeout) // Does not support: blink
20405			if (stristr($v, 'LINE-THROUGH')) {
20406				$this->textvar = ($this->textvar | TextVars::FD_LINETHROUGH);
20407				// mPDF 5.7.3  inline text-decoration parameters
20408				if (isset($this->textparam['text-baseline'])) {
20409					$this->textparam['s-decoration']['baseline'] = $this->textparam['text-baseline'];
20410				} else {
20411					$this->textparam['s-decoration']['baseline'] = 0;
20412				}
20413				$this->textparam['s-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
20414				$this->textparam['s-decoration']['fontsize'] = $this->FontSize;
20415				$this->textparam['s-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
20416			}
20417			if (stristr($v, 'UNDERLINE')) {
20418				$this->textvar = ($this->textvar | TextVars::FD_UNDERLINE);
20419				// mPDF 5.7.3  inline text-decoration parameters
20420				if (isset($this->textparam['text-baseline'])) {
20421					$this->textparam['u-decoration']['baseline'] = $this->textparam['text-baseline'];
20422				} else {
20423					$this->textparam['u-decoration']['baseline'] = 0;
20424				}
20425				$this->textparam['u-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
20426				$this->textparam['u-decoration']['fontsize'] = $this->FontSize;
20427				$this->textparam['u-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
20428			}
20429			if (stristr($v, 'OVERLINE')) {
20430				$this->textvar = ($this->textvar | TextVars::FD_OVERLINE);
20431				// mPDF 5.7.3  inline text-decoration parameters
20432				if (isset($this->textparam['text-baseline'])) {
20433					$this->textparam['o-decoration']['baseline'] = $this->textparam['text-baseline'];
20434				} else {
20435					$this->textparam['o-decoration']['baseline'] = 0;
20436				}
20437				$this->textparam['o-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
20438				$this->textparam['o-decoration']['fontsize'] = $this->FontSize;
20439				$this->textparam['o-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
20440			}
20441			if (stristr($v, 'NONE')) {
20442				$this->textvar = ($this->textvar & ~TextVars::FD_UNDERLINE);
20443				$this->textvar = ($this->textvar & ~TextVars::FD_LINETHROUGH);
20444				$this->textvar = ($this->textvar & ~TextVars::FD_OVERLINE);
20445				// mPDF 5.7.3  inline text-decoration parameters
20446				if (isset($this->textparam['u-decoration'])) {
20447					unset($this->textparam['u-decoration']);
20448				}
20449				if (isset($this->textparam['s-decoration'])) {
20450					unset($this->textparam['s-decoration']);
20451				}
20452				if (isset($this->textparam['o-decoration'])) {
20453					unset($this->textparam['o-decoration']);
20454				}
20455			}
20456		}
20457		// mPDF 6
20458		if ($spanbordset) { // BORDER has been set on this INLINE element
20459			if (isset($this->textparam['text-baseline'])) {
20460				$this->textparam['bord-decoration']['baseline'] = $this->textparam['text-baseline'];
20461			} else {
20462				$this->textparam['bord-decoration']['baseline'] = 0;
20463			}
20464			$this->textparam['bord-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
20465			$this->textparam['bord-decoration']['fontsize'] = $this->FontSize;
20466		}
20467		if ($spanbgset) { // BACKGROUND[-COLOR] has been set on this INLINE element
20468			if (isset($this->textparam['text-baseline'])) {
20469				$this->textparam['bg-decoration']['baseline'] = $this->textparam['text-baseline'];
20470			} else {
20471				$this->textparam['bg-decoration']['baseline'] = 0;
20472			}
20473			$this->textparam['bg-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
20474			$this->textparam['bg-decoration']['fontsize'] = $this->FontSize;
20475		}
20476	}
20477
20478	/* -- END HTML-CSS -- */
20479
20480	function SetStyle($tag, $enable)
20481	{
20482		$this->$tag = $enable;
20483		$style = '';
20484		foreach (['B', 'I'] as $s) {
20485			if ($this->$s) {
20486				$style.=$s;
20487			}
20488		}
20489		$this->currentfontstyle = $style;
20490		$this->SetFont('', $style, 0, false);
20491	}
20492
20493	// Set multiple styles at one time
20494	function SetStylesArray($arr)
20495	{
20496		$style = '';
20497		foreach (['B', 'I'] as $s) {
20498			if (isset($arr[$s])) {
20499				if ($arr[$s]) {
20500					$this->$s = true;
20501					$style.=$s;
20502				} else {
20503					$this->$s = false;
20504				}
20505			} elseif ($this->$s) {
20506				$style.=$s;
20507			}
20508		}
20509		$this->currentfontstyle = $style;
20510		$this->SetFont('', $style, 0, false);
20511	}
20512
20513	// Set multiple styles at one $str e.g. "BI"
20514	function SetStyles($str)
20515	{
20516		$style = '';
20517		foreach (['B', 'I'] as $s) {
20518			if (strpos($str, $s) !== false) {
20519				$this->$s = true;
20520				$style.=$s;
20521			} else {
20522				$this->$s = false;
20523			}
20524		}
20525		$this->currentfontstyle = $style;
20526		$this->SetFont('', $style, 0, false);
20527	}
20528
20529	function ResetStyles()
20530	{
20531		foreach (['B', 'I'] as $s) {
20532			$this->$s = false;
20533		}
20534		$this->currentfontstyle = '';
20535		$this->SetFont('', '', 0, false);
20536	}
20537
20538	function DisableTags($str = '')
20539	{
20540		if ($str == '') { // enable all tags
20541			// Insert new supported tags in the long string below.
20542			$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>";
20543		} else {
20544			$str = explode(",", $str);
20545			foreach ($str as $v) {
20546				$this->enabledtags = str_replace(trim($v), '', $this->enabledtags);
20547			}
20548		}
20549	}
20550
20551	/* -- TABLES -- */
20552
20553	function TableCheckMinWidth($maxwidth, $forcewrap = 0, $textbuffer = [], $checkletter = false)
20554	{
20555	// mPDF 6
20556		$acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)
20557		$acclongest = 0; // mPDF 6 (accumulated length max across > 1 chunk)
20558		$biggestword = 0;
20559		$toonarrow = false;
20560		if ((count($textbuffer) == 0) or ( (count($textbuffer) == 1) && ($textbuffer[0][0] == ''))) {
20561			return 0;
20562		}
20563
20564		foreach ($textbuffer as $chunk) {
20565			$line = $chunk[0];
20566			$OTLdata = (isset($chunk[18]) ? $chunk[18] : null);
20567
20568			// mPDF ITERATION
20569			if ($this->iterationCounter) {
20570				$line = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $line);
20571			}
20572
20573			// IMAGES & FORM ELEMENTS
20574			if (substr($line, 0, 3) == "\xbb\xa4\xac") { // inline object - FORM element or IMAGE!
20575				$objattr = $this->_getObjAttr($line);
20576				if ($objattr['type'] != 'hr' && isset($objattr['width']) && ($objattr['width'] / $this->shrin_k) > ($maxwidth + 0.0001)) {
20577					if (($objattr['width'] / $this->shrin_k) > $biggestword) {
20578						$biggestword = ($objattr['width'] / $this->shrin_k);
20579					}
20580					$toonarrow = true;
20581				}
20582				continue;
20583			}
20584
20585			if ($line == "\n") {
20586				$acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)
20587				continue;
20588			}
20589			$line = trim($line);
20590			if (!empty($OTLdata)) {
20591				$this->otl->trimOTLdata($OTLdata, true, true);
20592			} // *OTL*
20593			// SET FONT SIZE/STYLE from $chunk[n]
20594			// FONTSIZE
20595			if (isset($chunk[11]) and $chunk[11] != '') {
20596				if ($this->shrin_k) {
20597					$this->SetFontSize($chunk[11] / $this->shrin_k, false);
20598				} else {
20599					$this->SetFontSize($chunk[11], false);
20600				}
20601			}
20602			// FONTFAMILY
20603			if (isset($chunk[4]) and $chunk[4] != '') {
20604				$font = $this->SetFont($chunk[4], $this->FontStyle, 0, false);
20605			}
20606			// B I
20607			if (isset($chunk[2]) and $chunk[2] != '') {
20608				$this->SetStyles($chunk[2]);
20609			}
20610
20611			$lbw = $rbw = 0; // Border widths
20612			if (isset($chunk[16]) && !empty($chunk[16])) { // Border
20613				$this->spanborddet = $chunk[16];
20614				$lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
20615				$rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
20616			}
20617			if (isset($chunk[15])) {   // Word spacing
20618				$this->wSpacingCSS = $chunk[15];
20619				if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
20620					$this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
20621				}
20622			}
20623			if (isset($chunk[14])) {   // Letter spacing
20624				$this->lSpacingCSS = $chunk[14];
20625				if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
20626					$this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
20627				}
20628			}
20629			if (isset($chunk[8])) { // mPDF 5.7.1
20630				$this->textvar = $chunk[8];
20631			}
20632
20633			// mPDF 6
20634			// If overflow==wrap ($checkletter) OR (No word breaks and contains CJK)
20635			if ($checkletter || (!preg_match('/(\xe2\x80\x8b| )/', trim($line)) && preg_match("/([" . $this->pregCJKchars . "])/u", $line) )) {
20636				if (preg_match("/([" . $this->pregCJKchars . "])/u", $line)) {
20637					$checkCJK = true;
20638				} else {
20639					$checkCJK = false;
20640				}
20641
20642				$letters = preg_split('//u', $line);
20643				foreach ($letters as $k => $letter) {
20644					// mPDF 6
20645					if ($checkCJK) {
20646						if (preg_match("/[" . $this->CJKleading . "]/u", $letter) && $k > 0) {
20647							$letter = $letters[$k - 1] . $letter;
20648						}
20649						if (preg_match("/[" . $this->CJKfollowing . "]/u", $letter) && $k < (count($letters) - 1)) {
20650							$letter = $letter . $letters[$k + 1];
20651						}
20652					}
20653
20654					$letterwidth = $this->GetStringWidth($letter, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here
20655					// so don't have to split OTLdata for each word
20656					if ($k == 0) {
20657						$letterwidth += $lbw;
20658					}
20659					if ($k == (count($letters) - 1)) {
20660						$letterwidth += $rbw;
20661					}
20662
20663					// Warn user that maxwidth is insufficient
20664					if ($letterwidth > $maxwidth + 0.0001) {
20665						if ($letterwidth > $biggestword) {
20666							$biggestword = $letterwidth;
20667						}
20668						$toonarrow = true;
20669					}
20670				}
20671			} else {
20672				// mPDF 6
20673				// Need to account for any XAdvance in GPOSinfo (OTLdata = $chunk[18])
20674				$wordXAdvance = [];
20675				if (isset($chunk[18]) && $chunk[18]) {
20676					preg_match_all('/(\xe2\x80\x8b| )/', $line, $spaces, PREG_OFFSET_CAPTURE); // U+200B Zero Width word boundary, or space
20677					$lastoffset = 0;
20678					$k = -1; // Added so that if no spaces found, "last word" later is calculated for the one and only word
20679					foreach ($spaces[0] as $k => $m) {
20680						$offset = $m[1];
20681						// ...TableCheckMinWidth...
20682						// At this point, BIDI not applied, Writing direction is not set, and XAdvanceL balances XAdvanceR
20683						for ($n = $lastoffset; $n < $offset; $n++) {
20684							if (isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {
20685								if (isset($wordXAdvance[$k])) {
20686									$wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
20687								} else {
20688									$wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
20689								}
20690							}
20691						}
20692						$lastoffset = $offset + 1;
20693					}
20694
20695					$k++;  // last word
20696					foreach ($chunk[18]['GPOSinfo'] as $n => $gpos) {
20697						if ($n >= $lastoffset && isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {
20698							if (isset($wordXAdvance[$k])) {
20699								$wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
20700							} else {
20701								$wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
20702							}
20703						}
20704					}
20705				}
20706
20707				$words = preg_split('/(\xe2\x80\x8b| )/', $line); // U+200B Zero Width word boundary, or space
20708				foreach ($words as $k => $word) {
20709					$word = trim($word);
20710					$wordwidth = $this->GetStringWidth($word, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here
20711					// so don't have to split OTLdata for each word
20712					if (isset($wordXAdvance[$k])) {
20713						$wordwidth += ($wordXAdvance[$k] * 1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000);
20714					}
20715					if ($k == 0) {
20716						$wordwidth += $lbw;
20717					}
20718					if ($k == (count($words) - 1)) {
20719						$wordwidth += $rbw;
20720					}
20721
20722					// mPDF 6
20723					if (count($words) == 1 && substr($chunk[0], 0, 1) != ' ') {
20724						$acclength += $wordwidth;
20725					} elseif (count($words) > 1 && $k == 0 && substr($chunk[0], 0, 1) != ' ') {
20726						$acclength += $wordwidth;
20727					} else {
20728						$acclength = $wordwidth;
20729					}
20730					$acclongest = max($acclongest, $acclength);
20731					if (count($words) == 1 && substr($chunk[0], -1, 1) == ' ') {
20732						$acclength = 0;
20733					} elseif (count($words) > 1 && ($k != (count($words) - 1) || substr($chunk[0], -1, 1) == ' ')) {
20734						$acclength = 0;
20735					}
20736
20737					// Warn user that maxwidth is insufficient
20738					if ($wordwidth > $maxwidth + 0.0001) {
20739						if ($wordwidth > $biggestword) {
20740							$biggestword = $wordwidth;
20741						}
20742						$toonarrow = true;
20743					}
20744				}
20745			}
20746
20747			// mPDF 6  Accumulated length of biggest word - across multiple chunks
20748			if ($acclongest > $maxwidth + 0.0001) {
20749				if ($acclongest > $biggestword) {
20750					$biggestword = $acclongest;
20751				}
20752				$toonarrow = true;
20753			}
20754
20755			// RESET FONT SIZE/STYLE
20756			// RESETTING VALUES
20757			// Now we must deactivate what we have used
20758			if (isset($chunk[2]) and $chunk[2] != '') {
20759				$this->ResetStyles();
20760			}
20761			if (isset($chunk[4]) and $chunk[4] != '') {
20762				$this->SetFont($this->default_font, $this->FontStyle, 0, false);
20763			}
20764			if (isset($chunk[11]) and $chunk[11] != '') {
20765				$this->SetFontSize($this->default_font_size, false);
20766			}
20767			$this->spanborddet = [];
20768			$this->textvar = 0x00; // mPDF 5.7.1
20769			$this->OTLtags = [];
20770			$this->lSpacingCSS = '';
20771			$this->wSpacingCSS = '';
20772			$this->fixedlSpacing = false;
20773			$this->minwSpacing = 0;
20774		}
20775
20776		// Return -(wordsize) if word is bigger than maxwidth
20777		// ADDED
20778		if (($toonarrow) && ($this->table_error_report)) {
20779			throw new \Mpdf\MpdfException("Word is too long to fit in table - " . $this->table_error_report_param);
20780		}
20781		if ($toonarrow) {
20782			return -$biggestword;
20783		} else {
20784			return 1;
20785		}
20786	}
20787
20788	function shrinkTable(&$table, $k)
20789	{
20790		$table['border_spacing_H'] /= $k;
20791		$table['border_spacing_V'] /= $k;
20792
20793		$table['padding']['T'] /= $k;
20794		$table['padding']['R'] /= $k;
20795		$table['padding']['B'] /= $k;
20796		$table['padding']['L'] /= $k;
20797
20798		$table['margin']['T'] /= $k;
20799		$table['margin']['R'] /= $k;
20800		$table['margin']['B'] /= $k;
20801		$table['margin']['L'] /= $k;
20802
20803		$table['border_details']['T']['w'] /= $k;
20804		$table['border_details']['R']['w'] /= $k;
20805		$table['border_details']['B']['w'] /= $k;
20806		$table['border_details']['L']['w'] /= $k;
20807
20808		if (isset($table['max_cell_border_width']['T'])) {
20809			$table['max_cell_border_width']['T'] /= $k;
20810		}
20811		if (isset($table['max_cell_border_width']['R'])) {
20812			$table['max_cell_border_width']['R'] /= $k;
20813		}
20814		if (isset($table['max_cell_border_width']['B'])) {
20815			$table['max_cell_border_width']['B'] /= $k;
20816		}
20817		if (isset($table['max_cell_border_width']['L'])) {
20818			$table['max_cell_border_width']['L'] /= $k;
20819		}
20820
20821		if ($this->simpleTables) {
20822			$table['simple']['border_details']['T']['w'] /= $k;
20823			$table['simple']['border_details']['R']['w'] /= $k;
20824			$table['simple']['border_details']['B']['w'] /= $k;
20825			$table['simple']['border_details']['L']['w'] /= $k;
20826		}
20827
20828		$table['miw'] /= $k;
20829		$table['maw'] /= $k;
20830
20831		for ($j = 0; $j < $table['nc']; $j++) { // columns
20832
20833			$table['wc'][$j]['miw'] = isset($table['wc'][$j]['miw']) ? $table['wc'][$j]['miw'] : 0;
20834			$table['wc'][$j]['maw'] = isset($table['wc'][$j]['maw']) ? $table['wc'][$j]['maw'] : 0;
20835
20836			$table['wc'][$j]['miw'] /= $k;
20837			$table['wc'][$j]['maw'] /= $k;
20838
20839			if (isset($table['decimal_align'][$j]['maxs0']) && $table['decimal_align'][$j]['maxs0']) {
20840				$table['decimal_align'][$j]['maxs0'] /= $k;
20841			}
20842
20843			if (isset($table['decimal_align'][$j]['maxs1']) && $table['decimal_align'][$j]['maxs1']) {
20844				$table['decimal_align'][$j]['maxs1'] /= $k;
20845			}
20846
20847			if (isset($table['wc'][$j]['absmiw']) && $table['wc'][$j]['absmiw']) {
20848				$table['wc'][$j]['absmiw'] /= $k;
20849			}
20850
20851			for ($i = 0; $i < $table['nr']; $i++) { // rows
20852
20853				$c = &$table['cells'][$i][$j];
20854
20855				if (isset($c) && $c) {
20856
20857					if (!$this->simpleTables) {
20858
20859						if ($this->packTableData) {
20860
20861							$cell = $this->_unpackCellBorder($c['borderbin']);
20862
20863							$cell['border_details']['T']['w'] /= $k;
20864							$cell['border_details']['R']['w'] /= $k;
20865							$cell['border_details']['B']['w'] /= $k;
20866							$cell['border_details']['L']['w'] /= $k;
20867							$cell['border_details']['mbw']['TL'] /= $k;
20868							$cell['border_details']['mbw']['TR'] /= $k;
20869							$cell['border_details']['mbw']['BL'] /= $k;
20870							$cell['border_details']['mbw']['BR'] /= $k;
20871							$cell['border_details']['mbw']['LT'] /= $k;
20872							$cell['border_details']['mbw']['LB'] /= $k;
20873							$cell['border_details']['mbw']['RT'] /= $k;
20874							$cell['border_details']['mbw']['RB'] /= $k;
20875
20876							$c['borderbin'] = $this->_packCellBorder($cell);
20877
20878						} else {
20879
20880							$c['border_details']['T']['w'] /= $k;
20881							$c['border_details']['R']['w'] /= $k;
20882							$c['border_details']['B']['w'] /= $k;
20883							$c['border_details']['L']['w'] /= $k;
20884							$c['border_details']['mbw']['TL'] /= $k;
20885							$c['border_details']['mbw']['TR'] /= $k;
20886							$c['border_details']['mbw']['BL'] /= $k;
20887							$c['border_details']['mbw']['BR'] /= $k;
20888							$c['border_details']['mbw']['LT'] /= $k;
20889							$c['border_details']['mbw']['LB'] /= $k;
20890							$c['border_details']['mbw']['RT'] /= $k;
20891							$c['border_details']['mbw']['RB'] /= $k;
20892						}
20893					}
20894
20895					$c['padding']['T'] /= $k;
20896					$c['padding']['R'] /= $k;
20897					$c['padding']['B'] /= $k;
20898					$c['padding']['L'] /= $k;
20899
20900					$c['maxs'] = isset($c['maxs']) ? $c['maxs'] /= $k : 0;
20901					$c['w'] = isset($c['w']) ? $c['w'] /= $k : 0;
20902
20903					$c['s'] = isset($c['s']) ? $c['s'] /= $k : 0;
20904					$c['h'] = isset($c['h']) ? $c['h'] /= $k : 0;
20905
20906					$c['miw'] = isset($c['miw']) ? $c['miw'] /= $k : 0;
20907					$c['maw'] = isset($c['maw']) ? $c['maw'] /= $k : 0;
20908
20909					$c['absmiw'] = isset($c['absmiw']) ? $c['absmiw'] /= $k : 0;
20910
20911					$c['nestedmaw'] = isset($c['nestedmaw']) ? $c['nestedmaw'] /= $k : 0;
20912					$c['nestedmiw'] = isset($c['nestedmiw']) ? $c['nestedmiw'] /= $k : 0;
20913
20914					if (isset($c['textbuffer'])) {
20915						foreach ($c['textbuffer'] as $n => $tb) {
20916							if (!empty($tb[16])) {
20917								!isset($c['textbuffer'][$n][16]['T']) || $c['textbuffer'][$n][16]['T']['w'] /= $k;
20918								!isset($c['textbuffer'][$n][16]['B']) || $c['textbuffer'][$n][16]['B']['w'] /= $k;
20919								!isset($c['textbuffer'][$n][16]['L']) || $c['textbuffer'][$n][16]['L']['w'] /= $k;
20920								!isset($c['textbuffer'][$n][16]['R']) || $c['textbuffer'][$n][16]['R']['w'] /= $k;
20921							}
20922						}
20923					}
20924
20925					unset($c);
20926				}
20927
20928			} // rows
20929		} // columns
20930	}
20931
20932	function read_short(&$fh)
20933	{
20934		$s = fread($fh, 2);
20935		$a = (ord($s[0]) << 8) + ord($s[1]);
20936		if ($a & (1 << 15)) {
20937			$a = ($a - (1 << 16));
20938		}
20939		return $a;
20940	}
20941
20942	function _packCellBorder($cell)
20943	{
20944		if (!is_array($cell) || !isset($cell)) {
20945			return '';
20946		}
20947
20948		if (!$this->packTableData) {
20949			return $cell;
20950		}
20951		// = 186 bytes
20952		$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));
20953		return $bindata;
20954	}
20955
20956	function _getBorderWidths($bindata)
20957	{
20958		if (!$bindata) {
20959			return [0, 0, 0, 0];
20960		}
20961		if (!$this->packTableData) {
20962			return [$bindata['border_details']['T']['w'], $bindata['border_details']['R']['w'], $bindata['border_details']['B']['w'], $bindata['border_details']['L']['w']];
20963		}
20964
20965		$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);
20966		$cell['border_details']['R']['w'] = $bd['rw'];
20967		$cell['border_details']['L']['w'] = $bd['lw'];
20968		$cell['border_details']['T']['w'] = $bd['tw'];
20969		$cell['border_details']['B']['w'] = $bd['bw'];
20970		return [$bd['tw'], $bd['rw'], $bd['bw'], $bd['lw']];
20971	}
20972
20973	function _unpackCellBorder($bindata)
20974	{
20975		if (!$bindata) {
20976			return [];
20977		}
20978		if (!$this->packTableData) {
20979			return $bindata;
20980		}
20981
20982		$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);
20983
20984		$cell['border'] = $bd['bord'];
20985		$cell['border_details']['R']['s'] = $bd['rs'];
20986		$cell['border_details']['R']['w'] = $bd['rw'];
20987		$cell['border_details']['R']['c'] = str_pad($bd['rca'], 6, "\x00");
20988		$cell['border_details']['R']['style'] = trim($bd['rst']);
20989		$cell['border_details']['R']['dom'] = $bd['rd'];
20990
20991		$cell['border_details']['L']['s'] = $bd['ls'];
20992		$cell['border_details']['L']['w'] = $bd['lw'];
20993		$cell['border_details']['L']['c'] = str_pad($bd['lca'], 6, "\x00");
20994		$cell['border_details']['L']['style'] = trim($bd['lst']);
20995		$cell['border_details']['L']['dom'] = $bd['ld'];
20996
20997		$cell['border_details']['T']['s'] = $bd['ts'];
20998		$cell['border_details']['T']['w'] = $bd['tw'];
20999		$cell['border_details']['T']['c'] = str_pad($bd['tca'], 6, "\x00");
21000		$cell['border_details']['T']['style'] = trim($bd['tst']);
21001		$cell['border_details']['T']['dom'] = $bd['td'];
21002
21003		$cell['border_details']['B']['s'] = $bd['bs'];
21004		$cell['border_details']['B']['w'] = $bd['bw'];
21005		$cell['border_details']['B']['c'] = str_pad($bd['bca'], 6, "\x00");
21006		$cell['border_details']['B']['style'] = trim($bd['bst']);
21007		$cell['border_details']['B']['dom'] = $bd['bd'];
21008
21009		$cell['border_details']['mbw']['BL'] = $bd['mbl'];
21010		$cell['border_details']['mbw']['BR'] = $bd['mbr'];
21011		$cell['border_details']['mbw']['RT'] = $bd['mrt'];
21012		$cell['border_details']['mbw']['RB'] = $bd['mrb'];
21013		$cell['border_details']['mbw']['TL'] = $bd['mtl'];
21014		$cell['border_details']['mbw']['TR'] = $bd['mtr'];
21015		$cell['border_details']['mbw']['LT'] = $bd['mlt'];
21016		$cell['border_details']['mbw']['LB'] = $bd['mlb'];
21017		$cell['border_details']['cellposdom'] = $bd['cpd'];
21018
21019
21020		return($cell);
21021	}
21022
21023	////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
21024	////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
21025	////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
21026	// table		Array of (w, h, bc, nr, wc, hr, cells)
21027	// w			Width of table
21028	// h			Height of table
21029	// nc			Number column
21030	// nr			Number row
21031	// hr			List of height of each row
21032	// wc			List of width of each column
21033	// cells		List of cells of each rows, cells[i][j] is a cell in the table
21034	function _tableColumnWidth(&$table, $firstpass = false)
21035	{
21036		$cs = &$table['cells'];
21037
21038		$nc = $table['nc'];
21039		$nr = $table['nr'];
21040		$listspan = [];
21041
21042		if ($table['borders_separate']) {
21043			$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'];
21044		} else {
21045			$tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];
21046		}
21047
21048		// ADDED table['l'][colno]
21049		// = total length of text approx (using $c['s']) in that column - used to approximately distribute col widths in _tableWidth
21050		//
21051		for ($j = 0; $j < $nc; $j++) { // columns
21052			$wc = &$table['wc'][$j];
21053			for ($i = 0; $i < $nr; $i++) { // rows
21054				if (isset($cs[$i][$j]) && $cs[$i][$j]) {
21055					$c = &$cs[$i][$j];
21056
21057					if ($this->simpleTables) {
21058						if ($table['borders_separate']) { // NB twice border width
21059							$extrcw = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
21060						} else {
21061							$extrcw = $table['simple']['border_details']['L']['w'] / 2 + $table['simple']['border_details']['R']['w'] / 2 + $c['padding']['L'] + $c['padding']['R'];
21062						}
21063					} else {
21064						if ($this->packTableData) {
21065							list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
21066						} else {
21067							$br = $c['border_details']['R']['w'];
21068							$bl = $c['border_details']['L']['w'];
21069						}
21070						if ($table['borders_separate']) { // NB twice border width
21071							$extrcw = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
21072						} else {
21073							$extrcw = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];
21074						}
21075					}
21076
21077					// $mw = $this->GetStringWidth('W') + $extrcw ;
21078					$mw = $extrcw; // mPDF 6
21079					if (substr($c['a'], 0, 1) == 'D') {
21080						$mw = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'] + $extrcw;
21081					}
21082
21083					$c['absmiw'] = $mw;
21084
21085					if (isset($c['R']) && $c['R']) {
21086						$c['maw'] = $c['miw'] = $this->FontSize + $extrcw;
21087						if (isset($c['w'])) { // If cell width is specified
21088							if ($c['miw'] < $c['w']) {
21089								$c['miw'] = $c['w'];
21090							}
21091						}
21092						if (!isset($c['colspan'])) {
21093							if ($wc['miw'] < $c['miw']) {
21094								$wc['miw'] = $c['miw'];
21095							}
21096							if ($wc['maw'] < $c['maw']) {
21097								$wc['maw'] = $c['maw'];
21098							}
21099
21100							if ($firstpass) {
21101								if (isset($table['l'][$j])) {
21102									$table['l'][$j] += $c['miw'];
21103								} else {
21104									$table['l'][$j] = $c['miw'];
21105								}
21106							}
21107						}
21108						if ($c['miw'] > $wc['miw']) {
21109							$wc['miw'] = $c['miw'];
21110						}
21111						if ($wc['miw'] > $wc['maw']) {
21112							$wc['maw'] = $wc['miw'];
21113						}
21114						continue;
21115					}
21116
21117					if ($firstpass) {
21118						if (isset($c['s'])) {
21119							$c['s'] += $extrcw;
21120						}
21121						if (isset($c['maxs'])) {
21122							$c['maxs'] += $extrcw;
21123						}
21124						if (isset($c['nestedmiw'])) {
21125							$c['nestedmiw'] += $extrcw;
21126						}
21127						if (isset($c['nestedmaw'])) {
21128							$c['nestedmaw'] += $extrcw;
21129						}
21130					}
21131
21132
21133					// If minimum width has already been set by a nested table or inline object (image/form), use it
21134					if (isset($c['nestedmiw']) && (!isset($this->table[1][1]['overflow']) || $this->table[1][1]['overflow'] != 'visible')) {
21135						$miw = $c['nestedmiw'];
21136					} else {
21137						$miw = $mw;
21138					}
21139
21140					if (isset($c['maxs']) && $c['maxs'] != '') {
21141						$c['s'] = $c['maxs'];
21142					}
21143
21144					// If maximum width has already been set by a nested table, use it
21145					if (isset($c['nestedmaw'])) {
21146						$c['maw'] = $c['nestedmaw'];
21147					} else {
21148						$c['maw'] = $c['s'];
21149					}
21150
21151					if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {
21152						if (($c['maw'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
21153							$c['maw'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;
21154						}
21155					}
21156
21157					if (isset($c['nowrap']) && $c['nowrap']) {
21158						$miw = $c['maw'];
21159					}
21160
21161					if (isset($c['wpercent']) && $firstpass) {
21162						if (isset($c['colspan'])) { // Not perfect - but % set on colspan is shared equally on cols.
21163							for ($k = 0; $k < $c['colspan']; $k++) {
21164								$table['wc'][($j + $k)]['wpercent'] = $c['wpercent'] / $c['colspan'];
21165							}
21166						} else {
21167							if (isset($table['w']) && $table['w']) {
21168								$c['w'] = $c['wpercent'] / 100 * ($table['w'] - $tblbw );
21169							}
21170							$wc['wpercent'] = $c['wpercent'];
21171						}
21172					}
21173
21174					if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {
21175						if (isset($c['w']) && ($c['w'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
21176							$c['w'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;
21177						}
21178					}
21179
21180
21181					if (isset($c['w'])) { // If cell width is specified
21182						if ($miw < $c['w']) {
21183							$c['miw'] = $c['w'];
21184						} // Cell min width = that specified
21185						if ($miw > $c['w']) {
21186							$c['miw'] = $c['w'] = $miw;
21187						} // If width specified is less than minimum allowed (W) increase it
21188						// mPDF 5.7.4  Do not set column width in colspan
21189						// cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug
21190						if (!isset($c['colspan'])) {
21191							if (!isset($wc['w'])) {
21192								$wc['w'] = 1;
21193							}  // If the Col width is not specified = set it to 1
21194						}
21195						// mPDF 5.7.3  cf. http://www.mpdf1.com/forum/discussion/1648/nested-table-bug-
21196						$c['maw'] = $c['w'];
21197					} else {
21198						$c['miw'] = $miw;
21199					} // If cell width not specified -> set Cell min width it to minimum allowed (W)
21200
21201					if (isset($c['miw']) && $c['maw'] < $c['miw']) {
21202						$c['maw'] = $c['miw'];
21203					} // If Cell max width < Minwidth - increase it to =
21204					if (!isset($c['colspan'])) {
21205						if (isset($c['miw']) && $wc['miw'] < $c['miw']) {
21206							$wc['miw'] = $c['miw'];
21207						} // Update Col Minimum and maximum widths
21208						if ($wc['maw'] < $c['maw']) {
21209							$wc['maw'] = $c['maw'];
21210						}
21211						if ((isset($wc['absmiw']) && $wc['absmiw'] < $c['absmiw']) || !isset($wc['absmiw'])) {
21212							$wc['absmiw'] = $c['absmiw'];
21213						} // Update Col Minimum and maximum widths
21214
21215						if (isset($table['l'][$j])) {
21216							$table['l'][$j] += $c['s'];
21217						} else {
21218							$table['l'][$j] = $c['s'];
21219						}
21220					} else {
21221						$listspan[] = [$i, $j];
21222					}
21223
21224					// Check if minimum width of the whole column is big enough for largest word to fit
21225					// mPDF 6
21226					if (isset($c['textbuffer'])) {
21227						if (isset($table['overflow']) && $table['overflow'] == 'wrap') {
21228							$letter = true;
21229						} // check for maximum width of letters
21230						else {
21231							$letter = false;
21232						}
21233						$minwidth = $this->TableCheckMinWidth($wc['miw'] - $extrcw, 0, $c['textbuffer'], $letter);
21234					} else {
21235						$minwidth = 0;
21236					}
21237					if ($minwidth < 0) {
21238						// increase minimum width
21239						if (!isset($c['colspan'])) {
21240							$wc['miw'] = max($wc['miw'], ((-$minwidth) + $extrcw));
21241						} else {
21242							$c['miw'] = max($c['miw'], ((-$minwidth) + $extrcw));
21243						}
21244					}
21245					if (!isset($c['colspan'])) {
21246						if ($wc['miw'] > $wc['maw']) {
21247							$wc['maw'] = $wc['miw'];
21248						} // update maximum width, if needed
21249					}
21250				}
21251				unset($c);
21252			}//rows
21253		}//columns
21254		// COLUMN SPANS
21255		$wc = &$table['wc'];
21256		foreach ($listspan as $span) {
21257			list($i, $j) = $span;
21258			$c = &$cs[$i][$j];
21259			$lc = $j + $c['colspan'];
21260			if ($lc > $nc) {
21261				$lc = $nc;
21262			}
21263			$wis = $wisa = 0;
21264			$was = $wasa = 0;
21265			$list = [];
21266			for ($k = $j; $k < $lc; $k++) {
21267				if (isset($table['l'][$k])) {
21268					if ($c['R']) {
21269						$table['l'][$k] += $c['miw'] / $c['colspan'];
21270					} else {
21271						$table['l'][$k] += $c['s'] / $c['colspan'];
21272					}
21273				} else {
21274					if ($c['R']) {
21275						$table['l'][$k] = $c['miw'] / $c['colspan'];
21276					} else {
21277						$table['l'][$k] = $c['s'] / $c['colspan'];
21278					}
21279				}
21280				$wis += $wc[$k]['miw'];   // $wis is the sum of the column miw in the colspan
21281				$was += $wc[$k]['maw'];   // $was is the sum of the column maw in the colspan
21282				if (!isset($c['w'])) {
21283					$list[] = $k;
21284					$wisa += $wc[$k]['miw']; // $wisa is the sum of the column miw in cells with no width specified in the colspan
21285					$wasa += $wc[$k]['maw']; // $wasa is the sum of the column maw in cells with no width specified in the colspan
21286				}
21287			}
21288			if ($c['miw'] > $wis) {
21289				if (!$wis) {
21290					for ($k = $j; $k < $lc; $k++) {
21291						$wc[$k]['miw'] = $c['miw'] / $c['colspan'];
21292					}
21293				} elseif (!count($list)) {
21294					$wi = $c['miw'] - $wis;
21295					for ($k = $j; $k < $lc; $k++) {
21296						$wc[$k]['miw'] += ($wc[$k]['miw'] / $wis) * $wi;
21297					}
21298				} else {
21299					$wi = $c['miw'] - $wis;
21300					// mPDF 5.7.2   Extra min width distributed proportionately to all cells in colspan without a specified width
21301					// cf. http://www.mpdf1.com/forum/discussion/1607#Item_4
21302					foreach ($list as $k) {
21303						if (!isset($wc[$k]['w']) || !$wc[$k]['w']) {
21304							$wc[$k]['miw'] += ($wc[$k]['miw'] / $wisa) * $wi;
21305						}
21306					} // mPDF 5.7.2
21307				}
21308			}
21309			if ($c['maw'] > $was) {
21310				if (!$wis) {
21311					for ($k = $j; $k < $lc; $k++) {
21312						$wc[$k]['maw'] = $c['maw'] / $c['colspan'];
21313					}
21314				} elseif (!count($list)) {
21315					$wi = $c['maw'] - $was;
21316					for ($k = $j; $k < $lc; $k++) {
21317						$wc[$k]['maw'] += ($wc[$k]['maw'] / $was) * $wi;
21318					}
21319				} else {
21320					$wi = $c['maw'] - $was;
21321					// mPDF 5.7.4  Extra max width distributed evenly to all cells in colspan without a specified width
21322					// cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug
21323					foreach ($list as $k) {
21324						$wc[$k]['maw'] += $wi / count($list);
21325					}
21326				}
21327			}
21328			unset($c);
21329		}
21330
21331		$checkminwidth = 0;
21332		$checkmaxwidth = 0;
21333		$totallength = 0;
21334
21335		for ($i = 0; $i < $nc; $i++) {
21336			$checkminwidth += $table['wc'][$i]['miw'];
21337			$checkmaxwidth += $table['wc'][$i]['maw'];
21338			$totallength += isset($table['l']) ? $table['l'][$i] : 0;
21339		}
21340
21341		if (!isset($table['w']) && $firstpass) {
21342			$sumpc = 0;
21343			$notset = 0;
21344			for ($i = 0; $i < $nc; $i++) {
21345				if (isset($table['wc'][$i]['wpercent']) && $table['wc'][$i]['wpercent']) {
21346					$sumpc += $table['wc'][$i]['wpercent'];
21347				} else {
21348					$notset++;
21349				}
21350			}
21351
21352			// If sum of widths as %  >= 100% and not all columns are set
21353			// Set a nominal width of 1% for unset columns
21354			if ($sumpc >= 100 && $notset) {
21355				for ($i = 0; $i < $nc; $i++) {
21356					if ((!isset($table['wc'][$i]['wpercent']) || !$table['wc'][$i]['wpercent']) &&
21357						(!isset($table['wc'][$i]['w']) || !$table['wc'][$i]['w'])) {
21358						$table['wc'][$i]['wpercent'] = 1;
21359					}
21360				}
21361			}
21362
21363
21364			if ($sumpc) { // if any percents are set
21365				$sumnonpc = (100 - $sumpc);
21366				$sumpc = max($sumpc, 100);
21367				$miwleft = 0;
21368				$miwleftcount = 0;
21369				$miwsurplusnonpc = 0;
21370				$maxcalcmiw = 0;
21371				$mawleft = 0;
21372				$mawleftcount = 0;
21373				$mawsurplusnonpc = 0;
21374				$maxcalcmaw = 0;
21375				$mawnon = 0;
21376				$miwnon = 0;
21377				for ($i = 0; $i < $nc; $i++) {
21378					if (isset($table['wc'][$i]['wpercent'])) {
21379						$maxcalcmiw = max($maxcalcmiw, ($table['wc'][$i]['miw'] * $sumpc / $table['wc'][$i]['wpercent']));
21380						$maxcalcmaw = max($maxcalcmaw, ($table['wc'][$i]['maw'] * $sumpc / $table['wc'][$i]['wpercent']));
21381					} else {
21382						$miwleft += $table['wc'][$i]['miw'];
21383						$mawleft += $table['wc'][$i]['maw'];
21384						if (!isset($table['wc'][$i]['w'])) {
21385							$miwleftcount++;
21386							$mawleftcount++;
21387						}
21388					}
21389				}
21390				if ($miwleft && $sumnonpc > 0) {
21391					$miwnon = $miwleft * 100 / $sumnonpc;
21392				}
21393				if ($mawleft && $sumnonpc > 0) {
21394					$mawnon = $mawleft * 100 / $sumnonpc;
21395				}
21396				if (($miwnon > $checkminwidth || $maxcalcmiw > $checkminwidth) && $this->keep_table_proportions) {
21397					if ($miwnon > $maxcalcmiw) {
21398						$miwsurplusnonpc = round((($miwnon * $sumnonpc / 100) - $miwleft), 3);
21399						$checkminwidth = $miwnon;
21400					} else {
21401						$checkminwidth = $maxcalcmiw;
21402					}
21403					for ($i = 0; $i < $nc; $i++) {
21404						if (isset($table['wc'][$i]['wpercent'])) {
21405							$newmiw = $checkminwidth * $table['wc'][$i]['wpercent'] / 100;
21406							if ($table['wc'][$i]['miw'] < $newmiw) {
21407								$table['wc'][$i]['miw'] = $newmiw;
21408							}
21409							$table['wc'][$i]['w'] = 1;
21410						} elseif ($miwsurplusnonpc && !$table['wc'][$i]['w']) {
21411							$table['wc'][$i]['miw'] += $miwsurplusnonpc / $miwleftcount;
21412						}
21413					}
21414				}
21415				if (($mawnon > $checkmaxwidth || $maxcalcmaw > $checkmaxwidth)) {
21416					if ($mawnon > $maxcalcmaw) {
21417						$mawsurplusnonpc = round((($mawnon * $sumnonpc / 100) - $mawleft), 3);
21418						$checkmaxwidth = $mawnon;
21419					} else {
21420						$checkmaxwidth = $maxcalcmaw;
21421					}
21422					for ($i = 0; $i < $nc; $i++) {
21423						if (isset($table['wc'][$i]['wpercent'])) {
21424							$newmaw = $checkmaxwidth * $table['wc'][$i]['wpercent'] / 100;
21425							if ($table['wc'][$i]['maw'] < $newmaw) {
21426								$table['wc'][$i]['maw'] = $newmaw;
21427							}
21428							$table['wc'][$i]['w'] = 1;
21429						} elseif ($mawsurplusnonpc && !$table['wc'][$i]['w']) {
21430							$table['wc'][$i]['maw'] += $mawsurplusnonpc / $mawleftcount;
21431						}
21432						if ($table['wc'][$i]['maw'] < $table['wc'][$i]['miw']) {
21433							$table['wc'][$i]['maw'] = $table['wc'][$i]['miw'];
21434						}
21435					}
21436				}
21437				if ($checkminwidth > $checkmaxwidth) {
21438					$checkmaxwidth = $checkminwidth;
21439				}
21440			}
21441		}
21442
21443		if (isset($table['wpercent']) && $table['wpercent']) {
21444			$checkminwidth *= (100 / $table['wpercent']);
21445			$checkmaxwidth *= (100 / $table['wpercent']);
21446		}
21447
21448
21449		$checkminwidth += $tblbw;
21450		$checkmaxwidth += $tblbw;
21451
21452		// Table['miw'] set by percent in first pass may be larger than sum of column miw
21453		if ((isset($table['miw']) && $checkminwidth > $table['miw']) || !isset($table['miw'])) {
21454			$table['miw'] = $checkminwidth;
21455		}
21456		if ((isset($table['maw']) && $checkmaxwidth > $table['maw']) || !isset($table['maw'])) {
21457			$table['maw'] = $checkmaxwidth;
21458		}
21459		$table['tl'] = $totallength;
21460
21461		// mPDF 6
21462		if ($this->table_rotate) {
21463			$mxw = $this->tbrot_maxw;
21464		} else {
21465			$mxw = $this->blk[$this->blklvl]['inner_width'];
21466		}
21467
21468		if (!isset($table['overflow'])) {
21469			$table['overflow'] = null;
21470		}
21471
21472		if ($table['overflow'] == 'visible') {
21473			return [0, 0];
21474		} elseif ($table['overflow'] == 'hidden' && !$this->table_rotate && !$this->ColActive && $checkminwidth > $mxw) {
21475			$table['w'] = $table['miw'];
21476			return [0, 0];
21477		}
21478		// elseif ($table['overflow']=='wrap') { return array(0,0); }	// mPDF 6
21479
21480		if (isset($table['w']) && $table['w']) {
21481
21482			if ($table['w'] >= $checkminwidth && $table['w'] <= $mxw) {
21483				$table['maw'] = $mxw = $table['w'];
21484			} elseif ($table['w'] >= $checkminwidth && $table['w'] > $mxw && $this->keep_table_proportions) {
21485				$checkminwidth = $table['w'];
21486			} elseif ($table['w'] < $checkminwidth && $checkminwidth < $mxw && $this->keep_table_proportions) {
21487				$table['maw'] = $table['w'] = $checkminwidth;
21488			} else {
21489				unset($table['w']);
21490			}
21491		}
21492
21493		$ratio = $checkminwidth / $mxw;
21494
21495		if ($checkminwidth > $mxw) {
21496			return [($ratio + 0.001), $checkminwidth]; // 0.001 to allow for rounded numbers when resizing
21497		}
21498
21499		unset($cs);
21500
21501		return [0, 0];
21502	}
21503
21504	function _tableWidth(&$table)
21505	{
21506		$widthcols = &$table['wc'];
21507		$numcols = $table['nc'];
21508		$tablewidth = 0;
21509
21510		if ($table['borders_separate']) {
21511			$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'];
21512		} else {
21513			$tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];
21514		}
21515
21516		if ($table['level'] > 1 && isset($table['w'])) {
21517
21518			if (isset($table['wpercent']) && $table['wpercent']) {
21519				$table['w'] = $temppgwidth = (($table['w'] - $tblbw) * $table['wpercent'] / 100) + $tblbw;
21520			} else {
21521				$temppgwidth = $table['w'];
21522			}
21523
21524		} elseif ($this->table_rotate) {
21525
21526			$temppgwidth = $this->tbrot_maxw;
21527
21528			// 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
21529			$enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];
21530
21531			if ($enddiv / $temppgwidth < 0.05) {
21532				$temppgwidth -= $enddiv;
21533			}
21534
21535		} else {
21536
21537			if (isset($table['w']) && $table['w'] < $this->blk[$this->blklvl]['inner_width']) {
21538				$notfullwidth = 1;
21539				$temppgwidth = $table['w'];
21540			} elseif ($table['overflow'] == 'visible' && $table['level'] == 1) {
21541				$temppgwidth = null;
21542			} elseif ($table['overflow'] == 'hidden' && !$this->ColActive && isset($table['w']) && $table['w'] > $this->blk[$this->blklvl]['inner_width'] && $table['w'] == $table) {
21543				// $temppgwidth = $this->blk[$this->blklvl]['inner_width'];
21544				$temppgwidth = $table['w'];
21545			} else {
21546				$temppgwidth = $this->blk[$this->blklvl]['inner_width'];
21547			}
21548
21549		}
21550
21551		$totaltextlength = 0; // Added - to sum $table['l'][colno]
21552		$totalatextlength = 0; // Added - to sum $table['l'][colno] for those columns where width not set
21553		$percentages_set = 0;
21554
21555		for ($i = 0; $i < $numcols; $i++) {
21556			if (isset($widthcols[$i]['wpercent'])) {
21557				$tablewidth += $widthcols[$i]['maw'];
21558				$percentages_set = 1;
21559			} elseif (isset($widthcols[$i]['w'])) {
21560				$tablewidth += $widthcols[$i]['miw'];
21561			} else {
21562				$tablewidth += $widthcols[$i]['maw'];
21563			}
21564			$totaltextlength += isset($table['l']) ? $table['l'][$i] : 0;
21565		}
21566
21567		if (!$totaltextlength) {
21568			$totaltextlength = 1;
21569		}
21570
21571		$tablewidth += $tblbw; // Outer half of table borders
21572
21573		if ($tablewidth > $temppgwidth) {
21574			$table['w'] = $temppgwidth;
21575		} elseif ($tablewidth < $temppgwidth && !isset($table['w']) && $percentages_set) { // if any widths set as percentages and max width fits < page width
21576			$table['w'] = $table['maw'];
21577		}
21578
21579		// if table width is set and is > allowed width
21580		if (isset($table['w']) && $table['w'] > $temppgwidth) {
21581			$table['w'] = $temppgwidth;
21582		}
21583
21584		// IF the table width is now set - Need to distribute columns widths
21585		// mPDF 5.7.3
21586		// If the table width is already set to the maximum width (e.g. nested table), then use maximum column widths exactly
21587		if (isset($table['w']) && ($table['w'] == $tablewidth) && !$percentages_set) {
21588
21589			// This sets the columns all to maximum width
21590			for ($i = 0; $i < $numcols; $i++) {
21591				$widthcols[$i] = $widthcols[$i]['maw'];
21592			}
21593
21594		} elseif (isset($table['w'])) { // elseif the table width is set distribute width using algorithm
21595
21596			$wis = $wisa = 0;
21597			$list = [];
21598			$notsetlist = [];
21599
21600			for ($i = 0; $i < $numcols; $i++) {
21601				$wis += $widthcols[$i]['miw'];
21602				if (!isset($widthcols[$i]['w']) || ($widthcols[$i]['w'] && $table['w'] > $temppgwidth && !$this->keep_table_proportions && !$notfullwidth )) {
21603					$list[] = $i;
21604					$wisa += $widthcols[$i]['miw'];
21605					$totalatextlength += $table['l'][$i];
21606				}
21607			}
21608
21609			if (!$totalatextlength) {
21610				$totalatextlength = 1;
21611			}
21612
21613			// Allocate spare (more than col's minimum width) across the cols according to their approx total text length
21614			// Do it by setting minimum width here
21615			if ($table['w'] > $wis + $tblbw) {
21616
21617				// First set any cell widths set as percentages
21618				if ($table['w'] < $temppgwidth || $this->keep_table_proportions) {
21619					for ($k = 0; $k < $numcols; $k++) {
21620						if (isset($widthcols[$k]['wpercent'])) {
21621							$curr = $widthcols[$k]['miw'];
21622							$widthcols[$k]['miw'] = ($table['w'] - $tblbw) * $widthcols[$k]['wpercent'] / 100;
21623							$wis += $widthcols[$k]['miw'] - $curr;
21624							$wisa += $widthcols[$k]['miw'] - $curr;
21625						}
21626					}
21627				}
21628
21629				// Now allocate surplus up to maximum width of each column
21630				$surplus = 0;
21631				$ttl = 0; // number of surplus columns
21632
21633				if (!count($list)) {
21634
21635					$wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute
21636
21637					for ($k = 0; $k < $numcols; $k++) {
21638
21639						$spareratio = ($table['l'][$k] / $totaltextlength); //  gives ratio to divide up free space
21640
21641						// Don't allocate more than Maximum required width - save rest in surplus
21642						if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3
21643							$surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);
21644							$widthcols[$k]['miw'] = $widthcols[$k]['maw'];
21645						} else {
21646							$notsetlist[] = $k;
21647							$ttl += $table['l'][$k];
21648							$widthcols[$k]['miw'] += ($wi * $spareratio);
21649						}
21650					}
21651
21652				} else {
21653
21654					$wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute
21655
21656					foreach ($list as $k) {
21657
21658						$spareratio = ($table['l'][$k] / $totalatextlength); //  gives ratio to divide up free space
21659
21660						// Don't allocate more than Maximum required width - save rest in surplus
21661						if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3
21662							$surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);
21663							$widthcols[$k]['miw'] = $widthcols[$k]['maw'];
21664						} else {
21665							$notsetlist[] = $k;
21666							$ttl += $table['l'][$k];
21667							$widthcols[$k]['miw'] += ($wi * $spareratio);
21668						}
21669					}
21670				}
21671
21672				// If surplus still left over apportion it across columns
21673				if ($surplus) {
21674
21675					if (count($notsetlist) && count($notsetlist) < $numcols) { // if some are set only add to remaining - otherwise add to all of them
21676						foreach ($notsetlist as $i) {
21677							if ($ttl) {
21678								$widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;
21679							}
21680						}
21681					} elseif (count($list) && count($list) < $numcols) { // If some widths are defined, and others have been added up to their maxmum
21682						foreach ($list as $i) {
21683							$widthcols[$i]['miw'] += $surplus / count($list);
21684						}
21685					} elseif ($numcols) { // If all columns
21686						$ttl = array_sum($table['l']);
21687						if ($ttl) {
21688							for ($i = 0; $i < $numcols; $i++) {
21689								$widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;
21690							}
21691						}
21692					}
21693				}
21694			}
21695
21696			// This sets the columns all to minimum width (which has been increased above if appropriate)
21697			for ($i = 0; $i < $numcols; $i++) {
21698				$widthcols[$i] = $widthcols[$i]['miw'];
21699			}
21700
21701			// TABLE NOT WIDE ENOUGH EVEN FOR MINIMUM CONTENT WIDTH
21702			// If sum of column widths set are too wide for table
21703			$checktablewidth = 0;
21704			for ($i = 0; $i < $numcols; $i++) {
21705				$checktablewidth += $widthcols[$i];
21706			}
21707
21708			if ($checktablewidth > ($temppgwidth + 0.001 - $tblbw)) {
21709
21710				$usedup = 0;
21711				$numleft = 0;
21712
21713				for ($i = 0; $i < $numcols; $i++) {
21714					if ((isset($widthcols[$i]) && $widthcols[$i] > (($temppgwidth - $tblbw) / $numcols)) && (!isset($widthcols[$i]['w']))) {
21715						$numleft++;
21716						unset($widthcols[$i]);
21717					} else {
21718						$usedup += $widthcols[$i];
21719					}
21720				}
21721
21722				for ($i = 0; $i < $numcols; $i++) {
21723					if (!isset($widthcols[$i]) || !$widthcols[$i]) {
21724						$widthcols[$i] = ((($temppgwidth - $tblbw) - $usedup) / ($numleft));
21725					}
21726				}
21727			}
21728
21729		} else { // table has no width defined
21730
21731			$table['w'] = $tablewidth;
21732
21733			for ($i = 0; $i < $numcols; $i++) {
21734
21735				if (isset($widthcols[$i]['wpercent']) && $this->keep_table_proportions) {
21736					$colwidth = $widthcols[$i]['maw'];
21737				} elseif (isset($widthcols[$i]['w'])) {
21738					$colwidth = $widthcols[$i]['miw'];
21739				} else {
21740					$colwidth = $widthcols[$i]['maw'];
21741				}
21742
21743				unset($widthcols[$i]);
21744				$widthcols[$i] = $colwidth;
21745
21746			}
21747		}
21748
21749		if ($table['overflow'] === 'visible' && $table['level'] == 1) {
21750
21751			if ($tablewidth > $this->blk[$this->blklvl]['inner_width']) {
21752
21753				for ($j = 0; $j < $numcols; $j++) { // columns
21754
21755					for ($i = 0; $i < $table['nr']; $i++) { // rows
21756
21757						if (isset($table['cells'][$i][$j]) && $table['cells'][$i][$j]) {
21758
21759							$colspan = (isset($table['cells'][$i][$j]['colspan']) ? $table['cells'][$i][$j]['colspan'] : 1);
21760
21761							if ($colspan > 1) {
21762								$w = 0;
21763
21764								for ($c = $j; $c < ($j + $colspan); $c++) {
21765									$w += $widthcols[$c];
21766								}
21767
21768								if ($w > $this->blk[$this->blklvl]['inner_width']) {
21769									$diff = $w - ($this->blk[$this->blklvl]['inner_width'] - $tblbw);
21770									for ($c = $j; $c < ($j + $colspan); $c++) {
21771										$widthcols[$c] -= $diff * ($widthcols[$c] / $w);
21772									}
21773									$table['w'] -= $diff;
21774									$table['csp'][$j] = $w - $diff;
21775								}
21776							}
21777						}
21778					}
21779				}
21780			}
21781
21782			$pgNo = 0;
21783			$currWc = 0;
21784
21785			for ($i = 0; $i < $numcols; $i++) { // columns
21786
21787				if (isset($table['csp'][$i])) {
21788					$w = $table['csp'][$i];
21789					unset($table['csp'][$i]);
21790				} else {
21791					$w = $widthcols[$i];
21792				}
21793
21794				if (($currWc + $w + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
21795					$pgNo++;
21796					$currWc = $widthcols[$i];
21797				} else {
21798					$currWc += $widthcols[$i];
21799				}
21800
21801				$table['colPg'][$i] = $pgNo;
21802			}
21803		}
21804	}
21805
21806	function _tableHeight(&$table)
21807	{
21808		$level = $table['level'];
21809		$levelid = $table['levelid'];
21810		$cells = &$table['cells'];
21811		$numcols = $table['nc'];
21812		$numrows = $table['nr'];
21813		$listspan = [];
21814		$checkmaxheight = 0;
21815		$headerrowheight = 0;
21816		$checkmaxheightplus = 0;
21817		$headerrowheightplus = 0;
21818		$firstrowheight = 0;
21819
21820		$footerrowheight = 0;
21821		$footerrowheightplus = 0;
21822		if ($this->table_rotate) {
21823			$temppgheight = $this->tbrot_maxh;
21824			$remainingpage = $this->tbrot_maxh;
21825		} else {
21826			$temppgheight = ($this->h - $this->bMargin - $this->tMargin) - $this->kwt_height;
21827			$remainingpage = ($this->h - $this->bMargin - $this->y) - $this->kwt_height;
21828
21829			// If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin)
21830			// then allow for this
21831			$enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $table['margin']['B'];
21832			if ($remainingpage > $enddiv && $enddiv / $remainingpage < 0.05) {
21833				$remainingpage -= $enddiv;
21834			} elseif ($remainingpage == 0) {
21835				$remainingpage = 0.001;
21836			}
21837			if ($temppgheight > $enddiv && $enddiv / $temppgheight < 0.05) {
21838				$temppgheight -= $enddiv;
21839			} elseif ($temppgheight == 0) {
21840				$temppgheight = 0.001;
21841			}
21842		}
21843		if ($remainingpage < 0) {
21844			$remainingpage = 0.001;
21845		}
21846		if ($temppgheight < 0) {
21847			$temppgheight = 0.001;
21848		}
21849
21850		for ($i = 0; $i < $numrows; $i++) { // rows
21851			$heightrow = &$table['hr'][$i];
21852			for ($j = 0; $j < $numcols; $j++) { // columns
21853				if (isset($cells[$i][$j]) && $cells[$i][$j]) {
21854					$c = &$cells[$i][$j];
21855
21856					if ($this->simpleTables) {
21857						if ($table['borders_separate']) { // NB twice border width
21858							$extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) + ($c['padding']['L'] + $c['padding']['R']) + $table['border_spacing_H'];
21859							$extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) + ($c['padding']['T'] + $c['padding']['B']) + $table['border_spacing_V'];
21860						} else {
21861							$extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + ($c['padding']['L'] + $c['padding']['R']);
21862							$extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) / 2 + ($c['padding']['T'] + $c['padding']['B']);
21863						}
21864					} else {
21865						if ($this->packTableData) {
21866							list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
21867						} else {
21868							$bt = $c['border_details']['T']['w'];
21869							$bb = $c['border_details']['B']['w'];
21870							$br = $c['border_details']['R']['w'];
21871							$bl = $c['border_details']['L']['w'];
21872						}
21873						if ($table['borders_separate']) { // NB twice border width
21874							$extraWLR = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
21875							$extrh = $bt + $bb + $c['padding']['T'] + $c['padding']['B'] + $table['border_spacing_V'];
21876						} else {
21877							$extraWLR = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];
21878							$extrh = $bt / 2 + $bb / 2 + $c['padding']['T'] + $c['padding']['B'];
21879						}
21880					}
21881
21882					if ($table['overflow'] == 'visible' && $level == 1) {
21883						list($x, $cw) = $this->_splitTableGetWidth($table, $i, $j);
21884					} else {
21885						list($x, $cw) = $this->_tableGetWidth($table, $i, $j);
21886					}
21887
21888
21889					// Get CELL HEIGHT
21890					// ++ extra parameter forces wrap to break word
21891					if ($c['R'] && isset($c['textbuffer'])) {
21892						$str = '';
21893						foreach ($c['textbuffer'] as $t) {
21894							$str .= $t[0] . ' ';
21895						}
21896						$str = rtrim($str);
21897						$s_fs = $this->FontSizePt;
21898						$s_f = $this->FontFamily;
21899						$s_st = $this->FontStyle;
21900						$this->SetFont($c['textbuffer'][0][4], $c['textbuffer'][0][2], $c['textbuffer'][0][11] / $this->shrin_k, true, true);
21901						$tempch = $this->GetStringWidth($str, true, $c['textbuffer'][0][18], $c['textbuffer'][0][8]);
21902						if ($c['R'] >= 45 && $c['R'] < 90) {
21903							$tempch = ((sin(deg2rad($c['R']))) * $tempch ) + ((sin(deg2rad($c['R']))) * (($c['textbuffer'][0][11] / Mpdf::SCALE) / $this->shrin_k));
21904						}
21905						$this->SetFont($s_f, $s_st, $s_fs, true, true);
21906						$ch = ($tempch ) + $extrh;
21907					} else {
21908						if (isset($c['textbuffer']) && !empty($c['textbuffer'])) {
21909							$this->cellLineHeight = $c['cellLineHeight'];
21910							$this->cellLineStackingStrategy = $c['cellLineStackingStrategy'];
21911							$this->cellLineStackingShift = $c['cellLineStackingShift'];
21912							$this->divwidth = $cw - $extraWLR;
21913							$tempch = $this->printbuffer($c['textbuffer'], '', true, true);
21914						} else {
21915							$tempch = 0;
21916						}
21917
21918						// Added cellpadding top and bottom. (Lineheight already adjusted)
21919						$ch = $tempch + $extrh;
21920					}
21921					// If height is defined and it is bigger than calculated $ch then update values
21922					if (isset($c['h']) && $c['h'] > $ch) {
21923						$c['mih'] = $ch; // in order to keep valign working
21924						$ch = $c['h'];
21925					} else {
21926						$c['mih'] = $ch;
21927					}
21928					if (isset($c['rowspan'])) {
21929						$listspan[] = [$i, $j];
21930					} elseif ($heightrow < $ch) {
21931						$heightrow = $ch;
21932					}
21933
21934					// this is the extra used in _tableWrite to determine whether to trigger a page change
21935					if ($table['borders_separate']) {
21936						if ($i == ($numrows - 1) || (isset($c['rowspan']) && ($i + $c['rowspan']) == ($numrows))) {
21937							$extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
21938						} else {
21939							$extra = $table['border_spacing_V'] / 2;
21940						}
21941					} else {
21942						if (!$this->simpleTables) {
21943							$extra = $bb / 2;
21944						} elseif ($this->simpleTables) {
21945							$extra = $table['simple']['border_details']['B']['w'] / 2;
21946						}
21947					}
21948					if (isset($table['is_thead'][$i]) && $table['is_thead'][$i]) {
21949						if ($j == 0) {
21950							$headerrowheight += $ch;
21951							$headerrowheightplus += $ch + $extra;
21952						}
21953					} elseif (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {
21954						if ($j == 0) {
21955							$footerrowheight += $ch;
21956							$footerrowheightplus += $ch + $extra;
21957						}
21958					} else {
21959						$checkmaxheight = max($checkmaxheight, $ch);
21960						$checkmaxheightplus = max($checkmaxheightplus, $ch + $extra);
21961					}
21962					if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {
21963						$firstrowheight = max($ch, $firstrowheight);
21964					}
21965					unset($c);
21966				}
21967			}//end of columns
21968		}//end of rows
21969
21970		$heightrow = &$table['hr'];
21971		foreach ($listspan as $span) {
21972			list($i, $j) = $span;
21973			$c = &$cells[$i][$j];
21974			$lr = $i + $c['rowspan'];
21975			if ($lr > $numrows) {
21976				$lr = $numrows;
21977			}
21978			$hs = $hsa = 0;
21979			$list = [];
21980			for ($k = $i; $k < $lr; $k++) {
21981				$hs += $heightrow[$k];
21982				// mPDF 6
21983				$sh = false; // specified height
21984				for ($m = 0; $m < $numcols; $m++) { // columns
21985					$tc = &$cells[$k][$m];
21986					if (isset($tc['rowspan'])) {
21987						continue;
21988					}
21989					if (isset($tc['h'])) {
21990						$sh = true;
21991						break;
21992					}
21993				}
21994				if (!$sh) {
21995					$list[] = $k;
21996				}
21997			}
21998
21999			if ($table['borders_separate']) {
22000				if ($i == ($numrows - 1) || ($i + $c['rowspan']) == ($numrows)) {
22001					$extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
22002				} else {
22003					$extra = $table['border_spacing_V'] / 2;
22004				}
22005			} else {
22006				if (!$this->simpleTables) {
22007					if ($this->packTableData) {
22008						list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
22009					} else {
22010						$bb = $c['border_details']['B']['w'];
22011					}
22012					$extra = $bb / 2;
22013				} elseif ($this->simpleTables) {
22014					$extra = $table['simple']['border_details']['B']['w'] / 2;
22015				}
22016			}
22017			if (!empty($table['is_thead'][$i])) {
22018				$headerrowheight = max($headerrowheight, $hs);
22019				$headerrowheightplus = max($headerrowheightplus, $hs + $extra);
22020			} elseif (!empty($table['is_tfoot'][$i])) {
22021				$footerrowheight = max($footerrowheight, $hs);
22022				$footerrowheightplus = max($footerrowheightplus, $hs + $extra);
22023			} else {
22024				$checkmaxheight = max($checkmaxheight, $hs);
22025				$checkmaxheightplus = max($checkmaxheightplus, $hs + $extra);
22026			}
22027			if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {
22028				$firstrowheight = max($hs, $firstrowheight);
22029			}
22030
22031			if ($c['mih'] > $hs) {
22032				if (!$hs) {
22033					for ($k = $i; $k < $lr; $k++) {
22034						$heightrow[$k] = $c['mih'] / $c['rowspan'];
22035					}
22036				} elseif (!count($list)) { // no rows in the rowspan have a height specified, so share amongst all rows equally
22037					$hi = $c['mih'] - $hs;
22038					for ($k = $i; $k < $lr; $k++) {
22039						$heightrow[$k] += ($heightrow[$k] / $hs) * $hi;
22040					}
22041				} else {
22042					$hi = $c['mih'] - $hs; // mPDF 6
22043					foreach ($list as $k) {
22044						$heightrow[$k] += $hi / (count($list)); // mPDF 6
22045					}
22046				}
22047			}
22048			unset($c);
22049
22050			// If rowspans overlap so that one or more rows do not have a height set...
22051			// i.e. for one or more rows, the only cells (explicit) in that row have rowspan>1
22052			// so heightrow is still == 0
22053			if ($heightrow[$i] == 0) {
22054				// Get row extent to analyse above and below
22055				$top = $i;
22056				foreach ($listspan as $checkspan) {
22057					list($cki, $ckj) = $checkspan;
22058					$c = &$cells[$cki][$ckj];
22059					if (isset($c['rowspan']) && $c['rowspan'] > 1) {
22060						if (($cki + $c['rowspan'] - 1) >= $i) {
22061							$top = min($top, $cki);
22062						}
22063					}
22064				}
22065				$bottom = $i + $c['rowspan'] - 1;
22066				// Check for overconstrained conditions
22067				for ($k = $top; $k <= $bottom; $k++) {
22068					// if ['hr'] for any of the others is also 0, then abort (too complicated)
22069					if ($k != $i && $heightrow[$k] == 0) {
22070						break(1);
22071					}
22072					// check again that top and bottom are not crossed by rowspans - or abort (too complicated)
22073					if ($k == $top) {
22074						// ???? take account of colspan as well???
22075						for ($m = 0; $m < $numcols; $m++) { // columns
22076							if (!isset($cells[$k][$m]) || $cells[$k][$m] == 0) {
22077								break(2);
22078							}
22079						}
22080					} elseif ($k == $bottom) {
22081						// ???? take account of colspan as well???
22082						for ($m = 0; $m < $numcols; $m++) { // columns
22083							$c = &$cells[$k][$m];
22084							if (isset($c['rowspan']) && $c['rowspan'] > 1) {
22085								break(2);
22086							}
22087						}
22088					}
22089				}
22090				// By columns add up col height using ['h'] if set or ['mih'] if not
22091				// Intentionally do not substract border-spacing
22092				$colH = [];
22093				$extH = 0;
22094				$newhr = [];
22095				for ($m = 0; $m < $numcols; $m++) { // columns
22096					for ($k = $top; $k <= $bottom; $k++) {
22097						if (isset($cells[$k][$m]) && $cells[$k][$m] != 0) {
22098							$c = &$cells[$k][$m];
22099							if (isset($c['h']) && $c['h']) {
22100								$useh = $c['h'];
22101							} // ???? take account of colspan as well???
22102							else {
22103								$useh = $c['mih'];
22104							}
22105							if (isset($colH[$m])) {
22106								$colH[$m] += $useh;
22107							} else {
22108								$colH[$m] = $useh;
22109							}
22110							if (!isset($c['rowspan']) || $c['rowspan'] < 2) {
22111								$newhr[$k] = max((isset($newhr[$k]) ? $newhr[$k] : 0), $useh);
22112							}
22113						}
22114					}
22115					$extH = max($extH, $colH[$m]); // mPDF 6
22116				}
22117				$newhr[$i] = $extH - array_sum($newhr);
22118				for ($k = $top; $k <= $bottom; $k++) {
22119					$heightrow[$k] = $newhr[$k];
22120				}
22121			}
22122
22123
22124			unset($c);
22125		}
22126
22127		$table['h'] = array_sum($heightrow);
22128		unset($heightrow);
22129
22130		if ($table['borders_separate']) {
22131			$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'];
22132		} else {
22133			$table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['max_cell_border_width']['T'] / 2 + $table['max_cell_border_width']['B'] / 2;
22134		}
22135
22136		$maxrowheight = $checkmaxheightplus + $headerrowheightplus + $footerrowheightplus;
22137		$maxfirstrowheight = $firstrowheight + $headerrowheightplus + $footerrowheightplus; // includes thead, 1st row and tfoot
22138		return [$table['h'], $maxrowheight, $temppgheight, $remainingpage, $maxfirstrowheight];
22139	}
22140
22141	function _tableGetWidth(&$table, $i, $j)
22142	{
22143		$cell = &$table['cells'][$i][$j];
22144		if ($cell) {
22145			if (isset($cell['x0'])) {
22146				return [$cell['x0'], $cell['w0']];
22147			}
22148			$x = 0;
22149			$widthcols = &$table['wc'];
22150			for ($k = 0; $k < $j; $k++) {
22151				$x += $widthcols[$k];
22152			}
22153			$w = $widthcols[$j];
22154			if (isset($cell['colspan'])) {
22155				for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) {
22156					$w += $widthcols[$k];
22157				}
22158			}
22159			$cell['x0'] = $x;
22160			$cell['w0'] = $w;
22161			return [$x, $w];
22162		}
22163		return [0, 0];
22164	}
22165
22166	function _splitTableGetWidth(&$table, $i, $j)
22167	{
22168		$cell = &$table['cells'][$i][$j];
22169		if ($cell) {
22170			if (isset($cell['x0'])) {
22171				return [$cell['x0'], $cell['w0']];
22172			}
22173			$x = 0;
22174			$widthcols = &$table['wc'];
22175			$pg = $table['colPg'][$j];
22176			for ($k = 0; $k < $j; $k++) {
22177				if ($table['colPg'][$k] == $pg) {
22178					$x += $widthcols[$k];
22179				}
22180			}
22181			$w = $widthcols[$j];
22182			if (isset($cell['colspan'])) {
22183				for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) {
22184					if ($table['colPg'][$k] == $pg) {
22185						$w += $widthcols[$k];
22186					}
22187				}
22188			}
22189			$cell['x0'] = $x;
22190			$cell['w0'] = $w;
22191			return [$x, $w];
22192		}
22193		return [0, 0];
22194	}
22195
22196	function _tableGetHeight(&$table, $i, $j)
22197	{
22198		$cell = &$table['cells'][$i][$j];
22199		if ($cell) {
22200			if (isset($cell['y0'])) {
22201				return [$cell['y0'], $cell['h0']];
22202			}
22203			$y = 0;
22204			$heightrow = &$table['hr'];
22205			for ($k = 0; $k < $i; $k++) {
22206				$y += $heightrow[$k];
22207			}
22208			$h = $heightrow[$i];
22209			if (isset($cell['rowspan'])) {
22210				for ($k = $i + $cell['rowspan'] - 1; $k > $i; $k--) {
22211					$h += $heightrow[$k];
22212				}
22213			}
22214			$cell['y0'] = $y;
22215			$cell['h0'] = $h;
22216			return [$y, $h];
22217		}
22218		return [0, 0];
22219	}
22220
22221	function _tableGetMaxRowHeight($table, $row)
22222	{
22223		if ($row == $table['nc'] - 1) {
22224			return $table['hr'][$row];
22225		}
22226		$maxrowheight = $table['hr'][$row];
22227		for ($i = $row + 1; $i < $table['nr']; $i++) {
22228			$cellsset = 0;
22229			for ($j = 0; $j < $table['nc']; $j++) {
22230				if ($table['cells'][$i][$j]) {
22231					if (isset($table['cells'][$i][$j]['colspan'])) {
22232						$cellsset += $table['cells'][$i][$j]['colspan'];
22233					} else {
22234						$cellsset += 1;
22235					}
22236				}
22237			}
22238			if ($cellsset == $table['nc']) {
22239				return $maxrowheight;
22240			} else {
22241				$maxrowheight += $table['hr'][$i];
22242			}
22243		}
22244		return $maxrowheight;
22245	}
22246
22247	// CHANGED TO ALLOW TABLE BORDER TO BE SPECIFIED CORRECTLY - added border_details
22248	function _tableRect($x, $y, $w, $h, $bord = -1, $details = [], $buffer = false, $bSeparate = false, $cort = 'cell', $tablecorner = '', $bsv = 0, $bsh = 0)
22249	{
22250		$cellBorderOverlay = [];
22251
22252		if ($bord == -1) {
22253			$this->Rect($x, $y, $w, $h);
22254		} elseif ($this->simpleTables && ($cort == 'cell')) {
22255			$this->SetLineWidth($details['L']['w']);
22256			if ($details['L']['c']) {
22257				$this->SetDColor($details['L']['c']);
22258			} else {
22259				$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
22260			}
22261			$this->SetLineJoin(0);
22262			$this->Rect($x, $y, $w, $h);
22263		} elseif ($bord) {
22264			if (!$bSeparate && $buffer) {
22265				$priority = 'LRTB';
22266				for ($p = 0; $p < strlen($priority); $p++) {
22267					$side = $priority[$p];
22268					$details['p'] = $side;
22269
22270					$dom = 0;
22271					if (isset($details[$side]['w'])) {
22272						$dom += ($details[$side]['w'] * 100000);
22273					}
22274					if (isset($details[$side]['style'])) {
22275						$dom += (array_search($details[$side]['style'], $this->borderstyles) * 100);
22276					}
22277					if (isset($details[$side]['dom'])) {
22278						$dom += ($details[$side]['dom'] * 10);
22279					}
22280
22281					// Precedence to darker colours at joins
22282					$coldom = 0;
22283					if (isset($details[$side]['c']) && is_array($details[$side]['c'])) {
22284						if ($details[$side]['c']{0} == 3) {  // RGB
22285							$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);
22286						}
22287					} // 10 black - 0 white
22288					if ($coldom) {
22289						$dom += $coldom;
22290					}
22291					// Lastly precedence to RIGHT and BOTTOM cells at joins
22292					if (isset($details['cellposdom'])) {
22293						$dom += $details['cellposdom'];
22294					}
22295
22296					$save = false;
22297					if ($side == 'T' && $this->issetBorder($bord, Border::TOP)) {
22298						$cbord = Border::TOP;
22299						$save = true;
22300					} elseif ($side == 'L' && $this->issetBorder($bord, Border::LEFT)) {
22301						$cbord = Border::LEFT;
22302						$save = true;
22303					} elseif ($side == 'R' && $this->issetBorder($bord, Border::RIGHT)) {
22304						$cbord = Border::RIGHT;
22305						$save = true;
22306					} elseif ($side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) {
22307						$cbord = Border::BOTTOM;
22308						$save = true;
22309					}
22310
22311					if ($save) {
22312						$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);
22313						if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset' || $details[$side]['style'] == 'double') {
22314							$details[$side]['overlay'] = true;
22315							$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);
22316						}
22317					}
22318				}
22319				return;
22320			}
22321
22322			if (isset($details['p']) && strlen($details['p']) > 1) {
22323				$priority = $details['p'];
22324			} else {
22325				$priority = 'LTRB';
22326			}
22327			$Tw = 0;
22328			$Rw = 0;
22329			$Bw = 0;
22330			$Lw = 0;
22331			if (isset($details['T']['w'])) {
22332				$Tw = $details['T']['w'];
22333			}
22334			if (isset($details['R']['w'])) {
22335				$Rw = $details['R']['w'];
22336			}
22337			if (isset($details['B']['w'])) {
22338				$Bw = $details['B']['w'];
22339			}
22340			if (isset($details['L']['w'])) {
22341				$Lw = $details['L']['w'];
22342			}
22343
22344			$x2 = $x + $w;
22345			$y2 = $y + $h;
22346			$oldlinewidth = $this->LineWidth;
22347
22348			for ($p = 0; $p < strlen($priority); $p++) {
22349				$side = $priority[$p];
22350				$xadj = 0;
22351				$xadj2 = 0;
22352				$yadj = 0;
22353				$yadj2 = 0;
22354				$print = false;
22355				if ($Tw && $side == 'T' && $this->issetBorder($bord, Border::TOP)) { // TOP
22356					$ly1 = $y;
22357					$ly2 = $y;
22358					$lx1 = $x;
22359					$lx2 = $x2;
22360					$this->SetLineWidth($Tw);
22361					if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {
22362						if ($Tw > $Lw) {
22363							$xadj = ($Tw - $Lw) / 2;
22364						}
22365						if ($Tw < $Lw) {
22366							$xadj = ($Tw + $Lw) / 2;
22367						}
22368					} else {
22369						$xadj = $Tw / 2 - $bsh / 2;
22370					}
22371					if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {
22372						if ($Tw > $Rw) {
22373							$xadj2 = ($Tw - $Rw) / 2;
22374						}
22375						if ($Tw < $Rw) {
22376							$xadj2 = ($Tw + $Rw) / 2;
22377						}
22378					} else {
22379						$xadj2 = $Tw / 2 - $bsh / 2;
22380					}
22381					if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TL'])) {
22382						$xadj = ($Tw - $details['mbw']['TL']) / 2;
22383					}
22384					if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TR'])) {
22385						$xadj2 = ($Tw - $details['mbw']['TR']) / 2;
22386					}
22387					$print = true;
22388				}
22389				if ($Lw && $side == 'L' && $this->issetBorder($bord, Border::LEFT)) { // LEFT
22390					$ly1 = $y;
22391					$ly2 = $y2;
22392					$lx1 = $x;
22393					$lx2 = $x;
22394					$this->SetLineWidth($Lw);
22395					if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {
22396						if ($Lw > $Tw) {
22397							$yadj = ($Lw - $Tw) / 2;
22398						}
22399						if ($Lw < $Tw) {
22400							$yadj = ($Lw + $Tw) / 2;
22401						}
22402					} else {
22403						$yadj = $Lw / 2 - $bsv / 2;
22404					}
22405					if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {
22406						if ($Lw > $Bw) {
22407							$yadj2 = ($Lw - $Bw) / 2;
22408						}
22409						if ($Lw < $Bw) {
22410							$yadj2 = ($Lw + $Bw) / 2;
22411						}
22412					} else {
22413						$yadj2 = $Lw / 2 - $bsv / 2;
22414					}
22415					if (!$bSeparate && $details['mbw']['LT']) {
22416						$yadj = ($Lw - $details['mbw']['LT']) / 2;
22417					}
22418					if (!$bSeparate && $details['mbw']['LB']) {
22419						$yadj2 = ($Lw - $details['mbw']['LB']) / 2;
22420					}
22421					$print = true;
22422				}
22423				if ($Rw && $side == 'R' && $this->issetBorder($bord, Border::RIGHT)) { // RIGHT
22424					$ly1 = $y;
22425					$ly2 = $y2;
22426					$lx1 = $x2;
22427					$lx2 = $x2;
22428					$this->SetLineWidth($Rw);
22429					if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {
22430						if ($Rw < $Tw) {
22431							$yadj = ($Rw + $Tw) / 2;
22432						}
22433						if ($Rw > $Tw) {
22434							$yadj = ($Rw - $Tw) / 2;
22435						}
22436					} else {
22437						$yadj = $Rw / 2 - $bsv / 2;
22438					}
22439
22440					if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {
22441						if ($Rw > $Bw) {
22442							$yadj2 = ($Rw - $Bw) / 2;
22443						}
22444						if ($Rw < $Bw) {
22445							$yadj2 = ($Rw + $Bw) / 2;
22446						}
22447					} else {
22448						$yadj2 = $Rw / 2 - $bsv / 2;
22449					}
22450
22451					if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RT'])) {
22452						$yadj = ($Rw - $details['mbw']['RT']) / 2;
22453					}
22454					if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RB'])) {
22455						$yadj2 = ($Rw - $details['mbw']['RB']) / 2;
22456					}
22457					$print = true;
22458				}
22459				if ($Bw && $side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) { // BOTTOM
22460					$ly1 = $y2;
22461					$ly2 = $y2;
22462					$lx1 = $x;
22463					$lx2 = $x2;
22464					$this->SetLineWidth($Bw);
22465					if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {
22466						if ($Bw > $Lw) {
22467							$xadj = ($Bw - $Lw) / 2;
22468						}
22469						if ($Bw < $Lw) {
22470							$xadj = ($Bw + $Lw) / 2;
22471						}
22472					} else {
22473						$xadj = $Bw / 2 - $bsh / 2;
22474					}
22475					if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {
22476						if ($Bw > $Rw) {
22477							$xadj2 = ($Bw - $Rw) / 2;
22478						}
22479						if ($Bw < $Rw) {
22480							$xadj2 = ($Bw + $Rw) / 2;
22481						}
22482					} else {
22483						$xadj2 = $Bw / 2 - $bsh / 2;
22484					}
22485					if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BL'])) {
22486						$xadj = ($Bw - $details['mbw']['BL']) / 2;
22487					}
22488					if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BR'])) {
22489						$xadj2 = ($Bw - $details['mbw']['BR']) / 2;
22490					}
22491					$print = true;
22492				}
22493
22494				// Now draw line
22495				if ($print) {
22496					/* -- TABLES-ADVANCED-BORDERS -- */
22497					if ($details[$side]['style'] == 'double') {
22498						if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {
22499							if ($details[$side]['c']) {
22500								$this->SetDColor($details[$side]['c']);
22501							} else {
22502								$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
22503							}
22504							$this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
22505						}
22506						if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {
22507							if ($bSeparate && $cort == 'table') {
22508								if ($side == 'T') {
22509									$xadj -= $this->LineWidth / 2;
22510									$xadj2 -= $this->LineWidth;
22511									if ($this->issetBorder($bord, Border::LEFT)) {
22512										$xadj += $this->LineWidth / 2;
22513									}
22514									if ($this->issetBorder($bord, Border::RIGHT)) {
22515										$xadj2 += $this->LineWidth;
22516									}
22517								}
22518								if ($side == 'L') {
22519									$yadj -= $this->LineWidth / 2;
22520									$yadj2 -= $this->LineWidth;
22521									if ($this->issetBorder($bord, Border::TOP)) {
22522										$yadj += $this->LineWidth / 2;
22523									}
22524									if ($this->issetBorder($bord, Border::BOTTOM)) {
22525										$yadj2 += $this->LineWidth;
22526									}
22527								}
22528								if ($side == 'B') {
22529									$xadj -= $this->LineWidth / 2;
22530									$xadj2 -= $this->LineWidth;
22531									if ($this->issetBorder($bord, Border::LEFT)) {
22532										$xadj += $this->LineWidth / 2;
22533									}
22534									if ($this->issetBorder($bord, Border::RIGHT)) {
22535										$xadj2 += $this->LineWidth;
22536									}
22537								}
22538								if ($side == 'R') {
22539									$yadj -= $this->LineWidth / 2;
22540									$yadj2 -= $this->LineWidth;
22541									if ($this->issetBorder($bord, Border::TOP)) {
22542										$yadj += $this->LineWidth / 2;
22543									}
22544									if ($this->issetBorder($bord, Border::BOTTOM)) {
22545										$yadj2 += $this->LineWidth;
22546									}
22547								}
22548							}
22549
22550							$this->SetLineWidth($this->LineWidth / 3);
22551
22552							$tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);
22553							for ($l = 0; $l <= $this->blklvl; $l++) {
22554								if ($this->blk[$l]['bgcolor']) {
22555									$tbcol = ($this->blk[$l]['bgcolorarray']);
22556								}
22557							}
22558
22559							if ($bSeparate) {
22560								$cellBorderOverlay[] = [
22561									'x' => $lx1 + $xadj,
22562									'y' => $ly1 + $yadj,
22563									'x2' => $lx2 - $xadj2,
22564									'y2' => $ly2 - $yadj2,
22565									'col' => $tbcol,
22566									'lw' => $this->LineWidth,
22567								];
22568							} else {
22569								$this->SetDColor($tbcol);
22570								$this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
22571							}
22572						}
22573					} elseif (isset($details[$side]['style']) && ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset')) {
22574						if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {
22575							if ($details[$side]['c']) {
22576								$this->SetDColor($details[$side]['c']);
22577							} else {
22578								$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
22579							}
22580							if ($details[$side]['style'] == 'outset' || $details[$side]['style'] == 'groove') {
22581								$nc = $this->colorConverter->darken($details[$side]['c']);
22582								$this->SetDColor($nc);
22583							} elseif ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {
22584								$nc = $this->colorConverter->lighten($details[$side]['c']);
22585								$this->SetDColor($nc);
22586							}
22587							$this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
22588						}
22589						if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {
22590							if ($details[$side]['c']) {
22591								$this->SetDColor($details[$side]['c']);
22592							} else {
22593								$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
22594							}
22595							$doubleadj = ($this->LineWidth) / 3;
22596							$this->SetLineWidth($this->LineWidth / 2);
22597							$xadj3 = $yadj3 = $wadj3 = $hadj3 = 0;
22598
22599							if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {
22600								$nc = $this->colorConverter->darken($details[$side]['c']);
22601
22602								if ($bSeparate && $cort == 'table') {
22603									if ($side == 'T') {
22604										$yadj3 = $this->LineWidth / 2;
22605										$xadj3 = -$this->LineWidth / 2;
22606										$wadj3 = $this->LineWidth;
22607										if ($this->issetBorder($bord, Border::LEFT)) {
22608											$xadj3 += $this->LineWidth;
22609											$wadj3 -= $this->LineWidth;
22610										}
22611										if ($this->issetBorder($bord, Border::RIGHT)) {
22612											$wadj3 -= $this->LineWidth * 2;
22613										}
22614									}
22615									if ($side == 'L') {
22616										$xadj3 = $this->LineWidth / 2;
22617										$yadj3 = -$this->LineWidth / 2;
22618										$hadj3 = $this->LineWidth;
22619										if ($this->issetBorder($bord, Border::TOP)) {
22620											$yadj3 += $this->LineWidth;
22621											$hadj3 -= $this->LineWidth;
22622										}
22623										if ($this->issetBorder($bord, Border::BOTTOM)) {
22624											$hadj3 -= $this->LineWidth * 2;
22625										}
22626									}
22627									if ($side == 'B') {
22628										$yadj3 = $this->LineWidth / 2;
22629										$xadj3 = -$this->LineWidth / 2;
22630										$wadj3 = $this->LineWidth;
22631									}
22632									if ($side == 'R') {
22633										$xadj3 = $this->LineWidth / 2;
22634										$yadj3 = -$this->LineWidth / 2;
22635										$hadj3 = $this->LineWidth;
22636									}
22637								} elseif ($side == 'T') {
22638									$yadj3 = $this->LineWidth / 2;
22639									$xadj3 = $this->LineWidth / 2;
22640									$wadj3 = -$this->LineWidth * 2;
22641								} elseif ($side == 'L') {
22642									$xadj3 = $this->LineWidth / 2;
22643									$yadj3 = $this->LineWidth / 2;
22644									$hadj3 = -$this->LineWidth * 2;
22645								} elseif ($side == 'B' && $bSeparate) {
22646									$yadj3 = $this->LineWidth / 2;
22647									$wadj3 = $this->LineWidth / 2;
22648								} elseif ($side == 'R' && $bSeparate) {
22649									$xadj3 = $this->LineWidth / 2;
22650									$hadj3 = $this->LineWidth / 2;
22651								} elseif ($side == 'B') {
22652									$yadj3 = $this->LineWidth / 2;
22653									$xadj3 = $this->LineWidth / 2;
22654								} elseif ($side == 'R') {
22655									$xadj3 = $this->LineWidth / 2;
22656									$yadj3 = $this->LineWidth / 2;
22657								}
22658							} else {
22659								$nc = $this->colorConverter->lighten($details[$side]['c']);
22660
22661								if ($bSeparate && $cort == 'table') {
22662									if ($side == 'T') {
22663										$yadj3 = $this->LineWidth / 2;
22664										$xadj3 = -$this->LineWidth / 2;
22665										$wadj3 = $this->LineWidth;
22666										if ($this->issetBorder($bord, Border::LEFT)) {
22667											$xadj3 += $this->LineWidth;
22668											$wadj3 -= $this->LineWidth;
22669										}
22670									}
22671									if ($side == 'L') {
22672										$xadj3 = $this->LineWidth / 2;
22673										$yadj3 = -$this->LineWidth / 2;
22674										$hadj3 = $this->LineWidth;
22675										if ($this->issetBorder($bord, Border::TOP)) {
22676											$yadj3 += $this->LineWidth;
22677											$hadj3 -= $this->LineWidth;
22678										}
22679									}
22680									if ($side == 'B') {
22681										$yadj3 = $this->LineWidth / 2;
22682										$xadj3 = -$this->LineWidth / 2;
22683										$wadj3 = $this->LineWidth;
22684										if ($this->issetBorder($bord, Border::LEFT)) {
22685											$xadj3 += $this->LineWidth;
22686											$wadj3 -= $this->LineWidth;
22687										}
22688									}
22689									if ($side == 'R') {
22690										$xadj3 = $this->LineWidth / 2;
22691										$yadj3 = -$this->LineWidth / 2;
22692										$hadj3 = $this->LineWidth;
22693										if ($this->issetBorder($bord, Border::TOP)) {
22694											$yadj3 += $this->LineWidth;
22695											$hadj3 -= $this->LineWidth;
22696										}
22697									}
22698								} elseif ($side == 'T') {
22699									$yadj3 = $this->LineWidth / 2;
22700									$xadj3 = $this->LineWidth / 2;
22701								} elseif ($side == 'L') {
22702									$xadj3 = $this->LineWidth / 2;
22703									$yadj3 = $this->LineWidth / 2;
22704								} elseif ($side == 'B' && $bSeparate) {
22705									$yadj3 = $this->LineWidth / 2;
22706									$xadj3 = $this->LineWidth / 2;
22707								} elseif ($side == 'R' && $bSeparate) {
22708									$xadj3 = $this->LineWidth / 2;
22709									$yadj3 = $this->LineWidth / 2;
22710								} elseif ($side == 'B') {
22711									$yadj3 = $this->LineWidth / 2;
22712									$xadj3 = -$this->LineWidth / 2;
22713									$wadj3 = $this->LineWidth;
22714								} elseif ($side == 'R') {
22715									$xadj3 = $this->LineWidth / 2;
22716									$yadj3 = -$this->LineWidth / 2;
22717									$hadj3 = $this->LineWidth;
22718								}
22719							}
22720
22721							if ($bSeparate) {
22722								$cellBorderOverlay[] = [
22723									'x' => $lx1 + $xadj + $xadj3,
22724									'y' => $ly1 + $yadj + $yadj3,
22725									'x2' => $lx2 - $xadj2 + $xadj3 + $wadj3,
22726									'y2' => $ly2 - $yadj2 + $yadj3 + $hadj3,
22727									'col' => $nc,
22728									'lw' => $this->LineWidth,
22729								];
22730							} else {
22731								$this->SetDColor($nc);
22732								$this->Line($lx1 + $xadj + $xadj3, $ly1 + $yadj + $yadj3, $lx2 - $xadj2 + $xadj3 + $wadj3, $ly2 - $yadj2 + $yadj3 + $hadj3);
22733							}
22734						}
22735					} else {
22736						/* -- END TABLES-ADVANCED-BORDERS -- */
22737						if ($details[$side]['style'] == 'dashed') {
22738							$dashsize = 2; // final dash will be this + 1*linewidth
22739							$dashsizek = 1.5; // ratio of Dash/Blank
22740							$this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));
22741						} elseif ($details[$side]['style'] == 'dotted') {
22742							$this->SetLineJoin(1);
22743							$this->SetLineCap(1);
22744							$this->SetDash(0.001, ($this->LineWidth * 2));
22745						}
22746						if ($details[$side]['c']) {
22747							$this->SetDColor($details[$side]['c']);
22748						} else {
22749							$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
22750						}
22751						$this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
22752						/* -- TABLES-ADVANCED-BORDERS -- */
22753					}
22754					/* -- END TABLES-ADVANCED-BORDERS -- */
22755
22756					// Reset Corners
22757					$this->SetDash();
22758					// BUTT style line cap
22759					$this->SetLineCap(2);
22760				}
22761			}
22762
22763			if ($bSeparate && count($cellBorderOverlay)) {
22764				foreach ($cellBorderOverlay as $cbo) {
22765					$this->SetLineWidth($cbo['lw']);
22766					$this->SetDColor($cbo['col']);
22767					$this->Line($cbo['x'], $cbo['y'], $cbo['x2'], $cbo['y2']);
22768				}
22769			}
22770
22771			// $this->SetLineWidth($oldlinewidth);
22772			// $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
22773		}
22774	}
22775
22776	/* -- TABLES -- */
22777	/* -- TABLES-ADVANCED-BORDERS -- */
22778
22779	/* -- END TABLES-ADVANCED-BORDERS -- */
22780
22781	function setBorder(&$var, $flag, $set = true)
22782	{
22783		$flag = intval($flag);
22784		if ($set) {
22785			$set = true;
22786		}
22787		$var = intval($var);
22788		$var = $set ? ($var | $flag) : ($var & ~$flag);
22789	}
22790
22791	function issetBorder($var, $flag)
22792	{
22793		$flag = intval($flag);
22794		$var = intval($var);
22795		return (($var & $flag) == $flag);
22796	}
22797
22798	function _table2cellBorder(&$tableb, &$cbdb, &$cellb, $bval)
22799	{
22800		if ($tableb && $tableb['w'] > $cbdb['w']) {
22801			$cbdb = $tableb;
22802			$this->setBorder($cellb, $bval);
22803		} elseif ($tableb && $tableb['w'] == $cbdb['w'] && array_search($tableb['style'], $this->borderstyles) > array_search($cbdb['style'], $this->borderstyles)) {
22804			$cbdb = $tableb;
22805			$this->setBorder($cellb, $bval);
22806		}
22807	}
22808
22809	// FIX BORDERS ********************************************
22810	function _fixTableBorders(&$table)
22811	{
22812		if (!$table['borders_separate'] && $table['border_details']['L']['w']) {
22813			$table['max_cell_border_width']['L'] = $table['border_details']['L']['w'];
22814		}
22815		if (!$table['borders_separate'] && $table['border_details']['R']['w']) {
22816			$table['max_cell_border_width']['R'] = $table['border_details']['R']['w'];
22817		}
22818		if (!$table['borders_separate'] && $table['border_details']['T']['w']) {
22819			$table['max_cell_border_width']['T'] = $table['border_details']['T']['w'];
22820		}
22821		if (!$table['borders_separate'] && $table['border_details']['B']['w']) {
22822			$table['max_cell_border_width']['B'] = $table['border_details']['B']['w'];
22823		}
22824		if ($this->simpleTables) {
22825			return;
22826		}
22827		$cells = &$table['cells'];
22828		$numcols = $table['nc'];
22829		$numrows = $table['nr'];
22830		/* -- TABLES-ADVANCED-BORDERS -- */
22831		if (isset($table['topntail']) && $table['topntail']) {
22832			$tntborddet = $this->border_details($table['topntail']);
22833		}
22834		if (isset($table['thead-underline']) && $table['thead-underline']) {
22835			$thuborddet = $this->border_details($table['thead-underline']);
22836		}
22837		/* -- END TABLES-ADVANCED-BORDERS -- */
22838
22839		for ($i = 0; $i < $numrows; $i++) { // Rows
22840			for ($j = 0; $j < $numcols; $j++) { // Columns
22841				if (isset($cells[$i][$j]) && $cells[$i][$j]) {
22842					$cell = &$cells[$i][$j];
22843					if ($this->packTableData) {
22844						$cbord = $this->_unpackCellBorder($cell['borderbin']);
22845					} else {
22846						$cbord = &$cells[$i][$j];
22847					}
22848
22849					// mPDF 5.7.3
22850					if (!$cbord['border'] && $cbord['border'] !== 0 && isset($table['border']) && $table['border'] && $this->table_border_attr_set) {
22851						$cbord['border'] = $table['border'];
22852						$cbord['border_details'] = $table['border_details'];
22853					}
22854
22855					if (isset($cell['colspan']) && $cell['colspan'] > 1) {
22856						$ccolsp = $cell['colspan'];
22857					} else {
22858						$ccolsp = 1;
22859					}
22860					if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {
22861						$crowsp = $cell['rowspan'];
22862					} else {
22863						$crowsp = 1;
22864					}
22865
22866					$cbord['border_details']['cellposdom'] = ((($i + 1) / $numrows) / 10000 ) + ((($j + 1) / $numcols) / 10 );
22867					// Inherit Cell border from Table border
22868					if ($this->table_border_css_set && !$table['borders_separate']) {
22869						if ($i == 0) {
22870							$this->_table2cellBorder($table['border_details']['T'], $cbord['border_details']['T'], $cbord['border'], Border::TOP);
22871						}
22872						if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {
22873							$this->_table2cellBorder($table['border_details']['B'], $cbord['border_details']['B'], $cbord['border'], Border::BOTTOM);
22874						}
22875						if ($j == 0) {
22876							$this->_table2cellBorder($table['border_details']['L'], $cbord['border_details']['L'], $cbord['border'], Border::LEFT);
22877						}
22878						if ($j == ($numcols - 1) || ($j + $ccolsp) == ($numcols)) {
22879							$this->_table2cellBorder($table['border_details']['R'], $cbord['border_details']['R'], $cbord['border'], Border::RIGHT);
22880						}
22881					}
22882
22883					/* -- TABLES-ADVANCED-BORDERS -- */
22884					$fixbottom = true;
22885					if (isset($table['topntail']) && $table['topntail']) {
22886						if ($i == 0) {
22887							$cbord['border_details']['T'] = $tntborddet;
22888							$this->setBorder($cbord['border'], Border::TOP);
22889						}
22890						if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {
22891							$cbord['border_details']['B'] = $tntborddet;
22892							$this->setBorder($cbord['border'], Border::BOTTOM);
22893							$fixbottom = false;
22894						} elseif ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows']) {
22895							if (!$table['borders_separate']) {
22896								$cbord['border_details']['T'] = $tntborddet;
22897								$this->setBorder($cbord['border'], Border::TOP);
22898							}
22899						}
22900						if ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'] - 1)) {
22901							if (!$table['borders_separate']) {
22902								$cbord['border_details']['B'] = $tntborddet;
22903								$this->setBorder($cbord['border'], Border::BOTTOM);
22904								$fixbottom = false;
22905							}
22906						} elseif ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'])) {
22907							$cbord['border_details']['T'] = $tntborddet;
22908							$this->setBorder($cbord['border'], Border::TOP);
22909						}
22910						if ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader
22911							if (!$table['borders_separate']) {
22912								$cbord['border_details']['T'] = $tntborddet;
22913								$this->setBorder($cbord['border'], Border::TOP);
22914							}
22915						}
22916						if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {
22917							$cbord['border_details']['B'] = $tntborddet;
22918							$this->setBorder($cbord['border'], Border::BOTTOM);
22919						}
22920					}
22921					if (isset($table['thead-underline']) && $table['thead-underline']) {
22922						if ($table['borders_separate']) {
22923							if ($i == 0) {
22924								$cbord['border_details']['B'] = $thuborddet;
22925								$this->setBorder($cbord['border'], Border::BOTTOM);
22926								$fixbottom = false;
22927							}
22928						} else {
22929							if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {
22930								$cbord['border_details']['T'] = $thuborddet;
22931								$this->setBorder($cbord['border'], Border::TOP);
22932							} elseif ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader
22933								$cbord['border_details']['T'] = $thuborddet;
22934								$this->setBorder($cbord['border'], Border::TOP);
22935							}
22936						}
22937					}
22938
22939					// Collapse Border - Algorithm for conflicting borders
22940					// Hidden >> Width >> double>solid>dashed>dotted... >> style set on cell>table >> top/left>bottom/right
22941					// Do not turn off border which is overridden
22942					// Needed for page break for TOP/BOTTOM both to be defined in Collapsed borders
22943					// Means it is painted twice. (Left/Right can still disable overridden border)
22944					if (!$table['borders_separate']) {
22945
22946						if (($i < ($numrows - 1) || ($i + $crowsp) < $numrows ) && $fixbottom) { // Bottom
22947
22948							for ($cspi = 0; $cspi < $ccolsp; $cspi++) {
22949
22950								// already defined Top for adjacent cell below
22951								if (isset($cells[($i + $crowsp)][$j + $cspi])) {
22952									if ($this->packTableData) {
22953										$adjc = $cells[($i + $crowsp)][$j + $cspi];
22954										$celladj = $this->_unpackCellBorder($adjc['borderbin']);
22955									} else {
22956										$celladj = & $cells[($i + $crowsp)][$j + $cspi];
22957									}
22958								} else {
22959									$celladj = false;
22960								}
22961
22962								if ($celladj && $celladj['border_details']['T']['s'] == 1) {
22963
22964									$csadj = $celladj['border_details']['T']['w'];
22965									$csthis = $cbord['border_details']['B']['w'];
22966
22967									// Hidden
22968									if ($cbord['border_details']['B']['style'] == 'hidden') {
22969
22970										$celladj['border_details']['T'] = $cbord['border_details']['B'];
22971										$this->setBorder($celladj['border'], Border::TOP, false);
22972										$this->setBorder($cbord['border'], Border::BOTTOM, false);
22973
22974									} elseif ($celladj['border_details']['T']['style'] == 'hidden') {
22975
22976										$cbord['border_details']['B'] = $celladj['border_details']['T'];
22977										$this->setBorder($cbord['border'], Border::BOTTOM, false);
22978										$this->setBorder($celladj['border'], Border::TOP, false);
22979
22980									} elseif ($csthis > $csadj) { // Width
22981
22982										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
22983											$celladj['border_details']['T'] = $cbord['border_details']['B'];
22984											$this->setBorder($cbord['border'], Border::BOTTOM);
22985										}
22986
22987									} elseif ($csadj > $csthis) {
22988
22989										if ($ccolsp < 2) { // don't overwrite this cell if it spans
22990											$cbord['border_details']['B'] = $celladj['border_details']['T'];
22991											$this->setBorder($celladj['border'], Border::TOP);
22992										}
22993
22994									} elseif (array_search($cbord['border_details']['B']['style'], $this->borderstyles) > array_search($celladj['border_details']['T']['style'], $this->borderstyles)) { // double>solid>dashed>dotted...
22995
22996										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
22997											$celladj['border_details']['T'] = $cbord['border_details']['B'];
22998											$this->setBorder($cbord['border'], Border::BOTTOM);
22999										}
23000
23001									} elseif (array_search($celladj['border_details']['T']['style'], $this->borderstyles) > array_search($cbord['border_details']['B']['style'], $this->borderstyles)) {
23002
23003										if ($ccolsp < 2) { // don't overwrite this cell if it spans
23004											$cbord['border_details']['B'] = $celladj['border_details']['T'];
23005											$this->setBorder($celladj['border'], Border::TOP);
23006										}
23007
23008									} elseif ($celladj['border_details']['T']['dom'] > $celladj['border_details']['B']['dom']) { // Style set on cell vs. table
23009
23010										if ($ccolsp < 2) { // don't overwrite this cell if it spans
23011											$cbord['border_details']['B'] = $celladj['border_details']['T'];
23012											$this->setBorder($celladj['border'], Border::TOP);
23013										}
23014
23015									} else { // Style set on cell vs. table  - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT
23016
23017										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
23018											$celladj['border_details']['T'] = $cbord['border_details']['B'];
23019											$this->setBorder($cbord['border'], Border::BOTTOM);
23020										}
23021
23022									}
23023
23024								} elseif ($celladj) {
23025
23026									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
23027										$celladj['border_details']['T'] = $cbord['border_details']['B'];
23028									}
23029
23030								}
23031
23032								// mPDF 5.7.4
23033								if ($celladj && $this->packTableData) {
23034									$cells[$i + $crowsp][$j + $cspi]['borderbin'] = $this->_packCellBorder($celladj);
23035								}
23036
23037								unset($celladj);
23038							}
23039						}
23040
23041						if ($j < ($numcols - 1) || ($j + $ccolsp) < $numcols) { // Right-Left
23042
23043							for ($cspi = 0; $cspi < $crowsp; $cspi++) {
23044
23045								// already defined Left for adjacent cell to R
23046								if (isset($cells[($i + $cspi)][$j + $ccolsp])) {
23047									if ($this->packTableData) {
23048										$adjc = $cells[($i + $cspi)][$j + $ccolsp];
23049										$celladj = $this->_unpackCellBorder($adjc['borderbin']);
23050									} else {
23051										$celladj = & $cells[$i + $cspi][$j + $ccolsp];
23052									}
23053								} else {
23054									$celladj = false;
23055								}
23056								if ($celladj && $celladj['border_details']['L']['s'] == 1) {
23057									$csadj = $celladj['border_details']['L']['w'];
23058									$csthis = $cbord['border_details']['R']['w'];
23059									// Hidden
23060									if ($cbord['border_details']['R']['style'] == 'hidden') {
23061										$celladj['border_details']['L'] = $cbord['border_details']['R'];
23062										$this->setBorder($celladj['border'], Border::LEFT, false);
23063										$this->setBorder($cbord['border'], Border::RIGHT, false);
23064									} elseif ($celladj['border_details']['L']['style'] == 'hidden') {
23065										$cbord['border_details']['R'] = $celladj['border_details']['L'];
23066										$this->setBorder($cbord['border'], Border::RIGHT, false);
23067										$this->setBorder($celladj['border'], Border::LEFT, false);
23068									} // Width
23069									elseif ($csthis > $csadj) {
23070										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
23071											$celladj['border_details']['L'] = $cbord['border_details']['R'];
23072											$this->setBorder($cbord['border'], Border::RIGHT);
23073											$this->setBorder($celladj['border'], Border::LEFT, false);
23074										}
23075									} elseif ($csadj > $csthis) {
23076										if ($crowsp < 2) { // don't overwrite this cell if it spans
23077											$cbord['border_details']['R'] = $celladj['border_details']['L'];
23078											$this->setBorder($cbord['border'], Border::RIGHT, false);
23079											$this->setBorder($celladj['border'], Border::LEFT);
23080										}
23081									} // double>solid>dashed>dotted...
23082									elseif (array_search($cbord['border_details']['R']['style'], $this->borderstyles) > array_search($celladj['border_details']['L']['style'], $this->borderstyles)) {
23083										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
23084											$celladj['border_details']['L'] = $cbord['border_details']['R'];
23085											$this->setBorder($celladj['border'], Border::LEFT, false);
23086											$this->setBorder($cbord['border'], Border::RIGHT);
23087										}
23088									} elseif (array_search($celladj['border_details']['L']['style'], $this->borderstyles) > array_search($cbord['border_details']['R']['style'], $this->borderstyles)) {
23089										if ($crowsp < 2) { // don't overwrite this cell if it spans
23090											$cbord['border_details']['R'] = $celladj['border_details']['L'];
23091											$this->setBorder($cbord['border'], Border::RIGHT, false);
23092											$this->setBorder($celladj['border'], Border::LEFT);
23093										}
23094									} // Style set on cell vs. table
23095									elseif ($celladj['border_details']['L']['dom'] > $cbord['border_details']['R']['dom']) {
23096										if ($crowsp < 2) { // don't overwrite this cell if it spans
23097											$cbord['border_details']['R'] = $celladj['border_details']['L'];
23098											$this->setBorder($celladj['border'], Border::LEFT);
23099										}
23100									} // Style set on cell vs. table  - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT
23101									else {
23102										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
23103											$celladj['border_details']['L'] = $cbord['border_details']['R'];
23104											$this->setBorder($cbord['border'], Border::RIGHT);
23105										}
23106									}
23107								} elseif ($celladj) {
23108									// if right-cell border is not set
23109									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
23110										$celladj['border_details']['L'] = $cbord['border_details']['R'];
23111									}
23112								}
23113								// mPDF 5.7.4
23114								if ($celladj && $this->packTableData) {
23115									$cells[$i + $cspi][$j + $ccolsp]['borderbin'] = $this->_packCellBorder($celladj);
23116								}
23117								unset($celladj);
23118							}
23119						}
23120					}
23121
23122
23123					// Set maximum cell border width meeting at LRTB edges of cell - used for extended cell border
23124					// ['border_details']['mbw']['LT'] = meeting border width - Left border - Top end
23125					if (!$table['borders_separate']) {
23126						$cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $cbord['border_details']['L']['w']);
23127						$cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $cbord['border_details']['R']['w']);
23128						$cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $cbord['border_details']['T']['w']);
23129						$cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $cbord['border_details']['B']['w']);
23130						$cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $cbord['border_details']['L']['w']);
23131						$cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $cbord['border_details']['R']['w']);
23132						$cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $cbord['border_details']['T']['w']);
23133						$cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $cbord['border_details']['B']['w']);
23134						if (($i + $crowsp) < $numrows && isset($cells[$i + $crowsp][$j])) { // Has Bottom adjoining cell
23135							if ($this->packTableData) {
23136								$adjc = $cells[$i + $crowsp][$j];
23137								$celladj = $this->_unpackCellBorder($adjc['borderbin']);
23138							} else {
23139								$celladj = & $cells[$i + $crowsp][$j];
23140							}
23141							$cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $celladj['border_details']['L']['w'], $celladj['border_details']['mbw']['TL']);
23142							$cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $celladj['border_details']['R']['w'], $celladj['border_details']['mbw']['TR']);
23143							$cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $celladj['border_details']['mbw']['LT']);
23144							$cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $celladj['border_details']['mbw']['RT']);
23145							unset($celladj);
23146						}
23147						if (($j + $ccolsp) < $numcols && isset($cells[$i][$j + $ccolsp])) { // Has Right adjoining cell
23148							if ($this->packTableData) {
23149								$adjc = $cells[$i][$j + $ccolsp];
23150								$celladj = $this->_unpackCellBorder($adjc['borderbin']);
23151							} else {
23152								$celladj = & $cells[$i][$j + $ccolsp];
23153							}
23154							$cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $celladj['border_details']['T']['w'], $celladj['border_details']['mbw']['LT']);
23155							$cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $celladj['border_details']['B']['w'], $celladj['border_details']['mbw']['LB']);
23156							$cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $celladj['border_details']['mbw']['TL']);
23157							$cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $celladj['border_details']['mbw']['BL']);
23158							unset($celladj);
23159						}
23160
23161						if ($i > 0 && isset($cells[$i - 1][$j]) && (($this->packTableData && $cells[$i - 1][$j]['borderbin']) || $cells[$i - 1][$j]['border'])) { // Has Top adjoining cell
23162							if ($this->packTableData) {
23163								$adjc = $cells[$i - 1][$j];
23164								$celladj = $this->_unpackCellBorder($adjc['borderbin']);
23165							} else {
23166								$celladj = & $cells[$i - 1][$j];
23167							}
23168							$cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['L']['w'], $celladj['border_details']['mbw']['BL']);
23169							$cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $celladj['border_details']['R']['w'], $celladj['border_details']['mbw']['BR']);
23170							$cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $celladj['border_details']['mbw']['LB']);
23171							$cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $celladj['border_details']['mbw']['RB']);
23172
23173							if ($celladj['border_details']['mbw']['BL']) {
23174								$celladj['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['mbw']['BL']);
23175							}
23176							if ($celladj['border_details']['mbw']['BR']) {
23177								$celladj['border_details']['mbw']['BR'] = max($celladj['border_details']['mbw']['BR'], $cbord['border_details']['mbw']['TR']);
23178							}
23179							if ($this->packTableData) {
23180								$cells[$i - 1][$j]['borderbin'] = $this->_packCellBorder($celladj);
23181							}
23182							unset($celladj);
23183						}
23184						if ($j > 0 && isset($cells[$i][$j - 1]) && (($this->packTableData && $cells[$i][$j - 1]['borderbin']) || $cells[$i][$j - 1]['border'])) { // Has Left adjoining cell
23185							if ($this->packTableData) {
23186								$adjc = $cells[$i][$j - 1];
23187								$celladj = $this->_unpackCellBorder($adjc['borderbin']);
23188							} else {
23189								$celladj = & $cells[$i][$j - 1];
23190							}
23191							$cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $celladj['border_details']['T']['w'], $celladj['border_details']['mbw']['RT']);
23192							$cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $celladj['border_details']['B']['w'], $celladj['border_details']['mbw']['RB']);
23193							$cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $celladj['border_details']['mbw']['BR']);
23194							$cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['mbw']['TR']);
23195
23196							if ($celladj['border_details']['mbw']['RT']) {
23197								$celladj['border_details']['mbw']['RT'] = max($celladj['border_details']['mbw']['RT'], $cbord['border_details']['mbw']['LT']);
23198							}
23199							if ($celladj['border_details']['mbw']['RB']) {
23200								$celladj['border_details']['mbw']['RB'] = max($celladj['border_details']['mbw']['RB'], $cbord['border_details']['mbw']['LB']);
23201							}
23202							if ($this->packTableData) {
23203								$cells[$i][$j - 1]['borderbin'] = $this->_packCellBorder($celladj);
23204							}
23205							unset($celladj);
23206						}
23207
23208
23209						// Update maximum cell border width at LRTB edges of table - used for overall table width
23210						if ($j == 0 && $cbord['border_details']['L']['w']) {
23211							$table['max_cell_border_width']['L'] = max($table['max_cell_border_width']['L'], $cbord['border_details']['L']['w']);
23212						}
23213						if (($j == ($numcols - 1) || ($j + $ccolsp) == $numcols ) && $cbord['border_details']['R']['w']) {
23214							$table['max_cell_border_width']['R'] = max($table['max_cell_border_width']['R'], $cbord['border_details']['R']['w']);
23215						}
23216						if ($i == 0 && $cbord['border_details']['T']['w']) {
23217							$table['max_cell_border_width']['T'] = max($table['max_cell_border_width']['T'], $cbord['border_details']['T']['w']);
23218						}
23219						if (($i == ($numrows - 1) || ($i + $crowsp) == $numrows ) && $cbord['border_details']['B']['w']) {
23220							$table['max_cell_border_width']['B'] = max($table['max_cell_border_width']['B'], $cbord['border_details']['B']['w']);
23221						}
23222					}
23223					/* -- END TABLES-ADVANCED-BORDERS -- */
23224
23225					if ($this->packTableData) {
23226						$cell['borderbin'] = $this->_packCellBorder($cbord);
23227					}
23228
23229					unset($cbord);
23230
23231					unset($cell);
23232				}
23233			}
23234		}
23235		unset($cell);
23236	}
23237
23238	// END FIX BORDERS ************************************************************************************
23239
23240	function _reverseTableDir(&$table)
23241	{
23242		$cells = &$table['cells'];
23243		$numcols = $table['nc'];
23244		$numrows = $table['nr'];
23245		for ($i = 0; $i < $numrows; $i++) { // Rows
23246			$row = [];
23247			for ($j = ($numcols - 1); $j >= 0; $j--) { // Columns
23248				if (isset($cells[$i][$j]) && $cells[$i][$j]) {
23249					$cell = &$cells[$i][$j];
23250					$col = $numcols - $j - 1;
23251					if (isset($cell['colspan']) && $cell['colspan'] > 1) {
23252						$col -= ($cell['colspan'] - 1);
23253					}
23254					// Nested content
23255					if (isset($cell['textbuffer'])) {
23256						for ($n = 0; $n < count($cell['textbuffer']); $n++) {
23257							$t = $cell['textbuffer'][$n][0];
23258							if (substr($t, 0, 19) == "\xbb\xa4\xactype=nestedtable") {
23259								$objattr = $this->_getObjAttr($t);
23260								$objattr['col'] = $col;
23261								$cell['textbuffer'][$n][0] = "\xbb\xa4\xactype=nestedtable,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
23262								$this->table[($this->tableLevel + 1)][$objattr['nestedcontent']]['nestedpos'][1] = $col;
23263							}
23264						}
23265					}
23266					$row[$col] = $cells[$i][$j];
23267					unset($cell);
23268				}
23269			}
23270			for ($f = 0; $f < $numcols; $f++) {
23271				if (!isset($row[$f])) {
23272					$row[$f] = 0;
23273				}
23274			}
23275			$table['cells'][$i] = $row;
23276		}
23277	}
23278
23279	function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $splitpg = 0, $rety = 0)
23280	{
23281		$level = $table['level'];
23282		$levelid = $table['levelid'];
23283
23284		$cells = &$table['cells'];
23285		$numcols = $table['nc'];
23286		$numrows = $table['nr'];
23287		$maxbwtop = 0;
23288		if ($this->ColActive && $level == 1) {
23289			$this->breakpoints[$this->CurrCol][] = $this->y;
23290		} // *COLUMNS*
23291
23292		if (!$split || ($startrow == 0 && $splitpg == 0) || $startrow > 0) {
23293			// TABLE TOP MARGIN
23294			if ($table['margin']['T']) {
23295				if (!$this->table_rotate && $level == 1) {
23296					$this->DivLn($table['margin']['T'], $this->blklvl, true, 1);  // collapsible
23297				} else {
23298					$this->y += ($table['margin']['T']);
23299				}
23300			}
23301			// Advance down page by half width of top border
23302			if ($table['borders_separate']) {
23303				if ($startrow > 0 && (!isset($table['is_thead']) || count($table['is_thead']) == 0)) {
23304					$adv = $table['border_spacing_V'] / 2;
23305				} else {
23306					$adv = $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
23307				}
23308			} else {
23309				$adv = $table['max_cell_border_width']['T'] / 2;
23310			}
23311			if (!$this->table_rotate && $level == 1) {
23312				$this->DivLn($adv);
23313			} else {
23314				$this->y += $adv;
23315			}
23316		}
23317
23318		if ($level == 1) {
23319			$this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'];
23320			$x0 = $this->x;
23321			$y0 = $this->y;
23322			$right = $x0 + $this->blk[$this->blklvl]['inner_width'];
23323			$outerfilled = $this->y; // Keep track of how far down the outer DIV bgcolor is painted (NB rowspans)
23324			$this->outerfilled = $this->y;
23325			$this->colsums = [];
23326		} else {
23327			$x0 = $this->x;
23328			$y0 = $this->y;
23329			$right = $x0 + $table['w'];
23330		}
23331
23332		if ($this->table_rotate) {
23333			$temppgwidth = $this->tbrot_maxw;
23334			$this->PageBreakTrigger = $pagetrigger = $y0 + ($this->blk[$this->blklvl]['inner_width']);
23335			if ($level == 1) {
23336				$this->tbrot_y0 = $this->y - $adv - $table['margin']['T'];
23337				$this->tbrot_x0 = $this->x;
23338				$this->tbrot_w = $table['w'];
23339				if ($table['borders_separate']) {
23340					$this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
23341				} else {
23342					$this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['max_cell_border_width']['T'];
23343				}
23344			}
23345		} else {
23346			$this->PageBreakTrigger = $pagetrigger = ($this->h - $this->bMargin);
23347			if ($level == 1) {
23348				$temppgwidth = $this->blk[$this->blklvl]['inner_width'];
23349				if (isset($table['a']) and ( $table['w'] < $this->blk[$this->blklvl]['inner_width'])) {
23350					if ($table['a'] == 'C') {
23351						$x0 += ((($right - $x0) - $table['w']) / 2);
23352					} elseif ($table['a'] == 'R') {
23353						$x0 = $right - $table['w'];
23354					}
23355				}
23356			} else {
23357				$temppgwidth = $table['w'];
23358			}
23359		}
23360		if (!isset($table['overflow'])) {
23361			$table['overflow'] = null;
23362		}
23363		if ($table['overflow'] == 'hidden' && $level == 1 && !$this->table_rotate && !$this->ColActive) {
23364			// Bounding rectangle to clip
23365			$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);
23366			$this->_out($this->tableClipPath);
23367		} else {
23368			$this->tableClipPath = '';
23369		}
23370
23371
23372		if ($table['borders_separate']) {
23373			$indent = $table['margin']['L'] + $table['border_details']['L']['w'] + $table['padding']['L'] + $table['border_spacing_H'] / 2;
23374		} else {
23375			$indent = $table['margin']['L'] + $table['max_cell_border_width']['L'] / 2;
23376		}
23377		$x0 += $indent;
23378
23379		$returny = 0;
23380		$lastCol = 0;
23381		$tableheader = [];
23382		$tablefooter = [];
23383		$tableheaderrowheight = 0;
23384		$tablefooterrowheight = 0;
23385		$footery = 0;
23386
23387		// mPD 3.0 Set the Page & Column where table starts
23388		if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
23389			$tablestartpage = 'EVEN';
23390		} elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD
23391			$tablestartpage = 'ODD';
23392		} else {
23393			$tablestartpage = '';
23394		}
23395		if ($this->ColActive) {
23396			$tablestartcolumn = $this->CurrCol;
23397		} else {
23398			$tablestartcolumn = '';
23399		}
23400
23401		$y = $h = 0;
23402		for ($i = 0; $i < $numrows; $i++) { // Rows
23403			if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i] && $level == 1) {
23404				$tablefooterrowheight += $table['hr'][$i];
23405				$tablefooter[$i][0]['trbackground-images'] = $table['trbackground-images'][$i];
23406				$tablefooter[$i][0]['trgradients'] = $table['trgradients'][$i];
23407				$tablefooter[$i][0]['trbgcolor'] = $table['bgcolor'][$i];
23408				for ($j = $startcol; $j < $numcols; $j++) { // Columns
23409					if (isset($cells[$i][$j]) && $cells[$i][$j]) {
23410						$cell = &$cells[$i][$j];
23411						if ($split) {
23412							if ($table['colPg'][$j] != $splitpg) {
23413								continue;
23414							}
23415							list($x, $w) = $this->_splitTableGetWidth($table, $i, $j);
23416							$js = $j - $startcol;
23417						} else {
23418							list($x, $w) = $this->_tableGetWidth($table, $i, $j);
23419							$js = $j;
23420						}
23421
23422						list($y, $h) = $this->_tableGetHeight($table, $i, $j);
23423						$x += $x0;
23424						$y += $y0;
23425						// Get info of tfoot ==>> table footer
23426						$tablefooter[$i][$js]['x'] = $x;
23427						$tablefooter[$i][$js]['y'] = $y;
23428						$tablefooter[$i][$js]['h'] = $h;
23429						$tablefooter[$i][$js]['w'] = $w;
23430						if (isset($cell['textbuffer'])) {
23431							$tablefooter[$i][$js]['textbuffer'] = $cell['textbuffer'];
23432						} else {
23433							$tablefooter[$i][$js]['textbuffer'] = '';
23434						}
23435						$tablefooter[$i][$js]['a'] = $cell['a'];
23436						$tablefooter[$i][$js]['R'] = $cell['R'];
23437						$tablefooter[$i][$js]['va'] = $cell['va'];
23438						$tablefooter[$i][$js]['mih'] = $cell['mih'];
23439						if (isset($cell['gradient'])) {
23440							$tablefooter[$i][$js]['gradient'] = $cell['gradient']; // *BACKGROUNDS*
23441						}
23442						if (isset($cell['background-image'])) {
23443							$tablefooter[$i][$js]['background-image'] = $cell['background-image']; // *BACKGROUNDS*
23444						}
23445
23446						// CELL FILL BGCOLOR
23447						if (!$this->simpleTables) {
23448							if ($this->packTableData) {
23449								$c = $this->_unpackCellBorder($cell['borderbin']);
23450								$tablefooter[$i][$js]['border'] = $c['border'];
23451								$tablefooter[$i][$js]['border_details'] = $c['border_details'];
23452							} else {
23453								$tablefooter[$i][$js]['border'] = $cell['border'];
23454								$tablefooter[$i][$js]['border_details'] = $cell['border_details'];
23455							}
23456						} elseif ($this->simpleTables) {
23457							$tablefooter[$i][$js]['border'] = $table['simple']['border'];
23458							$tablefooter[$i][$js]['border_details'] = $table['simple']['border_details'];
23459						}
23460						$tablefooter[$i][$js]['bgcolor'] = $cell['bgcolor'];
23461						$tablefooter[$i][$js]['padding'] = $cell['padding'];
23462						if (isset($cell['rowspan'])) {
23463							$tablefooter[$i][$js]['rowspan'] = $cell['rowspan'];
23464						}
23465						if (isset($cell['colspan'])) {
23466							$tablefooter[$i][$js]['colspan'] = $cell['colspan'];
23467						}
23468						if (isset($cell['direction'])) {
23469							$tablefooter[$i][$js]['direction'] = $cell['direction'];
23470						}
23471						if (isset($cell['cellLineHeight'])) {
23472							$tablefooter[$i][$js]['cellLineHeight'] = $cell['cellLineHeight'];
23473						}
23474						if (isset($cell['cellLineStackingStrategy'])) {
23475							$tablefooter[$i][$js]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];
23476						}
23477						if (isset($cell['cellLineStackingShift'])) {
23478							$tablefooter[$i][$js]['cellLineStackingShift'] = $cell['cellLineStackingShift'];
23479						}
23480					}
23481				}
23482			}
23483		}
23484
23485		if ($level == 1) {
23486			$this->_out('___TABLE___BACKGROUNDS' . $this->uniqstr);
23487		}
23488		$tableheaderadj = 0;
23489		$tablefooteradj = 0;
23490
23491		$tablestartpageno = $this->page;
23492
23493		// Draw Table Contents and Borders
23494		for ($i = 0; $i < $numrows; $i++) { // Rows
23495			if ($split && $startrow > 0) {
23496				$thnr = (isset($table['is_thead']) ? count($table['is_thead']) : 0);
23497				if ($i >= $thnr && $i < $startrow) {
23498					continue;
23499				}
23500				if ($i == $startrow) {
23501					$returny = $rety - $tableheaderrowheight;
23502				}
23503			}
23504
23505			// Get Maximum row/cell height in row - including rowspan>1 + 1 overlapping
23506			$maxrowheight = $this->_tableGetMaxRowHeight($table, $i);
23507
23508			$skippage = false;
23509			$newpagestarted = false;
23510			for ($j = $startcol; $j < $numcols; $j++) { // Columns
23511				if ($split) {
23512					if ($table['colPg'][$j] > $splitpg) {
23513						break;
23514					}
23515					$lastCol = $j;
23516				}
23517				if (isset($cells[$i][$j]) && $cells[$i][$j]) {
23518					$cell = &$cells[$i][$j];
23519					if ($split) {
23520						$lastCol = $j + (isset($cell['colspan']) ? ($cell['colspan'] - 1) : 0);
23521						list($x, $w) = $this->_splitTableGetWidth($table, $i, $j);
23522					} else {
23523						list($x, $w) = $this->_tableGetWidth($table, $i, $j);
23524					}
23525
23526					list($y, $h) = $this->_tableGetHeight($table, $i, $j);
23527					$x += $x0;
23528					$y += $y0;
23529					$y -= $returny;
23530
23531					if ($table['borders_separate']) {
23532						if (!empty($tablefooter) || $i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) {
23533							$extra = $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
23534							// $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V']/2;
23535						} else {
23536							$extra = $table['border_spacing_V'] / 2;
23537						}
23538					} else {
23539						$extra = $table['max_cell_border_width']['B'] / 2;
23540					}
23541
23542					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) {
23543						if (!$skippage) {
23544							$finalSpread = true;
23545							$firstSpread = true;
23546							if ($split) {
23547								for ($t = $startcol; $t < $numcols; $t++) {
23548									// Are there more columns to print on a next page?
23549									if ($table['colPg'][$t] > $splitpg) {
23550										$finalSpread = false;
23551										break;
23552									}
23553								}
23554								if ($startcol > 0) {
23555									$firstSpread = false;
23556								}
23557							}
23558
23559							if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {
23560								$this->y = $y;
23561								$ya = $this->y;
23562								$this->TableHeaderFooter($tablefooter, $tablestartpage, $tablestartcolumn, 'F', $level, $firstSpread, $finalSpread);
23563								if ($this->table_rotate) {
23564									$this->tbrot_h += $this->y - $ya;
23565								}
23566								$tablefooteradj = $this->y - $ya;
23567							}
23568							$y -= $y0;
23569							$returny += $y;
23570
23571							$oldcolumn = $this->CurrCol;
23572							if ($this->AcceptPageBreak()) {
23573								$newpagestarted = true;
23574								$this->y = $y + $y0;
23575
23576								// Move down to account for border-spacing or
23577								// extra half border width in case page breaks in middle
23578								if ($i > 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {
23579									if ($table['borders_separate']) {
23580										$adv = $table['border_spacing_V'] / 2;
23581										// If table footer
23582										if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {
23583											$adv += ($table['padding']['B'] + $table['border_details']['B']['w']);
23584										}
23585									} else {
23586										$maxbwtop = 0;
23587										$maxbwbottom = 0;
23588										if (!$this->simpleTables) {
23589											if (!empty($tablefooter)) {
23590												$maxbwbottom = $table['max_cell_border_width']['B'];
23591											} else {
23592												$brow = $i - 1;
23593												for ($ctj = 0; $ctj < $numcols; $ctj++) {
23594													if (isset($cells[$brow][$ctj]) && $cells[$brow][$ctj]) {
23595														if ($this->packTableData) {
23596															list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$brow][$ctj]['borderbin']);
23597														} else {
23598															$bb = $cells[$brow][$ctj]['border_details']['B']['w'];
23599														}
23600														$maxbwbottom = max($maxbwbottom, $bb);
23601													}
23602												}
23603											}
23604											if (!empty($tableheader)) {
23605												$maxbwtop = $table['max_cell_border_width']['T'];
23606											} else {
23607												$trow = $i - 1;
23608												for ($ctj = 0; $ctj < $numcols; $ctj++) {
23609													if (isset($cells[$trow][$ctj]) && $cells[$trow][$ctj]) {
23610														if ($this->packTableData) {
23611															list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$trow][$ctj]['borderbin']);
23612														} else {
23613															$bt = $cells[$trow][$ctj]['border_details']['T']['w'];
23614														}
23615														$maxbwtop = max($maxbwtop, $bt);
23616													}
23617												}
23618											}
23619										} elseif ($this->simpleTables) {
23620											$maxbwtop = $table['simple']['border_details']['T']['w'];
23621											$maxbwbottom = $table['simple']['border_details']['B']['w'];
23622										}
23623										$adv = $maxbwbottom / 2;
23624									}
23625									$this->y += $adv;
23626								}
23627
23628								// Rotated table split over pages - needs this->y for borders/backgrounds
23629								if ($i > 0 && $this->table_rotate && $level == 1) {
23630									// 		$this->y = $y0 + $this->tbrot_w;
23631								}
23632
23633								if ($this->tableClipPath) {
23634									$this->_out("Q");
23635								}
23636
23637								$bx = $x0;
23638								$by = $y0;
23639
23640								if ($table['borders_separate']) {
23641									$bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);
23642									if ($tablestartpageno != $this->page) { // IF already broken across a previous pagebreak
23643										$by += $table['max_cell_border_width']['T'] / 2;
23644										if (empty($tableheader)) {
23645											$by -= ($table['border_spacing_V'] / 2);
23646										}
23647									} else {
23648										$by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);
23649									}
23650								} elseif ($tablestartpageno != $this->page && !empty($tableheader)) {
23651									$by += $maxbwtop / 2;
23652								}
23653
23654								$by -= $tableheaderadj;
23655								$bh = $this->y - $by + $tablefooteradj;
23656								if (!$table['borders_separate']) {
23657									$bh -= $adv;
23658								}
23659								if ($split) {
23660									$bw = 0;
23661									for ($t = $startcol; $t < $numcols; $t++) {
23662										if ($table['colPg'][$t] == $splitpg) {
23663											$bw += $table['wc'][$t];
23664										}
23665										if ($table['colPg'][$t] > $splitpg) {
23666											break;
23667										}
23668									}
23669									if ($table['borders_separate']) {
23670										if ($firstSpread) {
23671											$bw += $table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'];
23672										} else {
23673											$bx += ($table['padding']['L'] + $table['border_details']['L']['w']);
23674											$bw += $table['border_spacing_H'];
23675										}
23676										if ($finalSpread) {
23677											$bw += $table['padding']['R'] + $table['border_details']['R']['w'] / 2 + $table['border_spacing_H'];
23678										}
23679									}
23680								} else {
23681									$bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
23682								}
23683
23684								if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tablefooter) && $i > 0 && $table['border_details']['B']['w']) {
23685									$prevDrawColor = $this->DrawColor;
23686									$lw = $this->LineWidth;
23687									$this->SetLineWidth($this->splitTableBorderWidth);
23688									$this->SetDColor($table['border_details']['B']['c']);
23689									$this->SetLineJoin(0);
23690									$this->SetLineCap(0);
23691									$blx = $bx;
23692									$blw = $bw;
23693									if (!$table['borders_separate']) {
23694										$blx -= ($table['max_cell_border_width']['L'] / 2);
23695										$blw += ($table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2);
23696									}
23697									$this->Line($blx, $this->y + ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y + ($this->splitTableBorderWidth / 2));
23698									$this->DrawColor = $prevDrawColor;
23699									$this->_out($this->DrawColor);
23700									$this->SetLineWidth($lw);
23701									$this->SetLineJoin(2);
23702									$this->SetLineCap(2);
23703								}
23704
23705								if (!$this->ColActive && ($i > 0 || $j > 0)) {
23706									if (isset($table['bgcolor'][-1])) {
23707										$color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings);
23708										if ($color) {
23709											if (!$table['borders_separate']) {
23710												$bh -= $table['max_cell_border_width']['B'] / 2;
23711											}
23712											$this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color];
23713										}
23714									}
23715
23716									/* -- BACKGROUNDS -- */
23717									if (isset($table['gradient'])) {
23718										$g = $this->gradient->parseBackgroundGradient($table['gradient']);
23719										if ($g) {
23720											$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' => ''];
23721										}
23722									}
23723
23724									if (isset($table['background-image'])) {
23725										if ($table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {
23726											$g = $this->gradient->parseMozGradient($table['background-image']['gradient']);
23727											if ($g) {
23728												$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' => ''];
23729											}
23730										} else {
23731											$image_id = $table['background-image']['image_id'];
23732											$orig_w = $table['background-image']['orig_w'];
23733											$orig_h = $table['background-image']['orig_h'];
23734											$x_pos = $table['background-image']['x_pos'];
23735											$y_pos = $table['background-image']['y_pos'];
23736											$x_repeat = $table['background-image']['x_repeat'];
23737											$y_repeat = $table['background-image']['y_repeat'];
23738											$resize = $table['background-image']['resize'];
23739											$opacity = $table['background-image']['opacity'];
23740											$itype = $table['background-image']['itype'];
23741											$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];
23742										}
23743									}
23744									/* -- END BACKGROUNDS -- */
23745								}
23746
23747								// $this->AcceptPageBreak() has moved tablebuffer to $this->pages content
23748								if ($this->tableBackgrounds) {
23749									$s = $this->PrintTableBackgrounds();
23750									if ($this->bufferoutput) {
23751										$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer);
23752										$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer);
23753									} else {
23754										$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
23755										$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]);
23756									}
23757									$this->tableBackgrounds = [];
23758								}
23759
23760								if ($split) {
23761									if ($i == 0 && $j == 0) {
23762										$y0 = -1;
23763									} elseif ($finalSpread) {
23764										$splitpg = 0;
23765										$startcol = 0;
23766										$startrow = $i;
23767									} else {
23768										$splitpg++;
23769										$startcol = $t;
23770										$returny -= $y;
23771									}
23772									return [false, $startrow, $startcol, $splitpg, $returny, $y0];
23773								}
23774
23775								$this->AddPage($this->CurOrientation);
23776
23777								$this->_out('___TABLE___BACKGROUNDS' . $this->uniqstr);
23778
23779
23780								if ($this->tableClipPath) {
23781									$this->_out($this->tableClipPath);
23782								}
23783
23784								// Added to correct for OddEven Margins
23785								$x = $x + $this->MarginCorrection;
23786								$x0 = $x0 + $this->MarginCorrection;
23787
23788								if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tableheader) && $i > 0 && $table['border_details']['T']['w']) {
23789									$prevDrawColor = $this->DrawColor;
23790									$lw = $this->LineWidth;
23791									$this->SetLineWidth($this->splitTableBorderWidth);
23792									$this->SetDColor($table['border_details']['T']['c']);
23793									$this->SetLineJoin(0);
23794									$this->SetLineCap(0);
23795									$blx += $this->MarginCorrection;
23796									$this->Line($blx, $this->y - ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y - ($this->splitTableBorderWidth / 2));
23797									$this->DrawColor = $prevDrawColor;
23798									$this->_out($this->DrawColor);
23799									$this->SetLineWidth($lw);
23800									$this->SetLineJoin(2);
23801									$this->SetLineCap(2);
23802								}
23803
23804								// Move down to account for half of top border-spacing or
23805								// extra half border width in case page was broken in middle
23806								if ($i > 0 && !$this->table_rotate && $level == 1 && $table['headernrows'] == 0) {
23807									if ($table['borders_separate']) {
23808										$adv = $table['border_spacing_V'] / 2;
23809									} else {
23810										$maxbwtop = 0;
23811										for ($ctj = 0; $ctj < $numcols; $ctj++) {
23812											if (isset($cells[$i][$ctj]) && $cells[$i][$ctj]) {
23813												if (!$this->simpleTables) {
23814													if ($this->packTableData) {
23815														list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$i][$ctj]['borderbin']);
23816													} else {
23817														$bt = $cells[$i][$ctj]['border_details']['T']['w'];
23818													}
23819													$maxbwtop = max($maxbwtop, $bt);
23820												} elseif ($this->simpleTables) {
23821													$maxbwtop = max($maxbwtop, $table['simple']['border_details']['T']['w']);
23822												}
23823											}
23824										}
23825										$adv = $maxbwtop / 2;
23826									}
23827									$this->y += $adv;
23828								}
23829
23830
23831								if ($this->table_rotate) {
23832									$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'];
23833									if ($table['borders_separate']) {
23834										$this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
23835									} else {
23836										$this->tbrot_h = $table['margin']['T'] + $table['max_cell_border_width']['T'];
23837									}
23838									$this->tbrot_y0 = $this->y;
23839									$pagetrigger = $y0 - $tableheaderadj + ($this->blk[$this->blklvl]['inner_width']);
23840								} else {
23841									$pagetrigger = $this->PageBreakTrigger;
23842								}
23843
23844								if ($this->kwt_saved && $level == 1) {
23845									$this->kwt_moved = true;
23846								}
23847
23848
23849								if (!empty($tableheader)) {
23850									$ya = $this->y;
23851									$this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);
23852									if ($this->table_rotate) {
23853										$this->tbrot_h = $this->y - $ya;
23854									}
23855									$tableheaderadj = $this->y - $ya;
23856								} elseif ($i == 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {
23857									// Advance down page
23858									if ($table['borders_separate']) {
23859										$adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];
23860									} else {
23861										$adv = $table['max_cell_border_width']['T'] / 2;
23862									}
23863									if ($adv) {
23864										if ($this->table_rotate) {
23865											$this->y += ($adv);
23866										} else {
23867											$this->DivLn($adv, $this->blklvl, true);
23868										}
23869									}
23870								}
23871
23872								$outerfilled = 0;
23873								$y = $y0 = $this->y;
23874							}
23875
23876							/* -- COLUMNS -- */
23877							// COLS
23878							// COLUMN CHANGE
23879							if ($this->CurrCol != $oldcolumn) {
23880								// Added to correct for Columns
23881								$x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
23882								$x0 += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
23883								if ($this->CurrCol == 0) {  // just added a page - possibly with tableheader
23884									$y0 = $this->y;  // this->y0 is global used by Columns - $y0 is internal to tablewrite
23885								} else {
23886									$y0 = $this->y0;  // this->y0 is global used by Columns - $y0 is internal to tablewrite
23887								}
23888								$y = $y0;
23889								$outerfilled = 0;
23890								if ($this->CurrCol != 0 && ($this->keepColumns && $this->ColActive) && !empty($tableheader) && $i > 0) {
23891									$this->x = $x;
23892									$this->y = $y;
23893									$this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);
23894									$y0 = $y = $this->y;
23895								}
23896							}
23897							/* -- END COLUMNS -- */
23898						}
23899						$skippage = true;
23900					}
23901
23902					$this->x = $x;
23903					$this->y = $y;
23904
23905					if ($this->kwt_saved && $level == 1) {
23906						$this->printkwtbuffer();
23907						$x0 = $x = $this->x;
23908						$y0 = $y = $this->y;
23909						$this->kwt_moved = false;
23910						$this->kwt_saved = false;
23911					}
23912
23913
23914					// Set the Page & Column where table actually starts
23915					if ($i == 0 && $j == 0 && $level == 1) {
23916						if (($this->mirrorMargins) && (($this->page) % 2 == 0)) {    // EVEN
23917							$tablestartpage = 'EVEN';
23918						} elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) {    // ODD
23919							$tablestartpage = 'ODD';
23920						} else {
23921							$tablestartpage = '';
23922						}
23923						$tablestartpageno = $this->page;
23924						if ($this->ColActive) {
23925							$tablestartcolumn = $this->CurrCol;
23926						} // *COLUMNS*
23927					}
23928
23929					// ALIGN
23930					$align = $cell['a'];
23931
23932					/* -- COLUMNS -- */
23933					// If outside columns, this is done in PaintDivBB
23934					if ($this->ColActive) {
23935						// OUTER FILL BGCOLOR of DIVS
23936						if ($this->blklvl > 0 && ($j == 0) && !$this->table_rotate && $level == 1) {
23937							$firstblockfill = $this->GetFirstBlockFill();
23938							if ($firstblockfill && $this->blklvl >= $firstblockfill) {
23939								$divh = $maxrowheight;
23940								// Last row
23941								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)))) {
23942									if ($table['borders_separate']) {
23943										$adv = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
23944									} else {
23945										$adv = $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;
23946									}
23947									$divh += $adv;  // last row: fill bottom half of bottom border (y advanced at end)
23948								}
23949
23950								if (($this->y + $divh) > $outerfilled) { // if not already painted by previous rowspan
23951									$bak_x = $this->x;
23952									$bak_y = $this->y;
23953									if ($outerfilled > $this->y) {
23954										$divh = ($this->y + $divh) - $outerfilled;
23955										$this->y = $outerfilled;
23956									}
23957
23958									$this->DivLn($divh, -3, false);
23959									$outerfilled = $this->y + $divh;
23960									// Reset current block fill
23961									$bcor = $this->blk[$this->blklvl]['bgcolorarray'];
23962									if ($bcor) {
23963										$this->SetFColor($bcor);
23964									}
23965									$this->x = $bak_x;
23966									$this->y = $bak_y;
23967								}
23968							}
23969						}
23970					}
23971
23972					// TABLE BACKGROUND FILL BGCOLOR - for cellSpacing
23973					if ($this->ColActive) {
23974						if ($table['borders_separate']) {
23975							$fill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;
23976							if ($fill) {
23977								$color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
23978								if ($color) {
23979									$xadj = ($table['border_spacing_H'] / 2);
23980									$yadj = ($table['border_spacing_V'] / 2);
23981									$wadj = $table['border_spacing_H'];
23982									$hadj = $table['border_spacing_V'];
23983									if ($i == 0) {  // Top
23984										$yadj += $table['padding']['T'] + $table['border_details']['T']['w'];
23985										$hadj += $table['padding']['T'] + $table['border_details']['T']['w'];
23986									}
23987									if ($j == 0) {  // Left
23988										$xadj += $table['padding']['L'] + $table['border_details']['L']['w'];
23989										$wadj += $table['padding']['L'] + $table['border_details']['L']['w'];
23990									}
23991									if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) { // Bottom
23992										$hadj += $table['padding']['B'] + $table['border_details']['B']['w'];
23993									}
23994									if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols) || (!isset($cell['colspan']) && ($j + 1) == $numcols)) { // Right
23995										$wadj += $table['padding']['R'] + $table['border_details']['R']['w'];
23996									}
23997									$this->SetFColor($color);
23998									$this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');
23999								}
24000							}
24001						}
24002					}
24003					/* -- END COLUMNS -- */
24004
24005					if ($table['empty_cells'] != 'hide' || !empty($cell['textbuffer']) || (isset($cell['nestedcontent']) && $cell['nestedcontent']) || !$table['borders_separate']) {
24006						$paintcell = true;
24007					} else {
24008						$paintcell = false;
24009					}
24010
24011					// Set Borders
24012					$bord = 0;
24013					$bord_det = [];
24014
24015					if (!$this->simpleTables) {
24016						if ($this->packTableData) {
24017							$c = $this->_unpackCellBorder($cell['borderbin']);
24018							$bord = $c['border'];
24019							$bord_det = $c['border_details'];
24020						} else {
24021							$bord = $cell['border'];
24022							$bord_det = $cell['border_details'];
24023						}
24024					} elseif ($this->simpleTables) {
24025						$bord = $table['simple']['border'];
24026						$bord_det = $table['simple']['border_details'];
24027					}
24028
24029					// TABLE ROW OR CELL FILL BGCOLOR
24030					$fill = 0;
24031					if (isset($cell['bgcolor']) && $cell['bgcolor'] && $cell['bgcolor'] != 'transparent') {
24032						$fill = $cell['bgcolor'];
24033						$leveladj = 6;
24034					} elseif (isset($table['bgcolor'][$i]) && $table['bgcolor'][$i] && $table['bgcolor'][$i] != 'transparent') { // Row color
24035						$fill = $table['bgcolor'][$i];
24036						$leveladj = 3;
24037					}
24038					if ($fill && $paintcell) {
24039						$color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);
24040						if ($color) {
24041							if ($table['borders_separate']) {
24042								if ($this->ColActive) {
24043									$this->SetFColor($color);
24044									$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');
24045								} else {
24046									$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];
24047								}
24048							} else {
24049								if ($this->ColActive) {
24050									$this->SetFColor($color);
24051									$this->Rect($x, $y, $w, $h, 'F');
24052								} else {
24053									$this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color];
24054								}
24055							}
24056						}
24057					}
24058
24059					/* -- BACKGROUNDS -- */
24060					if (isset($cell['gradient']) && $cell['gradient'] && $paintcell) {
24061						$g = $this->gradient->parseBackgroundGradient($cell['gradient']);
24062						if ($g) {
24063							if ($table['borders_separate']) {
24064								$px = $x + ($table['border_spacing_H'] / 2);
24065								$py = $y + ($table['border_spacing_V'] / 2);
24066								$pw = $w - $table['border_spacing_H'];
24067								$ph = $h - $table['border_spacing_V'];
24068							} else {
24069								$px = $x;
24070								$py = $y;
24071								$pw = $w;
24072								$ph = $h;
24073							}
24074							if ($this->ColActive) {
24075								$this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
24076							} else {
24077								$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' => ''];
24078							}
24079						}
24080					}
24081
24082					if (isset($cell['background-image']) && $paintcell) {
24083						if (isset($cell['background-image']['gradient']) && $cell['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $cell['background-image']['gradient'])) {
24084							$g = $this->gradient->parseMozGradient($cell['background-image']['gradient']);
24085							if ($g) {
24086								if ($table['borders_separate']) {
24087									$px = $x + ($table['border_spacing_H'] / 2);
24088									$py = $y + ($table['border_spacing_V'] / 2);
24089									$pw = $w - $table['border_spacing_H'];
24090									$ph = $h - $table['border_spacing_V'];
24091								} else {
24092									$px = $x;
24093									$py = $y;
24094									$pw = $w;
24095									$ph = $h;
24096								}
24097								if ($this->ColActive) {
24098									$this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
24099								} else {
24100									$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' => ''];
24101								}
24102							}
24103						} elseif (isset($cell['background-image']['image_id']) && $cell['background-image']['image_id']) { // Background pattern
24104							$n = count($this->patterns) + 1;
24105							if ($table['borders_separate']) {
24106								$px = $x + ($table['border_spacing_H'] / 2);
24107								$py = $y + ($table['border_spacing_V'] / 2);
24108								$pw = $w - $table['border_spacing_H'];
24109								$ph = $h - $table['border_spacing_V'];
24110							} else {
24111								$px = $x;
24112								$py = $y;
24113								$pw = $w;
24114								$ph = $h;
24115							}
24116							if ($this->ColActive) {
24117								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']);
24118								$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];
24119								if ($cell['background-image']['opacity'] > 0 && $cell['background-image']['opacity'] < 1) {
24120									$opac = $this->SetAlpha($cell['background-image']['opacity'], 'Normal', true);
24121								} else {
24122									$opac = '';
24123								}
24124								$this->_out(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));
24125							} else {
24126								$image_id = $cell['background-image']['image_id'];
24127								$orig_w = $cell['background-image']['orig_w'];
24128								$orig_h = $cell['background-image']['orig_h'];
24129								$x_pos = $cell['background-image']['x_pos'];
24130								$y_pos = $cell['background-image']['y_pos'];
24131								$x_repeat = $cell['background-image']['x_repeat'];
24132								$y_repeat = $cell['background-image']['y_repeat'];
24133								$resize = $cell['background-image']['resize'];
24134								$opacity = $cell['background-image']['opacity'];
24135								$itype = $cell['background-image']['itype'];
24136								$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];
24137							}
24138						}
24139					}
24140					/* -- END BACKGROUNDS -- */
24141
24142					if (isset($cell['colspan']) && $cell['colspan'] > 1) {
24143						$ccolsp = $cell['colspan'];
24144					} else {
24145						$ccolsp = 1;
24146					}
24147					if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {
24148						$crowsp = $cell['rowspan'];
24149					} else {
24150						$crowsp = 1;
24151					}
24152
24153
24154					// but still need to do this for repeated headers...
24155					if (!$table['borders_separate'] && $this->tabletheadjustfinished && !$this->simpleTables) {
24156						if (isset($table['topntail']) && $table['topntail']) {
24157							$bord_det['T'] = $this->border_details($table['topntail']);
24158							$bord_det['T']['w'] /= $this->shrin_k;
24159							$this->setBorder($bord, Border::TOP);
24160						}
24161						if (isset($table['thead-underline']) && $table['thead-underline']) {
24162							$bord_det['T'] = $this->border_details($table['thead-underline']);
24163							$bord_det['T']['w'] /= $this->shrin_k;
24164							$this->setBorder($bord, Border::TOP);
24165						}
24166					}
24167
24168
24169					// Get info of first row ==>> table header
24170					// Use > 1 row if THEAD
24171					if (isset($table['is_thead'][$i]) && $table['is_thead'][$i] && $level == 1) {
24172						if ($j == 0) {
24173							$tableheaderrowheight += $table['hr'][$i];
24174						}
24175						$tableheader[$i][0]['trbackground-images'] = (isset($table['trbackground-images'][$i]) ? $table['trbackground-images'][$i] : null);
24176						$tableheader[$i][0]['trgradients'] = (isset($table['trgradients'][$i]) ? $table['trgradients'][$i] : null);
24177						$tableheader[$i][0]['trbgcolor'] = (isset($table['bgcolor'][$i]) ? $table['bgcolor'][$i] : null);
24178						$tableheader[$i][$j]['x'] = $x;
24179						$tableheader[$i][$j]['y'] = $y;
24180						$tableheader[$i][$j]['h'] = $h;
24181						$tableheader[$i][$j]['w'] = $w;
24182						if (isset($cell['textbuffer'])) {
24183							$tableheader[$i][$j]['textbuffer'] = $cell['textbuffer'];
24184						} else {
24185							$tableheader[$i][$j]['textbuffer'] = '';
24186						}
24187						$tableheader[$i][$j]['a'] = $cell['a'];
24188						$tableheader[$i][$j]['R'] = $cell['R'];
24189
24190						$tableheader[$i][$j]['va'] = $cell['va'];
24191						$tableheader[$i][$j]['mih'] = $cell['mih'];
24192						$tableheader[$i][$j]['gradient'] = (isset($cell['gradient']) ? $cell['gradient'] : null); // *BACKGROUNDS*
24193						$tableheader[$i][$j]['background-image'] = (isset($cell['background-image']) ? $cell['background-image'] : null); // *BACKGROUNDS*
24194						$tableheader[$i][$j]['rowspan'] = (isset($cell['rowspan']) ? $cell['rowspan'] : null);
24195						$tableheader[$i][$j]['colspan'] = (isset($cell['colspan']) ? $cell['colspan'] : null);
24196						$tableheader[$i][$j]['bgcolor'] = $cell['bgcolor'];
24197
24198						if (!$this->simpleTables) {
24199							$tableheader[$i][$j]['border'] = $bord;
24200							$tableheader[$i][$j]['border_details'] = $bord_det;
24201						} elseif ($this->simpleTables) {
24202							$tableheader[$i][$j]['border'] = $table['simple']['border'];
24203							$tableheader[$i][$j]['border_details'] = $table['simple']['border_details'];
24204						}
24205						$tableheader[$i][$j]['padding'] = $cell['padding'];
24206						if (isset($cell['direction'])) {
24207							$tableheader[$i][$j]['direction'] = $cell['direction'];
24208						}
24209						if (isset($cell['cellLineHeight'])) {
24210							$tableheader[$i][$j]['cellLineHeight'] = $cell['cellLineHeight'];
24211						}
24212						if (isset($cell['cellLineStackingStrategy'])) {
24213							$tableheader[$i][$j]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];
24214						}
24215						if (isset($cell['cellLineStackingShift'])) {
24216							$tableheader[$i][$j]['cellLineStackingShift'] = $cell['cellLineStackingShift'];
24217						}
24218					}
24219
24220					// CELL BORDER
24221					if ($bord) {
24222						if ($table['borders_separate'] && $paintcell) {
24223							$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']);
24224						} elseif (!$table['borders_separate']) {
24225							$this->_tableRect($x, $y, $w, $h, $bord, $bord_det, true, $table['borders_separate']);  // true causes buffer
24226						}
24227					}
24228
24229					// VERTICAL ALIGN
24230					if ($cell['R'] && intval($cell['R']) > 0 && intval($cell['R']) < 90 && isset($cell['va']) && $cell['va'] != 'B') {
24231						$cell['va'] = 'B';
24232					}
24233					if (!isset($cell['va']) || $cell['va'] == 'M') {
24234						$this->y += ($h - $cell['mih']) / 2;
24235					} elseif (isset($cell['va']) && $cell['va'] == 'B') {
24236						$this->y += $h - $cell['mih'];
24237					}
24238
24239					// NESTED CONTENT
24240					// TEXT (and nested tables)
24241
24242					$this->divwidth = $w;
24243					if (!empty($cell['textbuffer'])) {
24244						$this->cellTextAlign = $align;
24245						$this->cellLineHeight = $cell['cellLineHeight'];
24246						$this->cellLineStackingStrategy = $cell['cellLineStackingStrategy'];
24247						$this->cellLineStackingShift = $cell['cellLineStackingShift'];
24248						if ($level == 1) {
24249							if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {
24250								if (preg_match('/{colsum([0-9]*)[_]*}/', $cell['textbuffer'][0][0], $m)) {
24251									$rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$j]);
24252									$cell['textbuffer'][0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $cell['textbuffer'][0][0]);
24253								}
24254							} elseif (!isset($table['is_thead'][$i])) {
24255								if (isset($this->colsums[$j])) {
24256									$this->colsums[$j] += $this->toFloat($cell['textbuffer'][0][0]);
24257								} else {
24258									$this->colsums[$j] = $this->toFloat($cell['textbuffer'][0][0]);
24259								}
24260							}
24261						}
24262						$opy = $this->y;
24263						// mPDF ITERATION
24264						if ($this->iterationCounter) {
24265							foreach ($cell['textbuffer'] as $k => $t) {
24266								if (preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {
24267									$vname = '__' . $m[1] . '_';
24268									if (!isset($this->$vname)) {
24269										$this->$vname = 1;
24270									} else {
24271										$this->$vname++;
24272									}
24273									$cell['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $cell['textbuffer'][$k][0]);
24274								}
24275							}
24276						}
24277
24278
24279						if ($cell['R']) {
24280							$cellPtSize = $cell['textbuffer'][0][11] / $this->shrin_k;
24281							if (!$cellPtSize) {
24282								$cellPtSize = $this->default_font_size;
24283							}
24284							$cellFontHeight = ($cellPtSize / Mpdf::SCALE);
24285							$opx = $this->x;
24286							$angle = intval($cell['R']);
24287							// Only allow 45 to 89 degrees (when bottom-aligned) or exactly 90 or -90
24288							if ($angle > 90) {
24289								$angle = 90;
24290							} elseif ($angle > 0 && $angle < 45) {
24291								$angle = 45;
24292							} elseif ($angle < 0) {
24293								$angle = -90;
24294							}
24295							$offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);
24296							if (isset($cell['a']) && $cell['a'] == 'R') {
24297								$this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($cell['padding']['R'] + ($table['border_spacing_H'] / 2));
24298							} elseif (!isset($cell['a']) || $cell['a'] == 'C') {
24299								$this->x += ($w / 2) + ($offset);
24300							} else {
24301								$this->x += ($offset) + ($cellFontHeight / 3) + ($cell['padding']['L'] + ($table['border_spacing_H'] / 2));
24302							}
24303							$str = '';
24304							foreach ($cell['textbuffer'] as $t) {
24305								$str .= $t[0] . ' ';
24306							}
24307							$str = rtrim($str);
24308							if (!isset($cell['va']) || $cell['va'] == 'M') {
24309								$this->y -= ($h - $cell['mih']) / 2; // Undo what was added earlier VERTICAL ALIGN
24310								if ($angle > 0) {
24311									$this->y += (($h - $cell['mih']) / 2) + $cell['padding']['T'] + ($cell['mih'] - ($cell['padding']['T'] + $cell['padding']['B']));
24312								} elseif ($angle < 0) {
24313									$this->y += (($h - $cell['mih']) / 2) + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
24314								}
24315							} elseif (isset($cell['va']) && $cell['va'] == 'B') {
24316								$this->y -= $h - $cell['mih']; // Undo what was added earlier VERTICAL ALIGN
24317								if ($angle > 0) {
24318									$this->y += $h - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));
24319								} elseif ($angle < 0) {
24320									$this->y += $h - $cell['mih'] + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
24321								}
24322							} elseif (isset($cell['va']) && $cell['va'] == 'T') {
24323								if ($angle > 0) {
24324									$this->y += $cell['mih'] - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));
24325								} elseif ($angle < 0) {
24326									$this->y += ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
24327								}
24328							}
24329							$this->Rotate($angle, $this->x, $this->y);
24330							$s_fs = $this->FontSizePt;
24331							$s_f = $this->FontFamily;
24332							$s_st = $this->FontStyle;
24333							if (!empty($cell['textbuffer'][0][3])) { // Font Color
24334								$cor = $cell['textbuffer'][0][3];
24335								$this->SetTColor($cor);
24336							}
24337							$this->SetFont($cell['textbuffer'][0][4], $cell['textbuffer'][0][2], $cellPtSize, true, true);
24338
24339							$this->magic_reverse_dir($str, $this->directionality, $cell['textbuffer'][0][18]);
24340							$this->Text($this->x, $this->y, $str, $cell['textbuffer'][0][18], $cell['textbuffer'][0][8]); // textvar
24341							$this->Rotate(0);
24342							$this->SetFont($s_f, $s_st, $s_fs, true, true);
24343							$this->SetTColor(0);
24344							$this->x = $opx;
24345						} else {
24346							if (!$this->simpleTables) {
24347								if ($bord_det) {
24348									$btlw = $bord_det['L']['w'];
24349									$btrw = $bord_det['R']['w'];
24350									$bttw = $bord_det['T']['w'];
24351								} else {
24352									$btlw = 0;
24353									$btrw = 0;
24354									$bttw = 0;
24355								}
24356								if ($table['borders_separate']) {
24357									$xadj = $btlw + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);
24358									$wadj = $btlw + $btrw + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];
24359									$yadj = $bttw + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);
24360								} else {
24361									$xadj = $btlw / 2 + $cell['padding']['L'];
24362									$wadj = ($btlw + $btrw) / 2 + $cell['padding']['L'] + $cell['padding']['R'];
24363									$yadj = $bttw / 2 + $cell['padding']['T'];
24364								}
24365							} elseif ($this->simpleTables) {
24366								if ($table['borders_separate']) { // NB twice border width
24367									$xadj = $table['simple']['border_details']['L']['w'] + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);
24368									$wadj = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];
24369									$yadj = $table['simple']['border_details']['T']['w'] + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);
24370								} else {
24371									$xadj = $table['simple']['border_details']['L']['w'] / 2 + $cell['padding']['L'];
24372									$wadj = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + $cell['padding']['L'] + $cell['padding']['R'];
24373									$yadj = $table['simple']['border_details']['T']['w'] / 2 + $cell['padding']['T'];
24374								}
24375							}
24376							$this->decimal_offset = 0;
24377							if (substr($cell['a'], 0, 1) == 'D') {
24378								if (isset($cell['colspan']) && $cell['colspan'] > 1) {
24379									$this->cellTextAlign = $c['a'] = substr($cell['a'], 2, 1);
24380								} else {
24381									$smax = $table['decimal_align'][$j]['maxs0'];
24382									$d_content = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'];
24383									$this->decimal_offset = $smax;
24384									$extra = ($w - $d_content - $wadj);
24385									if ($extra > 0) {
24386										if (substr($cell['a'], 2, 1) == 'R') {
24387											$this->decimal_offset += $extra;
24388										} elseif (substr($cell['a'], 2, 1) == 'C') {
24389											$this->decimal_offset += ($extra) / 2;
24390										}
24391									}
24392								}
24393							}
24394							$this->divwidth = $w - $wadj;
24395							if ($this->divwidth == 0) {
24396								$this->divwidth = 0.0001;
24397							}
24398							$this->x += $xadj;
24399							$this->y += $yadj;
24400							$this->printbuffer($cell['textbuffer'], '', true, false, $cell['direction']);
24401						}
24402						$this->y = $opy;
24403					}
24404
24405					/* -- BACKGROUNDS -- */
24406					if (!$this->ColActive) {
24407						if (isset($table['trgradients'][$i]) && ($j == 0 || $table['borders_separate'])) {
24408							$g = $this->gradient->parseBackgroundGradient($table['trgradients'][$i]);
24409							if ($g) {
24410								$gx = $x0;
24411								$gy = $y;
24412								$gh = $h;
24413								$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
24414								if ($table['borders_separate']) {
24415									$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
24416									$clx = $x + ($table['border_spacing_H'] / 2);
24417									$cly = $y + ($table['border_spacing_V'] / 2);
24418									$clw = $w - $table['border_spacing_H'];
24419									$clh = $h - $table['border_spacing_V'];
24420									// Set clipping path
24421									$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
24422									$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];
24423								} else {
24424									$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' => ''];
24425								}
24426							}
24427						}
24428						if (isset($table['trbackground-images'][$i]) && ($j == 0 || $table['borders_separate'])) {
24429							if (isset($table['trbackground-images'][$i]['gradient']) && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['trbackground-images'][$i]['gradient'])) {
24430								$g = $this->gradient->parseMozGradient($table['trbackground-images'][$i]['gradient']);
24431								if ($g) {
24432									$gx = $x0;
24433									$gy = $y;
24434									$gh = $h;
24435									$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
24436									if ($table['borders_separate']) {
24437										$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
24438										$clx = $x + ($table['border_spacing_H'] / 2);
24439										$cly = $y + ($table['border_spacing_V'] / 2);
24440										$clw = $w - $table['border_spacing_H'];
24441										$clh = $h - $table['border_spacing_V'];
24442										// Set clipping path
24443										$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
24444										$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];
24445									} else {
24446										$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' => ''];
24447									}
24448								}
24449							} else {
24450								$image_id = $table['trbackground-images'][$i]['image_id'];
24451								$orig_w = $table['trbackground-images'][$i]['orig_w'];
24452								$orig_h = $table['trbackground-images'][$i]['orig_h'];
24453								$x_pos = $table['trbackground-images'][$i]['x_pos'];
24454								$y_pos = $table['trbackground-images'][$i]['y_pos'];
24455								$x_repeat = $table['trbackground-images'][$i]['x_repeat'];
24456								$y_repeat = $table['trbackground-images'][$i]['y_repeat'];
24457								$resize = $table['trbackground-images'][$i]['resize'];
24458								$opacity = $table['trbackground-images'][$i]['opacity'];
24459								$itype = $table['trbackground-images'][$i]['itype'];
24460								$clippath = '';
24461								$gx = $x0;
24462								$gy = $y;
24463								$gh = $h;
24464								$gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
24465								if ($table['borders_separate']) {
24466									$gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
24467									$clx = $x + ($table['border_spacing_H'] / 2);
24468									$cly = $y + ($table['border_spacing_V'] / 2);
24469									$clw = $w - $table['border_spacing_H'];
24470									$clh = $h - $table['border_spacing_V'];
24471									// Set clipping path
24472									$s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
24473									$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];
24474								} else {
24475									$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];
24476								}
24477							}
24478						}
24479					}
24480
24481					/* -- END BACKGROUNDS -- */
24482
24483					// TABLE BORDER - if separate
24484					if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {
24485						$halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);
24486						$halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);
24487						$halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);
24488						$halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);
24489						$tbx = $x;
24490						$tby = $y;
24491						$tbw = $w;
24492						$tbh = $h;
24493						$tab_bord = 0;
24494
24495						$corner = '';
24496						if ($i == 0) {  // Top
24497							$tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);
24498							$tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);
24499							$this->setBorder($tab_bord, Border::TOP);
24500							$corner .= 'T';
24501						}
24502						if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows)) { // Bottom
24503							$tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);
24504							$this->setBorder($tab_bord, Border::BOTTOM);
24505							$corner .= 'B';
24506						}
24507						if ($j == 0) {  // Left
24508							$tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);
24509							$tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);
24510							$this->setBorder($tab_bord, Border::LEFT);
24511							$corner .= 'L';
24512						}
24513						if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols)) { // Right
24514							$tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);
24515							$this->setBorder($tab_bord, Border::RIGHT);
24516							$corner .= 'R';
24517						}
24518						$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']);
24519					}
24520
24521					unset($cell);
24522					// Reset values
24523					$this->Reset();
24524				}//end of (if isset(cells)...)
24525			}// end of columns
24526
24527			$newpagestarted = false;
24528			$this->tabletheadjustfinished = false;
24529
24530			/* -- COLUMNS -- */
24531			if ($this->ColActive) {
24532				if (!$this->table_keep_together && $i < $numrows - 1 && $level == 1) {
24533					$this->breakpoints[$this->CurrCol][] = $y + $h;
24534				} // mPDF 6
24535				if (count($this->cellBorderBuffer)) {
24536					$this->printcellbuffer();
24537				}
24538			}
24539			/* -- END COLUMNS -- */
24540
24541			if ($i == $numrows - 1) {
24542				$this->y = $y + $h;
24543			} // last row jump (update this->y position)
24544			if ($this->table_rotate && $level == 1) {
24545				$this->tbrot_h += $h;
24546			}
24547		} // end of rows
24548
24549		if (count($this->cellBorderBuffer)) {
24550			$this->printcellbuffer();
24551		}
24552
24553
24554		if ($this->tableClipPath) {
24555			$this->_out("Q");
24556		}
24557		$this->tableClipPath = '';
24558
24559		// Advance down page by half width of bottom border
24560		if ($table['borders_separate']) {
24561			$this->y += $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
24562		} else {
24563			$this->y += $table['max_cell_border_width']['B'] / 2;
24564		}
24565
24566		if ($table['borders_separate'] && $level == 1) {
24567			$this->tbrot_h += $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
24568		} elseif ($level == 1) {
24569			$this->tbrot_h += $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;
24570		}
24571
24572		$bx = $x0;
24573		$by = $y0;
24574		if ($table['borders_separate']) {
24575			$bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);
24576			if ($tablestartpageno != $this->page) { // IF broken across page
24577				$by += $table['max_cell_border_width']['T'] / 2;
24578				if (empty($tableheader)) {
24579					$by -= ($table['border_spacing_V'] / 2);
24580				}
24581			} elseif ($split && $startrow > 0 && empty($tableheader)) {
24582				$by -= ($table['border_spacing_V'] / 2);
24583			} else {
24584				$by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);
24585			}
24586		} elseif ($tablestartpageno != $this->page && !empty($tableheader)) {
24587			$by += $maxbwtop / 2;
24588		}
24589		$by -= $tableheaderadj;
24590		$bh = $this->y - $by;
24591		if (!$table['borders_separate']) {
24592			$bh -= $table['max_cell_border_width']['B'] / 2;
24593		}
24594
24595		if ($split) {
24596			$bw = 0;
24597			$finalSpread = true;
24598			for ($t = $startcol; $t < $numcols; $t++) {
24599				if ($table['colPg'][$t] == $splitpg) {
24600					$bw += $table['wc'][$t];
24601				}
24602				if ($table['colPg'][$t] > $splitpg) {
24603					$finalSpread = false;
24604					break;
24605				}
24606			}
24607			if ($startcol == 0) {
24608				$firstSpread = true;
24609			} else {
24610				$firstSpread = false;
24611			}
24612			if ($table['borders_separate']) {
24613				$bw += $table['border_spacing_H'];
24614				if ($firstSpread) {
24615					$bw += $table['padding']['L'] + $table['border_details']['L']['w'];
24616				} else {
24617					$bx += ($table['padding']['L'] + $table['border_details']['L']['w']);
24618				}
24619				if ($finalSpread) {
24620					$bw += $table['padding']['R'] + $table['border_details']['R']['w'];
24621				}
24622			}
24623		} else {
24624			$bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
24625		}
24626
24627		if (!$this->ColActive) {
24628			if (isset($table['bgcolor'][-1])) {
24629				$color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings);
24630				if ($color) {
24631					$this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color];
24632				}
24633			}
24634
24635			/* -- BACKGROUNDS -- */
24636			if (isset($table['gradient'])) {
24637				$g = $this->gradient->parseBackgroundGradient($table['gradient']);
24638				if ($g) {
24639					$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' => ''];
24640				}
24641			}
24642
24643			if (isset($table['background-image'])) {
24644				if (isset($table['background-image']['gradient']) && $table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {
24645					$g = $this->gradient->parseMozGradient($table['background-image']['gradient']);
24646					if ($g) {
24647						$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' => ''];
24648					}
24649				} else {
24650					$image_id = $table['background-image']['image_id'];
24651					$orig_w = $table['background-image']['orig_w'];
24652					$orig_h = $table['background-image']['orig_h'];
24653					$x_pos = $table['background-image']['x_pos'];
24654					$y_pos = $table['background-image']['y_pos'];
24655					$x_repeat = $table['background-image']['x_repeat'];
24656					$y_repeat = $table['background-image']['y_repeat'];
24657					$resize = $table['background-image']['resize'];
24658					$opacity = $table['background-image']['opacity'];
24659					$itype = $table['background-image']['itype'];
24660					$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];
24661				}
24662			}
24663			/* -- END BACKGROUNDS -- */
24664		}
24665
24666		if ($this->tableBackgrounds && $level == 1) {
24667			$s = $this->PrintTableBackgrounds();
24668			if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) {
24669				$this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->tablebuffer);
24670				if ($level == 1) {
24671					$this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->tablebuffer);
24672				}
24673			} elseif ($this->bufferoutput) {
24674				$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer);
24675				if ($level == 1) {
24676					$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer);
24677				}
24678			} else {
24679				$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
24680				if ($level == 1) {
24681					$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]);
24682				}
24683			}
24684			$this->tableBackgrounds = [];
24685		}
24686
24687
24688		// TABLE BOTTOM MARGIN
24689		if ($table['margin']['B']) {
24690			if (!$this->table_rotate && $level == 1) {
24691				$this->DivLn($table['margin']['B'], $this->blklvl, true);  // collapsible
24692			} else {
24693				$this->y += ($table['margin']['B']);
24694			}
24695		}
24696
24697		if ($this->ColActive && $level == 1) {
24698			$this->breakpoints[$this->CurrCol][] = $this->y;
24699		} // *COLUMNS*
24700
24701		if ($split) {
24702			// Are there more columns to print on a next page?
24703			if ($lastCol < $numcols - 1) {
24704				$splitpg++;
24705				$startcol = $lastCol + 1;
24706				return [false, $startrow, $startcol, $splitpg, $returny, $y0];
24707			} else {
24708				return [true, 0, 0, 0, false, false];
24709			}
24710		}
24711	}
24712
24713	// END OF FUNCTION _tableWrite()
24714	/////////////////////////END OF TABLE CODE//////////////////////////////////
24715	/* -- END TABLES -- */
24716
24717	function _putextgstates()
24718	{
24719		for ($i = 1; $i <= count($this->extgstates); $i++) {
24720			$this->_newobj();
24721			$this->extgstates[$i]['n'] = $this->n;
24722			$this->_out('<</Type /ExtGState');
24723			foreach ($this->extgstates[$i]['parms'] as $k => $v) {
24724				$this->_out('/' . $k . ' ' . $v);
24725			}
24726			$this->_out('>>');
24727			$this->_out('endobj');
24728		}
24729	}
24730
24731	function _putocg()
24732	{
24733		if ($this->hasOC) {
24734			$this->_newobj();
24735			$this->n_ocg_print = $this->n;
24736			$this->_out('<</Type /OCG /Name ' . $this->_textstring('Print only'));
24737			$this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
24738			$this->_out('endobj');
24739			$this->_newobj();
24740			$this->n_ocg_view = $this->n;
24741			$this->_out('<</Type /OCG /Name ' . $this->_textstring('Screen only'));
24742			$this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
24743			$this->_out('endobj');
24744			$this->_newobj();
24745			$this->n_ocg_hidden = $this->n;
24746			$this->_out('<</Type /OCG /Name ' . $this->_textstring('Hidden'));
24747			$this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /OFF>>>>>>');
24748			$this->_out('endobj');
24749		}
24750		if (count($this->layers)) {
24751			ksort($this->layers);
24752			foreach ($this->layers as $id => $layer) {
24753				$this->_newobj();
24754				$this->layers[$id]['n'] = $this->n;
24755				if (isset($this->layerDetails[$id]['name']) && $this->layerDetails[$id]['name']) {
24756					$name = $this->layerDetails[$id]['name'];
24757				} else {
24758					$name = $layer['name'];
24759				}
24760				$this->_out('<</Type /OCG /Name ' . $this->_UTF16BEtextstring($name) . '>>');
24761				$this->_out('endobj');
24762			}
24763		}
24764	}
24765
24766	/* -- IMPORTS -- */
24767
24768	// from mPDFI
24769	function _putimportedobjects()
24770	{
24771		if (is_array($this->parsers) && count($this->parsers) > 0) {
24772			foreach ($this->parsers as $filename => $p) {
24773				$this->current_parser = $this->parsers[$filename];
24774				if (is_array($this->_obj_stack[$filename])) {
24775					while ($n = key($this->_obj_stack[$filename])) {
24776						$nObj = $this->current_parser->resolveObject($this->_obj_stack[$filename][$n][1]);
24777						$this->_newobj($this->_obj_stack[$filename][$n][0]);
24778						if ($nObj[0] == pdf_parser::TYPE_STREAM) {
24779							$this->pdf_write_value($nObj);
24780						} else {
24781							$this->pdf_write_value($nObj[1]);
24782						}
24783						$this->_out('endobj');
24784						$this->_obj_stack[$filename][$n] = null; // free memory
24785						unset($this->_obj_stack[$filename][$n]);
24786						reset($this->_obj_stack[$filename]);
24787					}
24788				}
24789			}
24790		}
24791	}
24792
24793	function _putformxobjects()
24794	{
24795		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
24796		reset($this->tpls);
24797		foreach ($this->tpls as $tplidx => $tpl) {
24798			$p = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
24799			$this->_newobj();
24800			$this->tpls[$tplidx]['n'] = $this->n;
24801			$this->_out('<<' . $filter . '/Type /XObject');
24802			$this->_out('/Subtype /Form');
24803			$this->_out('/FormType 1');
24804			// Left/Bottom/Right/Top
24805			$this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]', $tpl['box']['x'] * Mpdf::SCALE, $tpl['box']['y'] * Mpdf::SCALE, ($tpl['box']['x'] + $tpl['box']['w']) * Mpdf::SCALE, ($tpl['box']['y'] + $tpl['box']['h']) * Mpdf::SCALE));
24806
24807
24808			if (isset($tpl['box'])) {
24809				$this->_out(sprintf('/Matrix [1 0 0 1 %.5F %.5F]', -$tpl['box']['x'] * Mpdf::SCALE, -$tpl['box']['y'] * Mpdf::SCALE));
24810			}
24811			$this->_out('/Resources ');
24812
24813			if (isset($tpl['resources'])) {
24814				$this->current_parser = $tpl['parser'];
24815				$this->pdf_write_value($tpl['resources']);
24816			} else {
24817				$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
24818				if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
24819					$this->_out('/Font <<');
24820					foreach ($this->_res['tpl'][$tplidx]['fonts'] as $font) {
24821						$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
24822					}
24823					$this->_out('>>');
24824				}
24825				if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
24826					isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
24827					$this->_out('/XObject <<');
24828					if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
24829						foreach ($this->_res['tpl'][$tplidx]['images'] as $image) {
24830							$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
24831						}
24832					}
24833					if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
24834						foreach ($this->_res['tpl'][$tplidx]['tpls'] as $i => $itpl) {
24835							$this->_out($this->tplprefix . $i . ' ' . $itpl['n'] . ' 0 R');
24836						}
24837					}
24838					$this->_out('>>');
24839				}
24840				$this->_out('>>');
24841			}
24842
24843			$this->_out('/Length ' . strlen($p) . ' >>');
24844			$this->_putstream($p);
24845			$this->_out('endobj');
24846		}
24847	}
24848
24849	/* -- END IMPORTS -- */
24850
24851	function _putpatterns()
24852	{
24853		for ($i = 1; $i <= count($this->patterns); $i++) {
24854			$x = $this->patterns[$i]['x'];
24855			$y = $this->patterns[$i]['y'];
24856			$w = $this->patterns[$i]['w'];
24857			$h = $this->patterns[$i]['h'];
24858			$pgh = $this->patterns[$i]['pgh'];
24859			$orig_w = $this->patterns[$i]['orig_w'];
24860			$orig_h = $this->patterns[$i]['orig_h'];
24861			$image_id = $this->patterns[$i]['image_id'];
24862			$itype = $this->patterns[$i]['itype'];
24863
24864			if (isset($this->patterns[$i]['bpa'])) {
24865				$bpa = $this->patterns[$i]['bpa'];
24866			} else {
24867				$bpa = []; // background positioning area
24868			}
24869
24870			if ($this->patterns[$i]['x_repeat']) {
24871				$x_repeat = true;
24872			} else {
24873				$x_repeat = false;
24874			}
24875
24876			if ($this->patterns[$i]['y_repeat']) {
24877				$y_repeat = true;
24878			} else {
24879				$y_repeat = false;
24880			}
24881
24882			$x_pos = $this->patterns[$i]['x_pos'];
24883
24884			if (stristr($x_pos, '%')) {
24885				$x_pos = (float) $x_pos;
24886				$x_pos /= 100;
24887
24888				if (isset($bpa['w']) && $bpa['w']) {
24889					$x_pos = ($bpa['w'] * $x_pos) - ($orig_w / Mpdf::SCALE * $x_pos);
24890				} else {
24891					$x_pos = ($w * $x_pos) - ($orig_w / Mpdf::SCALE * $x_pos);
24892				}
24893			}
24894
24895			$y_pos = $this->patterns[$i]['y_pos'];
24896
24897			if (stristr($y_pos, '%')) {
24898				$y_pos = (float) $y_pos;
24899				$y_pos /= 100;
24900
24901				if (isset($bpa['h']) && $bpa['h']) {
24902					$y_pos = ($bpa['h'] * $y_pos) - ($orig_h / Mpdf::SCALE * $y_pos);
24903				} else {
24904					$y_pos = ($h * $y_pos) - ($orig_h / Mpdf::SCALE * $y_pos);
24905				}
24906			}
24907
24908			if (isset($bpa['x']) && $bpa['x']) {
24909				$adj_x = ($x_pos + $bpa['x']) * Mpdf::SCALE;
24910			} else {
24911				$adj_x = ($x_pos + $x) * Mpdf::SCALE;
24912			}
24913
24914			if (isset($bpa['y']) && $bpa['y']) {
24915				$adj_y = (($pgh - $y_pos - $bpa['y']) * Mpdf::SCALE) - $orig_h;
24916			} else {
24917				$adj_y = (($pgh - $y_pos - $y) * Mpdf::SCALE) - $orig_h;
24918			}
24919
24920			$img_obj = false;
24921
24922			if ($itype == 'svg' || $itype == 'wmf') {
24923				foreach ($this->formobjects as $fo) {
24924					if ($fo['i'] == $image_id) {
24925						$img_obj = $fo['n'];
24926						$fo_w = $fo['w'];
24927						$fo_h = -$fo['h'];
24928						$wmf_x = $fo['x'];
24929						$wmf_y = $fo['y'];
24930						break;
24931					}
24932				}
24933			} else {
24934				foreach ($this->images as $img) {
24935					if ($img['i'] == $image_id) {
24936						$img_obj = $img['n'];
24937						break;
24938					}
24939				}
24940			}
24941
24942			if (!$img_obj) {
24943				throw new \Mpdf\MpdfException("Problem: Image object not found for background pattern " . $img['i']);
24944			}
24945
24946			$this->_newobj();
24947			$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
24948			if ($itype == 'svg' || $itype == 'wmf') {
24949				$this->_out('/XObject <</FO' . $image_id . ' ' . $img_obj . ' 0 R >>');
24950				// ******* ADD ANY ExtGStates, Shading AND Fonts needed for the FormObject
24951				// Set in classes/svg array['fo'] = true
24952				// Required that _putshaders comes before _putpatterns in _putresources
24953				// This adds any resources associated with any FormObject to every Formobject - overkill but works!
24954				if (count($this->extgstates)) {
24955					$this->_out('/ExtGState <<');
24956					foreach ($this->extgstates as $k => $extgstate) {
24957						if (isset($extgstate['fo']) && $extgstate['fo']) {
24958							if (isset($extgstate['trans'])) {
24959								$this->_out('/' . $extgstate['trans'] . ' ' . $extgstate['n'] . ' 0 R');
24960							} else {
24961								$this->_out('/GS' . $k . ' ' . $extgstate['n'] . ' 0 R');
24962							}
24963						}
24964					}
24965					$this->_out('>>');
24966				}
24967				/* -- BACKGROUNDS -- */
24968				if (isset($this->gradients) and ( count($this->gradients) > 0)) {
24969					$this->_out('/Shading <<');
24970					foreach ($this->gradients as $id => $grad) {
24971						if (isset($grad['fo']) && $grad['fo']) {
24972							$this->_out('/Sh' . $id . ' ' . $grad['id'] . ' 0 R');
24973						}
24974					}
24975					$this->_out('>>');
24976				}
24977				/* -- END BACKGROUNDS -- */
24978				$this->_out('/Font <<');
24979				foreach ($this->fonts as $font) {
24980					if (!$font['used'] && $font['type'] == 'TTF') {
24981						continue;
24982					}
24983					if (isset($font['fo']) && $font['fo']) {
24984						if ($font['type'] == 'TTF' && ($font['sip'] || $font['smp'])) {
24985							foreach ($font['n'] as $k => $fid) {
24986								$this->_out('/F' . $font['subsetfontids'][$k] . ' ' . $font['n'][$k] . ' 0 R');
24987							}
24988						} else {
24989							$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
24990						}
24991					}
24992				}
24993				$this->_out('>>');
24994			} else {
24995				$this->_out('/XObject <</I' . $image_id . ' ' . $img_obj . ' 0 R >>');
24996			}
24997			$this->_out('>>');
24998			$this->_out('endobj');
24999
25000			$this->_newobj();
25001			$this->patterns[$i]['n'] = $this->n;
25002			$this->_out('<< /Type /Pattern /PatternType 1 /PaintType 1 /TilingType 2');
25003			$this->_out('/Resources ' . ($this->n - 1) . ' 0 R');
25004
25005			$this->_out(sprintf('/BBox [0 0 %.3F %.3F]', $orig_w, $orig_h));
25006			if ($x_repeat) {
25007				$this->_out(sprintf('/XStep %.3F', $orig_w));
25008			} else {
25009				$this->_out(sprintf('/XStep %d', 99999));
25010			}
25011			if ($y_repeat) {
25012				$this->_out(sprintf('/YStep %.3F', $orig_h));
25013			} else {
25014				$this->_out(sprintf('/YStep %d', 99999));
25015			}
25016
25017			if ($itype == 'svg' || $itype == 'wmf') {
25018				$this->_out(sprintf('/Matrix [1 0 0 -1 %.3F %.3F]', $adj_x, ($adj_y + $orig_h)));
25019				$s = sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q", ($orig_w / $fo_w), (-$orig_h / $fo_h), -($orig_w / $fo_w) * $wmf_x, ($orig_w / $fo_w) * $wmf_y, $image_id);
25020			} else {
25021				$this->_out(sprintf('/Matrix [1 0 0 1 %.3F %.3F]', $adj_x, $adj_y));
25022				$s = sprintf("q %.3F 0 0 %.3F 0 0 cm /I%d Do Q", $orig_w, $orig_h, $image_id);
25023			}
25024
25025			if ($this->compress) {
25026				$this->_out('/Filter /FlateDecode');
25027				$s = gzcompress($s);
25028			}
25029			$this->_out('/Length ' . strlen($s) . '>>');
25030			$this->_putstream($s);
25031			$this->_out('endobj');
25032		}
25033	}
25034
25035	/* -- BACKGROUNDS -- */
25036
25037	function _putshaders()
25038	{
25039		$maxid = count($this->gradients); // index for transparency gradients
25040		foreach ($this->gradients as $id => $grad) {
25041			if (($grad['type'] == 2 || $grad['type'] == 3) && empty($grad['is_mask'])) {
25042				$this->_newobj();
25043				$this->_out('<<');
25044				$this->_out('/FunctionType 3');
25045				$this->_out('/Domain [0 1]');
25046				$fn = [];
25047				$bd = [];
25048				$en = [];
25049				for ($i = 0; $i < (count($grad['stops']) - 1); $i++) {
25050					$fn[] = ($this->n + 1 + $i) . ' 0 R';
25051					$en[] = '0 1';
25052					if ($i > 0) {
25053						$bd[] = sprintf('%.3F', $grad['stops'][$i]['offset']);
25054					}
25055				}
25056				$this->_out('/Functions [' . implode(' ', $fn) . ']');
25057				$this->_out('/Bounds [' . implode(' ', $bd) . ']');
25058				$this->_out('/Encode [' . implode(' ', $en) . ']');
25059				$this->_out('>>');
25060				$this->_out('endobj');
25061				$f1 = $this->n;
25062				for ($i = 0; $i < (count($grad['stops']) - 1); $i++) {
25063					$this->_newobj();
25064					$this->_out('<<');
25065					$this->_out('/FunctionType 2');
25066					$this->_out('/Domain [0 1]');
25067					$this->_out('/C0 [' . $grad['stops'][$i]['col'] . ']');
25068					$this->_out('/C1 [' . $grad['stops'][$i + 1]['col'] . ']');
25069					$this->_out('/N 1');
25070					$this->_out('>>');
25071					$this->_out('endobj');
25072				}
25073			}
25074			if ($grad['type'] == 2 || $grad['type'] == 3) {
25075				if (isset($grad['trans']) && $grad['trans']) {
25076					$this->_newobj();
25077					$this->_out('<<');
25078					$this->_out('/FunctionType 3');
25079					$this->_out('/Domain [0 1]');
25080					$fn = [];
25081					$bd = [];
25082					$en = [];
25083					for ($i = 0; $i < (count($grad['stops']) - 1); $i++) {
25084						$fn[] = ($this->n + 1 + $i) . ' 0 R';
25085						$en[] = '0 1';
25086						if ($i > 0) {
25087							$bd[] = sprintf('%.3F', $grad['stops'][$i]['offset']);
25088						}
25089					}
25090					$this->_out('/Functions [' . implode(' ', $fn) . ']');
25091					$this->_out('/Bounds [' . implode(' ', $bd) . ']');
25092					$this->_out('/Encode [' . implode(' ', $en) . ']');
25093					$this->_out('>>');
25094					$this->_out('endobj');
25095					$f2 = $this->n;
25096					for ($i = 0; $i < (count($grad['stops']) - 1); $i++) {
25097						$this->_newobj();
25098						$this->_out('<<');
25099						$this->_out('/FunctionType 2');
25100						$this->_out('/Domain [0 1]');
25101						$this->_out(sprintf('/C0 [%.3F]', $grad['stops'][$i]['opacity']));
25102						$this->_out(sprintf('/C1 [%.3F]', $grad['stops'][$i + 1]['opacity']));
25103						$this->_out('/N 1');
25104						$this->_out('>>');
25105						$this->_out('endobj');
25106					}
25107				}
25108			}
25109
25110			if (empty($grad['is_mask'])) {
25111				$this->_newobj();
25112				$this->_out('<<');
25113				$this->_out('/ShadingType ' . $grad['type']);
25114				if (isset($grad['colorspace'])) {
25115					$this->_out('/ColorSpace /Device' . $grad['colorspace']);  // Can use CMYK if all C0 and C1 above have 4 values
25116				} else {
25117					$this->_out('/ColorSpace /DeviceRGB');
25118				}
25119				if ($grad['type'] == 2) {
25120					$this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
25121					$this->_out('/Function ' . $f1 . ' 0 R');
25122					$this->_out('/Extend [' . $grad['extend'][0] . ' ' . $grad['extend'][1] . '] ');
25123					$this->_out('>>');
25124				} elseif ($grad['type'] == 3) {
25125					// x0, y0, r0, x1, y1, r1
25126					// at this this time radius of inner circle is 0
25127					$ir = 0;
25128					if (isset($grad['coords'][5]) && $grad['coords'][5]) {
25129						$ir = $grad['coords'][5];
25130					}
25131					$this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $ir, $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
25132					$this->_out('/Function ' . $f1 . ' 0 R');
25133					$this->_out('/Extend [' . $grad['extend'][0] . ' ' . $grad['extend'][1] . '] ');
25134					$this->_out('>>');
25135				} elseif ($grad['type'] == 6) {
25136					$this->_out('/BitsPerCoordinate 16');
25137					$this->_out('/BitsPerComponent 8');
25138					if ($grad['colorspace'] == 'CMYK') {
25139						$this->_out('/Decode[0 1 0 1 0 1 0 1 0 1 0 1]');
25140					} elseif ($grad['colorspace'] == 'Gray') {
25141						$this->_out('/Decode[0 1 0 1 0 1]');
25142					} else {
25143						$this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
25144					}
25145					$this->_out('/BitsPerFlag 8');
25146					$this->_out('/Length ' . strlen($grad['stream']));
25147					$this->_out('>>');
25148					$this->_putstream($grad['stream']);
25149				}
25150				$this->_out('endobj');
25151			}
25152
25153			$this->gradients[$id]['id'] = $this->n;
25154
25155			// set pattern object
25156			$this->_newobj();
25157			$out = '<< /Type /Pattern /PatternType 2';
25158			$out .= ' /Shading ' . $this->gradients[$id]['id'] . ' 0 R';
25159			$out .= ' >>';
25160			$out .= "\n" . 'endobj';
25161			$this->_out($out);
25162
25163
25164			$this->gradients[$id]['pattern'] = $this->n;
25165
25166			if (isset($grad['trans']) && $grad['trans']) {
25167				// luminosity pattern
25168				$transid = $id + $maxid;
25169				$this->_newobj();
25170				$this->_out('<<');
25171				$this->_out('/ShadingType ' . $grad['type']);
25172				$this->_out('/ColorSpace /DeviceGray');
25173				if ($grad['type'] == 2) {
25174					$this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
25175					$this->_out('/Function ' . $f2 . ' 0 R');
25176					$this->_out('/Extend [' . $grad['extend'][0] . ' ' . $grad['extend'][1] . '] ');
25177					$this->_out('>>');
25178				} elseif ($grad['type'] == 3) {
25179					// x0, y0, r0, x1, y1, r1
25180					// at this this time radius of inner circle is 0
25181					$ir = 0;
25182					if (isset($grad['coords'][5]) && $grad['coords'][5]) {
25183						$ir = $grad['coords'][5];
25184					}
25185					$this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $ir, $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
25186					$this->_out('/Function ' . $f2 . ' 0 R');
25187					$this->_out('/Extend [' . $grad['extend'][0] . ' ' . $grad['extend'][1] . '] ');
25188					$this->_out('>>');
25189				} elseif ($grad['type'] == 6) {
25190					$this->_out('/BitsPerCoordinate 16');
25191					$this->_out('/BitsPerComponent 8');
25192					$this->_out('/Decode[0 1 0 1 0 1]');
25193					$this->_out('/BitsPerFlag 8');
25194					$this->_out('/Length ' . strlen($grad['stream_trans']));
25195					$this->_out('>>');
25196					$this->_putstream($grad['stream_trans']);
25197				}
25198				$this->_out('endobj');
25199
25200				$this->gradients[$transid]['id'] = $this->n;
25201				$this->_newobj();
25202				$this->_out('<< /Type /Pattern /PatternType 2');
25203				$this->_out('/Shading ' . $this->gradients[$transid]['id'] . ' 0 R');
25204				$this->_out('>>');
25205				$this->_out('endobj');
25206				$this->gradients[$transid]['pattern'] = $this->n;
25207				$this->_newobj();
25208				// Need to extend size of viewing box in case of transformations
25209				$str = 'q /a0 gs /Pattern cs /p' . $transid . ' scn -' . ($this->wPt / 2) . ' -' . ($this->hPt / 2) . ' ' . (2 * $this->wPt) . ' ' . (2 * $this->hPt) . ' re f Q';
25210				$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
25211				$p = ($this->compress) ? gzcompress($str) : $str;
25212				$this->_out('<< /Type /XObject /Subtype /Form /FormType 1 ' . $filter);
25213				$this->_out('/Length ' . strlen($p));
25214				$this->_out('/BBox [-' . ($this->wPt / 2) . ' -' . ($this->hPt / 2) . ' ' . (2 * $this->wPt) . ' ' . (2 * $this->hPt) . ']');
25215				$this->_out('/Group << /Type /Group /S /Transparency /CS /DeviceGray >>');
25216				$this->_out('/Resources <<');
25217				$this->_out('/ExtGState << /a0 << /ca 1 /CA 1 >> >>');
25218				$this->_out('/Pattern << /p' . $transid . ' ' . $this->gradients[$transid]['pattern'] . ' 0 R >>');
25219				$this->_out('>>');
25220				$this->_out('>>');
25221				$this->_putstream($p);
25222				$this->_out('endobj');
25223				$this->_newobj();
25224				$this->_out('<< /Type /Mask /S /Luminosity /G ' . ($this->n - 1) . ' 0 R >>' . "\n" . 'endobj');
25225				$this->_newobj();
25226				$this->_out('<< /Type /ExtGState /SMask ' . ($this->n - 1) . ' 0 R /AIS false >>' . "\n" . 'endobj');
25227				if (isset($grad['fo']) && $grad['fo']) {
25228					$this->extgstates[] = ['n' => $this->n, 'trans' => 'TGS' . $id, 'fo' => true];
25229				} else {
25230					$this->extgstates[] = ['n' => $this->n, 'trans' => 'TGS' . $id];
25231				}
25232			}
25233		}
25234	}
25235
25236	/* -- END BACKGROUNDS -- */
25237
25238	function _putspotcolors()
25239	{
25240		foreach ($this->spotColors as $name => $color) {
25241			$this->_newobj();
25242			$this->_out('[/Separation /' . str_replace(' ', '#20', $name));
25243			$this->_out('/DeviceCMYK <<');
25244			$this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
25245			$this->_out(sprintf('/C1 [%.3F %.3F %.3F %.3F] ', $color['c'] / 100, $color['m'] / 100, $color['y'] / 100, $color['k'] / 100));
25246			$this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
25247			$this->_out('endobj');
25248			$this->spotColors[$name]['n'] = $this->n;
25249		}
25250	}
25251
25252	function _putresources()
25253	{
25254		if ($this->hasOC || count($this->layers)) {
25255			$this->_putocg();
25256		}
25257		$this->_putextgstates();
25258		$this->_putspotcolors();
25259
25260		// @log Compiling Fonts
25261
25262		$this->_putfonts();
25263
25264		// @log Compiling Images
25265
25266		$this->_putimages();
25267		$this->_putformobjects(); // *IMAGES-CORE*
25268
25269		/* -- IMPORTS -- */
25270		if ($this->enableImports) {
25271			$this->_putformxobjects();
25272			$this->_putimportedobjects();
25273		}
25274		/* -- END IMPORTS -- */
25275
25276		/* -- BACKGROUNDS -- */
25277		$this->_putshaders();
25278		$this->_putpatterns();
25279		/* -- END BACKGROUNDS -- */
25280
25281
25282		// Resource dictionary
25283		$this->offsets[2] = strlen($this->buffer);
25284		$this->_out('2 0 obj');
25285		$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
25286
25287		$this->_out('/Font <<');
25288		foreach ($this->fonts as $font) {
25289			if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {
25290				continue;
25291			}
25292			if (isset($font['type']) && $font['type'] == 'TTF' && ($font['sip'] || $font['smp'])) {
25293				foreach ($font['n'] as $k => $fid) {
25294					$this->_out('/F' . $font['subsetfontids'][$k] . ' ' . $font['n'][$k] . ' 0 R');
25295				}
25296			} else {
25297				$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
25298			}
25299		}
25300		$this->_out('>>');
25301
25302		if (count($this->spotColors)) {
25303			$this->_out('/ColorSpace <<');
25304			foreach ($this->spotColors as $color) {
25305				$this->_out('/CS' . $color['i'] . ' ' . $color['n'] . ' 0 R');
25306			}
25307			$this->_out('>>');
25308		}
25309
25310		if (count($this->extgstates)) {
25311			$this->_out('/ExtGState <<');
25312			foreach ($this->extgstates as $k => $extgstate) {
25313				if (isset($extgstate['trans'])) {
25314					$this->_out('/' . $extgstate['trans'] . ' ' . $extgstate['n'] . ' 0 R');
25315				} else {
25316					$this->_out('/GS' . $k . ' ' . $extgstate['n'] . ' 0 R');
25317				}
25318			}
25319			$this->_out('>>');
25320		}
25321
25322		/* -- BACKGROUNDS -- */
25323		if ((isset($this->gradients) and ( count($this->gradients) > 0)) || ($this->enableImports && count($this->tpls))) { // mPDF 5.7.3
25324
25325			$this->_out('/Shading <<');
25326
25327			foreach ($this->gradients as $id => $grad) {
25328				$this->_out('/Sh' . $id . ' ' . $grad['id'] . ' 0 R');
25329			}
25330
25331			// mPDF 5.7.3
25332			// If a shading dictionary is in an object (tpl) imported from another PDF, it needs to be included
25333			// in the document resources, as well as the object resources
25334			// Otherwise get an error in some PDF viewers
25335			if ($this->enableImports && count($this->tpls)) {
25336
25337				foreach ($this->tpls as $tplidx => $tpl) {
25338
25339					if (isset($tpl['resources'])) {
25340
25341						$this->current_parser = $tpl['parser'];
25342
25343						foreach ($tpl['resources'][1] as $k => $v) {
25344							if ($k == '/Shading') {
25345								foreach ($v[1] as $k2 => $v2) {
25346									$this->_out($k2 . " ", false);
25347									$this->pdf_write_value($v2);
25348								}
25349							}
25350						}
25351					}
25352				}
25353			}
25354
25355			$this->_out('>>');
25356
25357			/*
25358			  // ??? Not needed !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
25359			  $this->_out('/Pattern <<');
25360			  foreach ($this->gradients as $id => $grad) {
25361			  $this->_out('/P'.$id.' '.$grad['pattern'].' 0 R');
25362			  }
25363			  $this->_out('>>');
25364			 */
25365		}
25366		/* -- END BACKGROUNDS -- */
25367
25368		if (count($this->images) || count($this->formobjects) || ($this->enableImports && count($this->tpls))) {
25369			$this->_out('/XObject <<');
25370			foreach ($this->images as $image) {
25371				$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
25372			}
25373			foreach ($this->formobjects as $formobject) {
25374				$this->_out('/FO' . $formobject['i'] . ' ' . $formobject['n'] . ' 0 R');
25375			}
25376			/* -- IMPORTS -- */
25377			if ($this->enableImports && count($this->tpls)) {
25378				foreach ($this->tpls as $tplidx => $tpl) {
25379					$this->_out($this->tplprefix . $tplidx . ' ' . $tpl['n'] . ' 0 R');
25380				}
25381			}
25382			/* -- END IMPORTS -- */
25383			$this->_out('>>');
25384		}
25385
25386		/* -- BACKGROUNDS -- */
25387
25388		if (count($this->patterns)) {
25389			$this->_out('/Pattern <<');
25390			foreach ($this->patterns as $k => $patterns) {
25391				$this->_out('/P' . $k . ' ' . $patterns['n'] . ' 0 R');
25392			}
25393			$this->_out('>>');
25394		}
25395		/* -- END BACKGROUNDS -- */
25396
25397		if ($this->hasOC || count($this->layers)) {
25398			$this->_out('/Properties <<');
25399			if ($this->hasOC) {
25400				$this->_out('/OC1 ' . $this->n_ocg_print . ' 0 R /OC2 ' . $this->n_ocg_view . ' 0 R /OC3 ' . $this->n_ocg_hidden . ' 0 R ');
25401			}
25402			if (count($this->layers)) {
25403				foreach ($this->layers as $id => $layer) {
25404					$this->_out('/ZI' . $id . ' ' . $layer['n'] . ' 0 R');
25405				}
25406			}
25407			$this->_out('>>');
25408		}
25409
25410		$this->_out('>>');
25411		$this->_out('endobj'); // end resource dictionary
25412
25413		$this->_putbookmarks();
25414
25415		if (isset($this->js) && $this->js) {
25416			$this->_putjavascript();
25417		}
25418
25419		if ($this->encrypted) {
25420			$this->_newobj();
25421			$this->enc_obj_id = $this->n;
25422			$this->_out('<<');
25423			$this->_putencryption();
25424			$this->_out('>>');
25425			$this->_out('endobj');
25426		}
25427	}
25428
25429	function _putjavascript()
25430	{
25431		$this->_newobj();
25432		$this->n_js = $this->n;
25433		$this->_out('<<');
25434		$this->_out('/Names [(EmbeddedJS) ' . (1 + $this->n) . ' 0 R ]');
25435		$this->_out('>>');
25436		$this->_out('endobj');
25437
25438		$this->_newobj();
25439		$this->_out('<<');
25440		$this->_out('/S /JavaScript');
25441		$this->_out('/JS ' . $this->_textstring($this->js));
25442		$this->_out('>>');
25443		$this->_out('endobj');
25444	}
25445
25446	function _putencryption()
25447	{
25448		$this->_out('/Filter /Standard');
25449		if ($this->protection->getUseRC128Encryption()) {
25450			$this->_out('/V 2');
25451			$this->_out('/R 3');
25452			$this->_out('/Length 128');
25453		} else {
25454			$this->_out('/V 1');
25455			$this->_out('/R 2');
25456		}
25457		$this->_out('/O (' . $this->_escape($this->protection->getOValue()) . ')');
25458		$this->_out('/U (' . $this->_escape($this->protection->getUvalue()) . ')');
25459		$this->_out('/P ' . $this->protection->getPvalue());
25460	}
25461
25462	function _puttrailer()
25463	{
25464		$this->_out('/Size ' . ($this->n + 1));
25465		$this->_out('/Root ' . $this->n . ' 0 R');
25466		$this->_out('/Info ' . $this->InfoRoot . ' 0 R');
25467
25468		if ($this->encrypted) {
25469			$this->_out('/Encrypt ' . $this->enc_obj_id . ' 0 R');
25470			$this->_out('/ID [<' . $this->protection->getUniqid() . '> <' . $this->protection->getUniqid() . '>]');
25471		} else {
25472			$uniqid = md5(time() . $this->buffer);
25473			$this->_out('/ID [<' . $uniqid . '> <' . $uniqid . '>]');
25474		}
25475	}
25476
25477	function SetProtection($permissions = [], $user_pass = '', $owner_pass = null, $length = 40)
25478	{
25479		if (!$this->protection) {
25480			$this->protection = new Protection(new UniqidGenerator());
25481		}
25482
25483		$this->encrypted = $this->protection->setProtection($permissions, $user_pass, $owner_pass, $length);
25484	}
25485
25486	// =========================================
25487	/* -- BOOKMARKS -- */
25488	// FROM class PDF_Bookmark
25489	function Bookmark($txt, $level = 0, $y = 0)
25490	{
25491		$txt = $this->purify_utf8_text($txt);
25492		if ($this->text_input_as_HTML) {
25493			$txt = $this->all_entities_to_utf8($txt);
25494		}
25495		if ($y == -1) {
25496			if (!$this->ColActive) {
25497				$y = $this->y;
25498			} else {
25499				$y = $this->y0;
25500			} // If columns are on - mark top of columns
25501		}
25502		// else y is used as set, or =0 i.e. top of page
25503		// DIRECTIONALITY RTL
25504		$bmo = ['t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->page];
25505		if ($this->keep_block_together) {
25506			// do nothing
25507		} /* -- TABLES -- */ elseif ($this->table_rotate) {
25508			$this->tbrot_BMoutlines[] = $bmo;
25509		} elseif ($this->kwt) {
25510			$this->kwt_BMoutlines[] = $bmo;
25511		} /* -- END TABLES -- */ elseif ($this->ColActive) { // *COLUMNS*
25512			$this->col_BMoutlines[] = $bmo; // *COLUMNS*
25513		} // *COLUMNS*
25514		else {
25515			$this->BMoutlines[] = $bmo;
25516		}
25517	}
25518
25519	function _putbookmarks()
25520	{
25521		$nb = count($this->BMoutlines);
25522		if ($nb == 0) {
25523			return;
25524		}
25525
25526		$bmo = $this->BMoutlines;
25527		$this->BMoutlines = [];
25528		$lastlevel = -1;
25529		for ($i = 0; $i < count($bmo); $i++) {
25530			if ($bmo[$i]['l'] > 0) {
25531				while ($bmo[$i]['l'] - $lastlevel > 1) { // If jump down more than one level, insert a new entry
25532					$new = $bmo[$i];
25533					$new['t'] = "[" . $new['t'] . "]"; // Put [] around text/title to highlight
25534					$new['l'] = $lastlevel + 1;
25535					$lastlevel++;
25536					$this->BMoutlines[] = $new;
25537				}
25538			}
25539			$this->BMoutlines[] = $bmo[$i];
25540			$lastlevel = $bmo[$i]['l'];
25541		}
25542		$nb = count($this->BMoutlines);
25543
25544		$lru = [];
25545		$level = 0;
25546		foreach ($this->BMoutlines as $i => $o) {
25547			if ($o['l'] > 0) {
25548				$parent = $lru[$o['l'] - 1];
25549				// Set parent and last pointers
25550				$this->BMoutlines[$i]['parent'] = $parent;
25551				$this->BMoutlines[$parent]['last'] = $i;
25552				if ($o['l'] > $level) {
25553					// Level increasing: set first pointer
25554					$this->BMoutlines[$parent]['first'] = $i;
25555				}
25556			} else {
25557				$this->BMoutlines[$i]['parent'] = $nb;
25558			}
25559			if ($o['l'] <= $level and $i > 0) {
25560				// Set prev and next pointers
25561				$prev = $lru[$o['l']];
25562				$this->BMoutlines[$prev]['next'] = $i;
25563				$this->BMoutlines[$i]['prev'] = $prev;
25564			}
25565			$lru[$o['l']] = $i;
25566			$level = $o['l'];
25567		}
25568
25569
25570		// Outline items
25571		$n = $this->n + 1;
25572		foreach ($this->BMoutlines as $i => $o) {
25573			$this->_newobj();
25574			$this->_out('<</Title ' . $this->_UTF16BEtextstring($o['t']));
25575			$this->_out('/Parent ' . ($n + $o['parent']) . ' 0 R');
25576			if (isset($o['prev'])) {
25577				$this->_out('/Prev ' . ($n + $o['prev']) . ' 0 R');
25578			}
25579			if (isset($o['next'])) {
25580				$this->_out('/Next ' . ($n + $o['next']) . ' 0 R');
25581			}
25582			if (isset($o['first'])) {
25583				$this->_out('/First ' . ($n + $o['first']) . ' 0 R');
25584			}
25585			if (isset($o['last'])) {
25586				$this->_out('/Last ' . ($n + $o['last']) . ' 0 R');
25587			}
25588
25589
25590			if (isset($this->pageDim[$o['p']]['h'])) {
25591				$h = $this->pageDim[$o['p']]['h'];
25592			} else {
25593				$h = 0;
25594			}
25595
25596			$this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.3F null]', 1 + 2 * ($o['p']), ($h - $o['y']) * Mpdf::SCALE));
25597			if (isset($this->bookmarkStyles) && isset($this->bookmarkStyles[$o['l']])) {
25598				// font style
25599				$bms = $this->bookmarkStyles[$o['l']]['style'];
25600				$style = 0;
25601				if (strpos($bms, 'B') !== false) {
25602					$style += 2;
25603				}
25604				if (strpos($bms, 'I') !== false) {
25605					$style += 1;
25606				}
25607				$this->_out(sprintf('/F %d', $style));
25608				// Colour
25609				$col = $this->bookmarkStyles[$o['l']]['color'];
25610				if (isset($col) && is_array($col) && count($col) == 3) {
25611					$this->_out(sprintf('/C [%.3F %.3F %.3F]', ($col[0] / 255), ($col[1] / 255), ($col[2] / 255)));
25612				}
25613			}
25614
25615			$this->_out('/Count 0>>');
25616			$this->_out('endobj');
25617		}
25618		// Outline root
25619		$this->_newobj();
25620		$this->OutlineRoot = $this->n;
25621		$this->_out('<</Type /BMoutlines /First ' . $n . ' 0 R');
25622		$this->_out('/Last ' . ($n + $lru[0]) . ' 0 R>>');
25623		$this->_out('endobj');
25624	}
25625
25626	/* -- END BOOKMARKS -- */
25627
25628	/**
25629	 * Initiate, and Mark a place for the Table of Contents to be inserted
25630	 */
25631	function TOC(
25632		$tocfont = '',
25633		$tocfontsize = 0,
25634		$tocindent = 0,
25635		$resetpagenum = '',
25636		$pagenumstyle = '',
25637		$suppress = '',
25638		$toc_orientation = '',
25639		$TOCusePaging = true,
25640		$TOCuseLinking = false,
25641		$toc_id = 0,
25642		$tocoutdent = ''
25643	) {
25644
25645		$this->tableOfContents->TOC(
25646			$tocfont,
25647			$tocfontsize,
25648			$tocindent,
25649			$resetpagenum,
25650			$pagenumstyle,
25651			$suppress,
25652			$toc_orientation,
25653			$TOCusePaging,
25654			$TOCuseLinking,
25655			$toc_id,
25656			$tocoutdent
25657		);
25658	}
25659
25660	function TOCpagebreakByArray($a)
25661	{
25662		if (!is_array($a)) {
25663			$a = [];
25664		}
25665		$tocoutdent = (isset($a['tocoutdent']) ? $a['tocoutdent'] : (isset($a['outdent']) ? $a['outdent'] : ''));
25666		$TOCusePaging = (isset($a['TOCusePaging']) ? $a['TOCusePaging'] : (isset($a['paging']) ? $a['paging'] : true));
25667		$TOCuseLinking = (isset($a['TOCuseLinking']) ? $a['TOCuseLinking'] : (isset($a['links']) ? $a['links'] : ''));
25668		$toc_orientation = (isset($a['toc_orientation']) ? $a['toc_orientation'] : (isset($a['toc-orientation']) ? $a['toc-orientation'] : ''));
25669		$toc_mgl = (isset($a['toc_mgl']) ? $a['toc_mgl'] : (isset($a['toc-margin-left']) ? $a['toc-margin-left'] : ''));
25670		$toc_mgr = (isset($a['toc_mgr']) ? $a['toc_mgr'] : (isset($a['toc-margin-right']) ? $a['toc-margin-right'] : ''));
25671		$toc_mgt = (isset($a['toc_mgt']) ? $a['toc_mgt'] : (isset($a['toc-margin-top']) ? $a['toc-margin-top'] : ''));
25672		$toc_mgb = (isset($a['toc_mgb']) ? $a['toc_mgb'] : (isset($a['toc-margin-bottom']) ? $a['toc-margin-bottom'] : ''));
25673		$toc_mgh = (isset($a['toc_mgh']) ? $a['toc_mgh'] : (isset($a['toc-margin-header']) ? $a['toc-margin-header'] : ''));
25674		$toc_mgf = (isset($a['toc_mgf']) ? $a['toc_mgf'] : (isset($a['toc-margin-footer']) ? $a['toc-margin-footer'] : ''));
25675		$toc_ohname = (isset($a['toc_ohname']) ? $a['toc_ohname'] : (isset($a['toc-odd-header-name']) ? $a['toc-odd-header-name'] : ''));
25676		$toc_ehname = (isset($a['toc_ehname']) ? $a['toc_ehname'] : (isset($a['toc-even-header-name']) ? $a['toc-even-header-name'] : ''));
25677		$toc_ofname = (isset($a['toc_ofname']) ? $a['toc_ofname'] : (isset($a['toc-odd-footer-name']) ? $a['toc-odd-footer-name'] : ''));
25678		$toc_efname = (isset($a['toc_efname']) ? $a['toc_efname'] : (isset($a['toc-even-footer-name']) ? $a['toc-even-footer-name'] : ''));
25679		$toc_ohvalue = (isset($a['toc_ohvalue']) ? $a['toc_ohvalue'] : (isset($a['toc-odd-header-value']) ? $a['toc-odd-header-value'] : 0));
25680		$toc_ehvalue = (isset($a['toc_ehvalue']) ? $a['toc_ehvalue'] : (isset($a['toc-even-header-value']) ? $a['toc-even-header-value'] : 0));
25681		$toc_ofvalue = (isset($a['toc_ofvalue']) ? $a['toc_ofvalue'] : (isset($a['toc-odd-footer-value']) ? $a['toc-odd-footer-value'] : 0));
25682		$toc_efvalue = (isset($a['toc_efvalue']) ? $a['toc_efvalue'] : (isset($a['toc-even-footer-value']) ? $a['toc-even-footer-value'] : 0));
25683		$toc_preHTML = (isset($a['toc_preHTML']) ? $a['toc_preHTML'] : (isset($a['toc-preHTML']) ? $a['toc-preHTML'] : ''));
25684		$toc_postHTML = (isset($a['toc_postHTML']) ? $a['toc_postHTML'] : (isset($a['toc-postHTML']) ? $a['toc-postHTML'] : ''));
25685		$toc_bookmarkText = (isset($a['toc_bookmarkText']) ? $a['toc_bookmarkText'] : (isset($a['toc-bookmarkText']) ? $a['toc-bookmarkText'] : ''));
25686		$resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');
25687		$pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');
25688		$suppress = (isset($a['suppress']) ? $a['suppress'] : '');
25689		$orientation = (isset($a['orientation']) ? $a['orientation'] : '');
25690		$mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));
25691		$mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));
25692		$mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));
25693		$mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));
25694		$mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));
25695		$mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));
25696		$ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));
25697		$ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));
25698		$ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));
25699		$efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));
25700		$ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));
25701		$ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));
25702		$ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));
25703		$efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));
25704		$toc_id = (isset($a['toc_id']) ? $a['toc_id'] : (isset($a['name']) ? $a['name'] : 0));
25705		$pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));
25706		$toc_pagesel = (isset($a['toc_pagesel']) ? $a['toc_pagesel'] : (isset($a['toc-pageselector']) ? $a['toc-pageselector'] : ''));
25707		$sheetsize = (isset($a['sheetsize']) ? $a['sheetsize'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));
25708		$toc_sheetsize = (isset($a['toc_sheetsize']) ? $a['toc_sheetsize'] : (isset($a['toc-sheet-size']) ? $a['toc-sheet-size'] : ''));
25709
25710		$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);
25711	}
25712
25713	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 = '')
25714	{
25715		// Start a new page
25716		if ($this->state == 0) {
25717			$this->AddPage();
25718		}
25719		if ($this->y == $this->tMargin && (!$this->mirrorMargins || ($this->mirrorMargins && $this->page % 2 == 1))) {
25720			// Don't add a page
25721			if ($this->page == 1 && count($this->PageNumSubstitutions) == 0) {
25722				if (!$suppress) {
25723					$suppress = 'off';
25724				}
25725				// $this->PageNumSubstitutions[] = array('from'=>1, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=> $suppress);
25726			}
25727			$this->PageNumSubstitutions[] = ['from' => $this->page, 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];
25728		} else {
25729			$this->AddPage($orientation, 'NEXT-ODD', $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $sheetsize);
25730		}
25731		$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);
25732	}
25733
25734	function TOC_Entry($txt, $level = 0, $toc_id = 0)
25735	{
25736		if ($this->ColActive) {
25737			$ily = $this->y0;
25738		} else {
25739			$ily = $this->y;
25740		} // use top of columns
25741
25742		$linkn = $this->AddLink();
25743		$uid = '__mpdfinternallink_' . $linkn;
25744		if ($this->table_rotate) {
25745			$this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "tbrot" => true];
25746		} elseif ($this->kwt) {
25747			$this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "kwt" => true];
25748		} elseif ($this->ColActive) {
25749			$this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol];
25750		} elseif (!$this->keep_block_together) {
25751			$this->internallink[$uid] = ["Y" => $ily, "PAGE" => $this->page];
25752		}
25753		$this->internallink['#' . $uid] = $linkn;
25754		$this->SetLink($linkn, $ily, $this->page);
25755
25756		if (strtoupper($toc_id) == 'ALL') {
25757			$toc_id = '_mpdf_all';
25758		} elseif (!$toc_id) {
25759			$toc_id = 0;
25760		} else {
25761			$toc_id = strtolower($toc_id);
25762		}
25763		$btoc = ['t' => $txt, 'l' => $level, 'p' => $this->page, 'link' => $linkn, 'toc_id' => $toc_id];
25764		if ($this->keep_block_together) {
25765			// do nothing
25766		} /* -- TABLES -- */ elseif ($this->table_rotate) {
25767			$this->tbrot_toc[] = $btoc;
25768		} elseif ($this->kwt) {
25769			$this->kwt_toc[] = $btoc;
25770		} /* -- END TABLES -- */ elseif ($this->ColActive) {  // *COLUMNS*
25771			$this->col_toc[] = $btoc; // *COLUMNS*
25772		} // *COLUMNS*
25773		else {
25774			$this->tableOfContents->_toc[] = $btoc;
25775		}
25776	}
25777
25778	/* -- END TOC -- */
25779
25780	// ======================================================
25781	function MovePages($target_page, $start_page, $end_page = -1)
25782	{
25783		// move a page/pages EARLIER in the document
25784		if ($end_page < 1) {
25785			$end_page = $start_page;
25786		}
25787		$n_toc = $end_page - $start_page + 1;
25788
25789		// Set/Update PageNumSubstitutions changes before moving anything
25790		if (count($this->PageNumSubstitutions)) {
25791			$tp_present = false;
25792			$sp_present = false;
25793			$ep_present = false;
25794			foreach ($this->PageNumSubstitutions as $k => $v) {
25795				if ($this->PageNumSubstitutions[$k]['from'] == $target_page) {
25796					$tp_present = true;
25797					if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
25798						$this->PageNumSubstitutions[$k]['suppress'] = 'off';
25799					}
25800				}
25801				if ($this->PageNumSubstitutions[$k]['from'] == $start_page) {
25802					$sp_present = true;
25803					if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
25804						$this->PageNumSubstitutions[$k]['suppress'] = 'off';
25805					}
25806				}
25807				if ($this->PageNumSubstitutions[$k]['from'] == ($end_page + 1)) {
25808					$ep_present = true;
25809					if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
25810						$this->PageNumSubstitutions[$k]['suppress'] = 'off';
25811					}
25812				}
25813			}
25814
25815			if (!$tp_present) {
25816				list($tp_type, $tp_suppress, $tp_reset) = $this->docPageSettings($target_page);
25817			}
25818			if (!$sp_present) {
25819				list($sp_type, $sp_suppress, $sp_reset) = $this->docPageSettings($start_page);
25820			}
25821			if (!$ep_present) {
25822				list($ep_type, $ep_suppress, $ep_reset) = $this->docPageSettings($start_page - 1);
25823			}
25824		}
25825
25826		$last = [];
25827		// store pages
25828		for ($i = $start_page; $i <= $end_page; $i++) {
25829			$last[] = $this->pages[$i];
25830		}
25831		// move pages
25832		for ($i = $start_page - 1; $i >= ($target_page); $i--) {
25833			$this->pages[$i + $n_toc] = $this->pages[$i];
25834		}
25835		// Put toc pages at insert point
25836		for ($i = 0; $i < $n_toc; $i++) {
25837			$this->pages[$target_page + $i] = $last[$i];
25838		}
25839
25840		/* -- BOOKMARKS -- */
25841		// Update Bookmarks
25842		foreach ($this->BMoutlines as $i => $o) {
25843			if ($o['p'] >= $target_page) {
25844				$this->BMoutlines[$i]['p'] += $n_toc;
25845			}
25846		}
25847		/* -- END BOOKMARKS -- */
25848
25849		// Update Page Links
25850		if (count($this->PageLinks)) {
25851			$newarr = [];
25852			foreach ($this->PageLinks as $i => $o) {
25853				foreach ($this->PageLinks[$i] as $key => $pl) {
25854					if (strpos($pl[4], '@') === 0) {
25855						$p = substr($pl[4], 1);
25856						if ($p >= $start_page && $p <= $end_page) {
25857							$this->PageLinks[$i][$key][4] = '@' . ($p + ($target_page - $start_page));
25858						} elseif ($p >= $target_page && $p < $start_page) {
25859							$this->PageLinks[$i][$key][4] = '@' . ($p + $n_toc);
25860						}
25861					}
25862				}
25863				if ($i >= $start_page && $i <= $end_page) {
25864					$newarr[($i + ($target_page - $start_page))] = $this->PageLinks[$i];
25865				} elseif ($i >= $target_page && $i < $start_page) {
25866					$newarr[($i + $n_toc)] = $this->PageLinks[$i];
25867				} else {
25868					$newarr[$i] = $this->PageLinks[$i];
25869				}
25870			}
25871			$this->PageLinks = $newarr;
25872		}
25873
25874		// OrientationChanges
25875		if (count($this->OrientationChanges)) {
25876			$newarr = [];
25877			foreach ($this->OrientationChanges as $p => $v) {
25878				if ($p >= $start_page && $p <= $end_page) {
25879					$newarr[($p + ($target_page - $start_page))] = $this->OrientationChanges[$p];
25880				} elseif ($p >= $target_page && $p < $start_page) {
25881					$newarr[$p + $n_toc] = $this->OrientationChanges[$p];
25882				} else {
25883					$newarr[$p] = $this->OrientationChanges[$p];
25884				}
25885			}
25886			ksort($newarr);
25887			$this->OrientationChanges = $newarr;
25888		}
25889
25890		// Page Dimensions
25891		if (count($this->pageDim)) {
25892			$newarr = [];
25893			foreach ($this->pageDim as $p => $v) {
25894				if ($p >= $start_page && $p <= $end_page) {
25895					$newarr[($p + ($target_page - $start_page))] = $this->pageDim[$p];
25896				} elseif ($p >= $target_page && $p < $start_page) {
25897					$newarr[$p + $n_toc] = $this->pageDim[$p];
25898				} else {
25899					$newarr[$p] = $this->pageDim[$p];
25900				}
25901			}
25902			ksort($newarr);
25903			$this->pageDim = $newarr;
25904		}
25905
25906		// HTML Headers & Footers
25907		if (count($this->saveHTMLHeader)) {
25908			$newarr = [];
25909			foreach ($this->saveHTMLHeader as $p => $v) {
25910				if ($p >= $start_page && $p <= $end_page) {
25911					$newarr[($p + ($target_page - $start_page))] = $this->saveHTMLHeader[$p];
25912				} elseif ($p >= $target_page && $p < $start_page) {
25913					$newarr[$p + $n_toc] = $this->saveHTMLHeader[$p];
25914				} else {
25915					$newarr[$p] = $this->saveHTMLHeader[$p];
25916				}
25917			}
25918			ksort($newarr);
25919			$this->saveHTMLHeader = $newarr;
25920		}
25921		if (count($this->saveHTMLFooter)) {
25922			$newarr = [];
25923			foreach ($this->saveHTMLFooter as $p => $v) {
25924				if ($p >= $start_page && $p <= $end_page) {
25925					$newarr[($p + ($target_page - $start_page))] = $this->saveHTMLFooter[$p];
25926				} elseif ($p >= $target_page && $p < $start_page) {
25927					$newarr[$p + $n_toc] = $this->saveHTMLFooter[$p];
25928				} else {
25929					$newarr[$p] = $this->saveHTMLFooter[$p];
25930				}
25931			}
25932			ksort($newarr);
25933			$this->saveHTMLFooter = $newarr;
25934		}
25935
25936		// Update Internal Links
25937		if (count($this->internallink)) {
25938			foreach ($this->internallink as $key => $o) {
25939				if ($o['PAGE'] >= $start_page && $o['PAGE'] <= $end_page) {
25940					$this->internallink[$key]['PAGE'] += ($target_page - $start_page);
25941				} elseif ($o['PAGE'] >= $target_page && $o['PAGE'] < $start_page) {
25942					$this->internallink[$key]['PAGE'] += $n_toc;
25943				}
25944			}
25945		}
25946
25947		// Update Links
25948		if (count($this->links)) {
25949			foreach ($this->links as $key => $o) {
25950				if ($o[0] >= $start_page && $o[0] <= $end_page) {
25951					$this->links[$key][0] += ($target_page - $start_page);
25952				}
25953				if ($o[0] >= $target_page && $o[0] < $start_page) {
25954					$this->links[$key][0] += $n_toc;
25955				}
25956			}
25957		}
25958
25959		// Update Form fields
25960		if (count($this->form->forms)) {
25961			foreach ($this->form->forms as $key => $f) {
25962				if ($f['page'] >= $start_page && $f['page'] <= $end_page) {
25963					$this->form->forms[$key]['page'] += ($target_page - $start_page);
25964				}
25965				if ($f['page'] >= $target_page && $f['page'] < $start_page) {
25966					$this->form->forms[$key]['page'] += $n_toc;
25967				}
25968			}
25969		}
25970
25971		/* -- ANNOTATIONS -- */
25972		// Update Annotations
25973		if (count($this->PageAnnots)) {
25974			$newarr = [];
25975			foreach ($this->PageAnnots as $p => $anno) {
25976				if ($p >= $start_page && $p <= $end_page) {
25977					$np = $p + ($target_page - $start_page);
25978					foreach ($anno as $o) {
25979						$newarr[$np][] = $o;
25980					}
25981				} elseif ($p >= $target_page && $p < $start_page) {
25982					$np = $p + $n_toc;
25983					foreach ($anno as $o) {
25984						$newarr[$np][] = $o;
25985					}
25986				} else {
25987					$newarr[$p] = $this->PageAnnots[$p];
25988				}
25989			}
25990			$this->PageAnnots = $newarr;
25991			unset($newarr);
25992		}
25993		/* -- END ANNOTATIONS -- */
25994
25995		// Update TOC pages
25996		if (count($this->tableOfContents->_toc)) {
25997			foreach ($this->tableOfContents->_toc as $key => $t) {
25998				if ($t['p'] >= $start_page && $t['p'] <= $end_page) {
25999					$this->tableOfContents->_toc[$key]['p'] += ($target_page - $start_page);
26000				}
26001				if ($t['p'] >= $target_page && $t['p'] < $start_page) {
26002					$this->tableOfContents->_toc[$key]['p'] += $n_toc;
26003				}
26004			}
26005		}
26006
26007		// Update PageNumSubstitutions
26008		if (count($this->PageNumSubstitutions)) {
26009			$newarr = [];
26010			foreach ($this->PageNumSubstitutions as $k => $v) {
26011				if ($this->PageNumSubstitutions[$k]['from'] >= $start_page && $this->PageNumSubstitutions[$k]['from'] <= $end_page) {
26012					$this->PageNumSubstitutions[$k]['from'] += ($target_page - $start_page);
26013					$newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
26014				} elseif ($this->PageNumSubstitutions[$k]['from'] >= $target_page && $this->PageNumSubstitutions[$k]['from'] < $start_page) {
26015					$this->PageNumSubstitutions[$k]['from'] += $n_toc;
26016					$newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
26017				} else {
26018					$newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
26019				}
26020			}
26021
26022			if (!$sp_present) {
26023				$newarr[$target_page] = ['from' => $target_page, 'suppress' => $sp_suppress, 'reset' => $sp_reset, 'type' => $sp_type];
26024			}
26025			if (!$tp_present) {
26026				$newarr[($target_page + $n_toc)] = ['from' => ($target_page + $n_toc), 'suppress' => $tp_suppress, 'reset' => $tp_reset, 'type' => $tp_type];
26027			}
26028			if (!$ep_present && $end_page > count($this->pages)) {
26029				$newarr[($end_page + 1)] = ['from' => ($end_page + 1), 'suppress' => $ep_suppress, 'reset' => $ep_reset, 'type' => $ep_type];
26030			}
26031			ksort($newarr);
26032			$this->PageNumSubstitutions = [];
26033			foreach ($newarr as $v) {
26034				$this->PageNumSubstitutions[] = $v;
26035			}
26036		}
26037	}
26038
26039	function DeletePages($start_page, $end_page = -1)
26040	{
26041		// move a page/pages EARLIER in the document
26042		if ($end_page < 1) {
26043			$end_page = $start_page;
26044		}
26045		$n_tod = $end_page - $start_page + 1;
26046		$last_page = count($this->pages);
26047		$n_atend = $last_page - $end_page + 1;
26048
26049		// move pages
26050		for ($i = 0; $i < $n_atend; $i++) {
26051			$this->pages[$start_page + $i] = $this->pages[$end_page + 1 + $i];
26052		}
26053		// delete pages
26054		for ($i = 0; $i < $n_tod; $i++) {
26055			unset($this->pages[$last_page - $i]);
26056		}
26057
26058
26059		/* -- BOOKMARKS -- */
26060		// Update Bookmarks
26061		foreach ($this->BMoutlines as $i => $o) {
26062			if ($o['p'] >= $end_page) {
26063				$this->BMoutlines[$i]['p'] -= $n_tod;
26064			} elseif ($p < $start_page) {
26065				unset($this->BMoutlines[$i]);
26066			}
26067		}
26068		/* -- END BOOKMARKS -- */
26069
26070		// Update Page Links
26071		if (count($this->PageLinks)) {
26072			$newarr = [];
26073			foreach ($this->PageLinks as $i => $o) {
26074				foreach ($this->PageLinks[$i] as $key => $pl) {
26075					if (strpos($pl[4], '@') === 0) {
26076						$p = substr($pl[4], 1);
26077						if ($p > $end_page) {
26078							$this->PageLinks[$i][$key][4] = '@' . ($p - $n_tod);
26079						} elseif ($p < $start_page) {
26080							unset($this->PageLinks[$i][$key]);
26081						}
26082					}
26083				}
26084				if ($i > $end_page) {
26085					$newarr[($i - $n_tod)] = $this->PageLinks[$i];
26086				} elseif ($p < $start_page) {
26087					$newarr[$i] = $this->PageLinks[$i];
26088				}
26089			}
26090			$this->PageLinks = $newarr;
26091		}
26092
26093		// OrientationChanges
26094		if (count($this->OrientationChanges)) {
26095			$newarr = [];
26096			foreach ($this->OrientationChanges as $p => $v) {
26097				if ($p > $end_page) {
26098					$newarr[($p - $t_tod)] = $this->OrientationChanges[$p];
26099				} elseif ($p < $start_page) {
26100					$newarr[$p] = $this->OrientationChanges[$p];
26101				}
26102			}
26103			ksort($newarr);
26104			$this->OrientationChanges = $newarr;
26105		}
26106
26107		// Page Dimensions
26108		if (count($this->pageDim)) {
26109			$newarr = [];
26110			foreach ($this->pageDim as $p => $v) {
26111				if ($p > $end_page) {
26112					$newarr[($p - $n_tod)] = $this->pageDim[$p];
26113				} elseif ($p < $start_page) {
26114					$newarr[$p] = $this->pageDim[$p];
26115				}
26116			}
26117			ksort($newarr);
26118			$this->pageDim = $newarr;
26119		}
26120
26121		// HTML Headers & Footers
26122		if (count($this->saveHTMLHeader)) {
26123			foreach ($this->saveHTMLHeader as $p => $v) {
26124				if ($p > $end_page) {
26125					$newarr[($p - $n_tod)] = $this->saveHTMLHeader[$p];
26126				} // mPDF 5.7.3
26127				elseif ($p < $start_page) {
26128					$newarr[$p] = $this->saveHTMLHeader[$p];
26129				}
26130			}
26131			ksort($newarr);
26132			$this->saveHTMLHeader = $newarr;
26133		}
26134		if (count($this->saveHTMLFooter)) {
26135			$newarr = [];
26136			foreach ($this->saveHTMLFooter as $p => $v) {
26137				if ($p > $end_page) {
26138					$newarr[($p - $n_tod)] = $this->saveHTMLFooter[$p];
26139				} elseif ($p < $start_page) {
26140					$newarr[$p] = $this->saveHTMLFooter[$p];
26141				}
26142			}
26143			ksort($newarr);
26144			$this->saveHTMLFooter = $newarr;
26145		}
26146
26147		// Update Internal Links
26148		foreach ($this->internallink as $key => $o) {
26149			if ($o['PAGE'] > $end_page) {
26150				$this->internallink[$key]['PAGE'] -= $n_tod;
26151			} elseif ($o['PAGE'] < $start_page) {
26152				unset($this->internallink[$key]);
26153			}
26154		}
26155
26156		// Update Links
26157		foreach ($this->links as $key => $o) {
26158			if ($o[0] > $end_page) {
26159				$this->links[$key][0] -= $n_tod;
26160			} elseif ($o[0] < $start_page) {
26161				unset($this->links[$key]);
26162			}
26163		}
26164
26165		// Update Form fields
26166		foreach ($this->form->forms as $key => $f) {
26167			if ($f['page'] > $end_page) {
26168				$this->form->forms[$key]['page'] -= $n_tod;
26169			} elseif ($f['page'] < $start_page) {
26170				unset($this->form->forms[$key]);
26171			}
26172		}
26173
26174		/* -- ANNOTATIONS -- */
26175		// Update Annotations
26176		if (count($this->PageAnnots)) {
26177			$newarr = [];
26178			foreach ($this->PageAnnots as $p => $anno) {
26179				if ($p > $end_page) {
26180					foreach ($anno as $o) {
26181						$newarr[($p - $n_tod)][] = $o;
26182					}
26183				} elseif ($p < $start_page) {
26184					$newarr[$p] = $this->PageAnnots[$p];
26185				}
26186			}
26187			ksort($newarr);
26188			$this->PageAnnots = $newarr;
26189		}
26190		/* -- END ANNOTATIONS -- */
26191
26192		// Update PageNumSubstitutions
26193		foreach ($this->PageNumSubstitutions as $k => $v) {
26194			if ($this->PageNumSubstitutions[$k]['from'] > $end_page) {
26195				$this->PageNumSubstitutions[$k]['from'] -= $n_tod;
26196			} elseif ($this->PageNumSubstitutions[$k]['from'] < $start_page) {
26197				unset($this->PageNumSubstitutions[$k]);
26198			}
26199		}
26200
26201		unset($newarr);
26202		$this->page = count($this->pages);
26203	}
26204
26205	// ======================================================
26206		/* -- INDEX -- */
26207	// FROM class PDF_Ref == INDEX
26208
26209	function IndexEntry($txt, $xref = '')
26210	{
26211		if ($xref) {
26212			$this->IndexEntrySee($txt, $xref);
26213			return;
26214		}
26215
26216		// Search the reference (AND Ref/PageNo) in the array
26217		$Present = false;
26218		if ($this->keep_block_together) {
26219			// do nothing
26220		} /* -- TABLES -- */ elseif ($this->kwt) {
26221			$size = count($this->kwt_Reference);
26222			for ($i = 0; $i < $size; $i++) {
26223				if (isset($this->kwt_Reference[$i]['t']) && $this->kwt_Reference[$i]['t'] == $txt) {
26224					$Present = true;
26225					if ($this->page != $this->kwt_Reference[$i]['op']) {
26226						$this->kwt_Reference[$i]['op'] = $this->page;
26227					}
26228				}
26229			}
26230			if (!$Present) { // If not found, add it
26231				$this->kwt_Reference[] = ['t' => $txt, 'op' => $this->page];
26232			}
26233		} /* -- END TABLES -- */ else {
26234			$size = count($this->Reference);
26235			for ($i = 0; $i < $size; $i++) {
26236				if (isset($this->Reference[$i]['t']) && $this->Reference[$i]['t'] == $txt) {
26237					$Present = true;
26238					if (!in_array($this->page, $this->Reference[$i]['p'])) {
26239						$this->Reference[$i]['p'][] = $this->page;
26240					}
26241				}
26242			}
26243			if (!$Present) { // If not found, add it
26244				$this->Reference[] = ['t' => $txt, 'p' => [$this->page]];
26245			}
26246		}
26247	}
26248
26249	// Added function to add a reference "Elephants. See Chickens"
26250	function IndexEntrySee($txta, $txtb)
26251	{
26252		if ($this->directionality == 'rtl') { // *OTL*
26253			// ONLY DO THIS IF NOT IN TAGS
26254			if ($txta == strip_tags($txta)) {
26255				$txta = str_replace(':', ' - ', $txta); // *OTL*
26256			}
26257			if ($txtb == strip_tags($txtb)) {
26258				$txtb = str_replace(':', ' - ', $txtb); // *OTL*
26259			}
26260		} // *OTL*
26261		else { // *OTL*
26262			if ($txta == strip_tags($txta)) {
26263				$txta = str_replace(':', ', ', $txta);
26264			}
26265			if ($txtb == strip_tags($txtb)) {
26266				$txtb = str_replace(':', ', ', $txtb);
26267			}
26268		} // *OTL*
26269		$this->Reference[] = ['t' => $txta . ' - see ' . $txtb, 'p' => []];
26270	}
26271
26272	function InsertIndex($usedivletters = 1, $useLinking = false, $indexCollationLocale = '', $indexCollationGroup = '')
26273	{
26274		$size = count($this->Reference);
26275		if ($size == 0) {
26276			return false;
26277		}
26278
26279		// $spacer used after named entry
26280		// $sep  separates number [groups], $joiner joins numbers in range
26281		//  e.g. "elephant 73, 97-99"  =  elephant[$spacer]73[$sep]97[$joiner]99
26282		// $subEntrySeparator separates main and subentry (if $this->indexUseSubentries == false;) e.g.
26283		// Mammal:elephant => Mammal[$subEntrySeparator]elephant
26284		// $subEntryInset specifies what precedes a subentry (if $this->indexUseSubentries == true;) e.g.
26285		// Mammal:elephant => [$subEntryInset]elephant
26286		$spacer = "\xc2\xa0 ";
26287		if ($this->directionality == 'rtl') {
26288			$sep = '&#x060c; ';
26289			$joiner = '-';
26290			$subEntrySeparator = '&#x060c; ';
26291			$subEntryInset = ' - ';
26292		} else {
26293			$sep = ', ';
26294			$joiner = '-';
26295			$subEntrySeparator = ', ';
26296			$subEntryInset = ' - ';
26297		}
26298
26299		for ($i = 0; $i < $size; $i++) {
26300			$txt = $this->Reference[$i]['t'];
26301			$txt = strip_tags($txt); // mPDF 6
26302			$txt = $this->purify_utf8($txt);
26303			$this->Reference[$i]['uf'] = $txt; // Unformatted e.g. pure utf-8 encoded characters, no mark-up/tags
26304			// Used for ordering and collation
26305		}
26306
26307		if ($usedivletters) {
26308			if ($indexCollationGroup) {
26309				$collation = require __DIR__ . '/../data/collations/' . $indexCollationGroup . '.php';
26310			} else {
26311				$collation = [];
26312			}
26313			for ($i = 0; $i < $size; $i++) {
26314				if ($this->Reference[$i]['uf']) {
26315					$l = mb_substr($this->Reference[$i]['uf'], 0, 1, 'UTF-8');
26316					if (isset($indexCollationGroup) && $indexCollationGroup) {
26317						$uni = $this->UTF8StringToArray($l);
26318						$ucode = $uni[0];
26319						if (isset($collation[$ucode])) {
26320							$this->Reference[$i]['d'] = UtfString::code2utf($collation[$ucode]);
26321						} else {
26322							$this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');
26323						}
26324					} else {
26325						$this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');
26326					}
26327				}
26328			}
26329		}
26330
26331		// Alphabetic sort of the references
26332		$originalLocale = setlocale(LC_COLLATE, 0);
26333		if ($indexCollationLocale) {
26334			setlocale(LC_COLLATE, $indexCollationLocale);
26335		}
26336
26337		usort($this->Reference, function ($a, $b) {
26338			return strcoll(strtolower($a['uf']), strtolower($b['uf']));
26339		});
26340
26341		if ($indexCollationLocale) {
26342			setlocale(LC_COLLATE, $originalLocale);
26343		}
26344
26345		$html = '<div class="mpdf_index_main">';
26346
26347		$lett = '';
26348		$last_lett = '';
26349		$mainentry = '';
26350		for ($i = 0; $i < $size; $i++) {
26351			if ($this->Reference[$i]['t']) {
26352				if ($usedivletters) {
26353					$lett = $this->Reference[$i]['d'];
26354					if ($lett != $last_lett) {
26355						$html .= '<div class="mpdf_index_letter">' . $lett . '</div>';
26356					}
26357				}
26358				$txt = $this->Reference[$i]['t'];
26359
26360				// Sub-entries e.g. Mammals:elephant
26361				// But allow for tags e.g. <b>Mammal</b>:elephants
26362				$a = preg_split('/(<.*?>)/', $txt, -1, PREG_SPLIT_DELIM_CAPTURE);
26363				$txt = '';
26364				$marker = false;
26365				foreach ($a as $k => $e) {
26366					if ($k % 2 == 0 && !$marker) {
26367						if (strpos($e, ':') !== false) { // == SubEntry
26368							if ($this->indexUseSubentries) {
26369								// If the Main entry does not have any page numbers associated with it
26370								// create and insert an entry
26371								list($txtmain, $sub) = preg_split('/[:]/', $e, 2);
26372								if (strip_tags($txt . $txtmain) != $mainentry) {
26373									$html .= '<div class="mpdf_index_entry">' . $txt . $txtmain . '</div>';
26374									$mainentry = strip_tags($txt . $txtmain);
26375								}
26376
26377								$txt = $subEntryInset;
26378								$e = $sub; // Only replace first one
26379							} else {
26380								$e = preg_replace('/[:]/', $subEntrySeparator, $e, 1); // Only replace first one
26381							}
26382							$marker = true; // Don't replace any more once the subentry marker has been found
26383						}
26384					}
26385					$txt .= $e;
26386				}
26387
26388				if (!$marker) {
26389					$mainentry = strip_tags($txt);
26390				}
26391
26392				$html .= '<div class="mpdf_index_entry">';
26393				$html .= $txt;
26394				$ppp = $this->Reference[$i]['p']; // = array of page numbers to point to
26395				if (count($ppp)) {
26396					sort($ppp);
26397					$newarr = [];
26398					$range_start = $ppp[0];
26399					$range_end = 0;
26400
26401					$html .= $spacer;
26402
26403					for ($zi = 1; $zi < count($ppp); $zi++) {
26404						if ($ppp[$zi] == ($ppp[($zi - 1)] + 1)) {
26405							$range_end = $ppp[$zi];
26406						} else {
26407							if ($range_end) {
26408								if ($range_end == $range_start + 1) {
26409									if ($useLinking) {
26410										$html .= '<a class="mpdf_index_link" href="@' . $range_start . '">';
26411									}
26412									$html .= $this->docPageNum($range_start);
26413									if ($useLinking) {
26414										$html .= '</a>';
26415									}
26416									$html .= $sep;
26417
26418									if ($useLinking) {
26419										$html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">';
26420									}
26421									$html .= $this->docPageNum($ppp[$zi - 1]);
26422									if ($useLinking) {
26423										$html .= '</a>';
26424									}
26425									$html .= $sep;
26426								}
26427							} else {
26428								if ($useLinking) {
26429									$html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">';
26430								}
26431								$html .= $this->docPageNum($ppp[$zi - 1]);
26432								if ($useLinking) {
26433									$html .= '</a>';
26434								}
26435								$html .= $sep;
26436							}
26437							$range_start = $ppp[$zi];
26438							$range_end = 0;
26439						}
26440					}
26441
26442					if ($range_end) {
26443						if ($useLinking) {
26444							$html .= '<a class="mpdf_index_link" href="@' . $range_start . '">';
26445						}
26446						$html .= $this->docPageNum($range_start);
26447						if ($range_end == $range_start + 1) {
26448							if ($useLinking) {
26449								$html .= '</a>';
26450							}
26451							$html .= $sep;
26452							if ($useLinking) {
26453								$html .= '<a class="mpdf_index_link" href="@' . $range_end . '">';
26454							}
26455							$html .= $this->docPageNum($range_end);
26456							if ($useLinking) {
26457								$html .= '</a>';
26458							}
26459						} else {
26460							$html .= $joiner;
26461							$html .= $this->docPageNum($range_end);
26462							if ($useLinking) {
26463								$html .= '</a>';
26464							}
26465						}
26466					} else {
26467						if ($useLinking) {
26468							$html .= '<a class="mpdf_index_link" href="@' . $ppp[(count($ppp) - 1)] . '">';
26469						}
26470						$html .= $this->docPageNum($ppp[(count($ppp) - 1)]);
26471						if ($useLinking) {
26472							$html .= '</a>';
26473						}
26474					}
26475				}
26476			}
26477			$html .= '</div>';
26478			$last_lett = $lett;
26479		}
26480		$html .= '</div>';
26481		$save_fpb = $this->fixedPosBlockSave;
26482		$this->WriteHTML($html);
26483		$this->fixedPosBlockSave = $save_fpb;
26484
26485		$this->breakpoints[$this->CurrCol][] = $this->y;  // *COLUMNS*
26486	}
26487
26488	/* -- END INDEX -- */
26489
26490	function AcceptPageBreak()
26491	{
26492		if (count($this->cellBorderBuffer)) {
26493			$this->printcellbuffer();
26494		} // *TABLES*
26495		/* -- COLUMNS -- */
26496		if ($this->ColActive == 1) {
26497			if ($this->CurrCol < $this->NbCol - 1) {
26498				// Go to the next column
26499				$this->CurrCol++;
26500				$this->SetCol($this->CurrCol);
26501				$this->y = $this->y0;
26502				$this->ChangeColumn = 1; // Number (and direction) of columns changed +1, +2, -2 etc.
26503				// DIRECTIONALITY RTL
26504				if ($this->directionality == 'rtl') {
26505					$this->ChangeColumn = -($this->ChangeColumn);
26506				} // *OTL*
26507				// Stay on the page
26508				return false;
26509			} else {
26510				// Go back to the first column - NEW PAGE
26511				if (count($this->columnbuffer)) {
26512					$this->printcolumnbuffer();
26513				}
26514				$this->SetCol(0);
26515				$this->y0 = $this->tMargin;
26516				$this->ChangeColumn = -($this->NbCol - 1);
26517				// DIRECTIONALITY RTL
26518				if ($this->directionality == 'rtl') {
26519					$this->ChangeColumn = -($this->ChangeColumn);
26520				} // *OTL*
26521				// Page break
26522				return true;
26523			}
26524		} /* -- END COLUMNS -- */
26525		/* -- TABLES -- */ elseif ($this->table_rotate) {
26526			if ($this->tablebuffer) {
26527				$this->printtablebuffer();
26528			}
26529			return true;
26530		} /* -- END TABLES -- */ else { // *COLUMNS*
26531			$this->ChangeColumn = 0;
26532			return $this->autoPageBreak;
26533		} // *COLUMNS*
26534		return $this->autoPageBreak;
26535	}
26536
26537	// ----------- COLUMNS ---------------------
26538	/* -- COLUMNS -- */
26539
26540	function SetColumns($NbCol, $vAlign = '', $gap = 5)
26541	{
26542		// NbCol = number of columns
26543		// Anything less than 2 turns columns off
26544		if ($NbCol < 2) { // SET COLUMNS OFF
26545			if ($this->ColActive) {
26546				$this->ColActive = 0;
26547				if (count($this->columnbuffer)) {
26548					$this->printcolumnbuffer();
26549				}
26550				$this->NbCol = 1;
26551				$this->ResetMargins();
26552				$this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
26553				$this->divwidth = 0;
26554				$this->Ln();
26555			}
26556			$this->ColActive = 0;
26557			$this->columnbuffer = [];
26558			$this->ColDetails = [];
26559			$this->columnLinks = [];
26560			$this->columnAnnots = [];
26561			$this->columnForms = [];
26562			$this->col_BMoutlines = [];
26563			$this->col_toc = [];
26564			$this->breakpoints = [];
26565		} else { // SET COLUMNS ON
26566			if ($this->ColActive) {
26567				$this->ColActive = 0;
26568				if (count($this->columnbuffer)) {
26569					$this->printcolumnbuffer();
26570				}
26571				$this->ResetMargins();
26572			}
26573			if (isset($this->y) && $this->y > $this->tMargin) {
26574				$this->Ln();
26575			}
26576			$this->NbCol = $NbCol;
26577			$this->ColGap = $gap;
26578			$this->divwidth = 0;
26579			$this->ColActive = 1;
26580			$this->ColumnAdjust = true; // enables column height adjustment for the page
26581			$this->columnbuffer = [];
26582			$this->ColDetails = [];
26583			$this->columnLinks = [];
26584			$this->columnAnnots = [];
26585			$this->columnForms = [];
26586			$this->col_BMoutlines = [];
26587			$this->col_toc = [];
26588			$this->breakpoints = [];
26589			if ((strtoupper($vAlign) == 'J') || (strtoupper($vAlign) == 'JUSTIFY')) {
26590				$vAlign = 'J';
26591			} else {
26592				$vAlign = '';
26593			}
26594			$this->colvAlign = $vAlign;
26595			// Save the ordinate
26596			$absL = $this->DeflMargin - ($gap / 2);
26597			$absR = $this->DefrMargin - ($gap / 2);
26598			$PageWidth = $this->w - $absL - $absR; // virtual pagewidth for calculation only
26599			$ColWidth = (($PageWidth - ($gap * ($NbCol))) / $NbCol);
26600			$this->ColWidth = $ColWidth;
26601			/* -- OTL -- */
26602
26603			if ($this->directionality == 'rtl') {
26604				for ($i = 0; $i < $this->NbCol; $i++) {
26605					$this->ColL[$i] = $absL + ($gap / 2) + (($NbCol - ($i + 1)) * ($PageWidth / $NbCol));
26606					$this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos
26607				}
26608			} else {
26609				/* -- END OTL -- */
26610				for ($i = 0; $i < $this->NbCol; $i++) {
26611					$this->ColL[$i] = $absL + ($gap / 2) + ($i * ($PageWidth / $NbCol) );
26612					$this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos
26613				}
26614			} // *OTL*
26615			$this->pgwidth = $ColWidth;
26616			$this->SetCol(0);
26617			$this->y0 = $this->y;
26618		}
26619		$this->x = $this->lMargin;
26620	}
26621
26622	function SetCol($CurrCol)
26623	{
26624		// Used internally to set column by number: 0 is 1st column
26625		// Set position on a column
26626		$this->CurrCol = $CurrCol;
26627		$x = $this->ColL[$CurrCol];
26628		$xR = $this->ColR[$CurrCol]; // NB This is not R margin -> R pos
26629		if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
26630			$x += $this->MarginCorrection;
26631			$xR += $this->MarginCorrection;
26632		}
26633		$this->SetMargins($x, ($this->w - $xR), $this->tMargin);
26634	}
26635
26636	function AddColumn()
26637	{
26638		$this->NewColumn();
26639		$this->ColumnAdjust = false; // disables all column height adjustment for the page.
26640	}
26641
26642	function NewColumn()
26643	{
26644		if ($this->ColActive == 1) {
26645			if ($this->CurrCol < $this->NbCol - 1) {
26646				// Go to the next column
26647				$this->CurrCol++;
26648				$this->SetCol($this->CurrCol);
26649				$this->y = $this->y0;
26650				$this->ChangeColumn = 1;
26651				// DIRECTIONALITY RTL
26652				if ($this->directionality == 'rtl') {
26653					$this->ChangeColumn = -($this->ChangeColumn);
26654				} // *OTL*
26655				// Stay on the page
26656			} else {
26657				// Go back to the first column
26658				// Page break
26659				if (count($this->columnbuffer)) {
26660					$this->printcolumnbuffer();
26661				}
26662				$this->AddPage($this->CurOrientation);
26663				$this->SetCol(0);
26664				$this->y0 = $this->tMargin;
26665				$this->ChangeColumn = -($this->NbCol - 1);
26666				// DIRECTIONALITY RTL
26667				if ($this->directionality == 'rtl') {
26668					$this->ChangeColumn = -($this->ChangeColumn);
26669				} // *OTL*
26670			}
26671			$this->x = $this->lMargin;
26672		} else {
26673			$this->AddPage($this->CurOrientation);
26674		}
26675	}
26676
26677	function printcolumnbuffer()
26678	{
26679		// Columns ended (but page not ended) -> try to match all columns - unless disabled by using a custom column-break
26680		if (!$this->ColActive && $this->ColumnAdjust && !$this->keepColumns) {
26681			// Calculate adjustment to add to each column to calculate rel_y value
26682			$this->ColDetails[0]['add_y'] = 0;
26683			$last_col = 0;
26684			// Recursively add previous column's height
26685			for ($i = 1; $i < $this->NbCol; $i++) {
26686				if (isset($this->ColDetails[$i]['bottom_margin']) && $this->ColDetails[$i]['bottom_margin']) { // If any entries in the column
26687					$this->ColDetails[$i]['add_y'] = ($this->ColDetails[$i - 1]['bottom_margin'] - $this->y0) + $this->ColDetails[$i - 1]['add_y'];
26688					$last_col = $i;  // Last column actually printed
26689				}
26690			}
26691
26692			// Calculate value for each position sensitive entry as though for one column
26693			foreach ($this->columnbuffer as $key => $s) {
26694				$t = $s['s'];
26695				if ($t == 'ACROFORM') {
26696					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
26697					$this->columnbuffer[$key]['s'] = '';
26698				} elseif (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) {
26699					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
26700				} elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) {
26701					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
26702				} elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) {
26703					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
26704				} elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) {
26705					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
26706				} 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)) {
26707					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
26708				} elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t)) {
26709					$this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
26710				}
26711			}
26712			foreach ($this->internallink as $key => $f) {
26713				if (is_array($f) && isset($f['col'])) {
26714					$this->internallink[$key]['rel_y'] = $f['Y'] + $this->ColDetails[$f['col']]['add_y'] - $this->y0;
26715				}
26716			}
26717
26718			$breaks = [];
26719			foreach ($this->breakpoints as $c => $bpa) {
26720				foreach ($bpa as $rely) {
26721					$breaks[] = $rely + $this->ColDetails[$c]['add_y'] - $this->y0;
26722				}
26723			}
26724
26725
26726			if (isset($this->ColDetails[$last_col]['bottom_margin'])) {
26727				$lcbm = $this->ColDetails[$last_col]['bottom_margin'];
26728			} else {
26729				$lcbm = 0;
26730			}
26731			$sum_h = $this->ColDetails[$last_col]['add_y'] + $lcbm - $this->y0;
26732			// $sum_h = max($this->ColDetails[$last_col]['add_y'] + $this->ColDetails[$last_col]['bottom_margin'] - $this->y0, end($breaks));
26733			$target_h = ($sum_h / $this->NbCol);
26734
26735			$cbr = [];
26736			for ($i = 1; $i < $this->NbCol; $i++) {
26737				$th = ($sum_h * $i / $this->NbCol);
26738				foreach ($breaks as $bk => $val) {
26739					if ($val > $th) {
26740						if (($val - $th) < ($th - $breaks[$bk - 1])) {
26741							$cbr[$i - 1] = $val;
26742						} else {
26743							$cbr[$i - 1] = $breaks[$bk - 1];
26744						}
26745						break;
26746					}
26747				}
26748			}
26749			$cbr[($this->NbCol - 1)] = $sum_h;
26750
26751			// mPDF 6
26752			// Avoid outputing with 1st column empty
26753			if (isset($cbr[0]) && $cbr[0] == 0) {
26754				for ($i = 0; $i < $this->NbCol - 1; $i++) {
26755					$cbr[$i] = $cbr[$i + 1];
26756				}
26757			}
26758
26759			// Now update the columns - divide into columns of approximately equal value
26760			$last_new_col = 0;
26761			$yadj = 0; // mm
26762			$xadj = 0;
26763			$last_col_bottom = 0;
26764			$lowest_bottom_y = 0;
26765			$block_bottom = 0;
26766			$newcolumn = 0;
26767			foreach ($this->columnbuffer as $key => $s) {
26768				if (isset($s['rel_y'])) { // only process position sensitive data
26769					if ($s['rel_y'] >= $cbr[$newcolumn]) {
26770						$newcolumn++;
26771					} else {
26772						$newcolumn = $last_new_col;
26773					}
26774
26775
26776					$block_bottom = max($block_bottom, ($s['rel_y'] + $s['h']));
26777
26778					if ($this->directionality == 'rtl') { // *OTL*
26779						$xadj = -(($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap)); // *OTL*
26780					} // *OTL*
26781					else { // *OTL*
26782						$xadj = ($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap);
26783					} // *OTL*
26784
26785					if ($last_new_col != $newcolumn) { // Added new column
26786						$last_col_bottom = $this->columnbuffer[$key]['rel_y'];
26787						$block_bottom = 0;
26788					}
26789					$yadj = ($s['rel_y'] - $s['y']) - ($last_col_bottom) + $this->y0;
26790					// callback function
26791					$t = $s['s'];
26792
26793					// mPDF 5.7+
26794					$t = $this->columnAdjustPregReplace('Td', $xadj, $yadj, '/BT (\d+\.\d\d+) (\d+\.\d\d+) Td/', $t);
26795					$t = $this->columnAdjustPregReplace('re', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) ([\-]{0,1}\d+\.\d\d+) re/', $t);
26796					$t = $this->columnAdjustPregReplace('l', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) l/', $t);
26797					$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);
26798					$t = $this->columnAdjustPregReplace('draw', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) m/', $t);
26799					$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);
26800
26801					$this->columnbuffer[$key]['s'] = $t;
26802					$this->columnbuffer[$key]['newcol'] = $newcolumn;
26803					$this->columnbuffer[$key]['newy'] = $s['y'] + $yadj;
26804					$last_new_col = $newcolumn;
26805					$clb = $s['y'] + $yadj + $s['h']; // bottom_margin of current
26806					if ((isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb > $this->ColDetails[$newcolumn]['max_bottom']) || (!isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb)) {
26807						$this->ColDetails[$newcolumn]['max_bottom'] = $clb;
26808					}
26809					if ($clb > $lowest_bottom_y) {
26810						$lowest_bottom_y = $clb;
26811					}
26812					// Adjust LINKS
26813					if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
26814						$ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
26815						$this->PageLinks[$this->page][$ref][0] += ($xadj * Mpdf::SCALE);
26816						$this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE);
26817						unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
26818					}
26819					// Adjust FORM FIELDS
26820					if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
26821						$ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
26822						$this->form->forms[$ref]['x'] += ($xadj);
26823						$this->form->forms[$ref]['y'] += ($yadj);
26824						unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
26825					}
26826					/* -- ANNOTATIONS -- */
26827					if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
26828						$ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
26829						if ($this->PageAnnots[$this->page][$ref]['x'] < 0) {
26830							$this->PageAnnots[$this->page][$ref]['x'] -= ($xadj);
26831						} else {
26832							$this->PageAnnots[$this->page][$ref]['x'] += ($xadj);
26833						}
26834						$this->PageAnnots[$this->page][$ref]['y'] += ($yadj); // unlike PageLinks, Page annots has y values from top in mm
26835						unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
26836					}
26837					/* -- END ANNOTATIONS -- */
26838				}
26839			}
26840
26841			/* -- BOOKMARKS -- */
26842			// Adjust Bookmarks
26843			foreach ($this->col_BMoutlines as $v) {
26844				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']];
26845			}
26846			/* -- END BOOKMARKS -- */
26847
26848			/* -- TOC -- */
26849
26850			// Adjust ToC
26851			foreach ($this->col_toc as $v) {
26852				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
26853				$this->links[$v['link']][1] = $this->y0;
26854			}
26855			/* -- END TOC -- */
26856
26857			// Adjust column length to be equal
26858			if ($this->colvAlign == 'J') {
26859				foreach ($this->columnbuffer as $key => $s) {
26860					if (isset($s['rel_y'])) { // only process position sensitive data
26861						// Set ratio to expand y values or heights
26862						if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {
26863							$ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));
26864						} else {
26865							$ratio = 1;
26866						}
26867						if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
26868							$yadj = ($s['newy'] - $this->y0) * ($ratio - 1);
26869
26870							// Adjust LINKS
26871							if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
26872								$ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
26873								$this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value
26874								$this->PageLinks[$this->page][$ref][3] *= $ratio; // height
26875								unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
26876							}
26877							// Adjust FORM FIELDS
26878							if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
26879								$ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
26880								$this->form->forms[$ref]['x'] += ($xadj);
26881								$this->form->forms[$ref]['y'] += ($yadj);
26882								unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
26883							}
26884							/* -- ANNOTATIONS -- */
26885							if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
26886								$ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
26887								$this->PageAnnots[$this->page][$ref]['y'] += ($yadj);
26888								unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
26889							}
26890							/* -- END ANNOTATIONS -- */
26891						}
26892					}
26893				}
26894				foreach ($this->internallink as $key => $f) {
26895					if (is_array($f) && isset($f['col'])) {
26896						$last_col_bottom = 0;
26897						for ($nbc = 0; $nbc < $this->NbCol; $nbc++) {
26898							if ($f['rel_y'] >= $cbr[$nbc]) {
26899								$last_col_bottom = $cbr[$nbc];
26900							}
26901						}
26902						$yadj = ($f['rel_y'] - $f['Y']) - $last_col_bottom + $this->y0;
26903						$f['Y'] += $yadj;
26904						unset($f['col']);
26905						unset($f['rel_y']);
26906						$this->internallink[$key] = $f;
26907					}
26908				}
26909
26910				$last_col = -1;
26911				$trans_on = false;
26912				foreach ($this->columnbuffer as $key => $s) {
26913					if (isset($s['rel_y'])) { // only process position sensitive data
26914						// Set ratio to expand y values or heights
26915						if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {
26916							$ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));
26917						} else {
26918							$ratio = 1;
26919						}
26920						if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
26921							// Start Transformation
26922							$this->pages[$this->page] .= $this->StartTransform(true) . "\n";
26923							$this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n";
26924							$trans_on = true;
26925						}
26926					}
26927					// Now output the adjusted values
26928					$this->pages[$this->page] .= $s['s'] . "\n";
26929					if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) { // only process position sensitive data
26930						// Stop Transformation
26931						$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
26932						$trans_on = false;
26933					}
26934				}
26935				if ($trans_on) {
26936					$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
26937				}
26938			} else { // if NOT $this->colvAlign == 'J'
26939				// Now output the adjusted values
26940				foreach ($this->columnbuffer as $s) {
26941					$this->pages[$this->page] .= $s['s'] . "\n";
26942				}
26943			}
26944			if ($lowest_bottom_y > 0) {
26945				$this->y = $lowest_bottom_y;
26946			}
26947		} // Columns not ended but new page -> align columns (can leave the columns alone - just tidy up the height)
26948		elseif ($this->colvAlign == 'J' && $this->ColumnAdjust && !$this->keepColumns) {
26949			// calculate the lowest bottom margin
26950			$lowest_bottom_y = 0;
26951			foreach ($this->columnbuffer as $key => $s) {
26952				// Only process output data
26953				$t = $s['s'];
26954				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)) ||
26955					(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) ||
26956					(preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) ||
26957					(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) ||
26958					(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) {
26959					$clb = $s['y'] + $s['h'];
26960					if ((isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom']) || !isset($this->ColDetails[$s['col']]['max_bottom'])) {
26961						$this->ColDetails[$s['col']]['max_bottom'] = $clb;
26962					}
26963					if ($clb > $lowest_bottom_y) {
26964						$lowest_bottom_y = $clb;
26965					}
26966					$this->columnbuffer[$key]['rel_y'] = $s['y']; // Marks position sensitive data to process later
26967					if ($t == 'ACROFORM') {
26968						$this->columnbuffer[$key]['s'] = '';
26969					}
26970				}
26971			}
26972			// Adjust column length equal
26973			foreach ($this->columnbuffer as $key => $s) {
26974				// Set ratio to expand y values or heights
26975				if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) {
26976					$ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));
26977				} else {
26978					$ratio = 1;
26979				}
26980				if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
26981					$yadj = ($s['y'] - $this->y0) * ($ratio - 1);
26982
26983					// Adjust LINKS
26984					if (isset($s['rel_y'])) { // only process position sensitive data
26985						// otherwise triggers for all entries in column buffer (.e.g. formatting) and makes below adjustments more than once
26986						if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {
26987							$ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];
26988							$this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value
26989							$this->PageLinks[$this->page][$ref][3] *= $ratio; // height
26990							unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);
26991						}
26992						// Adjust FORM FIELDS
26993						if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {
26994							$ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];
26995							$this->form->forms[$ref]['x'] += ($xadj);
26996							$this->form->forms[$ref]['y'] += ($yadj);
26997							unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);
26998						}
26999						/* -- ANNOTATIONS -- */
27000						if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {
27001							$ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];
27002							$this->PageAnnots[$this->page][$ref]['y'] += ($yadj);
27003							unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);
27004						}
27005						/* -- END ANNOTATIONS -- */
27006					}
27007				}
27008			}
27009
27010			/* -- BOOKMARKS -- */
27011
27012			// Adjust Bookmarks
27013			foreach ($this->col_BMoutlines as $v) {
27014				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']];
27015			}
27016			/* -- END BOOKMARKS -- */
27017
27018			/* -- TOC -- */
27019
27020			// Adjust ToC
27021			foreach ($this->col_toc as $v) {
27022				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
27023				$this->links[$v['link']][1] = $this->y0;
27024			}
27025			/* -- END TOC -- */
27026			$trans_on = false;
27027			foreach ($this->columnbuffer as $key => $s) {
27028				if (isset($s['rel_y'])) { // only process position sensitive data
27029					// Set ratio to expand y values or heights
27030					if ($this->ColDetails[$s['col']]['max_bottom']) {
27031						$ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));
27032					} else {
27033						$ratio = 1;
27034					}
27035					if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
27036						// Start Transformation
27037						$this->pages[$this->page] .= $this->StartTransform(true) . "\n";
27038						$this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n";
27039						$trans_on = true;
27040					}
27041				}
27042				// Now output the adjusted values
27043				$this->pages[$this->page] .= $s['s'] . "\n";
27044				if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
27045					// Stop Transformation
27046					$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
27047					$trans_on = false;
27048				}
27049			}
27050			if ($trans_on) {
27051				$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
27052			}
27053
27054			if ($lowest_bottom_y > 0) {
27055				$this->y = $lowest_bottom_y;
27056			}
27057		} // Just reproduce the page as it was
27058		else {
27059			// If page has not ended but height adjustment was disabled by custom column-break - adjust y
27060			$lowest_bottom_y = 0;
27061			if (!$this->ColActive && (!$this->ColumnAdjust || $this->keepColumns)) {
27062				// calculate the lowest bottom margin
27063				foreach ($this->columnbuffer as $key => $s) {
27064					// Only process output data
27065					$t = $s['s'];
27066					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)) ||
27067						(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) ||
27068						(preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) ||
27069						(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) ||
27070						(preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) {
27071						$clb = $s['y'] + $s['h'];
27072						if ($clb > $this->ColDetails[$s['col']]['max_bottom']) {
27073							$this->ColDetails[$s['col']]['max_bottom'] = $clb;
27074						}
27075						if ($clb > $lowest_bottom_y) {
27076							$lowest_bottom_y = $clb;
27077						}
27078					}
27079				}
27080			}
27081			foreach ($this->columnbuffer as $key => $s) {
27082				if ($s['s'] != 'ACROFORM') {
27083					$this->pages[$this->page] .= $s['s'] . "\n";
27084				}
27085			}
27086			if ($lowest_bottom_y > 0) {
27087				$this->y = $lowest_bottom_y;
27088			}
27089			/* -- BOOKMARKS -- */
27090			// Output Bookmarks
27091			foreach ($this->col_BMoutlines as $v) {
27092				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
27093			}
27094			/* -- END BOOKMARKS -- */
27095			/* -- TOC -- */
27096			// Output ToC
27097			foreach ($this->col_toc as $v) {
27098				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
27099			}
27100			/* -- END TOC -- */
27101		}
27102		foreach ($this->internallink as $key => $f) {
27103			if (isset($this->internallink[$key]['col'])) {
27104				unset($this->internallink[$key]['col']);
27105			}
27106			if (isset($this->internallink[$key]['rel_y'])) {
27107				unset($this->internallink[$key]['rel_y']);
27108			}
27109		}
27110
27111		$this->columnbuffer = [];
27112		$this->ColDetails = [];
27113		$this->columnLinks = [];
27114		$this->columnAnnots = [];
27115		$this->columnForms = [];
27116
27117		$this->col_BMoutlines = [];
27118		$this->col_toc = [];
27119		$this->breakpoints = [];
27120	}
27121
27122	// mPDF 5.7+
27123	function columnAdjustPregReplace($type, $xadj, $yadj, $pattern, $subject)
27124	{
27125		preg_match($pattern, $subject, $matches);
27126		if (!count($matches)) {
27127			return $subject;
27128		}
27129		if (!isset($matches[3])) {
27130			$matches[3] = 0;
27131		}
27132		if (!isset($matches[4])) {
27133			$matches[4] = 0;
27134		}
27135		if (!isset($matches[5])) {
27136			$matches[5] = 0;
27137		}
27138		if (!isset($matches[6])) {
27139			$matches[6] = 0;
27140		}
27141		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);
27142	}
27143
27144	/* -- END COLUMNS -- */
27145
27146	// ==================================================================
27147	/* -- TABLES -- */
27148	function printcellbuffer()
27149	{
27150		if (count($this->cellBorderBuffer)) {
27151			sort($this->cellBorderBuffer);
27152			foreach ($this->cellBorderBuffer as $cbb) {
27153				$cba = unpack("A16dom/nbord/A1side/ns/dbw/a6ca/A10style/dx/dy/dw/dh/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd/dover/", $cbb);
27154				$side = $cba['side'];
27155				$color = str_pad($cba['ca'], 6, "\x00");
27156				$details = [];
27157				$details[$side]['dom'] = (float) $cba['dom'];
27158				$details[$side]['s'] = $cba['s'];
27159				$details[$side]['w'] = $cba['bw'];
27160				$details[$side]['c'] = $color;
27161				$details[$side]['style'] = trim($cba['style']);
27162				$details['mbw']['BL'] = $cba['mbl'];
27163				$details['mbw']['BR'] = $cba['mbr'];
27164				$details['mbw']['RT'] = $cba['mrt'];
27165				$details['mbw']['RB'] = $cba['mrb'];
27166				$details['mbw']['TL'] = $cba['mtl'];
27167				$details['mbw']['TR'] = $cba['mtr'];
27168				$details['mbw']['LT'] = $cba['mlt'];
27169				$details['mbw']['LB'] = $cba['mlb'];
27170				$details['cellposdom'] = $cba['cpd'];
27171				$details['p'] = $side;
27172				if ($cba['over'] == 1) {
27173					$details[$side]['overlay'] = true;
27174				} else {
27175					$details[$side]['overlay'] = false;
27176				}
27177				$this->_tableRect($cba['x'], $cba['y'], $cba['w'], $cba['h'], $cba['bord'], $details, false, false);
27178			}
27179			$this->cellBorderBuffer = [];
27180		}
27181	}
27182
27183	// ==================================================================
27184	function printtablebuffer()
27185	{
27186
27187		if (!$this->table_rotate) {
27188			$this->pages[$this->page] .= $this->tablebuffer;
27189			foreach ($this->tbrot_Links as $p => $l) {
27190				foreach ($l as $v) {
27191					$this->PageLinks[$p][] = $v;
27192				}
27193			}
27194			$this->tbrot_Links = [];
27195			/* -- ANNOTATIONS -- */
27196			foreach ($this->tbrot_Annots as $p => $l) {
27197				foreach ($l as $v) {
27198					$this->PageAnnots[$p][] = $v;
27199				}
27200			}
27201			$this->tbrot_Annots = [];
27202			/* -- END ANNOTATIONS -- */
27203
27204			/* -- BOOKMARKS -- */
27205			// Output Bookmarks
27206			foreach ($this->tbrot_BMoutlines as $v) {
27207				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
27208			}
27209			$this->tbrot_BMoutlines = [];
27210			/* -- END BOOKMARKS -- */
27211
27212			/* -- TOC -- */
27213			// Output ToC
27214			foreach ($this->tbrot_toc as $v) {
27215				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
27216			}
27217			$this->tbrot_toc = [];
27218			/* -- END TOC -- */
27219
27220			return;
27221		}
27222		// elseif rotated
27223		$lm = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left'];
27224		$pw = $this->blk[$this->blklvl]['inner_width'];
27225		// Start Transformation
27226		$this->pages[$this->page] .= $this->StartTransform(true) . "\n";
27227
27228		if ($this->table_rotate > 1) { // clockwise
27229			if ($this->tbrot_align == 'L') {
27230				$xadj = $this->tbrot_h; // align L (as is)
27231			} elseif ($this->tbrot_align == 'R') {
27232				$xadj = $lm - $this->tbrot_x0 + ($pw); // align R
27233			} else {
27234				$xadj = $lm - $this->tbrot_x0 + (($pw + $this->tbrot_h) / 2); // align C
27235			}
27236			$yadj = 0;
27237		} else { // anti-clockwise
27238			if ($this->tbrot_align == 'L') {
27239				$xadj = 0; // align L (as is)
27240			} elseif ($this->tbrot_align == 'R') {
27241				$xadj = $lm - $this->tbrot_x0 + ($pw - $this->tbrot_h); // align R
27242			} else {
27243				$xadj = $lm - $this->tbrot_x0 + (($pw - $this->tbrot_h) / 2); // align C
27244			}
27245			$yadj = $this->tbrot_w;
27246		}
27247
27248
27249		$this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n";
27250		$this->pages[$this->page] .= $this->transformRotate($this->table_rotate, $this->tbrot_x0, $this->tbrot_y0, true) . "\n";
27251
27252		// Now output the adjusted values
27253		$this->pages[$this->page] .= $this->tablebuffer;
27254
27255
27256		foreach ($this->tbrot_Links as $p => $l) {
27257			foreach ($l as $v) {
27258				$w = $v[2] / Mpdf::SCALE;
27259				$h = $v[3] / Mpdf::SCALE;
27260				$ax = ($v[0] / Mpdf::SCALE) - $this->tbrot_x0;
27261				$ay = (($this->hPt - $v[1]) / Mpdf::SCALE) - $this->tbrot_y0;
27262				if ($this->table_rotate > 1) { // clockwise
27263					$bx = $this->tbrot_x0 + $xadj - $ay - $h;
27264					$by = $this->tbrot_y0 + $yadj + $ax;
27265				} else {
27266					$bx = $this->tbrot_x0 + $xadj + $ay;
27267					$by = $this->tbrot_y0 + $yadj - $ax - $w;
27268				}
27269				$v[0] = $bx * Mpdf::SCALE;
27270				$v[1] = ($this->h - $by) * Mpdf::SCALE;
27271				$v[2] = $h * Mpdf::SCALE; // swap width and height
27272				$v[3] = $w * Mpdf::SCALE;
27273				$this->PageLinks[$p][] = $v;
27274			}
27275		}
27276		$this->tbrot_Links = [];
27277		foreach ($this->internallink as $key => $f) {
27278			if (is_array($f) && isset($f['tbrot'])) {
27279				$f['Y'] = $this->tbrot_y0;
27280				$f['PAGE'] = $this->page;
27281				unset($f['tbrot']);
27282				$this->internallink[$key] = $f;
27283			}
27284		}
27285		/* -- ANNOTATIONS -- */
27286		foreach ($this->tbrot_Annots as $p => $l) {
27287			foreach ($l as $v) {
27288				$ax = abs($v['x']) - $this->tbrot_x0; // abs because -ve values are internally set and held for reference if annotMargin set
27289				$ay = $v['y'] - $this->tbrot_y0;
27290				if ($this->table_rotate > 1) { // clockwise
27291					$bx = $this->tbrot_x0 + $xadj - $ay;
27292					$by = $this->tbrot_y0 + $yadj + $ax;
27293				} else {
27294					$bx = $this->tbrot_x0 + $xadj + $ay;
27295					$by = $this->tbrot_y0 + $yadj - $ax;
27296				}
27297				if ($v['x'] < 0) {
27298					$v['x'] = -$bx;
27299				} else {
27300					$v['x'] = $bx;
27301				}
27302				$v['y'] = ($by);
27303				$this->PageAnnots[$p][] = $v;
27304			}
27305		}
27306		$this->tbrot_Annots = [];
27307		/* -- END ANNOTATIONS -- */
27308
27309
27310		/* -- BOOKMARKS -- */
27311
27312		// Adjust Bookmarks
27313		foreach ($this->tbrot_BMoutlines as $v) {
27314			$v['y'] = $this->tbrot_y0;
27315			$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page];
27316		}
27317		/* -- END BOOKMARKS -- */
27318
27319		/* -- TOC -- */
27320
27321		// Adjust ToC - uses document page number
27322		foreach ($this->tbrot_toc as $v) {
27323			$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']];
27324			$this->links[$v['link']][1] = $this->tbrot_y0;
27325		}
27326		/* -- END TOC -- */
27327
27328
27329
27330		$this->tbrot_BMoutlines = [];
27331		$this->tbrot_toc = [];
27332
27333		// Stop Transformation
27334		$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
27335
27336
27337		$this->y = $this->tbrot_y0 + $this->tbrot_w;
27338		$this->x = $this->lMargin;
27339
27340		$this->tablebuffer = '';
27341	}
27342
27343	// ==================================================================
27344	// Keep-with-table This buffers contents of h1-6 to keep on page with table
27345	function printkwtbuffer()
27346	{
27347		if (!$this->kwt_moved) {
27348			foreach ($this->kwt_buffer as $s) {
27349				$this->pages[$this->page] .= $s['s'] . "\n";
27350			}
27351			foreach ($this->kwt_Links as $p => $l) {
27352				foreach ($l as $v) {
27353					$this->PageLinks[$p][] = $v;
27354				}
27355			}
27356			$this->kwt_Links = [];
27357			/* -- ANNOTATIONS -- */
27358			foreach ($this->kwt_Annots as $p => $l) {
27359				foreach ($l as $v) {
27360					$this->PageAnnots[$p][] = $v;
27361				}
27362			}
27363			$this->kwt_Annots = [];
27364			/* -- END ANNOTATIONS -- */
27365
27366			/* -- INDEX -- */
27367			// Output Reference (index)
27368			foreach ($this->kwt_Reference as $v) {
27369				$Present = 0;
27370				for ($i = 0; $i < count($this->Reference); $i++) {
27371					if ($this->Reference[$i]['t'] == $v['t']) {
27372						$Present = 1;
27373						if (!in_array($v['op'], $this->Reference[$i]['p'])) {
27374							$this->Reference[$i]['p'][] = $v['op'];
27375						}
27376					}
27377				}
27378				if ($Present == 0) {
27379					$this->Reference[] = ['t' => $v['t'], 'p' => [$v['op']]];
27380				}
27381			}
27382			$this->kwt_Reference = [];
27383			/* -- END INDEX -- */
27384
27385			/* -- BOOKMARKS -- */
27386			// Output Bookmarks
27387			foreach ($this->kwt_BMoutlines as $v) {
27388				$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];
27389			}
27390			$this->kwt_BMoutlines = [];
27391			/* -- END BOOKMARKS -- */
27392
27393			/* -- TOC -- */
27394			// Output ToC
27395			foreach ($this->kwt_toc as $v) {
27396				$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];
27397			}
27398			$this->kwt_toc = [];
27399			/* -- END TOC -- */
27400
27401			$this->pageoutput[$this->page] = []; // mPDF 6
27402			return;
27403		}
27404
27405		// Start Transformation
27406		$this->pages[$this->page] .= $this->StartTransform(true) . "\n";
27407		$xadj = $this->lMargin - $this->kwt_x0;
27408		// $yadj = $this->y - $this->kwt_y0 ;
27409		$yadj = $this->tMargin - $this->kwt_y0;
27410
27411		$this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n";
27412
27413		// Now output the adjusted values
27414		foreach ($this->kwt_buffer as $s) {
27415			$this->pages[$this->page] .= $s['s'] . "\n";
27416		}
27417
27418		// Adjust hyperLinks
27419		foreach ($this->kwt_Links as $p => $l) {
27420			foreach ($l as $v) {
27421				$bx = $this->kwt_x0 + $xadj;
27422				$by = $this->kwt_y0 + $yadj;
27423				$v[0] = $bx * Mpdf::SCALE;
27424				$v[1] = ($this->h - $by) * Mpdf::SCALE;
27425				$this->PageLinks[$p][] = $v;
27426			}
27427		}
27428		foreach ($this->internallink as $key => $f) {
27429			if (is_array($f) && isset($f['kwt'])) {
27430				$f['Y'] += $yadj;
27431				$f['PAGE'] = $this->page;
27432				unset($f['kwt']);
27433				$this->internallink[$key] = $f;
27434			}
27435		}
27436		/* -- ANNOTATIONS -- */
27437		foreach ($this->kwt_Annots as $p => $l) {
27438			foreach ($l as $v) {
27439				$bx = $this->kwt_x0 + $xadj;
27440				$by = $this->kwt_y0 + $yadj;
27441				if ($v['x'] < 0) {
27442					$v['x'] = -$bx;
27443				} else {
27444					$v['x'] = $bx;
27445				}
27446				$v['y'] = $by;
27447				$this->PageAnnots[$p][] = $v;
27448			}
27449		}
27450		/* -- END ANNOTATIONS -- */
27451
27452		/* -- BOOKMARKS -- */
27453
27454		// Adjust Bookmarks
27455		foreach ($this->kwt_BMoutlines as $v) {
27456			if ($v['y'] != 0) {
27457				$v['y'] += $yadj;
27458			}
27459			$this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page];
27460		}
27461		/* -- END BOOKMARKS -- */
27462
27463		/* -- INDEX -- */
27464
27465		// Adjust Reference (index)
27466		foreach ($this->kwt_Reference as $v) {
27467			$Present = 0;
27468			// Search the reference (AND Ref/PageNo) in the array
27469			for ($i = 0; $i < count($this->Reference); $i++) {
27470				if ($this->Reference[$i]['t'] == $v['t']) {
27471					$Present = 1;
27472					if (!in_array($this->page, $this->Reference[$i]['p'])) {
27473						$this->Reference[$i]['p'][] = $this->page;
27474					}
27475				}
27476			}
27477			if ($Present == 0) {
27478				$this->Reference[] = ['t' => $v['t'], 'p' => [$this->page]];
27479			}
27480		}
27481		/* -- END INDEX -- */
27482
27483		/* -- TOC -- */
27484
27485		// Adjust ToC
27486		foreach ($this->kwt_toc as $v) {
27487			$this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']];
27488			$this->links[$v['link']][0] = $this->page;
27489			$this->links[$v['link']][1] += $yadj;
27490		}
27491		/* -- END TOC -- */
27492
27493
27494		$this->kwt_Links = [];
27495		$this->kwt_Annots = [];
27496
27497		$this->kwt_Reference = [];
27498		$this->kwt_BMoutlines = [];
27499		$this->kwt_toc = [];
27500		// Stop Transformation
27501		$this->pages[$this->page] .= $this->StopTransform(true) . "\n";
27502
27503		$this->kwt_buffer = [];
27504
27505		$this->y += $this->kwt_height;
27506		$this->pageoutput[$this->page] = []; // mPDF 6
27507	}
27508
27509	/* -- END TABLES -- */
27510
27511	// ==================================================================
27512
27513	function printfloatbuffer()
27514	{
27515		if (count($this->floatbuffer)) {
27516			$this->objectbuffer = $this->floatbuffer;
27517			$this->printobjectbuffer(false);
27518			$this->objectbuffer = [];
27519			$this->floatbuffer = [];
27520			$this->floatmargins = [];
27521		}
27522	}
27523
27524	// ==================================================================
27525	// ==================================================================
27526	// Added ELLIPSES and CIRCLES
27527	function Circle($x, $y, $r, $style = 'S')
27528	{
27529		$this->Ellipse($x, $y, $r, $r, $style);
27530	}
27531
27532	function Ellipse($x, $y, $rx, $ry, $style = 'S')
27533	{
27534		if ($style == 'F') {
27535			$op = 'f';
27536		} elseif ($style == 'FD' or $style == 'DF') {
27537			$op = 'B';
27538		} else {
27539			$op = 'S';
27540		}
27541		$lx = 4 / 3 * (M_SQRT2 - 1) * $rx;
27542		$ly = 4 / 3 * (M_SQRT2 - 1) * $ry;
27543		$h = $this->h;
27544		$this->_out(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));
27545		$this->_out(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));
27546		$this->_out(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));
27547		$this->_out(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));
27548	}
27549
27550	/* -- DIRECTW -- */
27551
27552	// Added adaptation of shaded_box = AUTOSIZE-TEXT
27553	function AutosizeText($text, $w, $font, $style, $szfont = 72)
27554	{
27555
27556		$text = ' ' . $text . ' ';
27557
27558		$this->SetFont($font, $style, $szfont, false);
27559
27560		$text = $this->purify_utf8_text($text);
27561		if ($this->text_input_as_HTML) {
27562			$text = $this->all_entities_to_utf8($text);
27563		}
27564		if ($this->usingCoreFont) {
27565			$text = mb_convert_encoding($text, $this->mb_enc, 'UTF-8');
27566		}
27567
27568		// DIRECTIONALITY
27569		if (preg_match("/([" . $this->pregRTLchars . "])/u", $text)) {
27570			$this->biDirectional = true;
27571		} // *OTL*
27572
27573		$textvar = 0;
27574		$save_OTLtags = $this->OTLtags;
27575		$this->OTLtags = [];
27576		if ($this->useKerning) {
27577			if ($this->CurrentFont['haskernGPOS']) {
27578				$this->OTLtags['Plus'] .= ' kern';
27579			} else {
27580				$textvar = ($textvar | TextVars::FC_KERNING);
27581			}
27582		}
27583
27584		/* -- OTL -- */
27585		// Use OTL OpenType Table Layout - GSUB & GPOS
27586		if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
27587			$text = $this->otl->applyOTL($text, $this->CurrentFont['useOTL']);
27588			$OTLdata = $this->otl->OTLdata;
27589		}
27590		/* -- END OTL -- */
27591		$this->OTLtags = $save_OTLtags;
27592
27593		$this->magic_reverse_dir($text, $this->directionality, $OTLdata);
27594
27595
27596		$width = $this->sizeConverter->convert($w);
27597		$loop = 0;
27598		while ($loop == 0) {
27599			$this->SetFont($font, $style, $szfont, false);
27600			$sz = $this->GetStringWidth($text, true, $OTLdata, $textvar);
27601			if ($sz > $w) {
27602				$szfont --;
27603			} else {
27604				$loop ++;
27605			}
27606		}
27607		$this->SetFont($font, $style, $szfont, true, true);
27608		$this->Cell($w, 0, $text, 0, 0, "C", 0, '', 0, 0, 0, 'M', 0, false, $OTLdata, $textvar);
27609	}
27610
27611	/* -- END DIRECTW -- */
27612
27613	// ====================================================
27614	// ====================================================
27615
27616	function magic_reverse_dir(&$chunk, $dir, &$chunkOTLdata)
27617	{
27618		/* -- OTL -- */
27619		if ($this->usingCoreFont) {
27620			return 0;
27621		}
27622		if ($chunk == '') {
27623			return 0;
27624		}
27625
27626		if ($this->biDirectional || $dir == 'rtl') {
27627			// check if string contains RTL text
27628			// including any added from OTL tables (in PUA)
27629			$pregRTLchars = $this->pregRTLchars;
27630			if (isset($this->CurrentFont['rtlPUAstr']) && $this->CurrentFont['rtlPUAstr']) {
27631				$pregRTLchars .= $this->CurrentFont['rtlPUAstr'];
27632			}
27633			if (!preg_match("/[" . $pregRTLchars . "]/u", $chunk) && $dir != 'rtl') {
27634				return 0;
27635			}   // Chunk doesn't contain RTL characters
27636
27637			$unicode = $this->UTF8StringToArray($chunk, false);
27638
27639			$is_strong = false;
27640			if (empty($chunkOTLdata)) {
27641				$this->getBasicOTLdata($chunkOTLdata, $unicode, $is_strong);
27642			}
27643
27644			if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0x80)) {
27645				$useGPOS = true;
27646			} else {
27647				$useGPOS = false;
27648			}
27649
27650			// NB Returned $chunk may be a shorter string (with adjusted $cOTLdata) by removal of LRE, RLE etc embedding codes.
27651			list($chunk, $rtl_content) = $this->otl->bidiSort($unicode, $chunk, $dir, $chunkOTLdata, $useGPOS);
27652
27653			return $rtl_content;
27654		}
27655		/* -- END OTL -- */
27656		return 0;
27657	}
27658
27659	/* -- OTL -- */
27660
27661	function getBasicOTLdata(&$chunkOTLdata, $unicode, &$is_strong)
27662	{
27663		if (empty($this->otl)) {
27664			$this->otl = new Otl($this, $this->fontCache);
27665		}
27666		$chunkOTLdata['group'] = '';
27667		$chunkOTLdata['GPOSinfo'] = [];
27668		$chunkOTLdata['char_data'] = [];
27669		foreach ($unicode as $char) {
27670			$ucd_record = Ucdn::get_ucd_record($char);
27671			$chunkOTLdata['char_data'][] = ['bidi_class' => $ucd_record[2], 'uni' => $char];
27672			if ($ucd_record[2] == 0 || $ucd_record[2] == 3 || $ucd_record[2] == 4) {
27673				$is_strong = true;
27674			} // contains strong character
27675			if ($ucd_record[0] == Ucdn::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
27676				$chunkOTLdata['group'] .= 'M';
27677			} elseif ($char == 32 || $char == 12288) {
27678				$chunkOTLdata['group'] .= 'S';
27679			} else {
27680				$chunkOTLdata['group'] .= 'C';
27681			}
27682		}
27683	}
27684
27685	function _setBidiCodes($mode = 'start', $bdf = '')
27686	{
27687		$s = '';
27688		if ($mode == 'end') {
27689			// PDF comes before PDI to close isolate-override (e.g. "LRILROPDFPDI")
27690			if (strpos($bdf, 'PDF') !== false) {
27691				$s .= UtfString::code2utf(0x202C);
27692			} // POP DIRECTIONAL FORMATTING
27693			if (strpos($bdf, 'PDI') !== false) {
27694				$s .= UtfString::code2utf(0x2069);
27695			} // POP DIRECTIONAL ISOLATE
27696		} elseif ($mode == 'start') {
27697			// LRI comes before LRO to open isolate-override (e.g. "LRILROPDFPDI")
27698			if (strpos($bdf, 'LRI') !== false) {
27699				$s .= UtfString::code2utf(0x2066);
27700			} // U+2066 LRI
27701			elseif (strpos($bdf, 'RLI') !== false) {
27702				$s .= UtfString::code2utf(0x2067);
27703			} // U+2067 RLI
27704			elseif (strpos($bdf, 'FSI') !== false) {
27705				$s .= UtfString::code2utf(0x2068);
27706			} // U+2068 FSI
27707			if (strpos($bdf, 'LRO') !== false) {
27708				$s .= UtfString::code2utf(0x202D);
27709			} // U+202D LRO
27710			elseif (strpos($bdf, 'RLO') !== false) {
27711				$s .= UtfString::code2utf(0x202E);
27712			} // U+202E RLO
27713			elseif (strpos($bdf, 'LRE') !== false) {
27714				$s .= UtfString::code2utf(0x202A);
27715			} // U+202A LRE
27716			elseif (strpos($bdf, 'RLE') !== false) {
27717				$s .= UtfString::code2utf(0x202B);
27718			} // U+202B RLE
27719		}
27720		return $s;
27721	}
27722
27723	/* -- END OTL -- */
27724
27725	//
27726	// ****************************
27727	// ****************************
27728
27729
27730	function SetSubstitutions()
27731	{
27732		$subsarray = [];
27733		require __DIR__ . '/../data/subs_win-1252.php';
27734		$this->substitute = [];
27735		foreach ($subsarray as $key => $val) {
27736			$this->substitute[UtfString::code2utf($key)] = $val;
27737		}
27738	}
27739
27740	function SubstituteChars($html)
27741	{
27742		// only substitute characters between tags
27743		if (count($this->substitute)) {
27744			$a = preg_split('/(<.*?>)/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
27745			$html = '';
27746			foreach ($a as $i => $e) {
27747				if ($i % 2 == 0) {
27748					$e = strtr($e, $this->substitute);
27749				}
27750				$html .= $e;
27751			}
27752		}
27753		return $html;
27754	}
27755
27756	function SubstituteCharsSIP(&$writehtml_a, &$writehtml_i, &$writehtml_e)
27757	{
27758		if (preg_match("/^(.*?)([\x{20000}-\x{2FFFF}]+)(.*)/u", $writehtml_e, $m)) {
27759			if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {
27760				$font = $this->CurrentFont['sipext'];
27761				if (!in_array($font, $this->available_unifonts)) {
27762					return 0;
27763				}
27764				$writehtml_a[$writehtml_i] = $writehtml_e = $m[1];
27765				array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
27766				$this->subPos = $writehtml_i;
27767				return 4;
27768			}
27769		}
27770		return 0;
27771	}
27772
27773	// If core font is selected in document which is not onlyCoreFonts - substitute with non-core font
27774	function SubstituteCharsNonCore(&$writehtml_a, &$writehtml_i, &$writehtml_e)
27775	{
27776		// Ignore if in Textarea
27777		if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {
27778			return 0;
27779		}
27780		if (mb_convert_encoding(mb_convert_encoding($writehtml_e, $this->mb_enc, "UTF-8"), "UTF-8", $this->mb_enc) == $writehtml_e) {
27781			return 0;
27782		}
27783		$cw = &$this->CurrentFont['cw'];
27784		$unicode = $this->UTF8StringToArray($writehtml_e, false);
27785		$start = -1;
27786		$end = 0;
27787		$flag = 0;
27788		$ftype = '';
27789		$u = [];
27790		if (!$this->subArrMB) {
27791			require __DIR__ . '/../data/subs_core.php';
27792			$this->subArrMB['a'] = $aarr;
27793			$this->subArrMB['s'] = $sarr;
27794			$this->subArrMB['z'] = $zarr;
27795		}
27796		foreach ($unicode as $c => $char) {
27797			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)) {
27798				if ($flag == 0) {
27799					$start = $c;
27800				}
27801				$flag = 1;
27802				$u[] = $char;
27803			} elseif ($flag > 0) {
27804				$end = $c - 1;
27805				break;
27806			}
27807		}
27808		if ($flag > 0 && !$end) {
27809			$end = count($unicode) - 1;
27810		}
27811		if ($start == -1) {
27812			return 0;
27813		}
27814
27815		// TRY IN BACKUP SUBS FONT
27816		if (!is_array($this->backupSubsFont)) {
27817			$this->backupSubsFont = ["$this->backupSubsFont"];
27818		}
27819
27820		foreach ($this->backupSubsFont as $bsfctr => $bsf) {
27821			if ($this->fonttrans[$bsf] == 'chelvetica' || $this->fonttrans[$bsf] == 'ctimes' || $this->fonttrans[$bsf] == 'ccourier') {
27822				continue;
27823			}
27824
27825			$font = $bsf;
27826			unset($cw);
27827			$cw = '';
27828
27829			if (isset($this->fonts[$font])) {
27830				$cw = &$this->fonts[$font]['cw'];
27831			} elseif ($this->fontCache->has($font . '.cw.dat')) {
27832				$cw = $this->fontCache->load($font . '.cw.dat');
27833			} else {
27834				$prevFontFamily = $this->FontFamily;
27835				$prevFontStyle = $this->currentfontstyle;
27836				$prevFontSizePt = $this->FontSizePt;
27837				$this->SetFont($bsf, '', '', false);
27838				$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
27839			}
27840
27841			if (!$cw) {
27842				continue;
27843			}
27844
27845			$l = 0;
27846			foreach ($u as $char) {
27847				if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) {
27848					$l++;
27849				} else {
27850					if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font
27851						$cont = mb_substr($writehtml_e, $start + 1);
27852						$writehtml_e = mb_substr($writehtml_e, 0, $start + 1, 'UTF-8');
27853						array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]);
27854						$this->subPos = $writehtml_i + 1;
27855						return 2;
27856					} else {
27857						break;
27858					}
27859				}
27860			}
27861
27862			if ($l > 0) {
27863				$patt = mb_substr($writehtml_e, $start, $l, 'UTF-8');
27864				if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
27865					$writehtml_e = $m[1];
27866					array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
27867					$this->subPos = $writehtml_i + 3;
27868					return 4;
27869				}
27870			}
27871		}
27872
27873		unset($cw);
27874		return 0;
27875	}
27876
27877	function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e)
27878	{
27879		// Ignore if in Textarea
27880		if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {
27881			return 0;
27882		}
27883		$cw = &$this->CurrentFont['cw'];
27884		$unicode = $this->UTF8StringToArray($writehtml_e, false);
27885		$start = -1;
27886		$end = 0;
27887		$flag = 0;
27888		$ftype = '';
27889		$u = [];
27890		foreach ($unicode as $c => $char) {
27891			if (($flag == 0 || $flag == 2) && (!$this->_charDefined($cw, $char) || ($flag == 2 && $char == 32)) && $this->checkSIP && $char > 131071) {  // Unicode Plane 2 (SIP)
27892				if (in_array($this->FontFamily, $this->available_CJK_fonts)) {
27893					return 0;
27894				}
27895				if ($flag == 0) {
27896					$start = $c;
27897				}
27898				$flag = 2;
27899				$u[] = $char;
27900			} // elseif (($flag == 0 || $flag==1) && $char != 173 && !$this->_charDefined($cw,$char) && ($char<1423 ||  ($char>3583 && $char < 11263))) {
27901			elseif (($flag == 0 || $flag == 1) && $char != 173 && (!$this->_charDefined($cw, $char) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) {
27902				if ($flag == 0) {
27903					$start = $c;
27904				}
27905				$flag = 1;
27906				$u[] = $char;
27907			} elseif ($flag > 0) {
27908				$end = $c - 1;
27909				break;
27910			}
27911		}
27912		if ($flag > 0 && !$end) {
27913			$end = count($unicode) - 1;
27914		}
27915		if ($start == -1) {
27916			return 0;
27917		}
27918
27919		if ($flag == 2) {  // SIP
27920			// Check if current CJK font has a ext-B related font
27921			if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {
27922				$font = $this->CurrentFont['sipext'];
27923				unset($cw);
27924				$cw = '';
27925				if (isset($this->fonts[$font])) {
27926					$cw = &$this->fonts[$font]['cw'];
27927				} elseif ($this->fontCache->has($font . '.cw.dat')) {
27928					$cw = $this->fontCache->load($font . '.cw.dat');
27929				} else {
27930					$prevFontFamily = $this->FontFamily;
27931					$prevFontStyle = $this->currentfontstyle;
27932					$prevFontSizePt = $this->FontSizePt;
27933					$this->SetFont($font, '', '', false);
27934					$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
27935				}
27936
27937				if (!$cw) {
27938					return 0;
27939				}
27940
27941				$l = 0;
27942				foreach ($u as $char) {
27943					if ($this->_charDefined($cw, $char) || $char > 131071) {
27944						$l++;
27945					} else {
27946						break;
27947					}
27948				}
27949
27950				if ($l > 0) {
27951					$patt = mb_substr($writehtml_e, $start, $l);
27952					if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
27953						$writehtml_e = $m[1];
27954						array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
27955						$this->subPos = $writehtml_i + 3;
27956						return 4;
27957					}
27958				}
27959			}
27960			// Check Backup SIP font (defined in Config\FontVariables)
27961			if (isset($this->backupSIPFont) && $this->backupSIPFont) {
27962				if ($this->currentfontfamily != $this->backupSIPFont) {
27963					$font = $this->backupSIPFont;
27964				} else {
27965					unset($cw);
27966					return 0;
27967				}
27968
27969				unset($cw);
27970				$cw = '';
27971
27972				if (isset($this->fonts[$font])) {
27973					$cw = &$this->fonts[$font]['cw'];
27974				} elseif ($this->fontCache->has($font . '.cw.dat')) {
27975					$cw = $this->fontCache->load($font . '.cw.dat');
27976				} else {
27977					$prevFontFamily = $this->FontFamily;
27978					$prevFontStyle = $this->currentfontstyle;
27979					$prevFontSizePt = $this->FontSizePt;
27980					$this->SetFont($this->backupSIPFont, '', '', false);
27981					$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
27982				}
27983
27984				if (!$cw) {
27985					return 0;
27986				}
27987
27988				$l = 0;
27989				foreach ($u as $char) {
27990					if ($this->_charDefined($cw, $char) || $char > 131071) {
27991						$l++;
27992					} else {
27993						break;
27994					}
27995				}
27996				if ($l > 0) {
27997					$patt = mb_substr($writehtml_e, $start, $l);
27998					if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
27999						$writehtml_e = $m[1];
28000						array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
28001						$this->subPos = $writehtml_i + 3;
28002						return 4;
28003					}
28004				}
28005			}
28006			return 0;
28007		}
28008
28009
28010		// FIRST TRY CORE FONTS (when appropriate)
28011		if (!$this->PDFA && !$this->PDFX && !$this->biDirectional) {  // mPDF 6
28012			$repl = [];
28013			if (!$this->subArrMB) {
28014				require __DIR__ . '/../data/subs_core.php';
28015				$this->subArrMB['a'] = $aarr;
28016				$this->subArrMB['s'] = $sarr;
28017				$this->subArrMB['z'] = $zarr;
28018			}
28019			if (isset($this->subArrMB['a'][$u[0]])) {
28020				$font = 'tta';
28021				$ftype = 'C';
28022				foreach ($u as $char) {
28023					if (isset($this->subArrMB['a'][$char])) {
28024						$repl[] = $this->subArrMB['a'][$char];
28025					} else {
28026						break;
28027					}
28028				}
28029			} elseif (isset($this->subArrMB['z'][$u[0]])) {
28030				$font = 'ttz';
28031				$ftype = 'C';
28032				foreach ($u as $char) {
28033					if (isset($this->subArrMB['z'][$char])) {
28034						$repl[] = $this->subArrMB['z'][$char];
28035					} else {
28036						break;
28037					}
28038				}
28039			} elseif (isset($this->subArrMB['s'][$u[0]])) {
28040				$font = 'tts';
28041				$ftype = 'C';
28042				foreach ($u as $char) {
28043					if (isset($this->subArrMB['s'][$char])) {
28044						$repl[] = $this->subArrMB['s'][$char];
28045					} else {
28046						break;
28047					}
28048				}
28049			}
28050			if ($ftype == 'C') {
28051				$patt = mb_substr($writehtml_e, $start, count($repl));
28052				if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
28053					$writehtml_e = $m[1];
28054					array_splice($writehtml_a, $writehtml_i + 1, 0, [$font, implode('|', $repl), '/' . $font, $m[3]]); // e.g. <tts>
28055					$this->subPos = $writehtml_i + 3;
28056					return 4;
28057				}
28058				return 0;
28059			}
28060		}
28061
28062		// LASTLY TRY IN BACKUP SUBS FONT
28063		if (!is_array($this->backupSubsFont)) {
28064			$this->backupSubsFont = ["$this->backupSubsFont"];
28065		}
28066		foreach ($this->backupSubsFont as $bsfctr => $bsf) {
28067			if ($this->currentfontfamily != $bsf) {
28068				$font = $bsf;
28069			} else {
28070				continue;
28071			}
28072
28073			unset($cw);
28074			$cw = '';
28075
28076			if (isset($this->fonts[$font])) {
28077				$cw = &$this->fonts[$font]['cw'];
28078			} elseif ($this->fontCache->has($font . '.cw.dat')) {
28079				$cw = $this->fontCache->load($font . '.cw.dat');
28080			} else {
28081				$prevFontFamily = $this->FontFamily;
28082				$prevFontStyle = $this->currentfontstyle;
28083				$prevFontSizePt = $this->FontSizePt;
28084				$this->SetFont($bsf, '', '', false);
28085				$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
28086			}
28087
28088			if (!$cw) {
28089				continue;
28090			}
28091
28092			$l = 0;
28093			foreach ($u as $char) {
28094				if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) {  // Arabic and Indic
28095					$l++;
28096				} else {
28097					if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font
28098						$cont = mb_substr($writehtml_e, $start + 1);
28099						$writehtml_e = mb_substr($writehtml_e, 0, $start + 1);
28100						array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]);
28101						$this->subPos = $writehtml_i + 1;
28102						return 2;
28103					} else {
28104						break;
28105					}
28106				}
28107			}
28108			if ($l > 0) {
28109				$patt = mb_substr($writehtml_e, $start, $l);
28110				if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
28111					$writehtml_e = $m[1];
28112					array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]]);
28113					$this->subPos = $writehtml_i + 3;
28114					return 4;
28115				}
28116			}
28117		}
28118
28119		unset($cw);
28120		return 0;
28121	}
28122
28123	function setHiEntitySubstitutions()
28124	{
28125		$entarr = [
28126			'nbsp' => '160', 'iexcl' => '161', 'cent' => '162', 'pound' => '163', 'curren' => '164', 'yen' => '165', 'brvbar' => '166', 'sect' => '167',
28127			'uml' => '168', 'copy' => '169', 'ordf' => '170', 'laquo' => '171', 'not' => '172', 'shy' => '173', 'reg' => '174', 'macr' => '175',
28128			'deg' => '176', 'plusmn' => '177', 'sup2' => '178', 'sup3' => '179', 'acute' => '180', 'micro' => '181', 'para' => '182', 'middot' => '183',
28129			'cedil' => '184', 'sup1' => '185', 'ordm' => '186', 'raquo' => '187', 'frac14' => '188', 'frac12' => '189', 'frac34' => '190',
28130			'iquest' => '191', 'Agrave' => '192', 'Aacute' => '193', 'Acirc' => '194', 'Atilde' => '195', 'Auml' => '196', 'Aring' => '197',
28131			'AElig' => '198', 'Ccedil' => '199', 'Egrave' => '200', 'Eacute' => '201', 'Ecirc' => '202', 'Euml' => '203', 'Igrave' => '204',
28132			'Iacute' => '205', 'Icirc' => '206', 'Iuml' => '207', 'ETH' => '208', 'Ntilde' => '209', 'Ograve' => '210', 'Oacute' => '211',
28133			'Ocirc' => '212', 'Otilde' => '213', 'Ouml' => '214', 'times' => '215', 'Oslash' => '216', 'Ugrave' => '217', 'Uacute' => '218',
28134			'Ucirc' => '219', 'Uuml' => '220', 'Yacute' => '221', 'THORN' => '222', 'szlig' => '223', 'agrave' => '224', 'aacute' => '225',
28135			'acirc' => '226', 'atilde' => '227', 'auml' => '228', 'aring' => '229', 'aelig' => '230', 'ccedil' => '231', 'egrave' => '232',
28136			'eacute' => '233', 'ecirc' => '234', 'euml' => '235', 'igrave' => '236', 'iacute' => '237', 'icirc' => '238', 'iuml' => '239',
28137			'eth' => '240', 'ntilde' => '241', 'ograve' => '242', 'oacute' => '243', 'ocirc' => '244', 'otilde' => '245', 'ouml' => '246',
28138			'divide' => '247', 'oslash' => '248', 'ugrave' => '249', 'uacute' => '250', 'ucirc' => '251', 'uuml' => '252', 'yacute' => '253',
28139			'thorn' => '254', 'yuml' => '255', 'OElig' => '338', 'oelig' => '339', 'Scaron' => '352', 'scaron' => '353', 'Yuml' => '376',
28140			'fnof' => '402', 'circ' => '710', 'tilde' => '732', 'Alpha' => '913', 'Beta' => '914', 'Gamma' => '915', 'Delta' => '916',
28141			'Epsilon' => '917', 'Zeta' => '918', 'Eta' => '919', 'Theta' => '920', 'Iota' => '921', 'Kappa' => '922', 'Lambda' => '923',
28142			'Mu' => '924', 'Nu' => '925', 'Xi' => '926', 'Omicron' => '927', 'Pi' => '928', 'Rho' => '929', 'Sigma' => '931', 'Tau' => '932',
28143			'Upsilon' => '933', 'Phi' => '934', 'Chi' => '935', 'Psi' => '936', 'Omega' => '937', 'alpha' => '945', 'beta' => '946', 'gamma' => '947',
28144			'delta' => '948', 'epsilon' => '949', 'zeta' => '950', 'eta' => '951', 'theta' => '952', 'iota' => '953', 'kappa' => '954',
28145			'lambda' => '955', 'mu' => '956', 'nu' => '957', 'xi' => '958', 'omicron' => '959', 'pi' => '960', 'rho' => '961', 'sigmaf' => '962',
28146			'sigma' => '963', 'tau' => '964', 'upsilon' => '965', 'phi' => '966', 'chi' => '967', 'psi' => '968', 'omega' => '969',
28147			'thetasym' => '977', 'upsih' => '978', 'piv' => '982', 'ensp' => '8194', 'emsp' => '8195', 'thinsp' => '8201', 'zwnj' => '8204',
28148			'zwj' => '8205', 'lrm' => '8206', 'rlm' => '8207', 'ndash' => '8211', 'mdash' => '8212', 'lsquo' => '8216', 'rsquo' => '8217',
28149			'sbquo' => '8218', 'ldquo' => '8220', 'rdquo' => '8221', 'bdquo' => '8222', 'dagger' => '8224', 'Dagger' => '8225', 'bull' => '8226',
28150			'hellip' => '8230', 'permil' => '8240', 'prime' => '8242', 'Prime' => '8243', 'lsaquo' => '8249', 'rsaquo' => '8250', 'oline' => '8254',
28151			'frasl' => '8260', 'euro' => '8364', 'image' => '8465', 'weierp' => '8472', 'real' => '8476', 'trade' => '8482', 'alefsym' => '8501',
28152			'larr' => '8592', 'uarr' => '8593', 'rarr' => '8594', 'darr' => '8595', 'harr' => '8596', 'crarr' => '8629', 'lArr' => '8656',
28153			'uArr' => '8657', 'rArr' => '8658', 'dArr' => '8659', 'hArr' => '8660', 'forall' => '8704', 'part' => '8706', 'exist' => '8707',
28154			'empty' => '8709', 'nabla' => '8711', 'isin' => '8712', 'notin' => '8713', 'ni' => '8715', 'prod' => '8719', 'sum' => '8721',
28155			'minus' => '8722', 'lowast' => '8727', 'radic' => '8730', 'prop' => '8733', 'infin' => '8734', 'ang' => '8736', 'and' => '8743',
28156			'or' => '8744', 'cap' => '8745', 'cup' => '8746', 'int' => '8747', 'there4' => '8756', 'sim' => '8764', 'cong' => '8773',
28157			'asymp' => '8776', 'ne' => '8800', 'equiv' => '8801', 'le' => '8804', 'ge' => '8805', 'sub' => '8834', 'sup' => '8835', 'nsub' => '8836',
28158			'sube' => '8838', 'supe' => '8839', 'oplus' => '8853', 'otimes' => '8855', 'perp' => '8869', 'sdot' => '8901', 'lceil' => '8968',
28159			'rceil' => '8969', 'lfloor' => '8970', 'rfloor' => '8971', 'lang' => '9001', 'rang' => '9002', 'loz' => '9674', 'spades' => '9824',
28160			'clubs' => '9827', 'hearts' => '9829', 'diams' => '9830',
28161		];
28162		foreach ($entarr as $key => $val) {
28163			$this->entsearch[] = '&' . $key . ';';
28164			$this->entsubstitute[] = UtfString::code2utf($val);
28165		}
28166	}
28167
28168	function SubstituteHiEntities($html)
28169	{
28170		// converts html_entities > ASCII 127 to unicode
28171		// Leaves in particular &lt; to distinguish from tag marker
28172		if (!is_null($this->entsearch)) {
28173			$html = str_replace($this->entsearch, $this->entsubstitute, $html);
28174		}
28175		return $html;
28176	}
28177
28178	// Edited v1.2 Pass by reference; option to continue if invalid UTF-8 chars
28179	function is_utf8(&$string)
28180	{
28181		if ($string === mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32")) {
28182			return true;
28183		} else {
28184			if ($this->ignore_invalid_utf8) {
28185				$string = mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32");
28186				return true;
28187			} else {
28188				return false;
28189			}
28190		}
28191	}
28192
28193	function purify_utf8($html, $lo = true)
28194	{
28195		// For HTML
28196		// Checks string is valid UTF-8 encoded
28197		// converts html_entities > ASCII 127 to UTF-8
28198		// Only exception - leaves low ASCII entities e.g. &lt; &amp; etc.
28199		// Leaves in particular &lt; to distinguish from tag marker
28200		if (!$this->is_utf8($html)) {
28201			while (mb_convert_encoding(mb_convert_encoding($html, "UTF-32", "UTF-8"), "UTF-8", "UTF-32") != $html) {
28202				$a = iconv('UTF-8', 'UTF-8', $html);
28203				// echo ($a);
28204				$pos = $start = strlen($a);
28205				$err = '';
28206				while (ord(substr($html, $pos, 1)) > 128) {
28207					$err .= '[[#' . ord(substr($html, $pos, 1)) . ']]';
28208					$pos++;
28209				}
28210				$this->logger->error($err, ['context' => LogContext::UTF8]);
28211				$html = substr($html, $pos);
28212			}
28213			throw new \Mpdf\MpdfException("HTML contains invalid UTF-8 character(s). See log for further details");
28214		}
28215		$html = preg_replace("/\r/", "", $html);
28216
28217		// converts html_entities > ASCII 127 to UTF-8
28218		// Leaves in particular &lt; to distinguish from tag marker
28219		$html = $this->SubstituteHiEntities($html);
28220
28221		// converts all &#nnn; or &#xHHH; to UTF-8 multibyte
28222		// If $lo==true then includes ASCII < 128
28223		$html = UtfString::strcode2utf($html, $lo);
28224		return ($html);
28225	}
28226
28227	function purify_utf8_text($txt)
28228	{
28229		// For TEXT
28230		// Make sure UTF-8 string of characters
28231		if (!$this->is_utf8($txt)) {
28232			throw new \Mpdf\MpdfException("Text contains invalid UTF-8 character(s)");
28233		}
28234
28235		$txt = preg_replace("/\r/", "", $txt);
28236
28237		return ($txt);
28238	}
28239
28240	function all_entities_to_utf8($txt)
28241	{
28242		// converts txt_entities > ASCII 127 to UTF-8
28243		// Leaves in particular &lt; to distinguish from tag marker
28244		$txt = $this->SubstituteHiEntities($txt);
28245
28246		// converts all &#nnn; or &#xHHH; to UTF-8 multibyte
28247		$txt = UtfString::strcode2utf($txt);
28248
28249		$txt = $this->lesser_entity_decode($txt);
28250		return ($txt);
28251	}
28252
28253	// ====================================================
28254		/* -- BARCODES -- */
28255	// UPC/EAN barcode
28256	// EAN13, EAN8, UPCA, UPCE, ISBN, ISSN
28257	// Accepts 12 or 13 digits with or without - hyphens
28258	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)
28259	{
28260		if (empty($code)) {
28261			return;
28262		}
28263		$codestr = $code;
28264		$code = preg_replace('/\-/', '', $code);
28265
28266		$this->barcode = new Barcode();
28267		if ($btype == 'ISSN' || $btype == 'ISBN') {
28268			$arrcode = $this->barcode->getBarcodeArray($code, 'EAN13');
28269		} else {
28270			$arrcode = $this->barcode->getBarcodeArray($code, $btype);
28271		}
28272
28273		if ($arrcode === false) {
28274			throw new \Mpdf\MpdfException('Error in barcode string: ' . $codestr);
28275		}
28276		if ((($btype == 'EAN13' || $btype == 'ISBN' || $btype == 'ISSN') && strlen($code) == 12) || ($btype == 'UPCA' && strlen($code) == 11) || ($btype == 'UPCE' && strlen($code) == 11) || ($btype == 'EAN8' && strlen($code) == 7)) {
28277			$code .= $arrcode['checkdigit'];
28278			if (stristr($codestr, '-')) {
28279				$codestr .= '-' . $arrcode['checkdigit'];
28280			} else {
28281				$codestr .= $arrcode['checkdigit'];
28282			}
28283		}
28284		if ($btype == 'ISBN') {
28285			$codestr = 'ISBN ' . $codestr;
28286		}
28287		if ($btype == 'ISSN') {
28288			$codestr = 'ISSN ' . $codestr;
28289		}
28290
28291		if (empty($x)) {
28292			$x = $this->x;
28293		}
28294		if (empty($y)) {
28295			$y = $this->y;
28296		}
28297		// set foreground color
28298		$prevDrawColor = $this->DrawColor;
28299		$prevTextColor = $this->TextColor;
28300		$prevFillColor = $this->FillColor;
28301		$lw = $this->LineWidth;
28302		$this->SetLineWidth(0.01);
28303
28304		$size /= $k; // in case resized in a table
28305
28306		$xres = $arrcode['nom-X'] * $size;
28307		$llm = $arrcode['lightmL'] * $arrcode['nom-X'] * $size; // Left Light margin
28308		$rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin
28309
28310		$bcw = ($arrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size
28311
28312		$fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
28313		$ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding
28314
28315		$fbwi = $fbw - 2; // Full barcode width incl. light margins - 2mm - for isbn string
28316		// cf. http://www.gs1uk.org/downloads/bar_code/Bar coding getting it right.pdf
28317		$num_height = 3 * $size;     // Height of numerals
28318		$fbh = $arrcode['nom-H'] * $size * $height;  // Full barcode height incl. numerals
28319		$bch = $fbh - (1.5 * $size);     // Barcode height of bars	 (3mm for numerals)
28320
28321		if (($btype == 'EAN13' && $showtext) || $btype == 'ISSN' || $btype == 'ISBN') { // Add height for ISBN string + margin from top of bars
28322			$tisbnm = 1.5 * $size; // Top margin between isbn (if shown) & bars
28323			$codestr_fontsize = 2.1 * $size;
28324			$paddingT += $codestr_fontsize + $tisbnm;
28325		}
28326		$oh = $fbh + $paddingT + $paddingB;  // Full overall height incl. user-defined padding
28327		// PRINT border background color
28328		$xpos = $x;
28329		$ypos = $y;
28330		if ($col) {
28331			$this->SetDColor($col);
28332			$this->SetTColor($col);
28333		} else {
28334			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
28335			$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
28336		}
28337		if ($bgcol) {
28338			$this->SetFColor($bgcol);
28339		} else {
28340			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
28341		}
28342		if (!$bgcol && !$col) { // fn. called directly - not via HTML
28343			if ($border) {
28344				$fillb = 'DF';
28345			} else {
28346				$fillb = 'F';
28347			}
28348			$this->Rect($xpos, $ypos, $ow, $oh, $fillb);
28349		}
28350
28351
28352		// PRINT BARS
28353		$xpos = $x + $paddingL + $llm;
28354		$ypos = $y + $paddingT;
28355		if ($col) {
28356			$this->SetFColor($col);
28357		} else {
28358			$this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
28359		}
28360		if ($arrcode !== false) {
28361			foreach ($arrcode["bcode"] as $v) {
28362				$bw = ($v["w"] * $xres);
28363				if ($v["t"]) {
28364					// draw a vertical bar
28365					$this->Rect($xpos, $ypos, $bw, $bch, 'F');
28366				}
28367				$xpos += $bw;
28368			}
28369		}
28370
28371
28372		// print text
28373		$prevFontFamily = $this->FontFamily;
28374		$prevFontStyle = $this->FontStyle;
28375		$prevFontSizePt = $this->FontSizePt;
28376
28377		// ISBN string
28378		if (($btype == 'EAN13' && $showtext) || $btype == 'ISBN' || $btype == 'ISSN') {
28379			if ($this->onlyCoreFonts) {
28380				$this->SetFont('chelvetica');
28381			} else {
28382				$this->SetFont('sans');
28383			}
28384
28385			if ($bgcol) {
28386				$this->SetFColor($bgcol);
28387			} else {
28388				$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
28389			}
28390			$this->x = $x + $paddingL + 1; // 1mm left margin (cf. $fbwi above)
28391			// max width is $fbwi
28392			$loop = 0;
28393			while ($loop == 0) {
28394				$this->SetFontSize($codestr_fontsize * 1.4 * Mpdf::SCALE, false); // don't write
28395				$sz = $this->GetStringWidth($codestr);
28396				if ($sz > $fbwi) {
28397					$codestr_fontsize -= 0.1;
28398				} else {
28399					$loop ++;
28400				}
28401			}
28402			$this->SetFont('', '', $codestr_fontsize * 1.4 * Mpdf::SCALE, true, true); // * 1.4 because font height is only 7/10 of given mm
28403			// WORD SPACING
28404			if ($fbwi > $sz) {
28405				$xtra = $fbwi - $sz;
28406				$charspacing = $xtra / (strlen($codestr) - 1);
28407				if ($charspacing) {
28408					$this->_out(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE));
28409				}
28410			}
28411			$this->y = $y + $paddingT - ($codestr_fontsize ) - $tisbnm;
28412			$this->Cell($fbw, $codestr_fontsize, $codestr);
28413			if ($charspacing) {
28414				$this->_out('BT 0 Tc ET');
28415			}
28416		}
28417
28418
28419		// Bottom NUMERALS
28420		// mPDF 5.7.4
28421		if ($this->onlyCoreFonts) {
28422			$this->SetFont('ccourier');
28423			$fh = 1.3;
28424		} else {
28425			$this->SetFont('ocrb');
28426			$fh = 1.06;
28427		}
28428		$charRO = '';
28429		if ($btype == 'EAN13' || $btype == 'ISBN' || $btype == 'ISSN') {
28430			$outerfontsize = 3; // Inner fontsize = 3
28431			$outerp = $xres * 4;
28432			$innerp = $xres * 2.5;
28433			$textw = ($bcw * 0.5) - $outerp - $innerp;
28434			$chars = 6; // number of numerals in each half
28435			$charLO = substr($code, 0, 1); // Left Outer
28436			$charLI = substr($code, 1, 6); // Left Inner
28437			$charRI = substr($code, 7, 6); // Right Inner
28438			if (!$supplement) {
28439				$charRO = '>'; // Right Outer
28440			}
28441		} elseif ($btype == 'UPCA') {
28442			$outerfontsize = 2.3; // Inner fontsize = 3
28443			$outerp = $xres * 10;
28444			$innerp = $xres * 2.5;
28445			$textw = ($bcw * 0.5) - $outerp - $innerp;
28446			$chars = 5;
28447			$charLO = substr($code, 0, 1); // Left Outer
28448			$charLI = substr($code, 1, 5); // Left Inner
28449			$charRI = substr($code, 6, 5); // Right Inner
28450			$charRO = substr($code, 11, 1); // Right Outer
28451		} elseif ($btype == 'UPCE') {
28452			$outerfontsize = 2.3; // Inner fontsize = 3
28453			$outerp = $xres * 4;
28454			$innerp = 0;
28455			$textw = ($bcw * 0.5) - $outerp - $innerp;
28456			$chars = 3;
28457			$upce_code = $arrcode['code'];
28458			$charLO = substr($code, 0, 1); // Left Outer
28459			$charLI = substr($upce_code, 0, 3); // Left Inner
28460			$charRI = substr($upce_code, 3, 3); // Right Inner
28461			$charRO = substr($code, 11, 1); // Right Outer
28462		} elseif ($btype == 'EAN8') {
28463			$outerfontsize = 3; // Inner fontsize = 3
28464			$outerp = $xres * 4;
28465			$innerp = $xres * 2.5;
28466			$textw = ($bcw * 0.5) - $outerp - $innerp;
28467			$chars = 4;
28468			$charLO = '<'; // Left Outer
28469			$charLI = substr($code, 0, 4); // Left Inner
28470			$charRI = substr($code, 4, 4); // Right Inner
28471			if (!$supplement) {
28472				$charRO = '>'; // Right Outer
28473			}
28474		}
28475
28476		$this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
28477
28478		if (!$this->usingCoreFont) {
28479			$cw = $this->_getCharWidth($this->CurrentFont['cw'], 32) * 3 * $fh * $size / 1000;
28480		} // character width at 3mm
28481		else {
28482			$cw = 600 * 3 * $fh * $size / 1000;
28483		} // mPDF 5.7.4
28484		// Outer left character
28485		$y_text = $y + $paddingT + $bch - ($num_height / 2);
28486		$y_text_outer = $y + $paddingT + $bch - ($num_height * ($outerfontsize / 3) / 2);
28487
28488		$this->x = $x + $paddingL - ($cw * ($outerfontsize / 3) * 0.1); // 0.1 is correction as char does not fill full width;
28489		$this->y = $y_text_outer;
28490		$this->Cell($cw, $num_height, $charLO);
28491
28492		// WORD SPACING for inner chars
28493		$xtra = $textw - ($cw * $chars);
28494		$charspacing = $xtra / ($chars - 1);
28495		if ($charspacing) {
28496			$this->_out(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE));
28497		}
28498
28499		if ($bgcol) {
28500			$this->SetFColor($bgcol);
28501		} else {
28502			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
28503		}
28504
28505		$this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
28506		// Inner left half characters
28507		$this->x = $x + $paddingL + $llm + $outerp;
28508		$this->y = $y_text;
28509		$this->Cell($textw, $num_height, $charLI, 0, 0, '', 1);
28510
28511		// Inner right half characters
28512		$this->x = $x + $paddingL + $llm + ($bcw * 0.5) + $innerp;
28513		$this->y = $y_text;
28514		$this->Cell($textw, $num_height, $charRI, 0, 0, '', 1);
28515
28516		if ($charspacing) {
28517			$this->_out('BT 0 Tc ET');
28518		}
28519
28520		// Outer Right character
28521		$this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
28522
28523		$this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * ($outerfontsize / 3) * 0.9); // 0.9 is correction as char does not fill full width
28524		$this->y = $y_text_outer;
28525		$this->Cell($cw * ($outerfontsize / 3), $num_height, $charRO, 0, 0, 'R');
28526
28527		if ($supplement) { // EAN-2 or -5 Supplement
28528			// PRINT BARS
28529			$supparrcode = $this->barcode->getBarcodeArray($supplement_code, 'EAN' . $supplement);
28530			if ($supparrcode === false) {
28531				throw new \Mpdf\MpdfException('Error in barcode string (supplement): ' . $codestr . ' ' . $supplement_code);
28532			}
28533			if (strlen($supplement_code) != $supplement) {
28534				throw new \Mpdf\MpdfException('Barcode supplement incorrect: ' . $supplement_code);
28535			}
28536			$llm = $fbw - (($arrcode['lightmR'] - $supparrcode['sepM']) * $arrcode['nom-X'] * $size); // Left Light margin
28537			$rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin
28538
28539			$bcw = ($supparrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size
28540
28541			$fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
28542			$ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding
28543			$bch = $fbh - (1.5 * $size) - ($num_height + 0.5);  // Barcode height of bars	 (3mm for numerals)
28544
28545			$xpos = $x + $paddingL + $llm;
28546			$ypos = $y + $paddingT + $num_height + 0.5;
28547			if ($col) {
28548				$this->SetFColor($col);
28549			} else {
28550				$this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
28551			}
28552			if ($supparrcode !== false) {
28553				foreach ($supparrcode["bcode"] as $v) {
28554					$bw = ($v["w"] * $xres);
28555					if ($v["t"]) {
28556						// draw a vertical bar
28557						$this->Rect($xpos, $ypos, $bw, $bch, 'F');
28558					}
28559					$xpos += $bw;
28560				}
28561			}
28562
28563			// Characters
28564			if ($bgcol) {
28565				$this->SetFColor($bgcol);
28566			} else {
28567				$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
28568			}
28569			$this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
28570			$this->x = $x + $paddingL + $llm;
28571			$this->y = $y + $paddingT;
28572			$this->Cell($bcw, $num_height, $supplement_code, 0, 0, 'C');
28573
28574			// Outer Right character (light margin)
28575			$this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)
28576			$this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * 0.9); // 0.9 is correction as char does not fill full width
28577			$this->y = $y + $paddingT;
28578			$this->Cell($cw * ($outerfontsize / 3), $num_height, '>', 0, 0, 'R');
28579		}
28580
28581		// Restore **************
28582		$this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
28583		$this->DrawColor = $prevDrawColor;
28584		$this->TextColor = $prevTextColor;
28585		$this->FillColor = $prevFillColor;
28586		$this->SetLineWidth($lw);
28587		$this->SetY($y);
28588	}
28589
28590	// ====================================================
28591	// POSTAL and OTHER barcodes
28592	function WriteBarcode2($code, $x = '', $y = '', $size = 1, $height = 1, $bgcol = false, $col = false, $btype = 'IMB', $print_ratio = '', $k = 1)
28593	{
28594		if (empty($code)) {
28595			return;
28596		}
28597
28598		$this->barcode = new Barcode();
28599		$arrcode = $this->barcode->getBarcodeArray($code, $btype, $print_ratio);
28600
28601		if (empty($x)) {
28602			$x = $this->x;
28603		}
28604		if (empty($y)) {
28605			$y = $this->y;
28606		}
28607		$prevDrawColor = $this->DrawColor;
28608		$prevTextColor = $this->TextColor;
28609		$prevFillColor = $this->FillColor;
28610		$lw = $this->LineWidth;
28611		$this->SetLineWidth(0.01);
28612		$size /= $k; // in case resized in a table
28613		$xres = $arrcode['nom-X'] * $size;
28614
28615		if ($btype == 'IMB' || $btype == 'RM4SCC' || $btype == 'KIX' || $btype == 'POSTNET' || $btype == 'PLANET') {
28616			$llm = $arrcode['quietL'] / $k; // Left Quiet margin
28617			$rlm = $arrcode['quietR'] / $k; // Right Quiet margin
28618			$tlm = $blm = $arrcode['quietTB'] / $k;
28619			$height = 1;  // Overrides
28620		} elseif (in_array($btype, ['C128A', 'C128B', 'C128C', 'EAN128A', 'EAN128B', 'EAN128C', 'C39', 'C39+', 'C39E', 'C39E+', 'S25', 'S25+', 'I25', 'I25+', 'I25B', 'I25B+', 'C93', 'MSI', 'MSI+', 'CODABAR', 'CODE11'])) {
28621			$llm = $arrcode['lightmL'] * $xres; // Left Quiet margin
28622			$rlm = $arrcode['lightmR'] * $xres; // Right Quiet margin
28623			$tlm = $blm = $arrcode['lightTB'] * $xres * $height;
28624		}
28625
28626
28627		$bcw = ($arrcode["maxw"] * $xres);
28628		$fbw = $bcw + $llm + $rlm;  // Full barcode width incl. light margins
28629
28630		$bch = ($arrcode["nom-H"] * $size * $height);
28631		$fbh = $bch + $tlm + $blm;  // Full barcode height
28632		// PRINT border background color
28633		$xpos = $x;
28634		$ypos = $y;
28635		if ($col) {
28636			$this->SetDColor($col);
28637			$this->SetTColor($col);
28638		} else {
28639			$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
28640			$this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
28641		}
28642		if ($bgcol) {
28643			$this->SetFColor($bgcol);
28644		} else {
28645			$this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));
28646		}
28647
28648		// PRINT BARS
28649		if ($col) {
28650			$this->SetFColor($col);
28651		} else {
28652			$this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
28653		}
28654		$xpos = $x + $llm;
28655
28656		if ($arrcode !== false) {
28657			foreach ($arrcode["bcode"] as $v) {
28658				$bw = ($v["w"] * $xres);
28659				if ($v["t"]) {
28660					$ypos = $y + $tlm + ($bch * $v['p'] / $arrcode['maxh']);
28661					$this->Rect($xpos, $ypos, $bw, ($v['h'] * $bch / $arrcode['maxh']), 'F');
28662				}
28663				$xpos += $bw;
28664			}
28665		}
28666
28667		// PRINT BEARER BARS
28668		if ($btype == 'I25B' || $btype == 'I25B+') {
28669			$this->Rect($x, $y, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');
28670			$this->Rect($x, $y + $tlm + $bch, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');
28671		}
28672
28673		// Restore **************
28674		$this->DrawColor = $prevDrawColor;
28675		$this->TextColor = $prevTextColor;
28676		$this->FillColor = $prevFillColor;
28677		$this->SetLineWidth($lw);
28678		$this->SetY($y);
28679	}
28680
28681	/* -- END BARCODES -- */
28682
28683	// ====================================================
28684	// ====================================================
28685
28686	function StartTransform($returnstring = false)
28687	{
28688		if ($returnstring) {
28689			return('q');
28690		} else {
28691			$this->_out('q');
28692		}
28693	}
28694
28695	function StopTransform($returnstring = false)
28696	{
28697		if ($returnstring) {
28698			return('Q');
28699		} else {
28700			$this->_out('Q');
28701		}
28702	}
28703
28704	function transformScale($s_x, $s_y, $x = '', $y = '', $returnstring = false)
28705	{
28706		if ($x === '') {
28707			$x = $this->x;
28708		}
28709		if ($y === '') {
28710			$y = $this->y;
28711		}
28712		if (($s_x == 0) or ( $s_y == 0)) {
28713			throw new \Mpdf\MpdfException('Please do not use values equal to zero for scaling');
28714		}
28715		$y = ($this->h - $y) * Mpdf::SCALE;
28716		$x *= Mpdf::SCALE;
28717		// calculate elements of transformation matrix
28718		$s_x /= 100;
28719		$s_y /= 100;
28720		$tm = [];
28721		$tm[0] = $s_x;
28722		$tm[1] = 0;
28723		$tm[2] = 0;
28724		$tm[3] = $s_y;
28725		$tm[4] = $x * (1 - $s_x);
28726		$tm[5] = $y * (1 - $s_y);
28727		// scale the coordinate system
28728		if ($returnstring) {
28729			return($this->_transform($tm, true));
28730		} else {
28731			$this->_transform($tm);
28732		}
28733	}
28734
28735	function transformTranslate($t_x, $t_y, $returnstring = false)
28736	{
28737		// calculate elements of transformation matrix
28738		$tm = [];
28739		$tm[0] = 1;
28740		$tm[1] = 0;
28741		$tm[2] = 0;
28742		$tm[3] = 1;
28743		$tm[4] = $t_x * Mpdf::SCALE;
28744		$tm[5] = -$t_y * Mpdf::SCALE;
28745		// translate the coordinate system
28746		if ($returnstring) {
28747			return($this->_transform($tm, true));
28748		} else {
28749			$this->_transform($tm);
28750		}
28751	}
28752
28753	function transformRotate($angle, $x = '', $y = '', $returnstring = false)
28754	{
28755		if ($x === '') {
28756			$x = $this->x;
28757		}
28758		if ($y === '') {
28759			$y = $this->y;
28760		}
28761		$angle = -$angle;
28762		$y = ($this->h - $y) * Mpdf::SCALE;
28763		$x *= Mpdf::SCALE;
28764		// calculate elements of transformation matrix
28765		$tm = [];
28766		$tm[0] = cos(deg2rad($angle));
28767		$tm[1] = sin(deg2rad($angle));
28768		$tm[2] = -$tm[1];
28769		$tm[3] = $tm[0];
28770		$tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
28771		$tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
28772		// rotate the coordinate system around ($x,$y)
28773		if ($returnstring) {
28774			return($this->_transform($tm, true));
28775		} else {
28776			$this->_transform($tm);
28777		}
28778	}
28779
28780	// mPDF 5.7.3 TRANSFORMS
28781	function transformSkew($angle_x, $angle_y, $x = '', $y = '', $returnstring = false)
28782	{
28783		if ($x === '') {
28784			$x = $this->x;
28785		}
28786		if ($y === '') {
28787			$y = $this->y;
28788		}
28789		$angle_x = -$angle_x;
28790		$angle_y = -$angle_y;
28791		$x *= Mpdf::SCALE;
28792		$y = ($this->h - $y) * Mpdf::SCALE;
28793		// calculate elements of transformation matrix
28794		$tm = [];
28795		$tm[0] = 1;
28796		$tm[1] = tan(deg2rad($angle_y));
28797		$tm[2] = tan(deg2rad($angle_x));
28798		$tm[3] = 1;
28799		$tm[4] = -$tm[2] * $y;
28800		$tm[5] = -$tm[1] * $x;
28801		// skew the coordinate system
28802		if ($returnstring) {
28803			return($this->_transform($tm, true));
28804		} else {
28805			$this->_transform($tm);
28806		}
28807	}
28808
28809	function _transform($tm, $returnstring = false)
28810	{
28811		if ($returnstring) {
28812			return(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
28813		} else {
28814			$this->_out(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
28815		}
28816	}
28817
28818	// AUTOFONT =========================
28819	function markScriptToLang($html)
28820	{
28821		if ($this->onlyCoreFonts) {
28822			return $html;
28823		}
28824
28825		$n = '';
28826		$a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
28827		foreach ($a as $i => $e) {
28828			if ($i % 2 == 0) {
28829				// ignore if in Textarea
28830				if ($i > 0 && strtolower(substr($a[$i - 1], 1, 8)) == 'textarea') {
28831					$a[$i] = $e;
28832					continue;
28833				}
28834				$e = UtfString::strcode2utf($e);
28835				$e = $this->lesser_entity_decode($e);
28836
28837				$earr = $this->UTF8StringToArray($e, false);
28838
28839				$scriptblock = 0;
28840				$scriptblocks = [];
28841				$scriptblocks[0] = 0;
28842				$chardata = [];
28843				$subchunk = 0;
28844				$charctr = 0;
28845				foreach ($earr as $char) {
28846					$ucd_record = Ucdn::get_ucd_record($char);
28847					$sbl = $ucd_record[6];
28848
28849					if ($sbl && $sbl != 40 && $sbl != 102) {
28850						if ($scriptblock == 0) {
28851							$scriptblock = $sbl;
28852							$scriptblocks[$subchunk] = $scriptblock;
28853						} elseif ($scriptblock > 0 && $scriptblock != $sbl) {
28854							// NEW (non-common) Script encountered in this chunk.
28855							// Start a new subchunk
28856							$subchunk++;
28857							$scriptblock = $sbl;
28858							$charctr = 0;
28859							$scriptblocks[$subchunk] = $scriptblock;
28860						}
28861					}
28862
28863					$chardata[$subchunk][$charctr]['script'] = $sbl;
28864					$chardata[$subchunk][$charctr]['uni'] = $char;
28865					$charctr++;
28866				}
28867
28868				// If scriptblock[x] = common & non-baseScript
28869				// and scriptblock[x+1] = baseScript
28870				// Move common script from end of x to start of x+1
28871				for ($sch = 0; $sch < $subchunk; $sch++) {
28872					if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $scriptblocks[$sch + 1] == $this->baseScript) {
28873						$end = count($chardata[$sch]) - 1;
28874						while ($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script
28875							$tmp = array_pop($chardata[$sch]);
28876							array_unshift($chardata[$sch + 1], $tmp);
28877							$end--;
28878						}
28879					}
28880				}
28881
28882				$o = '';
28883				for ($sch = 0; $sch <= $subchunk; $sch++) {
28884					if (isset($chardata[$sch])) {
28885						$s = '';
28886						for ($j = 0; $j < count($chardata[$sch]); $j++) {
28887							$s .= UtfString::code2utf($chardata[$sch][$j]['uni']);
28888						}
28889						// ZZZ99 Undo lesser_entity_decode as above - but only for <>&
28890						$s = str_replace("&", "&amp;", $s);
28891						$s = str_replace("<", "&lt;", $s);
28892						$s = str_replace(">", "&gt;", $s);
28893
28894						// Check Vietnamese if Latin script - even if Basescript
28895						if ($scriptblocks[$sch] == Ucdn::SCRIPT_LATIN && $this->autoVietnamese && preg_match("/([" . $this->scriptToLanguage->getLanguageDelimiters('viet') . "])/u", $s)) {
28896							$o .= '<span lang="vi" class="lang_vi">' . $s . '</span>';
28897						} // Check Arabic for different languages if Arabic script - even if Basescript
28898						elseif ($scriptblocks[$sch] == Ucdn::SCRIPT_ARABIC && $this->autoArabic) {
28899							if (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('sindhi') . "]/u", $s)) {
28900								$o .= '<span lang="sd" class="lang_sd">' . $s . '</span>';
28901							} elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('urdu') . "]/u", $s)) {
28902								$o .= '<span lang="ur" class="lang_ur">' . $s . '</span>';
28903							} elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('pashto') . "]/u", $s)) {
28904								$o .= '<span lang="ps" class="lang_ps">' . $s . '</span>';
28905							} elseif (preg_match("/[" . $this->scriptToLanguage->getLanguageDelimiters('persian') . "]/u", $s)) {
28906								$o .= '<span lang="fa" class="lang_fa">' . $s . '</span>';
28907							} elseif ($this->baseScript != Ucdn::SCRIPT_ARABIC && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) {
28908								$o .= '<span lang="' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '" class="lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '">' . $s . '</span>';
28909							} else {
28910								// Just output chars
28911								$o .= $s;
28912							}
28913						} // Identify Script block if not Basescript, and mark up as language
28914						elseif ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) {
28915							// Encase in <span>
28916							$o .= '<span lang="' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '" class="lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '">';
28917							$o .= $s;
28918							$o .= '</span>';
28919						} else {
28920							// Just output chars
28921							$o .= $s;
28922						}
28923					}
28924				}
28925
28926				$a[$i] = $o;
28927			} else {
28928				$a[$i] = '<' . $e . '>';
28929			}
28930		}
28931		$n = implode('', $a);
28932
28933		return $n;
28934	}
28935
28936	// ===========================
28937	// Functions
28938	// Call-back function Used for usort in fn _tableWrite
28939
28940	function _cmpdom($a, $b)
28941	{
28942		return ($a["dom"] < $b["dom"]) ? -1 : 1;
28943	}
28944
28945	function mb_strrev($str, $enc = 'utf-8')
28946	{
28947		$ch = [];
28948		$ch = preg_split('//u', $str);
28949		$revch = array_reverse($ch);
28950		return implode('', $revch);
28951	}
28952
28953	/* -- COLUMNS -- */
28954
28955	// Callback function from function printcolumnbuffer in mpdf
28956	function columnAdjustAdd($type, $k, $xadj, $yadj, $a, $b, $c = 0, $d = 0, $e = 0, $f = 0)
28957	{
28958		if ($type == 'Td') {  // xpos,ypos
28959			$a += ($xadj * $k);
28960			$b -= ($yadj * $k);
28961			return 'BT ' . sprintf('%.3F %.3F', $a, $b) . ' Td';
28962		} elseif ($type == 're') {  // xpos,ypos,width,height
28963			$a += ($xadj * $k);
28964			$b -= ($yadj * $k);
28965			return sprintf('%.3F %.3F %.3F %.3F', $a, $b, $c, $d) . ' re';
28966		} elseif ($type == 'l') {  // xpos,ypos,x2pos,y2pos
28967			$a += ($xadj * $k);
28968			$b -= ($yadj * $k);
28969			return sprintf('%.3F %.3F l', $a, $b);
28970		} elseif ($type == 'img') {  // width,height,xpos,ypos
28971			$c += ($xadj * $k);
28972			$d -= ($yadj * $k);
28973			return sprintf('q %.3F 0 0 %.3F %.3F %.3F', $a, $b, $c, $d) . ' cm /' . $e;
28974		} elseif ($type == 'draw') {  // xpos,ypos
28975			$a += ($xadj * $k);
28976			$b -= ($yadj * $k);
28977			return sprintf('%.3F %.3F m', $a, $b);
28978		} elseif ($type == 'bezier') {  // xpos,ypos,x2pos,y2pos,x3pos,y3pos
28979			$a += ($xadj * $k);
28980			$b -= ($yadj * $k);
28981			$c += ($xadj * $k);
28982			$d -= ($yadj * $k);
28983			$e += ($xadj * $k);
28984			$f -= ($yadj * $k);
28985			return sprintf('%.3F %.3F %.3F %.3F %.3F %.3F', $a, $b, $c, $d, $e, $f) . ' c';
28986		}
28987	}
28988
28989	/* -- END COLUMNS -- */
28990
28991	// mPDF 5.7.3 TRANSFORMS
28992	function ConvertAngle($s, $makepositive = true)
28993	{
28994		if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i', $s, $m)) {
28995			$angle = $m[1] + 0;
28996			if (strtolower($m[2]) == 'deg') {
28997				$angle = $angle;
28998			} elseif (strtolower($m[2]) == 'grad') {
28999				$angle *= (360 / 400);
29000			} elseif (strtolower($m[2]) == 'rad') {
29001				$angle = rad2deg($angle);
29002			}
29003			while ($angle >= 360) {
29004				$angle -= 360;
29005			}
29006			while ($angle <= -360) {
29007				$angle += 360;
29008			}
29009			if ($makepositive) { // always returns an angle between 0 and 360deg
29010				if ($angle < 0) {
29011					$angle += 360;
29012				}
29013			}
29014		} else {
29015			$angle = $s + 0;
29016		}
29017		return $angle;
29018	}
29019
29020	function lesser_entity_decode($html)
29021	{
29022		// supports the most used entity codes (only does ascii safe characters)
29023		$html = str_replace("&lt;", "<", $html);
29024		$html = str_replace("&gt;", ">", $html);
29025
29026		$html = str_replace("&apos;", "'", $html);
29027		$html = str_replace("&quot;", '"', $html);
29028		$html = str_replace("&amp;", "&", $html);
29029		return $html;
29030	}
29031
29032	function AdjustHTML($html, $tabSpaces = 8)
29033	{
29034		$limit = ini_get('pcre.backtrack_limit');
29035		if (strlen($html) > $limit) {
29036			throw new \Mpdf\MpdfException(sprintf(
29037				'The HTML code size is larger than pcre.backtrack_limit %d. You should use WriteHTML() with smaller string lengths.',
29038				$limit
29039			));
29040		}
29041
29042		preg_match_all("/(<annotation.*?>)/si", $html, $m);
29043		if (count($m[1])) {
29044			for ($i = 0; $i < count($m[1]); $i++) {
29045				$sub = preg_replace("/\n/si", "\xbb\xa4\xac", $m[1][$i]);
29046				$html = preg_replace('/' . preg_quote($m[1][$i], '/') . '/si', $sub, $html);
29047			}
29048		}
29049
29050		preg_match_all("/(<svg.*?<\/svg>)/si", $html, $svgi);
29051		if (count($svgi[0])) {
29052			for ($i = 0; $i < count($svgi[0]); $i++) {
29053				$file = $this->cache->write('/_tempSVG' . uniqid(random_int(1, 100000), true) . '_' . $i . '.svg', $svgi[0][$i]);
29054				$html = str_replace($svgi[0][$i], '<img src="' . $file . '" />', $html);
29055			}
29056		}
29057
29058		// Remove javascript code from HTML (should not appear in the PDF file)
29059		$html = preg_replace('/<script.*?<\/script>/is', '', $html);
29060
29061		// Remove special comments
29062		$html = preg_replace('/<!--mpdf/i', '', $html);
29063		$html = preg_replace('/mpdf-->/i', '', $html);
29064
29065		// Remove comments from HTML (should not appear in the PDF file)
29066		$html = preg_replace('/<!--.*?-->/s', '', $html);
29067
29068		$html = preg_replace('/\f/', '', $html); // replace formfeed by nothing
29069		$html = preg_replace('/\r/', '', $html); // replace carriage return by nothing
29070		// Well formed XHTML end tags
29071		$html = preg_replace('/<(br|hr)>/i', "<\\1 />", $html); // mPDF 6
29072		$html = preg_replace('/<(br|hr)\/>/i', "<\\1 />", $html);
29073		// Get rid of empty <thead></thead> etc
29074		$html = preg_replace('/<tr>\s*<\/tr>/i', '', $html);
29075		$html = preg_replace('/<thead>\s*<\/thead>/i', '', $html);
29076		$html = preg_replace('/<tfoot>\s*<\/tfoot>/i', '', $html);
29077		$html = preg_replace('/<table[^>]*>\s*<\/table>/i', '', $html);
29078
29079		// Remove spaces at end of table cells
29080		$html = preg_replace("/[ \n\r]+<\/t(d|h)/", '</t\\1', $html);
29081
29082		$html = preg_replace("/[ ]*<dottab\s*[\/]*>[ ]*/", '<dottab />', $html);
29083
29084		// Concatenates any Substitute characters from symbols/dingbats
29085		$html = str_replace('</tts><tts>', '|', $html);
29086		$html = str_replace('</ttz><ttz>', '|', $html);
29087		$html = str_replace('</tta><tta>', '|', $html);
29088
29089		$html = preg_replace('/<br \/>\s*/is', "<br />", $html);
29090
29091		$html = preg_replace('/<wbr[ \/]*>\s*/is', "&#173;", $html);
29092
29093		// Preserve '\n's in content between the tags <pre> and </pre>
29094		if (preg_match('/<pre/', $html)) {
29095			$html_a = preg_split('/(\<\/?pre[^\>]*\>)/', $html, -1, 2);
29096			$h = [];
29097			$c = 0;
29098			foreach ($html_a as $s) {
29099				if ($c > 1 && preg_match('/^<\/pre/i', $s)) {
29100					$c--;
29101					$s = preg_replace('/<\/pre/i', '</innerpre', $s);
29102				} elseif ($c > 0 && preg_match('/^<pre/i', $s)) {
29103					$c++;
29104					$s = preg_replace('/<pre/i', '<innerpre', $s);
29105				} elseif (preg_match('/^<pre/i', $s)) {
29106					$c++;
29107				} elseif (preg_match('/^<\/pre/i', $s)) {
29108					$c--;
29109				}
29110				array_push($h, $s);
29111			}
29112			$html = implode("", $h);
29113		}
29114		$thereispre = preg_match_all('#<pre(.*?)>(.*?)</pre>#si', $html, $temp);
29115		// Preserve '\n's in content between the tags <textarea> and </textarea>
29116		$thereistextarea = preg_match_all('#<textarea(.*?)>(.*?)</textarea>#si', $html, $temp2);
29117		$html = preg_replace('/[\n]/', ' ', $html); // replace linefeed by spaces
29118		$html = preg_replace('/[\t]/', ' ', $html); // replace tabs by spaces
29119		// Converts < to &lt; when not a tag
29120		$html = preg_replace('/<([^!\/a-zA-Z_:])/i', '&lt;\\1', $html); // mPDF 5.7.3
29121		$html = preg_replace("/[ ]+/", ' ', $html);
29122
29123		$html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html);
29124		$html = preg_replace('/\/(u|o)l>\s+<\/li/i', '/\\1l></li', $html);
29125		$html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html);
29126		$html = preg_replace('/\/li>\s+<li/i', '/li><li', $html);
29127		$html = preg_replace('/<(u|o)l([^>]*)>[ ]+/i', '<\\1l\\2>', $html);
29128		$html = preg_replace('/[ ]+<(u|o)l/i', '<\\1l', $html);
29129
29130		// Make self closing tabs valid XHTML
29131		// Tags which are self-closing: 1) Replaceable and 2) Non-replaced items
29132		$selftabs = 'input|hr|img|br|barcode|dottab';
29133		$selftabs2 = 'indexentry|indexinsert|bookmark|watermarktext|watermarkimage|column_break|columnbreak|newcolumn|newpage|page_break|pagebreak|formfeed|columns|toc|tocpagebreak|setpageheader|setpagefooter|sethtmlpageheader|sethtmlpagefooter|annotation';
29134
29135		// Fix self-closing tags which don't close themselves
29136		$html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . ')[^>\/]*)>/i', '\\1 />', $html);
29137
29138		// Fix self-closing tags that don't include a space between the tag name and the closing slash
29139		$html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . '))\/>/i', '\\1 />', $html);
29140
29141		$iterator = 0;
29142		while ($thereispre) { // Recover <pre attributes>content</pre>
29143			$temp[2][$iterator] = preg_replace('/<([^!\/a-zA-Z_:])/', '&lt;\\1', $temp[2][$iterator]); // mPDF 5.7.2	// mPDF 5.7.3
29144
29145			$temp[2][$iterator] = preg_replace_callback("/^([^\n\t]*?)\t/m", [$this, 'tabs2spaces_callback'], $temp[2][$iterator]); // mPDF 5.7+
29146			$temp[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp[2][$iterator]);
29147
29148			$temp[2][$iterator] = preg_replace('/\n/', "<br />", $temp[2][$iterator]);
29149			$temp[2][$iterator] = str_replace('\\', "\\\\", $temp[2][$iterator]);
29150			// $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.$temp[2][$iterator].'</erp>',$html,1);
29151			$html = preg_replace('#<pre(.*?)>(.*?)</pre>#si', '<erp' . $temp[1][$iterator] . '>' . str_replace('$', '\$', $temp[2][$iterator]) . '</erp>', $html, 1); // mPDF 5.7+
29152			$thereispre--;
29153			$iterator++;
29154		}
29155		$iterator = 0;
29156		while ($thereistextarea) { // Recover <textarea attributes>content</textarea>
29157			$temp2[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp2[2][$iterator]);
29158			$temp2[2][$iterator] = str_replace('\\', "\\\\", $temp2[2][$iterator]);
29159			$html = preg_replace('#<textarea(.*?)>(.*?)</textarea>#si', '<aeratxet' . $temp2[1][$iterator] . '>' . trim($temp2[2][$iterator]) . '</aeratxet>', $html, 1);
29160			$thereistextarea--;
29161			$iterator++;
29162		}
29163		// Restore original tag names
29164		$html = str_replace("<erp", "<pre", $html);
29165		$html = str_replace("</erp>", "</pre>", $html);
29166		$html = str_replace("<aeratxet", "<textarea", $html);
29167		$html = str_replace("</aeratxet>", "</textarea>", $html);
29168		$html = str_replace("</innerpre", "</pre", $html);
29169		$html = str_replace("<innerpre", "<pre", $html);
29170
29171		$html = preg_replace('/<textarea([^>]*)><\/textarea>/si', '<textarea\\1> </textarea>', $html);
29172		$html = preg_replace('/(<table[^>]*>)\s*(<caption)(.*?<\/caption>)(.*?<\/table>)/si', '\\2 position="top"\\3\\1\\4\\2 position="bottom"\\3', $html); // *TABLES*
29173		$html = preg_replace('/<(h[1-6])([^>]*)(>(?:(?!h[1-6]).)*?<\/\\1>\s*<table)/si', '<\\1\\2 keep-with-table="1"\\3', $html); // *TABLES*
29174		$html = preg_replace("/\xbb\xa4\xac/", "\n", $html);
29175
29176		// Fixes <p>&#8377</p> which browser copes with even though it is wrong!
29177		$html = preg_replace("/(&#[x]{0,1}[0-9a-f]{1,5})</i", "\\1;<", $html);
29178		return $html;
29179	}
29180
29181	// mPDF 5.7+
29182	function tabs2spaces_callback($matches)
29183	{
29184		return (stripslashes($matches[1]) . str_repeat(' ', $this->tabSpaces - (mb_strlen(stripslashes($matches[1])) % $this->tabSpaces)));
29185	}
29186
29187	// mPDF 5.7+
29188	function date_callback($matches)
29189	{
29190		return date($matches[1]);
29191	}
29192
29193	// ===========================
29194	/* -- IMPORTS -- */
29195	function SetImportUse()
29196	{
29197		if (!class_exists('fpdi_pdf_parser')) {
29198			throw new \Mpdf\MpdfException('Class fpdi_pdf_parser not found. Please run composer update or require setasign/fpdi 1.6.* manually');
29199		}
29200
29201		$this->enableImports = true;
29202	}
29203
29204	// from mPDFI
29205	function hex2str($hex)
29206	{
29207		return pack("H*", str_replace(["\r", "\n", " "], "", $hex));
29208	}
29209
29210	function str2hex($str)
29211	{
29212		return current(unpack("H*", $str));
29213	}
29214
29215	/**
29216	 * Un-escapes a PDF string
29217	 *
29218	 * @param string $s
29219	 * @return string
29220	 */
29221	function _unescape($s)
29222	{
29223		$out = '';
29224		for ($count = 0, $n = strlen($s); $count < $n; $count++) {
29225			if ($s[$count] != '\\' || $count == $n-1) {
29226				$out .= $s[$count];
29227			} else {
29228				switch ($s[++$count]) {
29229					case ')':
29230					case '(':
29231					case '\\':
29232						$out .= $s[$count];
29233						break;
29234					case 'f':
29235						$out .= chr(0x0C);
29236						break;
29237					case 'b':
29238						$out .= chr(0x08);
29239						break;
29240					case 't':
29241						$out .= chr(0x09);
29242						break;
29243					case 'r':
29244						$out .= chr(0x0D);
29245						break;
29246					case 'n':
29247						$out .= chr(0x0A);
29248						break;
29249					case "\r":
29250						if ($count != $n-1 && $s[$count+1] == "\n") {
29251							$count++;
29252						}
29253						break;
29254					case "\n":
29255						break;
29256					default:
29257						// Octal-Values
29258						if (ord($s[$count]) >= ord('0') &&
29259							ord($s[$count]) <= ord('9')) {
29260							$oct = ''. $s[$count];
29261							if (ord($s[$count+1]) >= ord('0') &&
29262								ord($s[$count+1]) <= ord('9')) {
29263								$oct .= $s[++$count];
29264								if (ord($s[$count+1]) >= ord('0') &&
29265									ord($s[$count+1]) <= ord('9')) {
29266									$oct .= $s[++$count];
29267								}
29268							}
29269							$out .= chr(octdec($oct));
29270						} else {
29271							$out .= $s[$count];
29272						}
29273				}
29274			}
29275		}
29276		return $out;
29277	}
29278
29279	function pdf_write_value(&$value)
29280	{
29281		switch ($value[0]) {
29282			case pdf_parser::TYPE_TOKEN:
29283				$this->_out($value[1] . ' ', false);
29284				break;
29285
29286			case pdf_parser::TYPE_NUMERIC:
29287			case pdf_parser::TYPE_REAL:
29288				if (is_float($value[1]) && $value[1] != 0) {
29289					$this->_out(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ', false);
29290				} else {
29291					$this->_out($value[1] . ' ', false);
29292				}
29293				break;
29294
29295			case pdf_parser::TYPE_ARRAY:
29296				// An array. Output the proper
29297				// structure and move on.
29298				$this->_out("[", false);
29299				for ($i = 0; $i < count($value[1]); $i++) {
29300					$this->pdf_write_value($value[1][$i]);
29301				}
29302				$this->_out("]");
29303				break;
29304
29305			case pdf_parser::TYPE_DICTIONARY:
29306				// A dictionary.
29307				$this->_out("<<", false);
29308
29309				foreach ($value[1] as $k => $v) {
29310					$this->_out($k . ' ', false);
29311					$this->pdf_write_value($v);
29312				}
29313
29314				$this->_out(">>");
29315				break;
29316
29317			case pdf_parser::TYPE_OBJREF:
29318				// An indirect object reference
29319				// Fill the object stack if needed
29320				$cpfn = $this->current_parser->filename;
29321				if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
29322					$this->_newobj(false, true);
29323					$this->_obj_stack[$cpfn][$value[1]] = [$this->n, $value];
29324					$this->_don_obj_stack[$cpfn][$value[1]] = [$this->n, $value];
29325				}
29326				$objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
29327				$this->_out("{$objid} 0 R"); // {$value[2]}
29328				break;
29329
29330			case pdf_parser::TYPE_STRING:
29331				if ($this->encrypted) {
29332					$value[1] = $this->_unescape($value[1]);
29333					$value[1] = $this->protection->rc4($this->protection->objectKey($this->_current_obj_id), $value[1]);
29334					$value[1] = $this->_escape($value[1]);
29335				}
29336				// A string.
29337				$this->_out('(' . $value[1] . ')');
29338				break;
29339
29340			case pdf_parser::TYPE_STREAM:
29341				// A stream. First, output the
29342				// stream dictionary, then the
29343				// stream data itself.
29344				$this->pdf_write_value($value[1]);
29345				if ($this->encrypted) {
29346					$value[2][1] = $this->protection->rc4($this->protection->objectKey($this->_current_obj_id), $value[2][1]);
29347				}
29348				$this->_out("stream");
29349				$this->_out($value[2][1]);
29350				$this->_out("endstream");
29351				break;
29352
29353			case pdf_parser::TYPE_HEX:
29354				if ($this->encrypted) {
29355					$value[1] = $this->hex2str($value[1]);
29356					$value[1] = $this->protection->rc4($this->protection->objectKey($this->_current_obj_id), $value[1]);
29357					// remake hexstring of encrypted string
29358					$value[1] = $this->str2hex($value[1]);
29359				}
29360				$this->_out("<" . $value[1] . ">");
29361				break;
29362
29363			case pdf_parser::TYPE_BOOLEAN:
29364				$this->_out($value[1] ? 'true' : 'false');
29365				break;
29366
29367			case pdf_parser::TYPE_NULL:
29368				// The null object.
29369				$this->_out("null");
29370				break;
29371		}
29372	}
29373
29374	// ========== OVERWRITE SEARCH STRING IN A PDF FILE ================
29375	function OverWrite($file_in, $search, $replacement, $dest = Destination::DOWNLOAD, $file_out = "mpdf")
29376	{
29377		$pdf = file_get_contents($file_in);
29378
29379		if (!is_array($search)) {
29380			$x = $search;
29381			$search = [$x];
29382		}
29383		if (!is_array($replacement)) {
29384			$x = $replacement;
29385			$replacement = [$x]; // mPDF 5.7.4
29386		}
29387
29388		if (!$this->onlyCoreFonts && !$this->usingCoreFont) {
29389			foreach ($search as $k => $val) {
29390				$search[$k] = $this->UTF8ToUTF16BE($search[$k], false);
29391				$search[$k] = $this->_escape($search[$k]);
29392				$replacement[$k] = $this->UTF8ToUTF16BE($replacement[$k], false);
29393				$replacement[$k] = $this->_escape($replacement[$k]);
29394			}
29395		} else {
29396			foreach ($replacement as $k => $val) {
29397				$replacement[$k] = mb_convert_encoding($replacement[$k], $this->mb_enc, 'utf-8');
29398				$replacement[$k] = $this->_escape($replacement[$k]);
29399			}
29400		}
29401
29402		// Get xref into array
29403		$xref = [];
29404		preg_match("/xref\n0 (\d+)\n(.*?)\ntrailer/s", $pdf, $m);
29405		$xref_objid = $m[1];
29406		preg_match_all('/(\d{10}) (\d{5}) (f|n)/', $m[2], $x);
29407		for ($i = 0; $i < count($x[0]); $i++) {
29408			$xref[] = [intval($x[1][$i]), $x[2][$i], $x[3][$i]];
29409		}
29410
29411		$changes = [];
29412		preg_match("/<<\s*\/Type\s*\/Pages\s*\/Kids\s*\[(.*?)\]\s*\/Count/s", $pdf, $m);
29413		preg_match_all("/(\d+) 0 R /s", $m[1], $o);
29414		$objlist = $o[1];
29415
29416		foreach ($objlist as $obj) {
29417			if ($this->compress) {
29418				preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Filter\s*\/FlateDecode\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m);
29419			} else {
29420				preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m);
29421			}
29422
29423			$s = $m[2];
29424			if (!$s) {
29425				continue;
29426			}
29427
29428			$oldlen = $m[1];
29429
29430			if ($this->encrypted) {
29431				$s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s);
29432			}
29433
29434			if ($this->compress) {
29435				$s = gzuncompress($s);
29436			}
29437
29438			foreach ($search as $k => $val) {
29439				$s = str_replace($search[$k], $replacement[$k], $s);
29440			}
29441
29442			if ($this->compress) {
29443				$s = gzcompress($s);
29444			}
29445
29446			if ($this->encrypted) {
29447				$s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s);
29448			}
29449
29450			$newlen = strlen($s);
29451
29452			$changes[($xref[$obj + 1][0])] = ($newlen - $oldlen) + (strlen($newlen) - strlen($oldlen));
29453
29454			if ($this->compress) {
29455				$newstr = ($obj + 1) . " 0 obj\n<</Filter /FlateDecode /Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n";
29456			} else {
29457				$newstr = ($obj + 1) . " 0 obj\n<</Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n";
29458			}
29459
29460			$pdf = str_replace($m[0], $newstr, $pdf);
29461		}
29462
29463		// Update xref in PDF
29464		krsort($changes);
29465		$newxref = "xref\n0 " . $xref_objid . "\n";
29466		foreach ($xref as $v) {
29467			foreach ($changes as $ck => $cv) {
29468				if ($v[0] > $ck) {
29469					$v[0] += $cv;
29470				}
29471			}
29472			$newxref .= sprintf('%010d', $v[0]) . ' ' . $v[1] . ' ' . $v[2] . " \n";
29473		}
29474		$newxref .= "trailer";
29475		$pdf = preg_replace("/xref\n0 \d+\n.*?\ntrailer/s", $newxref, $pdf);
29476
29477		// Update startxref in PDF
29478		preg_match("/startxref\n(\d+)\n%%EOF/s", $pdf, $m);
29479		$startxref = $m[1];
29480		$startxref += array_sum($changes);
29481		$pdf = preg_replace("/startxref\n(\d+)\n%%EOF/s", "startxref\n" . $startxref . "\n%%EOF", $pdf);
29482
29483		// OUTPUT
29484		switch ($dest) {
29485			case Destination::INLINE:
29486				if (isset($_SERVER['SERVER_NAME'])) {
29487					// We send to a browser
29488					header('Content-Type: application/pdf');
29489					header('Content-Length: ' . strlen($pdf));
29490					header('Content-disposition: inline; filename=' . $file_out);
29491				}
29492
29493				echo $pdf;
29494
29495				break;
29496
29497			case Destination::FILE:
29498				if (!$file_out) {
29499					$file_out = 'mpdf.pdf';
29500				}
29501
29502				$f = fopen($file_out, 'wb');
29503
29504				if (!$f) {
29505					throw new \Mpdf\MpdfException('Unable to create output file: ' . $file_out);
29506				}
29507
29508				fwrite($f, $pdf, strlen($pdf));
29509
29510				fclose($f);
29511
29512				break;
29513
29514			case Destination::STRING_RETURN:
29515				return $pdf;
29516
29517			case Destination::DOWNLOAD: // Download file
29518			default:
29519				if (isset($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
29520					header('Content-Type: application/force-download');
29521				} else {
29522					header('Content-Type: application/octet-stream');
29523				}
29524
29525				header('Content-Length: ' . strlen($pdf));
29526				header('Content-disposition: attachment; filename=' . $file_out);
29527
29528				echo $pdf;
29529
29530				break;
29531		}
29532	}
29533
29534	function GetTemplateSize($tplidx, $_w = 0, $_h = 0)
29535	{
29536		if (!$this->tpls[$tplidx]) {
29537			return false;
29538		}
29539		$w = $this->tpls[$tplidx]['box']['w'];
29540		$h = $this->tpls[$tplidx]['box']['h'];
29541		if ($_w == 0 and $_h == 0) {
29542			$_w = $w;
29543			$_h = $h;
29544		}
29545		if ($_w == 0) {
29546			$_w = $_h * $w / $h;
29547		}
29548		if ($_h == 0) {
29549			$_h = $_w * $h / $w;
29550		}
29551		return ["w" => $_w, "h" => $_h];
29552	}
29553
29554
29555	function Thumbnail($file, $npr = 3, $spacing = 10)
29556	{
29557		// $npr = number per row
29558		$w = (($this->pgwidth + $spacing) / $npr) - $spacing;
29559		$oldlinewidth = $this->LineWidth;
29560		$this->SetLineWidth(0.02);
29561		$this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));
29562		$h = 0;
29563		$maxh = 0;
29564		$x = $_x = $this->lMargin;
29565		$_y = $this->tMargin;
29566
29567		if ($this->y == 0) {
29568			$y = $_y;
29569		} else {
29570			$y = $this->y;
29571		}
29572
29573		$pagecount = $this->SetSourceFile($file);
29574
29575		for ($n = 1; $n <= $pagecount; $n++) {
29576			$tplidx = $this->ImportPage($n);
29577			$size = $this->UseTemplate($tplidx, $x, $y, $w);
29578			$this->Rect($x, $y, $size['w'], $size['h']);
29579			$h = max($h, $size['h']);
29580			$maxh = max($h, $maxh);
29581
29582			if ($n % $npr == 0) {
29583				if (($y + $h + $spacing + $maxh) > $this->PageBreakTrigger && $n != $pagecount) {
29584					$this->AddPage();
29585					$x = $_x;
29586					$y = $_y;
29587				} else {
29588					$y += $h + $spacing;
29589					$x = $_x;
29590					$h = 0;
29591				}
29592			} else {
29593				$x += $w + $spacing;
29594			}
29595		}
29596		$this->SetLineWidth($oldlinewidth);
29597	}
29598
29599	function SetSourceFile($filename)
29600	{
29601		$this->current_filename = $filename;
29602		$fn = $this->current_filename;
29603		if (!isset($this->parsers[$fn])) {
29604			try {
29605				$this->parsers[$fn] = new fpdi_pdf_parser($fn);
29606			} catch (\Exception $e) {
29607				throw new \Mpdf\MpdfException($e->getMessage());
29608			}
29609		}
29610
29611		$this->current_parser = $this->parsers[$fn];
29612		return $this->parsers[$fn]->getPageCount();
29613	}
29614
29615	function ImportPage($pageno = 1, $crop_x = null, $crop_y = null, $crop_w = 0, $crop_h = 0, $boxName = '/CropBox')
29616	{
29617		$fn     = $this->current_filename;
29618		$parser = $this->parsers[$fn];
29619		$parser->setPageno($pageno);
29620
29621		$this->tpl++;
29622		$this->tpls[$this->tpl] = [];
29623		$tpl = & $this->tpls[$this->tpl];
29624		$tpl['parser'] = $parser;
29625		$tpl['resources'] = $parser->getPageResources();
29626		$tpl['buffer'] = $parser->getContent();
29627
29628		if (!in_array($boxName, $parser->availableBoxes)) {
29629			throw new \Mpdf\MpdfException(sprintf("Unknown box: %s", $boxName));
29630		}
29631
29632		$pageboxes = $parser->getPageBoxes($pageno, Mpdf::SCALE);
29633
29634		/**
29635		 * MediaBox
29636		 * CropBox: Default -> MediaBox
29637		 * BleedBox: Default -> CropBox
29638		 * TrimBox: Default -> CropBox
29639		 * ArtBox: Default -> CropBox
29640		 */
29641		if (!isset($pageboxes[$boxName]) && ($boxName == "/BleedBox" || $boxName == "/TrimBox" || $boxName == "/ArtBox")) {
29642			$boxName = "/CropBox";
29643		}
29644
29645		if (!isset($pageboxes[$boxName]) && $boxName == "/CropBox") {
29646			$boxName = "/MediaBox";
29647		}
29648
29649		if (!isset($pageboxes[$boxName])) {
29650			return false;
29651		}
29652
29653		$box = $pageboxes[$boxName];
29654
29655		$tpl['box'] = $box;
29656		// To build an array that can be used by useTemplate()
29657		$this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
29658		// An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
29659		$tpl['x'] = 0;
29660		$tpl['y'] = 0;
29661		$tpl['w'] = $tpl['box']['w'];
29662		$tpl['h'] = $tpl['box']['h'];
29663
29664		if ($crop_w) {
29665			$tpl['box']['w'] = $crop_w;
29666		}
29667		if ($crop_h) {
29668			$tpl['box']['h'] = $crop_h;
29669		}
29670		if (isset($crop_x)) {
29671			$tpl['box']['x'] = $crop_x;
29672		}
29673		if (isset($crop_y)) {
29674			$tpl['box']['y'] = $tpl['h'] - $crop_y - $crop_h;
29675		}
29676
29677		// fix for rotated pages
29678		$rotation = $parser->getPageRotation($pageno);
29679
29680		if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0 && $tpl['box']['w'] == $tpl['w']) {
29681			$steps = $angle / 90;
29682
29683			$_w = $tpl['w'];
29684			$_h = $tpl['h'];
29685			$tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
29686			$tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
29687			if ($steps % 2 != 0) {
29688				$x = $y = ($steps == 1 || $steps == -3) ? $tpl['h'] : $tpl['w'];
29689			} else {
29690				$x = $tpl['w'];
29691				$y = $tpl['h'];
29692			}
29693			$cx = ($x / 2 + $tpl['box']['x']) * Mpdf::SCALE;
29694			$cy = ($y / 2 + $tpl['box']['y']) * Mpdf::SCALE;
29695			$angle*=-1;
29696			$angle*=M_PI / 180;
29697			$c = cos($angle);
29698			$s = sin($angle);
29699			$tpl['box']['w'] = $tpl['w'];
29700			$tpl['box']['h'] = $tpl['h'];
29701			$tpl['buffer'] = sprintf('q %.5F %.5F %.5F %.5F %.2F %.2F cm 1 0 0 1 %.2F %.2F cm %s Q', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy, $tpl['buffer']);
29702		}
29703
29704		return $this->tpl;
29705	}
29706
29707	function UseTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0)
29708	{
29709		if (!isset($this->tpls[$tplidx])) {
29710			throw new \Mpdf\MpdfException("Template does not exist!");
29711		}
29712
29713		if ($this->state == 0) {
29714			$this->AddPage();
29715		}
29716
29717		$out = 'q 0 J 1 w 0 j 0 G' . "\n"; // reset standard values
29718		$x = $this->tpls[$tplidx]['x'];
29719		$y = $this->tpls[$tplidx]['y'];
29720		$w = $this->tpls[$tplidx]['w'];
29721		$h = $this->tpls[$tplidx]['h'];
29722
29723		if ($_x == null) {
29724			$_x = $x;
29725		}
29726
29727		if ($_y == null) {
29728			$_y = $y;
29729		}
29730
29731		if ($_x === -1) {
29732			$_x = $this->x;
29733		}
29734
29735		if ($_y === -1) {
29736			$_y = $this->y;
29737		}
29738
29739		$wh = $this->GetTemplateSize($tplidx, $_w, $_h);
29740		$_w = $wh['w'];
29741		$_h = $wh['h'];
29742		$out .= sprintf("q %.4F 0 0 %.4F %.2F %.2F cm", ($_w / $this->tpls[$tplidx]['box']['w']), ($_h / $this->tpls[$tplidx]['box']['h']), $_x * Mpdf::SCALE, ($this->h - ($_y + $_h)) * Mpdf::SCALE) . "\n";
29743		$out .= $this->tplprefix . $tplidx . " Do Q\n";
29744
29745		$s = ["w" => $_w, "h" => $_h];
29746		$out .= "Q\n";
29747		$this->pages[$this->page] = $out . $this->pages[$this->page];
29748		return $s;
29749	}
29750
29751	function SetPageTemplate($tplidx = '')
29752	{
29753		if (!isset($this->tpls[$tplidx])) {
29754			$this->pageTemplate = '';
29755			return false;
29756		}
29757		$this->pageTemplate = $tplidx;
29758	}
29759
29760	function SetDocTemplate($file = '', $continue = 0)
29761	{
29762		$this->docTemplate = $file;
29763		$this->docTemplateContinue = $continue;
29764	}
29765
29766	/* -- END IMPORTS -- */
29767
29768	// JAVASCRIPT
29769	function _set_object_javascript($string)
29770	{
29771		$this->_newobj();
29772		$this->_out('<<');
29773		$this->_out('/S /JavaScript ');
29774		$this->_out('/JS ' . $this->_textstring($string));
29775		$this->_out('>>');
29776		$this->_out('endobj');
29777	}
29778
29779	function SetJS($script)
29780	{
29781		$this->js = $script;
29782	}
29783
29784	/**
29785	 * This function takes the last comma or dot (if any) to make a clean float, ignoring thousand separator, currency or any other letter
29786	 *
29787	 * @param string $num
29788	 * @see http://php.net/manual/de/function.floatval.php#114486
29789	 * @return float
29790	 */
29791	public function toFloat($num)
29792	{
29793		$dotPos = strrpos($num, '.');
29794		$commaPos = strrpos($num, ',');
29795		$sep = (($dotPos > $commaPos) && $dotPos) ? $dotPos : ((($commaPos > $dotPos) && $commaPos) ? $commaPos : false);
29796
29797		if (!$sep) {
29798			return floatval(preg_replace('/[^0-9]/', '', $num));
29799		}
29800
29801		return floatval(
29802			preg_replace('/[^0-9]/', '', substr($num, 0, $sep)) . '.' .
29803			preg_replace('/[^0-9]/', '', substr($num, $sep+1, strlen($num)))
29804		);
29805	}
29806
29807	public function getFontDescriptor()
29808	{
29809		return $this->fontDescriptor;
29810	}
29811
29812}
29813