Exemple #1
0
svgtiny_code svgtiny_parse_svg_from_dom(struct svgtiny_diagram *diagram,
        dom_document *document, int viewport_width, int viewport_height)
{
    dom_element *svg;
    dom_exception exc;
	svgtiny_code code;

    exc = dom_document_get_document_element(document, &svg);
	if (exc != DOM_NO_ERR) {
		dom_node_unref(document);
		return svgtiny_LIBDOM_ERROR;
	}

	struct svgtiny_parse_state state;
	float x, y, width, height;

	assert(diagram);

	state.gradient_x1 = NULL;
	state.gradient_y1 = NULL;
	state.gradient_x2 = NULL;
	state.gradient_y2 = NULL;


	/* get graphic dimensions */
	memset(&state, 0, sizeof(state));
	state.diagram = diagram;
	state.document = document;
	state.viewport_width = viewport_width;
	state.viewport_height = viewport_height;

#define SVGTINY_STRING_ACTION2(s,n)					\
	if (dom_string_create_interned((const uint8_t *) #n,		\
				       strlen(#n), &state.interned_##s)	\
	    != DOM_NO_ERR) {						\
		code = svgtiny_LIBDOM_ERROR;				\
		goto cleanup;						\
	}
#include "svgtiny_strings.h"
#undef SVGTINY_STRING_ACTION2

	svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
	diagram->width = width;
	diagram->height = height;

	/* set up parsing state */
	state.viewport_width = width;
	state.viewport_height = height;
	state.ctm.a = 1; /*(float) viewport_width / (float) width;*/
	state.ctm.b = 0;
	state.ctm.c = 0;
	state.ctm.d = 1; /*(float) viewport_height / (float) height;*/
	state.ctm.e = 0; /*x;*/
	state.ctm.f = 0; /*y;*/
	/*state.style = css_base_style;
	state.style.font_size.value.length.value = option_font_size * 0.1;*/
	state.fill = 0x000000;
	state.stroke = svgtiny_TRANSPARENT;
	state.stroke_width = 1;
	state.linear_gradient_stop_count = 0;

	/* parse tree */
	code = svgtiny_parse_svg(svg, state);

	dom_node_unref(svg);

cleanup:
	svgtiny_cleanup_state_local(&state);
#define SVGTINY_STRING_ACTION2(s,n)			\
	if (state.interned_##s != NULL)			\
		dom_string_unref(state.interned_##s);
#include "svgtiny_strings.h"
#undef SVGTINY_STRING_ACTION2
	return code;
}
Exemple #2
0
svgtiny_code svgtiny_parse_svg(dom_element *svg,
		struct svgtiny_parse_state state)
{
	float x, y, width, height;
	dom_string *view_box;
	dom_element *child;
	dom_exception exc;

	svgtiny_setup_state_local(&state);

	svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
	svgtiny_parse_paint_attributes(svg, &state);
	svgtiny_parse_font_attributes(svg, &state);

	exc = dom_element_get_attribute(svg, state.interned_viewBox,
					&view_box);
	if (exc != DOM_NO_ERR) {
		svgtiny_cleanup_state_local(&state);
		return svgtiny_LIBDOM_ERROR;
	}

	if (view_box) {
		char *s = strndup(dom_string_data(view_box),
				  dom_string_byte_length(view_box));
		float min_x, min_y, vwidth, vheight;
		if (sscanf(s, "%f,%f,%f,%f",
				&min_x, &min_y, &vwidth, &vheight) == 4 ||
				sscanf(s, "%f %f %f %f",
				&min_x, &min_y, &vwidth, &vheight) == 4) {
			state.ctm.a = (float) state.viewport_width / vwidth;
			state.ctm.d = (float) state.viewport_height / vheight;
			state.ctm.e += -min_x * state.ctm.a;
			state.ctm.f += -min_y * state.ctm.d;
		}
		free(s);
		dom_string_unref(view_box);
	}

	svgtiny_parse_transform_attributes(svg, &state);

	exc = dom_node_get_first_child(svg, (dom_node **) (void *) &child);
	if (exc != DOM_NO_ERR) {
		svgtiny_cleanup_state_local(&state);
		return svgtiny_LIBDOM_ERROR;
	}
	while (child != NULL) {
		dom_element *next;
		dom_node_type nodetype;
		svgtiny_code code = svgtiny_OK;

		exc = dom_node_get_node_type(child, &nodetype);
		if (exc != DOM_NO_ERR) {
			dom_node_unref(child);
			return svgtiny_LIBDOM_ERROR;
		}
		if (nodetype == DOM_ELEMENT_NODE) {
			dom_string *nodename;
			exc = dom_node_get_node_name(child, &nodename);
			if (exc != DOM_NO_ERR) {
				dom_node_unref(child);
				svgtiny_cleanup_state_local(&state);
				return svgtiny_LIBDOM_ERROR;
			}
			if (dom_string_caseless_isequal(state.interned_svg,
							nodename))
				code = svgtiny_parse_svg(child, state);
			else if (dom_string_caseless_isequal(state.interned_g,
							     nodename))
				code = svgtiny_parse_svg(child, state);
			else if (dom_string_caseless_isequal(state.interned_a,
							     nodename))
				code = svgtiny_parse_svg(child, state);
			else if (dom_string_caseless_isequal(state.interned_path,
							     nodename))
				code = svgtiny_parse_path(child, state);
			else if (dom_string_caseless_isequal(state.interned_rect,
							     nodename))
				code = svgtiny_parse_rect(child, state);
			else if (dom_string_caseless_isequal(state.interned_circle,
							     nodename))
				code = svgtiny_parse_circle(child, state);
			else if (dom_string_caseless_isequal(state.interned_ellipse,
							     nodename))
				code = svgtiny_parse_ellipse(child, state);
			else if (dom_string_caseless_isequal(state.interned_line,
							     nodename))
				code = svgtiny_parse_line(child, state);
			else if (dom_string_caseless_isequal(state.interned_polyline,
							     nodename))
				code = svgtiny_parse_poly(child, state, false);
			else if (dom_string_caseless_isequal(state.interned_polygon,
							     nodename))
				code = svgtiny_parse_poly(child, state, true);
			else if (dom_string_caseless_isequal(state.interned_text,
							     nodename))
				code = svgtiny_parse_text(child, state);
			dom_string_unref(nodename);
		}
		if (code != svgtiny_OK) {
			dom_node_unref(child);
			svgtiny_cleanup_state_local(&state);
			return code;
		}
		exc = dom_node_get_next_sibling(child,
						(dom_node **) (void *) &next);
		dom_node_unref(child);
		if (exc != DOM_NO_ERR) {
			svgtiny_cleanup_state_local(&state);
			return svgtiny_LIBDOM_ERROR;
		}
		child = next;
	}

	svgtiny_cleanup_state_local(&state);
	return svgtiny_OK;
}
Exemple #3
0
svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
		const char *buffer, size_t size, const char *url,
		int viewport_width, int viewport_height)
{
	xmlDoc *document;
	xmlNode *svg;
	struct svgtiny_parse_state state;
	float x, y, width, height;
	svgtiny_code code;

	assert(diagram);
	assert(buffer);
	assert(url);

	/* parse XML to tree */
	document = xmlReadMemory(buffer, size, url, 0,
			XML_PARSE_NONET | XML_PARSE_COMPACT);
	if (!document)
		return svgtiny_LIBXML_ERROR;

	/*xmlDebugDumpDocument(stderr, document);*/

	/* find root <svg> element */
	svg = xmlDocGetRootElement(document);
	if (!svg)
		return svgtiny_NOT_SVG;
	if (strcmp((const char *) svg->name, "svg") != 0)
		return svgtiny_NOT_SVG;

	/* get graphic dimensions */
	state.diagram = diagram;
	state.document = document;
	state.viewport_width = viewport_width;
	state.viewport_height = viewport_height;
	svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
	diagram->width = width;
	diagram->height = height;

	/* set up parsing state */
	state.viewport_width = width;
	state.viewport_height = height;
	state.ctm.a = 1; /*(float) viewport_width / (float) width;*/
	state.ctm.b = 0;
	state.ctm.c = 0;
	state.ctm.d = 1; /*(float) viewport_height / (float) height;*/
	state.ctm.e = 0; /*x;*/
	state.ctm.f = 0; /*y;*/
	/*state.style = css_base_style;
	state.style.font_size.value.length.value = option_font_size * 0.1;*/
	state.fill = 0x000000;
	state.stroke = svgtiny_TRANSPARENT;
	state.stroke_width = 1;
	state.linear_gradient_stop_count = 0;

	/* parse tree */
	code = svgtiny_parse_svg(svg, state);

	/* free XML tree */
	xmlFreeDoc(document);

	return code;
}
Exemple #4
0
svgtiny_code svgtiny_parse_svg(xmlNode *svg,
		struct svgtiny_parse_state state)
{
	float x, y, width, height;
	xmlAttr *view_box;
	xmlNode *child;

	svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
	svgtiny_parse_paint_attributes(svg, &state);
	svgtiny_parse_font_attributes(svg, &state);

	/* parse viewBox */
	view_box = xmlHasProp(svg, (const xmlChar *) "viewBox");
	if (view_box) {
		const char *s = (const char *) view_box->children->content;
		float min_x, min_y, vwidth, vheight;
		if (sscanf(s, "%f,%f,%f,%f",
				&min_x, &min_y, &vwidth, &vheight) == 4 ||
				sscanf(s, "%f %f %f %f",
				&min_x, &min_y, &vwidth, &vheight) == 4) {
			state.ctm.a = (float) state.viewport_width / vwidth;
			state.ctm.d = (float) state.viewport_height / vheight;
			state.ctm.e += -min_x * state.ctm.a;
			state.ctm.f += -min_y * state.ctm.d;
		}
	}

	svgtiny_parse_transform_attributes(svg, &state);

	for (child = svg->children; child; child = child->next) {
		svgtiny_code code = svgtiny_OK;

		if (child->type == XML_ELEMENT_NODE) {
			const char *name = (const char *) child->name;
			if (strcmp(name, "svg") == 0)
				code = svgtiny_parse_svg(child, state);
			else if (strcmp(name, "g") == 0)
				code = svgtiny_parse_svg(child, state);
			else if (strcmp(name, "a") == 0)
				code = svgtiny_parse_svg(child, state);
			else if (strcmp(name, "path") == 0)
				code = svgtiny_parse_path(child, state);
			else if (strcmp(name, "rect") == 0)
				code = svgtiny_parse_rect(child, state);
			else if (strcmp(name, "circle") == 0)
				code = svgtiny_parse_circle(child, state);
			else if (strcmp(name, "ellipse") == 0)
				code = svgtiny_parse_ellipse(child, state);
			else if (strcmp(name, "line") == 0)
				code = svgtiny_parse_line(child, state);
			else if (strcmp(name, "polyline") == 0)
				code = svgtiny_parse_poly(child, state, false);
			else if (strcmp(name, "polygon") == 0)
				code = svgtiny_parse_poly(child, state, true);
			else if (strcmp(name, "text") == 0)
				code = svgtiny_parse_text(child, state);
		}

		if (code != svgtiny_OK)
			return code;
	}

	return svgtiny_OK;
}
svgtiny_code svgtiny_parse(svgInfo &info,struct svgtiny_diagram *diagram, const char *buffer,int viewport_width, int viewport_height){
	
    Poco::XML::Document *document;
    Poco::XML::Element *svg;
	struct svgtiny_parse_state state;
	float x, y, width, height;
	svgtiny_code code;

    
    
    
	assert(diagram);
	assert(buffer);

    
    string rawXMLstring(buffer);
    
    //info.rawXML = rawXMLstring;
    
    //parser is statically allocated
    Poco::XML::DOMParser parser;
    
    /*
     Why does this upset the internal SVG parser?
     */
    //parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, false);
    
    document = parser.parseString(rawXMLstring);
    

    svg = document->documentElement();
    
    
    
    //std::cout << svg->localName() << std::endl;
    
	if (!svg){
		code = svgtiny_NOT_SVG;
        return code;
    }else if(svg->localName().compare("svg") != 0){
		code= svgtiny_NOT_SVG;
        return code;
    }else{
    
    
	/* get graphic dimensions */
	state.diagram = diagram;
	state.document = document;
	state.viewport_width = viewport_width;
	state.viewport_height = viewport_height;
	svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
	diagram->width = width;
	diagram->height = height;

	/* set up parsing state */
	state.viewport_width = width;
	state.viewport_height = height;
	state.ctm.a = 1; /*(float) viewport_width / (float) width;*/
	state.ctm.b = 0;
	state.ctm.c = 0;
	state.ctm.d = 1; /*(float) viewport_height / (float) height;*/
	state.ctm.e = 0; /*x;*/
	state.ctm.f = 0; /*y;*/
	/*state.style = css_base_style;
	state.style.font_size.value.length.value = option_font_size * 0.1;*/
	state.fill = 0x000000;
	state.stroke = svgtiny_TRANSPARENT;
	state.stroke_width = 1;
	state.linear_gradient_stop_count = 0;
        
        
        //borg
    state.info = &info;

	/* parse tree */

    ofPtr<svgNode> rootnode(new svgNode());
    rootnode->type = SVG_TAG_TYPE_DOCUMENT;
    info.rootnode = rootnode;
        
        
        
        //store root svg info
        
        info.width = ofToString(svg->getAttribute("width").c_str());
        info.height = ofToString(svg->getAttribute("height").c_str());
        info.x = ofToString(svg->getAttribute("x").c_str());
        info.y = ofToString(svg->getAttribute("y").c_str());
        info.viewbox = ofToString(svg->getAttribute("viewBox").c_str());
        info.id = ofToString(svg->getAttribute("id").c_str());
        info.xmlns = ofToString(svg->getAttribute("xmlns").c_str());
        info.version = ofToString(svg->getAttribute("version").c_str());
        info.preserveAspectRatio = ofToString(svg->getAttribute("preserveAspectRatio").c_str());
               
        
	code = svgtiny_parse_svg(info,svg, state,rootnode);

    }
    
        
    
	/* free XML tree */
	//xmlFreeDoc(document);
    
    
    //return without namespace
    //parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, false);
    //document = parser.parseString(rawXMLstring);

	return code;
}
/*
 Traverse the whole DOM and process each node in three ways
 1. Save XML doc as a nested object tree to be saved out
 2. Parse graphics into ofPaths
 3. Save reference to current processed path node in a lookup flatlist
 
 
 info belongs to ofxSVG
 node is the current object in the tree being parsed, preserving nesting
 cnl is the childlist from original DOM
 state is tinysvg control parse value
 */
svgtiny_code processChildren(svgInfo &info, ofPtr<svgNode> node, Poco::XML::ChildNodesList *cnl,
                             struct svgtiny_parse_state state){

    
        
    svgtiny_code code = svgtiny_OK;
    int i  = 0, l = cnl->length();
    
    while( i < l ){

    Element *child = (Poco::XML::Element *) cnl->item(i);
        ofPtr<svgNode> childnode(new svgNode());
        
    
    // I think this can't happen?
    if (child->nodeType() == Poco::XML::Element::ELEMENT_NODE) {
        const char *name = (const char *) child->localName().c_str();
        
        if (strcmp(name, "svg") == 0){
            
            ofLog()<<"processing a nested svg (not root)"<<endl;
            
            
                        
            //this svg is within root svg
 
            svgDef svgDef;
            svgDef.width = ofToString(child->getAttribute("width").c_str());
            svgDef.height = ofToString(child->getAttribute("height").c_str());
            svgDef.x = ofToString(child->getAttribute("x").c_str());
            svgDef.y = ofToString(child->getAttribute("y").c_str());
            svgDef.viewbox = ofToString(child->getAttribute("viewbox").c_str());
            
            
                
            
            
            
            /*recursive call for any nodes inside svg
            Poco::XML::ChildNodesList *cnl = ( Poco::XML::ChildNodesList *) child->childNodes();
            int ii  = 0, ll = cnl->length();
            while( ii < ll ) {
                 ofLog()<<"recursive svg call"<<ii<<endl;
                Poco::XML::Element * grandchild = (Poco::XML::Element *) cnl->item(ii);
                svgNode childnode;
                svgtiny_code code = processChildren(info,childnode,grandchild,state);
                svgDef.nodes.push_back(childnode);
                if (code != svgtiny_OK)
                    return code;
                 ii++;
                
            }*/
            
            childnode->svg = svgDef;
            childnode->type =SVG_TAG_TYPE_SVG;
            node->children.push_back(childnode);
            
            //recursive call...it seeems svg and groups are treated the same
            code = svgtiny_parse_svg(info,child, state,childnode);
        }else if (strcmp(name, "g") == 0){
           /// ofLog()<<"processing group"<<endl;
            
            svgGroupDef gDef;
            gDef.transform = ofToString(child->getAttribute("transform").c_str());
            
            gDef.fill = ofToString(child->getAttribute("fill").c_str());
            gDef.stroke = ofToString(child->getAttribute("stroke").c_str());
            gDef.stroke_width = ofToString(child->getAttribute("stroke-width").c_str());
            gDef.stroke_miterlimit = ofToString(child->getAttribute("stroke-miterlimit").c_str());
            gDef.fill_opacity = ofToString(child->getAttribute("fill-opacity").c_str());
            gDef.stroke_opacity = ofToString(child->getAttribute("stroke-opacity").c_str());
            
            
            
            
            //recursive call for any nodes inside svg
            /*Poco::XML::ChildNodesList *cnl = ( Poco::XML::ChildNodesList *) child->childNodes();
            int ii  = 0, ll = cnl->length();
            while( ii < ll ) {
                 ofLog()<<"recursive grop call"<<ii<<endl;
                Poco::XML::Element * grandchild = (Poco::XML::Element *) cnl->item(ii);
                svgNode childnode;
                svgtiny_code code = processChildren(info,childnode,grandchild,state);
                gDef.nodes.push_back(childnode);
                if (code != svgtiny_OK)
                    return code;
                ii++;
                
            }
            */
            childnode->group = gDef;  
            childnode->type =SVG_TAG_TYPE_GROUP;
            node->children.push_back(childnode);
            
            
            //recursive call...it seeems svg and groups are treated the same
            code = svgtiny_parse_svg(info,child, state,childnode);
        }else if (strcmp(name, "a") == 0){
            //a node?! who cares

            code = svgtiny_parse_svg(info,child, state,childnode);
        }else if (strcmp(name, "path") == 0){
            //ofLog()<<"--------  "<<child->getAttribute("d").c_str()<<endl;
            //<path fill="#D14E8F" stroke="#0C0404" stroke-miterlimit="10" d="M2802.998,4477.002c-2.002-30-2.002-31.001,7.998-8.003c5,13.999,6.006,28.003,1.006,33.003                C2807.998,4506.001,2802.998,4495,2802.998,4477.002z"/>
            //info.paths.push_back(child->getAttribute("d").c_str());
            //info.paths.push_back("<"+ofToString(child->nodeName().c_str())+"/>");
            //"<path fill='#000000' stroke='#000000' stroke-width='1' stroke-miterlimit='10' d='M590 35 c0 -19 5 -35 11 -35 7 0 9 10 4 28 -4 17 -3 24 3 17 6 -5 12 -19 15 -30 4 -16 5 -16 6 5 1 15 -7 30 -19 38 -19 11 -20 10 -20 -23z' />"

            svgPathDef pDef;
            pDef.d = ofToString(child->getAttribute("d").c_str());
            pDef.fill = ofToString(child->getAttribute("fill").c_str());
            pDef.stroke = ofToString(child->getAttribute("stroke").c_str());
            pDef.stroke_width = ofToString(child->getAttribute("stroke-width").c_str());
            pDef.stroke_miterlimit = ofToString(child->getAttribute("stroke-miterlimit").c_str());
            //info.paths.push_back(pDef);
            pDef.fill_opacity = ofToString(child->getAttribute("fill-opacity").c_str());
            pDef.stroke_opacity = ofToString(child->getAttribute("stroke-opacity").c_str());
            
            childnode->path = pDef;         
            childnode->type = SVG_TAG_TYPE_PATH;
            node->children.push_back(childnode);
            
           // ofLog()<<"defining currNode"<<endl;
            state.currNode = childnode;
            
            /*
             if(info.svgs.size()>0){
             //this path is within a nested svg
             info.svgs[info.svgs.size()-1]->groups[info.svgs[info.svgs.size()-1]->groups.size()-1]->paths.push_back(pDef);
             }else{
             //this path belongs within group in root svg
             info.groups[info.groups.size()-1]->paths.push_back(pDef);                  
             }
             */
            
            
            code = svgtiny_parse_path(child, state);
            
            
        } else if (strcmp(name, "rect") == 0){
            
            
            svgRectDef pDef;
 
            pDef.fill = ofToString(child->getAttribute("fill").c_str());
            pDef.stroke = ofToString(child->getAttribute("stroke").c_str());
            pDef.stroke_width = ofToString(child->getAttribute("stroke-width").c_str());
            pDef.stroke_miterlimit = ofToString(child->getAttribute("stroke-miterlimit").c_str());
            //info.paths.push_back(pDef);
            pDef.fill_opacity = ofToString(child->getAttribute("fill-opacity").c_str());
            pDef.stroke_opacity = ofToString(child->getAttribute("stroke-opacity").c_str());
            
            pDef.x = ofToString(child->getAttribute("x").c_str());
            pDef.y = ofToString(child->getAttribute("y").c_str());
            pDef.width = ofToString(child->getAttribute("width").c_str());
            pDef.height = ofToString(child->getAttribute("height").c_str());
            
            
            childnode->rect = pDef;         
            childnode->type = SVG_TAG_TYPE_RECT;
            node->children.push_back(childnode);
            
           // ofLog()<<"defining currNode"<<endl;
            state.currNode = childnode;

            
            
            
            code = svgtiny_parse_rect(child, state);
        }else if (strcmp(name, "circle") == 0){
            code = svgtiny_parse_circle(child, state);
        }else if (strcmp(name, "ellipse") == 0){
            code = svgtiny_parse_ellipse(child, state);
        }else if (strcmp(name, "line") == 0){
            code = svgtiny_parse_line(child, state);
        } else if (strcmp(name, "polyline") == 0){
            code = svgtiny_parse_poly(child, state, false);
        }else if (strcmp(name, "polygon") == 0){
            code = svgtiny_parse_poly(child, state, true);
        } else if (strcmp(name, "text") == 0){
            code = svgtiny_parse_text(child, state);
        }
        // not sure about this
    } else if (child->nodeType() == Poco::XML::Element::TEXT_NODE) {
        
        const char *name = (const char *) child->localName().c_str();
        
        if (strcmp(name, "text") == 0)
            code = svgtiny_parse_text(child, state);
    }
    
    
    
    
   
    

    
    //pNode = it.nextNode();
   
    
        if (code != svgtiny_OK){
            return code;
        }

        
        i++;
        
	}

    return code;
};
Exemple #7
0
svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
		const char *buffer, size_t size, const char *url,
		int viewport_width, int viewport_height)
{
	dom_document *document;
	dom_exception exc;
	dom_xml_parser *parser;
	dom_xml_error err;
	dom_element *svg;
	dom_string *svg_name;
	lwc_string *svg_name_lwc;
	struct svgtiny_parse_state state;
	float x, y, width, height;
	svgtiny_code code;

	assert(diagram);
	assert(buffer);
	assert(url);

	UNUSED(url);

	state.gradient_x1 = NULL;
	state.gradient_y1 = NULL;
	state.gradient_x2 = NULL;
	state.gradient_y2 = NULL;

	parser = dom_xml_parser_create(NULL, NULL,
				       ignore_msg, NULL, &document);

	if (parser == NULL)
		return svgtiny_LIBDOM_ERROR;

	err = dom_xml_parser_parse_chunk(parser, (uint8_t *)buffer, size);
	if (err != DOM_XML_OK) {
		dom_node_unref(document);
		dom_xml_parser_destroy(parser);
		return svgtiny_LIBDOM_ERROR;
	}

	err = dom_xml_parser_completed(parser);
	if (err != DOM_XML_OK) {
		dom_node_unref(document);
		dom_xml_parser_destroy(parser);
		return svgtiny_LIBDOM_ERROR;
	}

	/* We're done parsing, drop the parser.
	 * We now own the document entirely.
	 */
	dom_xml_parser_destroy(parser);

	/* find root <svg> element */
	exc = dom_document_get_document_element(document, &svg);
	if (exc != DOM_NO_ERR) {
		dom_node_unref(document);
		return svgtiny_LIBDOM_ERROR;
	}
	exc = dom_node_get_node_name(svg, &svg_name);
	if (exc != DOM_NO_ERR) {
		dom_node_unref(svg);
		dom_node_unref(document);
		return svgtiny_LIBDOM_ERROR;
	}
	if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
			      &svg_name_lwc) != lwc_error_ok) {
		dom_string_unref(svg_name);
		dom_node_unref(svg);
		dom_node_unref(document);
		return svgtiny_LIBDOM_ERROR;
	}
	if (!dom_string_caseless_lwc_isequal(svg_name, svg_name_lwc)) {
		lwc_string_unref(svg_name_lwc);
		dom_string_unref(svg_name);
		dom_node_unref(svg);
		dom_node_unref(document);
		return svgtiny_NOT_SVG;
	}

	lwc_string_unref(svg_name_lwc);
	dom_string_unref(svg_name);

	/* get graphic dimensions */
	memset(&state, 0, sizeof(state));
	state.diagram = diagram;
	state.document = document;
	state.viewport_width = viewport_width;
	state.viewport_height = viewport_height;

#define SVGTINY_STRING_ACTION2(s,n)					\
	if (dom_string_create_interned((const uint8_t *) #n,		\
				       strlen(#n), &state.interned_##s)	\
	    != DOM_NO_ERR) {						\
		code = svgtiny_LIBDOM_ERROR;				\
		goto cleanup;						\
	}
#include "svgtiny_strings.h"
#undef SVGTINY_STRING_ACTION2

	svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
	diagram->width = width;
	diagram->height = height;

	/* set up parsing state */
	state.viewport_width = width;
	state.viewport_height = height;
	state.ctm.a = 1; /*(float) viewport_width / (float) width;*/
	state.ctm.b = 0;
	state.ctm.c = 0;
	state.ctm.d = 1; /*(float) viewport_height / (float) height;*/
	state.ctm.e = 0; /*x;*/
	state.ctm.f = 0; /*y;*/
	/*state.style = css_base_style;
	state.style.font_size.value.length.value = option_font_size * 0.1;*/
	state.fill = 0x000000;
	state.stroke = svgtiny_TRANSPARENT;
	state.stroke_width = 1;
	state.linear_gradient_stop_count = 0;

	/* parse tree */
	code = svgtiny_parse_svg(svg, state);

	dom_node_unref(svg);
	dom_node_unref(document);

cleanup:
	svgtiny_cleanup_state_local(&state);
#define SVGTINY_STRING_ACTION2(s,n)			\
	if (state.interned_##s != NULL)			\
		dom_string_unref(state.interned_##s);
#include "svgtiny_strings.h"
#undef SVGTINY_STRING_ACTION2
	return code;
}