1<?php
2
3namespace Mpdf\Image;
4
5use Mpdf\Color\ColorConverter;
6use Mpdf\Css\TextVars;
7use Mpdf\CssManager;
8use Mpdf\Language\LanguageToFontInterface;
9use Mpdf\Language\ScriptToLanguageInterface;
10use Mpdf\Mpdf;
11use Mpdf\Otl;
12use Mpdf\SizeConverter;
13use Mpdf\Ucdn;
14use Mpdf\Utils\Arrays;
15use Mpdf\Utils\UtfString;
16
17/**
18 * SVG class modified for mPDF version >= 6.0
19 *
20 * Works in pixels as main units - converting to PDF units when outputing to PDF string and on returning size
21 *
22 * @author Ian Back
23 * @author sylvain briand (syb@godisaduck.com), modified by rick trevino (rtrevino1@yahoo.com)
24 *
25 * @link http://www.godisaduck.com/svg2pdf_with_fpdf
26 * @link http://rhodopsin.blogspot.com
27 */
28class Svg
29{
30
31	/**
32	 * ATM marked as public in spite of xml handling callbacks
33	 *
34	 * @var \Mpdf\Mpdf
35	 */
36	public $mpdf;
37
38	/**
39	 * @var \Mpdf\Otl
40	 */
41	public $otl;
42
43	/**
44	 * @var \Mpdf\CssManager
45	 */
46	public $cssManager;
47
48	/**
49	 * @var \Mpdf\SizeConverter
50	 */
51	public $sizeConverter;
52
53	/**
54	 * @var \Mpdf\Color\ColorConverter
55	 */
56	public $colorConverter;
57
58	/**
59	 * @var \Mpdf\Language\LanguageToFontInterface
60	 */
61	public $languageToFont;
62
63	/**
64	 * @var \Mpdf\Language\ScriptToLanguageInterface
65	 */
66	public $scriptToLanguage;
67
68	/**
69	 * @var \Mpdf\Image\ImageProcessor
70	 */
71	private $imageProcessor;
72
73	/**
74	 * Holds content of SVG fonts defined in image
75	 *
76	 * @var array
77	 */
78	var $svg_font;
79
80	/**
81	 * contient les infos sur les gradient fill du svg classé par id du svg
82	 *
83	 * @var array
84	 */
85	var $svg_gradient;
86
87	/**
88	 * contient les ids des objet shading
89	 *
90	 * @var array
91	 */
92	var $svg_shadinglist;
93
94	/**
95	 * contenant les infos du svg voulue par l'utilisateur
96	 *
97	 * @var array
98	 */
99	var $svg_info;
100
101	/**
102	 * holds all attributes of root <svg> tag
103	 *
104	 * @var array
105	 */
106	var $svg_attribs;
107
108	/**
109	 * contenant les style de groupes du svg
110	 *
111	 * @var array
112	 */
113	var $svg_style;
114
115	/**
116	 * contenant le tracage du svg en lui même.
117	 *
118	 * @var string
119	 */
120	var $svg_string;
121
122	/**
123	 * holds string info to write txt to image
124	 *
125	 * @var string
126	 */
127	var $txt_data;
128
129	/**
130	 * @var array
131	 */
132	var $txt_style;
133
134	var $xbase;
135
136	var $ybase;
137
138	var $svg_error;
139
140	var $subPathInit;
141
142	var $spxstart;
143
144	var $spystart;
145
146	var $kp; // convert pixels to PDF units
147
148	var $pathBBox;
149
150	var $textlength; // mPDF 5.7.4
151
152	var $texttotallength; // mPDF 5.7.4
153
154	var $textoutput; // mPDF 5.7.4
155
156	var $textanchor; // mPDF 5.7.4
157
158	var $textXorigin; // mPDF 5.7.4
159
160	var $textYorigin; // mPDF 5.7.4
161
162	var $textjuststarted; // mPDF 5.7.4
163
164	var $intext;  // mPDF 5.7.4
165
166	private $dashesUsed;
167
168	private $kf;
169
170	private $lastcommand;
171
172	private $lastcontrolpoints;
173
174	private $inDefs;
175
176	public function __construct(
177		Mpdf $mpdf,
178		Otl $otl,
179		CssManager $cssManager,
180		ImageProcessor $imageProcessor,
181		SizeConverter $sizeConverter,
182		ColorConverter $colorConverter,
183		LanguageToFontInterface $languageToFont,
184		ScriptToLanguageInterface $scriptToLanguage
185	) {
186
187		$this->mpdf = $mpdf;
188		$this->otl = $otl;
189		$this->cssManager = $cssManager;
190		$this->imageProcessor = $imageProcessor;
191		$this->sizeConverter = $sizeConverter;
192		$this->colorConverter = $colorConverter;
193		$this->languageToFont = $languageToFont;
194		$this->scriptToLanguage = $scriptToLanguage;
195
196		$this->svg_font = []; // mPDF 6
197		$this->svg_gradient = [];
198		$this->svg_shadinglist = [];
199		$this->txt_data = [];
200		$this->svg_string = '';
201		$this->svg_info = [];
202		$this->svg_attribs = [];
203		$this->xbase = 0;
204		$this->ybase = 0;
205		$this->svg_error = false;
206		$this->subPathInit = false;
207		$this->dashesUsed = false;
208
209		$this->textlength = 0; // mPDF 5.7.4
210		$this->texttotallength = 0; // mPDF 5.7.4
211		$this->textoutput = ''; // mPDF 5.7.4
212		$this->textanchor = 'start'; // mPDF 5.7.4
213		$this->textXorigin = 0; // mPDF 5.7.4
214		$this->textYorigin = 0; // mPDF 5.7.4
215		$this->textjuststarted = false; // mPDF 5.7.4
216		$this->intext = false; // mPDF 5.7.4
217
218		$this->kp = 72 / $mpdf->img_dpi; // constant To convert pixels to pts/PDF units
219		$this->kf = 1; // constant To convert font size if re-mapped
220		$this->pathBBox = [];
221
222		$this->svg_style = [
223			[
224				'fill' => 'black',
225				'fill-opacity' => 1, //	remplissage opaque par defaut
226				'fill-rule' => 'nonzero', //	mode de remplissage par defaut
227				'stroke' => 'none', //	pas de trait par defaut
228				'stroke-linecap' => 'butt', //	style de langle par defaut
229				'stroke-linejoin' => 'miter',
230				'stroke-miterlimit' => 4, //	limite de langle par defaut
231				'stroke-opacity' => 1, //	trait opaque par defaut
232				'stroke-width' => 1,
233				'stroke-dasharray' => 0,
234				'stroke-dashoffset' => 0,
235				'color' => ''
236			]
237		];
238
239		$this->txt_style = [
240			[
241				'fill' => 'black', //	pas de remplissage par defaut
242				'font-family' => $mpdf->default_font,
243				'font-size' => $mpdf->default_font_size, // 	****** this is pts
244				'font-weight' => 'normal', //	normal | bold
245				'font-style' => 'normal', //	italic | normal
246				'text-anchor' => 'start', // alignment: start, middle, end
247				'fill-opacity' => 1, //	remplissage opaque par defaut
248				'fill-rule' => 'nonzero', //	mode de remplissage par defaut
249				'stroke' => 'none', //	pas de trait par defaut
250				'stroke-opacity' => 1, //	trait opaque par defaut
251				'stroke-width' => 1,
252				'color' => ''
253			]
254		];
255	}
256
257	// mPDF 5.7.4 Embedded image
258	function svgImage($attribs)
259	{
260		// x and y are coordinates
261		$x = (isset($attribs['x']) ? $attribs['x'] : 0);
262		$y = (isset($attribs['y']) ? $attribs['y'] : 0);
263		// preserveAspectRatio
264		$par = (isset($attribs['preserveAspectRatio']) ? $attribs['preserveAspectRatio'] : 'xMidYMid meet');
265		// width and height are <lengths> - Required attributes
266		$wset = (isset($attribs['width']) ? $attribs['width'] : 0);
267		$hset = (isset($attribs['height']) ? $attribs['height'] : 0);
268		$w = $this->sizeConverter->convert($wset, $this->svg_info['w'] * (25.4 / $this->mpdf->dpi), $this->mpdf->FontSize, false);
269		$h = $this->sizeConverter->convert($hset, $this->svg_info['h'] * (25.4 / $this->mpdf->dpi), $this->mpdf->FontSize, false);
270		if ($w == 0 || $h == 0) {
271			return;
272		}
273		// Convert to pixels = SVG units
274		$w *= 1 / (25.4 / $this->mpdf->dpi);
275		$h *= 1 / (25.4 / $this->mpdf->dpi);
276
277		$srcpath = $attribs['xlink:href'];
278		$orig_srcpath = '';
279		if (trim($srcpath) != '' && substr($srcpath, 0, 4) == 'var:') {
280			$orig_srcpath = $srcpath;
281			$srcpath = $this->mpdf->GetFullPath($srcpath);
282		}
283
284		// Image file (does not allow vector images i.e. WMF/SVG)
285		// mPDF 6 Added $this->mpdf->interpolateImages
286		$info = $this->imageProcessor->getImage($srcpath, true, false, $orig_srcpath, $this->mpdf->interpolateImages);
287		if (!$info) {
288			return;
289		}
290
291		// x,y,w,h define the reference rectangle
292		$img_h = $h;
293		$img_w = $w;
294		$img_x = $x;
295		$img_y = $y;
296		$meetOrSlice = 'meet';
297
298		// preserveAspectRatio
299		$ar = preg_split('/\s+/', strtolower($par));
300		if ($ar[0] != 'none') { // If "none" need to do nothing
301			//  Force uniform scaling
302			if (isset($ar[1]) && $ar[1] == 'slice') {
303				$meetOrSlice = 'slice';
304			} else {
305				$meetOrSlice = 'meet';
306			}
307			if ($info['h'] / $info['w'] > $h / $w) {
308				if ($meetOrSlice == 'meet') { // the entire viewBox is visible within the viewport
309					$img_w = $img_h * $info['w'] / $info['h'];
310				} else { // the entire viewport is covered by the viewBox
311					$img_h = $img_w * $info['h'] / $info['w'];
312				}
313			} elseif ($info['h'] / $info['w'] < $h / $w) {
314				if ($meetOrSlice == 'meet') { // the entire viewBox is visible within the viewport
315					$img_h = $img_w * $info['h'] / $info['w'];
316				} else { // the entire viewport is covered by the viewBox
317					$img_w = $img_h * $info['w'] / $info['h'];
318				}
319			}
320			if ($ar[0] == 'xminymin') {
321				// do nothing to x
322				// do nothing to y
323			} elseif ($ar[0] == 'xmidymin') {
324				$img_x += $w / 2 - $img_w / 2; // xMid
325				// do nothing to y
326			} elseif ($ar[0] == 'xmaxymin') {
327				$img_x += $w - $img_w; // xMax
328				// do nothing to y
329			} elseif ($ar[0] == 'xminymid') {
330				// do nothing to x
331				$img_y += $h / 2 - $img_h / 2; // yMid
332			} elseif ($ar[0] == 'xmaxymid') {
333				$img_x += $w - $img_w; // xMax
334				$img_y += $h / 2 - $img_h / 2; // yMid
335			} elseif ($ar[0] == 'xminymax') {
336				// do nothing to x
337				$img_y += $h - $img_h; // yMax
338			} elseif ($ar[0] == 'xmidymax') {
339				$img_x += $w / 2 - $img_w / 2; // xMid
340				$img_y += $h - $img_h; // yMax
341			} elseif ($ar[0] == 'xmaxymax') {
342				$img_x += $w - $img_w; // xMax
343				$img_y += $h - $img_h; // yMax
344			} else { // xMidYMid (the default)
345				$img_x += $w / 2 - $img_w / 2; // xMid
346				$img_y += $h / 2 - $img_h / 2; // yMid
347			}
348		}
349
350		// Output
351		if ($meetOrSlice == 'slice') { // need to add a clipping path to reference rectangle
352			$s = ' q 0 w '; // Line width=0
353			$s .= sprintf('%.3F %.3F m ', ($x) * $this->kp, (-($y + $h)) * $this->kp); // start point TL before the arc
354			$s .= sprintf('%.3F %.3F l ', ($x) * $this->kp, (-($y)) * $this->kp); // line to BL
355			$s .= sprintf('%.3F %.3F l ', ($x + $w) * $this->kp, (-($y)) * $this->kp); // line to BR
356			$s .= sprintf('%.3F %.3F l ', ($x + $w) * $this->kp, (-($y + $h)) * $this->kp); // line to TR
357			$s .= sprintf('%.3F %.3F l ', ($x) * $this->kp, (-($y + $h)) * $this->kp); // line to TL
358			$s .= ' W n '; // Ends path no-op & Sets the clipping path
359			$this->svgWriteString($s);
360		}
361
362		$outstring = sprintf(" q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q ", $img_w * $this->kp, $img_h * $this->kp, $img_x * $this->kp, -($img_y + $img_h) * $this->kp, $info['i']);
363		$this->svgWriteString($outstring);
364
365		if ($meetOrSlice == 'slice') { // need to end clipping path
366			$this->svgWriteString(' Q ');
367		}
368	}
369
370	function svgGradient($gradient_info, $attribs, $element)
371	{
372		$n = count($this->mpdf->gradients) + 1;
373
374		// Get bounding dimensions of element
375		$w = 100;
376		$h = 100;
377		$x_offset = 0;
378		$y_offset = 0;
379		if ($element == 'rect') {
380			$w = $attribs['width'];
381			$h = $attribs['height'];
382			$x_offset = $attribs['x'];
383			$y_offset = $attribs['y'];
384		} elseif ($element == 'ellipse') {
385			$w = $attribs['rx'] * 2;
386			$h = $attribs['ry'] * 2;
387			$x_offset = $attribs['cx'] - $attribs['rx'];
388			$y_offset = $attribs['cy'] - $attribs['ry'];
389		} elseif ($element == 'circle') {
390			$w = $attribs['r'] * 2;
391			$h = $attribs['r'] * 2;
392			$x_offset = $attribs['cx'] - $attribs['r'];
393			$y_offset = $attribs['cy'] - $attribs['r'];
394		} elseif ($element == 'polygon') {
395			$pts = preg_split('/[ ,]+/', trim($attribs['points']));
396			$maxr = $maxb = 0;
397			$minl = $mint = 999999;
398			for ($i = 0; $i < count($pts); $i++) {
399				if ($i % 2 == 0) { // x values
400					$minl = min($minl, $pts[$i]);
401					$maxr = max($maxr, $pts[$i]);
402				} else { // y values
403					$mint = min($mint, $pts[$i]);
404					$maxb = max($maxb, $pts[$i]);
405				}
406			}
407			$w = $maxr - $minl;
408			$h = $maxb - $mint;
409			$x_offset = $minl;
410			$y_offset = $mint;
411		} elseif ($element == 'path') {
412			if (is_array($this->pathBBox) && $this->pathBBox[2] > 0) {
413				$w = $this->pathBBox[2];
414				$h = $this->pathBBox[3];
415				$x_offset = $this->pathBBox[0];
416				$y_offset = $this->pathBBox[1];
417			} else {
418				preg_match_all('/([a-z]|[A-Z])([ ,\-.\d]+)*/', $attribs['d'], $commands, PREG_SET_ORDER);
419				$maxr = $maxb = 0;
420				$minl = $mint = 999999;
421				foreach ($commands as $c) {
422					if (count($c) == 3) {
423						list($tmp, $cmd, $arg) = $c;
424						if ($cmd == 'M' || $cmd == 'L' || $cmd == 'C' || $cmd == 'S' || $cmd == 'Q' || $cmd == 'T') {
425							$pts = preg_split('/[ ,]+/', trim($arg));
426							for ($i = 0; $i < count($pts); $i++) {
427								if ($i % 2 == 0) { // x values
428									$minl = min($minl, $pts[$i]);
429									$maxr = max($maxr, $pts[$i]);
430								} else { // y values
431									$mint = min($mint, $pts[$i]);
432									$maxb = max($maxb, $pts[$i]);
433								}
434							}
435						}
436						if ($cmd == 'H') { // sets new x
437							$minl = min($minl, $arg);
438							$maxr = max($maxr, $arg);
439						}
440						if ($cmd == 'V') { // sets new y
441							$mint = min($mint, $arg);
442							$maxb = max($maxb, $arg);
443						}
444					}
445				}
446				$w = $maxr - $minl;
447				$h = $maxb - $mint;
448				$x_offset = $minl;
449				$y_offset = $mint;
450			}
451		}
452		if (!$w || $w == -999999) {
453			$w = 100;
454		}
455		if (!$h || $h == -999999) {
456			$h = 100;
457		}
458		if ($x_offset == 999999) {
459			$x_offset = 0;
460		}
461		if ($y_offset == 999999) {
462			$y_offset = 0;
463		}
464
465		// TRANSFORMATIONS
466		$transformations = '';
467		if (isset($gradient_info['transform'])) {
468			preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is', $gradient_info['transform'], $m);
469			if (count($m[0])) {
470				for ($i = 0; $i < count($m[0]); $i++) {
471					$c = strtolower($m[1][$i]);
472					$v = trim($m[2][$i]);
473					$vv = preg_split('/[ ,]+/', $v);
474					if ($c == 'matrix' && count($vv) == 6) {
475						// Note angle of rotation is reversed (from SVG to PDF), so vv[1] and vv[2] are negated
476						// cf svgDefineStyle()
477						$transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $vv[0], -$vv[1], -$vv[2], $vv[3], $vv[4] * $this->kp, -$vv[5] * $this->kp);
478					} elseif ($c == 'translate' && count($vv)) {
479						$tm[4] = $vv[0];
480						if (count($vv) == 2) {
481							$t_y = -$vv[1];
482						} else {
483							$t_y = 0;
484						}
485						$tm[5] = $t_y;
486						$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $tm[4] * $this->kp, $tm[5] * $this->kp);
487					} elseif ($c == 'scale' && count($vv)) {
488						if (count($vv) == 2) {
489							$s_y = $vv[1];
490						} else {
491							$s_y = $vv[0];
492						}
493						$tm[0] = $vv[0];
494						$tm[3] = $s_y;
495						$transformations .= sprintf(' %.3F 0 0 %.3F 0 0 cm ', $tm[0], $tm[3]);
496					} elseif ($c == 'rotate' && count($vv)) {
497						$tm[0] = cos(deg2rad(-$vv[0]));
498						$tm[1] = sin(deg2rad(-$vv[0]));
499						$tm[2] = -$tm[1];
500						$tm[3] = $tm[0];
501						if (count($vv) == 3) {
502							$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $vv[1] * $this->kp, -$vv[2] * $this->kp);
503						}
504						$transformations .= sprintf(' %.3F %.3F %.3F %.3F 0 0 cm ', $tm[0], $tm[1], $tm[2], $tm[3]);
505						if (count($vv) == 3) {
506							$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', -$vv[1] * $this->kp, $vv[2] * $this->kp);
507						}
508					} elseif ($c == 'skewx' && count($vv)) {
509						$tm[2] = tan(deg2rad(-$vv[0]));
510						$transformations .= sprintf(' 1 0 %.3F 1 0 0 cm ', $tm[2]);
511					} elseif ($c == 'skewy' && count($vv)) {
512						$tm[1] = tan(deg2rad(-$vv[0]));
513						$transformations .= sprintf(' 1 %.3F 0 1 0 0 cm ', $tm[1]);
514					}
515				}
516			}
517		}
518
519
520		$return = "";
521
522		if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
523			if ($transformations) {
524				$return .= $transformations;
525			}
526		}
527		$spread = 'P';  // pad
528		if (isset($gradient_info['spread'])) {
529			if (strtolower($gradient_info['spread']) == 'reflect') {
530				$spread = 'F';
531			} // reflect
532			elseif (strtolower($gradient_info['spread']) == 'repeat') {
533				$spread = 'R';
534			} // repeat
535		}
536
537
538		for ($i = 0; $i < (count($gradient_info['color'])); $i++) {
539			if (stristr($gradient_info['color'][$i]['offset'], '%') !== false) {
540				$gradient_info['color'][$i]['offset'] = ((float) $gradient_info['color'][$i]['offset']) / 100;
541			}
542			if (isset($gradient_info['color'][($i + 1)]['offset']) && stristr($gradient_info['color'][($i + 1)]['offset'], '%') !== false) {
543				$gradient_info['color'][($i + 1)]['offset'] = ((float) $gradient_info['color'][($i + 1)]['offset']) / 100;
544			}
545			if ($gradient_info['color'][$i]['offset'] < 0) {
546				$gradient_info['color'][$i]['offset'] = 0;
547			}
548			if ($gradient_info['color'][$i]['offset'] > 1) {
549				$gradient_info['color'][$i]['offset'] = 1;
550			}
551			if ($i > 0) {
552				if ($gradient_info['color'][$i]['offset'] < $gradient_info['color'][($i - 1)]['offset']) {
553					$gradient_info['color'][$i]['offset'] = $gradient_info['color'][($i - 1)]['offset'];
554				}
555			}
556		}
557
558		if (isset($gradient_info['color'][0]['offset']) && $gradient_info['color'][0]['offset'] > 0) {
559			array_unshift($gradient_info['color'], $gradient_info['color'][0]);
560			$gradient_info['color'][0]['offset'] = 0;
561		}
562		$ns = count($gradient_info['color']);
563		if (isset($gradient_info['color'][($ns - 1)]['offset']) && $gradient_info['color'][($ns - 1)]['offset'] < 1) {
564			$gradient_info['color'][] = $gradient_info['color'][($ns - 1)];
565			$gradient_info['color'][($ns)]['offset'] = 1;
566		}
567		$ns = count($gradient_info['color']);
568
569
570
571
572		if ($gradient_info['type'] == 'linear') {
573			// mPDF 4.4.003
574			if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
575				if (isset($gradient_info['info']['x1'])) {
576					$gradient_info['info']['x1'] = ($gradient_info['info']['x1'] - $x_offset) / $w;
577				}
578				if (isset($gradient_info['info']['y1'])) {
579					$gradient_info['info']['y1'] = ($gradient_info['info']['y1'] - $y_offset) / $h;
580				}
581				if (isset($gradient_info['info']['x2'])) {
582					$gradient_info['info']['x2'] = ($gradient_info['info']['x2'] - $x_offset) / $w;
583				}
584				if (isset($gradient_info['info']['y2'])) {
585					$gradient_info['info']['y2'] = ($gradient_info['info']['y2'] - $y_offset) / $h;
586				}
587			}
588			if (isset($gradient_info['info']['x1'])) {
589				$x1 = $gradient_info['info']['x1'];
590			} else {
591				$x1 = 0;
592			}
593			if (isset($gradient_info['info']['y1'])) {
594				$y1 = $gradient_info['info']['y1'];
595			} else {
596				$y1 = 0;
597			}
598			if (isset($gradient_info['info']['x2'])) {
599				$x2 = $gradient_info['info']['x2'];
600			} else {
601				$x2 = 1;
602			}
603			if (isset($gradient_info['info']['y2'])) {
604				$y2 = $gradient_info['info']['y2'];
605			} else {
606				$y2 = 0;
607			} // mPDF 6
608
609			if (stristr($x1, '%') !== false) {
610				$x1 = ($x1 + 0) / 100;
611			}
612			if (stristr($x2, '%') !== false) {
613				$x2 = ($x2 + 0) / 100;
614			}
615			if (stristr($y1, '%') !== false) {
616				$y1 = ($y1 + 0) / 100;
617			}
618			if (stristr($y2, '%') !== false) {
619				$y2 = ($y2 + 0) / 100;
620			}
621
622			// mPDF 5.0.042
623			$bboxw = $w;
624			$bboxh = $h;
625			$usex = $x_offset;
626			$usey = $y_offset;
627			$usew = $bboxw;
628			$useh = $bboxh;
629			if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
630				$angle = rad2deg(atan2(($gradient_info['info']['y2'] - $gradient_info['info']['y1']), ($gradient_info['info']['x2'] - $gradient_info['info']['x1'])));
631				if ($angle < 0) {
632					$angle += 360;
633				} elseif ($angle > 360) {
634					$angle -= 360;
635				}
636				if ($angle != 0 && $angle != 360 && $angle != 90 && $angle != 180 && $angle != 270) {
637					if ($w >= $h) {
638						$y1 *= $h / $w;
639						$y2 *= $h / $w;
640						$usew = $useh = $bboxw;
641					} else {
642						$x1 *= $w / $h;
643						$x2 *= $w / $h;
644						$usew = $useh = $bboxh;
645					}
646				}
647			}
648			$a = $usew;  // width
649			$d = -$useh; // height
650			$e = $usex;  // x- offset
651			$f = -$usey; // -y-offset
652
653			$return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a * $this->kp, $d * $this->kp, $e * $this->kp, $f * $this->kp);
654
655			if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'objectboundingbox') {
656				if ($transformations) {
657					$return .= $transformations;
658				}
659			}
660
661			$trans = false;
662
663			if ($spread == 'R' || $spread == 'F') { // Repeat  /  Reflect
664
665				$offs = [];
666				for ($i = 0; $i < $ns; $i++) {
667					$offs[$i] = $gradient_info['color'][$i]['offset'];
668				}
669
670				$gp = 0;
671				$inside = true;
672
673				while ($inside) {
674					$gp++;
675					for ($i = 0; $i < $ns; $i++) {
676						if ($spread == 'F' && ($gp % 2) == 1) { // Reflect
677							$gradient_info['color'][(($ns * $gp) + $i)] = $gradient_info['color'][(($ns * ($gp - 1)) + ($ns - $i - 1))];
678							$tmp = $gp + (1 - $offs[($ns - $i - 1)]);
679							$gradient_info['color'][(($ns * $gp) + $i)]['offset'] = $tmp;
680						} else { // Reflect
681							$gradient_info['color'][(($ns * $gp) + $i)] = $gradient_info['color'][$i];
682							$tmp = $gp + $offs[$i];
683							$gradient_info['color'][(($ns * $gp) + $i)]['offset'] = $tmp;
684						}
685						// IF STILL INSIDE BOX OR STILL VALID
686						// Point on axis to test
687						$px1 = $x1 + ($x2 - $x1) * $tmp;
688						$py1 = $y1 + ($y2 - $y1) * $tmp;
689						// Get perpendicular axis
690						$alpha = atan2($y2 - $y1, $x2 - $x1);
691						$alpha += M_PI / 2; // rotate 90 degrees
692						// Get arbitrary point to define line perpendicular to axis
693						$px2 = $px1 + cos($alpha);
694						$py2 = $py1 + sin($alpha);
695
696						$res1 = $this->testIntersect($px1, $py1, $px2, $py2, 0, 0, 0, 1); // $x=0 vert axis
697						$res2 = $this->testIntersect($px1, $py1, $px2, $py2, 1, 0, 1, 1); // $x=1 vert axis
698						$res3 = $this->testIntersect($px1, $py1, $px2, $py2, 0, 0, 1, 0); // $y=0 horiz axis
699						$res4 = $this->testIntersect($px1, $py1, $px2, $py2, 0, 1, 1, 1); // $y=1 horiz axis
700
701						if (!$res1 && !$res2 && !$res3 && !$res4) {
702							$inside = false;
703						}
704					}
705				}
706
707				$inside = true;
708				$gp = 0;
709				while ($inside) {
710					$gp++;
711					$newarr = [];
712					for ($i = 0; $i < $ns; $i++) {
713						if ($spread == 'F') { // Reflect
714							$newarr[$i] = $gradient_info['color'][($ns - $i - 1)];
715							if (($gp % 2) == 1) {
716								$tmp = -$gp + (1 - $offs[($ns - $i - 1)]);
717								$newarr[$i]['offset'] = $tmp;
718							} else {
719								$tmp = -$gp + $offs[$i];
720								$newarr[$i]['offset'] = $tmp;
721							}
722						} else { // Reflect
723							$newarr[$i] = $gradient_info['color'][$i];
724							$tmp = -$gp + $offs[$i];
725							$newarr[$i]['offset'] = $tmp;
726						}
727
728						// IF STILL INSIDE BOX OR STILL VALID
729						// Point on axis to test
730						$px1 = $x1 + ($x2 - $x1) * $tmp;
731						$py1 = $y1 + ($y2 - $y1) * $tmp;
732						// Get perpendicular axis
733						$alpha = atan2($y2 - $y1, $x2 - $x1);
734						$alpha += M_PI / 2; // rotate 90 degrees
735						// Get arbitrary point to define line perpendicular to axis
736						$px2 = $px1 + cos($alpha);
737						$py2 = $py1 + sin($alpha);
738
739						$res1 = $this->testIntersect($px1, $py1, $px2, $py2, 0, 0, 0, 1); // $x=0 vert axis
740						$res2 = $this->testIntersect($px1, $py1, $px2, $py2, 1, 0, 1, 1); // $x=1 vert axis
741						$res3 = $this->testIntersect($px1, $py1, $px2, $py2, 0, 0, 1, 0); // $y=0 horiz axis
742						$res4 = $this->testIntersect($px1, $py1, $px2, $py2, 0, 1, 1, 1); // $y=1 horiz axis
743						if (!$res1 && !$res2 && !$res3 && !$res4) {
744							$inside = false;
745						}
746					}
747					for ($i = ($ns - 1); $i >= 0; $i--) {
748						if (isset($newarr[$i]['offset'])) {
749							array_unshift($gradient_info['color'], $newarr[$i]);
750						}
751					}
752				}
753			}
754
755			// Gradient STOPs
756			$stops = count($gradient_info['color']);
757			if ($stops < 2) {
758				return '';
759			}
760
761			$range = $gradient_info['color'][count($gradient_info['color']) - 1]['offset'] - $gradient_info['color'][0]['offset'];
762			$min = $gradient_info['color'][0]['offset'];
763
764			for ($i = 0; $i < ($stops); $i++) {
765				if (!$gradient_info['color'][$i]['color']) {
766					if ($gradient_info['colorspace'] == 'RGB') {
767						$gradient_info['color'][$i]['color'] = '0 0 0';
768					} elseif ($gradient_info['colorspace'] == 'Gray') {
769						$gradient_info['color'][$i]['color'] = '0';
770					} elseif ($gradient_info['colorspace'] == 'CMYK') {
771						$gradient_info['color'][$i]['color'] = '1 1 1 1';
772					}
773				}
774				$offset = ($gradient_info['color'][$i]['offset'] - $min) / $range;
775				$this->mpdf->gradients[$n]['stops'][] = [
776					'col' => $gradient_info['color'][$i]['color'],
777					'opacity' => $gradient_info['color'][$i]['opacity'],
778					'offset' => $offset];
779				if ($gradient_info['color'][$i]['opacity'] < 1) {
780					$trans = true;
781				}
782			}
783			$grx1 = $x1 + ($x2 - $x1) * $gradient_info['color'][0]['offset'];
784			$gry1 = $y1 + ($y2 - $y1) * $gradient_info['color'][0]['offset'];
785			$grx2 = $x1 + ($x2 - $x1) * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
786			$gry2 = $y1 + ($y2 - $y1) * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
787
788			$this->mpdf->gradients[$n]['coords'] = [$grx1, $gry1, $grx2, $gry2];
789
790			$this->mpdf->gradients[$n]['colorspace'] = $gradient_info['colorspace'];
791
792			$this->mpdf->gradients[$n]['type'] = 2;
793			$this->mpdf->gradients[$n]['fo'] = true;
794
795			$this->mpdf->gradients[$n]['extend'] = ['true', 'true'];
796			if ($trans) {
797				$this->mpdf->gradients[$n]['trans'] = true;
798				$return .= ' /TGS' . ($n) . ' gs ';
799			}
800			$return .= ' /Sh' . ($n) . ' sh ';
801			$return .= " Q\n";
802		} elseif ($gradient_info['type'] == 'radial') {
803			if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
804				if ($w > $h) {
805					$h = $w;
806				} else {
807					$w = $h;
808				}
809				if (isset($gradient_info['info']['x0'])) {
810					$gradient_info['info']['x0'] = ($gradient_info['info']['x0'] - $x_offset) / $w;
811				}
812				if (isset($gradient_info['info']['y0'])) {
813					$gradient_info['info']['y0'] = ($gradient_info['info']['y0'] - $y_offset) / $h;
814				}
815				if (isset($gradient_info['info']['x1'])) {
816					$gradient_info['info']['x1'] = ($gradient_info['info']['x1'] - $x_offset) / $w;
817				}
818				if (isset($gradient_info['info']['y1'])) {
819					$gradient_info['info']['y1'] = ($gradient_info['info']['y1'] - $y_offset) / $h;
820				}
821				if (isset($gradient_info['info']['r'])) {
822					$gradient_info['info']['rx'] = $gradient_info['info']['r'] / $w;
823				}
824				if (isset($gradient_info['info']['r'])) {
825					$gradient_info['info']['ry'] = $gradient_info['info']['r'] / $h;
826				}
827			}
828
829			if (isset($gradient_info['info']['x0'])) {
830				$x0 = $gradient_info['info']['x0'];
831			} else {
832				$x0 = 0.5;
833			}
834			if (isset($gradient_info['info']['y0'])) {
835				$y0 = $gradient_info['info']['y0'];
836			} else {
837				$y0 = 0.5;
838			}
839			if (isset($gradient_info['info']['rx'])) {
840				$rx = $gradient_info['info']['rx'];
841			} elseif (isset($gradient_info['info']['r'])) {
842				$rx = $gradient_info['info']['r'];
843			} else {
844				$rx = 0.5;
845			}
846			if (isset($gradient_info['info']['ry'])) {
847				$ry = $gradient_info['info']['ry'];
848			} elseif (isset($gradient_info['info']['r'])) {
849				$ry = $gradient_info['info']['r'];
850			} else {
851				$ry = 0.5;
852			}
853			if (isset($gradient_info['info']['x1'])) {
854				$x1 = $gradient_info['info']['x1'];
855			} else {
856				$x1 = $x0;
857			}
858			if (isset($gradient_info['info']['y1'])) {
859				$y1 = $gradient_info['info']['y1'];
860			} else {
861				$y1 = $y0;
862			}
863
864			if (stristr($x1, '%') !== false) {
865				$x1 = ($x1 + 0) / 100;
866			}
867			if (stristr($x0, '%') !== false) {
868				$x0 = ($x0 + 0) / 100;
869			}
870			if (stristr($y1, '%') !== false) {
871				$y1 = ($y1 + 0) / 100;
872			}
873			if (stristr($y0, '%') !== false) {
874				$y0 = ($y0 + 0) / 100;
875			}
876			if (stristr($rx, '%') !== false) {
877				$rx = ($rx + 0) / 100;
878			}
879			if (stristr($ry, '%') !== false) {
880				$ry = ($ry + 0) / 100;
881			}
882
883			$bboxw = $w;
884			$bboxh = $h;
885			$usex = $x_offset;
886			$usey = $y_offset;
887			$usew = $bboxw;
888			$useh = $bboxh;
889
890			if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
891
892				$ay1 = isset($gradient_info['info']['y1']) ? $gradient_info['info']['y1'] : 0;
893				$ax1 = isset($gradient_info['info']['x1']) ? $gradient_info['info']['x1'] : 0;
894
895				$angle = rad2deg(atan2(
896					($gradient_info['info']['y0'] - $ay1),
897					($gradient_info['info']['x0'] - $ax1)
898				));
899
900				if ($angle < 0) {
901					$angle += 360;
902				} elseif ($angle > 360) {
903					$angle -= 360;
904				}
905
906				if ($angle != 0 && $angle != 360 && $angle != 90 && $angle != 180 && $angle != 270) {
907					if ($w >= $h) {
908						$y1 *= $h / $w;
909						$y0 *= $h / $w;
910						$rx *= $h / $w;
911						$ry *= $h / $w;
912						$usew = $useh = $bboxw;
913					} else {
914						$x1 *= $w / $h;
915						$x0 *= $w / $h;
916						$rx *= $w / $h;
917						$ry *= $w / $h;
918						$usew = $useh = $bboxh;
919					}
920				}
921			}
922
923			$a = $usew;  // width
924			$d = -$useh; // height
925			$e = $usex;  // x- offset
926			$f = -$usey; // -y-offset
927
928			$r = $rx;
929
930			$return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a * $this->kp, $d * $this->kp, $e * $this->kp, $f * $this->kp);
931
932			// mPDF 5.0.039
933			if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'objectboundingbox') {
934				if ($transformations) {
935					$return .= $transformations;
936				}
937			}
938
939			// mPDF 5.7.4
940			// x1 and y1 (fx, fy) should be inside the circle defined by x0 y0 (cx, cy)
941			// "If the point defined by fx and fy lies outside the circle defined by cx, cy and r, then the user agent shall set
942			// the focal point to the intersection of the line from (cx, cy) to (fx, fy) with the circle defined by cx, cy and r."
943			while (pow(($x1 - $x0), 2) + pow(($y1 - $y0), 2) >= pow($r, 2)) {
944				// Gradually move along fx,fy towards cx,cy in 100'ths until meets criteria
945				$x1 -= ($x1 - $x0) / 100;
946				$y1 -= ($y1 - $y0) / 100;
947			}
948
949
950			if ($spread == 'R' || $spread == 'F') { // Repeat  /  Reflect
951				$offs = [];
952				for ($i = 0; $i < $ns; $i++) {
953					$offs[$i] = $gradient_info['color'][$i]['offset'];
954				}
955				$gp = 0;
956				$inside = true;
957				while ($inside) {
958					$gp++;
959					for ($i = 0; $i < $ns; $i++) {
960						if ($spread == 'F' && ($gp % 2) == 1) { // Reflect
961							$gradient_info['color'][(($ns * $gp) + $i)] = $gradient_info['color'][(($ns * ($gp - 1)) + ($ns - $i - 1))];
962							$tmp = $gp + (1 - $offs[($ns - $i - 1)]);
963							$gradient_info['color'][(($ns * $gp) + $i)]['offset'] = $tmp;
964						} else { // Reflect
965							$gradient_info['color'][(($ns * $gp) + $i)] = $gradient_info['color'][$i];
966							$tmp = $gp + $offs[$i];
967							$gradient_info['color'][(($ns * $gp) + $i)]['offset'] = $tmp;
968						}
969						// IF STILL INSIDE BOX OR STILL VALID
970						// TEST IF circle (perimeter) intersects with
971						// or is enclosed
972						// Point on axis to test
973						$px = $x1 + ($x0 - $x1) * $tmp;
974						$py = $y1 + ($y0 - $y1) * $tmp;
975						$pr = $r * $tmp;
976						$res = $this->testIntersectCircle($px, $py, $pr);
977						if (!$res) {
978							$inside = false;
979						}
980					}
981				}
982			}
983
984			// Gradient STOPs
985			$stops = count($gradient_info['color']);
986			if ($stops < 2) {
987				return '';
988			}
989
990			$range = $gradient_info['color'][count($gradient_info['color']) - 1]['offset'] - $gradient_info['color'][0]['offset'];
991			$min = $gradient_info['color'][0]['offset'];
992
993			for ($i = 0; $i < ($stops); $i++) {
994				if (!$gradient_info['color'][$i]['color']) {
995					if ($gradient_info['colorspace'] == 'RGB') {
996						$gradient_info['color'][$i]['color'] = '0 0 0';
997					} elseif ($gradient_info['colorspace'] == 'Gray') {
998						$gradient_info['color'][$i]['color'] = '0';
999					} elseif ($gradient_info['colorspace'] == 'CMYK') {
1000						$gradient_info['color'][$i]['color'] = '1 1 1 1';
1001					}
1002				}
1003				$offset = ($gradient_info['color'][$i]['offset'] - $min) / $range;
1004				$this->mpdf->gradients[$n]['stops'][] = [
1005					'col' => $gradient_info['color'][$i]['color'],
1006					'opacity' => $gradient_info['color'][$i]['opacity'],
1007					'offset' => $offset];
1008				if ($gradient_info['color'][$i]['opacity'] < 1) {
1009					$trans = true;
1010				}
1011			}
1012			$grx1 = $x1 + ($x0 - $x1) * $gradient_info['color'][0]['offset'];
1013			$gry1 = $y1 + ($y0 - $y1) * $gradient_info['color'][0]['offset'];
1014			$grx2 = $x1 + ($x0 - $x1) * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
1015			$gry2 = $y1 + ($y0 - $y1) * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
1016			$grir = $r * $gradient_info['color'][0]['offset'];
1017			$grr = $r * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
1018
1019			$this->mpdf->gradients[$n]['coords'] = [$grx1, $gry1, $grx2, $gry2, abs($grr), abs($grir)];
1020
1021			$this->mpdf->gradients[$n]['colorspace'] = $gradient_info['colorspace'];
1022
1023			$this->mpdf->gradients[$n]['type'] = 3;
1024			$this->mpdf->gradients[$n]['fo'] = true;
1025
1026			$this->mpdf->gradients[$n]['extend'] = ['true', 'true'];
1027			if (isset($trans) && $trans) {
1028				$this->mpdf->gradients[$n]['trans'] = true;
1029				$return .= ' /TGS' . ($n) . ' gs ';
1030			}
1031			$return .= ' /Sh' . ($n) . ' sh ';
1032			$return .= " Q\n";
1033		}
1034
1035		return $return;
1036	}
1037
1038	function svgOffset($attribs)
1039	{
1040		// save all <svg> tag attributes
1041		$this->svg_attribs = $attribs;
1042		if (isset($this->svg_attribs['viewBox'])) {
1043			$vb = preg_split('/\s+/is', trim($this->svg_attribs['viewBox']));
1044			if (count($vb) == 4) {
1045				$this->svg_info['x'] = $vb[0];
1046				$this->svg_info['y'] = $vb[1];
1047				$this->svg_info['w'] = $vb[2];
1048				$this->svg_info['h'] = $vb[3];
1049//				return;
1050			}
1051		}
1052		$svg_w = 0;
1053		$svg_h = 0;
1054		if (isset($attribs['width']) && $attribs['width']) {
1055			$svg_w = $this->sizeConverter->convert($attribs['width']); // mm (interprets numbers as pixels)
1056		}
1057		if (isset($attribs['height']) && $attribs['height']) {
1058			$svg_h = $this->sizeConverter->convert($attribs['height']); // mm
1059		}
1060
1061
1062///*
1063		// mPDF 5.0.005
1064		if (isset($this->svg_info['w']) && $this->svg_info['w']) { // if 'w' set by viewBox
1065			if ($svg_w) { // if width also set, use these values to determine to set size of "pixel"
1066				$this->kp *= ($svg_w / 0.2645) / $this->svg_info['w'];
1067				$this->kf = ($svg_w / 0.2645) / $this->svg_info['w'];
1068			} elseif ($svg_h) {
1069				$this->kp *= ($svg_h / 0.2645) / $this->svg_info['h'];
1070				$this->kf = ($svg_h / 0.2645) / $this->svg_info['h'];
1071			}
1072			return;
1073		}
1074//*/
1075		// Added to handle file without height or width specified
1076		if (!$svg_w && !$svg_h) {
1077			$svg_w = $svg_h = $this->mpdf->blk[$this->mpdf->blklvl]['inner_width'];
1078		} // DEFAULT
1079		if (!$svg_w) {
1080			$svg_w = $svg_h;
1081		}
1082		if (!$svg_h) {
1083			$svg_h = $svg_w;
1084		}
1085
1086		$this->svg_info['x'] = 0;
1087		$this->svg_info['y'] = 0;
1088		$this->svg_info['w'] = $svg_w / 0.2645; // mm->pixels
1089		$this->svg_info['h'] = $svg_h / 0.2645; // mm->pixels
1090	}
1091
1092	//
1093	// check if points are within svg, if not, set to max
1094	function svg_overflow($x, $y)
1095	{
1096		$x2 = $x;
1097		$y2 = $y;
1098		if (isset($this->svg_attribs['overflow'])) {
1099			if ($this->svg_attribs['overflow'] == 'hidden') {
1100				// Not sure if this is supposed to strip off units, but since I dont use any I will omlt this step
1101				$svg_w = preg_replace("/([0-9\.]*)(.*)/i", "$1", $this->svg_attribs['width']);
1102				$svg_h = preg_replace("/([0-9\.]*)(.*)/i", "$1", $this->svg_attribs['height']);
1103
1104				// $xmax = floor($this->svg_attribs['width']);
1105				$xmax = floor($svg_w);
1106				$xmin = 0;
1107				// $ymax = floor(($this->svg_attribs['height'] * -1));
1108				$ymax = floor(($svg_h * -1));
1109				$ymin = 0;
1110
1111				if ($x > $xmax) {
1112					$x2 = $xmax; // right edge
1113				}
1114				if ($x < $xmin) {
1115					$x2 = $xmin; // left edge
1116				}
1117				if ($y < $ymax) {
1118					$y2 = $ymax; // bottom
1119				}
1120				if ($y > $ymin) {
1121					$y2 = $ymin; // top
1122				}
1123			}
1124		}
1125
1126
1127		return ['x' => $x2, 'y' => $y2];
1128	}
1129
1130	function svgDefineStyle($critere_style)
1131	{
1132
1133		$tmp = count($this->svg_style) - 1;
1134		$current_style = $this->svg_style[$tmp];
1135
1136		unset($current_style['transformations']);
1137
1138		// TRANSFORM SCALE
1139		$transformations = '';
1140		if (isset($critere_style['transform'])) {
1141			preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is', $critere_style['transform'], $m);
1142			if (count($m[0])) {
1143				for ($i = 0; $i < count($m[0]); $i++) {
1144					$c = strtolower($m[1][$i]);
1145					$v = trim($m[2][$i]);
1146					$vv = preg_split('/[ ,]+/', $v);
1147					if ($c == 'matrix' && count($vv) == 6) {
1148						// mPDF 5.0.039
1149						// Note angle of rotation is reversed (from SVG to PDF), so vv[1] and vv[2] are negated
1150						$transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $vv[0], -$vv[1], -$vv[2], $vv[3], $vv[4] * $this->kp, -$vv[5] * $this->kp);
1151
1152						/*
1153						  // The long way of doing this??
1154						  // need to reverse angle of rotation from SVG to PDF
1155						  $sx=sqrt(pow($vv[0],2)+pow($vv[2],2));
1156						  if ($vv[0] < 0) { $sx *= -1; } // change sign
1157						  $sy=sqrt(pow($vv[1],2)+pow($vv[3],2));
1158						  if ($vv[3] < 0) { $sy *= -1; } // change sign
1159
1160						  // rotation angle is
1161						  $t=atan2($vv[1],$vv[3]);
1162						  $t=atan2(-$vv[2],$vv[0]);	// Should be the same value or skew has been applied
1163
1164						  // Reverse angle
1165						  $t *= -1;
1166
1167						  // Rebuild matrix
1168						  $ma = $sx * cos($t);
1169						  $mb = $sy * sin($t);
1170						  $mc = -$sx * sin($t);
1171						  $md = $sy * cos($t);
1172
1173						  // $transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $ma, $mb, $mc, $md, $vv[4]*$this->kp, -$vv[5]*$this->kp);
1174						 */
1175					} elseif ($c == 'translate' && count($vv)) {
1176						$tm[4] = $vv[0];
1177						if (count($vv) == 2) {
1178							$t_y = -$vv[1];
1179						} else {
1180							$t_y = 0;
1181						}
1182						$tm[5] = $t_y;
1183						$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $tm[4] * $this->kp, $tm[5] * $this->kp);
1184					} elseif ($c == 'scale' && count($vv)) {
1185						if (count($vv) == 2) {
1186							$s_y = $vv[1];
1187						} else {
1188							$s_y = $vv[0];
1189						}
1190						$tm[0] = $vv[0];
1191						$tm[3] = $s_y;
1192						$transformations .= sprintf(' %.3F 0 0 %.3F 0 0 cm ', $tm[0], $tm[3]);
1193					} elseif ($c == 'rotate' && count($vv)) {
1194						$tm[0] = cos(deg2rad(-$vv[0]));
1195						$tm[1] = sin(deg2rad(-$vv[0]));
1196						$tm[2] = -$tm[1];
1197						$tm[3] = $tm[0];
1198						if (count($vv) == 3) {
1199							$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $vv[1] * $this->kp, -$vv[2] * $this->kp);
1200						}
1201						$transformations .= sprintf(' %.3F %.3F %.3F %.3F 0 0 cm ', $tm[0], $tm[1], $tm[2], $tm[3]);
1202						if (count($vv) == 3) {
1203							$transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', -$vv[1] * $this->kp, $vv[2] * $this->kp);
1204						}
1205					} elseif ($c == 'skewx' && count($vv)) {
1206						$tm[2] = tan(deg2rad(-$vv[0]));
1207						$transformations .= sprintf(' 1 0 %.3F 1 0 0 cm ', $tm[2]);
1208					} elseif ($c == 'skewy' && count($vv)) {
1209						$tm[1] = tan(deg2rad(-$vv[0]));
1210						$transformations .= sprintf(' 1 %.3F 0 1 0 0 cm ', $tm[1]);
1211					}
1212				}
1213			}
1214			$current_style['transformations'] = $transformations;
1215		}
1216
1217		if (isset($critere_style['style'])) {
1218			if (preg_match('/fill:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/i', $critere_style['style'], $m)) { // mPDF 5.7.2
1219				$current_style['fill'] = '#' . str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT) . str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT) . str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
1220			} else {
1221				$tmp = preg_replace("/(.*)fill:\s*([a-z0-9#_()]*|none)(.*)/i", "$2", $critere_style['style']);
1222				if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1223					$current_style['fill'] = $tmp;
1224				}
1225			}
1226
1227			// mPDF 5.7.2
1228			if ((preg_match("/[^-]opacity:\s*([a-z0-9.]*|none)/i", $critere_style['style'], $m) ||
1229				preg_match("/^opacity:\s*([a-z0-9.]*|none)/i", $critere_style['style'], $m)) && $m[1] != 'inherit') {
1230				$current_style['fill-opacity'] = $m[1];
1231				$current_style['stroke-opacity'] = $m[1];
1232			}
1233
1234			$tmp = preg_replace("/(.*)fill-opacity:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
1235			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1236				$current_style['fill-opacity'] = $tmp;
1237			}
1238
1239			$tmp = preg_replace("/(.*)fill-rule:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1240			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1241				$current_style['fill-rule'] = $tmp;
1242			}
1243
1244			if (preg_match('/stroke:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/', $critere_style['style'], $m)) {
1245				$current_style['stroke'] = '#' . str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT) . str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT) . str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
1246			} else {
1247				$tmp = preg_replace("/(.*)stroke:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1248				if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1249					$current_style['stroke'] = $tmp;
1250				}
1251			}
1252
1253			$tmp = preg_replace("/(.*)stroke-linecap:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1254			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1255				$current_style['stroke-linecap'] = $tmp;
1256			}
1257
1258			$tmp = preg_replace("/(.*)stroke-linejoin:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1259			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1260				$current_style['stroke-linejoin'] = $tmp;
1261			}
1262
1263			$tmp = preg_replace("/(.*)stroke-miterlimit:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1264			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1265				$current_style['stroke-miterlimit'] = $tmp;
1266			}
1267
1268			$tmp = preg_replace("/(.*)stroke-opacity:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
1269			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1270				$current_style['stroke-opacity'] = $tmp;
1271			}
1272
1273			$tmp = preg_replace("/(.*)stroke-width:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
1274			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1275				$current_style['stroke-width'] = $tmp;
1276			}
1277
1278			$tmp = preg_replace("/(.*)stroke-dasharray:\s*([a-z0-9., ]*|none)(.*)/i", "$2", $critere_style['style']);
1279			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1280				$current_style['stroke-dasharray'] = $tmp;
1281			}
1282
1283			$tmp = preg_replace("/(.*)stroke-dashoffset:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
1284			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1285				$current_style['stroke-dashoffset'] = $tmp;
1286			}
1287		}
1288
1289		// mPDF 5.7.2
1290		if (isset($critere_style['opacity']) && $critere_style['opacity'] != 'inherit') {
1291			$current_style['fill-opacity'] = $critere_style['opacity'];
1292			$current_style['stroke-opacity'] = $critere_style['opacity'];
1293		}
1294
1295		if (isset($critere_style['fill']) && $critere_style['fill'] != 'inherit') {
1296			$current_style['fill'] = $critere_style['fill'];
1297		}
1298
1299		if (isset($critere_style['fill-opacity']) && $critere_style['fill-opacity'] != 'inherit') {
1300			$current_style['fill-opacity'] = $critere_style['fill-opacity'];
1301		}
1302
1303		if (isset($critere_style['fill-rule']) && $critere_style['fill-rule'] != 'inherit') {
1304			$current_style['fill-rule'] = $critere_style['fill-rule'];
1305		}
1306
1307		if (isset($critere_style['stroke']) && $critere_style['stroke'] != 'inherit') {
1308			$current_style['stroke'] = $critere_style['stroke'];
1309		}
1310
1311		if (isset($critere_style['stroke-linecap']) && $critere_style['stroke-linecap'] != 'inherit') {
1312			$current_style['stroke-linecap'] = $critere_style['stroke-linecap'];
1313		}
1314
1315		if (isset($critere_style['stroke-linejoin']) && $critere_style['stroke-linejoin'] != 'inherit') {
1316			$current_style['stroke-linejoin'] = $critere_style['stroke-linejoin'];
1317		}
1318
1319		if (isset($critere_style['stroke-miterlimit']) && $critere_style['stroke-miterlimit'] != 'inherit') {
1320			$current_style['stroke-miterlimit'] = $critere_style['stroke-miterlimit'];
1321		}
1322
1323		if (isset($critere_style['stroke-opacity']) && $critere_style['stroke-opacity'] != 'inherit') {
1324			$current_style['stroke-opacity'] = $critere_style['stroke-opacity'];
1325		}
1326
1327		if (isset($critere_style['stroke-width']) && $critere_style['stroke-width'] != 'inherit') {
1328			$current_style['stroke-width'] = $critere_style['stroke-width'];
1329		}
1330
1331		if (isset($critere_style['stroke-dasharray']) && $critere_style['stroke-dasharray'] != 'inherit') {
1332			$current_style['stroke-dasharray'] = $critere_style['stroke-dasharray'];
1333		}
1334		if (isset($critere_style['stroke-dashoffset']) && $critere_style['stroke-dashoffset'] != 'inherit') {
1335			$current_style['stroke-dashoffset'] = $critere_style['stroke-dashoffset'];
1336		}
1337
1338		// Used as indirect setting for currentColor
1339		if (isset($critere_style['color']) && $critere_style['color'] != 'inherit') {
1340			$current_style['color'] = $critere_style['color'];
1341		}
1342
1343		return $current_style;
1344	}
1345
1346	//
1347	//	Cette fonction ecrit le style dans le stream svg.
1348	function svgStyle($critere_style, $attribs, $element)
1349	{
1350		$path_style = '';
1351		$fill_gradient = '';
1352		$w = '';
1353		$style = '';
1354		if (substr_count($critere_style['fill'], 'url') > 0 && $element != 'line') {
1355			//
1356			// couleur degradé
1357			$id_gradient = preg_replace("/url\(#([\w_]*)\)/i", "$1", $critere_style['fill']);
1358			if ($id_gradient != $critere_style['fill']) {
1359				if (isset($this->svg_gradient[$id_gradient])) {
1360					$fill_gradient = $this->svgGradient($this->svg_gradient[$id_gradient], $attribs, $element);
1361					if ($fill_gradient) {
1362						$path_style = "q ";
1363						$w = "W";
1364						$style .= 'N';
1365					}
1366				}
1367			}
1368		} // Used as indirect setting for currentColor
1369		elseif (strtolower($critere_style['fill']) == 'currentcolor' && $element != 'line') {
1370			$col = $this->colorConverter->convert($critere_style['color'], $this->mpdf->PDFAXwarnings);
1371			if ($col) {
1372				if ($col[0] == 5 && is_numeric($col[4])) {
1373					$critere_style['fill-opacity'] = ord($col[4] / 100);
1374				} // RGBa
1375				if ($col[0] == 6 && is_numeric($col[5])) {
1376					$critere_style['fill-opacity'] = ord($col[5] / 100);
1377				} // CMYKa
1378				$path_style .= $this->mpdf->SetFColor($col, true) . ' ';
1379				$style .= 'F';
1380			}
1381		} elseif ($critere_style['fill'] != 'none' && $element != 'line') {
1382			$col = $this->colorConverter->convert($critere_style['fill'], $this->mpdf->PDFAXwarnings);
1383			if ($col) {
1384				if ($col[0] == 5 && is_numeric($col[4])) {
1385					$critere_style['fill-opacity'] = ord($col[4] / 100);
1386				} // RGBa
1387				if ($col[0] == 6 && is_numeric($col[5])) {
1388					$critere_style['fill-opacity'] = ord($col[5] / 100);
1389				} // CMYKa
1390				$path_style .= $this->mpdf->SetFColor($col, true) . ' ';
1391				$style .= 'F';
1392			}
1393		}
1394		if (substr_count($critere_style['stroke'], 'url') > 0) {
1395			/*
1396			  // Cannot put a gradient on a "stroke" in PDF?
1397			  $id_gradient = preg_replace("/url\(#([\w_]*)\)/i","$1",$critere_style['stroke']);
1398			  if ($id_gradient != $critere_style['stroke']) {
1399			  if (isset($this->svg_gradient[$id_gradient])) {
1400			  $fill_gradient = $this->svgGradient($this->svg_gradient[$id_gradient], $attribs, $element);
1401			  if ($fill_gradient) {
1402			  $path_style = "q ";
1403			  $w = "W";
1404			  $style .= 'D';
1405			  }
1406			  }
1407			  }
1408			 */
1409		} // Used as indirect setting for currentColor
1410		elseif (strtolower($critere_style['stroke']) == 'currentcolor') {
1411			$col = $this->colorConverter->convert($critere_style['color'], $this->mpdf->PDFAXwarnings);
1412			if ($col) {
1413				if ($col[0] == 5 && is_numeric($col[4])) {
1414					$critere_style['stroke-opacity'] = ord($col[4] / 100);
1415				} // RGBa
1416				if ($col[0] == 6 && is_numeric($col[5])) {
1417					$critere_style['stroke-opacity'] = ord($col[5] / 100);
1418				} // CMYKa
1419				$path_style .= $this->mpdf->SetDColor($col, true) . ' ';
1420				$style .= 'D';
1421				$lw = $this->ConvertSVGSizePixels($critere_style['stroke-width']);
1422				$path_style .= sprintf('%.3F w ', $lw * $this->kp);
1423			}
1424		} elseif ($critere_style['stroke'] != 'none') {
1425			$col = $this->colorConverter->convert($critere_style['stroke'], $this->mpdf->PDFAXwarnings);
1426			if ($col) {
1427				// mPDF 5.0.051
1428				// mPDF 5.3.74
1429				if ($col[0] == 5 && is_numeric($col[4])) {
1430					$critere_style['stroke-opacity'] = ord($col[4] / 100);
1431				} // RGBa
1432				if ($col[0] == 6 && is_numeric($col[5])) {
1433					$critere_style['stroke-opacity'] = ord($col[5] / 100);
1434				} // CMYKa
1435				$path_style .= $this->mpdf->SetDColor($col, true) . ' ';
1436				$style .= 'D';
1437				$lw = $this->ConvertSVGSizePixels($critere_style['stroke-width']);
1438				$path_style .= sprintf('%.3F w ', $lw * $this->kp);
1439			}
1440		}
1441
1442
1443		if ($critere_style['stroke'] != 'none') {
1444			if ($critere_style['stroke-linejoin'] == 'miter') {
1445				$path_style .= ' 0 j ';
1446			} elseif ($critere_style['stroke-linejoin'] == 'round') {
1447				$path_style .= ' 1 j ';
1448			} elseif ($critere_style['stroke-linejoin'] == 'bevel') {
1449				$path_style .= ' 2 j ';
1450			}
1451
1452			if ($critere_style['stroke-linecap'] == 'butt') {
1453				$path_style .= ' 0 J ';
1454			} elseif ($critere_style['stroke-linecap'] == 'round') {
1455				$path_style .= ' 1 J ';
1456			} elseif ($critere_style['stroke-linecap'] == 'square') {
1457				$path_style .= ' 2 J ';
1458			}
1459
1460			if (isset($critere_style['stroke-miterlimit'])) {
1461				if ($critere_style['stroke-miterlimit'] == 'none') {
1462				} elseif (preg_match('/^[\d.]+$/', $critere_style['stroke-miterlimit'])) {
1463					$path_style .= sprintf('%.2F M ', $critere_style['stroke-miterlimit']);
1464				}
1465			}
1466			if (isset($critere_style['stroke-dasharray'])) {
1467				$off = 0;
1468				$d = preg_split('/(,\s?|\s)/', $critere_style['stroke-dasharray']);
1469				if (count($d) == 1 && $d[0] == 0) {
1470					$path_style .= '[] 0 d ';
1471				} else {
1472					if (count($d) % 2 == 1) {
1473						$d = array_merge($d, $d);
1474					} // 5, 3, 1 => 5,3,1,5,3,1  OR 3 => 3,3
1475					$arr = '';
1476					for ($i = 0; $i < count($d); $i+=2) {
1477						$arr .= sprintf('%.3F %.3F ', $d[$i] * $this->kp, $d[$i + 1] * $this->kp);
1478					}
1479					if (isset($critere_style['stroke-dashoffset'])) {
1480						$off = $critere_style['stroke-dashoffset'] + 0;
1481					}
1482					$path_style .= sprintf('[%s] %.3F d ', $arr, $off * $this->kp);
1483				}
1484			}
1485		}
1486
1487		if ($critere_style['fill-rule'] == 'evenodd') {
1488			$fr = '*';
1489		} else {
1490			$fr = '';
1491		}
1492
1493		if (isset($critere_style['fill-opacity'])) {
1494			$opacity = 1;
1495			if ($critere_style['fill-opacity'] == 0) {
1496				$opacity = 0;
1497			} elseif ($critere_style['fill-opacity'] > 1) {
1498				$opacity = 1;
1499			} elseif ($critere_style['fill-opacity'] > 0) {
1500				$opacity = $critere_style['fill-opacity'];
1501			} elseif ($critere_style['fill-opacity'] < 0) {
1502				$opacity = 0;
1503			}
1504			$gs = $this->mpdf->AddExtGState(['ca' => $opacity, 'BM' => '/Normal']);
1505			$this->mpdf->extgstates[$gs]['fo'] = true;
1506			$path_style .= sprintf(' /GS%d gs ', $gs);
1507		}
1508
1509		if (isset($critere_style['stroke-opacity'])) {
1510			$opacity = 1;
1511			if ($critere_style['stroke-opacity'] == 0) {
1512				$opacity = 0;
1513			} elseif ($critere_style['stroke-opacity'] > 1) {
1514				$opacity = 1;
1515			} elseif ($critere_style['stroke-opacity'] > 0) {
1516				$opacity = $critere_style['stroke-opacity'];
1517			} elseif ($critere_style['stroke-opacity'] < 0) {
1518				$opacity = 0;
1519			}
1520			$gs = $this->mpdf->AddExtGState(['CA' => $opacity, 'BM' => '/Normal']);
1521			$this->mpdf->extgstates[$gs]['fo'] = true;
1522			$path_style .= sprintf(' /GS%d gs ', $gs);
1523		}
1524
1525		switch ($style) {
1526			case 'F':
1527				$op = 'f';
1528				break;
1529			case 'FD':
1530				$op = 'B';
1531				break;
1532			case 'ND':
1533				$op = 'S';
1534				break;
1535			case 'D':
1536				$op = 'S';
1537				break;
1538			default:
1539				$op = 'n';
1540		}
1541
1542		$prestyle = $path_style . ' ';
1543		$poststyle = $w . ' ' . $op . $fr . ' ' . $fill_gradient . "\n";
1544		return [$prestyle, $poststyle];
1545	}
1546
1547	//	fonction retracant les <path />
1548	function svgPath($command, $arguments)
1549	{
1550		$path_cmd = '';
1551		$newsubpath = false;
1552		// mPDF 5.0.039
1553		$minl = $this->pathBBox[0];
1554		$mint = $this->pathBBox[1];
1555		$maxr = $this->pathBBox[2] + $this->pathBBox[0];
1556		$maxb = $this->pathBBox[3] + $this->pathBBox[1];
1557
1558		$start = [$this->xbase, -$this->ybase];
1559
1560		// taken from https://github.com/PhenX/php-svg-lib/blob/master/src/Svg/Tag/Path.php#L47
1561		// Handle args like: a5.38022,5.38022,0,0,1-2.4207.72246,4.50524,4.50524,0,0,1-3.12681-1.33942,9.67442,9.67442,0,0,1-2.38273-3.016,1.87506,1.87506,0,0,1,.34979-2.43562
1562		preg_match_all('/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/i', $arguments, $a, PREG_PATTERN_ORDER);
1563		$a = $a[0];
1564
1565		//	if the command is a capital letter, the coords go absolute, otherwise relative
1566		if (strtolower($command) == $command) {
1567			$relative = true;
1568		} else {
1569			$relative = false;
1570		}
1571
1572
1573		$ile_argumentow = count($a);
1574
1575		//	each command may have different needs for arguments [1 to 8]
1576
1577		switch (strtolower($command)) {
1578			case 'm': // move
1579				for ($i = 0; $i < $ile_argumentow; $i+=2) {
1580					$x = $a[$i];
1581					$y = $a[$i + 1];
1582					if ($relative) {
1583						$pdfx = ($this->xbase + $x);
1584						$pdfy = ($this->ybase - $y);
1585						$this->xbase += $x;
1586						$this->ybase += -$y;
1587					} else {
1588						$pdfx = $x;
1589						$pdfy = -$y;
1590						$this->xbase = $x;
1591						$this->ybase = -$y;
1592					}
1593					$pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1594					$minl = min($minl, $pdf_pt['x']);
1595					$maxr = max($maxr, $pdf_pt['x']);
1596					$mint = min($mint, -$pdf_pt['y']);
1597					$maxb = max($maxb, -$pdf_pt['y']);
1598					if ($i == 0) {
1599						$path_cmd .= sprintf('%.3F %.3F m ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1600					} else {
1601						$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1602					}
1603					// mPDF 4.4.003  Save start points of subpath
1604					if ($this->subPathInit) {
1605						$this->spxstart = $this->xbase;
1606						$this->spystart = $this->ybase;
1607						$this->subPathInit = false;
1608					}
1609				}
1610				break;
1611			case 'l': // a simple line
1612				for ($i = 0; $i < $ile_argumentow; $i+=2) {
1613					$x = ($a[$i]);
1614					$y = ($a[$i + 1]);
1615					if ($relative) {
1616						$pdfx = ($this->xbase + $x);
1617						$pdfy = ($this->ybase - $y);
1618						$this->xbase += $x;
1619						$this->ybase += -$y;
1620					} else {
1621						$pdfx = $x;
1622						$pdfy = -$y;
1623						$this->xbase = $x;
1624						$this->ybase = -$y;
1625					}
1626					$pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1627					$minl = min($minl, $pdf_pt['x']);
1628					$maxr = max($maxr, $pdf_pt['x']);
1629					$mint = min($mint, -$pdf_pt['y']);
1630					$maxb = max($maxb, -$pdf_pt['y']);
1631					$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1632				}
1633				break;
1634			case 'h': // a very simple horizontal line
1635				for ($i = 0; $i < $ile_argumentow; $i++) {
1636					$x = ($a[$i]);
1637					if ($relative) {
1638						$y = 0;
1639						$pdfx = ($this->xbase + $x);
1640						$pdfy = ($this->ybase - $y);
1641						$this->xbase += $x;
1642						$this->ybase += -$y;
1643					} else {
1644						$y = -$this->ybase;
1645						$pdfx = $x;
1646						$pdfy = -$y;
1647						$this->xbase = $x;
1648						$this->ybase = -$y;
1649					}
1650					$pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1651					$minl = min($minl, $pdf_pt['x']);
1652					$maxr = max($maxr, $pdf_pt['x']);
1653					$mint = min($mint, -$pdf_pt['y']);
1654					$maxb = max($maxb, -$pdf_pt['y']);
1655					$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1656				}
1657				break;
1658			case 'v': // the simplest line, vertical
1659				for ($i = 0; $i < $ile_argumentow; $i++) {
1660					$y = ($a[$i]);
1661					if ($relative) {
1662						$x = 0;
1663						$pdfx = ($this->xbase + $x);
1664						$pdfy = ($this->ybase - $y);
1665						$this->xbase += $x;
1666						$this->ybase += -$y;
1667					} else {
1668						$x = $this->xbase;
1669						$pdfx = $x;
1670						$pdfy = -$y;
1671						$this->xbase = $x;
1672						$this->ybase = -$y;
1673					}
1674					$pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1675					$minl = min($minl, $pdf_pt['x']);
1676					$maxr = max($maxr, $pdf_pt['x']);
1677					$mint = min($mint, -$pdf_pt['y']);
1678					$maxb = max($maxb, -$pdf_pt['y']);
1679					$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1680				}
1681				break;
1682			case 's': // bezier with first vertex equal first control
1683				// mPDF 4.4.003
1684				if (!($this->lastcommand == 'C' || $this->lastcommand == 'c' || $this->lastcommand == 'S' || $this->lastcommand == 's')) {
1685					$this->lastcontrolpoints = [0, 0];
1686				}
1687				for ($i = 0; $i < $ile_argumentow; $i += 4) {
1688					$x1 = $this->lastcontrolpoints[0];
1689					$y1 = $this->lastcontrolpoints[1];
1690					$x2 = ($a[$i]);
1691					$y2 = ($a[$i + 1]);
1692					$x = ($a[$i + 2]);
1693					$y = ($a[$i + 3]);
1694					if ($relative) {
1695						$pdfx1 = ($this->xbase + $x1);
1696						$pdfy1 = ($this->ybase - $y1);
1697						$pdfx2 = ($this->xbase + $x2);
1698						$pdfy2 = ($this->ybase - $y2);
1699						$pdfx = ($this->xbase + $x);
1700						$pdfy = ($this->ybase - $y);
1701						$this->xbase += $x;
1702						$this->ybase += -$y;
1703					} else {
1704						$pdfx1 = $this->xbase + $x1;
1705						$pdfy1 = $this->ybase - $y1;
1706						$pdfx2 = $x2;
1707						$pdfy2 = -$y2;
1708						$pdfx = $x;
1709						$pdfy = -$y;
1710						$this->xbase = $x;
1711						$this->ybase = -$y;
1712					}
1713					$this->lastcontrolpoints = [($pdfx - $pdfx2), -($pdfy - $pdfy2)]; // mPDF 4.4.003 always relative
1714
1715					$pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1716
1717					$curves = [$pdfx1, -$pdfy1, $pdfx2, -$pdfy2, $pdfx, -$pdfy];
1718					$bx = $this->computeBezierBoundingBox($start, $curves);
1719					$minl = min($minl, $bx[0]);
1720					$maxr = max($maxr, $bx[2]);
1721					$mint = min($mint, $bx[1]);
1722					$maxb = max($maxb, $bx[3]);
1723
1724					if (($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy)) {
1725						$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1726					} else {
1727						$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1 * $this->kp, $pdfy1 * $this->kp, $pdfx2 * $this->kp, $pdfy2 * $this->kp, $pdfx * $this->kp, $pdfy * $this->kp);
1728					}
1729				}
1730				break;
1731			case 'c': // bezier with second vertex equal second control
1732				for ($i = 0; $i < $ile_argumentow; $i += 6) {
1733					$x1 = ($a[$i]);
1734					$y1 = ($a[$i + 1]);
1735					$x2 = ($a[$i + 2]);
1736					$y2 = ($a[$i + 3]);
1737					$x = ($a[$i + 4]);
1738					$y = ($a[$i + 5]);
1739
1740
1741					if ($relative) {
1742						$pdfx1 = ($this->xbase + $x1);
1743						$pdfy1 = ($this->ybase - $y1);
1744						$pdfx2 = ($this->xbase + $x2);
1745						$pdfy2 = ($this->ybase - $y2);
1746						$pdfx = ($this->xbase + $x);
1747						$pdfy = ($this->ybase - $y);
1748						$this->xbase += $x;
1749						$this->ybase += -$y;
1750					} else {
1751						$pdfx1 = $x1;
1752						$pdfy1 = -$y1;
1753						$pdfx2 = $x2;
1754						$pdfy2 = -$y2;
1755						$pdfx = $x;
1756						$pdfy = -$y;
1757						$this->xbase = $x;
1758						$this->ybase = -$y;
1759					}
1760					$this->lastcontrolpoints = [($pdfx - $pdfx2), -($pdfy - $pdfy2)]; // mPDF 4.4.003 always relative
1761					// $pdf_pt2 = $this->svg_overflow($pdfx2,$pdfy2);
1762					// $pdf_pt1 = $this->svg_overflow($pdfx1,$pdfy1);
1763					$pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1764
1765					$curves = [$pdfx1, -$pdfy1, $pdfx2, -$pdfy2, $pdfx, -$pdfy];
1766					$bx = $this->computeBezierBoundingBox($start, $curves);
1767					$minl = min($minl, $bx[0]);
1768					$maxr = max($maxr, $bx[2]);
1769					$mint = min($mint, $bx[1]);
1770					$maxb = max($maxb, $bx[3]);
1771
1772					if (($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy)) {
1773						$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1774					} else {
1775						$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1 * $this->kp, $pdfy1 * $this->kp, $pdfx2 * $this->kp, $pdfy2 * $this->kp, $pdfx * $this->kp, $pdfy * $this->kp);
1776					}
1777				}
1778				break;
1779
1780			case 'q': // bezier quadratic avec point de control
1781				for ($i = 0; $i < $ile_argumentow; $i += 4) {
1782					$x1 = ($a[$i]);
1783					$y1 = ($a[$i + 1]);
1784					$x = ($a[$i + 2]);
1785					$y = ($a[$i + 3]);
1786					if ($relative) {
1787						$pdfx = ($this->xbase + $x);
1788						$pdfy = ($this->ybase - $y);
1789
1790						$pdfx1 = ($this->xbase + ($x1 * 2 / 3));
1791						$pdfy1 = ($this->ybase - ($y1 * 2 / 3));
1792						// mPDF 4.4.003
1793						$pdfx2 = $pdfx1 + 1 / 3 * ($x);
1794						$pdfy2 = $pdfy1 + 1 / 3 * (-$y);
1795
1796						$this->xbase += $x;
1797						$this->ybase += -$y;
1798					} else {
1799						$pdfx = $x;
1800						$pdfy = -$y;
1801
1802						$pdfx1 = ($this->xbase + (($x1 - $this->xbase) * 2 / 3));
1803						$pdfy1 = ($this->ybase - (($y1 + $this->ybase) * 2 / 3));
1804
1805						$pdfx2 = ($x + (($x1 - $x) * 2 / 3));
1806						$pdfy2 = (-$y - (($y1 - $y) * 2 / 3));
1807
1808						// mPDF 4.4.003
1809						$pdfx2 = $pdfx1 + 1 / 3 * ($x - $this->xbase);
1810						$pdfy2 = $pdfy1 + 1 / 3 * (-$y - $this->ybase);
1811
1812						$this->xbase = $x;
1813						$this->ybase = -$y;
1814					}
1815					$this->lastcontrolpoints = [($pdfx - $pdfx2), -($pdfy - $pdfy2)]; // mPDF 4.4.003 always relative
1816
1817					$pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1818
1819					$curves = [$pdfx1, -$pdfy1, $pdfx2, -$pdfy2, $pdfx, -$pdfy];
1820					$bx = $this->computeBezierBoundingBox($start, $curves);
1821					$minl = min($minl, $bx[0]);
1822					$maxr = max($maxr, $bx[2]);
1823					$mint = min($mint, $bx[1]);
1824					$maxb = max($maxb, $bx[3]);
1825
1826					if (($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy)) {
1827						$path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1828					} else {
1829						$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1 * $this->kp, $pdfy1 * $this->kp, $pdfx2 * $this->kp, $pdfy2 * $this->kp, $pdfx * $this->kp, $pdfy * $this->kp);
1830					}
1831				}
1832				break;
1833			case 't': // bezier quadratic avec point de control simetrique a lancien point de control
1834				if (!($this->lastcommand == 'Q' || $this->lastcommand == 'q' || $this->lastcommand == 'T' || $this->lastcommand == 't')) {
1835					$this->lastcontrolpoints = [0, 0];
1836				}
1837				for ($i = 0; $i < $ile_argumentow; $i += 2) {
1838					$x = ($a[$i]);
1839					$y = ($a[$i + 1]);
1840
1841					$x1 = $this->lastcontrolpoints[0];
1842					$y1 = $this->lastcontrolpoints[1];
1843
1844					if ($relative) {
1845						$pdfx = ($this->xbase + $x);
1846						$pdfy = ($this->ybase - $y);
1847
1848						$pdfx1 = ($this->xbase + ($x1));
1849						$pdfy1 = ($this->ybase - ($y1));
1850						// mPDF 4.4.003
1851						$pdfx2 = $pdfx1 + 1 / 3 * ($x);
1852						$pdfy2 = $pdfy1 + 1 / 3 * (-$y);
1853
1854						$this->xbase += $x;
1855						$this->ybase += -$y;
1856					} else {
1857						$pdfx = $x;
1858						$pdfy = -$y;
1859
1860						$pdfx1 = ($this->xbase + ($x1));
1861						$pdfy1 = ($this->ybase - ($y1));
1862						// mPDF 4.4.003
1863						$pdfx2 = $pdfx1 + 1 / 3 * ($x - $this->xbase);
1864						$pdfy2 = $pdfy1 + 1 / 3 * (-$y - $this->ybase);
1865
1866						$this->xbase = $x;
1867						$this->ybase = -$y;
1868					}
1869
1870					$this->lastcontrolpoints = [($pdfx - $pdfx2), -($pdfy - $pdfy2)]; // mPDF 4.4.003 always relative
1871
1872					$curves = [$pdfx1, -$pdfy1, $pdfx2, -$pdfy2, $pdfx, -$pdfy];
1873					$bx = $this->computeBezierBoundingBox($start, $curves);
1874					$minl = min($minl, $bx[0]);
1875					$maxr = max($maxr, $bx[2]);
1876					$mint = min($mint, $bx[1]);
1877					$maxb = max($maxb, $bx[3]);
1878
1879					$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $pdfx1 * $this->kp, $pdfy1 * $this->kp, $pdfx2 * $this->kp, $pdfy2 * $this->kp, $pdfx * $this->kp, $pdfy * $this->kp);
1880				}
1881
1882				break;
1883			case 'a': // Elliptical arc
1884				for ($i = 0; $i < $ile_argumentow; $i += 7) {
1885					$rx = ($a[$i]);
1886					$ry = ($a[$i + 1]);
1887					$angle = ($a[$i + 2]); //x-axis-rotation
1888					$largeArcFlag = ($a[$i + 3]);
1889					$sweepFlag = ($a[$i + 4]);
1890					$x2 = ($a[$i + 5]);
1891					$y2 = ($a[$i + 6]);
1892					$x1 = $this->xbase;
1893					$y1 = -$this->ybase;
1894					if ($relative) {
1895						$x2 = $this->xbase + $x2;
1896						$y2 = -$this->ybase + $y2;
1897						$this->xbase += ($a[$i + 5]);
1898						$this->ybase += -($a[$i + 6]);
1899					} else {
1900						$this->xbase = $x2;
1901						$this->ybase = -$y2;
1902					}
1903					list($pcmd, $bounds) = $this->Arcto($x1, $y1, $x2, $y2, $rx, $ry, $angle, $largeArcFlag, $sweepFlag);
1904					$minl = min($minl, $x2, min($bounds[0]));
1905					$maxr = max($maxr, $x2, max($bounds[0]));
1906					$mint = min($mint, $y2, min($bounds[1]));
1907					$maxb = max($maxb, $y2, max($bounds[1]));
1908					$path_cmd .= $pcmd;
1909				}
1910				break;
1911			case 'z':
1912				$path_cmd .= 'h ';
1913				$this->subPathInit = true;
1914				$newsubpath = true;
1915				$this->xbase = $this->spxstart;
1916				$this->ybase = $this->spystart;
1917				break;
1918			default:
1919				break;
1920		}
1921
1922		if (!$newsubpath) {
1923			$this->subPathInit = false;
1924		}
1925		$this->lastcommand = $command;
1926		// mPDF 5.0.039
1927		$this->pathBBox[0] = $minl;
1928		$this->pathBBox[1] = $mint;
1929		$this->pathBBox[2] = $maxr - $this->pathBBox[0];
1930		$this->pathBBox[3] = $maxb - $this->pathBBox[1];
1931		return $path_cmd;
1932	}
1933
1934	function Arcto($x1, $y1, $x2, $y2, $rx, $ry, $angle, $largeArcFlag, $sweepFlag)
1935	{
1936
1937		$bounds = [0 => [$x1, $x2], 1 => [$y1, $y2]];
1938		// 1. Treat out-of-range parameters as described in
1939		// http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
1940		// If the endpoints (x1, y1) and (x2, y2) are identical, then this
1941		// is equivalent to omitting the elliptical arc segment entirely
1942		if ($x1 == $x2 && $y1 == $y2) {
1943			return ['', $bounds]; // mPD 5.0.040
1944		}
1945
1946
1947// If rX = 0 or rY = 0 then this arc is treated as a straight line
1948		// segment (a "lineto") joining the endpoints.
1949		if ($rx == 0.0 || $ry == 0.0) {
1950			//   return array(Lineto(x2, y2), $bounds);
1951		}
1952
1953		// If rX or rY have negative signs, these are dropped; the absolute
1954		// value is used instead.
1955		if ($rx < 0.0) {
1956			$rx = -$rx;
1957		}
1958		if ($ry < 0.0) {
1959			$ry = -$ry;
1960		}
1961
1962		// 2. convert to center parameterization as shown in
1963		// http://www.w3.org/TR/SVG/implnote.html
1964		$sinPhi = sin(deg2rad($angle));
1965		$cosPhi = cos(deg2rad($angle));
1966
1967		$x1dash = $cosPhi * ($x1 - $x2) / 2.0 + $sinPhi * ($y1 - $y2) / 2.0;
1968		$y1dash = -$sinPhi * ($x1 - $x2) / 2.0 + $cosPhi * ($y1 - $y2) / 2.0;
1969
1970
1971		$numerator = $rx * $rx * $ry * $ry - $rx * $rx * $y1dash * $y1dash - $ry * $ry * $x1dash * $x1dash;
1972
1973		if ($numerator < 0.0) {
1974			//  If rX , rY and are such that there is no solution (basically,
1975			//  the ellipse is not big enough to reach from (x1, y1) to (x2,
1976			//  y2)) then the ellipse is scaled up uniformly until there is
1977			//  exactly one solution (until the ellipse is just big enough).
1978			// -> find factor s, such that numerator' with rx'=s*rx and
1979			//    ry'=s*ry becomes 0 :
1980			$s = sqrt(1.0 - $numerator / ($rx * $rx * $ry * $ry));
1981
1982			$rx *= $s;
1983			$ry *= $s;
1984			$root = 0.0;
1985		} else {
1986			$root = ($largeArcFlag == $sweepFlag ? -1.0 : 1.0) * sqrt($numerator / ($rx * $rx * $y1dash * $y1dash + $ry * $ry * $x1dash * $x1dash));
1987		}
1988
1989		$cxdash = $root * $rx * $y1dash / $ry;
1990		$cydash = -$root * $ry * $x1dash / $rx;
1991
1992		$cx = $cosPhi * $cxdash - $sinPhi * $cydash + ($x1 + $x2) / 2.0;
1993		$cy = $sinPhi * $cxdash + $cosPhi * $cydash + ($y1 + $y2) / 2.0;
1994
1995
1996		$theta1 = $this->CalcVectorAngle(1.0, 0.0, ($x1dash - $cxdash) / $rx, ($y1dash - $cydash) / $ry);
1997		$dtheta = $this->CalcVectorAngle(($x1dash - $cxdash) / $rx, ($y1dash - $cydash) / $ry, (-$x1dash - $cxdash) / $rx, (-$y1dash - $cydash) / $ry);
1998		if (!$sweepFlag && $dtheta > 0) {
1999			$dtheta -= 2.0 * M_PI;
2000		} elseif ($sweepFlag && $dtheta < 0) {
2001			$dtheta += 2.0 * M_PI;
2002		}
2003
2004		// 3. convert into cubic bezier segments <= 90deg
2005		$segments = ceil(abs($dtheta / (M_PI / 2.0)));
2006		$delta = $dtheta / $segments;
2007		$t = 8.0 / 3.0 * sin($delta / 4.0) * sin($delta / 4.0) / sin($delta / 2.0);
2008		$coords = [];
2009		for ($i = 0; $i < $segments; $i++) {
2010			$cosTheta1 = cos($theta1);
2011			$sinTheta1 = sin($theta1);
2012			$theta2 = $theta1 + $delta;
2013			$cosTheta2 = cos($theta2);
2014			$sinTheta2 = sin($theta2);
2015
2016			// a) calculate endpoint of the segment:
2017			$xe = $cosPhi * $rx * $cosTheta2 - $sinPhi * $ry * $sinTheta2 + $cx;
2018			$ye = $sinPhi * $rx * $cosTheta2 + $cosPhi * $ry * $sinTheta2 + $cy;
2019
2020			// b) calculate gradients at start/end points of segment:
2021			$dx1 = $t * ( - $cosPhi * $rx * $sinTheta1 - $sinPhi * $ry * $cosTheta1);
2022			$dy1 = $t * ( - $sinPhi * $rx * $sinTheta1 + $cosPhi * $ry * $cosTheta1);
2023
2024			$dxe = $t * ( $cosPhi * $rx * $sinTheta2 + $sinPhi * $ry * $cosTheta2);
2025			$dye = $t * ( $sinPhi * $rx * $sinTheta2 - $cosPhi * $ry * $cosTheta2);
2026
2027			// c) draw the cubic bezier:
2028			$coords[$i] = [($x1 + $dx1), ($y1 + $dy1), ($xe + $dxe), ($ye + $dye), $xe, $ye];
2029
2030			// do next segment
2031			$theta1 = $theta2;
2032			$x1 = $xe;
2033			$y1 = $ye;
2034		}
2035		$path = ' ';
2036		foreach ($coords as $c) {
2037			$cpx1 = $c[0];
2038			$cpy1 = $c[1];
2039			$cpx2 = $c[2];
2040			$cpy2 = $c[3];
2041			$x2 = $c[4];
2042			$y2 = $c[5];
2043			$path .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $cpx1 * $this->kp, -$cpy1 * $this->kp, $cpx2 * $this->kp, -$cpy2 * $this->kp, $x2 * $this->kp, -$y2 * $this->kp) . "\n";
2044
2045			// mPDF 5.0.040
2046			$bounds[0][] = $c[4];
2047			$bounds[1][] = $c[5];
2048		}
2049		return [$path, $bounds]; // mPD 5.0.040
2050	}
2051
2052	function CalcVectorAngle($ux, $uy, $vx, $vy)
2053	{
2054		$ta = atan2($uy, $ux);
2055		$tb = atan2($vy, $vx);
2056		if ($tb >= $ta) {
2057			return ($tb - $ta);
2058		}
2059		return (6.28318530718 - ($ta - $tb));
2060	}
2061
2062	function ConvertSVGSizePixels($size = 5, $maxsize = 'x')
2063	{
2064		// maxsize in pixels (user units) or 'y' or 'x'
2065		// e.g. $w = $this->ConvertSVGSizePixels($arguments['w'],$this->svg_info['w']*(25.4/$this->mpdf->dpi));
2066		// usefontsize - setfalse for e.g. margins - will ignore fontsize for % values
2067		// Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
2068		// For text $maxsize = Fontsize
2069		// Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
2070
2071		if ($maxsize == 'y') {
2072			$maxsize = $this->svg_info['h'];
2073		} elseif ($maxsize == 'x') {
2074			$maxsize = $this->svg_info['w'];
2075		}
2076		$maxsize *= (25.4 / $this->mpdf->dpi); // convert pixels to mm
2077		$fontsize = $this->mpdf->FontSize / $this->kf;
2078		//Return as pixels
2079		$size = $this->sizeConverter->convert($size, $maxsize, $fontsize, false) * 1 / (25.4 / $this->mpdf->dpi);
2080		return $size;
2081	}
2082
2083	function ConvertSVGSizePts($size = 5)
2084	{
2085		// usefontsize - setfalse for e.g. margins - will ignore fontsize for % values
2086		// Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
2087		// For text $maxsize = Fontsize
2088		// Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
2089		$maxsize = $this->mpdf->FontSize;
2090		//Return as pts
2091		$size = $this->sizeConverter->convert($size, $maxsize, false, true) * 72 / 25.4;
2092		return $size;
2093	}
2094
2095	//
2096	//	fonction retracant les <rect />
2097	function svgRect($arguments)
2098	{
2099
2100		if ($arguments['h'] == 0 || $arguments['w'] == 0) {
2101			return '';
2102		}
2103
2104		$x = $this->ConvertSVGSizePixels($arguments['x'], 'x'); // mPDF 4.4.003
2105		$y = $this->ConvertSVGSizePixels($arguments['y'], 'y'); // mPDF 4.4.003
2106		$h = $this->ConvertSVGSizePixels($arguments['h'], 'y'); // mPDF 4.4.003
2107		$w = $this->ConvertSVGSizePixels($arguments['w'], 'x'); // mPDF 4.4.003
2108		$rx = $this->ConvertSVGSizePixels($arguments['rx'], 'x'); // mPDF 4.4.003
2109		$ry = $this->ConvertSVGSizePixels($arguments['ry'], 'y'); // mPDF 4.4.003
2110
2111		if ($rx > $w / 2) {
2112			$rx = $w / 2;
2113		} // mPDF 4.4.003
2114		if ($ry > $h / 2) {
2115			$ry = $h / 2;
2116		} // mPDF 4.4.003
2117
2118		if ($rx > 0 and $ry == 0) {
2119			$ry = $rx;
2120		}
2121		if ($ry > 0 and $rx == 0) {
2122			$rx = $ry;
2123		}
2124
2125		if ($rx == 0 and $ry == 0) {
2126			//	trace un rectangle sans angle arrondit
2127			$path_cmd = sprintf('%.3F %.3F m ', ($x * $this->kp), -($y * $this->kp));
2128			$path_cmd .= sprintf('%.3F %.3F l ', (($x + $w) * $this->kp), -($y * $this->kp));
2129			$path_cmd .= sprintf('%.3F %.3F l ', (($x + $w) * $this->kp), -(($y + $h) * $this->kp));
2130			$path_cmd .= sprintf('%.3F %.3F l ', ($x) * $this->kp, -(($y + $h) * $this->kp));
2131			$path_cmd .= sprintf('%.3F %.3F l h ', ($x * $this->kp), -($y * $this->kp));
2132		} else {
2133			//	trace un rectangle avec les arrondit
2134			//	les points de controle du bezier sont deduis grace a la constante kappa
2135			$kappa = 4 * (sqrt(2) - 1) / 3;
2136
2137			$kx = $kappa * $rx;
2138			$ky = $kappa * $ry;
2139
2140			$path_cmd = sprintf('%.3F %.3F m ', ($x + $rx) * $this->kp, -$y * $this->kp);
2141			$path_cmd .= sprintf('%.3F %.3F l ', ($x + ($w - $rx)) * $this->kp, -$y * $this->kp);
2142			$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x + ($w - $rx + $kx)) * $this->kp, -$y * $this->kp, ($x + $w) * $this->kp, (-$y + (-$ry + $ky)) * $this->kp, ($x + $w) * $this->kp, (-$y + (-$ry)) * $this->kp);
2143			$path_cmd .= sprintf('%.3F %.3F l ', ($x + $w) * $this->kp, (-$y + (-$h + $ry)) * $this->kp);
2144			$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x + $w) * $this->kp, (-$y + (-$h - $ky + $ry)) * $this->kp, ($x + ($w - $rx + $kx)) * $this->kp, (-$y + (-$h)) * $this->kp, ($x + ($w - $rx)) * $this->kp, (-$y + (-$h)) * $this->kp);
2145
2146			$path_cmd .= sprintf('%.3F %.3F l ', ($x + $rx) * $this->kp, (-$y + (-$h)) * $this->kp);
2147			$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x + ($rx - $kx)) * $this->kp, (-$y + (-$h)) * $this->kp, $x * $this->kp, (-$y + (-$h - $ky + $ry)) * $this->kp, $x * $this->kp, (-$y + (-$h + $ry)) * $this->kp);
2148			$path_cmd .= sprintf('%.3F %.3F l ', $x * $this->kp, (-$y + (-$ry)) * $this->kp);
2149			$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c h ', $x * $this->kp, (-$y + (-$ry + $ky)) * $this->kp, ($x + ($rx - $kx)) * $this->kp, -$y * $this->kp, ($x + $rx) * $this->kp, -$y * $this->kp);
2150		}
2151		return $path_cmd;
2152	}
2153
2154	//
2155	//	fonction retracant les <ellipse /> et <circle />
2156	//	 le cercle est tracé grave a 4 bezier cubic, les poitn de controles
2157	//	sont deduis grace a la constante kappa * rayon
2158	function svgEllipse($arguments)
2159	{
2160		if ($arguments['rx'] == 0 || $arguments['ry'] == 0) {
2161			return '';
2162		}
2163
2164		$kappa = 4 * (sqrt(2) - 1) / 3;
2165
2166		$cx = $this->ConvertSVGSizePixels($arguments['cx'], 'x');
2167		$cy = $this->ConvertSVGSizePixels($arguments['cy'], 'y');
2168		$rx = $this->ConvertSVGSizePixels($arguments['rx'], 'x');
2169		$ry = $this->ConvertSVGSizePixels($arguments['ry'], 'y');
2170
2171		$x1 = $cx;
2172		$y1 = -$cy + $ry;
2173
2174		$x2 = $cx + $rx;
2175		$y2 = -$cy;
2176
2177		$x3 = $cx;
2178		$y3 = -$cy - $ry;
2179
2180		$x4 = $cx - $rx;
2181		$y4 = -$cy;
2182
2183		$path_cmd = sprintf('%.3F %.3F m ', $x1 * $this->kp, $y1 * $this->kp);
2184		$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x1 + ($rx * $kappa)) * $this->kp, $y1 * $this->kp, $x2 * $this->kp, ($y2 + ($ry * $kappa)) * $this->kp, $x2 * $this->kp, $y2 * $this->kp);
2185		$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x2 * $this->kp, ($y2 - ($ry * $kappa)) * $this->kp, ($x3 + ($rx * $kappa)) * $this->kp, $y3 * $this->kp, $x3 * $this->kp, $y3 * $this->kp);
2186		$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($x3 - ($rx * $kappa)) * $this->kp, $y3 * $this->kp, $x4 * $this->kp, ($y4 - ($ry * $kappa)) * $this->kp, $x4 * $this->kp, $y4 * $this->kp);
2187		$path_cmd .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x4 * $this->kp, ($y4 + ($ry * $kappa)) * $this->kp, ($x1 - ($rx * $kappa)) * $this->kp, $y1 * $this->kp, $x1 * $this->kp, $y1 * $this->kp);
2188		$path_cmd .= 'h ';
2189
2190		return $path_cmd;
2191	}
2192
2193	//
2194	//	fonction retracant les <polyline /> et les <line />
2195	function svgPolyline($arguments, $ispolyline = true)
2196	{
2197		if ($ispolyline) {
2198			$xbase = $arguments[0];
2199			$ybase = - $arguments[1];
2200		} else {
2201			if ($arguments[0] == $arguments[2] && $arguments[1] == $arguments[3]) {
2202				return '';
2203			} // Zero length line
2204			$xbase = $this->ConvertSVGSizePixels($arguments[0], 'x');
2205			$ybase = - $this->ConvertSVGSizePixels($arguments[1], 'y');
2206		}
2207		$path_cmd = sprintf('%.3F %.3F m ', $xbase * $this->kp, $ybase * $this->kp);
2208		for ($i = 2; $i < count($arguments); $i += 2) {
2209			if ($ispolyline) {
2210				$tmp_x = $arguments[$i];
2211				$tmp_y = - $arguments[($i + 1)];
2212			} else {
2213				$tmp_x = $this->ConvertSVGSizePixels($arguments[$i], 'x');
2214				$tmp_y = - $this->ConvertSVGSizePixels($arguments[($i + 1)], 'y');
2215			}
2216			$path_cmd .= sprintf('%.3F %.3F l ', $tmp_x * $this->kp, $tmp_y * $this->kp);
2217		}
2218
2219		//	$path_cmd .= 'h '; // ?? In error - don't close subpath here
2220		return $path_cmd;
2221	}
2222
2223	//
2224	//	fonction retracant les <polygone />
2225	function svgPolygon($arguments)
2226	{
2227		$xbase = $arguments[0];
2228		$ybase = - $arguments[1];
2229		$path_cmd = sprintf('%.3F %.3F m ', $xbase * $this->kp, $ybase * $this->kp);
2230		for ($i = 2; $i < count($arguments); $i += 2) {
2231			$tmp_x = $arguments[$i];
2232			$tmp_y = - $arguments[($i + 1)];
2233
2234			$path_cmd .= sprintf('%.3F %.3F l ', $tmp_x * $this->kp, $tmp_y * $this->kp);
2235		}
2236		$path_cmd .= sprintf('%.3F %.3F l ', $xbase * $this->kp, $ybase * $this->kp);
2237		$path_cmd .= 'h ';
2238		return $path_cmd;
2239	}
2240
2241	//
2242	//	write string to image
2243	function svgText()
2244	{
2245		// $tmp = count($this->txt_style)-1;
2246		$current_style = $this->txt_style[count($this->txt_style) - 1]; // mPDF 5.7.4
2247		$style = '';
2248		$op = '';
2249		$render = -1;
2250		if (isset($this->txt_data[2])) {
2251			// mPDF 6
2252			// If using SVG Font
2253			if (isset($this->svg_font[$current_style['font-family']])) {
2254				// select font
2255				$style = 'R';
2256				$style .= (isset($current_style['font-weight']) && $current_style['font-weight'] == 'bold') ? 'B' : '';
2257				$style .= (isset($current_style['font-style']) && $current_style['font-style'] == 'italic') ? 'I' : '';
2258				$style .= (isset($current_style['font-variant']) && $current_style['font-variant'] == 'small-caps') ? 'S' : '';
2259
2260				$fontsize = $current_style['font-size'] * $this->mpdf->dpi / 72;
2261				if (isset($this->svg_font[$current_style['font-family']][$style])) {
2262					$svg_font = $this->svg_font[$current_style['font-family']][$style];
2263				} elseif (isset($this->svg_font[$current_style['font-family']]['R'])) {
2264					$svg_font = $this->svg_font[$current_style['font-family']]['R'];
2265				}
2266
2267				if (!isset($svg_font['units-per-em']) || $svg_font['units-per-em'] < 1) {
2268					$svg_font['units-per-em'] = 1000;
2269				}
2270				$units_per_em = $svg_font['units-per-em'];
2271				$scale = $fontsize / $units_per_em;
2272				$stroke_width = $current_style['stroke-width'];
2273				$stroke_width /= $scale;
2274
2275				$opacitystr = '';
2276				$fopacity = 1;
2277				if (isset($current_style['fill-opacity'])) {
2278					if ($current_style['fill-opacity'] == 0) {
2279						$fopacity = 0;
2280					} elseif ($current_style['fill-opacity'] > 1) {
2281						$fopacity = 1;
2282					} elseif ($current_style['fill-opacity'] > 0) {
2283						$fopacity = $current_style['fill-opacity'];
2284					} elseif ($current_style['fill-opacity'] < 0) {
2285						$fopacity = 0;
2286					}
2287				}
2288				$sopacity = 1;
2289				if (isset($current_style['stroke-opacity'])) {
2290					if ($current_style['stroke-opacity'] == 0) {
2291						$sopacity = 0;
2292					} elseif ($current_style['stroke-opacity'] > 1) {
2293						$sopacity = 1;
2294					} elseif ($current_style['stroke-opacity'] > 0) {
2295						$sopacity = $current_style['stroke-opacity'];
2296					} elseif ($current_style['stroke-opacity'] < 0) {
2297						$sopacity = 0;
2298					}
2299				}
2300				$gs = $this->mpdf->AddExtGState(['ca' => $fopacity, 'CA' => $sopacity, 'BM' => '/Normal']);
2301				$this->mpdf->extgstates[$gs]['fo'] = true;
2302				$opacitystr = sprintf(' /GS%d gs ', $gs);
2303
2304				$fillstr = '';
2305				if (isset($current_style['fill']) && $current_style['fill'] != 'none') {
2306					$col = $this->colorConverter->convert($current_style['fill'], $this->mpdf->PDFAXwarnings);
2307					$fillstr = $this->mpdf->SetFColor($col, true);
2308					$render = "0"; // Fill (only)
2309					$op = 'f';
2310				}
2311				$strokestr = '';
2312				if ($stroke_width > 0 && $current_style['stroke'] != 'none') {
2313					$scol = $this->colorConverter->convert($current_style['stroke'], $this->mpdf->PDFAXwarnings);
2314					if ($scol) {
2315						$strokestr .= $this->mpdf->SetDColor($scol, true) . ' ';
2316					}
2317					$linewidth = $this->ConvertSVGSizePixels($stroke_width);
2318					if ($linewidth > 0) {
2319						$strokestr .= sprintf('%.3F w 0 J 0 j ', $linewidth * $this->kp);
2320						if ($render == -1) {
2321							$render = "1";
2322						} // stroke only
2323						else {
2324							$render = "2";
2325						}  // fill and stroke
2326						$op .= 'S';
2327					}
2328				}
2329				if ($render == -1) {
2330					return '';
2331				}
2332				if ($op == 'fS') {
2333					$op = 'B';
2334				}
2335
2336				$x = $this->txt_data[0]; // mPDF 5.7.4
2337				$y = $this->txt_data[1]; // mPDF 5.7.4
2338				$txt = $this->txt_data[2];
2339
2340				$txt = preg_replace('/\f/', '', $txt);
2341				$txt = preg_replace('/\r/', '', $txt);
2342				$txt = preg_replace('/\n/', ' ', $txt);
2343				$txt = preg_replace('/\t/', ' ', $txt);
2344				$txt = preg_replace("/[ ]+/u", ' ', $txt);
2345
2346				if ($this->textjuststarted) {
2347					$txt = ltrim($txt);
2348				}  // mPDF 5.7.4
2349				$this->textjuststarted = false;  // mPDF 5.7.4
2350
2351				$txt = $this->mpdf->purify_utf8_text($txt);
2352				if ($this->mpdf->text_input_as_HTML) {
2353					$txt = $this->mpdf->all_entities_to_utf8($txt);
2354				}
2355
2356				$nb = mb_strlen($txt, 'UTF-8');
2357				$i = 0;
2358				$sw = 0;
2359				$subpath_cmd = '';
2360				while ($i < $nb) {
2361					//Get next character
2362					$char = mb_substr($txt, $i, 1, 'UTF-8');
2363
2364
2365					if (isset($svg_font['glyphs'][$char])) {
2366						$d = $svg_font['glyphs'][$char]['d'];
2367						if (isset($svg_font['glyphs'][$char]['horiz-adv-x'])) {
2368							$horiz_adv_x = $svg_font['glyphs'][$char]['horiz-adv-x'];
2369						} else {
2370							$horiz_adv_x = $svg_font['horiz-adv-x'];
2371						} // missing glyph width
2372					} else {
2373						$d = $svg_font['d'];
2374						$horiz_adv_x = $svg_font['horiz-adv-x']; // missing glyph width
2375					}
2376					preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $d, $commands, PREG_SET_ORDER);
2377					$subpath_cmd .= sprintf('q %.4F 0 0 %.4F mPDF-AXS(%.4F) %.4F cm ', $scale, -$scale, ($x + $sw * $scale) * $this->kp, -$y * $this->kp);
2378
2379					$this->subPathInit = true;
2380					$this->pathBBox = [999999, 999999, -999999, -999999];
2381					foreach ($commands as $cmd) {
2382						if (count($cmd) == 3 || (isset($cmd[2]) && $cmd[2] == '')) {
2383							list($tmp, $command, $arguments) = $cmd;
2384						} else {
2385							list($tmp, $command) = $cmd;
2386							$arguments = '';
2387						}
2388
2389						$subpath_cmd .= $this->svgPath($command, $arguments);
2390					}
2391					$subpath_cmd .= $op . ' Q ';
2392					if ($this->pathBBox[2] == -1999998) {
2393						$this->pathBBox[2] = 100;
2394					}
2395					if ($this->pathBBox[3] == -1999998) {
2396						$this->pathBBox[3] = 100;
2397					}
2398					if ($this->pathBBox[0] == 999999) {
2399						$this->pathBBox[0] = 0;
2400					}
2401					if ($this->pathBBox[1] == 999999) {
2402						$this->pathBBox[1] = 0;
2403					}
2404
2405
2406					$sw += $horiz_adv_x;
2407					$i++;
2408				}
2409
2410				$sw *= $scale; // convert stringwidth to units
2411				// mPDF 5.7.4
2412				$this->textlength = $sw;
2413				$this->texttotallength += $this->textlength;
2414
2415				$path_cmd = sprintf('q %s %s Tr %s %s ', $opacitystr, $render, $fillstr, $strokestr);
2416				$path_cmd .= $subpath_cmd;
2417				$path_cmd .= 'Q ';
2418
2419				unset($this->txt_data[0], $this->txt_data[1], $this->txt_data[2]);
2420				return $path_cmd;
2421			}
2422
2423			// select font
2424			$style .= ($current_style['font-weight'] == 'bold') ? 'B' : '';
2425			$style .= ($current_style['font-style'] == 'italic') ? 'I' : '';
2426			$size = $current_style['font-size'] * $this->kf;
2427
2428			$current_style['font-family'] = $this->mpdf->SetFont($current_style['font-family'], $style, $size, false);
2429			$this->mpdf->CurrentFont['fo'] = true;
2430
2431			$opacitystr = '';
2432			// mPDF 6
2433			$fopacity = 1;
2434			if (isset($current_style['fill-opacity'])) {
2435				if ($current_style['fill-opacity'] == 0) {
2436					$fopacity = 0;
2437				} elseif ($current_style['fill-opacity'] > 1) {
2438					$fopacity = 1;
2439				} elseif ($current_style['fill-opacity'] > 0) {
2440					$fopacity = $current_style['fill-opacity'];
2441				} elseif ($current_style['fill-opacity'] < 0) {
2442					$fopacity = 0;
2443				}
2444			}
2445			$sopacity = 1;
2446			if (isset($current_style['stroke-opacity'])) {
2447				if ($current_style['stroke-opacity'] == 0) {
2448					$sopacity = 0;
2449				} elseif ($current_style['stroke-opacity'] > 1) {
2450					$sopacity = 1;
2451				} elseif ($current_style['stroke-opacity'] > 0) {
2452					$sopacity = $current_style['stroke-opacity'];
2453				} elseif ($current_style['stroke-opacity'] < 0) {
2454					$sopacity = 0;
2455				}
2456			}
2457			$gs = $this->mpdf->AddExtGState(['ca' => $fopacity, 'CA' => $sopacity, 'BM' => '/Normal']);
2458			$this->mpdf->extgstates[$gs]['fo'] = true;
2459			$opacitystr = sprintf(' /GS%d gs ', $gs);
2460
2461			$fillstr = '';
2462			if (isset($current_style['fill']) && $current_style['fill'] != 'none') {
2463				$col = $this->colorConverter->convert($current_style['fill'], $this->mpdf->PDFAXwarnings);
2464				$fillstr = $this->mpdf->SetFColor($col, true);
2465				$render = "0"; // Fill (only)
2466			}
2467			$strokestr = '';
2468			if (isset($current_style['stroke-width']) && $current_style['stroke-width'] > 0 && $current_style['stroke'] != 'none') {
2469				$scol = $this->colorConverter->convert($current_style['stroke'], $this->mpdf->PDFAXwarnings);
2470				if ($scol) {
2471					$strokestr .= $this->mpdf->SetDColor($scol, true) . ' ';
2472				}
2473				$linewidth = $this->ConvertSVGSizePixels($current_style['stroke-width']);
2474				if ($linewidth > 0) {
2475					$strokestr .= sprintf('%.3F w 1 J 1 j ', $linewidth * $this->kp);
2476					if ($render == -1) {
2477						$render = "1";
2478					} // stroke only
2479					else {
2480						$render = "2";
2481					}  // fill and stroke
2482				}
2483			}
2484			if ($render == -1) {
2485				return '';
2486			}
2487
2488			$x = $this->txt_data[0]; // mPDF 5.7.4
2489			$y = $this->txt_data[1]; // mPDF 5.7.4
2490			$txt = $this->txt_data[2];
2491
2492			$txt = preg_replace('/\f/', '', $txt);
2493			$txt = preg_replace('/\r/', '', $txt);
2494			$txt = preg_replace('/\n/', ' ', $txt);
2495			$txt = preg_replace('/\t/', ' ', $txt);
2496			$txt = preg_replace("/[ ]+/u", ' ', $txt);
2497
2498			if ($this->textjuststarted) {
2499				$txt = ltrim($txt);
2500			}  // mPDF 5.7.4
2501			$this->textjuststarted = false;  // mPDF 5.7.4
2502
2503			$txt = $this->mpdf->purify_utf8_text($txt);
2504			if ($this->mpdf->text_input_as_HTML) {
2505				$txt = $this->mpdf->all_entities_to_utf8($txt);
2506			}
2507
2508			if ($this->mpdf->usingCoreFont) {
2509				$txt = mb_convert_encoding($txt, $this->mpdf->mb_enc, 'UTF-8');
2510			}
2511			if (preg_match("/([" . $this->mpdf->pregRTLchars . "])/u", $txt)) {
2512				$this->mpdf->biDirectional = true;
2513			}
2514
2515			$textvar = 0;
2516			$save_OTLtags = $this->mpdf->OTLtags;
2517			$this->mpdf->OTLtags = [];
2518			if ($this->mpdf->useKerning) {
2519				if ($this->mpdf->CurrentFont['haskernGPOS']) {
2520					if (isset($this->mpdf->OTLtags['Plus'])) {
2521						$this->mpdf->OTLtags['Plus'] .= ' kern';
2522					} else {
2523						$this->mpdf->OTLtags['Plus'] = ' kern';
2524					}
2525				} else {
2526					$textvar = ($textvar | TextVars::FC_KERNING);
2527				}
2528			}
2529
2530			// Use OTL OpenType Table Layout - GSUB & GPOS
2531			if (isset($this->mpdf->CurrentFont['useOTL']) && $this->mpdf->CurrentFont['useOTL']) {
2532				$txt = $this->otl->applyOTL($txt, $this->mpdf->CurrentFont['useOTL']);
2533				$OTLdata = $this->otl->OTLdata;
2534			}
2535			$this->mpdf->OTLtags = $save_OTLtags;
2536
2537			$this->mpdf->magic_reverse_dir($txt, $this->mpdf->directionality, $OTLdata);
2538
2539			$this->mpdf->CurrentFont['used'] = true;
2540
2541			$sw = $this->mpdf->GetStringWidth($txt, true, $OTLdata, $textvar); // also adds characters to subset
2542			// mPDF 5.7.4
2543			$this->textlength = $sw * 1 / (25.4 / $this->mpdf->dpi);
2544			$this->texttotallength += $this->textlength;
2545
2546			$pdfx = $x * $this->kp;
2547			$pdfy = -$y * $this->kp;
2548
2549			$aixextra = sprintf(' /F%d %.3F Tf %s %s Tr %s %s ', $this->mpdf->CurrentFont['i'], $this->mpdf->FontSizePt, $opacitystr, $render, $fillstr, $strokestr);
2550
2551			$path_cmd = 'q 1 0 0 1 mPDF-AXS(0.00) 0 cm '; // Align X-shift
2552			$path_cmd .= $this->mpdf->Text($pdfx, $pdfy, $txt, $OTLdata, $textvar, $aixextra, 'SVG', true);
2553			$path_cmd .= " Q\n";
2554
2555			unset($this->txt_data[0], $this->txt_data[1], $this->txt_data[2]);
2556
2557			if (isset($current_style['font-size-parent'])) {
2558				$this->mpdf->SetFontSize($current_style['font-size-parent']);
2559			}
2560		} else {
2561			return ' ';
2562		}
2563		// Reset font	// mPDF 5.7.4
2564		$prev_style = $this->txt_style[count($this->txt_style) - 1];
2565		$style = '';
2566		$style .= ($prev_style['font-weight'] == 'bold') ? 'B' : '';
2567		$style .= ($prev_style['font-style'] == 'italic') ? 'I' : '';
2568		$size = $prev_style['font-size'] * $this->kf;
2569		$this->mpdf->SetFont($prev_style['font-family'], $style, $size, false);
2570
2571		return $path_cmd;
2572	}
2573
2574	function svgDefineTxtStyle($critere_style)
2575	{
2576		// get copy of current/default txt style, and modify it with supplied attributes
2577		$tmp = count($this->txt_style) - 1;
2578		$current_style = $this->txt_style[$tmp];
2579		if (isset($critere_style['style'])) {
2580			if (preg_match('/fill:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/', $critere_style['style'], $m)) {
2581				$current_style['fill'] = '#' . str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT) . str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT) . str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
2582			} else {
2583				$tmp = preg_replace("/(.*)fill:\s*([a-z0-9#_()]*|none)(.*)/i", "$2", $critere_style['style']);
2584				if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2585					$current_style['fill'] = $tmp;
2586				}
2587			}
2588
2589			// mPDF 6
2590			if (preg_match("/[^-]opacity:\s*([a-z0-9.]*|none)/i", $critere_style['style'], $m) ||
2591				preg_match("/^opacity:\s*([a-z0-9.]*|none)/i", $critere_style['style'], $m)) {
2592				$current_style['fill-opacity'] = $m[1];
2593				$current_style['stroke-opacity'] = $m[1];
2594			}
2595
2596			$tmp = preg_replace("/(.*)fill-opacity:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2597			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2598				$current_style['fill-opacity'] = $tmp;
2599			}
2600
2601			$tmp = preg_replace("/(.*)fill-rule:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2602			if ($tmp != $critere_style['style'] && $tmp != $critere_style['style']) {
2603				$current_style['fill-rule'] = $tmp;
2604			}
2605
2606			if (preg_match('/stroke:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/', $critere_style['style'], $m)) {
2607				$current_style['stroke'] = '#' . str_pad(dechex($m[1]), 2, "0", STR_PAD_LEFT) . str_pad(dechex($m[2]), 2, "0", STR_PAD_LEFT) . str_pad(dechex($m[3]), 2, "0", STR_PAD_LEFT);
2608			} else {
2609				$tmp = preg_replace("/(.*)stroke:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2610				if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2611					$current_style['stroke'] = $tmp;
2612				}
2613			}
2614
2615			$tmp = preg_replace("/(.*)stroke-linecap:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2616			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2617				$current_style['stroke-linecap'] = $tmp;
2618			}
2619
2620			$tmp = preg_replace("/(.*)stroke-linejoin:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2621			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2622				$current_style['stroke-linejoin'] = $tmp;
2623			}
2624
2625			$tmp = preg_replace("/(.*)stroke-miterlimit:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2626			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2627				$current_style['stroke-miterlimit'] = $tmp;
2628			}
2629
2630			$tmp = preg_replace("/(.*)stroke-opacity:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2631			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2632				$current_style['stroke-opacity'] = $tmp;
2633			}
2634
2635			$tmp = preg_replace("/(.*)stroke-width:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2636			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2637				$current_style['stroke-width'] = $tmp;
2638			}
2639
2640			$tmp = preg_replace("/(.*)stroke-dasharray:\s*([a-z0-9., ]*|none)(.*)/i", "$2", $critere_style['style']);
2641			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2642				$current_style['stroke-dasharray'] = $tmp;
2643			}
2644
2645			$tmp = preg_replace("/(.*)stroke-dashoffset:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2646			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2647				$current_style['stroke-dashoffset'] = $tmp;
2648			}
2649
2650			$tmp = preg_replace("/(.*)font-family:\s*([a-z0-9.\"' ,\-]*|none)(.*)/i", "$2", $critere_style['style']);
2651			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2652				$critere_style['font-family'] = $tmp;
2653			}
2654
2655			$tmp = preg_replace("/(.*)font-size:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2656			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2657				$critere_style['font-size'] = $tmp;
2658			}
2659
2660			$tmp = preg_replace("/(.*)font-weight:\s*([a-z0-9.]*|normal)(.*)/i", "$2", $critere_style['style']);
2661			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2662				$critere_style['font-weight'] = $tmp;
2663			}
2664
2665			$tmp = preg_replace("/(.*)font-style:\s*([a-z0-9.]*|normal)(.*)/i", "$2", $critere_style['style']);
2666			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2667				$critere_style['font-style'] = $tmp;
2668			}
2669
2670			$tmp = preg_replace("/(.*)font-variant:\s*([a-z0-9.]*|normal)(.*)/i", "$2", $critere_style['style']);
2671			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2672				$critere_style['font-variant'] = $tmp;
2673			}
2674
2675			$tmp = preg_replace("/(.*)text-anchor:\s*(start|middle|end)(.*)/i", "$2", $critere_style['style']);
2676			if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2677				$critere_style['text-anchor'] = $tmp;
2678			}
2679		}
2680		if (isset($critere_style['font'])) {
2681			// [ [ <'font-style'> || <'font-variant'> || <'font-weight'> ]?<'font-size'> [ / <'line-height'> ]? <'font-family'> ]
2682
2683			$tmp = preg_replace("/(.*)(italic|oblique)(.*)/i", "$2", $critere_style['font']);
2684			if ($tmp != $critere_style['font']) {
2685				if ($tmp == 'oblique') {
2686					$tmp = 'italic';
2687				}
2688				$current_style['font-style'] = $tmp;
2689			}
2690			$tmp = preg_replace("/(.*)(bold|bolder)(.*)/i", "$2", $critere_style['font']);
2691			if ($tmp != $critere_style['font']) {
2692				if ($tmp == 'bolder') {
2693					$tmp = 'bold';
2694				}
2695				$current_style['font-weight'] = $tmp;
2696			}
2697
2698			$tmp = preg_replace("/(.*)(small\-caps)(.*)/i", "$2", $critere_style['font']);
2699			if ($tmp != $critere_style['font']) {
2700				$current_style['font-variant'] = $tmp;
2701			}
2702
2703			// select digits not followed by percent sign nor preceeded by forward slash
2704			$tmp = preg_replace("/(.*)\b(\d+)[\b|\/](.*)/i", "$2", $critere_style['font']);
2705			if ($tmp != $critere_style['font']) {
2706				$current_style['font-size'] = $this->ConvertSVGSizePts($tmp);
2707				$this->mpdf->SetFont('', '', $current_style['font-size'], false);
2708			}
2709		}
2710
2711		// mPDF 6
2712		if (isset($critere_style['opacity']) && $critere_style['opacity'] != 'inherit') {
2713			$current_style['fill-opacity'] = $critere_style['opacity'];
2714			$current_style['stroke-opacity'] = $critere_style['opacity'];
2715		}
2716		// mPDF 6
2717		if (isset($critere_style['stroke-opacity']) && $critere_style['stroke-opacity'] != 'inherit') {
2718			$current_style['stroke-opacity'] = $critere_style['stroke-opacity'];
2719		}
2720		// mPDF 6
2721		if (isset($critere_style['fill-opacity']) && $critere_style['fill-opacity'] != 'inherit') {
2722			$current_style['fill-opacity'] = $critere_style['fill-opacity'];
2723		}
2724		if (isset($critere_style['fill']) && $critere_style['fill'] != 'inherit') {
2725			$current_style['fill'] = $critere_style['fill'];
2726		}
2727		if (isset($critere_style['stroke']) && $critere_style['stroke'] != 'inherit') {
2728			$current_style['stroke'] = $critere_style['stroke'];
2729		}
2730		if (isset($critere_style['stroke-width']) && $critere_style['stroke-width'] != 'inherit') {
2731			$current_style['stroke-width'] = $critere_style['stroke-width'];
2732		}
2733
2734		if (isset($critere_style['font-style']) && $critere_style['font-style'] != 'inherit') {
2735			if (strtolower($critere_style['font-style']) == 'oblique') {
2736				$critere_style['font-style'] = 'italic';
2737			}
2738			$current_style['font-style'] = $critere_style['font-style'];
2739		}
2740
2741		if (isset($critere_style['font-weight']) && $critere_style['font-weight'] != 'inherit') {
2742			if (strtolower($critere_style['font-weight']) == 'bolder') {
2743				$critere_style['font-weight'] = 'bold';
2744			}
2745			$current_style['font-weight'] = $critere_style['font-weight'];
2746		}
2747
2748		if (isset($critere_style['font-variant']) && $critere_style['font-variant'] != 'inherit') {
2749			$current_style['font-variant'] = $critere_style['font-variant'];
2750		}
2751
2752		if (isset($critere_style['font-size']) && $critere_style['font-size'] != 'inherit') {
2753			if (strpos($critere_style['font-size'], '%') !== false) {
2754				$current_style['font-size-parent'] = $current_style['font-size'];
2755			}
2756			$current_style['font-size'] = $this->ConvertSVGSizePts($critere_style['font-size']);
2757			$this->mpdf->SetFont('', '', $current_style['font-size'], false);
2758		}
2759
2760		if (isset($critere_style['font-family']) && $critere_style['font-family'] != 'inherit') {
2761			$v = $critere_style['font-family'];
2762			$aux_fontlist = explode(",", $v);
2763			$found = 0;
2764
2765			$svgfontstyle = 'R';
2766			$svgfontstyle .= (isset($current_style['font-weight']) && $current_style['font-weight'] == 'bold') ? 'B' : '';
2767			$svgfontstyle .= (isset($current_style['font-style']) && $current_style['font-style'] == 'italic') ? 'I' : '';
2768			$svgfontstyle .= (isset($current_style['font-variant']) && $current_style['font-variant'] == 'small-caps') ? 'S' : '';
2769
2770			foreach ($aux_fontlist as $f) {
2771				$fonttype = trim($f);
2772				$fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
2773				$fonttype = preg_replace('/ /', '', $fonttype);
2774				$v = strtolower(trim($fonttype));
2775				if (isset($this->mpdf->fonttrans[$v]) && $this->mpdf->fonttrans[$v]) {
2776					$v = $this->mpdf->fonttrans[$v];
2777				}
2778				if ((!$this->mpdf->usingCoreFont && in_array($v, $this->mpdf->available_unifonts)) ||
2779					($this->mpdf->usingCoreFont && in_array($v, ['courier', 'times', 'helvetica', 'arial'])) ||
2780					in_array($v, ['sjis', 'uhc', 'big5', 'gb']) || isset($this->svg_font[$v][$svgfontstyle])) { // mPDF 6
2781					$current_style['font-family'] = $v;
2782					$found = 1;
2783					break;
2784				}
2785			}
2786			if (!$found) {
2787				foreach ($aux_fontlist as $f) {
2788					$fonttype = trim($f);
2789					$fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
2790					$fonttype = preg_replace('/ /', '', $fonttype);
2791					$v = strtolower(trim($fonttype));
2792					if (isset($this->mpdf->fonttrans[$v]) && $this->mpdf->fonttrans[$v]) {
2793						$v = $this->mpdf->fonttrans[$v];
2794					}
2795					if (in_array($v, $this->mpdf->sans_fonts) || in_array($v, $this->mpdf->serif_fonts) || in_array($v, $this->mpdf->mono_fonts) || isset($this->svg_font[$v][$svgfontstyle])) {  // mPDF 6
2796						$current_style['font-family'] = $v;
2797						break;
2798					}
2799				}
2800			}
2801		}
2802		if (isset($critere_style['text-anchor']) && $critere_style['text-anchor'] != 'inherit') {
2803			$current_style['text-anchor'] = $critere_style['text-anchor'];
2804		}
2805
2806		// add current style to text style array (will remove it later after writing text to svg_string)
2807		array_push($this->txt_style, $current_style);
2808	}
2809
2810	//
2811	//	fonction ajoutant un gradient
2812	function svgAddGradient($id, $array_gradient)
2813	{
2814
2815		$this->svg_gradient[$id] = $array_gradient;
2816	}
2817
2818	//
2819	//	Ajoute une couleur dans le gradient correspondant
2820	//
2821	//	function ecrivant dans le svgstring
2822	function svgWriteString($content)
2823	{
2824		$this->svg_string .= $content;
2825	}
2826
2827	/**
2828	 * SVGs made with Adobe Illustrator use a style tag and classes instead of inline styles
2829	 * See: https://github.com/mpdf/mpdf/issues/450
2830	 *
2831	 * This function brutally copies the styles inline
2832	 *  ( Currently only looks for classes as a selector )
2833	 *
2834	 * @param string $data svg contents
2835	 * @return string svg contents
2836	 * @author Antonio Norman - softcodex.ch
2837	 */
2838	function mergeStyles($data)
2839	{
2840		$xml = new \DOMDocument();
2841		if (!$xml->loadXML($data, LIBXML_NOERROR)) {
2842			return $data;
2843		}
2844
2845		// Check it's an SVG
2846		$svgNode = $xml->getElementsByTagName('svg');
2847		if ($svgNode->length === 0) {
2848			return $data;
2849		}
2850
2851		// Find the style node
2852		$styles = [];
2853		/** @var $styleNode \DOMNode */
2854		foreach ($svgNode->item(0)->getElementsByTagName('style') as $styleNode) {
2855
2856			preg_match_all('/(\.[^{]+)\s*\{\s*([^}]+)\s*}/m', $styleNode->nodeValue, $matches, PREG_SET_ORDER);
2857			foreach ($matches as $cssBlock) {
2858				$css = preg_replace('/\s{2,}/', ' ', $cssBlock[2]); // Clean spaces or new lines
2859				$selector = trim($cssBlock[1]);
2860
2861				$styles[$selector] = isset($styles[$cssBlock[1]]) ?
2862					$styles[$selector] . ' ' . $css : // Append if the selector is already defined
2863					$css;
2864			}
2865		}
2866
2867		if (empty($styles)) {
2868			return $data;
2869		}
2870
2871		// Recursively loop the nodes inserting the styles inline
2872		$setStylesInline = function (\DOMNode $xml) use ($styles, &$setStylesInline) {
2873			// Apply the styles to the elements
2874			foreach ($xml->childNodes as $node) {
2875
2876				if ($node->hasChildNodes()) {
2877					$setStylesInline($node);
2878				}
2879
2880				if (!$node instanceof \DOMElement) {
2881					continue;
2882				}
2883
2884				// Check the node has the a class with a style
2885				if (!$node->hasAttribute('class')) {
2886					continue;
2887				}
2888
2889				// Allow for class=" class1  class2 "
2890				$classes = explode(' ', $node->getAttribute('class'));
2891
2892				foreach ($classes as $class) {
2893
2894					$class = '.' . trim($class);
2895					if (!empty($class) && isset($styles[$class])) {
2896
2897						$style = $node->hasAttribute('style') ?
2898							$styles[$class] . ' ' . $node->getAttribute('style') :
2899							$styles[$class];
2900
2901						$node->setAttribute('style', $style);
2902					}
2903				}
2904			}
2905		};
2906
2907		$setStylesInline($xml);
2908
2909		return $xml->saveXML();
2910	}
2911
2912	/**
2913	 * analise le svg et renvoie aux fonctions precedente our le traitement
2914	 */
2915	function ImageSVG($data)
2916	{
2917		$data = preg_replace('/^.*?<svg([> ])/is', '<svg\\1', $data); // mPDF 5.7.4
2918		$data = preg_replace('/<!--.*?-->/is', '', $data); // mPDF 5.7.4
2919
2920		// Converts < to &lt; when not a tag
2921		$data = preg_replace('/<([^!?\/a-zA-Z_:])/i', '&lt;\\1', $data); // mPDF 5.7.4
2922
2923		$data = $this->mergeStyles($data);
2924
2925		if ($this->mpdf->svgAutoFont) {
2926			$data = $this->markScriptToLang($data);
2927		}
2928
2929		$this->svg_info = [];
2930		$last_gradid = ''; // mPDF 6
2931		$last_svg_fontid = ''; // mPDF 6
2932		$last_svg_fontdefw = ''; // mPDF 6
2933		$last_svg_fontstyle = ''; // mPDF 6
2934
2935		if (preg_match('/<!ENTITY/si', $data)) {
2936			// Get User-defined entities
2937			preg_match_all('/<!ENTITY\s+([a-z]+)\s+\"(.*?)\">/si', $data, $ent);
2938			// Replace entities
2939			for ($i = 0; $i < count($ent[0]); $i++) {
2940				$data = preg_replace('/&' . preg_quote($ent[1][$i], '/') . ';/is', $ent[2][$i], $data);
2941			}
2942		}
2943
2944		if (preg_match('/xlink:href\s*=/si', $data)) {
2945
2946			// GRADIENTS
2947			// Get links
2948			preg_match_all('/(<(linearGradient|radialgradient)[^>]*)xlink:href\s*=\s*["\']#(.*?)["\'](.*?)\/>/si', $data, $links);
2949			if (count($links[0])) {
2950				$links[5] = [];
2951			}
2952
2953			// Delete links from data - keeping in $links
2954			for ($i = 0; $i < count($links[0]); $i++) {
2955				$links[5][$i] = 'tmpLink' . random_int(100000, 9999999);
2956				$data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', '<MYLINKS' . $links[5][$i] . '>', $data);
2957			}
2958
2959			// Get targets
2960			preg_match_all('/<(linearGradient|radialgradient)([^>]*)id\s*=\s*["\'](.*?)["\'](.*?)>(.*?)<\/(linearGradient|radialgradient)>/si', $data, $m);
2961			$targets = [];
2962			$stops = [];
2963
2964			// keeping in $targets
2965			for ($i = 0; $i < count($m[0]); $i++) {
2966				$stops[$m[3][$i]] = $m[5][$i];
2967			}
2968
2969			// Add back links this time as targets (gradients)
2970			for ($i = 0; $i < count($links[0]); $i++) {
2971				$def = $links[1][$i] . ' ' . $links[4][$i] . '>' . $stops[$links[3][$i]] . '</' . $links[2][$i] . '>';
2972				$data = preg_replace('/<MYLINKS' . $links[5][$i] . '>/is', $def, $data);
2973			}
2974
2975			// mPDF 5.7.4
2976			// <TREF>
2977			preg_match_all('/<tref ([^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)\/>/si', $data, $links);
2978			for ($i = 0; $i < count($links[0]); $i++) {
2979				// Get the item to use from defs
2980				$insert = '';
2981				if (preg_match('/<text [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*>(.*?)<\/text>/si', $data, $m)) {
2982					$insert = $m[1];
2983				}
2984				if ($insert) {
2985					$data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', $insert, $data);
2986				}
2987			}
2988
2989			// mPDF 5.7.2
2990			// <USE>
2991			preg_match_all('/<use( [^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)\/>/si', $data, $links);
2992			for ($i = 0; $i < count($links[0]); $i++) {
2993				// Get the item to use from defs
2994				$insert = '';
2995				if (preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*\/>/si', $data, $m)) {
2996					$insert = $m[0];
2997				}
2998				if (!$insert && preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\']/si', $data, $m)) {
2999					if (preg_match('/<' . $m[1] . '[^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*>.*?<\/' . $m[1] . '>/si', $data, $m)) {
3000						$insert = $m[0];
3001					}
3002				}
3003
3004				if ($insert) {
3005					$inners = $links[1][$i] . ' ' . $links[3][$i];
3006
3007					// Change x,y coords to translate()
3008					if (preg_match('/\sy\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
3009						$y = $m[1];
3010					} else {
3011						$y = 0;
3012					}
3013
3014					if (preg_match('/\sx\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
3015						$x = $m[1];
3016					} else {
3017						$x = 0;
3018					}
3019
3020					if ($x || $y) {
3021
3022						$inners = preg_replace('/(y|x)\s*=\s*["\']([^>]*?)["\']/', '', $inners);
3023						if (preg_match('/transform\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
3024
3025							if (preg_match('/translate\(\s*([0-9\.]+)\s*,\s*([0-9\.]+)\s*\)/', $m[1], $mm)) {
3026								$transform = $m[1]; // transform="...."
3027								$x += $mm[1];
3028								$y += $mm[2];
3029								$transform = preg_replace('/' . preg_quote($mm[0], '/') . '/', '', $transform);
3030								$transform = 'transform="' . $transform . ' translate(' . $x . ', ' . $y . ')"';
3031								$inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', $transform, $inners);
3032							} else {
3033								$inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', 'transform="' . $m[1] . ' translate(' . $x . ', ' . $y . ')"', $inners);
3034							}
3035
3036						} else {
3037							$inners .= ' transform="translate(' . $x . ', ' . $y . ')"';
3038						}
3039					}
3040				}
3041
3042				$replacement = '<g ' . $inners . '>' . $insert . '</g>';
3043				$data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', $replacement, $data);
3044			}
3045
3046			preg_match_all('/<use( [^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)>\s*<\/use>/si', $data, $links);
3047			for ($i = 0; $i < count($links[0]); $i++) {
3048
3049				// Get the item to use from defs
3050				$insert = '';
3051				if (preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*\/>/si', $data, $m)) {
3052					$insert = $m[0];
3053				}
3054
3055				if (!$insert && preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\']/si', $data, $m)) {
3056					if (preg_match('/<' . $m[1] . '[^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*>.*?<\/' . $m[1] . '>/si', $data, $m)) {
3057						$insert = $m[0];
3058					}
3059				}
3060
3061				if ($insert) {
3062					$inners = $links[1][$i] . ' ' . $links[3][$i];
3063
3064					// Change x,y coords to translate()
3065					if (preg_match('/\sy\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
3066						$y = $m[1];
3067					} else {
3068						$y = 0;
3069					}
3070
3071					if (preg_match('/\sx\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
3072						$x = $m[1];
3073					} else {
3074						$x = 0;
3075					}
3076
3077					if ($x || $y) {
3078						$inners = preg_replace('/(y|x)\s*=\s*["\']([^>]*?)["\']/', '', $inners);
3079						if (preg_match('/transform\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
3080							if (preg_match('/translate\(\s*([0-9\.]+)\s*,\s*([0-9\.]+)\s*\)/', $m[1], $mm)) {
3081								$transform = $m[1]; // transform="...."
3082								$x += $mm[1];
3083								$y += $mm[2];
3084								$transform = preg_replace('/' . preg_quote($mm[0], '/') . '/', '', $transform);
3085								$transform = 'transform="' . $transform . ' translate(' . $x . ', ' . $y . ')"';
3086								$inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', $transform, $inners);
3087							} else {
3088								$inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', 'transform="' . $m[1] . ' translate(' . $x . ', ' . $y . ')"', $inners);
3089							}
3090						} else {
3091							$inners .= ' transform="translate(' . $x . ', ' . $y . ')"';
3092						}
3093					}
3094
3095					$replacement = '<g ' . $inners . '>' . $insert . '</g>';
3096					$data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', $replacement, $data);
3097				}
3098			}
3099		}
3100
3101		// Removes <pattern>
3102		$data = preg_replace('/<pattern.*?<\/pattern>/is', '', $data);
3103
3104		// Removes <marker>
3105		$data = preg_replace('/<marker.*?<\/marker>/is', '', $data);
3106
3107		$this->svg_info['data'] = $data;
3108		$this->svg_string = '';
3109
3110		$svg2pdf_xml = '';
3111
3112		// Don't output stuff inside <defs>
3113		$this->inDefs = false;
3114
3115		$svg2pdf_xml_parser = xml_parser_create("utf-8");
3116
3117		xml_parser_set_option($svg2pdf_xml_parser, XML_OPTION_CASE_FOLDING, false);
3118
3119		xml_set_element_handler(
3120			$svg2pdf_xml_parser,
3121			[$this, 'xml_svg2pdf_start'],
3122			[$this, 'xml_svg2pdf_end']
3123		);
3124
3125		xml_set_character_data_handler(
3126			$svg2pdf_xml_parser,
3127			[$this, 'characterData']
3128		);
3129
3130		xml_parse($svg2pdf_xml_parser, $data);
3131
3132		if ($this->svg_error) {
3133			return false;
3134		} else {
3135			return [
3136				'x' => $this->svg_info['x'] * $this->kp,
3137				'y' => -$this->svg_info['y'] * $this->kp,
3138				'w' => $this->svg_info['w'] * $this->kp,
3139				'h' => -$this->svg_info['h'] * $this->kp,
3140				'data' => $this->svg_string,
3141			];
3142		}
3143	}
3144
3145	// AUTOFONT =========================
3146	/** @todo reuse as much code from Mpdf::markScriptToLang as possible */
3147	function markScriptToLang($html)
3148	{
3149		if ($this->mpdf->onlyCoreFonts) {
3150			return $html;
3151		}
3152
3153		$n = '';
3154		$a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
3155		foreach ($a as $i => $e) {
3156			if ($i % 2 == 0) {
3157				$e = UtfString::strcode2utf($e);
3158				$e = $this->mpdf->lesser_entity_decode($e);
3159
3160				$earr = $this->mpdf->UTF8StringToArray($e, false);
3161
3162				$scriptblock = 0;
3163				$scriptblocks = [];
3164				$scriptblocks[0] = 0;
3165				$chardata = [];
3166				$subchunk = 0;
3167				$charctr = 0;
3168				foreach ($earr as $char) {
3169					$ucd_record = Ucdn::get_ucd_record($char);
3170					$sbl = $ucd_record[6];
3171
3172					if ($sbl && $sbl != 40 && $sbl != 102) {
3173						if ($scriptblock == 0) {
3174							$scriptblock = $sbl;
3175							$scriptblocks[$subchunk] = $scriptblock;
3176						} elseif ($scriptblock > 0 && $scriptblock != $sbl) {
3177							// NEW (non-common) Script encountered in this chunk.
3178							// Start a new subchunk
3179							$subchunk++;
3180							$scriptblock = $sbl;
3181							$charctr = 0;
3182							$scriptblocks[$subchunk] = $scriptblock;
3183						}
3184					}
3185
3186					$chardata[$subchunk][$charctr]['script'] = $sbl;
3187					$chardata[$subchunk][$charctr]['uni'] = $char;
3188					$charctr++;
3189				}
3190
3191				// If scriptblock[x] = common & non-baseScript
3192				// and scriptblock[x+1] = baseScript
3193				// Move common script from end of x to start of x+1
3194				for ($sch = 0; $sch < $subchunk; $sch++) {
3195					if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->mpdf->baseScript && $scriptblocks[$sch + 1] == $this->mpdf->baseScript) {
3196						$end = count($chardata[$sch]) - 1;
3197						while ($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script
3198							$tmp = array_pop($chardata[$sch]);
3199							array_unshift($chardata[$sch + 1], $tmp);
3200							$end--;
3201						}
3202					}
3203				}
3204
3205				$o = '';
3206				for ($sch = 0; $sch <= $