1
0
aclindsay.com/svgparser/parser/parser/Parser.php
2011-02-24 12:25:26 -07:00

398 lines
15 KiB
PHP

<?php
/*
SVG to dojox.gfx Parser
Copyright (C) 2009 Aaron Lindsay <aclindsay@aclindsay.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
require_once("objects/SVG.php");
require_once("objects/Path.php");
require_once("objects/Rectangle.php");
require_once("objects/Circle.php");
require_once("objects/Ellipse.php");
require_once("objects/Line.php");
require_once("objects/Polyline.php");
require_once("objects/Line.php");
require_once("objects/Text.php");
require_once("objects/Gradient.php");
require_once("objects/RadialGradient.php");
require_once("objects/LinearGradient.php");
require_once("objects/GradientStop.php");
require_once("objects/Transform.php");
class Parser {
private $elementStack;
private $svg;
private $transformStack;
private $characterDataStack;
public function parse($SVGstring) {
//reset the stack of elements in case someone already ran this
unset($this->elementStack);
$this->elementStack = array();
//reset the stack of transforms in case someone already ran this
unset($this->transformStack);
$this->transformStack = array();
//reset the stack of character data (this is used for the text fields)
$this->characterDataStack = array();
$this->svg = new SVG();
//create the parser object
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); //set to 0 so all the elements don't get converted to upper case
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); //we don't like extra white-space so skip it
//set the handlers
xml_set_element_handler($parser, array($this, "startElementHandler"), array($this,"endElementHandler"));
xml_set_character_data_handler($parser, array($this, "characterDataHandler"));
xml_parse($parser, $SVGstring);
xml_parser_free($parser); //parser... be free!!!
return $this->svg;
}
private function startElementHandler($parser, $name, $attribs) {
$currentTransform = new Transform();
switch ($name) {
case "path":
$path = new Path();
if (array_key_exists("id", $attribs))
$path->setId($attribs["id"]);
if (array_key_exists("d", $attribs))
$path->setPath($attribs["d"]);
if (array_key_exists("style", $attribs))
$path->setStyles($attribs["style"]);
if (array_key_exists("stroke", $attribs))
$path->setStroke($attribs["stroke"]);
if (array_key_exists("stroke-width", $attribs))
$path->setStrokeWidth($attribs["stroke-width"]);
if (array_key_exists("stroke-linecap", $attribs))
$path->setStrokeLinecap($attribs["stroke-linecap"]);
if (array_key_exists("fill", $attribs))
$path->setFill($attribs["fill"]);
if (array_key_exists("transform", $attribs)) {
$path->setTransforms($attribs["transform"]);
$currentTransform = $path->getTransform();
}
$this->elementStack[] = $path;
break;
case "rect":
$rect = new Rectangle();
if (array_key_exists("id", $attribs))
$rect->setId($attribs["id"]);
if (array_key_exists("x", $attribs))
$rect->setX($attribs["x"]);
if (array_key_exists("y", $attribs))
$rect->setY($attribs["y"]);
if (array_key_exists("height", $attribs))
$rect->setHeight($attribs["height"]);
if (array_key_exists("width", $attribs))
$rect->setWidth($attribs["width"]);
if (array_key_exists("rx", $attribs))
$rect->setRx($attribs["rx"]);
if (array_key_exists("ry", $attribs))
$rect->setRy($attribs["ry"]);
if (array_key_exists("style", $attribs))
$rect->setStyles($attribs["style"]);
if (array_key_exists("stroke", $attribs))
$rect->setStroke($attribs["stroke"]);
if (array_key_exists("stroke-width", $attribs))
$rect->setStrokeWidth($attribs["stroke-width"]);
if (array_key_exists("stroke-linecap", $attribs))
$rect->setStrokeLinecap($attribs["stroke-linecap"]);
if (array_key_exists("fill", $attribs))
$rect->setFill($attribs["fill"]);
if (array_key_exists("transform", $attribs)) {
$rect->setTransforms($attribs["transform"]);
$currentTransform = $rect->getTransform();
}
$this->elementStack[] = $rect;
break;
case "circle":
$circle = new Circle();
if (array_key_exists("id", $attribs))
$circle->setId($attribs["id"]);
if (array_key_exists("cx", $attribs))
$circle->setCx($attribs["cx"]);
if (array_key_exists("cy", $attribs))
$circle->setCy($attribs["cy"]);
if (array_key_exists("r", $attribs))
$circle->setR($attribs["r"]);
if (array_key_exists("style", $attribs))
$circle->setStyles($attribs["style"]);
if (array_key_exists("stroke", $attribs))
$circle->setStroke($attribs["stroke"]);
if (array_key_exists("stroke-width", $attribs))
$circle->setStrokeWidth($attribs["stroke-width"]);
if (array_key_exists("stroke-linecap", $attribs))
$circle->setStrokeLinecap($attribs["stroke-linecap"]);
if (array_key_exists("fill", $attribs))
$circle->setFill($attribs["fill"]);
if (array_key_exists("transform", $attribs)) {
$circle->setTransforms($attribs["transform"]);
$currentTransform = $circle->getTransform();
}
$this->elementStack[] = $circle;
break;
case "ellipse":
$ellipse = new Ellipse();
if (array_key_exists("id", $attribs))
$ellipse->setId($attribs["id"]);
if (array_key_exists("cx", $attribs))
$ellipse->setCx($attribs["cx"]);
if (array_key_exists("cy", $attribs))
$ellipse->setCy($attribs["cy"]);
if (array_key_exists("rx", $attribs))
$ellipse->setRx($attribs["rx"]);
if (array_key_exists("ry", $attribs))
$ellipse->setRy($attribs["ry"]);
if (array_key_exists("style", $attribs))
$ellipse->setStyles($attribs["style"]);
if (array_key_exists("stroke", $attribs))
$ellipse->setStroke($attribs["stroke"]);
if (array_key_exists("stroke-width", $attribs))
$ellipse->setStrokeWidth($attribs["stroke-width"]);
if (array_key_exists("stroke-linecap", $attribs))
$ellipse->setStrokeLinecap($attribs["stroke-linecap"]);
if (array_key_exists("fill", $attribs))
$ellipse->setFill($attribs["fill"]);
if (array_key_exists("transform", $attribs)) {
$ellipse->setTransforms($attribs["transform"]);
$currentTransform = $ellipse->getTransform();
}
$this->elementStack[] = $ellipse;
break;
case "line":
$line = new Line();
if (array_key_exists("id", $attribs))
$line->setId($attribs["id"]);
if (array_key_exists("x1", $attribs))
$line->setX1($attribs["x1"]);
if (array_key_exists("y1", $attribs))
$line->setY1($attribs["y1"]);
if (array_key_exists("x2", $attribs))
$line->setX2($attribs["x2"]);
if (array_key_exists("y2", $attribs))
$line->setY2($attribs["y2"]);
if (array_key_exists("style", $attribs))
$line->setStyles($attribs["style"]);
if (array_key_exists("stroke", $attribs))
$line->setStroke($attribs["stroke"]);
if (array_key_exists("stroke-width", $attribs))
$line->setStrokeWidth($attribs["stroke-width"]);
if (array_key_exists("stroke-linecap", $attribs))
$line->setStrokeLinecap($attribs["stroke-linecap"]);
if (array_key_exists("transform", $attribs)) {
$line->setTransforms($attribs["transform"]);
$currentTransform = $line->getTransform();
}
$this->elementStack[] = $line;
break;
case "polyline":
$polyline = new Polyline();
if (array_key_exists("id", $attribs))
$polyline->setId($attribs["id"]);
if (array_key_exists("points", $attribs))
$polyline->setPoints($attribs["points"]);
if (array_key_exists("style", $attribs))
$polyline->setStyles($attribs["style"]);
if (array_key_exists("stroke", $attribs))
$polyline->setStroke($attribs["stroke"]);
if (array_key_exists("stroke-width", $attribs))
$polyline->setStrokeWidth($attribs["stroke-width"]);
if (array_key_exists("stroke-linecap", $attribs))
$polyline->setStrokeLinecap($attribs["stroke-linecap"]);
if (array_key_exists("transform", $attribs)) {
$polyline->setTransforms($attribs["transform"]);
$currentTransform = $polyline->getTransform();
}
$this->elementStack[] = $polyline;
break;
case "text":
case "tspan":
$text = new Text();
if (array_key_exists("id", $attribs))
$text->setId($attribs["id"]);
if (array_key_exists("x", $attribs))
$text->setX($attribs["x"]);
if (array_key_exists("y", $attribs))
$text->setY($attribs["y"]);
if (array_key_exists("style", $attribs))
$text->setStyles($attribs["style"]);
if (array_key_exists("stroke", $attribs))
$text->setStroke($attribs["stroke"]);
if (array_key_exists("stroke-width", $attribs))
$text->setStrokeWidth($attribs["stroke-width"]);
if (array_key_exists("stroke-linecap", $attribs))
$text->setStrokeLinecap($attribs["stroke-linecap"]);
if (array_key_exists("fill", $attribs))
$text->setFill($attribs["fill"]);
if (array_key_exists("text-anchor", $attribs))
$text->setTextAnchor($attribs["text-anchor"]);
if (array_key_exists("text-decoration", $attribs))
$text->setTextDecoration($attribs["text-decoration"]);
if (array_key_exists("transform", $attribs)) {
$text->setTransforms($attribs["transform"]);
$currentTransform = $text->getTransform();
}
$this->elementStack[] = $text;
break;
case "linearGradient":
$linearGradient = new LinearGradient();
if (array_key_exists("id", $attribs))
$linearGradient->setId($attribs["id"]);
if (array_key_exists("style", $attribs))
$linearGradient->setStyles($attribs["style"]);
if (array_key_exists("gradientTransform", $attribs))
$linearGradient->setTransforms($attribs["gradientTransform"]);
if (array_key_exists("xlink:href", $attribs))
$linearGradient->setLink($attribs["xlink:href"]);
if (array_key_exists("x1", $attribs))
$linearGradient->setX1($attribs["x1"]);
if (array_key_exists("y1", $attribs))
$linearGradient->setY1($attribs["y1"]);
if (array_key_exists("x2", $attribs))
$linearGradient->setX2($attribs["x2"]);
if (array_key_exists("y2", $attribs))
$linearGradient->setY2($attribs["y2"]);
$this->elementStack[] = $linearGradient;
break;
case "stop":
$stop = new GradientStop();
if (array_key_exists("id", $attribs))
$stop->setId($attribs["id"]);
if (array_key_exists("style", $attribs))
$stop->setStyles($attribs["style"]);
if (array_key_exists("offset", $attribs))
$stop->setOffset($attribs["offset"]);
$this->elementStack[] = $stop;
break;
case "radialGradient":
$radialGradient = new RadialGradient();
if (array_key_exists("id", $attribs))
$radialGradient->setId($attribs["id"]);
if (array_key_exists("style", $attribs))
$radialGradient->setStyles($attribs["style"]);
if (array_key_exists("gradientTransform", $attribs))
$radialGradient->setTransforms($attribs["gradientTransform"]);
if (array_key_exists("xlink:href", $attribs))
$radialGradient->setLink($attribs["xlink:href"]);
if (array_key_exists("cx", $attribs))
$radialGradient->setCx($attribs["cx"]);
if (array_key_exists("cy", $attribs))
$radialGradient->setCy($attribs["cy"]);
if (array_key_exists("r", $attribs))
$radialGradient->setR($attribs["r"]);
if (array_key_exists("fx", $attribs))
$radialGradient->setFx($attribs["fx"]);
if (array_key_exists("fy", $attribs))
$radialGradient->setFy($attribs["fy"]);
$this->elementStack[] = $radialGradient;
break;
case "g":
if (array_key_exists("transform", $attribs)) {
$element = new Element();
$element->setTransforms($attribs["transform"]);
$currentTransform = $element->getTransform();
}
break;
}
//dump the transforms on the stack of them so if there are any nested elements, they can access them
$this->transformStack[] = $currentTransform;
}
private function characterDataHandler($parser, $data) {
$this->characterDataStack[] = $data;
}
private function endElementHandler($parser, $name) {
//as well as the last text data
$lastText = array_pop($this->characterDataStack);
switch ($name) {
case "path":
$element = array_pop($this->elementStack);
$element->setTransform($this->getFullCurrentTransform());
$this->svg->addPath($element);
break;
case "rect":
$element = array_pop($this->elementStack);
$element->setTransform($this->getFullCurrentTransform());
$this->svg->addRectangle($element);
break;
case "circle":
$element = array_pop($this->elementStack);
$element->setTransform($this->getFullCurrentTransform());
$this->svg->addCircle($element);
break;
case "ellipse":
$element = array_pop($this->elementStack);
$element->setTransform($this->getFullCurrentTransform());
$this->svg->addEllipse($element);
break;
case "line":
$element = array_pop($this->elementStack);
$element->setTransform($this->getFullCurrentTransform());
$this->svg->addLine($element);
break;
case "polyline":
$element = array_pop($this->elementStack);
$element->setTransform($this->getFullCurrentTransform());
$this->svg->addPolyline($element);
break;
case "text":
case "tspan":
$element = array_pop($this->elementStack);
$element->setTransform($this->getFullCurrentTransform());
$element->setText($lastText);
//add the element only if the text has at least some non-whitespace
if (trim($lastText) != "")
$this->svg->addText($element);
break;
case "linearGradient":
$this->svg->addLinearGradient(array_pop($this->elementStack));
break;
case "stop":
$stop = array_pop($this->elementStack);
if ($this->elementStack[count($this->elementStack)-1] instanceof Gradient)
$this->elementStack[count($this->elementStack)-1]->addStop($stop);
break;
case "radialGradient":
$this->svg->addRadialGradient(array_pop($this->elementStack));
break;
}
//pop the last transform off the stack
array_pop($this->transformStack);
}
private function getFullCurrentTransform() {
$one = new Transform();
foreach ($this->transformStack as $transform)
$one->multiplyBy($transform);
return $one;
}
}
?>