diff options
Diffstat (limited to 'src/dom/svgparser.cpp')
| -rwxr-xr-x | src/dom/svgparser.cpp | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/src/dom/svgparser.cpp b/src/dom/svgparser.cpp new file mode 100755 index 000000000..640fe9237 --- /dev/null +++ b/src/dom/svgparser.cpp @@ -0,0 +1,762 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "svgparser.h" +#include "cssparser.h" +#include "charclass.h" + +#include <stdarg.h> + +#define SVG_NAMESPACE "http://www.w3.org/2000/svg" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace svg +{ + + +//######################################################################### +//# M E S S A G E S +//######################################################################### + + +/** + * + */ +void SvgParser::error(char *fmt, ...) +{ + va_list args; + fprintf(stderr, "SvgParser:error:"); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args) ; + fprintf(stderr, "\n"); +} + + + +//######################################################################### +//# P A R S I N G +//######################################################################### + +/** + * Get the character at the position and record the fact + */ +XMLCh SvgParser::get(int p) +{ + if (p >= parselen) + return 0; + XMLCh ch = parsebuf[p]; + //printf("%c", ch); + lastPosition = p; + return ch; +} + + + +/** + * Test if the given substring exists at the given position + * in parsebuf. Use get() in case of out-of-bounds + */ +bool SvgParser::match(int pos, char *str) +{ + while (*str) + { + if (get(pos++) != *str++) + return false; + } + return true; +} + +/** + * + */ +int SvgParser::skipwhite(int p) +{ + while (p < parselen) + { + //# XML COMMENT + if (match(p, "<!--")) + { + p+=4; + bool done=false; + while (p<parselen) + { + if (match(p, "-->")) + { + p+=3; + done=true; + break; + } + p++; + } + lastPosition = p; + if (!done) + { + error("unterminated <!-- .. --> comment"); + return -1; + } + } + //# C comment + else if (match(p, "/*")) + { + p+=2; + bool done=false; + while (p<parselen) + { + if (match(p, "*/")) + { + p+=2; + done=true; + break; + } + p++; + } + lastPosition = p; + if (!done) + { + error("unterminated /* .. */ comment"); + return -1; + } + } + else if (!isspace(get(p))) + break; + else + p++; + } + lastPosition = p; + return p; +} + +/** + * get a word from the buffer + */ +int SvgParser::getWord(int p, DOMString &result) +{ + XMLCh ch = get(p); + if (!isLetter(ch)) + return p; + DOMString str; + str.push_back(ch); + p++; + + while (p < parselen) + { + ch = get(p); + if (isLetterOrDigit(ch) || ch=='-' || ch=='_') + { + str.push_back(ch); + p++; + } + else if (ch == '\\') + { + p+=2; + } + else + break; + } + result = str; + return p; +} + + +# if 0 +/** + * get a word from the buffer + */ +int SvgParser::getNumber(int p0, double &result) +{ + int p=p0; + + DOMString str; + + //allow sign + if (get(p) == '-') + { + p++; + } + + while (p < parselen) + { + XMLCh ch = get(p); + if (ch<'0' || ch>'9') + break; + str.push_back(ch); + p++; + } + if (get(p) == '.' && get(p+1)>='0' && get(p+1)<='9') + { + p++; + str.push_back('.'); + while (p < parselen) + { + XMLCh ch = get(p); + if (ch<'0' || ch>'9') + break; + str.push_back(ch); + p++; + } + } + if (p>p0) + { + char *start = (char *)str.c_str(); + char *end = NULL; + double val = strtod(start, &end); + if (end > start) + { + result = val; + return p; + } + } + + //not a number + return p0; +} +#endif + + +/** + * get a word from the buffer + */ +int SvgParser::getNumber(int p0, double &result) +{ + int p=p0; + + char buf[64]; + + int i; + for (i=0 ; i<63 && p<parselen ; i++) + { + buf[i] = (char) get(p++); + } + buf[i] = '\0'; + + char *start = buf; + char *end = NULL; + double val = strtod(start, &end); + if (end > start) + { + result = val; + int count = (int)(end - start); + p = p0 + count; + return p; + } + + //not a number + return p0; +} + + +bool SvgParser::parseTransform(const DOMString &str) +{ + parsebuf = str; + parselen = str.size(); + + //printf("transform:%s\n", str.c_str()); + + SVGTransformList transformList; + + int p = 0; + + while (p < parselen) + { + p = skipwhite(p); + DOMString name; + int p2 = getWord(p, name); + if (p2<0) + return false; + if (p2<=p) + { + error("transform: need transform name"); + //return false; + break; + } + p = p2; + //printf("transform name:%s\n", name.c_str()); + + //######### MATRIX + if (name == "matrix") + { + p = skipwhite(p); + if (get(p++) != '(') + { + error("matrix transform needs opening '('"); + return false; + } + int nrVals = 0; + double vals[6]; + bool seenBrace = false; + while (p < parselen && nrVals < 6) + { + p = skipwhite(p); + double val; + p2 = getNumber(p, val); + if (p2<0) + return false; + if (p2<=p) + { + error("matrix() expected number"); + return false; + } + vals[nrVals++] = val; + p = skipwhite(p2); + XMLCh ch = get(p); + if (ch == ',') + { + p++; + p = skipwhite(p); + ch = get(p); + } + if (ch == ')') + { + seenBrace = true; + p++; + break; + } + } + if (!seenBrace) + { + error("matrix() needs closing brace"); + return false; + } + if (nrVals != 6) + { + error("matrix() requires exactly 6 arguments"); + return false; + } + //We got our arguments + //printf("translate: %f %f %f %f %f %f\n", + // vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]); + SVGMatrix matrix(vals[0], vals[1], vals[2], + vals[3], vals[4], vals[5]); + SVGTransform transform; + transform.setMatrix(matrix); + transformList.appendItem(transform); + } + + //######### TRANSLATE + else if (name == "translate") + { + p = skipwhite(p); + if (get(p++) != '(') + { + error("matrix transform needs opening '('"); + return false; + } + p = skipwhite(p); + double x; + p2 = getNumber(p, x); + if (p2<0) + return false; + if (p2<=p) + { + error("translate() expected 'x' value"); + return false; + } + p = skipwhite(p2); + if (get(p) == ',') + { + p++; + p = skipwhite(p); + } + double y; + p2 = getNumber(p, y); + if (p2<0) + return false; + if (p2<=p) //no y specified. use default + y = 0.0; + p = skipwhite(p2); + if (get(p++) != ')') + { + error("translate() needs closing ')'"); + return false; + } + //printf("translate: %f %f\n", x, y); + SVGTransform transform; + transform.setTranslate(x, y); + transformList.appendItem(transform); + } + + //######### SCALE + else if (name == "scale") + { + p = skipwhite(p); + if (get(p++) != '(') + { + error("scale transform needs opening '('"); + return false; + } + p = skipwhite(p); + double x; + p2 = getNumber(p, x); + if (p2<0) + return false; + if (p2<=p) + { + error("scale() expected 'x' value"); + return false; + } + p = skipwhite(p2); + if (get(p) == ',') + { + p++; + p = skipwhite(p); + } + double y; + p2 = getNumber(p, y); + if (p2<0) + return false; + if (p2<=p) //no y specified. use default + y = x; // y is same as x. uniform scaling + p = skipwhite(p2); + if (get(p++) != ')') + { + error("scale() needs closing ')'"); + return false; + } + //printf("scale: %f %f\n", x, y); + SVGTransform transform; + transform.setScale(x, y); + transformList.appendItem(transform); + } + + //######### ROTATE + else if (name == "rotate") + { + p = skipwhite(p); + if (get(p++) != '(') + { + error("rotate transform needs opening '('"); + return false; + } + p = skipwhite(p); + double angle; + p2 = getNumber(p, angle); + if (p2<0) + return false; + if (p2<=p) + { + error("rotate() expected 'angle' value"); + return false; + } + p = skipwhite(p2); + if (get(p) == ',') + { + p++; + p = skipwhite(p); + } + double cx = 0.0; + double cy = 0.0; + p2 = getNumber(p, cx); + if (p2>p) + { + p = skipwhite(p2); + if (get(p) == ',') + { + p++; + p = skipwhite(p); + } + p2 = getNumber(p, cy); + if (p2<0) + return false; + if (p2<=p) + { + error("rotate() arguments should be either rotate(angle) or rotate(angle, cx, cy)"); + return false; + } + p = skipwhite(p2); + } + if (get(p++) != ')') + { + error("rotate() needs closing ')'"); + return false; + } + //printf("rotate: %f %f %f\n", angle, cx, cy); + SVGTransform transform; + transform.setRotate(angle, cx, cy); + transformList.appendItem(transform); + } + + //######### SKEWX + else if (name == "skewX") + { + p = skipwhite(p); + if (get(p++) != '(') + { + error("skewX transform needs opening '('"); + return false; + } + p = skipwhite(p); + double x; + p2 = getNumber(p, x); + if (p2<0) + return false; + if (p2<=p) + { + error("skewX() expected 'x' value"); + return false; + } + p = skipwhite(p2); + if (get(p++) != ')') + { + error("skewX() needs closing ')'"); + return false; + } + //printf("skewX: %f\n", x); + SVGTransform transform; + transform.setSkewX(x); + transformList.appendItem(transform); + } + + //######### SKEWY + else if (name == "skewY") + { + p = skipwhite(p); + if (get(p++) != '(') + { + error("skewY transform needs opening '('"); + return false; + } + p = skipwhite(p); + double y; + p2 = getNumber(p, y); + if (p2<0) + return false; + if (p2<=p) + { + error("skewY() expected 'y' value"); + return false; + } + p = skipwhite(p2); + if (get(p++) != ')') + { + error("skewY() needs closing ')'"); + return false; + } + //printf("skewY: %f\n", y); + SVGTransform transform; + transform.setSkewY(y); + transformList.appendItem(transform); + } + + //### NONE OF THE ABOVE + else + { + error("unknown transform type:'%s'", name.c_str()); + } + + p = skipwhite(p); + XMLCh ch = get(p); + if (ch == ',') + { + p++; + p = skipwhite(p); + } + + }//WHILE p<parselen + + return true; +} + + +/** + * + */ +bool SvgParser::parseElement(ElementImpl *parent, ElementImpl *sourceElem) +{ + if (!parent || !sourceElem) + { + error("NULL source element"); + return false; + } + + DOMString namespaceURI = sourceElem->getNamespaceURI(); + //printf("namespaceURI:%s\n", namespaceURI.c_str()); + DOMString tagName = sourceElem->getTagName(); + printf("tag name:%s\n", tagName.c_str()); + + ElementImpl *newElement = NULL; + if (namespaceURI != SVG_NAMESPACE) + { + newElement = new SVGSVGElementImpl(); + newElement->assign(*sourceElem); + parent->appendChild(newElement); + } + else //## SVG!! + { + + //#################################################### + //## ATTRIBUTES + //#################################################### + DOMString style = sourceElem->getAttribute("style"); + if (style.size() > 0) + { + css::CssParser parser; + style.insert(0, "{"); + style.append("}"); + //printf("CSS:%s\n", style.c_str()); + if (!parser.parse(style)) + { + error("parsing style attribute"); + } + else + { + //printf("##parsed!\n"); + } + } + + DOMString transform = sourceElem->getAttribute("transform"); + if (transform.size() > 0) + { + if (!parseTransform(transform)) + { + error("parsing transform attribute"); + } + else + { + //printf("##parsed!\n"); + } + } + + //#################################################### + //## ELEMENT - SPECIFIC + //#################################################### + if (tagName == "svg") + { + newElement = new SVGSVGElementImpl(); + newElement->assign(*sourceElem); + parent->appendChild(newElement); + } + else if (tagName == "title") + { + newElement = new SVGTitleElementImpl(); + newElement->assign(*sourceElem); + parent->appendChild(newElement); + } + else if (tagName == "desc") + { + newElement = new SVGDescElementImpl(); + newElement->assign(*sourceElem); + parent->appendChild(newElement); + } + else if (tagName == "defs") + { + newElement = new SVGDefsElementImpl(); + newElement->assign(*sourceElem); + parent->appendChild(newElement); + } + else if (tagName == "style") + { + newElement = new SVGStyleElementImpl(); + newElement->assign(*sourceElem); + parent->appendChild(newElement); + } + else if (tagName == "g") + { + newElement = new SVGGElementImpl(); + newElement->assign(*sourceElem); + parent->appendChild(newElement); + } + else if (tagName == "path") + { + newElement = new SVGPathElementImpl(); + newElement->assign(*sourceElem); + parent->appendChild(newElement); + } + } + + NodeList children = sourceElem->getChildNodes(); + int nodeCount = children.getLength(); + for (int i=0 ; i<nodeCount ; i++) + { + Node *child = children.item(i); + int typ = child->getNodeType(); + if (typ == Node::TEXT_NODE) + { + Node *newNode = doc->createTextNode(child->getNodeValue()); + parent->appendChild(newNode); + } + else if (typ == Node::CDATA_SECTION_NODE) + { + Node *newNode = doc->createCDATASection(child->getNodeValue()); + parent->appendChild(newNode); + } + else if (newElement && typ == Node::ELEMENT_NODE) + { + ElementImpl *childElement = dynamic_cast<ElementImpl *>(child); + parseElement(newElement, childElement); + } + } + return true; +} + + +/** + * + */ +Document *SvgParser::parse(const Document *sourceDoc) +{ + if (!sourceDoc) + { + error("NULL source document"); + return NULL; + } + + Document *src = (Document *)sourceDoc; + DOMImplementationImpl impl; + doc = new SVGDocumentImpl(&impl, SVG_NAMESPACE, "svg" , NULL); + + ElementImpl *destElem = dynamic_cast<ElementImpl *>(doc->getDocumentElement()); + ElementImpl *srcElem = dynamic_cast<ElementImpl *>(src->getDocumentElement()); + if (!parseElement(destElem, srcElem)) + { + delete doc; + return NULL; + } + + return doc; +} + + + + +} //namespace svg +} //namespace dom +} //namespace w3c +} //namespace org + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + |
