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