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