fix php 5.6 in docker dev env (#1740)
[openemr.git] / vendor / mpdf / mpdf / classes / svg.php
blob1c10abe6261aa6136d15a7871e00d22c9255e04a
1 <?php
3 // svg class modified for mPDF version 6.0 by Ian Back: based on -
4 // svg2pdf fpdf class
5 // sylvain briand (syb@godisaduck.com), modified by rick trevino (rtrevino1@yahoo.com)
6 // http://www.godisaduck.com/svg2pdf_with_fpdf
7 // http://rhodopsin.blogspot.com
8 //
9 // cette class etendue est open source, toute modification devra cependant etre repertoriée~
10 // If you wish to use Automatic Font selection within SVG's. change this definition to true.
11 // This selects different fonts for different scripts used in text.
12 // This can be enabled/disabled independently of the use of Automatic Font selection within mPDF generally.
13 // Choice of font is determined by the config_script2lang.php and config_lang2fonts.php files, the same as for mPDF generally.
14 if (!defined("_SVG_AUTOFONT")) {
15 define("_SVG_AUTOFONT", false);
18 // Enable a limited use of classes within SVG <text> elements by setting this to true.
19 // This allows recognition of a "class" attribute on a <text> element.
20 // The CSS style for that class should be outside the SVG, and cannot use any other selectors (i.e. only .class {} can be defined)
21 // <style> definitions within the SVG code will be recognised if the SVG is included as an inline item within the HTML code passed to mPDF.
22 // The style property should be pertinent to SVG e.g. use fill:red rather than color:red
23 // Only the properties currently supported for SVG text can be specified:
24 // fill, fill-opacity, stroke, stroke-opacity, stroke-linecap, stroke-linejoin, stroke-width, stroke-dasharray, stroke-dashoffset
25 // font-family, font-size, font-weight, font-variant, font-style, opacity, text-anchor
26 if (!defined("_SVG_CLASSES")) {
27 define("_SVG_CLASSES", false);
30 // NB UNITS - Works in pixels as main units - converting to PDF units when outputing to PDF string
31 // and on returning size
33 class SVG
36 var $svg_font; // array - holds content of SVG fonts defined in image // mPDF 6
38 var $svg_gradient; // array - contient les infos sur les gradient fill du svg classé par id du svg
40 var $svg_shadinglist; // array - contient les ids des objet shading
42 var $svg_info; // array contenant les infos du svg voulue par l'utilisateur
44 var $svg_attribs; // array - holds all attributes of root <svg> tag
46 var $svg_style; // array contenant les style de groupes du svg
48 var $svg_string; // String contenant le tracage du svg en lui même.
50 var $txt_data; // array - holds string info to write txt to image
52 var $txt_style; // array - current text style
54 var $mpdf_ref;
56 var $xbase;
58 var $ybase;
60 var $svg_error;
62 var $subPathInit;
64 var $spxstart;
66 var $spystart;
68 var $kp; // convert pixels to PDF units
70 var $pathBBox;
72 var $script2lang;
74 var $viet;
76 var $pashto;
78 var $urdu;
80 var $persian;
82 var $sindhi;
84 var $textlength; // mPDF 5.7.4
86 var $texttotallength; // mPDF 5.7.4
88 var $textoutput; // mPDF 5.7.4
90 var $textanchor; // mPDF 5.7.4
92 var $textXorigin; // mPDF 5.7.4
94 var $textYorigin; // mPDF 5.7.4
96 var $textjuststarted; // mPDF 5.7.4
98 var $intext; // mPDF 5.7.4
100 public function __construct(mPDF $mpdf)
102 $this->svg_font = array(); // mPDF 6
103 $this->svg_gradient = array();
104 $this->svg_shadinglist = array();
105 $this->txt_data = array();
106 $this->svg_string = '';
107 $this->svg_info = array();
108 $this->svg_attribs = array();
109 $this->xbase = 0;
110 $this->ybase = 0;
111 $this->svg_error = false;
112 $this->subPathInit = false;
113 $this->dashesUsed = false;
114 $this->mpdf_ref = & $mpdf;
116 $this->textlength = 0; // mPDF 5.7.4
117 $this->texttotallength = 0; // mPDF 5.7.4
118 $this->textoutput = ''; // mPDF 5.7.4
119 $this->textanchor = 'start'; // mPDF 5.7.4
120 $this->textXorigin = 0; // mPDF 5.7.4
121 $this->textYorigin = 0; // mPDF 5.7.4
122 $this->textjuststarted = false; // mPDF 5.7.4
123 $this->intext = false; // mPDF 5.7.4
125 $this->kp = 72 / $mpdf->img_dpi; // constant To convert pixels to pts/PDF units
126 $this->kf = 1; // constant To convert font size if re-mapped
127 $this->pathBBox = array();
129 $this->svg_style = array(
130 array(
131 'fill' => 'black',
132 'fill-opacity' => 1, // remplissage opaque par defaut
133 'fill-rule' => 'nonzero', // mode de remplissage par defaut
134 'stroke' => 'none', // pas de trait par defaut
135 'stroke-linecap' => 'butt', // style de langle par defaut
136 'stroke-linejoin' => 'miter',
137 'stroke-miterlimit' => 4, // limite de langle par defaut
138 'stroke-opacity' => 1, // trait opaque par defaut
139 'stroke-width' => 1,
140 'stroke-dasharray' => 0,
141 'stroke-dashoffset' => 0,
142 'color' => ''
146 $this->txt_style = array(
147 array(
148 'fill' => 'black', // pas de remplissage par defaut
149 'font-family' => $mpdf->default_font,
150 'font-size' => $mpdf->default_font_size, // ****** this is pts
151 'font-weight' => 'normal', // normal | bold
152 'font-style' => 'normal', // italic | normal
153 'text-anchor' => 'start', // alignment: start, middle, end
154 'fill-opacity' => 1, // remplissage opaque par defaut
155 'fill-rule' => 'nonzero', // mode de remplissage par defaut
156 'stroke' => 'none', // pas de trait par defaut
157 'stroke-opacity' => 1, // trait opaque par defaut
158 'stroke-width' => 1,
159 'color' => ''
164 // mPDF 5.7.4 Embedded image
165 function svgImage($attribs)
167 // x and y are coordinates
168 $x = (isset($attribs['x']) ? $attribs['x'] : 0);
169 $y = (isset($attribs['y']) ? $attribs['y'] : 0);
170 // preserveAspectRatio
171 $par = (isset($attribs['preserveAspectRatio']) ? $attribs['preserveAspectRatio'] : 'xMidYMid meet');
172 // width and height are <lengths> - Required attributes
173 $wset = (isset($attribs['width']) ? $attribs['width'] : 0);
174 $hset = (isset($attribs['height']) ? $attribs['height'] : 0);
175 $w = $this->mpdf_ref->ConvertSize($wset, $this->svg_info['w'] * (25.4 / $this->mpdf_ref->dpi), $this->mpdf_ref->FontSize, false);
176 $h = $this->mpdf_ref->ConvertSize($hset, $this->svg_info['h'] * (25.4 / $this->mpdf_ref->dpi), $this->mpdf_ref->FontSize, false);
177 if ($w == 0 || $h == 0) {
178 return;
180 // Convert to pixels = SVG units
181 $w *= 1 / (25.4 / $this->mpdf_ref->dpi);
182 $h *= 1 / (25.4 / $this->mpdf_ref->dpi);
184 $srcpath = $attribs['xlink:href'];
185 $orig_srcpath = '';
186 if (trim($srcpath) != '' && substr($srcpath, 0, 4) == 'var:') {
187 $orig_srcpath = $srcpath;
188 $this->mpdf_ref->GetFullPath($srcpath);
191 // Image file (does not allow vector images i.e. WMF/SVG)
192 // mPDF 6 Added $this->mpdf_ref->interpolateImages
193 $info = $this->mpdf_ref->_getImage($srcpath, true, false, $orig_srcpath, $this->mpdf_ref->interpolateImages);
194 if (!$info)
195 return;
197 // x,y,w,h define the reference rectangle
198 $img_h = $h;
199 $img_w = $w;
200 $img_x = $x;
201 $img_y = $y;
202 $meetOrSlice = 'meet';
204 // preserveAspectRatio
205 $ar = preg_split('/\s+/', strtolower($par));
206 if ($ar[0] != 'none') { // If "none" need to do nothing
207 // Force uniform scaling
208 if (isset($ar[1]) && $ar[1] == 'slice') {
209 $meetOrSlice = 'slice';
210 } else {
211 $meetOrSlice = 'meet';
213 if ($info['h'] / $info['w'] > $h / $w) {
214 if ($meetOrSlice == 'meet') { // the entire viewBox is visible within the viewport
215 $img_w = $img_h * $info['w'] / $info['h'];
216 } else { // the entire viewport is covered by the viewBox
217 $img_h = $img_w * $info['h'] / $info['w'];
219 } else if ($info['h'] / $info['w'] < $h / $w) {
220 if ($meetOrSlice == 'meet') { // the entire viewBox is visible within the viewport
221 $img_h = $img_w * $info['h'] / $info['w'];
222 } else { // the entire viewport is covered by the viewBox
223 $img_w = $img_h * $info['w'] / $info['h'];
226 if ($ar[0] == 'xminymin') {
227 // do nothing to x
228 // do nothing to y
229 } else if ($ar[0] == 'xmidymin') {
230 $img_x += $w / 2 - $img_w / 2; // xMid
231 // do nothing to y
232 } else if ($ar[0] == 'xmaxymin') {
233 $img_x += $w - $img_w; // xMax
234 // do nothing to y
235 } else if ($ar[0] == 'xminymid') {
236 // do nothing to x
237 $img_y += $h / 2 - $img_h / 2; // yMid
238 } else if ($ar[0] == 'xmaxymid') {
239 $img_x += $w - $img_w; // xMax
240 $img_y += $h / 2 - $img_h / 2; // yMid
241 } else if ($ar[0] == 'xminymax') {
242 // do nothing to x
243 $img_y += $h - $img_h; // yMax
244 } else if ($ar[0] == 'xmidymax') {
245 $img_x += $w / 2 - $img_w / 2; // xMid
246 $img_y += $h - $img_h; // yMax
247 } else if ($ar[0] == 'xmaxymax') {
248 $img_x += $w - $img_w; // xMax
249 $img_y += $h - $img_h; // yMax
250 } else { // xMidYMid (the default)
251 $img_x += $w / 2 - $img_w / 2; // xMid
252 $img_y += $h / 2 - $img_h / 2; // yMid
256 // Output
257 if ($meetOrSlice == 'slice') { // need to add a clipping path to reference rectangle
258 $s = ' q 0 w '; // Line width=0
259 $s .= sprintf('%.3F %.3F m ', ($x) * $this->kp, (-($y + $h)) * $this->kp); // start point TL before the arc
260 $s .= sprintf('%.3F %.3F l ', ($x) * $this->kp, (-($y)) * $this->kp); // line to BL
261 $s .= sprintf('%.3F %.3F l ', ($x + $w) * $this->kp, (-($y)) * $this->kp); // line to BR
262 $s .= sprintf('%.3F %.3F l ', ($x + $w) * $this->kp, (-($y + $h)) * $this->kp); // line to TR
263 $s .= sprintf('%.3F %.3F l ', ($x) * $this->kp, (-($y + $h)) * $this->kp); // line to TL
264 $s .= ' W n '; // Ends path no-op & Sets the clipping path
265 $this->svgWriteString($s);
268 $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']);
269 $this->svgWriteString($outstring);
271 if ($meetOrSlice == 'slice') { // need to end clipping path
272 $this->svgWriteString(' Q ');
276 function svgGradient($gradient_info, $attribs, $element)
278 $n = count($this->mpdf_ref->gradients) + 1;
280 // Get bounding dimensions of element
281 $w = 100;
282 $h = 100;
283 $x_offset = 0;
284 $y_offset = 0;
285 if ($element == 'rect') {
286 $w = $attribs['width'];
287 $h = $attribs['height'];
288 $x_offset = $attribs['x'];
289 $y_offset = $attribs['y'];
290 } else if ($element == 'ellipse') {
291 $w = $attribs['rx'] * 2;
292 $h = $attribs['ry'] * 2;
293 $x_offset = $attribs['cx'] - $attribs['rx'];
294 $y_offset = $attribs['cy'] - $attribs['ry'];
295 } else if ($element == 'circle') {
296 $w = $attribs['r'] * 2;
297 $h = $attribs['r'] * 2;
298 $x_offset = $attribs['cx'] - $attribs['r'];
299 $y_offset = $attribs['cy'] - $attribs['r'];
300 } else if ($element == 'polygon') {
301 $pts = preg_split('/[ ,]+/', trim($attribs['points']));
302 $maxr = $maxb = 0;
303 $minl = $mint = 999999;
304 for ($i = 0; $i < count($pts); $i++) {
305 if ($i % 2 == 0) { // x values
306 $minl = min($minl, $pts[$i]);
307 $maxr = max($maxr, $pts[$i]);
308 } else { // y values
309 $mint = min($mint, $pts[$i]);
310 $maxb = max($maxb, $pts[$i]);
313 $w = $maxr - $minl;
314 $h = $maxb - $mint;
315 $x_offset = $minl;
316 $y_offset = $mint;
317 } else if ($element == 'path') {
318 if (is_array($this->pathBBox) && $this->pathBBox[2] > 0) {
319 $w = $this->pathBBox[2];
320 $h = $this->pathBBox[3];
321 $x_offset = $this->pathBBox[0];
322 $y_offset = $this->pathBBox[1];
323 } else {
324 preg_match_all('/([a-z]|[A-Z])([ ,\-.\d]+)*/', $attribs['d'], $commands, PREG_SET_ORDER);
325 $maxr = $maxb = 0;
326 $minl = $mint = 999999;
327 foreach ($commands as $c) {
328 if (count($c) == 3) {
329 list($tmp, $cmd, $arg) = $c;
330 if ($cmd == 'M' || $cmd == 'L' || $cmd == 'C' || $cmd == 'S' || $cmd == 'Q' || $cmd == 'T') {
331 $pts = preg_split('/[ ,]+/', trim($arg));
332 for ($i = 0; $i < count($pts); $i++) {
333 if ($i % 2 == 0) { // x values
334 $minl = min($minl, $pts[$i]);
335 $maxr = max($maxr, $pts[$i]);
336 } else { // y values
337 $mint = min($mint, $pts[$i]);
338 $maxb = max($maxb, $pts[$i]);
342 if ($cmd == 'H') { // sets new x
343 $minl = min($minl, $arg);
344 $maxr = max($maxr, $arg);
346 if ($cmd == 'V') { // sets new y
347 $mint = min($mint, $arg);
348 $maxb = max($maxb, $arg);
352 $w = $maxr - $minl;
353 $h = $maxb - $mint;
354 $x_offset = $minl;
355 $y_offset = $mint;
358 if (!$w || $w == -999999) {
359 $w = 100;
361 if (!$h || $h == -999999) {
362 $h = 100;
364 if ($x_offset == 999999) {
365 $x_offset = 0;
367 if ($y_offset == 999999) {
368 $y_offset = 0;
371 // TRANSFORMATIONS
372 $transformations = '';
373 if (isset($gradient_info['transform'])) {
374 preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is', $gradient_info['transform'], $m);
375 if (count($m[0])) {
376 for ($i = 0; $i < count($m[0]); $i++) {
377 $c = strtolower($m[1][$i]);
378 $v = trim($m[2][$i]);
379 $vv = preg_split('/[ ,]+/', $v);
380 if ($c == 'matrix' && count($vv) == 6) {
381 // Note angle of rotation is reversed (from SVG to PDF), so vv[1] and vv[2] are negated
382 // cf svgDefineStyle()
383 $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);
384 } else if ($c == 'translate' && count($vv)) {
385 $tm[4] = $vv[0];
386 if (count($vv) == 2) {
387 $t_y = -$vv[1];
388 } else {
389 $t_y = 0;
391 $tm[5] = $t_y;
392 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $tm[4] * $this->kp, $tm[5] * $this->kp);
393 } else if ($c == 'scale' && count($vv)) {
394 if (count($vv) == 2) {
395 $s_y = $vv[1];
396 } else {
397 $s_y = $vv[0];
399 $tm[0] = $vv[0];
400 $tm[3] = $s_y;
401 $transformations .= sprintf(' %.3F 0 0 %.3F 0 0 cm ', $tm[0], $tm[3]);
402 } else if ($c == 'rotate' && count($vv)) {
403 $tm[0] = cos(deg2rad(-$vv[0]));
404 $tm[1] = sin(deg2rad(-$vv[0]));
405 $tm[2] = -$tm[1];
406 $tm[3] = $tm[0];
407 if (count($vv) == 3) {
408 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $vv[1] * $this->kp, -$vv[2] * $this->kp);
410 $transformations .= sprintf(' %.3F %.3F %.3F %.3F 0 0 cm ', $tm[0], $tm[1], $tm[2], $tm[3]);
411 if (count($vv) == 3) {
412 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', -$vv[1] * $this->kp, $vv[2] * $this->kp);
414 } else if ($c == 'skewx' && count($vv)) {
415 $tm[2] = tan(deg2rad(-$vv[0]));
416 $transformations .= sprintf(' 1 0 %.3F 1 0 0 cm ', $tm[2]);
417 } else if ($c == 'skewy' && count($vv)) {
418 $tm[1] = tan(deg2rad(-$vv[0]));
419 $transformations .= sprintf(' 1 %.3F 0 1 0 0 cm ', $tm[1]);
426 $return = "";
428 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
429 if ($transformations) {
430 $return .= $transformations;
433 $spread = 'P'; // pad
434 if (isset($gradient_info['spread'])) {
435 if (strtolower($gradient_info['spread']) == 'reflect') {
436 $spread = 'F';
437 } // reflect
438 else if (strtolower($gradient_info['spread']) == 'repeat') {
439 $spread = 'R';
440 } // repeat
444 for ($i = 0; $i < (count($gradient_info['color'])); $i++) {
445 if (stristr($gradient_info['color'][$i]['offset'], '%') !== false) {
446 $gradient_info['color'][$i]['offset'] = ($gradient_info['color'][$i]['offset'] + 0) / 100;
448 if (isset($gradient_info['color'][($i + 1)]['offset']) && stristr($gradient_info['color'][($i + 1)]['offset'], '%') !== false) {
449 $gradient_info['color'][($i + 1)]['offset'] = ($gradient_info['color'][($i + 1)]['offset'] + 0) / 100;
451 if ($gradient_info['color'][$i]['offset'] < 0) {
452 $gradient_info['color'][$i]['offset'] = 0;
454 if ($gradient_info['color'][$i]['offset'] > 1) {
455 $gradient_info['color'][$i]['offset'] = 1;
457 if ($i > 0) {
458 if ($gradient_info['color'][$i]['offset'] < $gradient_info['color'][($i - 1)]['offset']) {
459 $gradient_info['color'][$i]['offset'] = $gradient_info['color'][($i - 1)]['offset'];
464 if (isset($gradient_info['color'][0]['offset']) && $gradient_info['color'][0]['offset'] > 0) {
465 array_unshift($gradient_info['color'], $gradient_info['color'][0]);
466 $gradient_info['color'][0]['offset'] = 0;
468 $ns = count($gradient_info['color']);
469 if (isset($gradient_info['color'][($ns - 1)]['offset']) && $gradient_info['color'][($ns - 1)]['offset'] < 1) {
470 $gradient_info['color'][] = $gradient_info['color'][($ns - 1)];
471 $gradient_info['color'][($ns)]['offset'] = 1;
473 $ns = count($gradient_info['color']);
478 if ($gradient_info['type'] == 'linear') {
479 // mPDF 4.4.003
480 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
481 if (isset($gradient_info['info']['x1'])) {
482 $gradient_info['info']['x1'] = ($gradient_info['info']['x1'] - $x_offset) / $w;
484 if (isset($gradient_info['info']['y1'])) {
485 $gradient_info['info']['y1'] = ($gradient_info['info']['y1'] - $y_offset) / $h;
487 if (isset($gradient_info['info']['x2'])) {
488 $gradient_info['info']['x2'] = ($gradient_info['info']['x2'] - $x_offset) / $w;
490 if (isset($gradient_info['info']['y2'])) {
491 $gradient_info['info']['y2'] = ($gradient_info['info']['y2'] - $y_offset) / $h;
494 if (isset($gradient_info['info']['x1'])) {
495 $x1 = $gradient_info['info']['x1'];
496 } else {
497 $x1 = 0;
499 if (isset($gradient_info['info']['y1'])) {
500 $y1 = $gradient_info['info']['y1'];
501 } else {
502 $y1 = 0;
504 if (isset($gradient_info['info']['x2'])) {
505 $x2 = $gradient_info['info']['x2'];
506 } else {
507 $x2 = 1;
509 if (isset($gradient_info['info']['y2'])) {
510 $y2 = $gradient_info['info']['y2'];
511 } else {
512 $y2 = 0;
513 } // mPDF 6
515 if (stristr($x1, '%') !== false) {
516 $x1 = ($x1 + 0) / 100;
518 if (stristr($x2, '%') !== false) {
519 $x2 = ($x2 + 0) / 100;
521 if (stristr($y1, '%') !== false) {
522 $y1 = ($y1 + 0) / 100;
524 if (stristr($y2, '%') !== false) {
525 $y2 = ($y2 + 0) / 100;
528 // mPDF 5.0.042
529 $bboxw = $w;
530 $bboxh = $h;
531 $usex = $x_offset;
532 $usey = $y_offset;
533 $usew = $bboxw;
534 $useh = $bboxh;
535 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
536 $angle = rad2deg(atan2(($gradient_info['info']['y2'] - $gradient_info['info']['y1']), ($gradient_info['info']['x2'] - $gradient_info['info']['x1'])));
537 if ($angle < 0) {
538 $angle += 360;
539 } else if ($angle > 360) {
540 $angle -= 360;
542 if ($angle != 0 && $angle != 360 && $angle != 90 && $angle != 180 && $angle != 270) {
543 if ($w >= $h) {
544 $y1 *= $h / $w;
545 $y2 *= $h / $w;
546 $usew = $useh = $bboxw;
547 } else {
548 $x1 *= $w / $h;
549 $x2 *= $w / $h;
550 $usew = $useh = $bboxh;
554 $a = $usew; // width
555 $d = -$useh; // height
556 $e = $usex; // x- offset
557 $f = -$usey; // -y-offset
559 $return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a * $this->kp, $d * $this->kp, $e * $this->kp, $f * $this->kp);
561 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'objectboundingbox') {
562 if ($transformations) {
563 $return .= $transformations;
567 $trans = false;
569 if ($spread == 'R' || $spread == 'F') { // Repeat / Reflect
570 $offs = array();
571 for ($i = 0; $i < $ns; $i++) {
572 $offs[$i] = $gradient_info['color'][$i]['offset'];
574 $gp = 0;
575 $inside = true;
576 while ($inside) {
577 $gp++;
578 for ($i = 0; $i < $ns; $i++) {
579 if ($spread == 'F' && ($gp % 2) == 1) { // Reflect
580 $gradient_info['color'][(($ns * $gp) + $i)] = $gradient_info['color'][(($ns * ($gp - 1)) + ($ns - $i - 1))];
581 $tmp = $gp + (1 - $offs[($ns - $i - 1)]);
582 $gradient_info['color'][(($ns * $gp) + $i)]['offset'] = $tmp;
583 } else { // Reflect
584 $gradient_info['color'][(($ns * $gp) + $i)] = $gradient_info['color'][$i];
585 $tmp = $gp + $offs[$i];
586 $gradient_info['color'][(($ns * $gp) + $i)]['offset'] = $tmp;
588 // IF STILL INSIDE BOX OR STILL VALID
589 // Point on axis to test
590 $px1 = $x1 + ($x2 - $x1) * $tmp;
591 $py1 = $y1 + ($y2 - $y1) * $tmp;
592 // Get perpendicular axis
593 $alpha = atan2($y2 - $y1, $x2 - $x1);
594 $alpha += M_PI / 2; // rotate 90 degrees
595 // Get arbitrary point to define line perpendicular to axis
596 $px2 = $px1 + cos($alpha);
597 $py2 = $py1 + sin($alpha);
599 $res1 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 0, 1); // $x=0 vert axis
600 $res2 = _testIntersect($px1, $py1, $px2, $py2, 1, 0, 1, 1); // $x=1 vert axis
601 $res3 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 1, 0); // $y=0 horiz axis
602 $res4 = _testIntersect($px1, $py1, $px2, $py2, 0, 1, 1, 1); // $y=1 horiz axis
603 if (!$res1 && !$res2 && !$res3 && !$res4) {
604 $inside = false;
609 $inside = true;
610 $gp = 0;
611 while ($inside) {
612 $gp++;
613 $newarr = array();
614 for ($i = 0; $i < $ns; $i++) {
615 if ($spread == 'F') { // Reflect
616 $newarr[$i] = $gradient_info['color'][($ns - $i - 1)];
617 if (($gp % 2) == 1) {
618 $tmp = -$gp + (1 - $offs[($ns - $i - 1)]);
619 $newarr[$i]['offset'] = $tmp;
620 } else {
621 $tmp = -$gp + $offs[$i];
622 $newarr[$i]['offset'] = $tmp;
624 } else { // Reflect
625 $newarr[$i] = $gradient_info['color'][$i];
626 $tmp = -$gp + $offs[$i];
627 $newarr[$i]['offset'] = $tmp;
630 // IF STILL INSIDE BOX OR STILL VALID
631 // Point on axis to test
632 $px1 = $x1 + ($x2 - $x1) * $tmp;
633 $py1 = $y1 + ($y2 - $y1) * $tmp;
634 // Get perpendicular axis
635 $alpha = atan2($y2 - $y1, $x2 - $x1);
636 $alpha += M_PI / 2; // rotate 90 degrees
637 // Get arbitrary point to define line perpendicular to axis
638 $px2 = $px1 + cos($alpha);
639 $py2 = $py1 + sin($alpha);
641 $res1 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 0, 1); // $x=0 vert axis
642 $res2 = _testIntersect($px1, $py1, $px2, $py2, 1, 0, 1, 1); // $x=1 vert axis
643 $res3 = _testIntersect($px1, $py1, $px2, $py2, 0, 0, 1, 0); // $y=0 horiz axis
644 $res4 = _testIntersect($px1, $py1, $px2, $py2, 0, 1, 1, 1); // $y=1 horiz axis
645 if (!$res1 && !$res2 && !$res3 && !$res4) {
646 $inside = false;
649 for ($i = ($ns - 1); $i >= 0; $i--) {
650 if (isset($newarr[$i]['offset']))
651 array_unshift($gradient_info['color'], $newarr[$i]);
656 // Gradient STOPs
657 $stops = count($gradient_info['color']);
658 if ($stops < 2) {
659 return '';
662 $range = $gradient_info['color'][count($gradient_info['color']) - 1]['offset'] - $gradient_info['color'][0]['offset'];
663 $min = $gradient_info['color'][0]['offset'];
665 for ($i = 0; $i < ($stops); $i++) {
666 if (!$gradient_info['color'][$i]['color']) {
667 if ($gradient_info['colorspace'] == 'RGB')
668 $gradient_info['color'][$i]['color'] = '0 0 0';
669 else if ($gradient_info['colorspace'] == 'Gray')
670 $gradient_info['color'][$i]['color'] = '0';
671 else if ($gradient_info['colorspace'] == 'CMYK')
672 $gradient_info['color'][$i]['color'] = '1 1 1 1';
674 $offset = ($gradient_info['color'][$i]['offset'] - $min) / $range;
675 $this->mpdf_ref->gradients[$n]['stops'][] = array(
676 'col' => $gradient_info['color'][$i]['color'],
677 'opacity' => $gradient_info['color'][$i]['opacity'],
678 'offset' => $offset);
679 if ($gradient_info['color'][$i]['opacity'] < 1) {
680 $trans = true;
683 $grx1 = $x1 + ($x2 - $x1) * $gradient_info['color'][0]['offset'];
684 $gry1 = $y1 + ($y2 - $y1) * $gradient_info['color'][0]['offset'];
685 $grx2 = $x1 + ($x2 - $x1) * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
686 $gry2 = $y1 + ($y2 - $y1) * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
688 $this->mpdf_ref->gradients[$n]['coords'] = array($grx1, $gry1, $grx2, $gry2);
690 $this->mpdf_ref->gradients[$n]['colorspace'] = $gradient_info['colorspace'];
692 $this->mpdf_ref->gradients[$n]['type'] = 2;
693 $this->mpdf_ref->gradients[$n]['fo'] = true;
695 $this->mpdf_ref->gradients[$n]['extend'] = array('true', 'true');
696 if ($trans) {
697 $this->mpdf_ref->gradients[$n]['trans'] = true;
698 $return .= ' /TGS' . ($n) . ' gs ';
700 $return .= ' /Sh' . ($n) . ' sh ';
701 $return .= " Q\n";
702 } else if ($gradient_info['type'] == 'radial') {
703 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
704 if ($w > $h) {
705 $h = $w;
706 } else {
707 $w = $h;
709 if (isset($gradient_info['info']['x0'])) {
710 $gradient_info['info']['x0'] = ($gradient_info['info']['x0'] - $x_offset) / $w;
712 if (isset($gradient_info['info']['y0'])) {
713 $gradient_info['info']['y0'] = ($gradient_info['info']['y0'] - $y_offset) / $h;
715 if (isset($gradient_info['info']['x1'])) {
716 $gradient_info['info']['x1'] = ($gradient_info['info']['x1'] - $x_offset) / $w;
718 if (isset($gradient_info['info']['y1'])) {
719 $gradient_info['info']['y1'] = ($gradient_info['info']['y1'] - $y_offset) / $h;
721 if (isset($gradient_info['info']['r'])) {
722 $gradient_info['info']['rx'] = $gradient_info['info']['r'] / $w;
724 if (isset($gradient_info['info']['r'])) {
725 $gradient_info['info']['ry'] = $gradient_info['info']['r'] / $h;
729 if (isset($gradient_info['info']['x0'])) {
730 $x0 = $gradient_info['info']['x0'];
731 } else {
732 $x0 = 0.5;
734 if (isset($gradient_info['info']['y0'])) {
735 $y0 = $gradient_info['info']['y0'];
736 } else {
737 $y0 = 0.5;
739 if (isset($gradient_info['info']['rx'])) {
740 $rx = $gradient_info['info']['rx'];
741 } else if (isset($gradient_info['info']['r'])) {
742 $rx = $gradient_info['info']['r'];
743 } else {
744 $rx = 0.5;
746 if (isset($gradient_info['info']['ry'])) {
747 $ry = $gradient_info['info']['ry'];
748 } else if (isset($gradient_info['info']['r'])) {
749 $ry = $gradient_info['info']['r'];
750 } else {
751 $ry = 0.5;
753 if (isset($gradient_info['info']['x1'])) {
754 $x1 = $gradient_info['info']['x1'];
755 } else {
756 $x1 = $x0;
758 if (isset($gradient_info['info']['y1'])) {
759 $y1 = $gradient_info['info']['y1'];
760 } else {
761 $y1 = $y0;
764 if (stristr($x1, '%') !== false) {
765 $x1 = ($x1 + 0) / 100;
767 if (stristr($x0, '%') !== false) {
768 $x0 = ($x0 + 0) / 100;
770 if (stristr($y1, '%') !== false) {
771 $y1 = ($y1 + 0) / 100;
773 if (stristr($y0, '%') !== false) {
774 $y0 = ($y0 + 0) / 100;
776 if (stristr($rx, '%') !== false) {
777 $rx = ($rx + 0) / 100;
779 if (stristr($ry, '%') !== false) {
780 $ry = ($ry + 0) / 100;
783 $bboxw = $w;
784 $bboxh = $h;
785 $usex = $x_offset;
786 $usey = $y_offset;
787 $usew = $bboxw;
788 $useh = $bboxh;
789 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
790 $angle = rad2deg(atan2(($gradient_info['info']['y0'] - $gradient_info['info']['y1']), ($gradient_info['info']['x0'] - $gradient_info['info']['x1'])));
791 if ($angle < 0) {
792 $angle += 360;
793 } else if ($angle > 360) {
794 $angle -= 360;
796 if ($angle != 0 && $angle != 360 && $angle != 90 && $angle != 180 && $angle != 270) {
797 if ($w >= $h) {
798 $y1 *= $h / $w;
799 $y0 *= $h / $w;
800 $rx *= $h / $w;
801 $ry *= $h / $w;
802 $usew = $useh = $bboxw;
803 } else {
804 $x1 *= $w / $h;
805 $x0 *= $w / $h;
806 $rx *= $w / $h;
807 $ry *= $w / $h;
808 $usew = $useh = $bboxh;
812 $a = $usew; // width
813 $d = -$useh; // height
814 $e = $usex; // x- offset
815 $f = -$usey; // -y-offset
817 $r = $rx;
820 $return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a * $this->kp, $d * $this->kp, $e * $this->kp, $f * $this->kp);
822 // mPDF 5.0.039
823 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'objectboundingbox') {
824 if ($transformations) {
825 $return .= $transformations;
829 // mPDF 5.7.4
830 // x1 and y1 (fx, fy) should be inside the circle defined by x0 y0 (cx, cy)
831 // "If the point defined by fx and fy lies outside the circle defined by cx, cy and r, then the user agent shall set
832 // the focal point to the intersection of the line from (cx, cy) to (fx, fy) with the circle defined by cx, cy and r."
833 while (pow(($x1 - $x0), 2) + pow(($y1 - $y0), 2) >= pow($r, 2)) {
834 // Gradually move along fx,fy towards cx,cy in 100'ths until meets criteria
835 $x1 -= ($x1 - $x0) / 100;
836 $y1 -= ($y1 - $y0) / 100;
840 if ($spread == 'R' || $spread == 'F') { // Repeat / Reflect
841 $offs = array();
842 for ($i = 0; $i < $ns; $i++) {
843 $offs[$i] = $gradient_info['color'][$i]['offset'];
845 $gp = 0;
846 $inside = true;
847 while ($inside) {
848 $gp++;
849 for ($i = 0; $i < $ns; $i++) {
850 if ($spread == 'F' && ($gp % 2) == 1) { // Reflect
851 $gradient_info['color'][(($ns * $gp) + $i)] = $gradient_info['color'][(($ns * ($gp - 1)) + ($ns - $i - 1))];
852 $tmp = $gp + (1 - $offs[($ns - $i - 1)]);
853 $gradient_info['color'][(($ns * $gp) + $i)]['offset'] = $tmp;
854 } else { // Reflect
855 $gradient_info['color'][(($ns * $gp) + $i)] = $gradient_info['color'][$i];
856 $tmp = $gp + $offs[$i];
857 $gradient_info['color'][(($ns * $gp) + $i)]['offset'] = $tmp;
859 // IF STILL INSIDE BOX OR STILL VALID
860 // TEST IF circle (perimeter) intersects with
861 // or is enclosed
862 // Point on axis to test
863 $px = $x1 + ($x0 - $x1) * $tmp;
864 $py = $y1 + ($y0 - $y1) * $tmp;
865 $pr = $r * $tmp;
866 $res = _testIntersectCircle($px, $py, $pr);
867 if (!$res) {
868 $inside = false;
874 // Gradient STOPs
875 $stops = count($gradient_info['color']);
876 if ($stops < 2) {
877 return '';
880 $range = $gradient_info['color'][count($gradient_info['color']) - 1]['offset'] - $gradient_info['color'][0]['offset'];
881 $min = $gradient_info['color'][0]['offset'];
883 for ($i = 0; $i < ($stops); $i++) {
884 if (!$gradient_info['color'][$i]['color']) {
885 if ($gradient_info['colorspace'] == 'RGB')
886 $gradient_info['color'][$i]['color'] = '0 0 0';
887 else if ($gradient_info['colorspace'] == 'Gray')
888 $gradient_info['color'][$i]['color'] = '0';
889 else if ($gradient_info['colorspace'] == 'CMYK')
890 $gradient_info['color'][$i]['color'] = '1 1 1 1';
892 $offset = ($gradient_info['color'][$i]['offset'] - $min) / $range;
893 $this->mpdf_ref->gradients[$n]['stops'][] = array(
894 'col' => $gradient_info['color'][$i]['color'],
895 'opacity' => $gradient_info['color'][$i]['opacity'],
896 'offset' => $offset);
897 if ($gradient_info['color'][$i]['opacity'] < 1) {
898 $trans = true;
901 $grx1 = $x1 + ($x0 - $x1) * $gradient_info['color'][0]['offset'];
902 $gry1 = $y1 + ($y0 - $y1) * $gradient_info['color'][0]['offset'];
903 $grx2 = $x1 + ($x0 - $x1) * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
904 $gry2 = $y1 + ($y0 - $y1) * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
905 $grir = $r * $gradient_info['color'][0]['offset'];
906 $grr = $r * $gradient_info['color'][count($gradient_info['color']) - 1]['offset'];
908 $this->mpdf_ref->gradients[$n]['coords'] = array($grx1, $gry1, $grx2, $gry2, abs($grr), abs($grir));
910 $this->mpdf_ref->gradients[$n]['colorspace'] = $gradient_info['colorspace'];
912 $this->mpdf_ref->gradients[$n]['type'] = 3;
913 $this->mpdf_ref->gradients[$n]['fo'] = true;
915 $this->mpdf_ref->gradients[$n]['extend'] = array('true', 'true');
916 if (isset($trans) && $trans) {
917 $this->mpdf_ref->gradients[$n]['trans'] = true;
918 $return .= ' /TGS' . ($n) . ' gs ';
920 $return .= ' /Sh' . ($n) . ' sh ';
921 $return .= " Q\n";
924 return $return;
927 function svgOffset($attribs)
929 // save all <svg> tag attributes
930 $this->svg_attribs = $attribs;
931 if (isset($this->svg_attribs['viewBox'])) {
932 $vb = preg_split('/\s+/is', trim($this->svg_attribs['viewBox']));
933 if (count($vb) == 4) {
934 $this->svg_info['x'] = $vb[0];
935 $this->svg_info['y'] = $vb[1];
936 $this->svg_info['w'] = $vb[2];
937 $this->svg_info['h'] = $vb[3];
938 // return;
941 $svg_w = 0;
942 $svg_h = 0;
943 if (isset($attribs['width']) && $attribs['width'])
944 $svg_w = $this->mpdf_ref->ConvertSize($attribs['width']); // mm (interprets numbers as pixels)
945 if (isset($attribs['height']) && $attribs['height'])
946 $svg_h = $this->mpdf_ref->ConvertSize($attribs['height']); // mm
949 ///*
950 // mPDF 5.0.005
951 if (isset($this->svg_info['w']) && $this->svg_info['w']) { // if 'w' set by viewBox
952 if ($svg_w) { // if width also set, use these values to determine to set size of "pixel"
953 $this->kp *= ($svg_w / 0.2645) / $this->svg_info['w'];
954 $this->kf = ($svg_w / 0.2645) / $this->svg_info['w'];
955 } else if ($svg_h) {
956 $this->kp *= ($svg_h / 0.2645) / $this->svg_info['h'];
957 $this->kf = ($svg_h / 0.2645) / $this->svg_info['h'];
959 return;
961 //*/
962 // Added to handle file without height or width specified
963 if (!$svg_w && !$svg_h) {
964 $svg_w = $svg_h = $this->mpdf_ref->blk[$this->mpdf_ref->blklvl]['inner_width'];
965 } // DEFAULT
966 if (!$svg_w) {
967 $svg_w = $svg_h;
969 if (!$svg_h) {
970 $svg_h = $svg_w;
973 $this->svg_info['x'] = 0;
974 $this->svg_info['y'] = 0;
975 $this->svg_info['w'] = $svg_w / 0.2645; // mm->pixels
976 $this->svg_info['h'] = $svg_h / 0.2645; // mm->pixels
980 // check if points are within svg, if not, set to max
981 function svg_overflow($x, $y)
983 $x2 = $x;
984 $y2 = $y;
985 if (isset($this->svg_attribs['overflow'])) {
986 if ($this->svg_attribs['overflow'] == 'hidden') {
987 // Not sure if this is supposed to strip off units, but since I dont use any I will omlt this step
988 $svg_w = preg_replace("/([0-9\.]*)(.*)/i", "$1", $this->svg_attribs['width']);
989 $svg_h = preg_replace("/([0-9\.]*)(.*)/i", "$1", $this->svg_attribs['height']);
991 // $xmax = floor($this->svg_attribs['width']);
992 $xmax = floor($svg_w);
993 $xmin = 0;
994 // $ymax = floor(($this->svg_attribs['height'] * -1));
995 $ymax = floor(($svg_h * -1));
996 $ymin = 0;
998 if ($x > $xmax)
999 $x2 = $xmax; // right edge
1000 if ($x < $xmin)
1001 $x2 = $xmin; // left edge
1002 if ($y < $ymax)
1003 $y2 = $ymax; // bottom
1004 if ($y > $ymin)
1005 $y2 = $ymin; // top
1010 return array('x' => $x2, 'y' => $y2);
1013 function svgDefineStyle($critere_style)
1016 $tmp = count($this->svg_style) - 1;
1017 $current_style = $this->svg_style[$tmp];
1019 unset($current_style['transformations']);
1021 // TRANSFORM SCALE
1022 $transformations = '';
1023 if (isset($critere_style['transform'])) {
1024 preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is', $critere_style['transform'], $m);
1025 if (count($m[0])) {
1026 for ($i = 0; $i < count($m[0]); $i++) {
1027 $c = strtolower($m[1][$i]);
1028 $v = trim($m[2][$i]);
1029 $vv = preg_split('/[ ,]+/', $v);
1030 if ($c == 'matrix' && count($vv) == 6) {
1031 // mPDF 5.0.039
1032 // Note angle of rotation is reversed (from SVG to PDF), so vv[1] and vv[2] are negated
1033 $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);
1036 // The long way of doing this??
1037 // need to reverse angle of rotation from SVG to PDF
1038 $sx=sqrt(pow($vv[0],2)+pow($vv[2],2));
1039 if ($vv[0] < 0) { $sx *= -1; } // change sign
1040 $sy=sqrt(pow($vv[1],2)+pow($vv[3],2));
1041 if ($vv[3] < 0) { $sy *= -1; } // change sign
1043 // rotation angle is
1044 $t=atan2($vv[1],$vv[3]);
1045 $t=atan2(-$vv[2],$vv[0]); // Should be the same value or skew has been applied
1047 // Reverse angle
1048 $t *= -1;
1050 // Rebuild matrix
1051 $ma = $sx * cos($t);
1052 $mb = $sy * sin($t);
1053 $mc = -$sx * sin($t);
1054 $md = $sy * cos($t);
1056 // $transformations .= sprintf(' %.3F %.3F %.3F %.3F %.3F %.3F cm ', $ma, $mb, $mc, $md, $vv[4]*$this->kp, -$vv[5]*$this->kp);
1058 } else if ($c == 'translate' && count($vv)) {
1059 $tm[4] = $vv[0];
1060 if (count($vv) == 2) {
1061 $t_y = -$vv[1];
1062 } else {
1063 $t_y = 0;
1065 $tm[5] = $t_y;
1066 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $tm[4] * $this->kp, $tm[5] * $this->kp);
1067 } else if ($c == 'scale' && count($vv)) {
1068 if (count($vv) == 2) {
1069 $s_y = $vv[1];
1070 } else {
1071 $s_y = $vv[0];
1073 $tm[0] = $vv[0];
1074 $tm[3] = $s_y;
1075 $transformations .= sprintf(' %.3F 0 0 %.3F 0 0 cm ', $tm[0], $tm[3]);
1076 } else if ($c == 'rotate' && count($vv)) {
1077 $tm[0] = cos(deg2rad(-$vv[0]));
1078 $tm[1] = sin(deg2rad(-$vv[0]));
1079 $tm[2] = -$tm[1];
1080 $tm[3] = $tm[0];
1081 if (count($vv) == 3) {
1082 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', $vv[1] * $this->kp, -$vv[2] * $this->kp);
1084 $transformations .= sprintf(' %.3F %.3F %.3F %.3F 0 0 cm ', $tm[0], $tm[1], $tm[2], $tm[3]);
1085 if (count($vv) == 3) {
1086 $transformations .= sprintf(' 1 0 0 1 %.3F %.3F cm ', -$vv[1] * $this->kp, $vv[2] * $this->kp);
1088 } else if ($c == 'skewx' && count($vv)) {
1089 $tm[2] = tan(deg2rad(-$vv[0]));
1090 $transformations .= sprintf(' 1 0 %.3F 1 0 0 cm ', $tm[2]);
1091 } else if ($c == 'skewy' && count($vv)) {
1092 $tm[1] = tan(deg2rad(-$vv[0]));
1093 $transformations .= sprintf(' 1 %.3F 0 1 0 0 cm ', $tm[1]);
1097 $current_style['transformations'] = $transformations;
1100 if (isset($critere_style['style'])) {
1101 if (preg_match('/fill:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/i', $critere_style['style'], $m)) { // mPDF 5.7.2
1102 $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);
1103 } else {
1104 $tmp = preg_replace("/(.*)fill:\s*([a-z0-9#_()]*|none)(.*)/i", "$2", $critere_style['style']);
1105 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1106 $current_style['fill'] = $tmp;
1110 // mPDF 5.7.2
1111 if ((preg_match("/[^-]opacity:\s*([a-z0-9.]*|none)/i", $critere_style['style'], $m) ||
1112 preg_match("/^opacity:\s*([a-z0-9.]*|none)/i", $critere_style['style'], $m)) && $m[1] != 'inherit') {
1113 $current_style['fill-opacity'] = $m[1];
1114 $current_style['stroke-opacity'] = $m[1];
1117 $tmp = preg_replace("/(.*)fill-opacity:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
1118 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1119 $current_style['fill-opacity'] = $tmp;
1122 $tmp = preg_replace("/(.*)fill-rule:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1123 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1124 $current_style['fill-rule'] = $tmp;
1127 if (preg_match('/stroke:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/', $critere_style['style'], $m)) {
1128 $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);
1129 } else {
1130 $tmp = preg_replace("/(.*)stroke:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1131 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1132 $current_style['stroke'] = $tmp;
1136 $tmp = preg_replace("/(.*)stroke-linecap:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1137 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1138 $current_style['stroke-linecap'] = $tmp;
1141 $tmp = preg_replace("/(.*)stroke-linejoin:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1142 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1143 $current_style['stroke-linejoin'] = $tmp;
1146 $tmp = preg_replace("/(.*)stroke-miterlimit:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
1147 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1148 $current_style['stroke-miterlimit'] = $tmp;
1151 $tmp = preg_replace("/(.*)stroke-opacity:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
1152 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1153 $current_style['stroke-opacity'] = $tmp;
1156 $tmp = preg_replace("/(.*)stroke-width:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
1157 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1158 $current_style['stroke-width'] = $tmp;
1161 $tmp = preg_replace("/(.*)stroke-dasharray:\s*([a-z0-9., ]*|none)(.*)/i", "$2", $critere_style['style']);
1162 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1163 $current_style['stroke-dasharray'] = $tmp;
1166 $tmp = preg_replace("/(.*)stroke-dashoffset:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
1167 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
1168 $current_style['stroke-dashoffset'] = $tmp;
1171 // mPDF 5.7.2
1172 if (isset($critere_style['opacity']) && $critere_style['opacity'] != 'inherit') {
1173 $current_style['fill-opacity'] = $critere_style['opacity'];
1174 $current_style['stroke-opacity'] = $critere_style['opacity'];
1177 if (isset($critere_style['fill']) && $critere_style['fill'] != 'inherit') {
1178 $current_style['fill'] = $critere_style['fill'];
1181 if (isset($critere_style['fill-opacity']) && $critere_style['fill-opacity'] != 'inherit') {
1182 $current_style['fill-opacity'] = $critere_style['fill-opacity'];
1185 if (isset($critere_style['fill-rule']) && $critere_style['fill-rule'] != 'inherit') {
1186 $current_style['fill-rule'] = $critere_style['fill-rule'];
1189 if (isset($critere_style['stroke']) && $critere_style['stroke'] != 'inherit') {
1190 $current_style['stroke'] = $critere_style['stroke'];
1193 if (isset($critere_style['stroke-linecap']) && $critere_style['stroke-linecap'] != 'inherit') {
1194 $current_style['stroke-linecap'] = $critere_style['stroke-linecap'];
1197 if (isset($critere_style['stroke-linejoin']) && $critere_style['stroke-linejoin'] != 'inherit') {
1198 $current_style['stroke-linejoin'] = $critere_style['stroke-linejoin'];
1201 if (isset($critere_style['stroke-miterlimit']) && $critere_style['stroke-miterlimit'] != 'inherit') {
1202 $current_style['stroke-miterlimit'] = $critere_style['stroke-miterlimit'];
1205 if (isset($critere_style['stroke-opacity']) && $critere_style['stroke-opacity'] != 'inherit') {
1206 $current_style['stroke-opacity'] = $critere_style['stroke-opacity'];
1209 if (isset($critere_style['stroke-width']) && $critere_style['stroke-width'] != 'inherit') {
1210 $current_style['stroke-width'] = $critere_style['stroke-width'];
1213 if (isset($critere_style['stroke-dasharray']) && $critere_style['stroke-dasharray'] != 'inherit') {
1214 $current_style['stroke-dasharray'] = $critere_style['stroke-dasharray'];
1216 if (isset($critere_style['stroke-dashoffset']) && $critere_style['stroke-dashoffset'] != 'inherit') {
1217 $current_style['stroke-dashoffset'] = $critere_style['stroke-dashoffset'];
1220 // Used as indirect setting for currentColor
1221 if (isset($critere_style['color']) && $critere_style['color'] != 'inherit') {
1222 $current_style['color'] = $critere_style['color'];
1225 return $current_style;
1229 // Cette fonction ecrit le style dans le stream svg.
1230 function svgStyle($critere_style, $attribs, $element)
1232 $path_style = '';
1233 $fill_gradient = '';
1234 $w = '';
1235 $style = '';
1236 if (substr_count($critere_style['fill'], 'url') > 0 && $element != 'line') {
1238 // couleur degradé
1239 $id_gradient = preg_replace("/url\(#([\w_]*)\)/i", "$1", $critere_style['fill']);
1240 if ($id_gradient != $critere_style['fill']) {
1241 if (isset($this->svg_gradient[$id_gradient])) {
1242 $fill_gradient = $this->svgGradient($this->svg_gradient[$id_gradient], $attribs, $element);
1243 if ($fill_gradient) {
1244 $path_style = "q ";
1245 $w = "W";
1246 $style .= 'N';
1251 // Used as indirect setting for currentColor
1252 else if (strtolower($critere_style['fill']) == 'currentcolor' && $element != 'line') {
1253 $col = $this->mpdf_ref->ConvertColor($critere_style['color']);
1254 if ($col) {
1255 if ($col{0} == 5) {
1256 $critere_style['fill-opacity'] = ord($col{4} / 100);
1257 } // RGBa
1258 if ($col{0} == 6) {
1259 $critere_style['fill-opacity'] = ord($col{5} / 100);
1260 } // CMYKa
1261 $path_style .= $this->mpdf_ref->SetFColor($col, true) . ' ';
1262 $style .= 'F';
1264 } else if ($critere_style['fill'] != 'none' && $element != 'line') {
1265 $col = $this->mpdf_ref->ConvertColor($critere_style['fill']);
1266 if ($col) {
1267 if ($col{0} == 5) {
1268 $critere_style['fill-opacity'] = ord($col{4} / 100);
1269 } // RGBa
1270 if ($col{0} == 6) {
1271 $critere_style['fill-opacity'] = ord($col{5} / 100);
1272 } // CMYKa
1273 $path_style .= $this->mpdf_ref->SetFColor($col, true) . ' ';
1274 $style .= 'F';
1277 if (substr_count($critere_style['stroke'], 'url') > 0) {
1279 // Cannot put a gradient on a "stroke" in PDF?
1280 $id_gradient = preg_replace("/url\(#([\w_]*)\)/i","$1",$critere_style['stroke']);
1281 if ($id_gradient != $critere_style['stroke']) {
1282 if (isset($this->svg_gradient[$id_gradient])) {
1283 $fill_gradient = $this->svgGradient($this->svg_gradient[$id_gradient], $attribs, $element);
1284 if ($fill_gradient) {
1285 $path_style = "q ";
1286 $w = "W";
1287 $style .= 'D';
1293 // Used as indirect setting for currentColor
1294 else if (strtolower($critere_style['stroke']) == 'currentcolor') {
1295 $col = $this->mpdf_ref->ConvertColor($critere_style['color']);
1296 if ($col) {
1297 if ($col{0} == 5) {
1298 $critere_style['stroke-opacity'] = ord($col{4} / 100);
1299 } // RGBa
1300 if ($col{0} == 6) {
1301 $critere_style['stroke-opacity'] = ord($col{5} / 100);
1302 } // CMYKa
1303 $path_style .= $this->mpdf_ref->SetDColor($col, true) . ' ';
1304 $style .= 'D';
1305 $lw = $this->ConvertSVGSizePixels($critere_style['stroke-width']);
1306 $path_style .= sprintf('%.3F w ', $lw * $this->kp);
1308 } else if ($critere_style['stroke'] != 'none') {
1309 $col = $this->mpdf_ref->ConvertColor($critere_style['stroke']);
1310 if ($col) {
1311 // mPDF 5.0.051
1312 // mPDF 5.3.74
1313 if ($col{0} == 5) {
1314 $critere_style['stroke-opacity'] = ord($col{4} / 100);
1315 } // RGBa
1316 if ($col{0} == 6) {
1317 $critere_style['stroke-opacity'] = ord($col{5} / 100);
1318 } // CMYKa
1319 $path_style .= $this->mpdf_ref->SetDColor($col, true) . ' ';
1320 $style .= 'D';
1321 $lw = $this->ConvertSVGSizePixels($critere_style['stroke-width']);
1322 $path_style .= sprintf('%.3F w ', $lw * $this->kp);
1327 if ($critere_style['stroke'] != 'none') {
1328 if ($critere_style['stroke-linejoin'] == 'miter') {
1329 $path_style .= ' 0 j ';
1330 } else if ($critere_style['stroke-linejoin'] == 'round') {
1331 $path_style .= ' 1 j ';
1332 } else if ($critere_style['stroke-linejoin'] == 'bevel') {
1333 $path_style .= ' 2 j ';
1336 if ($critere_style['stroke-linecap'] == 'butt') {
1337 $path_style .= ' 0 J ';
1338 } else if ($critere_style['stroke-linecap'] == 'round') {
1339 $path_style .= ' 1 J ';
1340 } else if ($critere_style['stroke-linecap'] == 'square') {
1341 $path_style .= ' 2 J ';
1344 if (isset($critere_style['stroke-miterlimit'])) {
1345 if ($critere_style['stroke-miterlimit'] == 'none') {
1347 } else if (preg_match('/^[\d.]+$/', $critere_style['stroke-miterlimit'])) {
1348 $path_style .= sprintf('%.2F M ', $critere_style['stroke-miterlimit']);
1351 if (isset($critere_style['stroke-dasharray'])) {
1352 $off = 0;
1353 $d = preg_split('/[ ,]/', $critere_style['stroke-dasharray']);
1354 if (count($d) == 1 && $d[0] == 0) {
1355 $path_style .= '[] 0 d ';
1356 } else {
1357 if (count($d) % 2 == 1) {
1358 $d = array_merge($d, $d);
1359 } // 5, 3, 1 => 5,3,1,5,3,1 OR 3 => 3,3
1360 $arr = '';
1361 for ($i = 0; $i < count($d); $i+=2) {
1362 $arr .= sprintf('%.3F %.3F ', $d[$i] * $this->kp, $d[$i + 1] * $this->kp);
1364 if (isset($critere_style['stroke-dashoffset'])) {
1365 $off = $critere_style['stroke-dashoffset'] + 0;
1367 $path_style .= sprintf('[%s] %.3F d ', $arr, $off * $this->kp);
1372 if ($critere_style['fill-rule'] == 'evenodd') {
1373 $fr = '*';
1374 } else {
1375 $fr = '';
1378 if (isset($critere_style['fill-opacity'])) {
1379 $opacity = 1;
1380 if ($critere_style['fill-opacity'] == 0) {
1381 $opacity = 0;
1382 } else if ($critere_style['fill-opacity'] > 1) {
1383 $opacity = 1;
1384 } else if ($critere_style['fill-opacity'] > 0) {
1385 $opacity = $critere_style['fill-opacity'];
1386 } else if ($critere_style['fill-opacity'] < 0) {
1387 $opacity = 0;
1389 $gs = $this->mpdf_ref->AddExtGState(array('ca' => $opacity, 'BM' => '/Normal'));
1390 $this->mpdf_ref->extgstates[$gs]['fo'] = true;
1391 $path_style .= sprintf(' /GS%d gs ', $gs);
1394 if (isset($critere_style['stroke-opacity'])) {
1395 $opacity = 1;
1396 if ($critere_style['stroke-opacity'] == 0) {
1397 $opacity = 0;
1398 } else if ($critere_style['stroke-opacity'] > 1) {
1399 $opacity = 1;
1400 } else if ($critere_style['stroke-opacity'] > 0) {
1401 $opacity = $critere_style['stroke-opacity'];
1402 } else if ($critere_style['stroke-opacity'] < 0) {
1403 $opacity = 0;
1405 $gs = $this->mpdf_ref->AddExtGState(array('CA' => $opacity, 'BM' => '/Normal'));
1406 $this->mpdf_ref->extgstates[$gs]['fo'] = true;
1407 $path_style .= sprintf(' /GS%d gs ', $gs);
1410 switch ($style) {
1411 case 'F':
1412 $op = 'f';
1413 break;
1414 case 'FD':
1415 $op = 'B';
1416 break;
1417 case 'ND':
1418 $op = 'S';
1419 break;
1420 case 'D':
1421 $op = 'S';
1422 break;
1423 default:
1424 $op = 'n';
1427 $prestyle = $path_style . ' ';
1428 $poststyle = $w . ' ' . $op . $fr . ' ' . $fill_gradient . "\n";
1429 return array($prestyle, $poststyle);
1432 // fonction retracant les <path />
1433 function svgPath($command, $arguments)
1435 $path_cmd = '';
1436 $newsubpath = false;
1437 // mPDF 5.0.039
1438 $minl = $this->pathBBox[0];
1439 $mint = $this->pathBBox[1];
1440 $maxr = $this->pathBBox[2] + $this->pathBBox[0];
1441 $maxb = $this->pathBBox[3] + $this->pathBBox[1];
1443 $start = array($this->xbase, -$this->ybase);
1445 preg_match_all('/[\-^]?[\d.]+(e[\-]?[\d]+){0,1}/i', $arguments, $a, PREG_SET_ORDER);
1447 // if the command is a capital letter, the coords go absolute, otherwise relative
1448 if (strtolower($command) == $command)
1449 $relative = true;
1450 else
1451 $relative = false;
1454 $ile_argumentow = count($a);
1456 // each command may have different needs for arguments [1 to 8]
1458 switch (strtolower($command)) {
1459 case 'm': // move
1460 for ($i = 0; $i < $ile_argumentow; $i+=2) {
1461 $x = $a[$i][0];
1462 $y = $a[$i + 1][0];
1463 if ($relative) {
1464 $pdfx = ($this->xbase + $x);
1465 $pdfy = ($this->ybase - $y);
1466 $this->xbase += $x;
1467 $this->ybase += -$y;
1468 } else {
1469 $pdfx = $x;
1470 $pdfy = -$y;
1471 $this->xbase = $x;
1472 $this->ybase = -$y;
1474 $pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1475 $minl = min($minl, $pdf_pt['x']);
1476 $maxr = max($maxr, $pdf_pt['x']);
1477 $mint = min($mint, -$pdf_pt['y']);
1478 $maxb = max($maxb, -$pdf_pt['y']);
1479 if ($i == 0)
1480 $path_cmd .= sprintf('%.3F %.3F m ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1481 else
1482 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1483 // mPDF 4.4.003 Save start points of subpath
1484 if ($this->subPathInit) {
1485 $this->spxstart = $this->xbase;
1486 $this->spystart = $this->ybase;
1487 $this->subPathInit = false;
1490 break;
1491 case 'l': // a simple line
1492 for ($i = 0; $i < $ile_argumentow; $i+=2) {
1493 $x = ($a[$i][0]);
1494 $y = ($a[$i + 1][0]);
1495 if ($relative) {
1496 $pdfx = ($this->xbase + $x);
1497 $pdfy = ($this->ybase - $y);
1498 $this->xbase += $x;
1499 $this->ybase += -$y;
1500 } else {
1501 $pdfx = $x;
1502 $pdfy = -$y;
1503 $this->xbase = $x;
1504 $this->ybase = -$y;
1506 $pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1507 $minl = min($minl, $pdf_pt['x']);
1508 $maxr = max($maxr, $pdf_pt['x']);
1509 $mint = min($mint, -$pdf_pt['y']);
1510 $maxb = max($maxb, -$pdf_pt['y']);
1511 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1513 break;
1514 case 'h': // a very simple horizontal line
1515 for ($i = 0; $i < $ile_argumentow; $i++) {
1516 $x = ($a[$i][0]);
1517 if ($relative) {
1518 $y = 0;
1519 $pdfx = ($this->xbase + $x);
1520 $pdfy = ($this->ybase - $y);
1521 $this->xbase += $x;
1522 $this->ybase += -$y;
1523 } else {
1524 $y = -$this->ybase;
1525 $pdfx = $x;
1526 $pdfy = -$y;
1527 $this->xbase = $x;
1528 $this->ybase = -$y;
1530 $pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1531 $minl = min($minl, $pdf_pt['x']);
1532 $maxr = max($maxr, $pdf_pt['x']);
1533 $mint = min($mint, -$pdf_pt['y']);
1534 $maxb = max($maxb, -$pdf_pt['y']);
1535 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1537 break;
1538 case 'v': // the simplest line, vertical
1539 for ($i = 0; $i < $ile_argumentow; $i++) {
1540 $y = ($a[$i][0]);
1541 if ($relative) {
1542 $x = 0;
1543 $pdfx = ($this->xbase + $x);
1544 $pdfy = ($this->ybase - $y);
1545 $this->xbase += $x;
1546 $this->ybase += -$y;
1547 } else {
1548 $x = $this->xbase;
1549 $pdfx = $x;
1550 $pdfy = -$y;
1551 $this->xbase = $x;
1552 $this->ybase = -$y;
1554 $pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1555 $minl = min($minl, $pdf_pt['x']);
1556 $maxr = max($maxr, $pdf_pt['x']);
1557 $mint = min($mint, -$pdf_pt['y']);
1558 $maxb = max($maxb, -$pdf_pt['y']);
1559 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1561 break;
1562 case 's': // bezier with first vertex equal first control
1563 // mPDF 4.4.003
1564 if (!($this->lastcommand == 'C' || $this->lastcommand == 'c' || $this->lastcommand == 'S' || $this->lastcommand == 's')) {
1565 $this->lastcontrolpoints = array(0, 0);
1567 for ($i = 0; $i < $ile_argumentow; $i += 4) {
1568 $x1 = $this->lastcontrolpoints[0];
1569 $y1 = $this->lastcontrolpoints[1];
1570 $x2 = ($a[$i][0]);
1571 $y2 = ($a[$i + 1][0]);
1572 $x = ($a[$i + 2][0]);
1573 $y = ($a[$i + 3][0]);
1574 if ($relative) {
1575 $pdfx1 = ($this->xbase + $x1);
1576 $pdfy1 = ($this->ybase - $y1);
1577 $pdfx2 = ($this->xbase + $x2);
1578 $pdfy2 = ($this->ybase - $y2);
1579 $pdfx = ($this->xbase + $x);
1580 $pdfy = ($this->ybase - $y);
1581 $this->xbase += $x;
1582 $this->ybase += -$y;
1583 } else {
1584 $pdfx1 = $this->xbase + $x1;
1585 $pdfy1 = $this->ybase - $y1;
1586 $pdfx2 = $x2;
1587 $pdfy2 = -$y2;
1588 $pdfx = $x;
1589 $pdfy = -$y;
1590 $this->xbase = $x;
1591 $this->ybase = -$y;
1593 $this->lastcontrolpoints = array(($pdfx - $pdfx2), -($pdfy - $pdfy2)); // mPDF 4.4.003 always relative
1595 $pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1597 $curves = array($pdfx1, -$pdfy1, $pdfx2, -$pdfy2, $pdfx, -$pdfy);
1598 $bx = calc_bezier_bbox($start, $curves);
1599 $minl = min($minl, $bx[0]);
1600 $maxr = max($maxr, $bx[2]);
1601 $mint = min($mint, $bx[1]);
1602 $maxb = max($maxb, $bx[3]);
1604 if (($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy)) {
1605 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1606 } else {
1607 $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);
1610 break;
1611 case 'c': // bezier with second vertex equal second control
1612 for ($i = 0; $i < $ile_argumentow; $i += 6) {
1613 $x1 = ($a[$i][0]);
1614 $y1 = ($a[$i + 1][0]);
1615 $x2 = ($a[$i + 2][0]);
1616 $y2 = ($a[$i + 3][0]);
1617 $x = ($a[$i + 4][0]);
1618 $y = ($a[$i + 5][0]);
1621 if ($relative) {
1622 $pdfx1 = ($this->xbase + $x1);
1623 $pdfy1 = ($this->ybase - $y1);
1624 $pdfx2 = ($this->xbase + $x2);
1625 $pdfy2 = ($this->ybase - $y2);
1626 $pdfx = ($this->xbase + $x);
1627 $pdfy = ($this->ybase - $y);
1628 $this->xbase += $x;
1629 $this->ybase += -$y;
1630 } else {
1631 $pdfx1 = $x1;
1632 $pdfy1 = -$y1;
1633 $pdfx2 = $x2;
1634 $pdfy2 = -$y2;
1635 $pdfx = $x;
1636 $pdfy = -$y;
1637 $this->xbase = $x;
1638 $this->ybase = -$y;
1640 $this->lastcontrolpoints = array(($pdfx - $pdfx2), -($pdfy - $pdfy2)); // mPDF 4.4.003 always relative
1641 // $pdf_pt2 = $this->svg_overflow($pdfx2,$pdfy2);
1642 // $pdf_pt1 = $this->svg_overflow($pdfx1,$pdfy1);
1643 $pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1645 $curves = array($pdfx1, -$pdfy1, $pdfx2, -$pdfy2, $pdfx, -$pdfy);
1646 $bx = calc_bezier_bbox($start, $curves);
1647 $minl = min($minl, $bx[0]);
1648 $maxr = max($maxr, $bx[2]);
1649 $mint = min($mint, $bx[1]);
1650 $maxb = max($maxb, $bx[3]);
1652 if (($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy)) {
1653 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1654 } else {
1655 $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);
1658 break;
1660 case 'q': // bezier quadratic avec point de control
1661 for ($i = 0; $i < $ile_argumentow; $i += 4) {
1662 $x1 = ($a[$i][0]);
1663 $y1 = ($a[$i + 1][0]);
1664 $x = ($a[$i + 2][0]);
1665 $y = ($a[$i + 3][0]);
1666 if ($relative) {
1667 $pdfx = ($this->xbase + $x);
1668 $pdfy = ($this->ybase - $y);
1670 $pdfx1 = ($this->xbase + ($x1 * 2 / 3));
1671 $pdfy1 = ($this->ybase - ($y1 * 2 / 3));
1672 // mPDF 4.4.003
1673 $pdfx2 = $pdfx1 + 1 / 3 * ($x);
1674 $pdfy2 = $pdfy1 + 1 / 3 * (-$y);
1676 $this->xbase += $x;
1677 $this->ybase += -$y;
1678 } else {
1679 $pdfx = $x;
1680 $pdfy = -$y;
1682 $pdfx1 = ($this->xbase + (($x1 - $this->xbase) * 2 / 3));
1683 $pdfy1 = ($this->ybase - (($y1 + $this->ybase) * 2 / 3));
1685 $pdfx2 = ($x + (($x1 - $x) * 2 / 3));
1686 $pdfy2 = (-$y - (($y1 - $y) * 2 / 3));
1688 // mPDF 4.4.003
1689 $pdfx2 = $pdfx1 + 1 / 3 * ($x - $this->xbase);
1690 $pdfy2 = $pdfy1 + 1 / 3 * (-$y - $this->ybase);
1692 $this->xbase = $x;
1693 $this->ybase = -$y;
1695 $this->lastcontrolpoints = array(($pdfx - $pdfx2), -($pdfy - $pdfy2)); // mPDF 4.4.003 always relative
1697 $pdf_pt = $this->svg_overflow($pdfx, $pdfy);
1699 $curves = array($pdfx1, -$pdfy1, $pdfx2, -$pdfy2, $pdfx, -$pdfy);
1700 $bx = calc_bezier_bbox($start, $curves);
1701 $minl = min($minl, $bx[0]);
1702 $maxr = max($maxr, $bx[2]);
1703 $mint = min($mint, $bx[1]);
1704 $maxb = max($maxb, $bx[3]);
1706 if (($pdf_pt['x'] != $pdfx) || ($pdf_pt['y'] != $pdfy)) {
1707 $path_cmd .= sprintf('%.3F %.3F l ', $pdf_pt['x'] * $this->kp, $pdf_pt['y'] * $this->kp);
1708 } else {
1709 $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);
1712 break;
1713 case 't': // bezier quadratic avec point de control simetrique a lancien point de control
1714 if (!($this->lastcommand == 'Q' || $this->lastcommand == 'q' || $this->lastcommand == 'T' || $this->lastcommand == 't')) {
1715 $this->lastcontrolpoints = array(0, 0);
1717 for ($i = 0; $i < $ile_argumentow; $i += 2) {
1718 $x = ($a[$i][0]);
1719 $y = ($a[$i + 1][0]);
1721 $x1 = $this->lastcontrolpoints[0];
1722 $y1 = $this->lastcontrolpoints[1];
1724 if ($relative) {
1725 $pdfx = ($this->xbase + $x);
1726 $pdfy = ($this->ybase - $y);
1728 $pdfx1 = ($this->xbase + ($x1));
1729 $pdfy1 = ($this->ybase - ($y1));
1730 // mPDF 4.4.003
1731 $pdfx2 = $pdfx1 + 1 / 3 * ($x);
1732 $pdfy2 = $pdfy1 + 1 / 3 * (-$y);
1734 $this->xbase += $x;
1735 $this->ybase += -$y;
1736 } else {
1737 $pdfx = $x;
1738 $pdfy = -$y;
1740 $pdfx1 = ($this->xbase + ($x1));
1741 $pdfy1 = ($this->ybase - ($y1));
1742 // mPDF 4.4.003
1743 $pdfx2 = $pdfx1 + 1 / 3 * ($x - $this->xbase);
1744 $pdfy2 = $pdfy1 + 1 / 3 * (-$y - $this->ybase);
1746 $this->xbase = $x;
1747 $this->ybase = -$y;
1750 $this->lastcontrolpoints = array(($pdfx - $pdfx2), -($pdfy - $pdfy2)); // mPDF 4.4.003 always relative
1752 $curves = array($pdfx1, -$pdfy1, $pdfx2, -$pdfy2, $pdfx, -$pdfy);
1753 $bx = calc_bezier_bbox($start, $curves);
1754 $minl = min($minl, $bx[0]);
1755 $maxr = max($maxr, $bx[2]);
1756 $mint = min($mint, $bx[1]);
1757 $maxb = max($maxb, $bx[3]);
1759 $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);
1762 break;
1763 case 'a': // Elliptical arc
1764 for ($i = 0; $i < $ile_argumentow; $i += 7) {
1765 $rx = ($a[$i][0]);
1766 $ry = ($a[$i + 1][0]);
1767 $angle = ($a[$i + 2][0]); //x-axis-rotation
1768 $largeArcFlag = ($a[$i + 3][0]);
1769 $sweepFlag = ($a[$i + 4][0]);
1770 $x2 = ($a[$i + 5][0]);
1771 $y2 = ($a[$i + 6][0]);
1772 $x1 = $this->xbase;
1773 $y1 = -$this->ybase;
1774 if ($relative) {
1775 $x2 = $this->xbase + $x2;
1776 $y2 = -$this->ybase + $y2;
1777 $this->xbase += ($a[$i + 5][0]);
1778 $this->ybase += -($a[$i + 6][0]);
1779 } else {
1780 $this->xbase = $x2;
1781 $this->ybase = -$y2;
1783 list($pcmd, $bounds) = $this->Arcto($x1, $y1, $x2, $y2, $rx, $ry, $angle, $largeArcFlag, $sweepFlag);
1784 $minl = min($minl, $x2, min($bounds[0]));
1785 $maxr = max($maxr, $x2, max($bounds[0]));
1786 $mint = min($mint, $y2, min($bounds[1]));
1787 $maxb = max($maxb, $y2, max($bounds[1]));
1788 $path_cmd .= $pcmd;
1790 break;
1791 case'z':
1792 $path_cmd .= 'h ';
1793 $this->subPathInit = true;
1794 $newsubpath = true;
1795 $this->xbase = $this->spxstart;
1796 $this->ybase = $this->spystart;
1797 break;
1798 default:
1799 break;
1802 if (!$newsubpath) {
1803 $this->subPathInit = false;
1805 $this->lastcommand = $command;
1806 // mPDF 5.0.039
1807 $this->pathBBox[0] = $minl;
1808 $this->pathBBox[1] = $mint;
1809 $this->pathBBox[2] = $maxr - $this->pathBBox[0];
1810 $this->pathBBox[3] = $maxb - $this->pathBBox[1];
1811 return $path_cmd;
1814 function Arcto($x1, $y1, $x2, $y2, $rx, $ry, $angle, $largeArcFlag, $sweepFlag)
1817 $bounds = array(0 => array($x1, $x2), 1 => array($y1, $y2));
1818 // 1. Treat out-of-range parameters as described in
1819 // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
1820 // If the endpoints (x1, y1) and (x2, y2) are identical, then this
1821 // is equivalent to omitting the elliptical arc segment entirely
1822 if ($x1 == $x2 && $y1 == $y2)
1823 return array('', $bounds); // mPD 5.0.040
1826 // If rX = 0 or rY = 0 then this arc is treated as a straight line
1827 // segment (a "lineto") joining the endpoints.
1828 if ($rx == 0.0 || $ry == 0.0) {
1829 // return array(Lineto(x2, y2), $bounds);
1832 // If rX or rY have negative signs, these are dropped; the absolute
1833 // value is used instead.
1834 if ($rx < 0.0)
1835 $rx = -$rx;
1836 if ($ry < 0.0)
1837 $ry = -$ry;
1839 // 2. convert to center parameterization as shown in
1840 // http://www.w3.org/TR/SVG/implnote.html
1841 $sinPhi = sin(deg2rad($angle));
1842 $cosPhi = cos(deg2rad($angle));
1844 $x1dash = $cosPhi * ($x1 - $x2) / 2.0 + $sinPhi * ($y1 - $y2) / 2.0;
1845 $y1dash = -$sinPhi * ($x1 - $x2) / 2.0 + $cosPhi * ($y1 - $y2) / 2.0;
1848 $numerator = $rx * $rx * $ry * $ry - $rx * $rx * $y1dash * $y1dash - $ry * $ry * $x1dash * $x1dash;
1850 if ($numerator < 0.0) {
1851 // If rX , rY and are such that there is no solution (basically,
1852 // the ellipse is not big enough to reach from (x1, y1) to (x2,
1853 // y2)) then the ellipse is scaled up uniformly until there is
1854 // exactly one solution (until the ellipse is just big enough).
1855 // -> find factor s, such that numerator' with rx'=s*rx and
1856 // ry'=s*ry becomes 0 :
1857 $s = sqrt(1.0 - $numerator / ($rx * $rx * $ry * $ry));
1859 $rx *= $s;
1860 $ry *= $s;
1861 $root = 0.0;
1862 } else {
1863 $root = ($largeArcFlag == $sweepFlag ? -1.0 : 1.0) * sqrt($numerator / ($rx * $rx * $y1dash * $y1dash + $ry * $ry * $x1dash * $x1dash));
1866 $cxdash = $root * $rx * $y1dash / $ry;
1867 $cydash = -$root * $ry * $x1dash / $rx;
1869 $cx = $cosPhi * $cxdash - $sinPhi * $cydash + ($x1 + $x2) / 2.0;
1870 $cy = $sinPhi * $cxdash + $cosPhi * $cydash + ($y1 + $y2) / 2.0;
1873 $theta1 = $this->CalcVectorAngle(1.0, 0.0, ($x1dash - $cxdash) / $rx, ($y1dash - $cydash) / $ry);
1874 $dtheta = $this->CalcVectorAngle(($x1dash - $cxdash) / $rx, ($y1dash - $cydash) / $ry, (-$x1dash - $cxdash) / $rx, (-$y1dash - $cydash) / $ry);
1875 if (!$sweepFlag && $dtheta > 0)
1876 $dtheta -= 2.0 * M_PI;
1877 else if ($sweepFlag && $dtheta < 0)
1878 $dtheta += 2.0 * M_PI;
1880 // 3. convert into cubic bezier segments <= 90deg
1881 $segments = ceil(abs($dtheta / (M_PI / 2.0)));
1882 $delta = $dtheta / $segments;
1883 $t = 8.0 / 3.0 * sin($delta / 4.0) * sin($delta / 4.0) / sin($delta / 2.0);
1884 $coords = array();
1885 for ($i = 0; $i < $segments; $i++) {
1886 $cosTheta1 = cos($theta1);
1887 $sinTheta1 = sin($theta1);
1888 $theta2 = $theta1 + $delta;
1889 $cosTheta2 = cos($theta2);
1890 $sinTheta2 = sin($theta2);
1892 // a) calculate endpoint of the segment:
1893 $xe = $cosPhi * $rx * $cosTheta2 - $sinPhi * $ry * $sinTheta2 + $cx;
1894 $ye = $sinPhi * $rx * $cosTheta2 + $cosPhi * $ry * $sinTheta2 + $cy;
1896 // b) calculate gradients at start/end points of segment:
1897 $dx1 = $t * ( - $cosPhi * $rx * $sinTheta1 - $sinPhi * $ry * $cosTheta1);
1898 $dy1 = $t * ( - $sinPhi * $rx * $sinTheta1 + $cosPhi * $ry * $cosTheta1);
1900 $dxe = $t * ( $cosPhi * $rx * $sinTheta2 + $sinPhi * $ry * $cosTheta2);
1901 $dye = $t * ( $sinPhi * $rx * $sinTheta2 - $cosPhi * $ry * $cosTheta2);
1903 // c) draw the cubic bezier:
1904 $coords[$i] = array(($x1 + $dx1), ($y1 + $dy1), ($xe + $dxe), ($ye + $dye), $xe, $ye);
1906 // do next segment
1907 $theta1 = $theta2;
1908 $x1 = $xe;
1909 $y1 = $ye;
1911 $path = ' ';
1912 foreach ($coords AS $c) {
1913 $cpx1 = $c[0];
1914 $cpy1 = $c[1];
1915 $cpx2 = $c[2];
1916 $cpy2 = $c[3];
1917 $x2 = $c[4];
1918 $y2 = $c[5];
1919 $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";
1921 // mPDF 5.0.040
1922 $bounds[0][] = $c[4];
1923 $bounds[1][] = $c[5];
1925 return array($path, $bounds); // mPD 5.0.040
1928 function CalcVectorAngle($ux, $uy, $vx, $vy)
1930 $ta = atan2($uy, $ux);
1931 $tb = atan2($vy, $vx);
1932 if ($tb >= $ta)
1933 return ($tb - $ta);
1934 return (6.28318530718 - ($ta - $tb));
1937 function ConvertSVGSizePixels($size = 5, $maxsize = 'x')
1939 // maxsize in pixels (user units) or 'y' or 'x'
1940 // e.g. $w = $this->ConvertSVGSizePixels($arguments['w'],$this->svg_info['w']*(25.4/$this->mpdf_ref->dpi));
1941 // usefontsize - setfalse for e.g. margins - will ignore fontsize for % values
1942 // Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
1943 // For text $maxsize = Fontsize
1944 // Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
1946 if ($maxsize == 'y') {
1947 $maxsize = $this->svg_info['h'];
1948 } else if ($maxsize == 'x') {
1949 $maxsize = $this->svg_info['w'];
1951 $maxsize *= (25.4 / $this->mpdf_ref->dpi); // convert pixels to mm
1952 $fontsize = $this->mpdf_ref->FontSize / $this->kf;
1953 //Return as pixels
1954 $size = $this->mpdf_ref->ConvertSize($size, $maxsize, $fontsize, false) * 1 / (25.4 / $this->mpdf_ref->dpi);
1955 return $size;
1958 function ConvertSVGSizePts($size = 5)
1960 // usefontsize - setfalse for e.g. margins - will ignore fontsize for % values
1961 // Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
1962 // For text $maxsize = Fontsize
1963 // Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
1964 $maxsize = $this->mpdf_ref->FontSize;
1965 //Return as pts
1966 $size = $this->mpdf_ref->ConvertSize($size, $maxsize, false, true) * 72 / 25.4;
1967 return $size;
1971 // fonction retracant les <rect />
1972 function svgRect($arguments)
1975 if ($arguments['h'] == 0 || $arguments['w'] == 0) {
1976 return '';
1979 $x = $this->ConvertSVGSizePixels($arguments['x'], 'x'); // mPDF 4.4.003
1980 $y = $this->ConvertSVGSizePixels($arguments['y'], 'y'); // mPDF 4.4.003
1981 $h = $this->ConvertSVGSizePixels($arguments['h'], 'y'); // mPDF 4.4.003
1982 $w = $this->ConvertSVGSizePixels($arguments['w'], 'x'); // mPDF 4.4.003
1983 $rx = $this->ConvertSVGSizePixels($arguments['rx'], 'x'); // mPDF 4.4.003
1984 $ry = $this->ConvertSVGSizePixels($arguments['ry'], 'y'); // mPDF 4.4.003
1986 if ($rx > $w / 2) {
1987 $rx = $w / 2;
1988 } // mPDF 4.4.003
1989 if ($ry > $h / 2) {
1990 $ry = $h / 2;
1991 } // mPDF 4.4.003
1993 if ($rx > 0 and $ry == 0) {
1994 $ry = $rx;
1996 if ($ry > 0 and $rx == 0) {
1997 $rx = $ry;
2000 if ($rx == 0 and $ry == 0) {
2001 // trace un rectangle sans angle arrondit
2002 $path_cmd = sprintf('%.3F %.3F m ', ($x * $this->kp), -($y * $this->kp));
2003 $path_cmd .= sprintf('%.3F %.3F l ', (($x + $w) * $this->kp), -($y * $this->kp));
2004 $path_cmd .= sprintf('%.3F %.3F l ', (($x + $w) * $this->kp), -(($y + $h) * $this->kp));
2005 $path_cmd .= sprintf('%.3F %.3F l ', ($x) * $this->kp, -(($y + $h) * $this->kp));
2006 $path_cmd .= sprintf('%.3F %.3F l h ', ($x * $this->kp), -($y * $this->kp));
2007 } else {
2008 // trace un rectangle avec les arrondit
2009 // les points de controle du bezier sont deduis grace a la constante kappa
2010 $kappa = 4 * (sqrt(2) - 1) / 3;
2012 $kx = $kappa * $rx;
2013 $ky = $kappa * $ry;
2015 $path_cmd = sprintf('%.3F %.3F m ', ($x + $rx) * $this->kp, -$y * $this->kp);
2016 $path_cmd .= sprintf('%.3F %.3F l ', ($x + ($w - $rx)) * $this->kp, -$y * $this->kp);
2017 $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);
2018 $path_cmd .= sprintf('%.3F %.3F l ', ($x + $w) * $this->kp, (-$y + (-$h + $ry)) * $this->kp);
2019 $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);
2021 $path_cmd .= sprintf('%.3F %.3F l ', ($x + $rx) * $this->kp, (-$y + (-$h)) * $this->kp);
2022 $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);
2023 $path_cmd .= sprintf('%.3F %.3F l ', $x * $this->kp, (-$y + (-$ry)) * $this->kp);
2024 $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);
2026 return $path_cmd;
2030 // fonction retracant les <ellipse /> et <circle />
2031 // le cercle est tracé grave a 4 bezier cubic, les poitn de controles
2032 // sont deduis grace a la constante kappa * rayon
2033 function svgEllipse($arguments)
2035 if ($arguments['rx'] == 0 || $arguments['ry'] == 0) {
2036 return '';
2039 $kappa = 4 * (sqrt(2) - 1) / 3;
2041 $cx = $this->ConvertSVGSizePixels($arguments['cx'], 'x');
2042 $cy = $this->ConvertSVGSizePixels($arguments['cy'], 'y');
2043 $rx = $this->ConvertSVGSizePixels($arguments['rx'], 'x');
2044 $ry = $this->ConvertSVGSizePixels($arguments['ry'], 'y');
2046 $x1 = $cx;
2047 $y1 = -$cy + $ry;
2049 $x2 = $cx + $rx;
2050 $y2 = -$cy;
2052 $x3 = $cx;
2053 $y3 = -$cy - $ry;
2055 $x4 = $cx - $rx;
2056 $y4 = -$cy;
2058 $path_cmd = sprintf('%.3F %.3F m ', $x1 * $this->kp, $y1 * $this->kp);
2059 $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);
2060 $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);
2061 $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);
2062 $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);
2063 $path_cmd .= 'h ';
2065 return $path_cmd;
2069 // fonction retracant les <polyline /> et les <line />
2070 function svgPolyline($arguments, $ispolyline = true)
2072 if ($ispolyline) {
2073 $xbase = $arguments[0];
2074 $ybase = - $arguments[1];
2075 } else {
2076 if ($arguments[0] == $arguments[2] && $arguments[1] == $arguments[3]) {
2077 return '';
2078 } // Zero length line
2079 $xbase = $this->ConvertSVGSizePixels($arguments[0], 'x');
2080 $ybase = - $this->ConvertSVGSizePixels($arguments[1], 'y');
2082 $path_cmd = sprintf('%.3F %.3F m ', $xbase * $this->kp, $ybase * $this->kp);
2083 for ($i = 2; $i < count($arguments); $i += 2) {
2084 if ($ispolyline) {
2085 $tmp_x = $arguments[$i];
2086 $tmp_y = - $arguments[($i + 1)];
2087 } else {
2088 $tmp_x = $this->ConvertSVGSizePixels($arguments[$i], 'x');
2089 $tmp_y = - $this->ConvertSVGSizePixels($arguments[($i + 1)], 'y');
2091 $path_cmd .= sprintf('%.3F %.3F l ', $tmp_x * $this->kp, $tmp_y * $this->kp);
2094 // $path_cmd .= 'h '; // ?? In error - don't close subpath here
2095 return $path_cmd;
2099 // fonction retracant les <polygone />
2100 function svgPolygon($arguments)
2102 $xbase = $arguments[0];
2103 $ybase = - $arguments[1];
2104 $path_cmd = sprintf('%.3F %.3F m ', $xbase * $this->kp, $ybase * $this->kp);
2105 for ($i = 2; $i < count($arguments); $i += 2) {
2106 $tmp_x = $arguments[$i];
2107 $tmp_y = - $arguments[($i + 1)];
2109 $path_cmd .= sprintf('%.3F %.3F l ', $tmp_x * $this->kp, $tmp_y * $this->kp);
2111 $path_cmd .= sprintf('%.3F %.3F l ', $xbase * $this->kp, $ybase * $this->kp);
2112 $path_cmd .= 'h ';
2113 return $path_cmd;
2117 // write string to image
2118 function svgText()
2120 // $tmp = count($this->txt_style)-1;
2121 $current_style = $this->txt_style[count($this->txt_style) - 1]; // mPDF 5.7.4
2122 $style = '';
2123 $op = '';
2124 $render = -1;
2125 if (isset($this->txt_data[2])) {
2126 // mPDF 6
2127 // If using SVG Font
2128 if (isset($this->svg_font[$current_style['font-family']])) {
2129 // select font
2130 $style = 'R';
2131 $style .= (isset($current_style['font-weight']) && $current_style['font-weight'] == 'bold') ? 'B' : '';
2132 $style .= (isset($current_style['font-style']) && $current_style['font-style'] == 'italic') ? 'I' : '';
2133 $style .= (isset($current_style['font-variant']) && $current_style['font-variant'] == 'small-caps') ? 'S' : '';
2135 $fontsize = $current_style['font-size'] * $this->mpdf_ref->dpi / 72;
2136 if (isset($this->svg_font[$current_style['font-family']][$style])) {
2137 $svg_font = $this->svg_font[$current_style['font-family']][$style];
2138 } else if (isset($this->svg_font[$current_style['font-family']]['R'])) {
2139 $svg_font = $this->svg_font[$current_style['font-family']]['R'];
2142 if (!isset($svg_font['units-per-em']) || $svg_font['units-per-em'] < 1) {
2143 $svg_font['units-per-em'] = 1000;
2145 $units_per_em = $svg_font['units-per-em'];
2146 $scale = $fontsize / $units_per_em;
2147 $stroke_width = $current_style['stroke-width'];
2148 $stroke_width /= $scale;
2150 $opacitystr = '';
2151 $fopacity = 1;
2152 if (isset($current_style['fill-opacity'])) {
2153 if ($current_style['fill-opacity'] == 0) {
2154 $fopacity = 0;
2155 } else if ($current_style['fill-opacity'] > 1) {
2156 $fopacity = 1;
2157 } else if ($current_style['fill-opacity'] > 0) {
2158 $fopacity = $current_style['fill-opacity'];
2159 } else if ($current_style['fill-opacity'] < 0) {
2160 $fopacity = 0;
2163 $sopacity = 1;
2164 if (isset($current_style['stroke-opacity'])) {
2165 if ($current_style['stroke-opacity'] == 0) {
2166 $sopacity = 0;
2167 } else if ($current_style['stroke-opacity'] > 1) {
2168 $sopacity = 1;
2169 } else if ($current_style['stroke-opacity'] > 0) {
2170 $sopacity = $current_style['stroke-opacity'];
2171 } else if ($current_style['stroke-opacity'] < 0) {
2172 $sopacity = 0;
2175 $gs = $this->mpdf_ref->AddExtGState(array('ca' => $fopacity, 'CA' => $sopacity, 'BM' => '/Normal'));
2176 $this->mpdf_ref->extgstates[$gs]['fo'] = true;
2177 $opacitystr = sprintf(' /GS%d gs ', $gs);
2179 $fillstr = '';
2180 if (isset($current_style['fill']) && $current_style['fill'] != 'none') {
2181 $col = $this->mpdf_ref->ConvertColor($current_style['fill']);
2182 $fillstr = $this->mpdf_ref->SetFColor($col, true);
2183 $render = "0"; // Fill (only)
2184 $op = 'f';
2186 $strokestr = '';
2187 if ($stroke_width > 0 && $current_style['stroke'] != 'none') {
2188 $scol = $this->mpdf_ref->ConvertColor($current_style['stroke']);
2189 if ($scol) {
2190 $strokestr .= $this->mpdf_ref->SetDColor($scol, true) . ' ';
2192 $linewidth = $this->ConvertSVGSizePixels($stroke_width);
2193 if ($linewidth > 0) {
2194 $strokestr .= sprintf('%.3F w 0 J 0 j ', $linewidth * $this->kp);
2195 if ($render == -1) {
2196 $render = "1";
2197 } // stroke only
2198 else {
2199 $render = "2";
2200 } // fill and stroke
2201 $op .= 'S';
2204 if ($render == -1) {
2205 return '';
2207 if ($op == 'fS') {
2208 $op = 'B';
2211 $x = $this->txt_data[0]; // mPDF 5.7.4
2212 $y = $this->txt_data[1]; // mPDF 5.7.4
2213 $txt = $this->txt_data[2];
2215 $txt = preg_replace('/\f/', '', $txt);
2216 $txt = preg_replace('/\r/', '', $txt);
2217 $txt = preg_replace('/\n/', ' ', $txt);
2218 $txt = preg_replace('/\t/', ' ', $txt);
2219 $txt = preg_replace("/[ ]+/u", ' ', $txt);
2221 if ($this->textjuststarted) {
2222 $txt = ltrim($txt);
2223 } // mPDF 5.7.4
2224 $this->textjuststarted = false; // mPDF 5.7.4
2226 $txt = $this->mpdf_ref->purify_utf8_text($txt);
2227 if ($this->mpdf_ref->text_input_as_HTML) {
2228 $txt = $this->mpdf_ref->all_entities_to_utf8($txt);
2231 $nb = mb_strlen($txt, 'UTF-8');
2232 $i = 0;
2233 $sw = 0;
2234 $subpath_cmd = '';
2235 while ($i < $nb) {
2236 //Get next character
2237 $char = mb_substr($txt, $i, 1, 'UTF-8');
2240 if (isset($svg_font['glyphs'][$char])) {
2241 $d = $svg_font['glyphs'][$char]['d'];
2242 if (isset($svg_font['glyphs'][$char]['horiz-adv-x'])) {
2243 $horiz_adv_x = $svg_font['glyphs'][$char]['horiz-adv-x'];
2244 } else {
2245 $horiz_adv_x = $svg_font['horiz-adv-x'];
2246 } // missing glyph width
2247 } else {
2248 $d = $svg_font['d'];
2249 $horiz_adv_x = $svg_font['horiz-adv-x']; // missing glyph width
2251 preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $d, $commands, PREG_SET_ORDER);
2252 $subpath_cmd .= sprintf('q %.4F 0 0 %.4F mPDF-AXS(%.4F) %.4F cm ', $scale, -$scale, ($x + $sw * $scale) * $this->kp, -$y * $this->kp);
2254 $this->subPathInit = true;
2255 $this->pathBBox = array(999999, 999999, -999999, -999999);
2256 foreach ($commands as $cmd) {
2257 if (count($cmd) == 3 || (isset($cmd[2]) && $cmd[2] == '')) {
2258 list($tmp, $command, $arguments) = $cmd;
2259 } else {
2260 list($tmp, $command) = $cmd;
2261 $arguments = '';
2264 $subpath_cmd .= $this->svgPath($command, $arguments);
2266 $subpath_cmd .= $op . ' Q ';
2267 if ($this->pathBBox[2] == -1999998) {
2268 $this->pathBBox[2] = 100;
2270 if ($this->pathBBox[3] == -1999998) {
2271 $this->pathBBox[3] = 100;
2273 if ($this->pathBBox[0] == 999999) {
2274 $this->pathBBox[0] = 0;
2276 if ($this->pathBBox[1] == 999999) {
2277 $this->pathBBox[1] = 0;
2281 $sw += $horiz_adv_x;
2282 $i++;
2285 $sw *= $scale; // convert stringwidth to units
2286 // mPDF 5.7.4
2287 $this->textlength = $sw;
2288 $this->texttotallength += $this->textlength;
2290 $path_cmd = sprintf('q %s %s Tr %s %s ', $opacitystr, $render, $fillstr, $strokestr);
2291 $path_cmd .= $subpath_cmd;
2292 $path_cmd .= 'Q ';
2294 unset($this->txt_data[0], $this->txt_data[1], $this->txt_data[2]);
2295 return $path_cmd;
2298 // select font
2299 $style .= ($current_style['font-weight'] == 'bold') ? 'B' : '';
2300 $style .= ($current_style['font-style'] == 'italic') ? 'I' : '';
2301 $size = $current_style['font-size'] * $this->kf;
2303 $current_style['font-family'] = $this->mpdf_ref->SetFont($current_style['font-family'], $style, $size, false);
2304 $this->mpdf_ref->CurrentFont['fo'] = true;
2306 $opacitystr = '';
2307 // mPDF 6
2308 $fopacity = 1;
2309 if (isset($current_style['fill-opacity'])) {
2310 if ($current_style['fill-opacity'] == 0) {
2311 $fopacity = 0;
2312 } else if ($current_style['fill-opacity'] > 1) {
2313 $fopacity = 1;
2314 } else if ($current_style['fill-opacity'] > 0) {
2315 $fopacity = $current_style['fill-opacity'];
2316 } else if ($current_style['fill-opacity'] < 0) {
2317 $fopacity = 0;
2320 $sopacity = 1;
2321 if (isset($current_style['stroke-opacity'])) {
2322 if ($current_style['stroke-opacity'] == 0) {
2323 $sopacity = 0;
2324 } else if ($current_style['stroke-opacity'] > 1) {
2325 $sopacity = 1;
2326 } else if ($current_style['stroke-opacity'] > 0) {
2327 $sopacity = $current_style['stroke-opacity'];
2328 } else if ($current_style['stroke-opacity'] < 0) {
2329 $sopacity = 0;
2332 $gs = $this->mpdf_ref->AddExtGState(array('ca' => $fopacity, 'CA' => $sopacity, 'BM' => '/Normal'));
2333 $this->mpdf_ref->extgstates[$gs]['fo'] = true;
2334 $opacitystr = sprintf(' /GS%d gs ', $gs);
2336 $fillstr = '';
2337 if (isset($current_style['fill']) && $current_style['fill'] != 'none') {
2338 $col = $this->mpdf_ref->ConvertColor($current_style['fill']);
2339 $fillstr = $this->mpdf_ref->SetFColor($col, true);
2340 $render = "0"; // Fill (only)
2342 $strokestr = '';
2343 if (isset($current_style['stroke-width']) && $current_style['stroke-width'] > 0 && $current_style['stroke'] != 'none') {
2344 $scol = $this->mpdf_ref->ConvertColor($current_style['stroke']);
2345 if ($scol) {
2346 $strokestr .= $this->mpdf_ref->SetDColor($scol, true) . ' ';
2348 $linewidth = $this->ConvertSVGSizePixels($current_style['stroke-width']);
2349 if ($linewidth > 0) {
2350 $strokestr .= sprintf('%.3F w 1 J 1 j ', $linewidth * $this->kp);
2351 if ($render == -1) {
2352 $render = "1";
2353 } // stroke only
2354 else {
2355 $render = "2";
2356 } // fill and stroke
2359 if ($render == -1) {
2360 return '';
2363 $x = $this->txt_data[0]; // mPDF 5.7.4
2364 $y = $this->txt_data[1]; // mPDF 5.7.4
2365 $txt = $this->txt_data[2];
2367 $txt = preg_replace('/\f/', '', $txt);
2368 $txt = preg_replace('/\r/', '', $txt);
2369 $txt = preg_replace('/\n/', ' ', $txt);
2370 $txt = preg_replace('/\t/', ' ', $txt);
2371 $txt = preg_replace("/[ ]+/u", ' ', $txt);
2373 if ($this->textjuststarted) {
2374 $txt = ltrim($txt);
2375 } // mPDF 5.7.4
2376 $this->textjuststarted = false; // mPDF 5.7.4
2378 $txt = $this->mpdf_ref->purify_utf8_text($txt);
2379 if ($this->mpdf_ref->text_input_as_HTML) {
2380 $txt = $this->mpdf_ref->all_entities_to_utf8($txt);
2383 if ($this->mpdf_ref->usingCoreFont) {
2384 $txt = mb_convert_encoding($txt, $this->mpdf_ref->mb_enc, 'UTF-8');
2386 if (preg_match("/([" . $this->mpdf_ref->pregRTLchars . "])/u", $txt)) {
2387 $this->mpdf_ref->biDirectional = true;
2390 $textvar = 0;
2391 $save_OTLtags = $this->mpdf_ref->OTLtags;
2392 $this->mpdf_ref->OTLtags = array();
2393 if ($this->mpdf_ref->useKerning) {
2394 if ($this->mpdf_ref->CurrentFont['haskernGPOS']) {
2395 if (isset($this->mpdf_ref->OTLtags['Plus'])) {
2396 $this->mpdf_ref->OTLtags['Plus'] .= ' kern';
2397 } else {
2398 $this->mpdf_ref->OTLtags['Plus'] = ' kern';
2400 } else {
2401 $textvar = ($textvar | FC_KERNING);
2405 // Use OTL OpenType Table Layout - GSUB & GPOS
2406 if (isset($this->mpdf_ref->CurrentFont['useOTL']) && $this->mpdf_ref->CurrentFont['useOTL']) {
2407 $txt = $this->mpdf_ref->otl->applyOTL($txt, $this->mpdf_ref->CurrentFont['useOTL']);
2408 $OTLdata = $this->mpdf_ref->otl->OTLdata;
2410 $this->mpdf_ref->OTLtags = $save_OTLtags;
2412 $this->mpdf_ref->magic_reverse_dir($txt, $this->mpdf_ref->directionality, $OTLdata);
2414 $this->mpdf_ref->CurrentFont['used'] = true;
2416 $sw = $this->mpdf_ref->GetStringWidth($txt, true, $OTLdata, $textvar); // also adds characters to subset
2417 // mPDF 5.7.4
2418 $this->textlength = $sw * 1 / (25.4 / $this->mpdf_ref->dpi);
2419 $this->texttotallength += $this->textlength;
2421 $pdfx = $x * $this->kp;
2422 $pdfy = -$y * $this->kp;
2424 $aixextra = sprintf(' /F%d %.3F Tf %s %s Tr %s %s ', $this->mpdf_ref->CurrentFont['i'], $this->mpdf_ref->FontSizePt, $opacitystr, $render, $fillstr, $strokestr);
2426 $path_cmd = 'q 1 0 0 1 mPDF-AXS(0.00) 0 cm '; // Align X-shift
2427 $path_cmd .= $this->mpdf_ref->Text($pdfx, $pdfy, $txt, $OTLdata, $textvar, $aixextra, 'SVG', true);
2428 $path_cmd .= " Q\n";
2430 unset($this->txt_data[0], $this->txt_data[1], $this->txt_data[2]);
2432 if (isset($current_style['font-size-parent'])) {
2433 $this->mpdf_ref->SetFontSize($current_style['font-size-parent']);
2435 } else {
2436 return ' ';
2438 // Reset font // mPDF 5.7.4
2439 $prev_style = $this->txt_style[count($this->txt_style) - 1];
2440 $style = '';
2441 $style .= ($prev_style['font-weight'] == 'bold') ? 'B' : '';
2442 $style .= ($prev_style['font-style'] == 'italic') ? 'I' : '';
2443 $size = $prev_style['font-size'] * $this->kf;
2444 $this->mpdf_ref->SetFont($prev_style['font-family'], $style, $size, false);
2446 return $path_cmd;
2449 function svgDefineTxtStyle($critere_style)
2451 // get copy of current/default txt style, and modify it with supplied attributes
2452 $tmp = count($this->txt_style) - 1;
2453 $current_style = $this->txt_style[$tmp];
2454 if (isset($critere_style['style'])) {
2455 if (preg_match('/fill:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/', $critere_style['style'], $m)) {
2456 $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);
2457 } else {
2458 $tmp = preg_replace("/(.*)fill:\s*([a-z0-9#_()]*|none)(.*)/i", "$2", $critere_style['style']);
2459 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2460 $current_style['fill'] = $tmp;
2464 // mPDF 6
2465 if (preg_match("/[^-]opacity:\s*([a-z0-9.]*|none)/i", $critere_style['style'], $m) ||
2466 preg_match("/^opacity:\s*([a-z0-9.]*|none)/i", $critere_style['style'], $m)) {
2467 $current_style['fill-opacity'] = $m[1];
2468 $current_style['stroke-opacity'] = $m[1];
2471 $tmp = preg_replace("/(.*)fill-opacity:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2472 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2473 $current_style['fill-opacity'] = $tmp;
2476 $tmp = preg_replace("/(.*)fill-rule:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2477 if ($tmp != $critere_style['style'] && $tmp != $critere_style['style']) {
2478 $current_style['fill-rule'] = $tmp;
2481 if (preg_match('/stroke:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/', $critere_style['style'], $m)) {
2482 $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);
2483 } else {
2484 $tmp = preg_replace("/(.*)stroke:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2485 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2486 $current_style['stroke'] = $tmp;
2490 $tmp = preg_replace("/(.*)stroke-linecap:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2491 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2492 $current_style['stroke-linecap'] = $tmp;
2495 $tmp = preg_replace("/(.*)stroke-linejoin:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2496 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2497 $current_style['stroke-linejoin'] = $tmp;
2500 $tmp = preg_replace("/(.*)stroke-miterlimit:\s*([a-z0-9#]*|none)(.*)/i", "$2", $critere_style['style']);
2501 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2502 $current_style['stroke-miterlimit'] = $tmp;
2505 $tmp = preg_replace("/(.*)stroke-opacity:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2506 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2507 $current_style['stroke-opacity'] = $tmp;
2510 $tmp = preg_replace("/(.*)stroke-width:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2511 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2512 $current_style['stroke-width'] = $tmp;
2515 $tmp = preg_replace("/(.*)stroke-dasharray:\s*([a-z0-9., ]*|none)(.*)/i", "$2", $critere_style['style']);
2516 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2517 $current_style['stroke-dasharray'] = $tmp;
2520 $tmp = preg_replace("/(.*)stroke-dashoffset:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2521 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2522 $current_style['stroke-dashoffset'] = $tmp;
2525 $tmp = preg_replace("/(.*)font-family:\s*([a-z0-9.\"' ,\-]*|none)(.*)/i", "$2", $critere_style['style']);
2526 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2527 $critere_style['font-family'] = $tmp;
2530 $tmp = preg_replace("/(.*)font-size:\s*([a-z0-9.]*|none)(.*)/i", "$2", $critere_style['style']);
2531 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2532 $critere_style['font-size'] = $tmp;
2535 $tmp = preg_replace("/(.*)font-weight:\s*([a-z0-9.]*|normal)(.*)/i", "$2", $critere_style['style']);
2536 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2537 $critere_style['font-weight'] = $tmp;
2540 $tmp = preg_replace("/(.*)font-style:\s*([a-z0-9.]*|normal)(.*)/i", "$2", $critere_style['style']);
2541 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2542 $critere_style['font-style'] = $tmp;
2545 $tmp = preg_replace("/(.*)font-variant:\s*([a-z0-9.]*|normal)(.*)/i", "$2", $critere_style['style']);
2546 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2547 $critere_style['font-variant'] = $tmp;
2550 $tmp = preg_replace("/(.*)text-anchor:\s*(start|middle|end)(.*)/i", "$2", $critere_style['style']);
2551 if ($tmp && $tmp != 'inherit' && $tmp != $critere_style['style']) {
2552 $critere_style['text-anchor'] = $tmp;
2555 if (isset($critere_style['font'])) {
2557 // [ [ <'font-style'> || <'font-variant'> || <'font-weight'> ]?<'font-size'> [ / <'line-height'> ]? <'font-family'> ]
2559 $tmp = preg_replace("/(.*)(italic|oblique)(.*)/i", "$2", $critere_style['font']);
2560 if ($tmp != $critere_style['font']) {
2561 if ($tmp == 'oblique') {
2562 $tmp = 'italic';
2564 $current_style['font-style'] = $tmp;
2566 $tmp = preg_replace("/(.*)(bold|bolder)(.*)/i", "$2", $critere_style['font']);
2567 if ($tmp != $critere_style['font']) {
2568 if ($tmp == 'bolder') {
2569 $tmp = 'bold';
2571 $current_style['font-weight'] = $tmp;
2574 $tmp = preg_replace("/(.*)(small\-caps)(.*)/i", "$2", $critere_style['font']);
2575 if ($tmp != $critere_style['font']) {
2576 $current_style['font-variant'] = $tmp;
2579 // select digits not followed by percent sign nor preceeded by forward slash
2580 $tmp = preg_replace("/(.*)\b(\d+)[\b|\/](.*)/i", "$2", $critere_style['font']);
2581 if ($tmp != $critere_style['font']) {
2582 $current_style['font-size'] = $this->ConvertSVGSizePts($tmp);
2583 $this->mpdf_ref->SetFont('', '', $current_style['font-size'], false);
2587 // mPDF 6
2588 if (isset($critere_style['opacity']) && $critere_style['opacity'] != 'inherit') {
2589 $current_style['fill-opacity'] = $critere_style['opacity'];
2590 $current_style['stroke-opacity'] = $critere_style['opacity'];
2592 // mPDF 6
2593 if (isset($critere_style['stroke-opacity']) && $critere_style['stroke-opacity'] != 'inherit') {
2594 $current_style['stroke-opacity'] = $critere_style['stroke-opacity'];
2596 // mPDF 6
2597 if (isset($critere_style['fill-opacity']) && $critere_style['fill-opacity'] != 'inherit') {
2598 $current_style['fill-opacity'] = $critere_style['fill-opacity'];
2600 if (isset($critere_style['fill']) && $critere_style['fill'] != 'inherit') {
2601 $current_style['fill'] = $critere_style['fill'];
2603 if (isset($critere_style['stroke']) && $critere_style['stroke'] != 'inherit') {
2604 $current_style['stroke'] = $critere_style['stroke'];
2606 if (isset($critere_style['stroke-width']) && $critere_style['stroke-width'] != 'inherit') {
2607 $current_style['stroke-width'] = $critere_style['stroke-width'];
2610 if (isset($critere_style['font-style']) && $critere_style['font-style'] != 'inherit') {
2611 if (strtolower($critere_style['font-style']) == 'oblique') {
2612 $critere_style['font-style'] = 'italic';
2614 $current_style['font-style'] = $critere_style['font-style'];
2617 if (isset($critere_style['font-weight']) && $critere_style['font-weight'] != 'inherit') {
2618 if (strtolower($critere_style['font-weight']) == 'bolder') {
2619 $critere_style['font-weight'] = 'bold';
2621 $current_style['font-weight'] = $critere_style['font-weight'];
2624 if (isset($critere_style['font-variant']) && $critere_style['font-variant'] != 'inherit') {
2625 $current_style['font-variant'] = $critere_style['font-variant'];
2628 if (isset($critere_style['font-size']) && $critere_style['font-size'] != 'inherit') {
2629 if (strpos($critere_style['font-size'], '%') !== false) {
2630 $current_style['font-size-parent'] = $current_style['font-size'];
2632 $current_style['font-size'] = $this->ConvertSVGSizePts($critere_style['font-size']);
2633 $this->mpdf_ref->SetFont('', '', $current_style['font-size'], false);
2636 if (isset($critere_style['font-family']) && $critere_style['font-family'] != 'inherit') {
2637 $v = $critere_style['font-family'];
2638 $aux_fontlist = explode(",", $v);
2639 $found = 0;
2641 $svgfontstyle = 'R';
2642 $svgfontstyle .= (isset($current_style['font-weight']) && $current_style['font-weight'] == 'bold') ? 'B' : '';
2643 $svgfontstyle .= (isset($current_style['font-style']) && $current_style['font-style'] == 'italic') ? 'I' : '';
2644 $svgfontstyle .= (isset($current_style['font-variant']) && $current_style['font-variant'] == 'small-caps') ? 'S' : '';
2646 foreach ($aux_fontlist AS $f) {
2647 $fonttype = trim($f);
2648 $fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
2649 $fonttype = preg_replace('/ /', '', $fonttype);
2650 $v = strtolower(trim($fonttype));
2651 if (isset($this->mpdf_ref->fonttrans[$v]) && $this->mpdf_ref->fonttrans[$v]) {
2652 $v = $this->mpdf_ref->fonttrans[$v];
2654 if ((!$this->mpdf_ref->usingCoreFont && in_array($v, $this->mpdf_ref->available_unifonts)) ||
2655 ($this->mpdf_ref->usingCoreFont && in_array($v, array('courier', 'times', 'helvetica', 'arial'))) ||
2656 in_array($v, array('sjis', 'uhc', 'big5', 'gb')) || isset($this->svg_font[$v][$svgfontstyle])) { // mPDF 6
2657 $current_style['font-family'] = $v;
2658 $found = 1;
2659 break;
2662 if (!$found) {
2663 foreach ($aux_fontlist AS $f) {
2664 $fonttype = trim($f);
2665 $fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
2666 $fonttype = preg_replace('/ /', '', $fonttype);
2667 $v = strtolower(trim($fonttype));
2668 if (isset($this->mpdf_ref->fonttrans[$v]) && $this->mpdf_ref->fonttrans[$v]) {
2669 $v = $this->mpdf_ref->fonttrans[$v];
2671 if (in_array($v, $this->mpdf_ref->sans_fonts) || in_array($v, $this->mpdf_ref->serif_fonts) || in_array($v, $this->mpdf_ref->mono_fonts) || isset($this->svg_font[$v][$svgfontstyle])) { // mPDF 6
2672 $current_style['font-family'] = $v;
2673 break;
2678 if (isset($critere_style['text-anchor']) && $critere_style['text-anchor'] != 'inherit') {
2679 $current_style['text-anchor'] = $critere_style['text-anchor'];
2682 // add current style to text style array (will remove it later after writing text to svg_string)
2683 array_push($this->txt_style, $current_style);
2687 // fonction ajoutant un gradient
2688 function svgAddGradient($id, $array_gradient)
2691 $this->svg_gradient[$id] = $array_gradient;
2695 // Ajoute une couleur dans le gradient correspondant
2697 // function ecrivant dans le svgstring
2698 function svgWriteString($content)
2701 $this->svg_string .= $content;
2704 // analise le svg et renvoie aux fonctions precedente our le traitement
2705 function ImageSVG($data)
2707 // Try to clean up the start of the file
2708 // removing DOCTYPE fails with this:
2710 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd"
2712 <!ELEMENT Paragraph (#PCDATA)>
2715 //$data = preg_replace('/<!DOCTYPE.*? >/is', '', $data);
2716 //$data = preg_replace('/<\?xml.*? >/is', '', $data);
2718 $data = preg_replace('/^.*?<svg([> ])/is', '<svg\\1', $data); // mPDF 5.7.4
2720 $data = preg_replace('/<!--.*?-->/is', '', $data); // mPDF 5.7.4
2721 // Converts < to &lt; when not a tag
2722 $data = preg_replace('/<([^!?\/a-zA-Z_:])/i', '&lt;\\1', $data); // mPDF 5.7.4
2724 if (_SVG_AUTOFONT) {
2725 $data = $this->markScriptToLang($data);
2728 $this->svg_info = array();
2729 $last_gradid = ''; // mPDF 6
2730 $last_svg_fontid = ''; // mPDF 6
2731 $last_svg_fontdefw = ''; // mPDF 6
2732 $last_svg_fontstyle = ''; // mPDF 6
2734 if (preg_match('/<!ENTITY/si', $data)) {
2735 // Get User-defined entities
2736 preg_match_all('/<!ENTITY\s+([a-z]+)\s+\"(.*?)\">/si', $data, $ent);
2737 // Replace entities
2738 for ($i = 0; $i < count($ent[0]); $i++) {
2739 $data = preg_replace('/&' . preg_quote($ent[1][$i], '/') . ';/is', $ent[2][$i], $data);
2743 if (preg_match('/xlink:href\s*=/si', $data)) {
2744 // GRADIENTS
2745 // Get links
2746 preg_match_all('/(<(linearGradient|radialgradient)[^>]*)xlink:href\s*=\s*["\']#(.*?)["\'](.*?)\/>/si', $data, $links);
2747 if (count($links[0])) {
2748 $links[5] = array();
2750 // Delete links from data - keeping in $links
2751 for ($i = 0; $i < count($links[0]); $i++) {
2752 $links[5][$i] = 'tmpLink' . RAND(100000, 9999999);
2753 $data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', '<MYLINKS' . $links[5][$i] . '>', $data);
2755 // Get targets
2756 preg_match_all('/<(linearGradient|radialgradient)([^>]*)id\s*=\s*["\'](.*?)["\'](.*?)>(.*?)<\/(linearGradient|radialgradient)>/si', $data, $m);
2757 $targets = array();
2758 $stops = array();
2759 // keeping in $targets
2760 for ($i = 0; $i < count($m[0]); $i++) {
2761 $stops[$m[3][$i]] = $m[5][$i];
2763 // Add back links this time as targets (gradients)
2764 for ($i = 0; $i < count($links[0]); $i++) {
2765 $def = $links[1][$i] . ' ' . $links[4][$i] . '>' . $stops[$links[3][$i]] . '</' . $links[2][$i] . '>';
2766 $data = preg_replace('/<MYLINKS' . $links[5][$i] . '>/is', $def, $data);
2769 // mPDF 5.7.4
2770 // <TREF>
2771 preg_match_all('/<tref ([^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)\/>/si', $data, $links);
2772 for ($i = 0; $i < count($links[0]); $i++) {
2774 // Get the item to use from defs
2775 $insert = '';
2776 if (preg_match('/<text [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*>(.*?)<\/text>/si', $data, $m)) {
2777 $insert = $m[1];
2779 if ($insert) {
2780 $data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', $insert, $data);
2784 // mPDF 5.7.2
2785 // <USE>
2786 preg_match_all('/<use ([^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)\/>/si', $data, $links);
2787 for ($i = 0; $i < count($links[0]); $i++) {
2789 // Get the item to use from defs
2790 $insert = '';
2791 if (preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*\/>/si', $data, $m)) {
2792 $insert = $m[0];
2794 if (!$insert && preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\']/si', $data, $m)) {
2796 if (preg_match('/<' . $m[1] . '[^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*>.*?<\/' . $m[1] . '>/si', $data, $m)) {
2797 $insert = $m[0];
2801 if ($insert) {
2803 $inners = $links[1][$i] . ' ' . $links[3][$i];
2804 // Change x,y coords to translate()
2805 if (preg_match('/y\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
2806 $y = $m[1];
2807 } else {
2808 $y = 0;
2810 if (preg_match('/x\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
2811 $x = $m[1];
2812 } else {
2813 $x = 0;
2815 if ($x || $y) {
2816 $inners = preg_replace('/(y|x)\s*=\s*["\']([^>]*?)["\']/', '', $inners);
2817 if (preg_match('/transform\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
2818 if (preg_match('/translate\(\s*([0-9\.]+)\s*,\s*([0-9\.]+)\s*\)/', $m[1], $mm)) {
2819 $transform = $m[1]; // transform="...."
2820 $x += $mm[1];
2821 $y += $mm[2];
2822 $transform = preg_replace('/' . preg_quote($mm[0], '/') . '/', '', $transform);
2823 $transform = 'transform="' . $transform . ' translate(' . $x . ', ' . $y . ')"';
2824 $inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', $transform, $inners);
2825 } else {
2826 $inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', 'transform="' . $m[1] . ' translate(' . $x . ', ' . $y . ')"', $inners);
2828 } else {
2829 $inners .= ' transform="translate(' . $x . ', ' . $y . ')"';
2833 $replacement = '<g ' . $inners . '>' . $insert . '</g>';
2834 $data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', $replacement, $data);
2836 preg_match_all('/<use ([^>]*)xlink:href\s*=\s*["\']#([^>]*?)["\']([^>]*)>\s*<\/use>/si', $data, $links);
2837 for ($i = 0; $i < count($links[0]); $i++) {
2839 // Get the item to use from defs
2840 $insert = '';
2841 if (preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*\/>/si', $data, $m)) {
2842 $insert = $m[0];
2844 if (!$insert && preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\']/si', $data, $m)) {
2846 if (preg_match('/<' . $m[1] . '[^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*>.*?<\/' . $m[1] . '>/si', $data, $m)) {
2847 $insert = $m[0];
2851 if ($insert) {
2853 $inners = $links[1][$i] . ' ' . $links[3][$i];
2854 // Change x,y coords to translate()
2855 if (preg_match('/y\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
2856 $y = $m[1];
2857 } else {
2858 $y = 0;
2860 if (preg_match('/x\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
2861 $x = $m[1];
2862 } else {
2863 $x = 0;
2865 if ($x || $y) {
2866 $inners = preg_replace('/(y|x)\s*=\s*["\']([^>]*?)["\']/', '', $inners);
2867 if (preg_match('/transform\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
2868 if (preg_match('/translate\(\s*([0-9\.]+)\s*,\s*([0-9\.]+)\s*\)/', $m[1], $mm)) {
2869 $transform = $m[1]; // transform="...."
2870 $x += $mm[1];
2871 $y += $mm[2];
2872 $transform = preg_replace('/' . preg_quote($mm[0], '/') . '/', '', $transform);
2873 $transform = 'transform="' . $transform . ' translate(' . $x . ', ' . $y . ')"';
2874 $inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', $transform, $inners);
2875 } else {
2876 $inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', 'transform="' . $m[1] . ' translate(' . $x . ', ' . $y . ')"', $inners);
2878 } else {
2879 $inners .= ' transform="translate(' . $x . ', ' . $y . ')"';
2882 $replacement = '<g ' . $inners . '>' . $insert . '</g>';
2883 $data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', $replacement, $data);
2888 // Removes <pattern>
2889 $data = preg_replace('/<pattern.*?<\/pattern>/is', '', $data);
2890 // Removes <marker>
2891 $data = preg_replace('/<marker.*?<\/marker>/is', '', $data);
2893 $this->svg_info['data'] = $data;
2895 $this->svg_string = '';
2898 // chargement unique des fonctions
2899 if (!function_exists("xml_svg2pdf_start")) {
2901 function xml_svg2pdf_start($parser, $name, $attribs)
2904 // definition
2905 global $svg_class, $last_gradid, $last_svg_fontid, $last_svg_fontdefw, $last_svg_fontstyle; // mPDF 6
2906 // mPDF 6
2907 if (strtolower($name) == 'font') {
2908 $last_svg_fontid = '';
2909 if (isset($attribs['horiz-adv-x']) && $attribs['horiz-adv-x']) {
2910 $last_svg_fontdefw = $attribs['horiz-adv-x'];
2912 return;
2914 // mPDF 6
2915 else if (strtolower($name) == 'font-face') {
2916 $last_svg_fontstyle = 'R';
2917 $last_svg_fontstyle .= (isset($attribs['font-weight']) && $attribs['font-weight'] == 'bold') ? 'B' : '';
2918 $last_svg_fontstyle .= (isset($attribs['font-style']) && $attribs['font-style'] == 'italic') ? 'I' : '';
2919 $last_svg_fontstyle .= (isset($attribs['font-variant']) && $attribs['font-variant'] == 'small-caps') ? 'S' : '';
2921 if (isset($attribs['font-family']) && $attribs['font-family']) {
2922 $tmp_svg_font = array(
2923 'units-per-em' => (isset($attribs['units-per-em']) ? $attribs['units-per-em'] : ''),
2924 'd' => '',
2925 'glyphs' => array()
2927 $last_svg_fontid = strtolower($attribs['font-family']);
2928 if ($last_svg_fontdefw) {
2929 $tmp_svg_font['horiz-adv-x'] = $last_svg_fontdefw;
2930 } else {
2931 $tmp_svg_font['horiz-adv-x'] = 500;
2933 $svg_class->svg_font[$last_svg_fontid][$last_svg_fontstyle] = $tmp_svg_font;
2935 return;
2937 // mPDF 6
2938 else if (strtolower($name) == 'missing-glyph') {
2939 if ($last_svg_fontid && isset($attribs['horiz-adv-x'])) {
2940 $svg_class->svg_font[$last_svg_fontid][$last_svg_fontstyle]['horiz-adv-x'] = (isset($attribs['horiz-adv-x']) ? $attribs['horiz-adv-x'] : '');
2941 $svg_class->svg_font[$last_svg_fontid][$last_svg_fontstyle]['d'] = (isset($attribs['d']) ? $attribs['d'] : '');
2943 return;
2945 // mPDF 6
2946 else if (strtolower($name) == 'glyph') {
2947 if ($last_svg_fontid && isset($attribs['unicode'])) {
2948 $svg_class->svg_font[$last_svg_fontid][$last_svg_fontstyle]['glyphs'][$attribs['unicode']] = array(
2949 'horiz-adv-x' => (isset($attribs['horiz-adv-x']) ? $attribs['horiz-adv-x'] : $last_svg_fontdefw),
2950 'd' => (isset($attribs['d']) ? $attribs['d'] : ''),
2953 return;
2955 // mPDF 5.7.2
2956 else if (strtolower($name) == 'lineargradient') {
2957 $tmp_gradient = array(
2958 'type' => 'linear',
2959 'transform' => (isset($attribs['gradientTransform']) ? $attribs['gradientTransform'] : ''),
2960 'units' => (isset($attribs['gradientUnits']) ? $attribs['gradientUnits'] : ''),
2961 'spread' => (isset($attribs['spreadMethod']) ? $attribs['spreadMethod'] : ''),
2962 'color' => array()
2964 if (isset($attribs['x1']))
2965 $tmp_gradient['info']['x1'] = $attribs['x1'];
2966 if (isset($attribs['y1']))
2967 $tmp_gradient['info']['y1'] = $attribs['y1'];
2968 if (isset($attribs['x2']))
2969 $tmp_gradient['info']['x2'] = $attribs['x2'];
2970 if (isset($attribs['y2']))
2971 $tmp_gradient['info']['y2'] = $attribs['y2'];
2972 $last_gradid = $attribs['id'];
2973 $svg_class->svgAddGradient($attribs['id'], $tmp_gradient);
2974 return;
2976 else if (strtolower($name) == 'radialgradient') {
2977 $tmp_gradient = array(
2978 'type' => 'radial',
2979 'transform' => (isset($attribs['gradientTransform']) ? $attribs['gradientTransform'] : ''),
2980 'units' => (isset($attribs['gradientUnits']) ? $attribs['gradientUnits'] : ''),
2981 'spread' => (isset($attribs['spreadMethod']) ? $attribs['spreadMethod'] : ''),
2982 'color' => array()
2984 if (isset($attribs['cx']))
2985 $tmp_gradient['info']['x0'] = $attribs['cx'];
2986 if (isset($attribs['cy']))
2987 $tmp_gradient['info']['y0'] = $attribs['cy'];
2988 if (isset($attribs['fx']))
2989 $tmp_gradient['info']['x1'] = $attribs['fx'];
2990 if (isset($attribs['fy']))
2991 $tmp_gradient['info']['y1'] = $attribs['fy'];
2992 if (isset($attribs['r']))
2993 $tmp_gradient['info']['r'] = $attribs['r'];
2994 $last_gradid = $attribs['id'];
2995 $svg_class->svgAddGradient($attribs['id'], $tmp_gradient);
2996 return;
2998 else if (strtolower($name) == 'stop') {
2999 if (!$last_gradid)
3000 return;
3001 $color = '#000000';
3002 if (isset($attribs['style']) AND preg_match('/stop-color:\s*([^;]*)/i', $attribs['style'], $m)) {
3003 $color = trim($m[1]);
3004 } else if (isset($attribs['stop-color']) && $attribs['stop-color']) {
3005 $color = $attribs['stop-color'];
3007 $col = $svg_class->mpdf_ref->ConvertColor($color);
3008 if (!$col) {
3009 $col = $svg_class->mpdf_ref->ConvertColor('#000000');
3010 } // In case "transparent" or "inherit" returned
3011 if ($col{0} == 3 || $col{0} == 5) { // RGB
3012 $color_final = sprintf('%.3F %.3F %.3F', ord($col{1}) / 255, ord($col{2}) / 255, ord($col{3}) / 255);
3013 $svg_class->svg_gradient[$last_gradid]['colorspace'] = 'RGB';
3014 } else if ($col{0} == 4 || $col{0} == 6) { // CMYK
3015 $color_final = sprintf('%.3F %.3F %.3F %.3F', ord($col{1}) / 100, ord($col{2}) / 100, ord($col{3}) / 100, ord($col{4}) / 100);
3016 $svg_class->svg_gradient[$last_gradid]['colorspace'] = 'CMYK';
3017 } else if ($col{0} == 1) { // Grayscale
3018 $color_final = sprintf('%.3F', ord($col{1}) / 255);
3019 $svg_class->svg_gradient[$last_gradid]['colorspace'] = 'Gray';
3022 $stop_opacity = 1;
3023 if (isset($attribs['style']) AND preg_match('/stop-opacity:\s*([0-9.]*)/i', $attribs['style'], $m)) {
3024 $stop_opacity = $m[1];
3025 } else if (isset($attribs['stop-opacity'])) {
3026 $stop_opacity = $attribs['stop-opacity'];
3027 } else if ($col{0} == 5) { // RGBa
3028 $stop_opacity = ord($col{4} / 100);
3029 } else if ($col{0} == 6) { // CMYKa
3030 $stop_opacity = ord($col{5} / 100);
3033 $tmp_color = array(
3034 'color' => $color_final,
3035 'offset' => (isset($attribs['offset']) ? $attribs['offset'] : ''),
3036 'opacity' => $stop_opacity
3038 array_push($svg_class->svg_gradient[$last_gradid]['color'], $tmp_color);
3039 return;
3041 if ($svg_class->inDefs) {
3042 return;
3045 $svg_class->xbase = 0;
3046 $svg_class->ybase = 0;
3047 switch (strtolower($name)) {
3049 // Don't output stuff inside <defs>
3050 case 'defs':
3051 $svg_class->inDefs = true;
3052 return;
3054 case 'svg':
3055 $svg_class->svgOffset($attribs);
3056 break;
3058 case 'path':
3059 $path = $attribs['d'];
3060 preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $path, $commands, PREG_SET_ORDER);
3061 $path_cmd = '';
3062 $svg_class->subPathInit = true;
3063 $svg_class->pathBBox = array(999999, 999999, -999999, -999999);
3064 foreach ($commands as $c) {
3065 if ((isset($c) && count($c) == 3) || (isset($c[2]) && $c[2] == '')) {
3066 list($tmp, $command, $arguments) = $c;
3067 } else {
3068 list($tmp, $command) = $c;
3069 $arguments = '';
3072 $path_cmd .= $svg_class->svgPath($command, $arguments);
3074 if ($svg_class->pathBBox[2] == -1999998) {
3075 $svg_class->pathBBox[2] = 100;
3077 if ($svg_class->pathBBox[3] == -1999998) {
3078 $svg_class->pathBBox[3] = 100;
3080 if ($svg_class->pathBBox[0] == 999999) {
3081 $svg_class->pathBBox[0] = 0;
3083 if ($svg_class->pathBBox[1] == 999999) {
3084 $svg_class->pathBBox[1] = 0;
3086 $critere_style = $attribs;
3087 unset($critere_style['d']);
3088 $path_style = $svg_class->svgDefineStyle($critere_style);
3089 break;
3091 case 'rect':
3092 if (!isset($attribs['x'])) {
3093 $attribs['x'] = 0;
3095 if (!isset($attribs['y'])) {
3096 $attribs['y'] = 0;
3098 if (!isset($attribs['rx'])) {
3099 $attribs['rx'] = 0;
3101 if (!isset($attribs['ry'])) {
3102 $attribs['ry'] = 0;
3104 $arguments = array();
3105 if (isset($attribs['x']))
3106 $arguments['x'] = $attribs['x'];
3107 if (isset($attribs['y']))
3108 $arguments['y'] = $attribs['y'];
3109 if (isset($attribs['width']))
3110 $arguments['w'] = $attribs['width'];
3111 if (isset($attribs['height']))
3112 $arguments['h'] = $attribs['height'];
3113 if (isset($attribs['rx']))
3114 $arguments['rx'] = $attribs['rx'];
3115 if (isset($attribs['ry']))
3116 $arguments['ry'] = $attribs['ry'];
3117 $path_cmd = $svg_class->svgRect($arguments);
3118 $critere_style = $attribs;
3119 unset($critere_style['x'], $critere_style['y'], $critere_style['rx'], $critere_style['ry'], $critere_style['height'], $critere_style['width']);
3120 $path_style = $svg_class->svgDefineStyle($critere_style);
3121 break;
3123 case 'circle':
3124 if (!isset($attribs['cx'])) {
3125 $attribs['cx'] = 0;
3127 if (!isset($attribs['cy'])) {
3128 $attribs['cy'] = 0;
3130 $arguments = array();
3131 if (isset($attribs['cx']))
3132 $arguments['cx'] = $attribs['cx'];
3133 if (isset($attribs['cy']))
3134 $arguments['cy'] = $attribs['cy'];
3135 if (isset($attribs['r']))
3136 $arguments['rx'] = $attribs['r'];
3137 if (isset($attribs['r']))
3138 $arguments['ry'] = $attribs['r'];
3139 $path_cmd = $svg_class->svgEllipse($arguments);
3140 $critere_style = $attribs;
3141 unset($critere_style['cx'], $critere_style['cy'], $critere_style['r']);
3142 $path_style = $svg_class->svgDefineStyle($critere_style);
3143 break;
3145 case 'ellipse':
3146 if (!isset($attribs['cx'])) {
3147 $attribs['cx'] = 0;
3149 if (!isset($attribs['cy'])) {
3150 $attribs['cy'] = 0;
3152 $arguments = array();
3153 if (isset($attribs['cx']))
3154 $arguments['cx'] = $attribs['cx'];
3155 if (isset($attribs['cy']))
3156 $arguments['cy'] = $attribs['cy'];
3157 if (isset($attribs['rx']))
3158 $arguments['rx'] = $attribs['rx'];
3159 if (isset($attribs['ry']))
3160 $arguments['ry'] = $attribs['ry'];
3161 $path_cmd = $svg_class->svgEllipse($arguments);
3162 $critere_style = $attribs;
3163 unset($critere_style['cx'], $critere_style['cy'], $critere_style['rx'], $critere_style['ry']);
3164 $path_style = $svg_class->svgDefineStyle($critere_style);
3165 break;
3167 case 'line':
3168 $arguments = array();
3169 $arguments[0] = (isset($attribs['x1']) ? $attribs['x1'] : '');
3170 $arguments[1] = (isset($attribs['y1']) ? $attribs['y1'] : '');
3171 $arguments[2] = (isset($attribs['x2']) ? $attribs['x2'] : '');
3172 $arguments[3] = (isset($attribs['y2']) ? $attribs['y2'] : '');
3173 $path_cmd = $svg_class->svgPolyline($arguments, false);
3174 $critere_style = $attribs;
3175 unset($critere_style['x1'], $critere_style['y1'], $critere_style['x2'], $critere_style['y2']);
3176 $path_style = $svg_class->svgDefineStyle($critere_style);
3177 break;
3179 case 'polyline':
3180 $path = $attribs['points'];
3181 preg_match_all('/[0-9\-\.]*/', $path, $tmp, PREG_SET_ORDER);
3182 $arguments = array();
3183 for ($i = 0; $i < count($tmp); $i++) {
3184 if ($tmp[$i][0] != '') {
3185 array_push($arguments, $tmp[$i][0]);
3188 $path_cmd = $svg_class->svgPolyline($arguments);
3189 $critere_style = $attribs;
3190 unset($critere_style['points']);
3191 $path_style = $svg_class->svgDefineStyle($critere_style);
3192 break;
3194 case 'polygon':
3195 $path = $attribs['points'];
3196 preg_match_all('/([\-]*[0-9\.]+)/', $path, $tmp);
3197 $arguments = array();
3198 for ($i = 0; $i < count($tmp[0]); $i++) {
3199 if ($tmp[0][$i] != '') {
3200 array_push($arguments, $tmp[0][$i]);
3203 $path_cmd = $svg_class->svgPolygon($arguments);
3204 // definition du style de la forme:
3205 $critere_style = $attribs;
3206 unset($critere_style['points']);
3207 $path_style = $svg_class->svgDefineStyle($critere_style);
3208 break;
3210 // mPDF 5.7.4 Embedded image
3211 case 'image':
3212 if (isset($attribs['xlink:href']) && $attribs['xlink:href']) {
3213 $svg_class->svgImage($attribs);
3215 break;
3218 case 'a':
3219 if (isset($attribs['xlink:href'])) {
3220 unset($attribs['xlink:href']); // this should be a hyperlink
3221 // not handled like a xlink:href in other elements
3222 } // then continue like a <g>
3223 case 'g':
3224 $array_style = $svg_class->svgDefineStyle($attribs);
3225 if (!empty($array_style['transformations'])) {
3226 // If in the middle of <text> element, add to textoutput, else WriteString
3227 if ($svg_class->intext) {
3228 $svg_class->textoutput .= ' q ' . $array_style['transformations'];
3229 } // mPDF 5.7.4
3230 else {
3231 $svg_class->svgWriteString(' q ' . $array_style['transformations']);
3234 array_push($svg_class->svg_style, $array_style);
3236 $svg_class->svgDefineTxtStyle($attribs);
3238 break;
3240 case 'text':
3241 $svg_class->textlength = 0; // mPDF 5.7.4
3242 $svg_class->texttotallength = 0; // mPDF 5.7.4
3243 $svg_class->textoutput = ''; // mPDF 5.7.4
3244 $svg_class->textanchor = 'start'; // mPDF 5.7.4
3245 $svg_class->textXorigin = 0; // mPDF 5.7.4
3246 $svg_class->textYorigin = 0; // mPDF 5.7.4
3248 $svg_class->intext = true; // mPDF 5.7.4
3250 $styl = '';
3251 if (_SVG_CLASSES && isset($attribs['class']) && $attribs['class']) {
3252 $classes = preg_split('/\s+/', trim($attribs['class']));
3253 foreach ($classes AS $class) {
3254 if (isset($svg_class->mpdf_ref->cssmgr->CSS['CLASS>>' . strtoupper($class)])) {
3255 $c = $svg_class->mpdf_ref->cssmgr->CSS['CLASS>>' . strtoupper($class)];
3256 foreach ($c AS $prop => $val) {
3257 $styl .= strtolower($prop) . ':' . $val . ';';
3263 if (_SVG_AUTOFONT && isset($attribs['lang']) && $attribs['lang']) {
3264 if (!$svg_class->mpdf_ref->usingCoreFont) {
3265 if ($attribs['lang'] != $svg_class->mpdf_ref->default_lang) {
3266 list ($coreSuitable, $mpdf_unifont) = GetLangOpts($attribs['lang'], $svg_class->mpdf_ref->useAdobeCJK, $svg_class->mpdf_ref->fontdata);
3267 if ($mpdf_unifont) {
3268 $styl .= 'font-family:' . $mpdf_unifont . ';';
3274 if ($styl) {
3275 if (isset($attribs['style'])) {
3276 $attribs['style'] = $styl . $attribs['style'];
3277 } else {
3278 $attribs['style'] = $styl;
3282 $array_style = $svg_class->svgDefineStyle($attribs);
3283 if (!empty($array_style['transformations'])) {
3284 $svg_class->textoutput .= ' q ' . $array_style['transformations']; // mPDF 5.7.4
3286 array_push($svg_class->svg_style, $array_style);
3288 $svg_class->txt_data = array();
3289 $x = isset($attribs['x']) ? $svg_class->ConvertSVGSizePixels($attribs['x'], 'x') : 0; // mPDF 5.7.4
3290 $y = isset($attribs['y']) ? $svg_class->ConvertSVGSizePixels($attribs['y'], 'y') : 0; // mPDF 5.7.4
3291 $x += isset($attribs['dx']) ? $svg_class->ConvertSVGSizePixels($attribs['dx'], 'x') : 0; // mPDF 5.7.4
3292 $y += isset($attribs['dy']) ? $svg_class->ConvertSVGSizePixels($attribs['dy'], 'y') : 0; // mPDF 5.7.4
3294 $svg_class->txt_data[0] = $x; // mPDF 5.7.4
3295 $svg_class->txt_data[1] = $y; // mPDF 5.7.4
3296 $critere_style = $attribs;
3297 unset($critere_style['x'], $critere_style['y']);
3298 $svg_class->svgDefineTxtStyle($critere_style);
3300 $svg_class->textanchor = $svg_class->txt_style[count($svg_class->txt_style) - 1]['text-anchor']; // mPDF 5.7.4
3301 $svg_class->textXorigin = $svg_class->txt_data[0]; // mPDF 5.7.4
3302 $svg_class->textYorigin = $svg_class->txt_data[1]; // mPDF 5.7.4
3303 $svg_class->textjuststarted = true; // mPDF 5.7.4
3305 break;
3307 // mPDF 5.7.4
3308 case 'tspan':
3310 // OUTPUT CHUNK(s) UP To NOW (svgText updates $svg_class->textlength)
3311 $p_cmd = $svg_class->svgText();
3312 $svg_class->textoutput .= $p_cmd;
3313 $tmp = count($svg_class->svg_style) - 1;
3314 $current_style = $svg_class->svg_style[$tmp];
3316 $styl = '';
3317 if (_SVG_CLASSES && isset($attribs['class']) && $attribs['class']) {
3318 $classes = preg_split('/\s+/', trim($attribs['class']));
3319 foreach ($classes AS $class) {
3320 if (isset($svg_class->mpdf_ref->cssmgr->CSS['CLASS>>' . strtoupper($class)])) {
3321 $c = $svg_class->mpdf_ref->cssmgr->CSS['CLASS>>' . strtoupper($class)];
3322 foreach ($c AS $prop => $val) {
3323 $styl .= strtolower($prop) . ':' . $val . ';';
3329 if (_SVG_AUTOFONT && isset($attribs['lang']) && $attribs['lang']) {
3330 if (!$svg_class->mpdf_ref->usingCoreFont) {
3331 if ($attribs['lang'] != $svg_class->mpdf_ref->default_lang) {
3332 list ($coreSuitable, $mpdf_unifont) = GetLangOpts($attribs['lang'], $svg_class->mpdf_ref->useAdobeCJK, $svg_class->mpdf_ref->fontdata);
3333 if ($mpdf_unifont) {
3334 $styl .= 'font-family:' . $mpdf_unifont . ';';
3340 if ($styl) {
3341 if (isset($attribs['style'])) {
3342 $attribs['style'] = $styl . $attribs['style'];
3343 } else {
3344 $attribs['style'] = $styl;
3348 $array_style = $svg_class->svgDefineStyle($attribs);
3350 $svg_class->txt_data = array();
3353 // If absolute position adjustment (x or y), creates new block of text for text-alignment
3354 if (isset($attribs['x']) || isset($attribs['y'])) {
3355 // If text-anchor middle|end, adjust
3356 if ($svg_class->textanchor == 'end') {
3357 $tx = -$svg_class->texttotallength;
3358 } else if ($svg_class->textanchor == 'middle') {
3359 $tx = -$svg_class->texttotallength / 2;
3360 } else {
3361 $tx = 0;
3363 while (preg_match('/mPDF-AXS\((.*?)\)/', $svg_class->textoutput, $m)) {
3364 if ($tx) {
3365 $txk = $m[1] + ($tx * $svg_class->kp);
3366 $svg_class->textoutput = preg_replace('/mPDF-AXS\((.*?)\)/', sprintf('%.4F', $txk), $svg_class->textoutput, 1);
3367 } else {
3368 $svg_class->textoutput = preg_replace('/mPDF-AXS\((.*?)\)/', '\\1', $svg_class->textoutput, 1);
3372 $svg_class->svgWriteString($svg_class->textoutput);
3374 $svg_class->textXorigin += $svg_class->textlength;
3375 $currentX = $svg_class->textXorigin;
3376 $currentY = $svg_class->textYorigin;
3377 $svg_class->textlength = 0;
3378 $svg_class->texttotallength = 0;
3379 $svg_class->textoutput = '';
3381 $x = isset($attribs['x']) ? $svg_class->ConvertSVGSizePixels($attribs['x'], 'x') : $currentX;
3382 $y = isset($attribs['y']) ? $svg_class->ConvertSVGSizePixels($attribs['y'], 'y') : $currentY;
3384 $svg_class->txt_data[0] = $x;
3385 $svg_class->txt_data[1] = $y;
3386 $critere_style = $attribs;
3387 unset($critere_style['x'], $critere_style['y']);
3388 $svg_class->svgDefineTxtStyle($critere_style);
3390 $svg_class->textanchor = $svg_class->txt_style[count($svg_class->txt_style) - 1]['text-anchor'];
3391 $svg_class->textXorigin = $x;
3392 $svg_class->textYorigin = $y;
3393 } else {
3395 $svg_class->textXorigin += $svg_class->textlength;
3396 $currentX = $svg_class->textXorigin;
3397 $currentY = $svg_class->textYorigin;
3399 $currentX += isset($attribs['dx']) ? $svg_class->ConvertSVGSizePixels($attribs['dx'], 'x') : 0;
3400 $currentY += isset($attribs['dy']) ? $svg_class->ConvertSVGSizePixels($attribs['dy'], 'y') : 0;
3402 $svg_class->txt_data[0] = $currentX;
3403 $svg_class->txt_data[1] = $currentY;
3404 $critere_style = $attribs;
3405 unset($critere_style['x'], $critere_style['y']);
3406 $svg_class->svgDefineTxtStyle($critere_style);
3407 $svg_class->textXorigin = $currentX;
3408 $svg_class->textYorigin = $currentY;
3411 if (!empty($array_style['transformations'])) {
3412 $svg_class->textoutput .= ' q ' . $array_style['transformations'];
3414 array_push($svg_class->svg_style, $array_style);
3416 break;
3420 //insertion des path et du style dans le flux de donné general.
3421 if (isset($path_cmd) && $path_cmd) {
3422 // mPDF 5.0
3423 list($prestyle, $poststyle) = $svg_class->svgStyle($path_style, $attribs, strtolower($name));
3424 if (isset($path_style['transformations']) && $path_style['transformations']) { // transformation on an element
3425 $svg_class->svgWriteString(" q " . $path_style['transformations'] . $prestyle . $path_cmd . $poststyle . " Q\n");
3426 } else {
3427 $svg_class->svgWriteString(" q " . $prestyle . $path_cmd . $poststyle . " Q\n"); // mPDF 5.7.4
3432 function characterData($parser, $data)
3434 global $svg_class;
3435 if ($svg_class->inDefs) {
3436 return;
3437 } // mPDF 5.7.2
3438 if (isset($svg_class->txt_data[2])) {
3439 $svg_class->txt_data[2] .= $data;
3440 } else {
3441 $svg_class->txt_data[2] = $data;
3442 $svg_class->txt_data[0] = $svg_class->textXorigin;
3443 $svg_class->txt_data[1] = $svg_class->textYorigin;
3447 function xml_svg2pdf_end($parser, $name)
3449 global $svg_class;
3450 // mPDF 5.7.2
3451 // Don't output stuff inside <defs>
3452 if ($name == 'defs') {
3453 $svg_class->inDefs = false;
3454 return;
3456 if ($svg_class->inDefs) {
3457 return;
3459 switch ($name) {
3461 case "g":
3462 case "a":
3463 if ($svg_class->intext) {
3464 $p_cmd = $svg_class->svgText();
3465 $svg_class->textoutput .= $p_cmd;
3468 $tmp = count($svg_class->svg_style) - 1;
3469 $current_style = $svg_class->svg_style[$tmp];
3470 if (!empty($current_style['transformations'])) {
3471 // If in the middle of <text> element, add to textoutput, else WriteString
3472 if ($svg_class->intext) {
3473 $svg_class->textoutput .= " Q\n";
3474 } // mPDF 5.7.4
3475 else {
3476 $svg_class->svgWriteString(" Q\n");
3479 array_pop($svg_class->svg_style);
3480 array_pop($svg_class->txt_style);
3481 if ($svg_class->intext) {
3482 $svg_class->textXorigin += $svg_class->textlength;
3483 $svg_class->textlength = 0;
3486 break;
3487 case 'font':
3488 $last_svg_fontdefw = '';
3489 break;
3490 case 'font-face':
3491 $last_svg_fontid = '';
3492 $last_svg_fontstyle = '';
3493 break;
3494 case 'radialgradient':
3495 case 'lineargradient':
3496 $last_gradid = '';
3497 break;
3498 case "text":
3499 $svg_class->txt_data[2] = rtrim($svg_class->txt_data[2]); // mPDF 5.7.4
3500 $path_cmd = $svg_class->svgText();
3501 $svg_class->textoutput .= $path_cmd; // mPDF 5.7.4
3502 $tmp = count($svg_class->svg_style) - 1;
3503 $current_style = $svg_class->svg_style[$tmp];
3504 if (!empty($current_style['transformations'])) {
3505 $svg_class->textoutput .= " Q\n"; // mPDF 5.7.4
3507 array_pop($svg_class->svg_style);
3508 array_pop($svg_class->txt_style); // mPDF 5.7.4
3509 // mPDF 5.7.4
3510 // If text-anchor middle|end, adjust
3511 if ($svg_class->textanchor == 'end') {
3512 $tx = -$svg_class->texttotallength;
3513 } else if ($svg_class->textanchor == 'middle') {
3514 $tx = -$svg_class->texttotallength / 2;
3515 } else {
3516 $tx = 0;
3518 while (preg_match('/mPDF-AXS\((.*?)\)/', $svg_class->textoutput, $m)) {
3519 if ($tx) {
3520 $txk = $m[1] + ($tx * $svg_class->kp);
3521 $svg_class->textoutput = preg_replace('/mPDF-AXS\((.*?)\)/', sprintf('%.4F', $txk), $svg_class->textoutput, 1);
3522 } else {
3523 $svg_class->textoutput = preg_replace('/mPDF-AXS\((.*?)\)/', '\\1', $svg_class->textoutput, 1);
3527 $svg_class->svgWriteString($svg_class->textoutput);
3528 $svg_class->textlength = 0;
3529 $svg_class->texttotallength = 0;
3530 $svg_class->textoutput = '';
3531 $svg_class->intext = false; // mPDF 5.7.4
3533 break;
3534 // mPDF 5.7.4
3535 case "tspan":
3536 $p_cmd = $svg_class->svgText();
3537 $svg_class->textoutput .= $p_cmd;
3538 $tmp = count($svg_class->svg_style) - 1;
3539 $current_style = $svg_class->svg_style[$tmp];
3540 if ($current_style['transformations']) {
3541 $svg_class->textoutput .= " Q\n";
3543 array_pop($svg_class->svg_style);
3544 array_pop($svg_class->txt_style);
3546 $svg_class->textXorigin += $svg_class->textlength;
3547 $svg_class->textlength = 0;
3549 break;
3555 $svg2pdf_xml = '';
3556 global $svg_class;
3557 $svg_class = $this;
3558 // Don't output stuff inside <defs>
3559 $svg_class->inDefs = false;
3560 $svg2pdf_xml_parser = xml_parser_create("utf-8");
3561 xml_parser_set_option($svg2pdf_xml_parser, XML_OPTION_CASE_FOLDING, false);
3562 xml_set_element_handler($svg2pdf_xml_parser, "xml_svg2pdf_start", "xml_svg2pdf_end");
3563 xml_set_character_data_handler($svg2pdf_xml_parser, "characterData");
3564 xml_parse($svg2pdf_xml_parser, $data);
3565 if ($this->svg_error) {
3566 return false;
3567 } else {
3568 return array('x' => $this->svg_info['x'] * $this->kp, 'y' => -$this->svg_info['y'] * $this->kp, 'w' => $this->svg_info['w'] * $this->kp, 'h' => -$this->svg_info['h'] * $this->kp, 'data' => $svg_class->svg_string);
3572 // AUTOFONT =========================
3573 function markScriptToLang($html)
3575 if ($this->mpdf_ref->onlyCoreFonts) {
3576 return $html;
3578 if (empty($this->script2lang)) {
3579 if (!empty($this->mpdf_ref->script2lang)) {
3580 $this->script2lang = $this->mpdf_ref->script2lang;
3581 $this->viet = $this->mpdf_ref->viet;
3582 $this->pashto = $this->mpdf_ref->pashto;
3583 $this->urdu = $this->mpdf_ref->urdu;
3584 $this->persian = $this->mpdf_ref->persian;
3585 $this->sindhi = $this->mpdf_ref->sindhi;
3586 } else {
3587 include(_MPDF_PATH . 'config_script2lang.php');
3591 $n = '';
3592 $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
3593 foreach ($a as $i => $e) {
3594 if ($i % 2 == 0) {
3595 $e = strcode2utf($e);
3596 $e = $this->mpdf_ref->lesser_entity_decode($e);
3598 $earr = $this->mpdf_ref->UTF8StringToArray($e, false);
3600 $scriptblock = 0;
3601 $scriptblocks = array();
3602 $scriptblocks[0] = 0;
3603 $chardata = array();
3604 $subchunk = 0;
3605 $charctr = 0;
3606 foreach ($earr as $char) {
3607 $ucd_record = UCDN::get_ucd_record($char);
3608 $sbl = $ucd_record[6];
3610 if ($sbl && $sbl != 40 && $sbl != 102) {
3611 if ($scriptblock == 0) {
3612 $scriptblock = $sbl;
3613 $scriptblocks[$subchunk] = $scriptblock;
3614 } else if ($scriptblock > 0 && $scriptblock != $sbl) {
3615 // NEW (non-common) Script encountered in this chunk.
3616 // Start a new subchunk
3617 $subchunk++;
3618 $scriptblock = $sbl;
3619 $charctr = 0;
3620 $scriptblocks[$subchunk] = $scriptblock;
3624 $chardata[$subchunk][$charctr]['script'] = $sbl;
3625 $chardata[$subchunk][$charctr]['uni'] = $char;
3626 $charctr++;
3629 // If scriptblock[x] = common & non-baseScript
3630 // and scriptblock[x+1] = baseScript
3631 // Move common script from end of x to start of x+1
3632 for ($sch = 0; $sch < $subchunk; $sch++) {
3633 if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->mpdf_ref->baseScript && $scriptblocks[$sch + 1] == $this->mpdf_ref->baseScript) {
3634 $end = count($chardata[$sch]) - 1;
3635 while ($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script
3636 $tmp = array_pop($chardata[$sch]);
3637 array_unshift($chardata[$sch + 1], $tmp);
3638 $end--;
3643 $o = '';
3644 for ($sch = 0; $sch <= $subchunk; $sch++) {
3645 if (isset($chardata[$sch])) {
3646 $s = '';
3647 for ($j = 0; $j < count($chardata[$sch]); $j++) {
3648 $s.=code2utf($chardata[$sch][$j]['uni']);
3650 // ZZZ99 Undo lesser_entity_decode as above - but only for <>&
3651 $s = str_replace("&", "&amp;", $s);
3652 $s = str_replace("<", "&lt;", $s);
3653 $s = str_replace(">", "&gt;", $s);
3655 if (substr($a[$i - 1], 0, 5) != '<text' && substr($a[$i - 1], 0, 5) != '<tspa') {
3656 continue;
3657 } // <tspan> or <text> only
3659 $lang = '';
3660 // Check Vietnamese if Latin script - even if Basescript
3661 if ($scriptblocks[$sch] == UCDN::SCRIPT_LATIN && $this->mpdf_ref->autoVietnamese && preg_match("/([" . $this->viet . "])/u", $s)) {
3662 $lang = "vi";
3664 // Check Arabic for different languages if Arabic script - even if Basescript
3665 else if ($scriptblocks[$sch] == UCDN::SCRIPT_ARABIC && $this->mpdf_ref->autoArabic) {
3666 if (preg_match("/[" . $this->sindhi . "]/u", $s)) {
3667 $lang = "sd";
3668 } else if (preg_match("/[" . $this->urdu . "]/u", $s)) {
3669 $lang = "ur";
3670 } else if (preg_match("/[" . $this->pashto . "]/u", $s)) {
3671 $lang = "ps";
3672 } else if (preg_match("/[" . $this->persian . "]/u", $s)) {
3673 $lang = "fa";
3674 } else if ($this->mpdf_ref->baseScript != UCDN::SCRIPT_ARABIC && isset($this->script2lang[$scriptblocks[$sch]])) {
3675 $lang = "'.$this->script2lang[$scriptblocks[$sch]].'";
3678 // Identify Script block if not Basescript, and mark up as language
3679 else if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->mpdf_ref->baseScript && isset($this->script2lang[$scriptblocks[$sch]])) {
3680 $lang = $this->script2lang[$scriptblocks[$sch]];
3682 if ($lang) {
3683 $o .= '<tspan lang="' . $lang . '">' . $s . '</tspan>';
3684 } else {
3685 $o .= $s;
3690 $a[$i] = $o;
3691 } else {
3692 $a[$i] = '<' . $e . '>';
3695 $n = implode('', $a);
3696 return $n;
3701 // END OF CLASS
3702 // END OF CLASS
3703 // END OF CLASS
3706 function calc_bezier_bbox($start, $c)
3708 $P0 = array($start[0], $start[1]);
3709 $P1 = array($c[0], $c[1]);
3710 $P2 = array($c[2], $c[3]);
3711 $P3 = array($c[4], $c[5]);
3712 $bounds = array();
3713 $bounds[0][] = $P0[0];
3714 $bounds[1][] = $P0[1];
3715 $bounds[0][] = $P3[0];
3716 $bounds[1][] = $P3[1];
3717 for ($i = 0; $i <= 1; $i++) {
3718 $b = 6 * $P0[$i] - 12 * $P1[$i] + 6 * $P2[$i];
3719 $a = -3 * $P0[$i] + 9 * $P1[$i] - 9 * $P2[$i] + 3 * $P3[$i];
3720 $c = 3 * $P1[$i] - 3 * $P0[$i];
3721 if ($a == 0) {
3722 if ($b == 0) {
3723 continue;
3725 $t = -$c / $b;
3726 if ($t > 0 && $t < 1) {
3727 $bounds[$i][] = (pow((1 - $t), 3) * $P0[$i] + 3 * pow((1 - $t), 2) * $t * $P1[$i] + 3 * (1 - $t) * pow($t, 2) * $P2[$i] + pow($t, 3) * $P3[$i]);
3729 continue;
3731 $b2ac = pow($b, 2) - 4 * $c * $a;
3732 if ($b2ac < 0) {
3733 continue;
3735 $t1 = (-$b + sqrt($b2ac)) / (2 * $a);
3736 if ($t1 > 0 && $t1 < 1) {
3737 $bounds[$i][] = (pow((1 - $t1), 3) * $P0[$i] + 3 * pow((1 - $t1), 2) * $t1 * $P1[$i] + 3 * (1 - $t1) * pow($t1, 2) * $P2[$i] + pow($t1, 3) * $P3[$i]);
3739 $t2 = (-$b - sqrt($b2ac)) / (2 * $a);
3740 if ($t2 > 0 && $t2 < 1) {
3741 $bounds[$i][] = (pow((1 - $t2), 3) * $P0[$i] + 3 * pow((1 - $t2), 2) * $t2 * $P1[$i] + 3 * (1 - $t2) * pow($t2, 2) * $P2[$i] + pow($t2, 3) * $P3[$i]);
3744 $x = min($bounds[0]);
3745 $x2 = max($bounds[0]);
3746 $y = min($bounds[1]);
3747 $y2 = max($bounds[1]);
3748 return array($x, $y, $x2, $y2);
3751 function _testIntersectCircle($cx, $cy, $cr)
3753 // Tests whether a circle fully encloses a rectangle 0,0,1,1
3754 // to see if any further radial gradients need adding (SVG)
3755 // If centre of circle is inside 0,0,1,1 square
3756 if ($cx >= 0 && $cx <= 1 && $cy >= 0 && $cy <= 1) {
3757 $maxd = 1.5;
3759 // distance to four corners
3760 else {
3761 $d1 = sqrt(pow(($cy - 0), 2) + pow(($cx - 0), 2));
3762 $d2 = sqrt(pow(($cy - 1), 2) + pow(($cx - 0), 2));
3763 $d3 = sqrt(pow(($cy - 0), 2) + pow(($cx - 1), 2));
3764 $d4 = sqrt(pow(($cy - 1), 2) + pow(($cx - 1), 2));
3765 $maxd = max($d1, $d2, $d3, $d4);
3767 if ($cr < $maxd) {
3768 return true;
3769 } else {
3770 return false;
3774 function _testIntersect($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4)
3776 // Tests whether line (x1, y1) and (x2, y2) [a gradient axis (perpendicular)]
3777 // intersects with a specific line segment (x3, y3) and (x4, y4)
3778 $a1 = $y2 - $y1;
3779 $b1 = $x1 - $x2;
3780 $c1 = $a1 * $x1 + $b1 * $y1;
3781 $a2 = $y4 - $y3;
3782 $b2 = $x3 - $x4;
3783 $c2 = $a2 * $x3 + $b2 * $y3;
3784 $det = $a1 * $b2 - $a2 * $b1;
3785 if ($det == 0) { //Lines are parallel
3786 return false;
3787 } else {
3788 $x = ($b2 * $c1 - $b1 * $c2) / $det;
3789 $y = ($a1 * $c2 - $a2 * $c1) / $det;
3790 if ($x >= $x3 && $x <= $x4 && $y >= $y3 && $y <= $y4) {
3791 return true;
3794 return false;