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