summaryrefslogtreecommitdiff
path: root/svg
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2008-08-07 18:10:55 +0000
committerTor Andersson <tor.andersson@artifex.com>2008-08-07 18:10:55 +0000
commitb1fa0d7430800e3c2cd358c6abec210e380c6f2b (patch)
treeeee1982b38205d53b00010d78c2a8bc5e2ea51e0 /svg
parent72aa32b89ccc1f858b5fc8d4231b1a48309ff3f6 (diff)
Parse SVG basic data types, colors and shapes.
git-svn-id: http://svn.ghostscript.com/ghostscript/trunk@8951 a1074d23-0009-0410-80fe-cf8c14f379e6
Diffstat (limited to 'svg')
-rw-r--r--svg/ghostsvg.h55
-rw-r--r--svg/svg.mak22
-rw-r--r--svg/svgcolor.c115
-rw-r--r--svg/svgcolorlist.h147
-rw-r--r--svg/svgdoc.c339
-rw-r--r--svg/svgshapes.c574
-rw-r--r--svg/svgtop.c53
-rw-r--r--svg/svgtransform.c176
-rw-r--r--svg/svgtypes.c79
-rw-r--r--svg/svgxml.c19
10 files changed, 1545 insertions, 34 deletions
diff --git a/svg/ghostsvg.h b/svg/ghostsvg.h
index 52d1c322d..e8ef5b190 100644
--- a/svg/ghostsvg.h
+++ b/svg/ghostsvg.h
@@ -103,11 +103,52 @@ int svg_show_page(svg_context_t *ctx, int num_copies, int flush);
int svg_utf8_to_ucs(int *p, const char *s, int n);
/*
+ * Parse basic data type units.
+ */
+
+float svg_parse_length(char *str, float percent, float font_size);
+float svg_parse_angle(char *str);
+
+int svg_parse_color(char *str, float *rgb);
+
+int svg_is_whitespace_or_comma(int c);
+int svg_is_whitespace(int c);
+int svg_is_alpha(int c);
+int svg_is_digit(int c);
+
+/*
+ * Graphics content parsing.
+ */
+
+int svg_parse_common(svg_context_t *ctx, svg_item_t *node);
+
+int svg_parse_transform(svg_context_t *ctx, char *str);
+
+void svg_set_color(svg_context_t *ctx, float *rgb);
+void svg_set_fill_color(svg_context_t *ctx);
+void svg_set_stroke_color(svg_context_t *ctx);
+
+int svg_parse_image(svg_context_t *ctx, svg_item_t *node);
+
+int svg_parse_path(svg_context_t *ctx, svg_item_t *node);
+
+int svg_parse_rect(svg_context_t *ctx, svg_item_t *node);
+int svg_parse_circle(svg_context_t *ctx, svg_item_t *node);
+int svg_parse_ellipse(svg_context_t *ctx, svg_item_t *node);
+int svg_parse_line(svg_context_t *ctx, svg_item_t *node);
+int svg_parse_polyline(svg_context_t *ctx, svg_item_t *node);
+int svg_parse_polygon(svg_context_t *ctx, svg_item_t *node);
+
+int svg_parse_text(svg_context_t *ctx, svg_item_t *node);
+
+/*
* Global context and XML parsing.
*/
+int svg_parse_element(svg_context_t *ctx, svg_item_t *root);
+
int svg_open_xml_parser(svg_context_t *ctx);
-int svg_feed_xml_parser(svg_context_t *ctx, char *buf, int len);
+int svg_feed_xml_parser(svg_context_t *ctx, const char *buf, int len);
svg_item_t * svg_close_xml_parser(svg_context_t *ctx);
int svg_parse_document(svg_context_t *ctx, svg_item_t *root);
@@ -135,6 +176,18 @@ struct svg_context_s
gs_font_dir *fontdir;
gs_color_space *srgb;
+ int fill_rule;
+
+ int fill_is_set;
+ float fill_color[3];
+
+ int stroke_is_set;
+ float stroke_color[3];
+
+ float viewbox_width;
+ float viewbox_height;
+ float viewbox_size;
+
svg_item_t *root;
svg_item_t *head;
const char *error;
diff --git a/svg/svg.mak b/svg/svg.mak
index 2ebeaee6b..4d318b335 100644
--- a/svg/svg.mak
+++ b/svg/svg.mak
@@ -32,20 +32,36 @@ ghostsvg_h=$(SVGSRC)ghostsvg.h $(memory__h) $(math__h) \
SVGINCLUDES=$(ghostsvg_h)
-$(SVGOBJ)svgxml.$(OBJ): $(SVGSRC)svgxml.c $(SVGINCLUDES)
- $(SVGCCC) $(SVGSRC)svgxml.c $(SVGO_)svgxml.$(OBJ)
+$(SVGOBJ)svgtypes.$(OBJ): $(SVGSRC)svgtypes.c $(SVGINCLUDES)
+ $(SVGCCC) $(SVGSRC)svgtypes.c $(SVGO_)svgtypes.$(OBJ)
+
+$(SVGOBJ)svgcolor.$(OBJ): $(SVGSRC)svgcolor.c $(SVGINCLUDES)
+ $(SVGCCC) $(SVGSRC)svgcolor.c $(SVGO_)svgcolor.$(OBJ)
+
+$(SVGOBJ)svgtransform.$(OBJ): $(SVGSRC)svgtransform.c $(SVGINCLUDES)
+ $(SVGCCC) $(SVGSRC)svgtransform.c $(SVGO_)svgtransform.$(OBJ)
+
+$(SVGOBJ)svgshapes.$(OBJ): $(SVGSRC)svgshapes.c $(SVGINCLUDES)
+ $(SVGCCC) $(SVGSRC)svgshapes.c $(SVGO_)svgshapes.$(OBJ)
$(SVGOBJ)svgdoc.$(OBJ): $(SVGSRC)svgdoc.c $(SVGINCLUDES)
$(SVGCCC) $(SVGSRC)svgdoc.c $(SVGO_)svgdoc.$(OBJ)
+$(SVGOBJ)svgxml.$(OBJ): $(SVGSRC)svgxml.c $(SVGINCLUDES)
+ $(SVGCCC) $(SVGSRC)svgxml.c $(SVGO_)svgxml.$(OBJ)
+
$(SVG_TOP_OBJ): $(SVGSRC)svgtop.c $(pltop_h) $(SVGGEN)pconf.h $(SVGINCLUDES)
$(CP_) $(SVGGEN)pconf.h $(SVGGEN)pconfig.h
$(SVGCCC) $(SVGSRC)svgtop.c $(SVGO_)svgtop.$(OBJ)
SVG_OBJS=\
- $(SVGOBJ)svgxml.$(OBJ) \
+ $(SVGOBJ)svgtypes.$(OBJ) \
+ $(SVGOBJ)svgcolor.$(OBJ) \
+ $(SVGOBJ)svgtransform.$(OBJ) \
+ $(SVGOBJ)svgshapes.$(OBJ) \
$(SVGOBJ)svgdoc.$(OBJ) \
+ $(SVGOBJ)svgxml.$(OBJ) \
# NB - note this is a bit squirrely. Right now the pjl interpreter is
# required and shouldn't be and PLOBJ==SVGGEN is required.
diff --git a/svg/svgcolor.c b/svg/svgcolor.c
new file mode 100644
index 000000000..65eb80b4b
--- /dev/null
+++ b/svg/svgcolor.c
@@ -0,0 +1,115 @@
+#include "ghostsvg.h"
+
+/* Color keywords (white, blue, fuchsia)
+ * System color keywords (ActiveBorder, ButtonFace -- need to find reasonable defaults)
+ * #fb0 (expand to #ffbb00)
+ * #ffbb00
+ * rgb(255,255,255)
+ * rgb(100%,100%,100%)
+ *
+ * "red icc-color(profileName,255,0,0)" (not going to support for now)
+ */
+
+struct
+{
+ const char *name;
+ float red, green, blue;
+}
+svg_predefined_colors[] =
+{
+#include "svgcolorlist.h"
+};
+
+static int unhex(int chr)
+{
+ const char *hextable = "0123456789ABCDEF";
+ return strchr(hextable, (toupper(chr))) - hextable;
+}
+
+void
+svg_set_color(svg_context_t *ctx, float *rgb)
+{
+ gs_client_color cc;
+
+ cc.pattern = 0;
+ cc.paint.values[0] = rgb[0];
+ cc.paint.values[1] = rgb[1];
+ cc.paint.values[2] = rgb[2];
+
+ gs_setcolor(ctx->pgs, &cc);
+}
+
+void svg_set_fill_color(svg_context_t *ctx)
+{
+ svg_set_color(ctx, ctx->fill_color);
+}
+
+void svg_set_stroke_color(svg_context_t *ctx)
+{
+ svg_set_color(ctx, ctx->stroke_color);
+}
+
+int
+svg_parse_color(char *str, float *rgb)
+{
+ int l, m, r, cmp;
+
+ rgb[0] = 0.0;
+ rgb[1] = 0.0;
+ rgb[2] = 0.0;
+
+ /* Crack hex-coded RGB */
+
+ if (str[0] == '#')
+ {
+ str ++;
+
+ if (strlen(str) == 3)
+ {
+ rgb[0] = (unhex(str[0]) * 16 + unhex(str[0])) / 255.0;
+ rgb[1] = (unhex(str[1]) * 16 + unhex(str[1])) / 255.0;
+ rgb[2] = (unhex(str[2]) * 16 + unhex(str[2])) / 255.0;
+ return 0;
+ }
+
+ if (strlen(str) == 6)
+ {
+ rgb[0] = (unhex(str[0]) * 16 + unhex(str[1])) / 255.0;
+ rgb[1] = (unhex(str[2]) * 16 + unhex(str[3])) / 255.0;
+ rgb[2] = (unhex(str[4]) * 16 + unhex(str[5])) / 255.0;
+ return 0;
+ }
+
+ return gs_throw(-1, "syntax error in color - wrong length of string after #");
+ }
+
+ /* TODO: parse rgb(X,Y,Z) syntax */
+ /* TODO: parse icc-profile(X,Y,Z,W) syntax */
+
+ /* Search for a pre-defined color */
+
+ else
+ {
+ l = 0;
+ r = sizeof(svg_predefined_colors) / sizeof(svg_predefined_colors[0]);
+
+ while (l <= r)
+ {
+ m = (l + r) / 2;
+ cmp = strcmp(svg_predefined_colors[m].name, str);
+ if (cmp > 0)
+ r = m - 1;
+ else if (cmp < 0)
+ l = m + 1;
+ else
+ {
+ rgb[0] = svg_predefined_colors[m].red;
+ rgb[1] = svg_predefined_colors[m].green;
+ rgb[2] = svg_predefined_colors[m].blue;
+ return 0;
+ }
+ }
+ }
+
+ return gs_throw1(-1, "cannot recognize color syntax: '%s'", str);
+}
diff --git a/svg/svgcolorlist.h b/svg/svgcolorlist.h
new file mode 100644
index 000000000..6f0f17415
--- /dev/null
+++ b/svg/svgcolorlist.h
@@ -0,0 +1,147 @@
+{ "aliceblue", 240, 248, 255 },
+{ "antiquewhite", 250, 235, 215 },
+{ "aqua", 0, 255, 255 },
+{ "aquamarine", 127, 255, 212 },
+{ "azure", 240, 255, 255 },
+{ "beige", 245, 245, 220 },
+{ "bisque", 255, 228, 196 },
+{ "black", 0, 0, 0 },
+{ "blanchedalmond", 255, 235, 205 },
+{ "blue", 0, 0, 255 },
+{ "blueviolet", 138, 43, 226 },
+{ "brown", 165, 42, 42 },
+{ "burlywood", 222, 184, 135 },
+{ "cadetblue", 95, 158, 160 },
+{ "chartreuse", 127, 255, 0 },
+{ "chocolate", 210, 105, 30 },
+{ "coral", 255, 127, 80 },
+{ "cornflowerblue", 100, 149, 237 },
+{ "cornsilk", 255, 248, 220 },
+{ "crimson", 220, 20, 60 },
+{ "cyan", 0, 255, 255 },
+{ "darkblue", 0, 0, 139 },
+{ "darkcyan", 0, 139, 139 },
+{ "darkgoldenrod", 184, 134, 11 },
+{ "darkgray", 169, 169, 169 },
+{ "darkgreen", 0, 100, 0 },
+{ "darkgrey", 169, 169, 169 },
+{ "darkkhaki", 189, 183, 107 },
+{ "darkmagenta", 139, 0, 139 },
+{ "darkolivegreen", 85, 107, 47 },
+{ "darkorange", 255, 140, 0 },
+{ "darkorchid", 153, 50, 204 },
+{ "darkred", 139, 0, 0 },
+{ "darksalmon", 233, 150, 122 },
+{ "darkseagreen", 143, 188, 143 },
+{ "darkslateblue", 72, 61, 139 },
+{ "darkslategray", 47, 79, 79 },
+{ "darkslategrey", 47, 79, 79 },
+{ "darkturquoise", 0, 206, 209 },
+{ "darkviolet", 148, 0, 211 },
+{ "deeppink", 255, 20, 147 },
+{ "deepskyblue", 0, 191, 255 },
+{ "dimgray", 105, 105, 105 },
+{ "dimgrey", 105, 105, 105 },
+{ "dodgerblue", 30, 144, 255 },
+{ "firebrick", 178, 34, 34 },
+{ "floralwhite", 255, 250, 240 },
+{ "forestgreen", 34, 139, 34 },
+{ "fuchsia", 255, 0, 255 },
+{ "gainsboro", 220, 220, 220 },
+{ "ghostwhite", 248, 248, 255 },
+{ "gold", 255, 215, 0 },
+{ "goldenrod", 218, 165, 32 },
+{ "gray", 128, 128, 128 },
+{ "green", 0, 128, 0 },
+{ "greenyellow", 173, 255, 47 },
+{ "grey", 128, 128, 128 },
+{ "honeydew", 240, 255, 240 },
+{ "hotpink", 255, 105, 180 },
+{ "indianred", 205, 92, 92 },
+{ "indigo", 75, 0, 130 },
+{ "ivory", 255, 255, 240 },
+{ "khaki", 240, 230, 140 },
+{ "lavender", 230, 230, 250 },
+{ "lavenderblush", 255, 240, 245 },
+{ "lawngreen", 124, 252, 0 },
+{ "lemonchiffon", 255, 250, 205 },
+{ "lightblue", 173, 216, 230 },
+{ "lightcoral", 240, 128, 128 },
+{ "lightcyan", 224, 255, 255 },
+{ "lightgoldenrodyellow", 250, 250, 210 },
+{ "lightgray", 211, 211, 211 },
+{ "lightgreen", 144, 238, 144 },
+{ "lightgrey", 211, 211, 211 },
+{ "lightpink", 255, 182, 193 },
+{ "lightsalmon", 255, 160, 122 },
+{ "lightseagreen", 32, 178, 170 },
+{ "lightskyblue", 135, 206, 250 },
+{ "lightslategray", 119, 136, 153 },
+{ "lightslategrey", 119, 136, 153 },
+{ "lightsteelblue", 176, 196, 222 },
+{ "lightyellow", 255, 255, 224 },
+{ "lime", 0, 255, 0 },
+{ "limegreen", 50, 205, 50 },
+{ "linen", 250, 240, 230 },
+{ "magenta", 255, 0, 255 },
+{ "maroon", 128, 0, 0 },
+{ "mediumaquamarine", 102, 205, 170 },
+{ "mediumblue", 0, 0, 205 },
+{ "mediumorchid", 186, 85, 211 },
+{ "mediumpurple", 147, 112, 219 },
+{ "mediumseagreen", 60, 179, 113 },
+{ "mediumslateblue", 123, 104, 238 },
+{ "mediumspringgreen", 0, 250, 154 },
+{ "mediumturquoise", 72, 209, 204 },
+{ "mediumvioletred", 199, 21, 133 },
+{ "midnightblue", 25, 25, 112 },
+{ "mintcream", 245, 255, 250 },
+{ "mistyrose", 255, 228, 225 },
+{ "moccasin", 255, 228, 181 },
+{ "navajowhite", 255, 222, 173 },
+{ "navy", 0, 0, 128 },
+{ "oldlace", 253, 245, 230 },
+{ "olive", 128, 128, 0 },
+{ "olivedrab", 107, 142, 35 },
+{ "orange", 255, 165, 0 },
+{ "orangered", 255, 69, 0 },
+{ "orchid", 218, 112, 214 },
+{ "palegoldenrod", 238, 232, 170 },
+{ "palegreen", 152, 251, 152 },
+{ "paleturquoise", 175, 238, 238 },
+{ "palevioletred", 219, 112, 147 },
+{ "papayawhip", 255, 239, 213 },
+{ "peachpuff", 255, 218, 185 },
+{ "peru", 205, 133, 63 },
+{ "pink", 255, 192, 203 },
+{ "plum", 221, 160, 221 },
+{ "powderblue", 176, 224, 230 },
+{ "purple", 128, 0, 128 },
+{ "red", 255, 0, 0 },
+{ "rosybrown", 188, 143, 143 },
+{ "royalblue", 65, 105, 225 },
+{ "saddlebrown", 139, 69, 19 },
+{ "salmon", 250, 128, 114 },
+{ "sandybrown", 244, 164, 96 },
+{ "seagreen", 46, 139, 87 },
+{ "seashell", 255, 245, 238 },
+{ "sienna", 160, 82, 45 },
+{ "silver", 192, 192, 192 },
+{ "skyblue", 135, 206, 235 },
+{ "slateblue", 106, 90, 205 },
+{ "slategray", 112, 128, 144 },
+{ "slategrey", 112, 128, 144 },
+{ "snow", 255, 250, 250 },
+{ "springgreen", 0, 255, 127 },
+{ "steelblue", 70, 130, 180 },
+{ "tan", 210, 180, 140 },
+{ "teal", 0, 128, 128 },
+{ "thistle", 216, 191, 216 },
+{ "tomato", 255, 99, 71 },
+{ "turquoise", 64, 224, 208 },
+{ "violet", 238, 130, 238 },
+{ "wheat", 245, 222, 179 },
+{ "white", 255, 255, 255 },
+{ "whitesmoke", 245, 245, 245 },
+{ "yellow", 255, 255, 0 },
+{ "yellowgreen", 154, 205, 50 },
diff --git a/svg/svgdoc.c b/svg/svgdoc.c
index 73b0cabc8..ebd0b8202 100644
--- a/svg/svgdoc.c
+++ b/svg/svgdoc.c
@@ -1,9 +1,346 @@
#include "ghostsvg.h"
int
+svg_parse_common(svg_context_t *ctx, svg_item_t *node)
+{
+ char *fill_att = svg_att(node, "fill");
+ char *stroke_att = svg_att(node, "stroke");
+ char *transform_att = svg_att(node, "transform");
+ char *fill_rule_att = svg_att(node, "fill-rule");
+ char *stroke_width_att = svg_att(node, "stroke-width");
+ char *stroke_linecap_att = svg_att(node, "stroke-linecap");
+ char *stroke_linejoin_att = svg_att(node, "stroke-linejoin");
+ char *stroke_miterlimit_att = svg_att(node, "stroke-miterlimit");
+
+ int code;
+
+ if (fill_att)
+ {
+ if (!strcmp(fill_att, "none"))
+ {
+ ctx->fill_is_set = 0;
+ }
+ else
+ {
+ ctx->fill_is_set = 1;
+ code = svg_parse_color(fill_att, ctx->fill_color);
+ if (code < 0)
+ return gs_rethrow(code, "cannot parse fill attribute");
+ }
+ }
+
+ if (stroke_att)
+ {
+ if (!strcmp(stroke_att, "none"))
+ {
+ ctx->stroke_is_set = 0;
+ }
+ else
+ {
+ ctx->stroke_is_set = 1;
+ code = svg_parse_color(stroke_att, ctx->stroke_color);
+ if (code < 0)
+ return gs_rethrow(code, "cannot parse stroke attribute");
+ }
+ }
+
+ if (transform_att)
+ {
+ code = svg_parse_transform(ctx, transform_att);
+ if (code < 0)
+ return gs_rethrow(code, "cannot parse transform attribute");
+ }
+
+ if (fill_rule_att)
+ {
+ if (!strcmp(fill_rule_att, "nonzero"))
+ ctx->fill_rule = 1;
+ if (!strcmp(fill_rule_att, "evenodd"))
+ ctx->fill_rule = 0;
+ }
+
+ if (stroke_width_att)
+ {
+ if (!strcmp(stroke_width_att, "inherit"))
+ ;
+ else
+ gs_setlinewidth(ctx->pgs, svg_parse_length(stroke_width_att, ctx->viewbox_size, 12));
+ }
+ else
+ {
+ gs_setlinewidth(ctx->pgs, 1);
+ }
+
+ if (stroke_linecap_att)
+ {
+ if (!strcmp(stroke_linecap_att, "butt"))
+ gs_setlinecap(ctx->pgs, gs_cap_butt);
+ if (!strcmp(stroke_linecap_att, "round"))
+ gs_setlinecap(ctx->pgs, gs_cap_round);
+ if (!strcmp(stroke_linecap_att, "square"))
+ gs_setlinecap(ctx->pgs, gs_cap_square);
+ }
+ else
+ {
+ gs_setlinecap(ctx->pgs, gs_cap_butt);
+ }
+
+ if (stroke_linejoin_att)
+ {
+ if (!strcmp(stroke_linejoin_att, "miter"))
+ gs_setlinejoin(ctx->pgs, gs_join_miter);
+ if (!strcmp(stroke_linejoin_att, "round"))
+ gs_setlinejoin(ctx->pgs, gs_join_round);
+ if (!strcmp(stroke_linejoin_att, "bevel"))
+ gs_setlinejoin(ctx->pgs, gs_join_bevel);
+ }
+ else
+ {
+ gs_setlinejoin(ctx->pgs, gs_join_miter);
+ }
+
+ if (stroke_miterlimit_att)
+ {
+ if (!strcmp(stroke_miterlimit_att, "inherit"))
+ ;
+ else
+ gs_setmiterlimit(ctx->pgs, svg_parse_length(stroke_miterlimit_att, ctx->viewbox_size, 12));
+ }
+ else
+ {
+ gs_setmiterlimit(ctx->pgs, 4.0);
+ }
+
+ return 0;
+}
+
+static int
+svg_parse_g(svg_context_t *ctx, svg_item_t *root)
+{
+ gs_matrix mtx;
+ svg_item_t *node;
+ int code;
+
+ gs_currentmatrix(ctx->pgs, &mtx);
+
+ svg_parse_common(ctx, root);
+
+ for (node = svg_down(root); node; node = svg_next(node))
+ {
+ code = svg_parse_element(ctx, node);
+ if (code < 0)
+ return gs_rethrow(code, "cannot parse <g> element");
+ }
+
+ gs_setmatrix(ctx->pgs, &mtx);
+
+ return 0;
+}
+
+int
+svg_parse_element(svg_context_t *ctx, svg_item_t *root)
+{
+ char *tag = svg_tag(root);
+ int code;
+
+ if (!strcmp(tag, "g"))
+ code = svg_parse_g(ctx, root);
+
+ else if (!strcmp(tag, "title"))
+ return 0;
+ else if (!strcmp(tag, "desc"))
+ return 0;
+
+#if 0
+ else if (!strcmp(tag, "defs"))
+ code = svg_parse_defs(ctx, root);
+ else if (!strcmp(tag, "symbol"))
+ code = svg_parse_symbol(ctx, root);
+ else if (!strcmp(tag, "use"))
+ code = svg_parse_use(ctx, root);
+
+ else if (!strcmp(tag, "image"))
+ code = svg_parse_image(ctx, root);
+#endif
+
+ else if (!strcmp(tag, "path"))
+ code = svg_parse_path(ctx, root);
+ else if (!strcmp(tag, "rect"))
+ code = svg_parse_rect(ctx, root);
+ else if (!strcmp(tag, "circle"))
+ code = svg_parse_circle(ctx, root);
+ else if (!strcmp(tag, "ellipse"))
+ code = svg_parse_ellipse(ctx, root);
+ else if (!strcmp(tag, "line"))
+ code = svg_parse_line(ctx, root);
+ else if (!strcmp(tag, "polyline"))
+ code = svg_parse_polyline(ctx, root);
+ else if (!strcmp(tag, "polygon"))
+ code = svg_parse_polygon(ctx, root);
+
+#if 0
+ else if (!strcmp(tag, "text"))
+ code = svg_parse_text(ctx, root);
+ else if (!strcmp(tag, "tspan"))
+ code = svg_parse_text_span(ctx, root);
+ else if (!strcmp(tag, "tref"))
+ code = svg_parse_text_ref(ctx, root);
+ else if (!strcmp(tag, "textPath"))
+ code = svg_parse_text_path(ctx, root);
+#endif
+
+ else
+ {
+ /* debug print unrecognized tags */
+ code = 0;
+ svg_debug_item(root, 0);
+ }
+
+ if (code < 0)
+ return gs_rethrow(code, "cannot parse svg element");
+
+ return 0;
+}
+
+int
svg_parse_document(svg_context_t *ctx, svg_item_t *root)
{
- svg_debug_item(root, 0);
+ char *version_att;
+ char *width_att;
+ char *height_att;
+ char *view_box_att;
+
+ int use_transparency = 0;
+ svg_item_t *node;
+ int code;
+
+ int version;
+ int width;
+ int height;
+
+ float vb_min_x = 0;
+ float vb_min_y = 0;
+ float vb_width = 595;
+ float vb_height = 842;
+
+ if (strcmp(svg_tag(root), "svg"))
+ return gs_throw1(-1, "expected svg element (found %s)", svg_tag(root));
+
+ version_att = svg_att(root, "version");
+ width_att = svg_att(root, "width");
+ height_att = svg_att(root, "height");
+ view_box_att = svg_att(root, "viewBox");
+
+ version = 10;
+ if (version_att)
+ version = atof(version_att) * 10;
+
+ if (view_box_att)
+ {
+ sscanf(view_box_att, "%g %g %g %g",
+ &vb_min_x, &vb_min_y,
+ &vb_width, &vb_height);
+ }
+
+ width = vb_width;
+ if (!width_att)
+ width = svg_parse_length(width_att, vb_width, 12);
+
+ height = vb_height;
+ if (!height_att)
+ height = svg_parse_length(height_att, vb_height, 12);
+
+ if (version > 12)
+ gs_warn("svg document version is newer than we support");
+
+ /* Setup new page */
+ {
+ gs_memory_t *mem = ctx->memory;
+ gs_state *pgs = ctx->pgs;
+ gx_device *dev = gs_currentdevice(pgs);
+ gs_param_float_array fa;
+ float fv[2];
+ gs_c_param_list list;
+ int code;
+
+ gs_c_param_list_write(&list, mem);
+
+ fv[0] = width;
+ fv[1] = height;
+ fa.persistent = false;
+ fa.data = fv;
+ fa.size = 2;
+
+ code = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa);
+ if ( code >= 0 )
+ {
+ gs_c_param_list_read(&list);
+ code = gs_putdeviceparams(dev, (gs_param_list *)&list);
+ }
+ gs_c_param_list_release(&list);
+
+ /* nb this is for the demo it is wrong and should be removed */
+ gs_initgraphics(pgs);
+
+ /* put the origin at the top of the page */
+
+ gs_initmatrix(pgs);
+
+ code = gs_scale(pgs, 1.0, -1.0);
+ if (code < 0)
+ return gs_rethrow(code, "cannot set page transform");
+
+ code = gs_translate(pgs, 0.0, -height);
+ if (code < 0)
+ return gs_rethrow(code, "cannot set page transform");
+
+ code = gs_erasepage(pgs);
+ if (code < 0)
+ return gs_rethrow(code, "cannot clear page");
+
+ gs_setcolorspace(ctx->pgs, ctx->srgb);
+ }
+
+ /* Need viewbox dimensions for percentage based units */
+ ctx->viewbox_width = vb_width;
+ ctx->viewbox_height = vb_height;
+ ctx->viewbox_size = sqrt(vb_width * vb_width + vb_height * vb_height) / sqrt(2.0);
+
+ /* save the state with the original device before we push */
+ gs_gsave(ctx->pgs);
+
+ if (use_transparency)
+ {
+ code = gs_push_pdf14trans_device(ctx->pgs);
+ if (code < 0)
+ return gs_rethrow(code, "cannot install transparency device");
+ }
+
+ /* Draw contents */
+
+ for (node = svg_down(root); node; node = svg_next(node))
+ {
+ code = svg_parse_element(ctx, node);
+ if (code < 0)
+ break;
+ }
+
+ if (use_transparency)
+ {
+ code = gs_pop_pdf14trans_device(ctx->pgs);
+ if (code < 0)
+ return gs_rethrow(code, "cannot uninstall transparency device");
+ }
+
+ /* Flush page */
+ {
+ code = svg_show_page(ctx, 1, true); /* copies, flush */
+ if (code < 0)
+ return gs_rethrow(code, "cannot flush page");
+ }
+
+ /* restore the original device, discarding the pdf14 compositor */
+ gs_grestore(ctx->pgs);
return 0;
}
diff --git a/svg/svgshapes.c b/svg/svgshapes.c
new file mode 100644
index 000000000..a60589fcb
--- /dev/null
+++ b/svg/svgshapes.c
@@ -0,0 +1,574 @@
+#include "ghostsvg.h"
+
+static int svg_fill(svg_context_t *ctx)
+{
+ if (ctx->fill_rule == 0)
+ gs_eofill(ctx->pgs);
+ else
+ gs_fill(ctx->pgs);
+}
+
+int
+svg_parse_rect(svg_context_t *ctx, svg_item_t *node)
+{
+ char *x_att = svg_att(node, "x");
+ char *y_att = svg_att(node, "y");
+ char *w_att = svg_att(node, "width");
+ char *h_att = svg_att(node, "height");
+ char *rx_att = svg_att(node, "rx");
+ char *ry_att = svg_att(node, "ry");
+
+ float x = 0;
+ float y = 0;
+ float w = 0;
+ float h = 0;
+ float rx = 0;
+ float ry = 0;
+ int draw_rounded = 1;
+
+ svg_parse_common(ctx, node);
+
+ if (x_att) x = svg_parse_length(x_att, ctx->viewbox_width, 12);
+ if (y_att) y = svg_parse_length(y_att, ctx->viewbox_height, 12);
+ if (w_att) w = svg_parse_length(w_att, ctx->viewbox_width, 12);
+ if (h_att) h = svg_parse_length(h_att, ctx->viewbox_height, 12);
+ if (rx_att) rx = svg_parse_length(rx_att, ctx->viewbox_width, 12);
+ if (ry_att) ry = svg_parse_length(ry_att, ctx->viewbox_height, 12);
+
+ if (rx_att && !ry_att)
+ ry = rx;
+ if (ry_att && !rx_att)
+ rx = ry;
+ if (rx > w * 0.5)
+ rx = w * 0.5;
+ if (ry > h * 0.5)
+ ry = h * 0.5;
+
+ if (w <= 0 || h <= 0)
+ return 0;
+
+ /* TODO: we need elliptical arcs to draw rounded corners */
+
+ if (ctx->fill_is_set)
+ {
+ svg_set_fill_color(ctx);
+
+ gs_moveto(ctx->pgs, x, y);
+ gs_lineto(ctx->pgs, x + w, y);
+ gs_lineto(ctx->pgs, x + w, y + h);
+ gs_lineto(ctx->pgs, x, y + h);
+ gs_closepath(ctx->pgs);
+
+ svg_fill(ctx);
+ }
+
+ if (ctx->stroke_is_set)
+ {
+ svg_set_stroke_color(ctx);
+
+ gs_moveto(ctx->pgs, x, y);
+ gs_lineto(ctx->pgs, x + w, y);
+ gs_lineto(ctx->pgs, x + w, y + h);
+ gs_lineto(ctx->pgs, x, y + h);
+ gs_closepath(ctx->pgs);
+
+ gs_stroke(ctx->pgs);
+ }
+
+ return 0;
+}
+
+int
+svg_parse_circle(svg_context_t *ctx, svg_item_t *node)
+{
+ char *cx_att = svg_att(node, "cx");
+ char *cy_att = svg_att(node, "cy");
+ char *r_att = svg_att(node, "r");
+
+ float cx = 0;
+ float cy = 0;
+ float r = 0;
+
+ svg_parse_common(ctx, node);
+
+ if (cx_att) cx = svg_parse_length(cx_att, ctx->viewbox_width, 12);
+ if (cy_att) cy = svg_parse_length(cy_att, ctx->viewbox_height, 12);
+ if (r_att) r = svg_parse_length(r_att, ctx->viewbox_size, 12);
+
+ if (r <= 0)
+ return 0;
+
+ if (ctx->fill_is_set)
+ {
+ svg_set_fill_color(ctx);
+
+ gs_moveto(ctx->pgs, cx + r, cy);
+ gs_arcn(ctx->pgs, cx, cy, r, 0, 90);
+ gs_arcn(ctx->pgs, cx, cy, r, 90, 180);
+ gs_arcn(ctx->pgs, cx, cy, r, 180, 270);
+ gs_arcn(ctx->pgs, cx, cy, r, 270, 360);
+ gs_closepath(ctx->pgs);
+
+ svg_fill(ctx);
+ }
+
+ if (ctx->stroke_is_set)
+ {
+ svg_set_stroke_color(ctx);
+
+ gs_moveto(ctx->pgs, cx + r, cy);
+ gs_arcn(ctx->pgs, cx, cy, r, 0, 90);
+ gs_arcn(ctx->pgs, cx, cy, r, 90, 180);
+ gs_arcn(ctx->pgs, cx, cy, r, 180, 270);
+ gs_arcn(ctx->pgs, cx, cy, r, 270, 360);
+ gs_closepath(ctx->pgs);
+
+ gs_stroke(ctx->pgs);
+ }
+
+ return 0;
+}
+
+int
+svg_parse_ellipse(svg_context_t *ctx, svg_item_t *node)
+{
+ char *cx_att = svg_att(node, "cx");
+ char *cy_att = svg_att(node, "cy");
+ char *rx_att = svg_att(node, "rx");
+ char *ry_att = svg_att(node, "ry");
+
+ float cx = 0;
+ float cy = 0;
+ float rx = 0;
+ float ry = 0;
+
+ svg_parse_common(ctx, node);
+
+ if (cx_att) cx = svg_parse_length(cx_att, ctx->viewbox_width, 12);
+ if (cy_att) cy = svg_parse_length(cy_att, ctx->viewbox_height, 12);
+ if (rx_att) rx = svg_parse_length(rx_att, ctx->viewbox_width, 12);
+ if (ry_att) ry = svg_parse_length(ry_att, ctx->viewbox_height, 12);
+
+ if (rx <= 0 || ry <= 0)
+ return 0;
+
+ /* TODO: we need elliptic arcs */
+
+#if 0
+ if (ctx->fill_is_set)
+ {
+ svg_set_fill_color(ctx);
+ svg_fill(ctx);
+ }
+
+ if (ctx->stroke_is_set)
+ {
+ svg_set_stroke_color(ctx);
+ gs_stroke(ctx->pgs);
+ }
+#endif
+
+ return 0;
+}
+
+int
+svg_parse_line(svg_context_t *ctx, svg_item_t *node)
+{
+ char *x1_att = svg_att(node, "x1");
+ char *y1_att = svg_att(node, "y1");
+ char *x2_att = svg_att(node, "x2");
+ char *y2_att = svg_att(node, "y2");
+
+ float x1 = 0;
+ float y1 = 0;
+ float x2 = 0;
+ float y2 = 0;
+
+ svg_parse_common(ctx, node);
+
+ if (x1_att) x1 = svg_parse_length(x1_att, ctx->viewbox_width, 12);
+ if (y1_att) y1 = svg_parse_length(y1_att, ctx->viewbox_height, 12);
+ if (x2_att) x2 = svg_parse_length(x2_att, ctx->viewbox_width, 12);
+ if (y2_att) y2 = svg_parse_length(y2_att, ctx->viewbox_height, 12);
+
+ if (ctx->stroke_is_set)
+ {
+ svg_set_stroke_color(ctx);
+
+ gs_moveto(ctx->pgs, x1, y1);
+ gs_lineto(ctx->pgs, x2, y2);
+
+ gs_stroke(ctx->pgs);
+ }
+
+ return 0;
+}
+
+static int
+svg_parse_polygon_imp(svg_context_t *ctx, svg_item_t *node, int doclose)
+{
+ char *points_att = svg_att(node, "points");
+ float x, y;
+ char *s;
+
+ if (!points_att)
+ return 0;
+
+ svg_parse_common(ctx, node);
+
+ dprintf1("drawing polygon '%s'\n", points_att);
+
+ return 0;
+}
+
+int
+svg_parse_polyline(svg_context_t *ctx, svg_item_t *node)
+{
+ svg_parse_common(ctx, node);
+
+ if (ctx->stroke_is_set)
+ {
+ svg_set_stroke_color(ctx);
+ svg_parse_polygon_imp(ctx, node, 0);
+ gs_stroke(ctx->pgs);
+ }
+
+ return 0;
+}
+
+int
+svg_parse_polygon(svg_context_t *ctx, svg_item_t *node)
+{
+ svg_parse_common(ctx, node);
+
+ if (ctx->fill_is_set)
+ {
+ svg_set_fill_color(ctx);
+ svg_parse_polygon_imp(ctx, node, 1);
+ svg_fill(ctx);
+ }
+
+ if (ctx->stroke_is_set)
+ {
+ svg_set_stroke_color(ctx);
+ svg_parse_polygon_imp(ctx, node, 1);
+ gs_stroke(ctx->pgs);
+ }
+
+ return 0;
+}
+
+static int
+svg_parse_path_data(svg_context_t *ctx, char *str)
+{
+ gs_point pt;
+ float x1, y1, x2, y2;
+
+ int cmd;
+ int numberlen;
+ char number[20];
+ float args[6];
+ int nargs;
+
+ /* saved control point for smooth curves */
+ int reset_smooth = 1;
+ float smooth_x = 0.0;
+ float smooth_y = 0.0;
+
+ cmd = 0;
+ nargs = 0;
+
+ gs_moveto(ctx->pgs, 0.0, 0.0); /* for the case of opening 'm' */
+
+ while (*str)
+ {
+ while (svg_is_whitespace_or_comma(*str))
+ str ++;
+
+ if (svg_is_digit(*str))
+ {
+ numberlen = 0;
+ while (svg_is_digit(*str) && numberlen < sizeof(number) - 1)
+ number[numberlen++] = *str++;
+ number[numberlen] = 0;
+ if (nargs == 6)
+ return gs_throw(-1, "stack overflow in path data");
+ args[nargs++] = atof(number);
+ }
+ else if (svg_is_alpha(*str))
+ {
+ cmd = *str++;
+ }
+ else if (*str == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return gs_throw1(-1, "syntax error in path data: '%c'", *str);
+ }
+
+ if (reset_smooth)
+ {
+ smooth_x = 0.0;
+ smooth_y = 0.0;
+ }
+
+ reset_smooth = 1;
+
+ switch (cmd)
+ {
+ case 'M':
+ if (nargs == 2)
+ {
+ // dprintf2("moveto %g %g\n", args[0], args[1]);
+ gs_moveto(ctx->pgs, args[0], args[1]);
+ nargs = 0;
+ cmd = 'L'; /* implicit lineto after */
+ }
+ break;
+
+ case 'm':
+ if (nargs == 2)
+ {
+ // dprintf2("rmoveto %g %g\n", args[0], args[1]);
+ gs_rmoveto(ctx->pgs, args[0], args[1]);
+ nargs = 0;
+ cmd = 'l'; /* implicit lineto after */
+ }
+ break;
+
+
+ case 'Z':
+ case 'z':
+ if (nargs == 0)
+ {
+ // dprintf("closepath\n");
+ gs_closepath(ctx->pgs);
+ }
+ break;
+
+ case 'L':
+ if (nargs == 2)
+ {
+ // dprintf2("lineto %g %g\n", args[0], args[1]);
+ gs_lineto(ctx->pgs, args[0], args[1]);
+ nargs = 0;
+ }
+ break;
+
+ case 'l':
+ if (nargs == 2)
+ {
+ // dprintf2("rlineto %g %g\n", args[0], args[1]);
+ gs_rlineto(ctx->pgs, args[0], args[1]);
+ nargs = 0;
+ }
+ break;
+
+ case 'H':
+ if (nargs == 1)
+ {
+ gs_currentpoint(ctx->pgs, &pt);
+ // dprintf1("hlineto %g\n", args[0]);
+ gs_lineto(ctx->pgs, args[0], pt.y);
+ nargs = 0;
+ }
+ break;
+
+ case 'h':
+ if (nargs == 1)
+ {
+ // dprintf1("rhlineto %g\n", args[0]);
+ gs_rlineto(ctx->pgs, args[0], 0.0);
+ nargs = 0;
+ }
+ break;
+
+ case 'V':
+ if (nargs == 1)
+ {
+ gs_currentpoint(ctx->pgs, &pt);
+ // dprintf1("vlineto %g\n", args[0]);
+ gs_lineto(ctx->pgs, pt.x, args[0]);
+ nargs = 0;
+ }
+ break;
+
+ case 'v':
+ if (nargs == 1)
+ {
+ // dprintf1("rvlineto %g\n", args[0]);
+ gs_rlineto(ctx->pgs, 0.0, args[0]);
+ nargs = 0;
+ }
+ break;
+
+ case 'C':
+ reset_smooth = 0;
+ if (nargs == 6)
+ {
+ gs_curveto(ctx->pgs, args[0], args[1], args[2], args[3], args[4], args[5]);
+ smooth_x = args[4] - args[2];
+ smooth_y = args[5] - args[3];
+ nargs = 0;
+ }
+ break;
+
+ case 'c':
+ reset_smooth = 0;
+ if (nargs == 6)
+ {
+ gs_rcurveto(ctx->pgs, args[0], args[1], args[2], args[3], args[4], args[5]);
+ smooth_x = args[4] - args[2];
+ smooth_y = args[5] - args[3];
+ nargs = 0;
+ }
+ break;
+
+ case 'S':
+ reset_smooth = 0;
+ if (nargs == 4)
+ {
+ gs_currentpoint(ctx->pgs, &pt);
+ gs_curveto(ctx->pgs,
+ pt.x + smooth_x, pt.y + smooth_y,
+ args[0], args[1],
+ args[2], args[3]);
+ smooth_x = args[2] - args[0];
+ smooth_y = args[3] - args[1];
+ nargs = 0;
+ }
+ break;
+
+ case 's':
+ reset_smooth = 0;
+ if (nargs == 4)
+ {
+ gs_currentpoint(ctx->pgs, &pt);
+ gs_curveto(ctx->pgs,
+ pt.x + smooth_x, pt.y + smooth_y,
+ pt.x + args[0], pt.y + args[1],
+ pt.x + args[2], pt.y + args[3]);
+ smooth_x = args[2] - args[0];
+ smooth_y = args[3] - args[1];
+ nargs = 0;
+ }
+ break;
+
+ case 'Q':
+ reset_smooth = 0;
+ if (nargs == 4)
+ {
+ gs_currentpoint(ctx->pgs, &pt);
+ x1 = args[0];
+ y1 = args[1];
+ x2 = args[2];
+ y2 = args[3];
+ gs_curveto(ctx->pgs,
+ (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
+ (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
+ x2, y2);
+ smooth_x = x2 - x1;
+ smooth_y = y2 - y1;
+ nargs = 0;
+ }
+ break;
+
+ case 'q':
+ reset_smooth = 0;
+ if (nargs == 4)
+ {
+ gs_currentpoint(ctx->pgs, &pt);
+ x1 = args[0] + pt.x;
+ y1 = args[1] + pt.y;
+ x2 = args[2] + pt.x;
+ y2 = args[3] + pt.y;
+ gs_curveto(ctx->pgs,
+ (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
+ (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
+ x2, y2);
+ smooth_x = x2 - x1;
+ smooth_y = y2 - y1;
+ nargs = 0;
+ }
+ break;
+
+ case 'T':
+ reset_smooth = 0;
+ if (nargs == 4)
+ {
+ gs_currentpoint(ctx->pgs, &pt);
+ x1 = pt.x + smooth_x;
+ y1 = pt.y + smooth_y;
+ x2 = args[0];
+ y2 = args[1];
+ gs_curveto(ctx->pgs,
+ (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
+ (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
+ x2, y2);
+ smooth_x = x2 - x1;
+ smooth_y = y2 - y1;
+ nargs = 0;
+ }
+ break;
+
+ case 't':
+ reset_smooth = 0;
+ if (nargs == 4)
+ {
+ gs_currentpoint(ctx->pgs, &pt);
+ x1 = pt.x + smooth_x;
+ y1 = pt.y + smooth_y;
+ x2 = args[0] + pt.x;
+ y2 = args[1] + pt.y;
+ gs_curveto(ctx->pgs,
+ (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
+ (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
+ x2, y2);
+ smooth_x = x2 - x1;
+ smooth_y = y2 - y1;
+ nargs = 0;
+ }
+ break;
+
+ case 0:
+ if (nargs != 0)
+ return gs_throw(-1, "path data must begin with a command");
+ break;
+
+ default:
+ return gs_throw1(-1, "unrecognized command in path data: '%c'", cmd);
+ }
+ }
+
+ return 0;
+}
+
+int
+svg_parse_path(svg_context_t *ctx, svg_item_t *node)
+{
+ char *d_att = svg_att(node, "d");
+ char *path_length_att = svg_att(node, "pathLength");
+
+ svg_parse_common(ctx, node);
+
+ if (d_att)
+ {
+ if (ctx->fill_is_set)
+ {
+ svg_set_fill_color(ctx);
+ svg_parse_path_data(ctx, d_att);
+ svg_fill(ctx);
+ }
+
+ if (ctx->stroke_is_set)
+ {
+ svg_set_stroke_color(ctx);
+ svg_parse_path_data(ctx, d_att);
+ gs_stroke(ctx->pgs);
+ }
+ }
+
+ return 0;
+}
diff --git a/svg/svgtop.c b/svg/svgtop.c
index 2e537cfe8..4c0f1d636 100644
--- a/svg/svgtop.c
+++ b/svg/svgtop.c
@@ -51,7 +51,7 @@ struct svg_interp_instance_s
#define SVG_VERSION NULL
#define SVG_BUILD_DATE NULL
-const pl_interp_characteristics_t *
+static const pl_interp_characteristics_t *
svg_imp_characteristics(const pl_interp_implementation_t *pimpl)
{
static pl_interp_characteristics_t svg_characteristics =
@@ -117,6 +117,18 @@ svg_imp_allocate_interp_instance(pl_interp_instance_t **ppinstance,
/* TODO: load some builtin ICC profiles here */
ctx->srgb = gs_cspace_new_DeviceRGB(ctx->memory);
+ ctx->fill_rule = 1; /* 0=evenodd, 1=nonzero */
+
+ ctx->fill_is_set = 0;
+ ctx->fill_color[0] = 0.0;
+ ctx->fill_color[1] = 0.0;
+ ctx->fill_color[2] = 0.0;
+
+ ctx->stroke_is_set = 0;
+ ctx->stroke_color[0] = 0.0;
+ ctx->stroke_color[1] = 0.0;
+ ctx->stroke_color[2] = 0.0;
+
instance->pre_page_action = 0;
instance->pre_page_closure = 0;
instance->post_page_action = 0;
@@ -218,11 +230,17 @@ svg_imp_get_device_memory(pl_interp_instance_t *pinstance, gs_memory_t **ppmem)
/* Parse a cursor-full of data */
static int
-svg_imp_process(pl_interp_instance_t *pinstance, stream_cursor_read *pcursor)
+svg_imp_process(pl_interp_instance_t *pinstance, stream_cursor_read *buf)
{
svg_interp_instance_t *instance = (svg_interp_instance_t *)pinstance;
svg_context_t *ctx = instance->ctx;
- return svg_process_data(ctx, pcursor);
+ int code;
+
+ code = svg_feed_xml_parser(ctx, (const char*)buf->ptr + 1, buf->limit - buf->ptr);
+
+ buf->ptr = buf->limit;
+
+ return code;
}
/* Skip to end of job.
@@ -259,21 +277,6 @@ svg_imp_init_job(pl_interp_instance_t *pinstance)
return svg_open_xml_parser(ctx);
}
-/* Process data for job */
-int
-svg_process_data(svg_context_t *ctx, stream_cursor_read *buf)
-{
- int code;
-
- dputs("svg_process_data\n");
-
- code = svg_feed_xml_parser(ctx, buf->ptr + 1, buf->limit - buf->ptr);
-
- buf->ptr = buf->limit;
-
- return code;
-}
-
/* Parser action for end-of-file */
static int
svg_imp_process_eof(pl_interp_instance_t *pinstance)
@@ -281,23 +284,25 @@ svg_imp_process_eof(pl_interp_instance_t *pinstance)
svg_interp_instance_t *instance = (svg_interp_instance_t *)pinstance;
svg_context_t *ctx = instance->ctx;
svg_item_t *root;
+ int code;
- dputs("svg_process_eof\n");
+ dputs("-- svg_imp_process_eof --\n");
root = svg_close_xml_parser(ctx);
if (!root)
return gs_rethrow(-1, "cannot parse xml document");
- return svg_parse_document(ctx, root);
+ code = svg_parse_document(ctx, root);
+
+ svg_free_item(ctx, root);
+
+ return code;
}
/* Wrap up interp instance after a "job" */
static int
svg_imp_dnit_job(pl_interp_instance_t *pinstance)
{
- svg_interp_instance_t *instance = (svg_interp_instance_t *)pinstance;
- svg_context_t *ctx = instance->ctx;
-
dputs("-- svg_imp_dnit_job --\n");
return 0;
@@ -426,7 +431,7 @@ identity_transfer(floatp tint, const gx_transfer_map *ignore_map)
/* The following is a 45 degree spot screen with the spots enumerated
* in a defined order. */
-static const byte order16x16[256] = {
+static byte order16x16[256] = {
38, 11, 14, 32, 165, 105, 90, 171, 38, 12, 14, 33, 161, 101, 88, 167,
30, 6, 0, 16, 61, 225, 231, 125, 30, 6, 1, 17, 63, 222, 227, 122,
27, 3, 8, 19, 71, 242, 205, 110, 28, 4, 9, 20, 74, 246, 208, 106,
diff --git a/svg/svgtransform.c b/svg/svgtransform.c
new file mode 100644
index 000000000..2a309dccb
--- /dev/null
+++ b/svg/svgtransform.c
@@ -0,0 +1,176 @@
+#include "ghostsvg.h"
+
+int svg_parse_transform(svg_context_t *ctx, char *str)
+{
+ char keyword[20];
+ int keywordlen;
+ char number[20];
+ int numberlen;
+ float args[6];
+ int nargs;
+
+ nargs = 0;
+ keywordlen = 0;
+
+ while (*str)
+ {
+ keywordlen = 0;
+ nargs = 0;
+
+ /*
+ * Parse keyword and opening parenthesis.
+ */
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ if (*str == 0)
+ break;
+
+ while (svg_is_alpha(*str) && keywordlen < sizeof(keyword) - 1)
+ keyword[keywordlen++] = *str++;
+ keyword[keywordlen] = 0;
+
+ if (keywordlen == 0)
+ return gs_throw(-1, "syntax error in transform attribute - no keyword");
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ if (*str != '(')
+ return gs_throw(-1, "syntax error in transform attribute - no open paren");
+ str ++;
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ /*
+ * Parse list of numbers until closing parenthesis
+ */
+
+ while (nargs < 6)
+ {
+ numberlen = 0;
+
+ while (svg_is_digit(*str) && numberlen < sizeof(number) - 1)
+ number[numberlen++] = *str++;
+ number[numberlen] = 0;
+
+ args[nargs++] = atof(number);
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ if (*str == ',')
+ str ++;
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ if (*str == ')')
+ {
+ str ++;
+ break;
+ }
+
+ if (*str == 0)
+ return gs_throw(-1, "syntax error in transform attribute - no close paren");
+ }
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ if (*str == ',')
+ str ++;
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ /*
+ * Execute the transform.
+ */
+
+ if (!strcmp(keyword, "matrix"))
+ {
+ gs_matrix mtx;
+
+ if (nargs != 6)
+ return gs_throw1(-1, "wrong number of arguments to matrix(): %d", nargs);
+
+ mtx.xx = args[0];
+ mtx.xy = args[1];
+ mtx.yx = args[2];
+ mtx.yy = args[3];
+ mtx.tx = args[4];
+ mtx.ty = args[5];
+
+ gs_concat(ctx->pgs, &mtx);
+ }
+
+ else if (!strcmp(keyword, "translate"))
+ {
+ if (nargs != 2)
+ return gs_throw1(-1, "wrong number of arguments to translate(): %d", nargs);
+ gs_translate(ctx->pgs, args[0], args[1]);
+ }
+
+ else if (!strcmp(keyword, "scale"))
+ {
+ if (nargs == 1)
+ gs_scale(ctx->pgs, args[0], args[0]);
+ else if (nargs == 2)
+ gs_scale(ctx->pgs, args[0], args[1]);
+ else
+ return gs_throw1(-1, "wrong number of arguments to scale(): %d", nargs);
+ }
+
+ else if (!strcmp(keyword, "rotate"))
+ {
+ if (nargs != 1)
+ return gs_throw1(-1, "wrong number of arguments to rotate(): %d", nargs);
+ gs_rotate(ctx->pgs, args[0]);
+ }
+
+ else if (!strcmp(keyword, "skewX"))
+ {
+ gs_matrix mtx;
+
+ if (nargs != 1)
+ return gs_throw1(-1, "wrong number of arguments to skewX(): %d", nargs);
+
+ mtx.xx = 1.0;
+ mtx.xy = 0.0;
+ mtx.yx = tan(args[0] * 0.0174532925);
+ mtx.yy = 1.0;
+ mtx.tx = 0.0;
+ mtx.ty = 0.0;
+
+ gs_concat(ctx->pgs, &mtx);
+ }
+
+ else if (!strcmp(keyword, "skewY"))
+ {
+ gs_matrix mtx;
+
+ if (nargs != 1)
+ return gs_throw1(-1, "wrong number of arguments to skewY(): %d", nargs);
+
+ mtx.xx = 1.0;
+ mtx.xy = tan(args[0] * 0.0174532925);
+ mtx.yx = 0.0;
+ mtx.yy = 1.0;
+ mtx.tx = 0.0;
+ mtx.ty = 0.0;
+
+ gs_concat(ctx->pgs, &mtx);
+ }
+
+ else
+ {
+ return gs_throw1(-1, "unknown transform function: %d", keyword);
+ }
+ }
+
+ return 0;
+}
+
diff --git a/svg/svgtypes.c b/svg/svgtypes.c
new file mode 100644
index 000000000..68684b503
--- /dev/null
+++ b/svg/svgtypes.c
@@ -0,0 +1,79 @@
+#include "ghostsvg.h"
+
+int svg_is_whitespace_or_comma(int c)
+{
+ return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA) || (c == ',');
+}
+
+int svg_is_whitespace(int c)
+{
+ return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA);
+}
+
+int svg_is_alpha(int c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+int svg_is_digit(int c)
+{
+ return (c >= '0' && c <= '9') ||
+ (c == 'e') || (c == 'E') ||
+ (c == '+') || (c == '-') || (c == '.');
+}
+
+
+/* Return length/coordinate in points */
+float
+svg_parse_length(char *str, float percent, float font_size)
+{
+ char *end;
+ float val;
+
+ val = strtof(str, &end);
+ if (end == str)
+ return 0; /* failed */
+
+ if (!strcmp(end, "px")) return val;
+
+ if (!strcmp(end, "pt")) return val * 1.0;
+ if (!strcmp(end, "pc")) return val * 12.0;
+ if (!strcmp(end, "mm")) return val * 2.83464567;
+ if (!strcmp(end, "cm")) return val * 28.3464567;
+ if (!strcmp(end, "in")) return val * 72.0;
+
+ if (!strcmp(end, "em")) return val * font_size;
+ if (!strcmp(end, "ex")) return val * font_size * 0.5;
+
+ if (!strcmp(end, "%"))
+ return val * percent * 0.01;
+
+ if (end[0] == 0)
+ return val;
+
+ return 0;
+}
+
+/* Return angle in degrees */
+float
+svg_parse_angle(char *str)
+{
+ char *end;
+ float val;
+
+ val = strtof(str, &end);
+ if (end == str)
+ return 0; /* failed */
+
+ if (!strcmp(end, "deg"))
+ return val;
+
+ if (!strcmp(end, "grad"))
+ return val * 0.9;
+
+ if (!strcmp(end, "rad"))
+ return val * 57.2957795;
+
+ return val;
+}
+
diff --git a/svg/svgxml.c b/svg/svgxml.c
index 32168550e..6384a3260 100644
--- a/svg/svgxml.c
+++ b/svg/svgxml.c
@@ -99,7 +99,7 @@ static inline int is_svg_space(int c)
static void on_text(void *zp, const char *buf, int len)
{
svg_context_t *ctx = zp;
- char *atts[3];
+ const char *atts[3];
int i;
if (ctx->error)
@@ -155,7 +155,7 @@ svg_open_xml_parser(svg_context_t *ctx)
}
int
-svg_feed_xml_parser(svg_context_t *ctx, char *buf, int len)
+svg_feed_xml_parser(svg_context_t *ctx, const char *buf, int len)
{
XML_Parser xp = ctx->parser;
int code = XML_Parse(xp, buf, len, 0);
@@ -249,8 +249,8 @@ static void indent(int n)
printf(" ");
}
-void
-svg_debug_item(svg_item_t *item, int level)
+static void
+svg_debug_item_imp(svg_item_t *item, int level, int loop)
{
int i;
@@ -270,7 +270,7 @@ svg_debug_item(svg_item_t *item, int level)
if (item->down)
{
printf(">\n");
- svg_debug_item(item->down, level + 1);
+ svg_debug_item_imp(item->down, level + 1, 1);
indent(level);
printf("</%s>\n", item->name);
}
@@ -279,6 +279,15 @@ svg_debug_item(svg_item_t *item, int level)
}
item = item->next;
+
+ if (!loop)
+ return;
}
}
+void
+svg_debug_item(svg_item_t *item, int level)
+{
+ svg_debug_item_imp(item, level, 0);
+}
+