summaryrefslogtreecommitdiff
path: root/image
diff options
context:
space:
mode:
authorJames Bursa <james@netsurf-browser.org>2007-07-15 23:22:54 +0000
committerJames Bursa <james@netsurf-browser.org>2007-07-15 23:22:54 +0000
commit9cd109060a8d2e57ba5147bdef1a4561d905da46 (patch)
tree7120aae38060bf1276e36ad3527ed13384a3224d /image
parent895c2e2896e48802067cc95e0093f95b6dbc6556 (diff)
downloadnetsurf-9cd109060a8d2e57ba5147bdef1a4561d905da46.tar.gz
netsurf-9cd109060a8d2e57ba5147bdef1a4561d905da46.tar.bz2
Add SVG support for a few basic shapes.
svn path=/trunk/netsurf/; revision=3420
Diffstat (limited to 'image')
-rw-r--r--image/svg.c414
1 files changed, 368 insertions, 46 deletions
diff --git a/image/svg.c b/image/svg.c
index 378b6d97e..65337314a 100644
--- a/image/svg.c
+++ b/image/svg.c
@@ -18,16 +18,52 @@
#include <libxml/debugXML.h>
#include "utils/config.h"
#include "content/content.h"
+#include "css/css.h"
#include "desktop/plotters.h"
+#include "desktop/options.h"
#include "image/svg.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/utils.h"
-static bool svg_redraw_svg(xmlNode *svg, int x, int y);
-static bool svg_redraw_rect(xmlNode *rect, int x, int y);
-static bool svg_redraw_text(xmlNode *text, int x, int y);
+struct svg_redraw_state {
+ /* screen origin */
+ int origin_x;
+ int origin_y;
+
+ float viewport_width;
+ float viewport_height;
+
+ /* current transformation matrix */
+ struct {
+ float a, b, c, d, e, f;
+ } ctm;
+
+ struct css_style style;
+
+ /* paint attributes */
+ colour fill;
+ colour stroke;
+ int stroke_width;
+};
+
+
+static bool svg_redraw_svg(xmlNode *svg, struct svg_redraw_state state);
+static bool svg_redraw_rect(xmlNode *rect, struct svg_redraw_state state);
+static bool svg_redraw_circle(xmlNode *circle, struct svg_redraw_state state);
+static bool svg_redraw_line(xmlNode *line, struct svg_redraw_state state);
+static bool svg_redraw_text(xmlNode *text, struct svg_redraw_state state);
+static void svg_parse_position_attributes(const xmlNode *node,
+ const struct svg_redraw_state state,
+ float *x, float *y, float *width, float *height);
+static float svg_parse_length(const xmlChar *s, int viewport_size,
+ const struct svg_redraw_state state);
+static void svg_parse_paint_attributes(const xmlNode *node,
+ struct svg_redraw_state *state);
+static void svg_parse_color(const char *s, colour *c);
+static void svg_parse_font_attributes(const xmlNode *node,
+ struct svg_redraw_state *state);
/**
@@ -86,21 +122,13 @@ bool svg_convert(struct content *c, int w, int h)
c->data.svg.svg = svg;
/* get graphic dimensions */
- xmlChar *width = xmlGetProp(svg, "width");
- if (width) {
- c->width = atoi(width);
- xmlFree(width);
- } else {
- c->width = 100;
- }
-
- xmlChar *height = xmlGetProp(svg, "height");
- if (height) {
- c->height = atoi(height);
- xmlFree(height);
- } else {
- c->height = 100;
- }
+ struct svg_redraw_state state;
+ state.viewport_width = w;
+ state.viewport_height = h;
+ float x, y, width, height;
+ svg_parse_position_attributes(svg, state, &x, &y, &width, &height);
+ c->width = width;
+ c->height = height;
/*c->title = malloc(100);
if (c->title)
@@ -123,28 +151,74 @@ bool svg_redraw(struct content *c, int x, int y,
{
assert(c->data.svg.svg);
- return svg_redraw_svg(c->data.svg.svg, x, y);
+ struct svg_redraw_state state;
+
+ state.origin_x = x;
+ state.origin_y = y;
+ state.viewport_width = width;
+ state.viewport_height = height;
+ state.ctm.a = 1;
+ state.ctm.b = 0;
+ state.ctm.c = 0;
+ state.ctm.d = 1;
+ state.ctm.e = 0;
+ state.ctm.f = 0;
+ state.style = css_base_style;
+ state.style.font_size.value.length.value = option_font_size * 0.1;
+ state.fill = 0x000000;
+ state.stroke = TRANSPARENT;
+ state.stroke_width = 1;
+
+ return svg_redraw_svg(c->data.svg.svg, state);
}
/**
- * Redraw a <svg> element node.
+ * Redraw a <svg> or <g> element node.
*/
-bool svg_redraw_svg(xmlNode *svg, int x, int y)
+bool svg_redraw_svg(xmlNode *svg, struct svg_redraw_state state)
{
+ float x, y;
+
+ svg_parse_position_attributes(svg, state,
+ &x, &y,
+ &state.viewport_width, &state.viewport_height);
+ svg_parse_paint_attributes(svg, &state);
+ svg_parse_font_attributes(svg, &state);
+
+ /* parse viewBox */
+ xmlAttr *view_box = xmlHasProp(svg, (const xmlChar *) "viewBox");
+ if (view_box) {
+ const char *s = (const char *) view_box->children->content;
+ float min_x, min_y, width, height;
+ if (sscanf(s, "%f,%f,%f,%f",
+ &min_x, &min_y, &width, &height) == 4 ||
+ sscanf(s, "%f %f %f %f",
+ &min_x, &min_y, &width, &height) == 4) {
+ state.ctm.a = state.viewport_width / width;
+ state.ctm.d = state.viewport_height / height;
+ state.ctm.e = -min_x;
+ state.ctm.f = -min_y;
+ }
+ }
+
for (xmlNode *child = svg->children; child; child = child->next) {
bool ok = true;
if (child->type == XML_ELEMENT_NODE) {
if (strcmp(child->name, "svg") == 0)
- ok = svg_redraw_svg(child, x, y);
+ ok = svg_redraw_svg(child, state);
else if (strcmp(child->name, "g") == 0)
- ok = svg_redraw_svg(child, x, y);
+ ok = svg_redraw_svg(child, state);
else if (strcmp(child->name, "rect") == 0)
- ok = svg_redraw_rect(child, x, y);
+ ok = svg_redraw_rect(child, state);
+ else if (strcmp(child->name, "circle") == 0)
+ ok = svg_redraw_circle(child, state);
+ else if (strcmp(child->name, "line") == 0)
+ ok = svg_redraw_line(child, state);
else if (strcmp(child->name, "text") == 0)
- ok = svg_redraw_text(child, x, y);
+ ok = svg_redraw_text(child, state);
}
if (!ok)
@@ -159,22 +233,116 @@ bool svg_redraw_svg(xmlNode *svg, int x, int y)
* Redraw a <rect> element node.
*/
-bool svg_redraw_rect(xmlNode *rect, int x, int y)
+bool svg_redraw_rect(xmlNode *rect, struct svg_redraw_state state)
{
- int width = 0, height = 0;
+ float x, y, width, height;
+
+ svg_parse_position_attributes(rect, state,
+ &x, &y, &width, &height);
+ svg_parse_paint_attributes(rect, &state);
+
+ int p[8] = { x, y,
+ x + width, y,
+ x + width, y + height,
+ x, y + height };
+ for (unsigned int i = 0; i != 8; i += 2) {
+ p[i] = state.origin_x + state.ctm.a * p[i] +
+ state.ctm.c * p[i+1] + state.ctm.e;
+ p[i+1] = state.origin_y + state.ctm.b * p[i] +
+ state.ctm.d * p[i+1] + state.ctm.f;
+ }
- for (xmlAttr *attr = rect->properties; attr; attr = attr->next) {
- if (strcmp(attr->name, "x") == 0)
- x += atoi(attr->children->content);
- else if (strcmp(attr->name, "y") == 0)
- y += atoi(attr->children->content);
- else if (strcmp(attr->name, "width") == 0)
- width = atoi(attr->children->content);
- else if (strcmp(attr->name, "height") == 0)
- height = atoi(attr->children->content);
+ if (state.fill != TRANSPARENT)
+ if (!plot.polygon(p, 4, state.fill))
+ return false;
+
+ if (state.stroke != TRANSPARENT) {
+ for (unsigned int i = 0; i != 8; i += 2) {
+ if (!plot.line(p[i], p[i+1], p[(i+2)%8], p[(i+3)%8],
+ state.stroke_width, state.stroke,
+ false, false))
+ return false;
+ }
}
- return plot.rectangle(x, y, width, height, 5, 0x000000, false, false);
+ return true;
+}
+
+
+/**
+ * Redraw a <circle> element node.
+ */
+
+bool svg_redraw_circle(xmlNode *circle, struct svg_redraw_state state)
+{
+ float x = 0, y = 0, r = 0;
+
+ for (xmlAttr *attr = circle->properties; attr; attr = attr->next) {
+ if (strcmp(attr->name, "cx") == 0)
+ x = svg_parse_length(attr->children->content,
+ state.viewport_width, state);
+ else if (strcmp(attr->name, "cy") == 0)
+ y = svg_parse_length(attr->children->content,
+ state.viewport_height, state);
+ else if (strcmp(attr->name, "r") == 0)
+ r = svg_parse_length(attr->children->content,
+ state.viewport_width, state);
+ }
+ svg_parse_paint_attributes(circle, &state);
+
+ int px = state.origin_x + state.ctm.a * x +
+ state.ctm.c * y + state.ctm.e;
+ int py = state.origin_y + state.ctm.b * x +
+ state.ctm.d * y + state.ctm.f;
+ int pr = r * state.ctm.a;
+
+ if (state.fill != TRANSPARENT)
+ if (!plot.disc(px, py, pr, state.fill, true))
+ return false;
+
+ if (state.stroke != TRANSPARENT)
+ if (!plot.disc(px, py, pr, state.stroke, false))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Redraw a <line> element node.
+ */
+
+bool svg_redraw_line(xmlNode *line, struct svg_redraw_state state)
+{
+ float x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+
+ for (xmlAttr *attr = line->properties; attr; attr = attr->next) {
+ if (strcmp(attr->name, "x1") == 0)
+ x1 = svg_parse_length(attr->children->content,
+ state.viewport_width, state);
+ else if (strcmp(attr->name, "y1") == 0)
+ y1 = svg_parse_length(attr->children->content,
+ state.viewport_height, state);
+ else if (strcmp(attr->name, "x2") == 0)
+ x2 = svg_parse_length(attr->children->content,
+ state.viewport_width, state);
+ else if (strcmp(attr->name, "y2") == 0)
+ y2 = svg_parse_length(attr->children->content,
+ state.viewport_height, state);
+ }
+ svg_parse_paint_attributes(line, &state);
+
+ int px1 = state.origin_x + state.ctm.a * x1 +
+ state.ctm.c * y1 + state.ctm.e;
+ int py1 = state.origin_y + state.ctm.b * x1 +
+ state.ctm.d * y1 + state.ctm.f;
+ int px2 = state.origin_x + state.ctm.a * x2 +
+ state.ctm.c * y2 + state.ctm.e;
+ int py2 = state.origin_y + state.ctm.b * x2 +
+ state.ctm.d * y2 + state.ctm.f;
+
+ return plot.line(px1, py1, px2, py2, state.stroke_width, state.stroke,
+ false, false);
}
@@ -182,25 +350,34 @@ bool svg_redraw_rect(xmlNode *rect, int x, int y)
* Redraw a <text> or <tspan> element node.
*/
-bool svg_redraw_text(xmlNode *text, int x, int y)
+bool svg_redraw_text(xmlNode *text, struct svg_redraw_state state)
{
- for (xmlAttr *attr = text->properties; attr; attr = attr->next) {
- if (strcmp(attr->name, "x") == 0)
- x += atoi(attr->children->content);
- else if (strcmp(attr->name, "y") == 0)
- y += atoi(attr->children->content);
- }
+ float x, y, width, height;
+
+ svg_parse_position_attributes(text, state,
+ &x, &y, &width, &height);
+ svg_parse_font_attributes(text, &state);
+ int px = state.origin_x + state.ctm.a * x +
+ state.ctm.c * y + state.ctm.e;
+ int py = state.origin_y + 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;
for (xmlNode *child = text->children; child; child = child->next) {
bool ok = true;
if (child->type == XML_TEXT_NODE) {
- ok = plot.text(x, y, &css_base_style,
+ ok = plot.text(px, py,
+ &css_base_style,
child->content, strlen(child->content),
0xffffff, 0x000000);
} else if (child->type == XML_ELEMENT_NODE &&
strcmp(child->name, "tspan") == 0) {
- ok = svg_redraw_text(child, x, y);
+ ok = svg_redraw_text(child, state);
}
if (!ok)
@@ -212,6 +389,151 @@ bool svg_redraw_text(xmlNode *text, int x, int y)
/**
+ * Parse x, y, width, and height attributes, if present.
+ */
+
+void svg_parse_position_attributes(const xmlNode *node,
+ const struct svg_redraw_state state,
+ float *x, float *y, float *width, float *height)
+{
+ *x = 0;
+ *y = 0;
+ *width = state.viewport_width;
+ *height = state.viewport_height;
+
+ for (xmlAttr *attr = node->properties; attr; attr = attr->next) {
+ if (strcmp(attr->name, "x") == 0)
+ *x = svg_parse_length(attr->children->content,
+ state.viewport_width, state);
+ else if (strcmp(attr->name, "y") == 0)
+ *y = svg_parse_length(attr->children->content,
+ state.viewport_height, state);
+ else if (strcmp(attr->name, "width") == 0)
+ *width = svg_parse_length(attr->children->content,
+ state.viewport_width, state);
+ else if (strcmp(attr->name, "height") == 0)
+ *height = svg_parse_length(attr->children->content,
+ state.viewport_height, state);
+ }
+}
+
+
+/**
+ * Parse a length as a number of pixels.
+ */
+
+float svg_parse_length(const xmlChar *s, int viewport_size,
+ const struct svg_redraw_state state)
+{
+ int num_length = strspn(s, "0123456789+-.");
+ const xmlChar *unit = s + num_length;
+ float n = atof((const char *) s);
+ float font_size = css_len2px(&state.style.font_size.value.length, 0);
+
+ if (unit[0] == 0) {
+ return n;
+ } else if (unit[0] == '%') {
+ return n / 100.0 * viewport_size;
+ } else if (unit[0] == 'e' && unit[1] == 'm') {
+ return n * font_size;
+ } else if (unit[0] == 'e' && unit[1] == 'x') {
+ return n / 2.0 * font_size;
+ } else if (unit[0] == 'p' && unit[1] == 'x') {
+ return n;
+ } else if (unit[0] == 'p' && unit[1] == 't') {
+ return n * 1.25;
+ } else if (unit[0] == 'p' && unit[1] == 'c') {
+ return n * 15.0;
+ } else if (unit[0] == 'm' && unit[1] == 'm') {
+ return n * 3.543307;
+ } else if (unit[0] == 'c' && unit[1] == 'm') {
+ return n * 35.43307;
+ } else if (unit[0] == 'i' && unit[1] == 'n') {
+ return n * 90;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Parse paint attributes, if present.
+ */
+
+void svg_parse_paint_attributes(const xmlNode *node,
+ struct svg_redraw_state *state)
+{
+ for (const xmlAttr *attr = node->properties; attr; attr = attr->next) {
+ if (strcmp(attr->name, "fill") == 0)
+ svg_parse_color((const char *) attr->children->content,
+ &state->fill);
+ else if (strcmp(attr->name, "stroke") == 0)
+ svg_parse_color((const char *) attr->children->content,
+ &state->stroke);
+ else if (strcmp(attr->name, "stroke-width") == 0)
+ state->stroke_width = svg_parse_length(
+ attr->children->content,
+ state->viewport_width, *state);
+ }
+}
+
+
+/**
+ * Parse a colour.
+ */
+
+void svg_parse_color(const char *s, colour *c)
+{
+ unsigned int r, g, b;
+ float rf, gf, bf;
+ size_t len = strlen(s);
+
+ if (len == 4 && s[0] == '#') {
+ if (sscanf(s + 1, "%1x%1x%1x", &r, &g, &b) == 3)
+ *c = (b << 20) | (b << 16) |
+ (g << 12) | (g << 8) |
+ (r << 4) | r;
+ } else if (len == 7 && s[0] == '#') {
+ if (sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3)
+ *c = (b << 16) | (g << 8) | r;
+ } else if (10 <= len && s[0] == 'r' && s[1] == 'g' && s[2] == 'b' &&
+ s[3] == '(' && s[len - 1] == ')') {
+ if (sscanf(s + 4, "%i,%i,%i", &r, &g, &b) == 3)
+ *c = (b << 16) | (g << 8) | r;
+ else if (sscanf(s + 4, "%f%%,%f%%,%f%%", &rf, &gf, &bf) == 3) {
+ b = bf * 255 / 100;
+ g = gf * 255 / 100;
+ r = rf * 255 / 100;
+ *c = (b << 16) | (g << 8) | r;
+ }
+ } else {
+ *c = named_colour(s);
+ }
+}
+
+
+/**
+ * Parse font attributes, if present.
+ */
+
+void svg_parse_font_attributes(const xmlNode *node,
+ struct svg_redraw_state *state)
+{
+ for (const xmlAttr *attr = node->properties; attr; attr = attr->next) {
+ if (strcmp(attr->name, "font-size") == 0) {
+ /*if (css_parse_length(
+ (const char *) attr->children->content,
+ &state->style.font_size.value.length,
+ true, true)) {
+ state->style.font_size.size =
+ CSS_FONT_SIZE_LENGTH;
+ }*/
+ }
+ }
+}
+
+
+/**
* Destroy a CONTENT_SVG and free all resources it owns.
*/