diff options
author | Tor Andersson <tor.andersson@artifex.com> | 2008-08-07 18:10:55 +0000 |
---|---|---|
committer | Tor Andersson <tor.andersson@artifex.com> | 2008-08-07 18:10:55 +0000 |
commit | b1fa0d7430800e3c2cd358c6abec210e380c6f2b (patch) | |
tree | eee1982b38205d53b00010d78c2a8bc5e2ea51e0 /svg | |
parent | 72aa32b89ccc1f858b5fc8d4231b1a48309ff3f6 (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.h | 55 | ||||
-rw-r--r-- | svg/svg.mak | 22 | ||||
-rw-r--r-- | svg/svgcolor.c | 115 | ||||
-rw-r--r-- | svg/svgcolorlist.h | 147 | ||||
-rw-r--r-- | svg/svgdoc.c | 339 | ||||
-rw-r--r-- | svg/svgshapes.c | 574 | ||||
-rw-r--r-- | svg/svgtop.c | 53 | ||||
-rw-r--r-- | svg/svgtransform.c | 176 | ||||
-rw-r--r-- | svg/svgtypes.c | 79 | ||||
-rw-r--r-- | svg/svgxml.c | 19 |
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); +} + |