diff options
Diffstat (limited to 'librsvg/rsvg.c')
-rw-r--r-- | librsvg/rsvg.c | 1452 |
1 files changed, 0 insertions, 1452 deletions
diff --git a/librsvg/rsvg.c b/librsvg/rsvg.c deleted file mode 100644 index 67902f86f..000000000 --- a/librsvg/rsvg.c +++ /dev/null @@ -1,1452 +0,0 @@ -/* - rsvg.c: SAX-based renderer for SVG files into a GdkPixbuf. - - Copyright (C) 2000 Eazel, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - - Author: Raph Levien <raph@artofcode.com> -*/ - -#include <stdio.h> -#include <string.h> -#include <math.h> -#include <ctype.h> - -#include <glib.h> - -#include <libart_lgpl/art_misc.h> -#include <libart_lgpl/art_filterlevel.h> -#include <libart_lgpl/art_affine.h> -#include <libart_lgpl/art_svp.h> -#include <libart_lgpl/art_bpath.h> -#include <libart_lgpl/art_vpath.h> -#include <libart_lgpl/art_vpath_bpath.h> -#include <libart_lgpl/art_rgb_svp.h> -#include <libart_lgpl/art_svp_vpath_stroke.h> -#include <libart_lgpl/art_svp_vpath.h> -#include <libart_lgpl/art_svp_wind.h> - -#include "art_rgba.h" - -#include "art_render.h" -#include "art_render_gradient.h" -#include "art_render_svp.h" -#include "art_render_mask.h" - -#include <gdk-pixbuf/gdk-pixbuf.h> - -#include <gnome-xml/SAX.h> -#include <gnome-xml/xmlmemory.h> - -#include "rsvg-bpath-util.h" -#include "rsvg-defs.h" -#include "rsvg-path.h" -#include "rsvg-css.h" -#include "rsvg-paint-server.h" -#include "rsvg-ft.h" -#include "rsvg.h" - -#define noVERBOSE - -typedef struct _RsvgCtx RsvgCtx; -typedef struct _RsvgState RsvgState; -typedef struct _RsvgSaxHandler RsvgSaxHandler; - -struct _RsvgCtx { - GdkPixbuf *pixbuf; - - double zoom; - - /* stack; there is a state for each element */ - RsvgState *state; - int n_state; - int n_state_max; - - RsvgDefs *defs; - - RsvgSaxHandler *handler; /* should this be a handler stack? */ - int handler_nest; - - GHashTable *entities; /* g_malloc'd string -> xmlEntityPtr */ - - RsvgFTCtx *ft_ctx; -}; - -struct _RsvgState { - double affine[6]; - - gint opacity; /* 0..255 */ - - RsvgPaintServer *fill; - gint fill_opacity; /* 0..255 */ - - RsvgPaintServer *stroke; - gint stroke_opacity; /* 0..255 */ - double stroke_width; - - ArtPathStrokeCapType cap; - ArtPathStrokeJoinType join; - - double font_size; - - guint32 stop_color; /* rgb */ - gint stop_opacity; /* 0..255 */ - - gboolean in_defs; - - GdkPixbuf *save_pixbuf; -}; - -struct _RsvgSaxHandler { - void (*free) (RsvgSaxHandler *self); - void (*start_element) (RsvgSaxHandler *self, const xmlChar *name, const xmlChar **atts); - void (*end_element) (RsvgSaxHandler *self, const xmlChar *name); - void (*characters) (RsvgSaxHandler *self, const xmlChar *ch, int len); -}; - -static RsvgCtx * -rsvg_ctx_new (void) -{ - RsvgCtx *result; - - result = g_new (RsvgCtx, 1); - result->pixbuf = NULL; - result->zoom = 1.0; - result->n_state = 0; - result->n_state_max = 16; - result->state = g_new (RsvgState, result->n_state_max); - result->defs = rsvg_defs_new (); - result->handler = NULL; - result->handler_nest = 0; - result->entities = g_hash_table_new (g_str_hash, g_str_equal); - result->ft_ctx = NULL; - return result; -} - -static void -rsvg_state_init (RsvgState *state) -{ - memset (state, 0, sizeof (*state)); - - art_affine_identity (state->affine); - - state->opacity = 0xff; - state->fill = rsvg_paint_server_parse (NULL, "#000"); - state->fill_opacity = 0xff; - state->stroke_opacity = 0xff; - state->stroke_width = 1; - state->cap = ART_PATH_STROKE_CAP_BUTT; - state->join = ART_PATH_STROKE_JOIN_MITER; - state->stop_opacity = 0xff; -} - -static void -rsvg_state_clone (RsvgState *dst, const RsvgState *src) -{ - *dst = *src; - rsvg_paint_server_ref (dst->fill); - rsvg_paint_server_ref (dst->stroke); - dst->save_pixbuf = NULL; -} - -static void -rsvg_state_finalize (RsvgState *state) -{ - rsvg_paint_server_unref (state->fill); - rsvg_paint_server_unref (state->stroke); -} - -static void -rsvg_ctx_free_helper (gpointer key, gpointer value, gpointer user_data) -{ - xmlEntityPtr entval = (xmlEntityPtr)value; - - /* key == entval->name, so it's implicitly freed below */ - - g_free ((xmlChar *)entval->name); - g_free ((xmlChar *)entval->ExternalID); - g_free ((xmlChar *)entval->SystemID); - xmlFree (entval->content); - xmlFree (entval->orig); - g_free (entval); -} - -/* does not destroy the pixbuf */ -static void -rsvg_ctx_free (RsvgCtx *ctx) -{ - int i; - - if (ctx->ft_ctx != NULL) - rsvg_ft_ctx_done (ctx->ft_ctx); - rsvg_defs_free (ctx->defs); - - for (i = 0; i < ctx->n_state; i++) - rsvg_state_finalize (&ctx->state[i]); - g_free (ctx->state); - - g_hash_table_foreach (ctx->entities, rsvg_ctx_free_helper, NULL); - g_hash_table_destroy (ctx->entities); - - g_free (ctx); -} - -static void -rsvg_pixmap_destroy (guchar *pixels, gpointer data) -{ - g_free (pixels); -} - -static void -rsvg_start_svg (RsvgCtx *ctx, const xmlChar **atts) -{ - int i; - int width = -1, height = -1; - int rowstride; - art_u8 *pixels; - gint fixed; - RsvgState *state; - gboolean has_alpha = 1; - - if (atts != NULL) - { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "width")) - width = rsvg_css_parse_length ((char *)atts[i + 1], &fixed); - else if (!strcmp ((char *)atts[i], "height")) - height = rsvg_css_parse_length ((char *)atts[i + 1], &fixed); - } -#ifdef VERBOSE - fprintf (stdout, "rsvg_start_svg: width = %d, height = %d\n", - width, height); -#endif - - if (width < 0 || height < 0) - { - g_warning ("rsvg_start_svg: width and height attributes are not present in SVG\n"); - if (width < 0) width = 500; - if (height < 0) height = 500; - } - - /* Scale size of target pixbuf */ - width = ceil (width * ctx->zoom); - height = ceil (height * ctx->zoom); - - state = &ctx->state[ctx->n_state - 1]; - art_affine_scale (state->affine, ctx->zoom, ctx->zoom); - - rowstride = (width * (has_alpha ? 4 : 3) + 3) & -4; - pixels = g_new (art_u8, rowstride * height); - memset (pixels, has_alpha ? 0 : 255, rowstride * height); - ctx->pixbuf = gdk_pixbuf_new_from_data (pixels, - GDK_COLORSPACE_RGB, - has_alpha, 8, - width, height, - rowstride, - rsvg_pixmap_destroy, - NULL); - } -} - -/* Parse a CSS2 style argument, setting the SVG context attributes. */ -static void -rsvg_parse_style_arg (RsvgCtx *ctx, RsvgState *state, const char *str) -{ - int arg_off; - - arg_off = rsvg_css_param_arg_offset (str); - if (rsvg_css_param_match (str, "opacity")) - { - state->opacity = rsvg_css_parse_opacity (str + arg_off); - } - else if (rsvg_css_param_match (str, "fill")) - { - rsvg_paint_server_unref (state->fill); - state->fill = rsvg_paint_server_parse (ctx->defs, str + arg_off); - } - else if (rsvg_css_param_match (str, "fill-opacity")) - { - state->fill_opacity = rsvg_css_parse_opacity (str + arg_off); - } - else if (rsvg_css_param_match (str, "stroke")) - { - rsvg_paint_server_unref (state->stroke); - state->stroke = rsvg_paint_server_parse (ctx->defs, str + arg_off); - } - else if (rsvg_css_param_match (str, "stroke-width")) - { - int fixed; - state->stroke_width = rsvg_css_parse_length (str + arg_off, &fixed); - } - else if (rsvg_css_param_match (str, "stroke-linecap")) - { - if (!strcmp (str + arg_off, "butt")) - state->cap = ART_PATH_STROKE_CAP_BUTT; - else if (!strcmp (str + arg_off, "round")) - state->cap = ART_PATH_STROKE_CAP_ROUND; - else if (!strcmp (str + arg_off, "square")) - state->cap = ART_PATH_STROKE_CAP_SQUARE; - else - g_warning ("unknown line cap style %s", str + arg_off); - } - else if (rsvg_css_param_match (str, "stroke-opacity")) - { - state->stroke_opacity = rsvg_css_parse_opacity (str + arg_off); - } - else if (rsvg_css_param_match (str, "stroke-linejoin")) - { - if (!strcmp (str + arg_off, "miter")) - state->join = ART_PATH_STROKE_JOIN_MITER; - else if (!strcmp (str + arg_off, "round")) - state->join = ART_PATH_STROKE_JOIN_ROUND; - else if (!strcmp (str + arg_off, "bevel")) - state->join = ART_PATH_STROKE_JOIN_BEVEL; - else - g_warning ("unknown line join style %s", str + arg_off); - } - else if (rsvg_css_param_match (str, "font-size")) - { - state->font_size = rsvg_css_parse_fontsize (str + arg_off); - } - else if (rsvg_css_param_match (str, "font-family")) - { - /* state->font_family = g_strdup (str + arg_off); */ - } - else if (rsvg_css_param_match (str, "stop-color")) - { - state->stop_color = rsvg_css_parse_color (str + arg_off); - } - else if (rsvg_css_param_match (str, "stop-opacity")) - { - state->stop_opacity = rsvg_css_parse_opacity (str + arg_off); - } -} - -/* Split a CSS2 style into individual style arguments, setting attributes - in the SVG context. - - It's known that this is _way_ out of spec. A more complete CSS2 - implementation will happen later. -*/ -static void -rsvg_parse_style (RsvgCtx *ctx, RsvgState *state, const char *str) -{ - int start, end; - char *arg; - - start = 0; - while (str[start] != '\0') - { - for (end = start; str[end] != '\0' && str[end] != ';'; end++); - arg = g_new (char, 1 + end - start); - memcpy (arg, str + start, end - start); - arg[end - start] = '\0'; - rsvg_parse_style_arg (ctx, state, arg); - g_free (arg); - start = end; - if (str[start] == ';') start++; - while (str[start] == ' ') start++; - } -} - -/* Parse an SVG transform string into an affine matrix. Reference: SVG - working draft dated 1999-07-06, section 8.5. Return TRUE on - success. */ -static gboolean -rsvg_parse_transform (double dst[6], const char *src) -{ - int idx; - char keyword[32]; - double args[6]; - int n_args; - guint key_len; - double tmp_affine[6]; - - art_affine_identity (dst); - - idx = 0; - while (src[idx]) - { - /* skip initial whitespace */ - while (isspace (src[idx])) - idx++; - - /* parse keyword */ - for (key_len = 0; key_len < sizeof (keyword); key_len++) - { - char c; - - c = src[idx]; - if (isalpha (c) || c == '-') - keyword[key_len] = src[idx++]; - else - break; - } - if (key_len >= sizeof (keyword)) - return FALSE; - keyword[key_len] = '\0'; - - /* skip whitespace */ - while (isspace (src[idx])) - idx++; - - if (src[idx] != '(') - return FALSE; - idx++; - - for (n_args = 0; ; n_args++) - { - char c; - char *end_ptr; - - /* skip whitespace */ - while (isspace (src[idx])) - idx++; - c = src[idx]; - if (isdigit (c) || c == '+' || c == '-' || c == '.') - { - if (n_args == sizeof(args) / sizeof(args[0])) - return FALSE; /* too many args */ - args[n_args] = strtod (src + idx, &end_ptr); - idx = end_ptr - src; - - while (isspace (src[idx])) - idx++; - - /* skip optional comma */ - if (src[idx] == ',') - idx++; - } - else if (c == ')') - break; - else - return FALSE; - } - idx++; - - /* ok, have parsed keyword and args, now modify the transform */ - if (!strcmp (keyword, "matrix")) - { - if (n_args != 6) - return FALSE; - art_affine_multiply (dst, args, dst); - } - else if (!strcmp (keyword, "translate")) - { - if (n_args == 1) - args[1] = 0; - else if (n_args != 2) - return FALSE; - art_affine_translate (tmp_affine, args[0], args[1]); - art_affine_multiply (dst, tmp_affine, dst); - } - else if (!strcmp (keyword, "scale")) - { - if (n_args == 1) - args[1] = args[0]; - else if (n_args != 2) - return FALSE; - art_affine_scale (tmp_affine, args[0], args[1]); - art_affine_multiply (dst, tmp_affine, dst); - } - else if (!strcmp (keyword, "rotate")) - { - if (n_args != 1) - return FALSE; - art_affine_rotate (tmp_affine, args[0]); - art_affine_multiply (dst, tmp_affine, dst); - } - else if (!strcmp (keyword, "skewX")) - { - if (n_args != 1) - return FALSE; - art_affine_shear (tmp_affine, args[0]); - art_affine_multiply (dst, tmp_affine, dst); - } - else if (!strcmp (keyword, "skewY")) - { - if (n_args != 1) - return FALSE; - art_affine_shear (tmp_affine, args[0]); - /* transpose the affine, given that we know [1] is zero */ - tmp_affine[1] = tmp_affine[2]; - tmp_affine[2] = 0; - art_affine_multiply (dst, tmp_affine, dst); - } - else - return FALSE; /* unknown keyword */ - } - return TRUE; -} - -/** - * rsvg_parse_transform_attr: Parse transform attribute and apply to state. - * @ctx: Rsvg context. - * @state: State in which to apply the transform. - * @str: String containing transform. - * - * Parses the transform attribute in @str and applies it to @state. - **/ -static void -rsvg_parse_transform_attr (RsvgCtx *ctx, RsvgState *state, const char *str) -{ - double affine[6]; - - if (rsvg_parse_transform (affine, str)) - { - art_affine_multiply (state->affine, affine, state->affine); - } - else - { - /* parse error for transform attribute. todo: report */ - } -} - -/** - * rsvg_parse_style_attrs: Parse style attribute. - * @ctx: Rsvg context. - * @atts: Attributes in SAX style. - * - * Parses style and transform attributes and modifies state at top of - * stack. - **/ -static void -rsvg_parse_style_attrs (RsvgCtx *ctx, const xmlChar **atts) -{ - int i; - - if (atts != NULL) - { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "style")) - rsvg_parse_style (ctx, &ctx->state[ctx->n_state - 1], - (char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "transform")) - rsvg_parse_transform_attr (ctx, &ctx->state[ctx->n_state - 1], - (char *)atts[i + 1]); - } - } -} - -/** - * rsvg_push_opacity_group: Begin a new transparency group. - * @ctx: Context in which to push. - * - * Pushes a new transparency group onto the stack. The top of the stack - * is stored in the context, while the "saved" value is in the state - * stack. - **/ -static void -rsvg_push_opacity_group (RsvgCtx *ctx) -{ - RsvgState *state; - GdkPixbuf *pixbuf; - art_u8 *pixels; - int width, height, rowstride; - - state = &ctx->state[ctx->n_state - 1]; - pixbuf = ctx->pixbuf; - - if (!gdk_pixbuf_get_has_alpha (pixbuf)) - { - g_warning ("push/pop transparency group on non-alpha buffer nyi"); - return; - } - - state->save_pixbuf = pixbuf; - - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); - rowstride = gdk_pixbuf_get_rowstride (pixbuf); - pixels = g_new (art_u8, rowstride * height); - memset (pixels, 0, rowstride * height); - - pixbuf = gdk_pixbuf_new_from_data (pixels, - GDK_COLORSPACE_RGB, - TRUE, - gdk_pixbuf_get_bits_per_sample (pixbuf), - width, - height, - rowstride, - rsvg_pixmap_destroy, - NULL); - ctx->pixbuf = pixbuf; -} - -/** - * rsvg_pop_opacity_group: End a transparency group. - * @ctx: Context in which to push. - * @opacity: Opacity for blending (0..255). - * - * Pops a new transparency group from the stack, recompositing with the - * next on stack. - **/ -static void -rsvg_pop_opacity_group (RsvgCtx *ctx, int opacity) -{ - RsvgState *state = &ctx->state[ctx->n_state - 1]; - GdkPixbuf *tos, *nos; - art_u8 *tos_pixels, *nos_pixels; - int width; - int height; - int rowstride; - int x, y; - int tmp; - - tos = ctx->pixbuf; - nos = state->save_pixbuf; - - if (!gdk_pixbuf_get_has_alpha (nos)) - { - g_warning ("push/pop transparency group on non-alpha buffer nyi"); - return; - } - - width = gdk_pixbuf_get_width (tos); - height = gdk_pixbuf_get_height (tos); - rowstride = gdk_pixbuf_get_rowstride (tos); - - tos_pixels = gdk_pixbuf_get_pixels (tos); - nos_pixels = gdk_pixbuf_get_pixels (nos); - - for (y = 0; y < height; y++) - { - for (x = 0; x < width; x++) - { - art_u8 r, g, b, a; - a = tos_pixels[4 * x + 3]; - if (a) - { - r = tos_pixels[4 * x]; - g = tos_pixels[4 * x + 1]; - b = tos_pixels[4 * x + 2]; - tmp = a * opacity + 0x80; - a = (tmp + (tmp >> 8)) >> 8; - art_rgba_run_alpha (nos_pixels + 4 * x, r, g, b, a, 1); - } - } - tos_pixels += rowstride; - nos_pixels += rowstride; - } - - gdk_pixbuf_unref (tos); - ctx->pixbuf = nos; -} - -static void -rsvg_start_g (RsvgCtx *ctx, const xmlChar **atts) -{ - RsvgState *state = &ctx->state[ctx->n_state - 1]; - - rsvg_parse_style_attrs (ctx, atts); - - if (state->opacity != 0xff) - rsvg_push_opacity_group (ctx); -} - -static void -rsvg_end_g (RsvgCtx *ctx) -{ - RsvgState *state = &ctx->state[ctx->n_state - 1]; - - if (state->opacity != 0xff) - rsvg_pop_opacity_group (ctx, state->opacity); -} - -/** - * rsvg_close_vpath: Close a vector path. - * @src: Source vector path. - * - * Closes any open subpaths in the vector path. - * - * Return value: Closed vector path, allocated with g_new. - **/ -static ArtVpath * -rsvg_close_vpath (const ArtVpath *src) -{ - ArtVpath *result; - int n_result, n_result_max; - int src_ix; - double beg_x, beg_y; - gboolean open; - - n_result = 0; - n_result_max = 16; - result = g_new (ArtVpath, n_result_max); - - beg_x = 0; - beg_y = 0; - open = FALSE; - - for (src_ix = 0; src[src_ix].code != ART_END; src_ix++) - { - if (n_result == n_result_max) - result = g_renew (ArtVpath, result, n_result_max <<= 1); - result[n_result].code = src[src_ix].code == ART_MOVETO_OPEN ? - ART_MOVETO : src[src_ix].code; - result[n_result].x = src[src_ix].x; - result[n_result].y = src[src_ix].y; - n_result++; - if (src[src_ix].code == ART_MOVETO_OPEN) - { - beg_x = src[src_ix].x; - beg_y = src[src_ix].y; - open = TRUE; - } - else if (src[src_ix + 1].code != ART_LINETO) - { - if (open && (beg_x != src[src_ix].x || beg_y != src[src_ix].y)) - { - if (n_result == n_result_max) - result = g_renew (ArtVpath, result, n_result_max <<= 1); - result[n_result].code = ART_LINETO; - result[n_result].x = beg_x; - result[n_result].y = beg_y; - n_result++; - } - open = FALSE; - } - } - if (n_result == n_result_max) - result = g_renew (ArtVpath, result, n_result_max <<= 1); - result[n_result].code = ART_END; - result[n_result].x = 0.0; - result[n_result].y = 0.0; - return result; -} - -/** - * rsvg_render_svp: Render an SVP. - * @ctx: Context in which to render. - * @svp: SVP to render. - * @ps: Paint server for rendering. - * @opacity: Opacity as 0..0xff. - * - * Renders the SVP over the pixbuf in @ctx. - **/ -static void -rsvg_render_svp (RsvgCtx *ctx, const ArtSVP *svp, - RsvgPaintServer *ps, int opacity) -{ - GdkPixbuf *pixbuf; - ArtRender *render; - gboolean has_alpha; - - pixbuf = ctx->pixbuf; - /* if a pixbuf hasn't been allocated, the svg is probably misformed. Exit - * to avoid crashing. - */ - if (pixbuf == NULL) { - return; - } - - has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); - - render = art_render_new (0, 0, - gdk_pixbuf_get_width (pixbuf), - gdk_pixbuf_get_height (pixbuf), - gdk_pixbuf_get_pixels (pixbuf), - gdk_pixbuf_get_rowstride (pixbuf), - gdk_pixbuf_get_n_channels (pixbuf) - - (has_alpha ? 1 : 0), - gdk_pixbuf_get_bits_per_sample (pixbuf), - has_alpha ? ART_ALPHA_SEPARATE : ART_ALPHA_NONE, - NULL); - - art_render_svp (render, svp); - art_render_mask_solid (render, (opacity << 8) + opacity + (opacity >> 7)); - rsvg_render_paint_server (render, ps, NULL); /* todo: paint server ctx */ - art_render_invoke (render); -} - -static void -rsvg_render_bpath (RsvgCtx *ctx, const ArtBpath *bpath) -{ - RsvgState *state; - ArtBpath *affine_bpath; - ArtVpath *vpath; - ArtSVP *svp; - GdkPixbuf *pixbuf; - gboolean need_tmpbuf; - int opacity; - int tmp; - - state = &ctx->state[ctx->n_state - 1]; - pixbuf = ctx->pixbuf; - affine_bpath = art_bpath_affine_transform (bpath, - state->affine); - - vpath = art_bez_path_to_vec (affine_bpath, 0.25); - art_free (affine_bpath); - - need_tmpbuf = (state->fill != NULL) && (state->stroke != NULL) && - state->opacity != 0xff; - - if (need_tmpbuf) - rsvg_push_opacity_group (ctx); - - if (state->fill != NULL) - { - ArtVpath *closed_vpath; - ArtVpath *perturbed_vpath; - ArtSVP *tmp_svp; - ArtWindRule art_wind; - - closed_vpath = rsvg_close_vpath (vpath); - perturbed_vpath = art_vpath_perturb (closed_vpath); - g_free (closed_vpath); - svp = art_svp_from_vpath (perturbed_vpath); - art_free (perturbed_vpath); - tmp_svp = art_svp_uncross (svp); - art_svp_free (svp); - art_wind = ART_WIND_RULE_NONZERO; /* todo - get from state */ - svp = art_svp_rewind_uncrossed (tmp_svp, art_wind); - art_svp_free (tmp_svp); - - opacity = state->fill_opacity; - if (!need_tmpbuf && state->opacity != 0xff) - { - tmp = opacity * state->opacity + 0x80; - opacity = (tmp + (tmp >> 8)) >> 8; - } - rsvg_render_svp (ctx, svp, state->fill, opacity); - art_svp_free (svp); - } - - if (state->stroke != NULL) - { - /* todo: libart doesn't yet implement anamorphic scaling of strokes */ - double stroke_width = state->stroke_width * - art_affine_expansion (state->affine); - - if (stroke_width < 0.25) - stroke_width = 0.25; - - svp = art_svp_vpath_stroke (vpath, state->join, state->cap, - stroke_width, 4, 0.25); - opacity = state->stroke_opacity; - if (!need_tmpbuf && state->opacity != 0xff) - { - tmp = opacity * state->opacity + 0x80; - opacity = (tmp + (tmp >> 8)) >> 8; - } - rsvg_render_svp (ctx, svp, state->stroke, opacity); - art_svp_free (svp); - } - - if (need_tmpbuf) - rsvg_pop_opacity_group (ctx, state->opacity); - - art_free (vpath); -} - -static void -rsvg_start_path (RsvgCtx *ctx, const xmlChar **atts) -{ - int i; - char *d = NULL; - - rsvg_parse_style_attrs (ctx, atts); - if (atts != NULL) - { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "d")) - d = (char *)atts[i + 1]; - } - } - if (d != NULL) - { - RsvgBpathDef *bpath_def; - - bpath_def = rsvg_parse_path (d); - rsvg_bpath_def_art_finish (bpath_def); - - rsvg_render_bpath (ctx, bpath_def->bpath); - - rsvg_bpath_def_free (bpath_def); - } -} - -/* begin text - this should likely get split into its own .c file */ - -typedef struct _RsvgSaxHandlerText RsvgSaxHandlerText; - -struct _RsvgSaxHandlerText { - RsvgSaxHandler super; - RsvgCtx *ctx; - double xpos; - double ypos; -}; - -static void -rsvg_text_handler_free (RsvgSaxHandler *self) -{ - g_free (self); -} - -static void -rsvg_text_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) -{ - RsvgSaxHandlerText *z = (RsvgSaxHandlerText *)self; - RsvgCtx *ctx = z->ctx; - char *string; - int beg, end; - RsvgFTFontHandle fh; - RsvgFTGlyph *glyph; - int glyph_xy[2]; - RsvgState *state; - ArtRender *render; - GdkPixbuf *pixbuf; - gboolean has_alpha; - int opacity; - - /* Copy ch into string, chopping off leading and trailing whitespace */ - for (beg = 0; beg < len; beg++) - if (!isspace (ch[beg])) - break; - - for (end = len; end > beg; end--) - if (!isspace (ch[end - 1])) - break; - - string = g_malloc (end - beg + 1); - memcpy (string, ch + beg, end - beg); - string[end - beg] = 0; - - -#ifdef VERBOSE - fprintf (stderr, "text characters(%s, %d)\n", string, len); -#endif - - if (ctx->ft_ctx == NULL) - ctx->ft_ctx = rsvg_ft_ctx_new (); - - /* FIXME bugzilla.eazel.com 3904: We need to make rsvg use the - * Nautilus font mapping stuff in NautilusScalableFont. See bug - * for details. - */ - fh = rsvg_ft_intern (ctx->ft_ctx, - NAUTILUS_DATADIR "/fonts/urw/n019003l.pfb"); - rsvg_ft_font_attach (ctx->ft_ctx, fh, - NAUTILUS_DATADIR "/fonts/urw/n019003l.afm"); - - state = &ctx->state[ctx->n_state - 1]; - - if (state->fill != NULL && state->font_size > 0) - { - pixbuf = ctx->pixbuf; - has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); - - render = art_render_new (0, 0, - gdk_pixbuf_get_width (pixbuf), - gdk_pixbuf_get_height (pixbuf), - gdk_pixbuf_get_pixels (pixbuf), - gdk_pixbuf_get_rowstride (pixbuf), - gdk_pixbuf_get_n_channels (pixbuf) - - (has_alpha ? 1 : 0), - gdk_pixbuf_get_bits_per_sample (pixbuf), - has_alpha ? ART_ALPHA_SEPARATE : ART_ALPHA_NONE, - NULL); - - glyph = rsvg_ft_render_string (ctx->ft_ctx, fh, - string, - strlen (string), - state->font_size, state->font_size, - state->affine, glyph_xy); - - rsvg_render_paint_server (render, state->fill, NULL); /* todo: paint server ctx */ - opacity = state->fill_opacity * state->opacity; - opacity = opacity + (opacity >> 7) + (opacity >> 14); -#ifdef VERBOSE - fprintf (stderr, "opacity = %d\n", opacity); -#endif - art_render_mask_solid (render, opacity); - art_render_mask (render, - glyph_xy[0], glyph_xy[1], - glyph_xy[0] + glyph->width, glyph_xy[1] + glyph->height, - glyph->buf, glyph->rowstride); - art_render_invoke (render); - rsvg_ft_glyph_unref (glyph); - } - - g_free (string); -} - -static void -rsvg_start_text (RsvgCtx *ctx, const xmlChar **atts) -{ - RsvgSaxHandlerText *handler = g_new0 (RsvgSaxHandlerText, 1); - - handler->super.free = rsvg_text_handler_free; - handler->super.characters = rsvg_text_handler_characters; - handler->ctx = ctx; - - /* todo: parse "x" and "y" attributes */ - handler->xpos = 0; - handler->ypos = 0; - - rsvg_parse_style_attrs (ctx, atts); - ctx->handler = &handler->super; -#ifdef VERBOSE - fprintf (stderr, "begin text!\n"); -#endif -} - -/* end text */ - -static void -rsvg_start_defs (RsvgCtx *ctx, const xmlChar **atts) -{ - RsvgState *state = &ctx->state[ctx->n_state - 1]; - - state->in_defs = TRUE; -} - -typedef struct _RsvgSaxHandlerGstops RsvgSaxHandlerGstops; - -struct _RsvgSaxHandlerGstops { - RsvgSaxHandler super; - RsvgCtx *ctx; - RsvgGradientStops *stops; -}; - -static void -rsvg_gradient_stop_handler_free (RsvgSaxHandler *self) -{ - g_free (self); -} - -static void -rsvg_gradient_stop_handler_start (RsvgSaxHandler *self, const xmlChar *name, - const xmlChar **atts) -{ - RsvgSaxHandlerGstops *z = (RsvgSaxHandlerGstops *)self; - RsvgGradientStops *stops = z->stops; - int i; - double offset = 0; - gboolean got_offset = FALSE; - gint fixed; - RsvgState state; - int n_stop; - - if (strcmp ((char *)name, "stop")) - { - g_warning ("unexpected <%s> element in gradient\n", name); - return; - } - - rsvg_state_init (&state); - - if (atts != NULL) - { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "offset")) - { - offset = rsvg_css_parse_length ((char *)atts[i + 1], &fixed); - got_offset = TRUE; - } - else if (!strcmp ((char *)atts[i], "style")) - rsvg_parse_style (z->ctx, &state, (char *)atts[i + 1]); - } - } - - rsvg_state_finalize (&state); - - if (!got_offset) - { - g_warning ("gradient stop must specify offset\n"); - return; - } - - n_stop = stops->n_stop++; - if (n_stop == 0) - stops->stop = g_new (RsvgGradientStop, 1); - else if (!(n_stop & (n_stop - 1))) - /* double the allocation if size is a power of two */ - stops->stop = g_renew (RsvgGradientStop, stops->stop, n_stop << 1); - stops->stop[n_stop].offset = offset; - stops->stop[n_stop].rgba = (state.stop_color << 8) | state.stop_opacity; -} - -static void -rsvg_gradient_stop_handler_end (RsvgSaxHandler *self, const xmlChar *name) -{ -} - -static RsvgSaxHandler * -rsvg_gradient_stop_handler_new (RsvgCtx *ctx, RsvgGradientStops **p_stops) -{ - RsvgSaxHandlerGstops *gstops = g_new0 (RsvgSaxHandlerGstops, 1); - RsvgGradientStops *stops = g_new (RsvgGradientStops, 1); - - gstops->super.free = rsvg_gradient_stop_handler_free; - gstops->super.start_element = rsvg_gradient_stop_handler_start; - gstops->super.end_element = rsvg_gradient_stop_handler_end; - gstops->ctx = ctx; - gstops->stops = stops; - - stops->n_stop = 0; - stops->stop = NULL; - - *p_stops = stops; - return &gstops->super; -} - -static void -rsvg_linear_gradient_free (RsvgDefVal *self) -{ - RsvgLinearGradient *z = (RsvgLinearGradient *)self; - - g_free (z->stops->stop); - g_free (z->stops); - g_free (self); -} - -static void -rsvg_start_linear_gradient (RsvgCtx *ctx, const xmlChar **atts) -{ - RsvgState *state = &ctx->state[ctx->n_state - 1]; - RsvgLinearGradient *grad; - int i; - char *id = NULL; - double x1 = 0, y1 = 0, x2 = 100, y2 = 0; - ArtGradientSpread spread = ART_GRADIENT_PAD; - - /* todo: only handles numeric coordinates in gradientUnits = userSpace */ - if (atts != NULL) - { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "id")) - id = (char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "x1")) - x1 = atof ((char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "y1")) - y1 = atof ((char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "x2")) - x2 = atof ((char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "y2")) - y2 = atof ((char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "spreadMethod")) - { - if (!strcmp ((char *)atts[i + 1], "pad")) - spread = ART_GRADIENT_PAD; - else if (!strcmp ((char *)atts[i + 1], "reflect")) - spread = ART_GRADIENT_REFLECT; - else if (!strcmp ((char *)atts[i + 1], "repeat")) - spread = ART_GRADIENT_REPEAT; - } - } - } - - grad = g_new (RsvgLinearGradient, 1); - grad->super.type = RSVG_DEF_LINGRAD; - grad->super.free = rsvg_linear_gradient_free; - - ctx->handler = rsvg_gradient_stop_handler_new (ctx, &grad->stops); - - rsvg_defs_set (ctx->defs, id, &grad->super); - - for (i = 0; i < 6; i++) - grad->affine[i] = state->affine[i]; - grad->x1 = x1; - grad->y1 = y1; - grad->x2 = x2; - grad->y2 = y2; - grad->spread = spread; -} - -static void -rsvg_radial_gradient_free (RsvgDefVal *self) -{ - RsvgRadialGradient *z = (RsvgRadialGradient *)self; - - g_free (z->stops->stop); - g_free (z->stops); - g_free (self); -} - -static void -rsvg_start_radial_gradient (RsvgCtx *ctx, const xmlChar **atts) -{ - RsvgState *state = &ctx->state[ctx->n_state - 1]; - RsvgRadialGradient *grad; - int i; - char *id = NULL; - double cx = 50, cy = 50, r = 50, fx = 50, fy = 50; - - /* todo: only handles numeric coordinates in gradientUnits = userSpace */ - if (atts != NULL) - { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "id")) - id = (char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "cx")) - cx = atof ((char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "cy")) - cy = atof ((char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "r")) - r = atof ((char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "fx")) - fx = atof ((char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "fy")) - fy = atof ((char *)atts[i + 1]); - } - } - - grad = g_new (RsvgRadialGradient, 1); - grad->super.type = RSVG_DEF_RADGRAD; - grad->super.free = rsvg_radial_gradient_free; - - ctx->handler = rsvg_gradient_stop_handler_new (ctx, &grad->stops); - - rsvg_defs_set (ctx->defs, id, &grad->super); - - for (i = 0; i < 6; i++) - grad->affine[i] = state->affine[i]; - grad->cx = cx; - grad->cy = cy; - grad->r = r; - grad->fx = fx; - grad->fy = fy; -} - -static void -rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts) -{ - RsvgCtx *ctx = (RsvgCtx *)data; -#ifdef VERBOSE - int i; -#endif - -#ifdef VERBOSE - fprintf (stdout, "SAX.startElement(%s", (char *) name); - if (atts != NULL) { - for (i = 0;(atts[i] != NULL);i++) { - fprintf (stdout, ", %s='", atts[i++]); - fprintf (stdout, "%s'", atts[i]); - } - } - fprintf (stdout, ")\n"); -#endif - - if (ctx->handler) - { - ctx->handler_nest++; - if (ctx->handler->start_element != NULL) - ctx->handler->start_element (ctx->handler, name, atts); - } - else - { - /* push the state stack */ - if (ctx->n_state == ctx->n_state_max) - ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1); - if (ctx->n_state) - rsvg_state_clone (&ctx->state[ctx->n_state], - &ctx->state[ctx->n_state - 1]); - else - rsvg_state_init (ctx->state); - ctx->n_state++; - - if (!strcmp ((char *)name, "svg")) - rsvg_start_svg (ctx, atts); - else if (!strcmp ((char *)name, "g")) - rsvg_start_g (ctx, atts); - else if (!strcmp ((char *)name, "path")) - rsvg_start_path (ctx, atts); - else if (!strcmp ((char *)name, "text")) - rsvg_start_text (ctx, atts); - else if (!strcmp ((char *)name, "defs")) - rsvg_start_defs (ctx, atts); - else if (!strcmp ((char *)name, "linearGradient")) - rsvg_start_linear_gradient (ctx, atts); - else if (!strcmp ((char *)name, "radialGradient")) - rsvg_start_radial_gradient (ctx, atts); - } -} - -static void -rsvg_end_element (void *data, const xmlChar *name) -{ - RsvgCtx *ctx = (RsvgCtx *)data; - - if (ctx->handler_nest > 0) - { - if (ctx->handler->end_element != NULL) - ctx->handler->end_element (ctx->handler, name); - ctx->handler_nest--; - } - else - { - if (ctx->handler != NULL) - { - ctx->handler->free (ctx->handler); - ctx->handler = NULL; - } - - if (!strcmp ((char *)name, "g")) - rsvg_end_g (ctx); - - /* pop the state stack */ - ctx->n_state--; - rsvg_state_finalize (&ctx->state[ctx->n_state]); - -#ifdef VERBOSE - fprintf (stdout, "SAX.endElement(%s)\n", (char *) name); -#endif - } -} - -static void -rsvg_characters (void *data, const xmlChar *ch, int len) -{ - RsvgCtx *ctx = (RsvgCtx *)data; - - if (ctx->handler && ctx->handler->characters != NULL) - ctx->handler->characters (ctx->handler, ch, len); -} - -static xmlEntityPtr -rsvg_get_entity (void *data, const xmlChar *name) -{ - RsvgCtx *ctx = (RsvgCtx *)data; - - return (xmlEntityPtr)g_hash_table_lookup (ctx->entities, name); -} - -static void -rsvg_entity_decl (void *data, const xmlChar *name, int type, - const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) -{ - RsvgCtx *ctx = (RsvgCtx *)data; - GHashTable *entities = ctx->entities; - xmlEntityPtr entity; - char *dupname; - - entity = g_new (xmlEntity, 1); - entity->type = type; - entity->len = strlen (name); - dupname = g_strdup (name); - entity->name = dupname; - entity->ExternalID = g_strdup (publicId); - entity->SystemID = g_strdup (systemId); - entity->content = xmlMemStrdup (content); - entity->length = strlen (content); - entity->orig = NULL; - g_hash_table_insert (entities, dupname, entity); -} - -static xmlSAXHandler rsvgSAXHandlerStruct = { - NULL, /* internalSubset */ - NULL, /* isStandalone */ - NULL, /* hasInternalSubset */ - NULL, /* hasExternalSubset */ - NULL, /* resolveEntity */ - rsvg_get_entity, /* getEntity */ - rsvg_entity_decl, /* entityDecl */ - NULL, /* notationDecl */ - NULL, /* attributeDecl */ - NULL, /* elementDecl */ - NULL, /* unparsedEntityDecl */ - NULL, /* setDocumentLocator */ - NULL, /* startDocument */ - NULL, /* endDocument */ - rsvg_start_element, /* startElement */ - rsvg_end_element, /* endElement */ - NULL, /* reference */ - rsvg_characters, /* characters */ - NULL, /* ignorableWhitespace */ - NULL, /* processingInstruction */ - NULL, /* comment */ - NULL, /* xmlParserWarning */ - NULL, /* xmlParserError */ - NULL, /* xmlParserError */ - NULL, /* getParameterEntity */ -}; - -static xmlSAXHandlerPtr rsvgSAXHandler = &rsvgSAXHandlerStruct; - -GdkPixbuf * -rsvg_render_file (FILE *f, double zoom) -{ - int res; - char chars[10]; - xmlParserCtxtPtr ctxt; - RsvgCtx *ctx; - GdkPixbuf *result; - - ctx = rsvg_ctx_new (); - ctx->zoom = zoom; - res = fread(chars, 1, 4, f); - if (res > 0) { - ctxt = xmlCreatePushParserCtxt(rsvgSAXHandler, ctx, - chars, res, "filename XXX"); - ctxt->replaceEntities = TRUE; - while ((res = fread(chars, 1, 3, f)) > 0) { - xmlParseChunk(ctxt, chars, res, 0); - } - xmlParseChunk(ctxt, chars, 0, 1); - xmlFreeParserCtxt(ctxt); - } - result = ctx->pixbuf; - rsvg_ctx_free (ctx); - return result; -} - -#ifdef RSVG_MAIN -static void -write_pixbuf (ArtPixBuf *pixbuf) -{ - int y; - printf ("P6\n%d %d\n255\n", pixbuf->width, pixbuf->height); - for (y = 0; y < pixbuf->height; y++) - fwrite (pixbuf->pixels + y * pixbuf->rowstride, 1, pixbuf->width * 3, - stdout); -} - -int -main (int argc, char **argv) -{ - FILE *f; - ArtPixBuf *pixbuf; - - if (argc == 1) - f = stdin; - else - { - f = fopen (argv[1], "r"); - if (f == NULL) - { - fprintf (stderr, "Error opening source file %s\n", argv[1]); - } - } - - pixbuf = rsvg_render_file (f); - - if (f != stdin) - fclose (f); - - write_pixbuf (pixbuf); - - return 0; -} -#endif |