/** * Draws an SVG path * @param $d (string) attribute d of the path SVG element * @param $style (string) Style of rendering. Possible values are: * <ul> * <li>D or empty string: Draw (default).</li> * <li>F: Fill.</li> * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li> * <li>DF or FD: Draw and fill.</li> * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li> * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> * </ul> * @return array of container box measures (x, y, w, h) * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function SVGPath($d, $style = '') { if ($this->state != 2) { return; } // set fill/stroke style $op = TCPDF_STATIC::getPathPaintOperator($style, ''); if (empty($op)) { return; } $paths = array(); $d = preg_replace('/([0-9ACHLMQSTVZ])([\\-\\+])/si', '\\1 \\2', $d); preg_match_all('/([ACHLMQSTVZ])[\\s]*([^ACHLMQSTVZ\\"]*)/si', $d, $paths, PREG_SET_ORDER); $x = 0; $y = 0; $x1 = 0; $y1 = 0; $x2 = 0; $y2 = 0; $xmin = 2147483647; $xmax = 0; $ymin = 2147483647; $ymax = 0; $relcoord = false; $minlen = 0.01 / $this->k; // minimum acceptable length (3 point) $firstcmd = true; // used to print first point // draw curve pieces foreach ($paths as $key => $val) { // get curve type $cmd = trim($val[1]); if (strtolower($cmd) == $cmd) { // use relative coordinated instead of absolute $relcoord = true; $xoffset = $x; $yoffset = $y; } else { $relcoord = false; $xoffset = 0; $yoffset = 0; } $params = array(); if (isset($val[2])) { // get curve parameters $rawparams = preg_split('/([\\,\\s]+)/si', trim($val[2])); $params = array(); foreach ($rawparams as $ck => $cp) { $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false); if (abs($params[$ck]) < $minlen) { // aproximate little values to zero $params[$ck] = 0; } } } // store current origin point $x0 = $x; $y0 = $y; switch (strtoupper($cmd)) { case 'M': // moveto foreach ($params as $ck => $cp) { if ($ck % 2 == 0) { $x = $cp + $xoffset; } else { $y = $cp + $yoffset; if ($firstcmd or abs($x0 - $x) >= $minlen or abs($y0 - $y) >= $minlen) { if ($ck == 1) { $this->_outPoint($x, $y); $firstcmd = false; } else { $this->_outLine($x, $y); } $x0 = $x; $y0 = $y; } $xmin = min($xmin, $x); $ymin = min($ymin, $y); $xmax = max($xmax, $x); $ymax = max($ymax, $y); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; case 'L': // lineto foreach ($params as $ck => $cp) { if ($ck % 2 == 0) { $x = $cp + $xoffset; } else { $y = $cp + $yoffset; if (abs($x0 - $x) >= $minlen or abs($y0 - $y) >= $minlen) { $this->_outLine($x, $y); $x0 = $x; $y0 = $y; } $xmin = min($xmin, $x); $ymin = min($ymin, $y); $xmax = max($xmax, $x); $ymax = max($ymax, $y); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; case 'H': // horizontal lineto foreach ($params as $ck => $cp) { $x = $cp + $xoffset; if (abs($x0 - $x) >= $minlen or abs($y0 - $y) >= $minlen) { $this->_outLine($x, $y); $x0 = $x; $y0 = $y; } $xmin = min($xmin, $x); $xmax = max($xmax, $x); if ($relcoord) { $xoffset = $x; } } break; case 'V': // vertical lineto foreach ($params as $ck => $cp) { $y = $cp + $yoffset; if (abs($x0 - $x) >= $minlen or abs($y0 - $y) >= $minlen) { $this->_outLine($x, $y); $x0 = $x; $y0 = $y; } $ymin = min($ymin, $y); $ymax = max($ymax, $y); if ($relcoord) { $yoffset = $y; } } break; case 'C': // curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if (($ck + 1) % 6 == 0) { $x1 = $params[$ck - 5] + $xoffset; $y1 = $params[$ck - 4] + $yoffset; $x2 = $params[$ck - 3] + $xoffset; $y2 = $params[$ck - 2] + $yoffset; $x = $params[$ck - 1] + $xoffset; $y = $params[$ck] + $yoffset; $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); $xmin = min($xmin, $x, $x1, $x2); $ymin = min($ymin, $y, $y1, $y2); $xmax = max($xmax, $x, $x1, $x2); $ymax = max($ymax, $y, $y1, $y2); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; case 'S': // shorthand/smooth curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if (($ck + 1) % 4 == 0) { if ($key > 0 and (strtoupper($paths[$key - 1][1]) == 'C' or strtoupper($paths[$key - 1][1]) == 'S')) { $x1 = 2 * $x - $x2; $y1 = 2 * $y - $y2; } else { $x1 = $x; $y1 = $y; } $x2 = $params[$ck - 3] + $xoffset; $y2 = $params[$ck - 2] + $yoffset; $x = $params[$ck - 1] + $xoffset; $y = $params[$ck] + $yoffset; $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); $xmin = min($xmin, $x, $x1, $x2); $ymin = min($ymin, $y, $y1, $y2); $xmax = max($xmax, $x, $x1, $x2); $ymax = max($ymax, $y, $y1, $y2); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; case 'Q': // quadratic Bézier curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if (($ck + 1) % 4 == 0) { // convert quadratic points to cubic points $x1 = $params[$ck - 3] + $xoffset; $y1 = $params[$ck - 2] + $yoffset; $xa = ($x + 2 * $x1) / 3; $ya = ($y + 2 * $y1) / 3; $x = $params[$ck - 1] + $xoffset; $y = $params[$ck] + $yoffset; $xb = ($x + 2 * $x1) / 3; $yb = ($y + 2 * $y1) / 3; $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); $xmin = min($xmin, $x, $xa, $xb); $ymin = min($ymin, $y, $ya, $yb); $xmax = max($xmax, $x, $xa, $xb); $ymax = max($ymax, $y, $ya, $yb); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; case 'T': // shorthand/smooth quadratic Bézier curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if ($ck % 2 != 0) { if ($key > 0 and (strtoupper($paths[$key - 1][1]) == 'Q' or strtoupper($paths[$key - 1][1]) == 'T')) { $x1 = 2 * $x - $x1; $y1 = 2 * $y - $y1; } else { $x1 = $x; $y1 = $y; } // convert quadratic points to cubic points $xa = ($x + 2 * $x1) / 3; $ya = ($y + 2 * $y1) / 3; $x = $params[$ck - 1] + $xoffset; $y = $params[$ck] + $yoffset; $xb = ($x + 2 * $x1) / 3; $yb = ($y + 2 * $y1) / 3; $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); $xmin = min($xmin, $x, $xa, $xb); $ymin = min($ymin, $y, $ya, $yb); $xmax = max($xmax, $x, $xa, $xb); $ymax = max($ymax, $y, $ya, $yb); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; case 'A': // elliptical arc foreach ($params as $ck => $cp) { $params[$ck] = $cp; if (($ck + 1) % 7 == 0) { $x0 = $x; $y0 = $y; $rx = abs($params[$ck - 6]); $ry = abs($params[$ck - 5]); $ang = -$rawparams[$ck - 4]; $angle = deg2rad($ang); $fa = $rawparams[$ck - 3]; // large-arc-flag $fs = $rawparams[$ck - 2]; // sweep-flag $x = $params[$ck - 1] + $xoffset; $y = $params[$ck] + $yoffset; if (abs($x0 - $x) < $minlen and abs($y0 - $y) < $minlen) { // endpoints are almost identical $xmin = min($xmin, $x); $ymin = min($ymin, $y); $xmax = max($xmax, $x); $ymax = max($ymax, $y); } else { $cos_ang = cos($angle); $sin_ang = sin($angle); $a = ($x0 - $x) / 2; $b = ($y0 - $y) / 2; $xa = $a * $cos_ang - $b * $sin_ang; $ya = $a * $sin_ang + $b * $cos_ang; $rx2 = $rx * $rx; $ry2 = $ry * $ry; $xa2 = $xa * $xa; $ya2 = $ya * $ya; $delta = $xa2 / $rx2 + $ya2 / $ry2; if ($delta > 1) { $rx *= sqrt($delta); $ry *= sqrt($delta); $rx2 = $rx * $rx; $ry2 = $ry * $ry; } $numerator = $rx2 * $ry2 - $rx2 * $ya2 - $ry2 * $xa2; if ($numerator < 0) { $root = 0; } else { $root = sqrt($numerator / ($rx2 * $ya2 + $ry2 * $xa2)); } if ($fa == $fs) { $root *= -1; } $cax = $root * ($rx * $ya / $ry); $cay = -$root * ($ry * $xa / $rx); // coordinates of ellipse center $cx = $cax * $cos_ang - $cay * $sin_ang + ($x0 + $x) / 2; $cy = $cax * $sin_ang + $cay * $cos_ang + ($y0 + $y) / 2; // get angles $angs = TCPDF_STATIC::getVectorsAngle(1, 0, ($xa - $cax) / $rx, ($cay - $ya) / $ry); $dang = TCPDF_STATIC::getVectorsAngle(($xa - $cax) / $rx, ($ya - $cay) / $ry, (-$xa - $cax) / $rx, (-$ya - $cay) / $ry); if ($fs == 0 and $dang > 0) { $dang -= 2 * M_PI; } elseif ($fs == 1 and $dang < 0) { $dang += 2 * M_PI; } $angf = $angs - $dang; if ($fs == 0 and $angs > $angf or $fs == 1 and $angs < $angf) { // reverse angles $tmp = $angs; $angs = $angf; $angf = $tmp; } $angs = round(rad2deg($angs), 6); $angf = round(rad2deg($angf), 6); // covent angles to positive values if ($angs < 0 and $angf < 0) { $angs += 360; $angf += 360; } $pie = false; if ($key == 0 and isset($paths[$key + 1][1]) and trim($paths[$key + 1][1]) == 'z') { $pie = true; } list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, $fs == 0, true); $xmin = min($xmin, $x, $axmin); $ymin = min($ymin, $y, $aymin); $xmax = max($xmax, $x, $axmax); $ymax = max($ymax, $y, $aymax); } if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; case 'Z': $this->_out('h'); break; } $firstcmd = false; } // end foreach if (!empty($op)) { $this->_out($op); } return array($xmin, $ymin, $xmax - $xmin, $ymax - $ymin); }