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