3 // svg class modified for mPDF version 6.0 by Ian Back: based on -
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
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
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
68 var $kp; // convert pixels to PDF units
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();
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(
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
140 'stroke-dasharray' => 0,
141 'stroke-dashoffset' => 0,
146 $this->txt_style
= 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
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) {
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'];
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
);
197 // x,y,w,h define the reference rectangle
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';
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') {
229 } else if ($ar[0] == 'xmidymin') {
230 $img_x +
= $w / 2 - $img_w / 2; // xMid
232 } else if ($ar[0] == 'xmaxymin') {
233 $img_x +
= $w - $img_w; // xMax
235 } else if ($ar[0] == 'xminymid') {
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') {
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
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
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']));
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]);
309 $mint = min($mint, $pts[$i]);
310 $maxb = max($maxb, $pts[$i]);
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];
324 preg_match_all('/([a-z]|[A-Z])([ ,\-.\d]+)*/', $attribs['d'], $commands, PREG_SET_ORDER
);
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]);
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);
358 if (!$w ||
$w == -999999) {
361 if (!$h ||
$h == -999999) {
364 if ($x_offset == 999999) {
367 if ($y_offset == 999999) {
372 $transformations = '';
373 if (isset($gradient_info['transform'])) {
374 preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is', $gradient_info['transform'], $m);
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)) {
386 if (count($vv) == 2) {
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) {
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]));
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]);
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') {
438 else if (strtolower($gradient_info['spread']) == '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;
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') {
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'];
499 if (isset($gradient_info['info']['y1'])) {
500 $y1 = $gradient_info['info']['y1'];
504 if (isset($gradient_info['info']['x2'])) {
505 $x2 = $gradient_info['info']['x2'];
509 if (isset($gradient_info['info']['y2'])) {
510 $y2 = $gradient_info['info']['y2'];
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;
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'])));
539 } else if ($angle > 360) {
542 if ($angle != 0 && $angle != 360 && $angle != 90 && $angle != 180 && $angle != 270) {
546 $usew = $useh = $bboxw;
550 $usew = $useh = $bboxh;
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;
569 if ($spread == 'R' ||
$spread == 'F') { // Repeat / Reflect
571 for ($i = 0; $i < $ns; $i++
) {
572 $offs[$i] = $gradient_info['color'][$i]['offset'];
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;
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) {
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;
621 $tmp = -$gp +
$offs[$i];
622 $newarr[$i]['offset'] = $tmp;
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) {
649 for ($i = ($ns - 1); $i >= 0; $i--) {
650 if (isset($newarr[$i]['offset']))
651 array_unshift($gradient_info['color'], $newarr[$i]);
657 $stops = count($gradient_info['color']);
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) {
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');
697 $this->mpdf_ref
->gradients
[$n]['trans'] = true;
698 $return .= ' /TGS' . ($n) . ' gs ';
700 $return .= ' /Sh' . ($n) . ' sh ';
702 } else if ($gradient_info['type'] == 'radial') {
703 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'userspaceonuse') {
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'];
734 if (isset($gradient_info['info']['y0'])) {
735 $y0 = $gradient_info['info']['y0'];
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'];
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'];
753 if (isset($gradient_info['info']['x1'])) {
754 $x1 = $gradient_info['info']['x1'];
758 if (isset($gradient_info['info']['y1'])) {
759 $y1 = $gradient_info['info']['y1'];
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;
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'])));
793 } else if ($angle > 360) {
796 if ($angle != 0 && $angle != 360 && $angle != 90 && $angle != 180 && $angle != 270) {
802 $usew = $useh = $bboxw;
808 $usew = $useh = $bboxh;
813 $d = -$useh; // height
814 $e = $usex; // x- offset
815 $f = -$usey; // -y-offset
820 $return .= sprintf('%.3F 0 0 %.3F %.3F %.3F cm ', $a * $this->kp
, $d * $this->kp
, $e * $this->kp
, $f * $this->kp
);
823 if (isset($gradient_info['units']) && strtolower($gradient_info['units']) == 'objectboundingbox') {
824 if ($transformations) {
825 $return .= $transformations;
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
842 for ($i = 0; $i < $ns; $i++
) {
843 $offs[$i] = $gradient_info['color'][$i]['offset'];
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;
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
862 // Point on axis to test
863 $px = $x1 +
($x0 - $x1) * $tmp;
864 $py = $y1 +
($y0 - $y1) * $tmp;
866 $res = _testIntersectCircle($px, $py, $pr);
875 $stops = count($gradient_info['color']);
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) {
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 ';
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];
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
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'];
956 $this->kp
*= ($svg_h / 0.2645) / $this->svg_info
['h'];
957 $this->kf
= ($svg_h / 0.2645) / $this->svg_info
['h'];
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'];
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)
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);
994 // $ymax = floor(($this->svg_attribs['height'] * -1));
995 $ymax = floor(($svg_h * -1));
999 $x2 = $xmax; // right edge
1001 $x2 = $xmin; // left edge
1003 $y2 = $ymax; // bottom
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']);
1022 $transformations = '';
1023 if (isset($critere_style['transform'])) {
1024 preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is', $critere_style['transform'], $m);
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) {
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
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)) {
1060 if (count($vv) == 2) {
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) {
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]));
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
);
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;
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
);
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;
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)
1233 $fill_gradient = '';
1236 if (substr_count($critere_style['fill'], 'url') > 0 && $element != 'line') {
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) {
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']);
1256 $critere_style['fill-opacity'] = ord($col{4} / 100);
1259 $critere_style['fill-opacity'] = ord($col{5} / 100);
1261 $path_style .= $this->mpdf_ref
->SetFColor($col, true) . ' ';
1264 } else if ($critere_style['fill'] != 'none' && $element != 'line') {
1265 $col = $this->mpdf_ref
->ConvertColor($critere_style['fill']);
1268 $critere_style['fill-opacity'] = ord($col{4} / 100);
1271 $critere_style['fill-opacity'] = ord($col{5} / 100);
1273 $path_style .= $this->mpdf_ref
->SetFColor($col, true) . ' ';
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) {
1293 // Used as indirect setting for currentColor
1294 else if (strtolower($critere_style['stroke']) == 'currentcolor') {
1295 $col = $this->mpdf_ref
->ConvertColor($critere_style['color']);
1298 $critere_style['stroke-opacity'] = ord($col{4} / 100);
1301 $critere_style['stroke-opacity'] = ord($col{5} / 100);
1303 $path_style .= $this->mpdf_ref
->SetDColor($col, true) . ' ';
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']);
1314 $critere_style['stroke-opacity'] = ord($col{4} / 100);
1317 $critere_style['stroke-opacity'] = ord($col{5} / 100);
1319 $path_style .= $this->mpdf_ref
->SetDColor($col, true) . ' ';
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'])) {
1353 $d = preg_split('/[ ,]/', $critere_style['stroke-dasharray']);
1354 if (count($d) == 1 && $d[0] == 0) {
1355 $path_style .= '[] 0 d ';
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
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') {
1378 if (isset($critere_style['fill-opacity'])) {
1380 if ($critere_style['fill-opacity'] == 0) {
1382 } else if ($critere_style['fill-opacity'] > 1) {
1384 } else if ($critere_style['fill-opacity'] > 0) {
1385 $opacity = $critere_style['fill-opacity'];
1386 } else if ($critere_style['fill-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'])) {
1396 if ($critere_style['stroke-opacity'] == 0) {
1398 } else if ($critere_style['stroke-opacity'] > 1) {
1400 } else if ($critere_style['stroke-opacity'] > 0) {
1401 $opacity = $critere_style['stroke-opacity'];
1402 } else if ($critere_style['stroke-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);
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)
1436 $newsubpath = false;
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)
1454 $ile_argumentow = count($a);
1456 // each command may have different needs for arguments [1 to 8]
1458 switch (strtolower($command)) {
1460 for ($i = 0; $i < $ile_argumentow; $i+
=2) {
1464 $pdfx = ($this->xbase +
$x);
1465 $pdfy = ($this->ybase
- $y);
1467 $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']);
1480 $path_cmd .= sprintf('%.3F %.3F m ', $pdf_pt['x'] * $this->kp
, $pdf_pt['y'] * $this->kp
);
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;
1491 case 'l': // a simple line
1492 for ($i = 0; $i < $ile_argumentow; $i+
=2) {
1494 $y = ($a[$i +
1][0]);
1496 $pdfx = ($this->xbase +
$x);
1497 $pdfy = ($this->ybase
- $y);
1499 $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
);
1514 case 'h': // a very simple horizontal line
1515 for ($i = 0; $i < $ile_argumentow; $i++
) {
1519 $pdfx = ($this->xbase +
$x);
1520 $pdfy = ($this->ybase
- $y);
1522 $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
);
1538 case 'v': // the simplest line, vertical
1539 for ($i = 0; $i < $ile_argumentow; $i++
) {
1543 $pdfx = ($this->xbase +
$x);
1544 $pdfy = ($this->ybase
- $y);
1546 $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
);
1562 case 's': // bezier with first vertex equal first control
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];
1571 $y2 = ($a[$i +
1][0]);
1572 $x = ($a[$i +
2][0]);
1573 $y = ($a[$i +
3][0]);
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);
1582 $this->ybase +
= -$y;
1584 $pdfx1 = $this->xbase +
$x1;
1585 $pdfy1 = $this->ybase
- $y1;
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
);
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
);
1611 case 'c': // bezier with second vertex equal second control
1612 for ($i = 0; $i < $ile_argumentow; $i +
= 6) {
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]);
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);
1629 $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
);
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
);
1660 case 'q': // bezier quadratic avec point de control
1661 for ($i = 0; $i < $ile_argumentow; $i +
= 4) {
1663 $y1 = ($a[$i +
1][0]);
1664 $x = ($a[$i +
2][0]);
1665 $y = ($a[$i +
3][0]);
1667 $pdfx = ($this->xbase +
$x);
1668 $pdfy = ($this->ybase
- $y);
1670 $pdfx1 = ($this->xbase +
($x1 * 2 / 3));
1671 $pdfy1 = ($this->ybase
- ($y1 * 2 / 3));
1673 $pdfx2 = $pdfx1 +
1 / 3 * ($x);
1674 $pdfy2 = $pdfy1 +
1 / 3 * (-$y);
1677 $this->ybase +
= -$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));
1689 $pdfx2 = $pdfx1 +
1 / 3 * ($x - $this->xbase
);
1690 $pdfy2 = $pdfy1 +
1 / 3 * (-$y - $this->ybase
);
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
);
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
);
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) {
1719 $y = ($a[$i +
1][0]);
1721 $x1 = $this->lastcontrolpoints
[0];
1722 $y1 = $this->lastcontrolpoints
[1];
1725 $pdfx = ($this->xbase +
$x);
1726 $pdfy = ($this->ybase
- $y);
1728 $pdfx1 = ($this->xbase +
($x1));
1729 $pdfy1 = ($this->ybase
- ($y1));
1731 $pdfx2 = $pdfx1 +
1 / 3 * ($x);
1732 $pdfy2 = $pdfy1 +
1 / 3 * (-$y);
1735 $this->ybase +
= -$y;
1740 $pdfx1 = ($this->xbase +
($x1));
1741 $pdfy1 = ($this->ybase
- ($y1));
1743 $pdfx2 = $pdfx1 +
1 / 3 * ($x - $this->xbase
);
1744 $pdfy2 = $pdfy1 +
1 / 3 * (-$y - $this->ybase
);
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
);
1763 case 'a': // Elliptical arc
1764 for ($i = 0; $i < $ile_argumentow; $i +
= 7) {
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]);
1773 $y1 = -$this->ybase
;
1775 $x2 = $this->xbase +
$x2;
1776 $y2 = -$this->ybase +
$y2;
1777 $this->xbase +
= ($a[$i +
5][0]);
1778 $this->ybase +
= -($a[$i +
6][0]);
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]));
1793 $this->subPathInit
= true;
1795 $this->xbase
= $this->spxstart
;
1796 $this->ybase
= $this->spystart
;
1803 $this->subPathInit
= false;
1805 $this->lastcommand
= $command;
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];
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.
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));
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);
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);
1912 foreach ($coords AS $c) {
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";
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);
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
;
1954 $size = $this->mpdf_ref
->ConvertSize($size, $maxsize, $fontsize, false) * 1 / (25.4 / $this->mpdf_ref
->dpi
);
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
;
1966 $size = $this->mpdf_ref
->ConvertSize($size, $maxsize, false, true) * 72 / 25.4;
1971 // fonction retracant les <rect />
1972 function svgRect($arguments)
1975 if ($arguments['h'] == 0 ||
$arguments['w'] == 0) {
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
1993 if ($rx > 0 and $ry == 0) {
1996 if ($ry > 0 and $rx == 0) {
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
));
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;
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
);
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) {
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');
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
);
2069 // fonction retracant les <polyline /> et les <line />
2070 function svgPolyline($arguments, $ispolyline = true)
2073 $xbase = $arguments[0];
2074 $ybase = - $arguments[1];
2076 if ($arguments[0] == $arguments[2] && $arguments[1] == $arguments[3]) {
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) {
2085 $tmp_x = $arguments[$i];
2086 $tmp_y = - $arguments[($i +
1)];
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
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
);
2117 // write string to image
2120 // $tmp = count($this->txt_style)-1;
2121 $current_style = $this->txt_style
[count($this->txt_style
) - 1]; // mPDF 5.7.4
2125 if (isset($this->txt_data
[2])) {
2127 // If using SVG Font
2128 if (isset($this->svg_font
[$current_style['font-family']])) {
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;
2152 if (isset($current_style['fill-opacity'])) {
2153 if ($current_style['fill-opacity'] == 0) {
2155 } else if ($current_style['fill-opacity'] > 1) {
2157 } else if ($current_style['fill-opacity'] > 0) {
2158 $fopacity = $current_style['fill-opacity'];
2159 } else if ($current_style['fill-opacity'] < 0) {
2164 if (isset($current_style['stroke-opacity'])) {
2165 if ($current_style['stroke-opacity'] == 0) {
2167 } else if ($current_style['stroke-opacity'] > 1) {
2169 } else if ($current_style['stroke-opacity'] > 0) {
2170 $sopacity = $current_style['stroke-opacity'];
2171 } else if ($current_style['stroke-opacity'] < 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);
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)
2187 if ($stroke_width > 0 && $current_style['stroke'] != 'none') {
2188 $scol = $this->mpdf_ref
->ConvertColor($current_style['stroke']);
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) {
2200 } // fill and stroke
2204 if ($render == -1) {
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
) {
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');
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'];
2245 $horiz_adv_x = $svg_font['horiz-adv-x'];
2246 } // missing glyph width
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;
2260 list($tmp, $command) = $cmd;
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;
2285 $sw *= $scale; // convert stringwidth to units
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;
2294 unset($this->txt_data
[0], $this->txt_data
[1], $this->txt_data
[2]);
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;
2309 if (isset($current_style['fill-opacity'])) {
2310 if ($current_style['fill-opacity'] == 0) {
2312 } else if ($current_style['fill-opacity'] > 1) {
2314 } else if ($current_style['fill-opacity'] > 0) {
2315 $fopacity = $current_style['fill-opacity'];
2316 } else if ($current_style['fill-opacity'] < 0) {
2321 if (isset($current_style['stroke-opacity'])) {
2322 if ($current_style['stroke-opacity'] == 0) {
2324 } else if ($current_style['stroke-opacity'] > 1) {
2326 } else if ($current_style['stroke-opacity'] > 0) {
2327 $sopacity = $current_style['stroke-opacity'];
2328 } else if ($current_style['stroke-opacity'] < 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);
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)
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']);
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) {
2356 } // fill and stroke
2359 if ($render == -1) {
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
) {
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;
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';
2398 $this->mpdf_ref
->OTLtags
['Plus'] = ' kern';
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
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']);
2438 // Reset font // mPDF 5.7.4
2439 $prev_style = $this->txt_style
[count($this->txt_style
) - 1];
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);
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
);
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;
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
);
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') {
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') {
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);
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'];
2593 if (isset($critere_style['stroke-opacity']) && $critere_style['stroke-opacity'] != 'inherit') {
2594 $current_style['stroke-opacity'] = $critere_style['stroke-opacity'];
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);
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;
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;
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 < when not a tag
2722 $data = preg_replace('/<([^!?\/a-zA-Z_:])/i', '<\\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);
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)) {
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);
2756 preg_match_all('/<(linearGradient|radialgradient)([^>]*)id\s*=\s*["\'](.*?)["\'](.*?)>(.*?)<\/(linearGradient|radialgradient)>/si', $data, $m);
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);
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
2776 if (preg_match('/<text [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*>(.*?)<\/text>/si', $data, $m)) {
2780 $data = preg_replace('/' . preg_quote($links[0][$i], '/') . '/is', $insert, $data);
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
2791 if (preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*\/>/si', $data, $m)) {
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)) {
2803 $inners = $links[1][$i] . ' ' . $links[3][$i];
2804 // Change x,y coords to translate()
2805 if (preg_match('/y\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
2810 if (preg_match('/x\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
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="...."
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);
2826 $inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', 'transform="' . $m[1] . ' translate(' . $x . ', ' . $y . ')"', $inners);
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
2841 if (preg_match('/<([a-zA-Z]*) [^>]*id\s*=\s*["\']' . $links[2][$i] . '["\'][^>]*\/>/si', $data, $m)) {
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)) {
2853 $inners = $links[1][$i] . ' ' . $links[3][$i];
2854 // Change x,y coords to translate()
2855 if (preg_match('/y\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
2860 if (preg_match('/x\s*=\s*["\']([^>]*?)["\']/', $inners, $m)) {
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="...."
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);
2876 $inners = preg_replace('/' . preg_quote($m[0], '/') . '/is', 'transform="' . $m[1] . ' translate(' . $x . ', ' . $y . ')"', $inners);
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);
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)
2905 global $svg_class, $last_gradid, $last_svg_fontid, $last_svg_fontdefw, $last_svg_fontstyle; // 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'];
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'] : ''),
2927 $last_svg_fontid = strtolower($attribs['font-family']);
2928 if ($last_svg_fontdefw) {
2929 $tmp_svg_font['horiz-adv-x'] = $last_svg_fontdefw;
2931 $tmp_svg_font['horiz-adv-x'] = 500;
2933 $svg_class->svg_font
[$last_svg_fontid][$last_svg_fontstyle] = $tmp_svg_font;
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'] : '');
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'] : ''),
2956 else if (strtolower($name) == 'lineargradient') {
2957 $tmp_gradient = array(
2959 'transform' => (isset($attribs['gradientTransform']) ?
$attribs['gradientTransform'] : ''),
2960 'units' => (isset($attribs['gradientUnits']) ?
$attribs['gradientUnits'] : ''),
2961 'spread' => (isset($attribs['spreadMethod']) ?
$attribs['spreadMethod'] : ''),
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);
2976 else if (strtolower($name) == 'radialgradient') {
2977 $tmp_gradient = array(
2979 'transform' => (isset($attribs['gradientTransform']) ?
$attribs['gradientTransform'] : ''),
2980 'units' => (isset($attribs['gradientUnits']) ?
$attribs['gradientUnits'] : ''),
2981 'spread' => (isset($attribs['spreadMethod']) ?
$attribs['spreadMethod'] : ''),
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);
2998 else if (strtolower($name) == 'stop') {
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);
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';
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);
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);
3041 if ($svg_class->inDefs
) {
3045 $svg_class->xbase
= 0;
3046 $svg_class->ybase
= 0;
3047 switch (strtolower($name)) {
3049 // Don't output stuff inside <defs>
3051 $svg_class->inDefs
= true;
3055 $svg_class->svgOffset($attribs);
3059 $path = $attribs['d'];
3060 preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $path, $commands, PREG_SET_ORDER
);
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;
3068 list($tmp, $command) = $c;
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);
3092 if (!isset($attribs['x'])) {
3095 if (!isset($attribs['y'])) {
3098 if (!isset($attribs['rx'])) {
3101 if (!isset($attribs['ry'])) {
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);
3124 if (!isset($attribs['cx'])) {
3127 if (!isset($attribs['cy'])) {
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);
3146 if (!isset($attribs['cx'])) {
3149 if (!isset($attribs['cy'])) {
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);
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);
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);
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);
3210 // mPDF 5.7.4 Embedded image
3212 if (isset($attribs['xlink:href']) && $attribs['xlink:href']) {
3213 $svg_class->svgImage($attribs);
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>
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'];
3231 $svg_class->svgWriteString(' q ' . $array_style['transformations']);
3234 array_push($svg_class->svg_style
, $array_style);
3236 $svg_class->svgDefineTxtStyle($attribs);
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
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 . ';';
3275 if (isset($attribs['style'])) {
3276 $attribs['style'] = $styl . $attribs['style'];
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
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];
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 . ';';
3341 if (isset($attribs['style'])) {
3342 $attribs['style'] = $styl . $attribs['style'];
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;
3363 while (preg_match('/mPDF-AXS\((.*?)\)/', $svg_class->textoutput
, $m)) {
3365 $txk = $m[1] +
($tx * $svg_class->kp
);
3366 $svg_class->textoutput
= preg_replace('/mPDF-AXS\((.*?)\)/', sprintf('%.4F', $txk), $svg_class->textoutput
, 1);
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;
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);
3420 //insertion des path et du style dans le flux de donné general.
3421 if (isset($path_cmd) && $path_cmd) {
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");
3427 $svg_class->svgWriteString(" q " . $prestyle . $path_cmd . $poststyle . " Q\n"); // mPDF 5.7.4
3432 function characterData($parser, $data)
3435 if ($svg_class->inDefs
) {
3438 if (isset($svg_class->txt_data
[2])) {
3439 $svg_class->txt_data
[2] .= $data;
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)
3451 // Don't output stuff inside <defs>
3452 if ($name == 'defs') {
3453 $svg_class->inDefs
= false;
3456 if ($svg_class->inDefs
) {
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";
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;
3488 $last_svg_fontdefw = '';
3491 $last_svg_fontid = '';
3492 $last_svg_fontstyle = '';
3494 case 'radialgradient':
3495 case 'lineargradient':
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
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;
3518 while (preg_match('/mPDF-AXS\((.*?)\)/', $svg_class->textoutput
, $m)) {
3520 $txk = $m[1] +
($tx * $svg_class->kp
);
3521 $svg_class->textoutput
= preg_replace('/mPDF-AXS\((.*?)\)/', sprintf('%.4F', $txk), $svg_class->textoutput
, 1);
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
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;
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
) {
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
) {
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
;
3587 include(_MPDF_PATH
. 'config_script2lang.php');
3592 $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE
);
3593 foreach ($a as $i => $e) {
3595 $e = strcode2utf($e);
3596 $e = $this->mpdf_ref
->lesser_entity_decode($e);
3598 $earr = $this->mpdf_ref
->UTF8StringToArray($e, false);
3601 $scriptblocks = array();
3602 $scriptblocks[0] = 0;
3603 $chardata = array();
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
3618 $scriptblock = $sbl;
3620 $scriptblocks[$subchunk] = $scriptblock;
3624 $chardata[$subchunk][$charctr]['script'] = $sbl;
3625 $chardata[$subchunk][$charctr]['uni'] = $char;
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);
3644 for ($sch = 0; $sch <= $subchunk; $sch++
) {
3645 if (isset($chardata[$sch])) {
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("&", "&", $s);
3652 $s = str_replace("<", "<", $s);
3653 $s = str_replace(">", ">", $s);
3655 if (substr($a[$i - 1], 0, 5) != '<text' && substr($a[$i - 1], 0, 5) != '<tspa') {
3657 } // <tspan> or <text> only
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)) {
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)) {
3668 } else if (preg_match("/[" . $this->urdu
. "]/u", $s)) {
3670 } else if (preg_match("/[" . $this->pashto
. "]/u", $s)) {
3672 } else if (preg_match("/[" . $this->persian
. "]/u", $s)) {
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]];
3683 $o .= '<tspan lang="' . $lang . '">' . $s . '</tspan>';
3692 $a[$i] = '<' . $e . '>';
3695 $n = implode('', $a);
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]);
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];
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]);
3731 $b2ac = pow($b, 2) - 4 * $c * $a;
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) {
3759 // distance to four corners
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);
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)
3780 $c1 = $a1 * $x1 +
$b1 * $y1;
3783 $c2 = $a2 * $x3 +
$b2 * $y3;
3784 $det = $a1 * $b2 - $a2 * $b1;
3785 if ($det == 0) { //Lines are parallel
3788 $x = ($b2 * $c1 - $b1 * $c2) / $det;
3789 $y = ($a1 * $c2 - $a2 * $c1) / $det;
3790 if ($x >= $x3 && $x <= $x4 && $y >= $y3 && $y <= $y4) {