Esempio n. 1
0
/**
 * Callback to determine if a node is a linking element.
 *
 * \param pw     HTML document
 * \param n      DOM node
 * \param match  Pointer to location to receive result
 * \return CSS_OK.
 *
 * \post \a match will contain true if the node matches and false otherwise.
 */
css_error node_is_link(void *pw, void *n, bool *match)
{
	dom_node *node = n;
	dom_exception exc;
	dom_string *node_name = NULL;

	exc = dom_node_get_node_name(node, &node_name);
	if ((exc != DOM_NO_ERR) || (node_name == NULL)) {
		return CSS_NOMEM;
	}

	if (dom_string_caseless_lwc_isequal(node_name, corestring_lwc_a)) {
		bool has_href;
		exc = dom_element_has_attribute(node, corestring_dom_href,
				&has_href);
		if ((exc == DOM_NO_ERR) && (has_href)) {
			*match = true;
		} else {
			*match = false;
		}
	} else {
		*match = false;
	}
	dom_string_unref(node_name);

	return CSS_OK;
}
Esempio n. 2
0
static int
node_count_siblings_check(dom_node *node,
			  bool check_name,
			  dom_string *name)
{
	dom_node_type type;
	int ret = 0;
	dom_exception exc;

	if (node == NULL)
		return 0;

	exc = dom_node_get_node_type(node, &type);
	if ((exc != DOM_NO_ERR) || (type != DOM_ELEMENT_NODE)) {
		return 0;
	}

	if (check_name) {
		dom_string *node_name = NULL;
		exc = dom_node_get_node_name(node, &node_name);

		if ((exc == DOM_NO_ERR) && (node_name != NULL)) {

			if (dom_string_caseless_isequal(name,
							node_name)) {
				ret = 1;
			}
			dom_string_unref(node_name);
		}
	} else {
		ret = 1;
	}

	return ret;
}
Esempio n. 3
0
/**
 * Callback to determine if a node is a linking element whose target has been
 * visited.
 *
 * \param pw     HTML document
 * \param node   DOM node
 * \param match  Pointer to location to receive result
 * \return CSS_OK.
 *
 * \post \a match will contain true if the node matches and false otherwise.
 */
css_error node_is_visited(void *pw, void *node, bool *match)
{
	nscss_select_ctx *ctx = pw;
	nsurl *url;
	nserror error;
	const struct url_data *data;

	dom_exception exc;
	dom_node *n = node;
	dom_string *s = NULL;

	*match = false;

	exc = dom_node_get_node_name(n, &s);
	if ((exc != DOM_NO_ERR) || (s == NULL)) {
		return CSS_NOMEM;
	}

	if (!dom_string_caseless_lwc_isequal(s, corestring_lwc_a)) {
		/* Can't be visited; not ancher element */
		dom_string_unref(s);
		return CSS_OK;
	}

	/* Finished with node name string */
	dom_string_unref(s);
	s = NULL;

	exc = dom_element_get_attribute(n, corestring_dom_href, &s);
	if ((exc != DOM_NO_ERR) || (s == NULL)) {
		/* Can't be visited; not got a URL */
		return CSS_OK;
	}

	/* Make href absolute */
	/* TODO: this duplicates what we do for box->href
	 *       should we put the absolute URL on the dom node? */
	error = nsurl_join(ctx->base_url, dom_string_data(s), &url);

	/* Finished with href string */
	dom_string_unref(s);

	if (error != NSERROR_OK) {
		/* Couldn't make nsurl object */
		return CSS_NOMEM;
	}

	data = urldb_get_url_data(url);

	/* Visited if in the db and has
	 * non-zero visit count */
	if (data != NULL && data->visits > 0)
		*match = true;

	nsurl_unref(url);

	return CSS_OK;
}
Esempio n. 4
0
/**
 * Callback to find a named sibling node.
 *
 * \param pw       HTML document
 * \param node     DOM node
 * \param qname    Node name to search for
 * \param sibling  Pointer to location to receive sibling
 * \return CSS_OK.
 *
 * \post \a sibling will contain the result, or NULL if there is no match
 */
css_error named_sibling_node(void *pw, void *node,
		const css_qname *qname, void **sibling)
{
	dom_node *n = node;
	dom_node *prev;
	dom_exception err;

	*sibling = NULL;

	/* Find sibling element */
	err = dom_node_get_previous_sibling(n, &n);
	if (err != DOM_NO_ERR)
		return CSS_OK;

	while (n != NULL) {
		dom_node_type type;

		err = dom_node_get_node_type(n, &type);
		if (err != DOM_NO_ERR) {
			dom_node_unref(n);
			return CSS_OK;
		}

		if (type == DOM_ELEMENT_NODE)
			break;

		err = dom_node_get_previous_sibling(n, &prev);
		if (err != DOM_NO_ERR) {
			dom_node_unref(n);
			return CSS_OK;
		}

		dom_node_unref(n);
		n = prev;
	}

	if (n != NULL) {
		dom_string *name;

		err = dom_node_get_node_name(n, &name);
		if (err != DOM_NO_ERR) {
			dom_node_unref(n);
			return CSS_OK;
		}

		dom_node_unref(n);

		if (dom_string_caseless_lwc_isequal(name, qname->name)) {
			*sibling = n;
		}

		dom_string_unref(name);
	}

	return CSS_OK;
}
Esempio n. 5
0
/**
 * Callback to count a node's siblings.
 *
 * \param pw         HTML document
 * \param n          DOM node
 * \param same_name  Only count siblings with the same name, or all
 * \param after      Count anteceding instead of preceding siblings
 * \param count      Pointer to location to receive result
 * \return CSS_OK.
 *
 * \post \a count will contain the number of siblings
 */
css_error node_count_siblings(void *pw, void *n, bool same_name,
		bool after, int32_t *count)
{
	int32_t cnt = 0;
	dom_exception exc;
	dom_string *node_name = NULL;

	if (same_name) {
		dom_node *node = n;
		exc = dom_node_get_node_name(node, &node_name);
		if ((exc != DOM_NO_ERR) || (node_name == NULL)) {
			return CSS_NOMEM;
		}
	}

	if (after) {
		dom_node *node = dom_node_ref(n);
		dom_node *next;

		do {
			exc = dom_node_get_next_sibling(node, &next);
			if ((exc != DOM_NO_ERR))
				break;

			dom_node_unref(node);
			node = next;

			cnt += node_count_siblings_check(node, same_name, node_name);
		} while (node != NULL);
	} else {
		dom_node *node = dom_node_ref(n);
		dom_node *next;

		do {
			exc = dom_node_get_previous_sibling(node, &next);
			if ((exc != DOM_NO_ERR))
				break;

			dom_node_unref(node);
			node = next;

			cnt += node_count_siblings_check(node, same_name, node_name);

		} while (node != NULL);
	}

	if (node_name != NULL) {
		dom_string_unref(node_name);
	}

	*count = cnt;
	return CSS_OK;
}
Esempio n. 6
0
/**
 * Callback to retrieve a node's name.
 *
 * \param pw     HTML document
 * \param node   DOM node
 * \param qname  Pointer to location to receive node name
 * \return CSS_OK on success,
 *         CSS_NOMEM on memory exhaustion.
 */
css_error node_name(void *pw, void *node, css_qname *qname)
{
	dom_node *n = node;
	dom_string *name;
	dom_exception err;

	err = dom_node_get_node_name(n, &name);
	if (err != DOM_NO_ERR)
		return CSS_NOMEM;

	qname->ns = NULL;

	err = dom_string_intern(name, &qname->name);
	if (err != DOM_NO_ERR) {
		dom_string_unref(name);
		return CSS_NOMEM;
	}

	dom_string_unref(name);

	return CSS_OK;
}
Esempio n. 7
0
/**
 * Callback to determine if a node has the given name.
 *
 * \param pw     HTML document
 * \param node   DOM node
 * \param qname  Name to match
 * \param match  Pointer to location to receive result
 * \return CSS_OK.
 *
 * \post \a match will contain true if the node matches and false otherwise.
 */
css_error node_has_name(void *pw, void *node,
		const css_qname *qname, bool *match)
{
	nscss_select_ctx *ctx = pw;
	dom_node *n = node;

	if (lwc_string_isequal(qname->name, ctx->universal, match) ==
			lwc_error_ok && *match == false) {
		dom_string *name;
		dom_exception err;

		err = dom_node_get_node_name(n, &name);
		if (err != DOM_NO_ERR)
			return CSS_OK;

		/* Element names are case insensitive in HTML */
		*match = dom_string_caseless_lwc_isequal(name, qname->name);

		dom_string_unref(name);
	}

	return CSS_OK;
}
Esempio n. 8
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;
}
Esempio n. 9
0
svgtiny_code svgtiny_parse_dom(const char *buffer, size_t size, const char *url, dom_document **output_dom)
{
	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;

    assert(buffer);
	assert(url);

	UNUSED(url);

    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;
	}

	dom_node_unref(svg);
	lwc_string_unref(svg_name_lwc);
	dom_string_unref(svg_name);

    *output_dom = document;
    return svgtiny_OK;
}
Esempio n. 10
0
svgtiny_code svgtiny_parse_text(dom_element *text,
		struct svgtiny_parse_state state)
{
	float x, y, width, height;
	float px, py;
	dom_node *child;
	dom_exception exc;

	svgtiny_setup_state_local(&state);

	svgtiny_parse_position_attributes(text, state,
			&x, &y, &width, &height);
	svgtiny_parse_font_attributes(text, &state);
	svgtiny_parse_transform_attributes(text, &state);

	px = state.ctm.a * x + state.ctm.c * y + state.ctm.e;
	py = state.ctm.b * x + state.ctm.d * y + state.ctm.f;
/* 	state.ctm.e = px - state.origin_x; */
/* 	state.ctm.f = py - state.origin_y; */

	/*struct css_style style = state.style;
	style.font_size.value.length.value *= state.ctm.a;*/
	
        exc = dom_node_get_first_child(text, &child);
	if (exc != DOM_NO_ERR) {
		return svgtiny_LIBDOM_ERROR;
		svgtiny_cleanup_state_local(&state);
	}
	while (child != NULL) {
		dom_node *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);
			svgtiny_cleanup_state_local(&state);
			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(nodename,
							state.interned_tspan))
				code = svgtiny_parse_text((dom_element *)child,
							  state);
			dom_string_unref(nodename);
		} else if (nodetype == DOM_TEXT_NODE) {
			struct svgtiny_shape *shape = svgtiny_add_shape(&state);
			dom_string *content;
			if (shape == NULL) {
				dom_node_unref(child);
				svgtiny_cleanup_state_local(&state);
				return svgtiny_OUT_OF_MEMORY;
			}
			exc = dom_text_get_whole_text(child, &content);
			if (exc != DOM_NO_ERR) {
				dom_node_unref(child);
				svgtiny_cleanup_state_local(&state);
				return svgtiny_LIBDOM_ERROR;
			}
			if (content != NULL) {
				shape->text = strndup(dom_string_data(content),
						      dom_string_byte_length(content));
				dom_string_unref(content);
			} else {
				shape->text = strdup("");
			}
			shape->text_x = px;
			shape->text_y = py;
			state.diagram->shape_count++;
		}

		if (code != svgtiny_OK) {
			dom_node_unref(child);
			svgtiny_cleanup_state_local(&state);
			return code;
		}
		exc = dom_node_get_next_sibling(child, &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;
}
Esempio n. 11
0
static bool save_complete_handle_element(save_complete_ctx *ctx,
		dom_node *node, save_complete_event_type event_type)
{
	dom_string *name;
	dom_namednodemap *attrs;
	const char *name_data;
	size_t name_len;
	bool process = true;
	dom_exception error;

	ctx->iter_state = STATE_NORMAL;

	error = dom_node_get_node_name(node, &name);
	if (error != DOM_NO_ERR)
		return false;

	if (name == NULL)
		return true;

	name_data = dom_string_data(name);
	name_len = dom_string_byte_length(name);

	if (name_len == SLEN("base") &&
			strncasecmp(name_data, "base", name_len) == 0) {
		/* Elide BASE elements from the output */
		process = false;
	} else if (name_len == SLEN("meta") && 
			strncasecmp(name_data, "meta", name_len) == 0) {
		/* Don't emit close tags for META elements */
		if (event_type == EVENT_LEAVE) {
			process = false;
		} else {
			/* Elide meta charsets */
			dom_string *value;
			error = dom_element_get_attribute(node,
					corestring_dom_http_equiv, &value);
			if (error != DOM_NO_ERR) {
				dom_string_unref(name);
				return false;
			}

			if (value != NULL) {
				if (dom_string_length(value) ==
					SLEN("Content-Type") &&
					strncasecmp(dom_string_data(value),
						"Content-Type",
						SLEN("Content-Type")) == 0)
					process = false;

				dom_string_unref(value);
			} else {
				bool yes;

				error = dom_element_has_attribute(node,
						corestring_dom_charset, &yes);
				if (error != DOM_NO_ERR) {
					dom_string_unref(name);
					return false;
				}

				if (yes)
					process = false;
			}
		}
	} else if (event_type == EVENT_LEAVE && 
			((name_len == SLEN("link") && 
			strncasecmp(name_data, "link", name_len) == 0))) {
		/* Don't emit close tags for void elements */
		process = false;
	}

	if (process == false) {
		dom_string_unref(name);
		return true;
	}

	fputc('<', ctx->fp);
	if (event_type == EVENT_LEAVE)
		fputc('/', ctx->fp);
	fwrite(name_data, sizeof(*name_data), name_len, ctx->fp);

	if (event_type == EVENT_ENTER) {
		error = dom_node_get_attributes(node, &attrs);
		if (error != DOM_NO_ERR) {
			dom_string_unref(name);
			return false;
		}

		if (save_complete_handle_attrs(ctx, name, attrs) == false) {
			dom_namednodemap_unref(attrs);
			dom_string_unref(name);
			return false;
		}

		dom_namednodemap_unref(attrs);
	}

	fputc('>', ctx->fp);

	/* Rewrite contents of style elements */
	if (event_type == EVENT_ENTER && name_len == SLEN("style") &&
			strncasecmp(name_data, "style", name_len) == 0) {
		dom_string *content;

		error = dom_node_get_text_content(node, &content);
		if (error != DOM_NO_ERR) {
			dom_string_unref(name);
			return false;
		}

		if (content != NULL) {
			char *rewritten;
			unsigned long len;

			/* Rewrite @import rules */
			rewritten = save_complete_rewrite_stylesheet_urls(
					ctx,
					dom_string_data(content),
					dom_string_byte_length(content),
					ctx->base,
					&len);
			if (rewritten == NULL) {
				dom_string_unref(content);
				dom_string_unref(name);
				return false;
			}

			dom_string_unref(content);

			fwrite(rewritten, sizeof(*rewritten), len, ctx->fp);

			free(rewritten);
		}

		ctx->iter_state = STATE_IN_STYLE;
	} else if (event_type == EVENT_ENTER && name_len == SLEN("head") &&
			strncasecmp(name_data, "head", name_len) == 0) {
		/* If this is a HEAD element, insert a meta charset */
		fputs("<META http-equiv=\"Content-Type\" "
				"content=\"text/html; charset=utf-8\">",
				ctx->fp);
	}

	dom_string_unref(name);

	return true;
}
Esempio n. 12
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;
}