/** * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***) * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. * @param $attribs (array) The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on. * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix). * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function startSVGElementHandler($parser, $name, $attribs, $ctm = array()) { // check if we are in clip mode if ($this->svgclipmode) { $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]); return; } if ($this->svgdefsmode and !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) { if (!isset($attribs['id'])) { $attribs['id'] = 'DF_' . (count($this->svgdefs) + 1); } $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs); return; } $clipping = false; if ($parser == 'clip-path') { // set clipping mode $clipping = true; } // get styling properties $prev_svgstyle = $this->svgstyles[count($this->svgstyles) - 1]; // previous style $svgstyle = $this->svgstyles[0]; // set default style if ($clipping and !isset($attribs['fill']) and (!isset($attribs['style']) or !preg_match('/[;\\"\\s]{1}fill[\\s]*:[\\s]*([^;\\"]*)/si', $attribs['style'], $attrval))) { // default fill attribute for clipping $attribs['fill'] = 'none'; } if (isset($attribs['style']) and !TCPDF_STATIC::empty_string($attribs['style'])) { // fix style for regular expression $attribs['style'] = ';' . $attribs['style']; } foreach ($prev_svgstyle as $key => $val) { if (in_array($key, TCPDF_IMAGES::$svginheritprop)) { // inherit previous value $svgstyle[$key] = $val; } if (isset($attribs[$key]) and !TCPDF_STATIC::empty_string($attribs[$key])) { // specific attribute settings if ($attribs[$key] == 'inherit') { $svgstyle[$key] = $val; } else { $svgstyle[$key] = $attribs[$key]; } } elseif (isset($attribs['style']) and !TCPDF_STATIC::empty_string($attribs['style'])) { // CSS style syntax $attrval = array(); if (preg_match('/[;\\"\\s]{1}' . $key . '[\\s]*:[\\s]*([^;\\"]*)/si', $attribs['style'], $attrval) and isset($attrval[1])) { if ($attrval[1] == 'inherit') { $svgstyle[$key] = $val; } else { $svgstyle[$key] = $attrval[1]; } } } } // transformation matrix if (!empty($ctm)) { $tm = $ctm; } else { //$tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix']; $tm = array(1, 0, 0, 1, 0, 0); } if (isset($attribs['transform']) and !empty($attribs['transform'])) { $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform'])); } $svgstyle['transfmatrix'] = $tm; $invisible = false; if ($svgstyle['visibility'] == 'hidden' or $svgstyle['visibility'] == 'collapse' or $svgstyle['display'] == 'none') { // the current graphics element is invisible (nothing is painted) $invisible = true; } // process tag switch ($name) { case 'defs': $this->svgdefsmode = true; break; // clipPath // clipPath case 'clipPath': if ($invisible) { break; } $this->svgclipmode = true; if (!isset($attribs['id'])) { $attribs['id'] = 'CP_' . (count($this->svgcliptm) + 1); } $this->svgclipid = $attribs['id']; $this->svgclippaths[$this->svgclipid] = array(); $this->svgcliptm[$this->svgclipid] = $tm; break; case 'svg': // start of SVG object break; case 'g': // group together related graphics elements array_push($this->svgstyles, $svgstyle); $this->StartTransform(); $this->SVGTransform($tm); $this->setSVGStyles($svgstyle, $prev_svgstyle); break; case 'linearGradient': if ($this->pdfa_mode) { break; } if (!isset($attribs['id'])) { $attribs['id'] = 'GR_' . (count($this->svggradients) + 1); } $this->svggradientid = $attribs['id']; $this->svggradients[$this->svggradientid] = array(); $this->svggradients[$this->svggradientid]['type'] = 2; $this->svggradients[$this->svggradientid]['stops'] = array(); if (isset($attribs['gradientUnits'])) { $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; } else { $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; } //$attribs['spreadMethod'] if (!isset($attribs['x1']) and !isset($attribs['y1']) and !isset($attribs['x2']) and !isset($attribs['y2']) or (isset($attribs['x1']) and substr($attribs['x1'], -1) == '%' or isset($attribs['y1']) and substr($attribs['y1'], -1) == '%' or isset($attribs['x2']) and substr($attribs['x2'], -1) == '%' or isset($attribs['y2']) and substr($attribs['y2'], -1) == '%')) { $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; } else { $this->svggradients[$this->svggradientid]['mode'] = 'measure'; } $x1 = isset($attribs['x1']) ? $attribs['x1'] : '0'; $y1 = isset($attribs['y1']) ? $attribs['y1'] : '0'; $x2 = isset($attribs['x2']) ? $attribs['x2'] : '100'; $y2 = isset($attribs['y2']) ? $attribs['y2'] : '0'; if (isset($attribs['gradientTransform'])) { $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); } $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2); if (isset($attribs['xlink:href']) and !empty($attribs['xlink:href'])) { // gradient is defined on another place $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); } break; case 'radialGradient': if ($this->pdfa_mode) { break; } if (!isset($attribs['id'])) { $attribs['id'] = 'GR_' . (count($this->svggradients) + 1); } $this->svggradientid = $attribs['id']; $this->svggradients[$this->svggradientid] = array(); $this->svggradients[$this->svggradientid]['type'] = 3; $this->svggradients[$this->svggradientid]['stops'] = array(); if (isset($attribs['gradientUnits'])) { $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; } else { $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; } //$attribs['spreadMethod'] if (!isset($attribs['cx']) and !isset($attribs['cy']) or (isset($attribs['cx']) and substr($attribs['cx'], -1) == '%' or isset($attribs['cy']) and substr($attribs['cy'], -1) == '%')) { $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; } else { $this->svggradients[$this->svggradientid]['mode'] = 'measure'; } $cx = isset($attribs['cx']) ? $attribs['cx'] : 0.5; $cy = isset($attribs['cy']) ? $attribs['cy'] : 0.5; $fx = isset($attribs['fx']) ? $attribs['fx'] : $cx; $fy = isset($attribs['fy']) ? $attribs['fy'] : $cy; $r = isset($attribs['r']) ? $attribs['r'] : 0.5; if (isset($attribs['gradientTransform'])) { $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); } $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r); if (isset($attribs['xlink:href']) and !empty($attribs['xlink:href'])) { // gradient is defined on another place $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); } break; case 'stop': // gradient stops if (substr($attribs['offset'], -1) == '%') { $offset = floatval(substr($attribs['offset'], -1)) / 100; } else { $offset = floatval($attribs['offset']); if ($offset > 1) { $offset /= 100; } } $stop_color = isset($svgstyle['stop-color']) ? TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors) : 'black'; $opacity = isset($svgstyle['stop-opacity']) ? $svgstyle['stop-opacity'] : 1; $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity); break; // paths // paths case 'path': if ($invisible) { break; } if (isset($attribs['d'])) { $d = trim($attribs['d']); if (!empty($d)) { if ($clipping) { $this->SVGTransform($tm); $this->SVGPath($d, 'CNZ'); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ')); if (!empty($obstyle)) { $this->SVGPath($d, $obstyle); } $this->StopTransform(); } } } break; // shapes // shapes case 'rect': if ($invisible) { break; } $x = isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0; $y = isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0; $w = isset($attribs['width']) ? $this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false) : 0; $h = isset($attribs['height']) ? $this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false) : 0; $rx = isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0; $ry = isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : $rx; if ($clipping) { $this->SVGTransform($tm); $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array()); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ')); if (!empty($obstyle)) { $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array()); } $this->StopTransform(); } break; case 'circle': if ($invisible) { break; } $r = isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0; $cx = isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0); $cy = isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0); $x = $cx - $r; $y = $cy - $r; $w = 2 * $r; $h = $w; if ($clipping) { $this->SVGTransform($tm); $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ')); if (!empty($obstyle)) { $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8); } $this->StopTransform(); } break; case 'ellipse': if ($invisible) { break; } $rx = isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0; $ry = isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0; $cx = isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0); $cy = isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0); $x = $cx - $rx; $y = $cy - $ry; $w = 2 * $rx; $h = 2 * $ry; if ($clipping) { $this->SVGTransform($tm); $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ')); if (!empty($obstyle)) { $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8); } $this->StopTransform(); } break; case 'line': if ($invisible) { break; } $x1 = isset($attribs['x1']) ? $this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false) : 0; $y1 = isset($attribs['y1']) ? $this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false) : 0; $x2 = isset($attribs['x2']) ? $this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false) : 0; $y2 = isset($attribs['y2']) ? $this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false) : 0; $x = $x1; $y = $y1; $w = abs($x2 - $x1); $h = abs($y2 - $y1); if (!$clipping) { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2)); $this->Line($x1, $y1, $x2, $y2); $this->StopTransform(); } break; case 'polyline': case 'polygon': if ($invisible) { break; } $points = isset($attribs['points']) ? $attribs['points'] : '0 0'; $points = trim($points); // note that point may use a complex syntax not covered here $points = preg_split('/[\\,\\s]+/si', $points); if (count($points) < 4) { break; } $p = array(); $xmin = 2147483647; $xmax = 0; $ymin = 2147483647; $ymax = 0; foreach ($points as $key => $val) { $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); if ($key % 2 == 0) { // X coordinate $xmin = min($xmin, $p[$key]); $xmax = max($xmax, $p[$key]); } else { // Y coordinate $ymin = min($ymin, $p[$key]); $ymax = max($ymax, $p[$key]); } } $x = $xmin; $y = $ymin; $w = $xmax - $xmin; $h = $ymax - $ymin; if ($name == 'polyline') { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); if (!empty($obstyle)) { $this->PolyLine($p, $obstyle, array(), array()); } $this->StopTransform(); } else { // polygon if ($clipping) { $this->SVGTransform($tm); $this->Polygon($p, 'CNZ', array(), array(), true); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ')); if (!empty($obstyle)) { $this->Polygon($p, $obstyle, array(), array(), true); } $this->StopTransform(); } } break; // image // image case 'image': if ($invisible) { break; } if (!isset($attribs['xlink:href']) or empty($attribs['xlink:href'])) { break; } $x = isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0; $y = isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0; $w = isset($attribs['width']) ? $this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false) : 0; $h = isset($attribs['height']) ? $this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false) : 0; $img = $attribs['xlink:href']; if (!$clipping) { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h); if (preg_match('/^data:image\\/[^;]+;base64,/', $img, $m) > 0) { // embedded image encoded as base64 $img = '@' . base64_decode(substr($img, strlen($m[0]))); } else { // fix image path if (!TCPDF_STATIC::empty_string($this->svgdir) and ($img[0] == '.' or basename($img) == $img)) { // replace relative path with full server path $img = $this->svgdir . '/' . $img; } if ($img[0] == '/' and !empty($_SERVER['DOCUMENT_ROOT']) and $_SERVER['DOCUMENT_ROOT'] != '/') { $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']); if ($findroot === false or $findroot > 1) { if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1) . $img; } else { $img = $_SERVER['DOCUMENT_ROOT'] . $img; } } } $img = urldecode($img); $testscrtype = @parse_url($img); if (!isset($testscrtype['query']) or empty($testscrtype['query'])) { // convert URL to server path $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img); } } // get image type $imgtype = TCPDF_IMAGES::getImageFileType($img); if ($imgtype == 'eps' or $imgtype == 'ai') { $this->ImageEps($img, $x, $y, $w, $h); } elseif ($imgtype == 'svg') { $this->ImageSVG($img, $x, $y, $w, $h); } else { $this->Image($img, $x, $y, $w, $h); } $this->StopTransform(); } break; // text // text case 'text': case 'tspan': // only basic support - advanced features must be implemented $this->svgtextmode['invisible'] = $invisible; if ($invisible) { break; } array_push($this->svgstyles, $svgstyle); if (isset($attribs['x'])) { $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false); } elseif ($name == 'tspan') { $x = $this->x; } else { $x = 0; } if (isset($attribs['dx'])) { $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false); } if (isset($attribs['y'])) { $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false); } elseif ($name == 'tspan') { $y = $this->y; } else { $y = 0; } if (isset($attribs['dy'])) { $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false); } $svgstyle['text-color'] = $svgstyle['fill']; $this->svgtext = ''; if (isset($svgstyle['text-anchor'])) { $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor']; } else { $this->svgtextmode['text-anchor'] = 'start'; } if (isset($svgstyle['direction'])) { if ($svgstyle['direction'] == 'rtl') { $this->svgtextmode['rtl'] = true; } else { $this->svgtextmode['rtl'] = false; } } else { $this->svgtextmode['rtl'] = false; } if (isset($svgstyle['stroke']) and $svgstyle['stroke'] != 'none' and isset($svgstyle['stroke-width']) and $svgstyle['stroke-width'] > 0) { $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false); } else { $this->svgtextmode['stroke'] = false; } $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1); $this->x = $x; $this->y = $y; break; // use // use case 'use': if (isset($attribs['xlink:href']) and !empty($attribs['xlink:href'])) { $svgdefid = substr($attribs['xlink:href'], 1); if (isset($this->svgdefs[$svgdefid])) { $use = $this->svgdefs[$svgdefid]; if (isset($attribs['xlink:href'])) { unset($attribs['xlink:href']); } if (isset($attribs['id'])) { unset($attribs['id']); } $attribs = array_merge($attribs, $use['attribs']); $this->startSVGElementHandler($parser, $use['name'], $attribs); } } break; default: break; } // end of switch }
/** * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***) * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. * @param $attribs (array) The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on. * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix). * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) { $name = $this->removeTagNamespace($name); // check if we are in clip mode if ($this->svgclipmode) { $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]); return; } if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) { if (isset($attribs['id'])) { $attribs['child_elements'] = array(); $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs); return; } if (end($this->svgdefs) !== FALSE) { $last_svgdefs_id = key($this->svgdefs); if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) { $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1); $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs); return; } } return; } $clipping = false; if ($parser == 'clip-path') { // set clipping mode $clipping = true; } // get styling properties $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style $svgstyle = $this->svgstyles[0]; // set default style if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) { // default fill attribute for clipping $attribs['fill'] = 'none'; } if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) { // fix style for regular expression $attribs['style'] = ';'.$attribs['style']; } foreach ($prev_svgstyle as $key => $val) { if (in_array($key, TCPDF_IMAGES::$svginheritprop)) { // inherit previous value $svgstyle[$key] = $val; } if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) { // specific attribute settings if ($attribs[$key] == 'inherit') { $svgstyle[$key] = $val; } else { $svgstyle[$key] = $attribs[$key]; } } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) { // CSS style syntax $attrval = array(); if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) { if ($attrval[1] == 'inherit') { $svgstyle[$key] = $val; } else { $svgstyle[$key] = $attrval[1]; } } } } // transformation matrix if (!empty($ctm)) { $tm = $ctm; } else { $tm = array(1,0,0,1,0,0); } if (isset($attribs['transform']) AND !empty($attribs['transform'])) { $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform'])); } $svgstyle['transfmatrix'] = $tm; $invisible = false; if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) { // the current graphics element is invisible (nothing is painted) $invisible = true; } // process tag switch($name) { case 'defs': { $this->svgdefsmode = true; break; } // clipPath case 'clipPath': { if ($invisible) { break; } $this->svgclipmode = true; if (!isset($attribs['id'])) { $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1); } $this->svgclipid = $attribs['id']; $this->svgclippaths[$this->svgclipid] = array(); $this->svgcliptm[$this->svgclipid] = $tm; break; } case 'svg': { // start of SVG object if(++$this->svg_tag_depth <= 1) { break; } // inner SVG array_push($this->svgstyles, $svgstyle); $this->StartTransform(); $svgX = (isset($attribs['x'])?$attribs['x']:0); $svgY = (isset($attribs['y'])?$attribs['y']:0); $svgW = (isset($attribs['width'])?$attribs['width']:0); $svgH = (isset($attribs['height'])?$attribs['height']:0); // set x, y position using transform matrix $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY)); $this->SVGTransform($tm); // set clipping for width and height $x = 0; $y = 0; $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w); $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h); // draw clipping rect $this->Rect($x, $y, $w, $h, 'CNZ', array(), array()); // parse viewbox, calculate extra transformation matrix if (isset($attribs['viewBox'])) { $tmp = array(); preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp); $tmp = $tmp[0]; if (sizeof($tmp) == 4) { $vx = $tmp[0]; $vy = $tmp[1]; $vw = $tmp[2]; $vh = $tmp[3]; // get aspect ratio $tmp = array(); $aspectX = 'xMid'; $aspectY = 'YMid'; $fit = 'meet'; if (isset($attribs['preserveAspectRatio'])) { if($attribs['preserveAspectRatio'] == 'none') { $fit = 'none'; } else { preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp); $tmp = $tmp[0]; if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) { $aspectX = substr($tmp[0], 0, 4); $aspectY = substr($tmp[0], 4, 4); $fit = $tmp[1]; } } } $wr = ($svgW / $vw); $hr = ($svgH / $vh); $ax = $ay = 0; if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) { if ($aspectX == 'xMax') { $ax = (($vw * ($wr / $hr)) - $vw); } if ($aspectX == 'xMid') { $ax = ((($vw * ($wr / $hr)) - $vw) / 2); } $wr = $hr; } elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) { if ($aspectY == 'YMax') { $ay = (($vh * ($hr / $wr)) - $vh); } if ($aspectY == 'YMid') { $ay = ((($vh * ($hr / $wr)) - $vh) / 2); } $hr = $wr; } $newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY)); $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, $newtm); $this->SVGTransform($tm); } } $this->setSVGStyles($svgstyle, $prev_svgstyle); break; } case 'g': { // group together related graphics elements array_push($this->svgstyles, $svgstyle); $this->StartTransform(); $x = (isset($attribs['x'])?$attribs['x']:0); $y = (isset($attribs['y'])?$attribs['y']:0); $w = 1;//(isset($attribs['width'])?$attribs['width']:1); $h = 1;//(isset($attribs['height'])?$attribs['height']:1); $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y)); $this->SVGTransform($tm); $this->setSVGStyles($svgstyle, $prev_svgstyle); break; } case 'linearGradient': { if ($this->pdfa_mode) { break; } if (!isset($attribs['id'])) { $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); } $this->svggradientid = $attribs['id']; $this->svggradients[$this->svggradientid] = array(); $this->svggradients[$this->svggradientid]['type'] = 2; $this->svggradients[$this->svggradientid]['stops'] = array(); if (isset($attribs['gradientUnits'])) { $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; } else { $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; } //$attribs['spreadMethod'] if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2']))) OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%')) OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%')) OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%')) OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) { $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; } else { $this->svggradients[$this->svggradientid]['mode'] = 'measure'; } $x1 = (isset($attribs['x1'])?$attribs['x1']:'0'); $y1 = (isset($attribs['y1'])?$attribs['y1']:'0'); $x2 = (isset($attribs['x2'])?$attribs['x2']:'100'); $y2 = (isset($attribs['y2'])?$attribs['y2']:'0'); if (isset($attribs['gradientTransform'])) { $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); } $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2); if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { // gradient is defined on another place $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); } break; } case 'radialGradient': { if ($this->pdfa_mode) { break; } if (!isset($attribs['id'])) { $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); } $this->svggradientid = $attribs['id']; $this->svggradients[$this->svggradientid] = array(); $this->svggradients[$this->svggradientid]['type'] = 3; $this->svggradients[$this->svggradientid]['stops'] = array(); if (isset($attribs['gradientUnits'])) { $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; } else { $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; } //$attribs['spreadMethod'] if (((!isset($attribs['cx'])) AND (!isset($attribs['cy']))) OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%')) OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) { $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) { $this->svggradients[$this->svggradientid]['mode'] = 'ratio'; } else { $this->svggradients[$this->svggradientid]['mode'] = 'measure'; } $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5); $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5); $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx); $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy); $r = (isset($attribs['r']) ? $attribs['r'] : 0.5); if (isset($attribs['gradientTransform'])) { $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']); } $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r); if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { // gradient is defined on another place $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); } break; } case 'stop': { // gradient stops if (substr($attribs['offset'], -1) == '%') { $offset = floatval(substr($attribs['offset'], -1)) / 100; } else { $offset = floatval($attribs['offset']); if ($offset > 1) { $offset /= 100; } } $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black'; $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1; $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity); break; } // paths case 'path': { if ($invisible) { break; } if (isset($attribs['d'])) { $d = trim($attribs['d']); if (!empty($d)) { $x = (isset($attribs['x'])?$attribs['x']:0); $y = (isset($attribs['y'])?$attribs['y']:0); $w = (isset($attribs['width'])?$attribs['width']:1); $h = (isset($attribs['height'])?$attribs['height']:1); $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y)); if ($clipping) { $this->SVGTransform($tm); $this->SVGPath($d, 'CNZ'); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ')); if (!empty($obstyle)) { $this->SVGPath($d, $obstyle); } $this->StopTransform(); } } } break; } // shapes case 'rect': { if ($invisible) { break; } $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx); if ($clipping) { $this->SVGTransform($tm); $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array()); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ')); if (!empty($obstyle)) { $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array()); } $this->StopTransform(); } break; } case 'circle': { if ($invisible) { break; } $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0); $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); $x = ($cx - $r); $y = ($cy - $r); $w = (2 * $r); $h = $w; if ($clipping) { $this->SVGTransform($tm); $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ')); if (!empty($obstyle)) { $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8); } $this->StopTransform(); } break; } case 'ellipse': { if ($invisible) { break; } $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0); $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0); $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); $x = ($cx - $rx); $y = ($cy - $ry); $w = (2 * $rx); $h = (2 * $ry); if ($clipping) { $this->SVGTransform($tm); $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ')); if (!empty($obstyle)) { $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8); } $this->StopTransform(); } break; } case 'line': { if ($invisible) { break; } $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0); $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0); $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0); $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0); $x = $x1; $y = $y1; $w = abs($x2 - $x1); $h = abs($y2 - $y1); if (!$clipping) { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2)); $this->Line($x1, $y1, $x2, $y2); $this->StopTransform(); } break; } case 'polyline': case 'polygon': { if ($invisible) { break; } $points = (isset($attribs['points'])?$attribs['points']:'0 0'); $points = trim($points); // note that point may use a complex syntax not covered here $points = preg_split('/[\,\s]+/si', $points); if (count($points) < 4) { break; } $p = array(); $xmin = 2147483647; $xmax = 0; $ymin = 2147483647; $ymax = 0; foreach ($points as $key => $val) { $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); if (($key % 2) == 0) { // X coordinate $xmin = min($xmin, $p[$key]); $xmax = max($xmax, $p[$key]); } else { // Y coordinate $ymin = min($ymin, $p[$key]); $ymax = max($ymax, $p[$key]); } } $x = $xmin; $y = $ymin; $w = ($xmax - $xmin); $h = ($ymax - $ymin); if ($name == 'polyline') { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); if (!empty($obstyle)) { $this->PolyLine($p, $obstyle, array(), array()); } $this->StopTransform(); } else { // polygon if ($clipping) { $this->SVGTransform($tm); $this->Polygon($p, 'CNZ', array(), array(), true); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ')); if (!empty($obstyle)) { $this->Polygon($p, $obstyle, array(), array(), true); } $this->StopTransform(); } } break; } // image case 'image': { if ($invisible) { break; } if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) { break; } $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); $img = $attribs['xlink:href']; if (!$clipping) { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h); if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) { // embedded image encoded as base64 $img = '@'.base64_decode(substr($img, strlen($m[0]))); } else { // fix image path if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) { // replace relative path with full server path $img = $this->svgdir.'/'.$img; } if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']); if (($findroot === false) OR ($findroot > 1)) { if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img; } else { $img = $_SERVER['DOCUMENT_ROOT'].$img; } } } $img = urldecode($img); $testscrtype = @parse_url($img); if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { // convert URL to server path $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img); } } // get image type $imgtype = TCPDF_IMAGES::getImageFileType($img); if (($imgtype == 'eps') OR ($imgtype == 'ai')) { $this->ImageEps($img, $x, $y, $w, $h); } elseif ($imgtype == 'svg') { // store SVG vars $svggradients = $this->svggradients; $svggradientid = $this->svggradientid; $svgdefsmode = $this->svgdefsmode; $svgdefs = $this->svgdefs; $svgclipmode = $this->svgclipmode; $svgclippaths = $this->svgclippaths; $svgcliptm = $this->svgcliptm; $svgclipid = $this->svgclipid; $svgtext = $this->svgtext; $svgtextmode = $this->svgtextmode; $this->ImageSVG($img, $x, $y, $w, $h); // restore SVG vars $this->svggradients = $svggradients; $this->svggradientid = $svggradientid; $this->svgdefsmode = $svgdefsmode; $this->svgdefs = $svgdefs; $this->svgclipmode = $svgclipmode; $this->svgclippaths = $svgclippaths; $this->svgcliptm = $svgcliptm; $this->svgclipid = $svgclipid; $this->svgtext = $svgtext; $this->svgtextmode = $svgtextmode; } else { $this->Image($img, $x, $y, $w, $h); } $this->StopTransform(); } break; } // text case 'text': case 'tspan': { if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) { // @TODO: unsupported feature } // only basic support - advanced features must be implemented $this->svgtextmode['invisible'] = $invisible; if ($invisible) { break; } array_push($this->svgstyles, $svgstyle); if (isset($attribs['x'])) { $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false); } elseif ($name == 'tspan') { $x = $this->x; } else { $x = 0; } if (isset($attribs['dx'])) { $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false); } if (isset($attribs['y'])) { $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false); } elseif ($name == 'tspan') { $y = $this->y; } else { $y = 0; } if (isset($attribs['dy'])) { $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false); } $svgstyle['text-color'] = $svgstyle['fill']; $this->svgtext = ''; if (isset($svgstyle['text-anchor'])) { $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor']; } else { $this->svgtextmode['text-anchor'] = 'start'; } if (isset($svgstyle['direction'])) { if ($svgstyle['direction'] == 'rtl') { $this->svgtextmode['rtl'] = true; } else { $this->svgtextmode['rtl'] = false; } } else { $this->svgtextmode['rtl'] = false; } if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) { $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false); } else { $this->svgtextmode['stroke'] = false; } $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1); $this->x = $x; $this->y = $y; break; } // use case 'use': { if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { $svgdefid = substr($attribs['xlink:href'], 1); if (isset($this->svgdefs[$svgdefid])) { $use = $this->svgdefs[$svgdefid]; if (isset($attribs['xlink:href'])) { unset($attribs['xlink:href']); } if (isset($attribs['id'])) { unset($attribs['id']); } if (isset($use['attribs']['x']) AND isset($attribs['x'])) { $attribs['x'] += $use['attribs']['x']; } if (isset($use['attribs']['y']) AND isset($attribs['y'])) { $attribs['y'] += $use['attribs']['y']; } if (empty($attribs['style'])) { $attribs['style'] = ''; } if (!empty($use['attribs']['style'])) { // merge styles $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']); } $attribs = array_merge($use['attribs'], $attribs); $this->startSVGElementHandler($parser, $use['name'], $attribs); return; } } break; } default: { break; } } // end of switch // process child elements if (!empty($attribs['child_elements'])) { $child_elements = $attribs['child_elements']; unset($attribs['child_elements']); foreach($child_elements as $child_element) { if (empty($child_element['attribs']['closing_tag'])) { $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']); } else { if (isset($child_element['attribs']['content'])) { $this->svgtext = $child_element['attribs']['content']; } $this->endSVGElementHandler('child-tag', $child_element['name']); } } } }