[18608] | 1 | /* vim: set sw=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ |
---|
[18274] | 2 | /* |
---|
[17276] | 3 | rsvg.c: SAX-based renderer for SVG files into a GdkPixbuf. |
---|
[18274] | 4 | |
---|
[17276] | 5 | Copyright (C) 2000 Eazel, Inc. |
---|
[18608] | 6 | Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com> |
---|
[18274] | 7 | |
---|
[17276] | 8 | This program is free software; you can redistribute it and/or |
---|
[18274] | 9 | modify it under the terms of the GNU Library General Public License as |
---|
[17276] | 10 | published by the Free Software Foundation; either version 2 of the |
---|
| 11 | License, or (at your option) any later version. |
---|
[18274] | 12 | |
---|
[17276] | 13 | This program is distributed in the hope that it will be useful, |
---|
| 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
[18274] | 16 | Library General Public License for more details. |
---|
| 17 | |
---|
| 18 | You should have received a copy of the GNU Library General Public |
---|
[17276] | 19 | License along with this program; if not, write to the |
---|
| 20 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
| 21 | Boston, MA 02111-1307, USA. |
---|
[18274] | 22 | |
---|
[17276] | 23 | Author: Raph Levien <raph@artofcode.com> |
---|
| 24 | */ |
---|
| 25 | |
---|
[18274] | 26 | #include "config.h" |
---|
[18608] | 27 | |
---|
[17276] | 28 | #include "rsvg.h" |
---|
[18608] | 29 | #include "rsvg-css.h" |
---|
| 30 | #include "rsvg-styles.h" |
---|
| 31 | #include "rsvg-private.h" |
---|
| 32 | #include "rsvg-shapes.h" |
---|
| 33 | #include "rsvg-text.h" |
---|
[17276] | 34 | |
---|
[18274] | 35 | #include <math.h> |
---|
[17276] | 36 | #include <string.h> |
---|
[18274] | 37 | #include <stdarg.h> |
---|
[17276] | 38 | |
---|
| 39 | #include <libart_lgpl/art_affine.h> |
---|
| 40 | |
---|
| 41 | #include "rsvg-bpath-util.h" |
---|
| 42 | #include "rsvg-path.h" |
---|
| 43 | #include "rsvg-paint-server.h" |
---|
| 44 | |
---|
[18608] | 45 | /* |
---|
| 46 | * This is configurable at runtime |
---|
| 47 | */ |
---|
| 48 | #define RSVG_DEFAULT_DPI 90.0 |
---|
| 49 | static double internal_dpi = RSVG_DEFAULT_DPI; |
---|
[18351] | 50 | |
---|
[17276] | 51 | static void |
---|
| 52 | rsvg_ctx_free_helper (gpointer key, gpointer value, gpointer user_data) |
---|
| 53 | { |
---|
[18608] | 54 | xmlEntityPtr entval = (xmlEntityPtr)value; |
---|
| 55 | |
---|
| 56 | /* key == entval->name, so it's implicitly freed below */ |
---|
| 57 | |
---|
| 58 | g_free ((char *) entval->name); |
---|
| 59 | g_free ((char *) entval->ExternalID); |
---|
| 60 | g_free ((char *) entval->SystemID); |
---|
| 61 | xmlFree (entval->content); |
---|
| 62 | xmlFree (entval->orig); |
---|
| 63 | g_free (entval); |
---|
[17276] | 64 | } |
---|
| 65 | |
---|
| 66 | static void |
---|
| 67 | rsvg_pixmap_destroy (guchar *pixels, gpointer data) |
---|
| 68 | { |
---|
[18608] | 69 | g_free (pixels); |
---|
[17276] | 70 | } |
---|
| 71 | |
---|
| 72 | static void |
---|
[18274] | 73 | rsvg_start_svg (RsvgHandle *ctx, const xmlChar **atts) |
---|
[17276] | 74 | { |
---|
[18608] | 75 | int i; |
---|
| 76 | int width = -1, height = -1, x = -1, y = -1; |
---|
| 77 | int rowstride; |
---|
| 78 | art_u8 *pixels; |
---|
| 79 | gint percent, em, ex; |
---|
| 80 | RsvgState *state; |
---|
| 81 | gboolean has_alpha = TRUE; |
---|
| 82 | gint new_width, new_height; |
---|
| 83 | double x_zoom = 1.; |
---|
| 84 | double y_zoom = 1.; |
---|
| 85 | |
---|
| 86 | double vbox_x = 0, vbox_y = 0, vbox_w = 0, vbox_h = 0; |
---|
| 87 | gboolean has_vbox = TRUE; |
---|
[17276] | 88 | |
---|
[18608] | 89 | if (atts != NULL) |
---|
| 90 | { |
---|
| 91 | for (i = 0; atts[i] != NULL; i += 2) |
---|
| 92 | { |
---|
| 93 | /* x & y should be ignored since we should always be the outermost SVG, |
---|
| 94 | at least for now, but i'll include them here anyway */ |
---|
| 95 | if (!strcmp ((char *)atts[i], "width")) |
---|
| 96 | width = rsvg_css_parse_length ((char *)atts[i + 1], ctx->dpi, &percent, &em, &ex); |
---|
| 97 | else if (!strcmp ((char *)atts[i], "height")) |
---|
| 98 | height = rsvg_css_parse_length ((char *)atts[i + 1], ctx->dpi, &percent, &em, &ex); |
---|
| 99 | else if (!strcmp ((char *)atts[i], "x")) |
---|
| 100 | x = rsvg_css_parse_length ((char *)atts[i + 1], ctx->dpi, &percent, &em, &ex); |
---|
| 101 | else if (!strcmp ((char *)atts[i], "y")) |
---|
| 102 | y = rsvg_css_parse_length ((char *)atts[i + 1], ctx->dpi, &percent, &em, &ex); |
---|
| 103 | else if (!strcmp ((char *)atts[i], "viewBox")) |
---|
| 104 | { |
---|
| 105 | has_vbox = rsvg_css_parse_vbox ((char *)atts[i + 1], &vbox_x, &vbox_y, |
---|
| 106 | &vbox_w, &vbox_h); |
---|
| 107 | } |
---|
| 108 | } |
---|
| 109 | |
---|
| 110 | if (has_vbox && vbox_w > 0. && vbox_h > 0.) |
---|
| 111 | { |
---|
| 112 | new_width = (int)floor (vbox_w); |
---|
| 113 | new_height = (int)floor (vbox_h); |
---|
| 114 | |
---|
| 115 | /* apply the sizing function on the *original* width and height |
---|
| 116 | to acquire our real destination size. we'll scale it against |
---|
| 117 | the viewBox's coordinates later */ |
---|
| 118 | if (ctx->size_func) |
---|
| 119 | (* ctx->size_func) (&width, &height, ctx->user_data); |
---|
| 120 | } |
---|
| 121 | else |
---|
| 122 | { |
---|
| 123 | new_width = width; |
---|
| 124 | new_height = height; |
---|
| 125 | |
---|
| 126 | /* apply the sizing function to acquire our new width and height. |
---|
| 127 | we'll scale this against the old values later */ |
---|
| 128 | if (ctx->size_func) |
---|
| 129 | (* ctx->size_func) (&new_width, &new_height, ctx->user_data); |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | /* set these here because % are relative to viewbox */ |
---|
| 133 | ctx->width = new_width; |
---|
| 134 | ctx->height = new_height; |
---|
| 135 | |
---|
| 136 | if (!has_vbox) |
---|
| 137 | { |
---|
| 138 | x_zoom = (width < 0 || new_width < 0) ? 1 : (double) new_width / width; |
---|
| 139 | y_zoom = (height < 0 || new_height < 0) ? 1 : (double) new_height / height; |
---|
| 140 | } |
---|
| 141 | else |
---|
| 142 | { |
---|
| 143 | #if 1 |
---|
| 144 | x_zoom = (width < 0 || new_width < 0) ? 1 : (double) width / new_width; |
---|
| 145 | y_zoom = (height < 0 || new_height < 0) ? 1 : (double) height / new_height; |
---|
| 146 | #else |
---|
| 147 | x_zoom = (width < 0 || new_width < 0) ? 1 : (double) new_width / width; |
---|
| 148 | y_zoom = (height < 0 || new_height < 0) ? 1 : (double) new_height / height; |
---|
[18351] | 149 | #endif |
---|
[18608] | 150 | |
---|
| 151 | /* reset these so that we get a properly sized SVG and not a huge one */ |
---|
| 152 | new_width = (width == -1 ? new_width : width); |
---|
| 153 | new_height = (height == -1 ? new_height : height); |
---|
| 154 | } |
---|
| 155 | |
---|
| 156 | /* Scale size of target pixbuf */ |
---|
| 157 | state = &ctx->state[ctx->n_state - 1]; |
---|
| 158 | art_affine_scale (state->affine, x_zoom, y_zoom); |
---|
| 159 | |
---|
| 160 | #if 0 |
---|
| 161 | if (vbox_x != 0. || vbox_y != 0.) |
---|
| 162 | { |
---|
| 163 | double affine[6]; |
---|
| 164 | art_affine_translate (affine, vbox_x, vbox_y); |
---|
| 165 | art_affine_multiply (state->affine, affine, state->affine); |
---|
| 166 | } |
---|
| 167 | #endif |
---|
| 168 | |
---|
| 169 | if (new_width < 0 || new_height < 0) |
---|
| 170 | { |
---|
| 171 | g_warning ("rsvg_start_svg: width and height not specified in the SVG, nor supplied by the size callback"); |
---|
| 172 | if (new_width < 0) new_width = 500; |
---|
| 173 | if (new_height < 0) new_height = 500; |
---|
| 174 | } |
---|
| 175 | |
---|
| 176 | if (new_width >= INT_MAX / 4) |
---|
| 177 | { |
---|
| 178 | /* FIXME: GError here? */ |
---|
| 179 | g_warning ("rsvg_start_svg: width too large"); |
---|
| 180 | return; |
---|
| 181 | } |
---|
| 182 | rowstride = (new_width * (has_alpha ? 4 : 3) + 3) & ~3; |
---|
| 183 | if (rowstride > INT_MAX / new_height) |
---|
| 184 | { |
---|
| 185 | /* FIXME: GError here? */ |
---|
| 186 | g_warning ("rsvg_start_svg: width too large"); |
---|
| 187 | return; |
---|
| 188 | } |
---|
| 189 | |
---|
| 190 | /* FIXME: Add GError here if size is too big. */ |
---|
| 191 | |
---|
| 192 | pixels = g_try_malloc (rowstride * new_height); |
---|
| 193 | if (pixels == NULL) |
---|
| 194 | { |
---|
| 195 | /* FIXME: GError here? */ |
---|
| 196 | g_warning ("rsvg_start_svg: dimensions too large"); |
---|
| 197 | return; |
---|
| 198 | } |
---|
| 199 | memset (pixels, has_alpha ? 0 : 255, rowstride * new_height); |
---|
| 200 | ctx->pixbuf = gdk_pixbuf_new_from_data (pixels, |
---|
| 201 | GDK_COLORSPACE_RGB, |
---|
| 202 | has_alpha, 8, |
---|
| 203 | new_width, new_height, |
---|
| 204 | rowstride, |
---|
| 205 | rsvg_pixmap_destroy, |
---|
| 206 | NULL); |
---|
| 207 | } |
---|
[17276] | 208 | } |
---|
| 209 | |
---|
| 210 | static void |
---|
[18608] | 211 | rsvg_start_g (RsvgHandle *ctx, const xmlChar **atts) |
---|
[17276] | 212 | { |
---|
[18608] | 213 | RsvgState *state = &ctx->state[ctx->n_state - 1]; |
---|
| 214 | const char * klazz = NULL, * id = NULL; |
---|
| 215 | int i; |
---|
| 216 | |
---|
| 217 | if (atts != NULL) |
---|
| 218 | { |
---|
| 219 | for (i = 0; atts[i] != NULL; i += 2) |
---|
| 220 | { |
---|
| 221 | if (!strcmp ((char *)atts[i], "class")) |
---|
| 222 | klazz = (const char *)atts[i + 1]; |
---|
| 223 | else if (!strcmp ((char *)atts[i], "id")) |
---|
| 224 | id = (const char *)atts[i + 1]; |
---|
| 225 | } |
---|
| 226 | } |
---|
| 227 | |
---|
| 228 | rsvg_parse_style_attrs (ctx, "g", klazz, id, atts); |
---|
| 229 | if (state->opacity != 0xff) |
---|
| 230 | rsvg_push_opacity_group (ctx); |
---|
[17276] | 231 | } |
---|
| 232 | |
---|
| 233 | static void |
---|
[18608] | 234 | rsvg_end_g (RsvgHandle *ctx) |
---|
[17276] | 235 | { |
---|
[18608] | 236 | RsvgState *state = &ctx->state[ctx->n_state - 1]; |
---|
| 237 | |
---|
| 238 | if (state->opacity != 0xff) |
---|
| 239 | rsvg_pop_opacity_group (ctx, state->opacity); |
---|
[17276] | 240 | } |
---|
| 241 | |
---|
[18608] | 242 | typedef struct _RsvgSaxHandlerDefs { |
---|
| 243 | RsvgSaxHandler super; |
---|
| 244 | RsvgHandle *ctx; |
---|
| 245 | } RsvgSaxHandlerDefs; |
---|
[17276] | 246 | |
---|
[18608] | 247 | typedef struct _RsvgSaxHandlerStyle { |
---|
| 248 | RsvgSaxHandler super; |
---|
| 249 | RsvgSaxHandlerDefs *parent; |
---|
| 250 | RsvgHandle *ctx; |
---|
| 251 | GString *style; |
---|
| 252 | } RsvgSaxHandlerStyle; |
---|
[17276] | 253 | |
---|
[18608] | 254 | typedef struct _RsvgSaxHandlerGstops { |
---|
| 255 | RsvgSaxHandler super; |
---|
| 256 | RsvgSaxHandlerDefs *parent; |
---|
| 257 | RsvgHandle *ctx; |
---|
| 258 | RsvgGradientStops *stops; |
---|
| 259 | const char * parent_tag; |
---|
| 260 | } RsvgSaxHandlerGstops; |
---|
[17276] | 261 | |
---|
| 262 | static void |
---|
[18608] | 263 | rsvg_gradient_stop_handler_free (RsvgSaxHandler *self) |
---|
[17276] | 264 | { |
---|
[18608] | 265 | g_free (self); |
---|
[17276] | 266 | } |
---|
| 267 | |
---|
| 268 | static void |
---|
[18608] | 269 | rsvg_gradient_stop_handler_start (RsvgSaxHandler *self, const xmlChar *name, |
---|
| 270 | const xmlChar **atts) |
---|
[17276] | 271 | { |
---|
[18608] | 272 | RsvgSaxHandlerGstops *z = (RsvgSaxHandlerGstops *)self; |
---|
| 273 | RsvgGradientStops *stops = z->stops; |
---|
| 274 | int i; |
---|
| 275 | double offset = 0; |
---|
| 276 | gboolean got_offset = FALSE; |
---|
| 277 | RsvgState state; |
---|
| 278 | int n_stop; |
---|
| 279 | |
---|
| 280 | if (strcmp ((char *)name, "stop")) |
---|
| 281 | { |
---|
| 282 | g_warning ("unexpected <%s> element in gradient\n", name); |
---|
| 283 | return; |
---|
| 284 | } |
---|
| 285 | |
---|
| 286 | rsvg_state_init (&state); |
---|
| 287 | |
---|
| 288 | if (atts != NULL) |
---|
| 289 | { |
---|
| 290 | for (i = 0; atts[i] != NULL; i += 2) |
---|
| 291 | { |
---|
| 292 | if (!strcmp ((char *)atts[i], "offset")) |
---|
| 293 | { |
---|
| 294 | /* either a number [0,1] or a percentage */ |
---|
| 295 | offset = rsvg_css_parse_normalized_length ((char *)atts[i + 1], z->ctx->dpi, 1., 0.); |
---|
| 296 | |
---|
| 297 | if (offset < 0.) |
---|
| 298 | offset = 0.; |
---|
| 299 | else if (offset > 1.) |
---|
| 300 | offset = 1.; |
---|
| 301 | |
---|
| 302 | got_offset = TRUE; |
---|
| 303 | } |
---|
| 304 | else if (!strcmp ((char *)atts[i], "style")) |
---|
| 305 | rsvg_parse_style (z->ctx, &state, (char *)atts[i + 1]); |
---|
| 306 | else if (rsvg_is_style_arg ((char *)atts[i])) |
---|
| 307 | rsvg_parse_style_pair (z->ctx, &state, |
---|
| 308 | (char *)atts[i], (char *)atts[i + 1]); |
---|
| 309 | } |
---|
| 310 | } |
---|
| 311 | |
---|
| 312 | rsvg_state_finalize (&state); |
---|
| 313 | |
---|
| 314 | if (!got_offset) |
---|
| 315 | { |
---|
| 316 | g_warning ("gradient stop must specify offset\n"); |
---|
| 317 | return; |
---|
| 318 | } |
---|
| 319 | |
---|
| 320 | n_stop = stops->n_stop++; |
---|
| 321 | if (n_stop == 0) |
---|
| 322 | stops->stop = g_new (RsvgGradientStop, 1); |
---|
| 323 | else if (!(n_stop & (n_stop - 1))) |
---|
| 324 | /* double the allocation if size is a power of two */ |
---|
| 325 | stops->stop = g_renew (RsvgGradientStop, stops->stop, n_stop << 1); |
---|
| 326 | stops->stop[n_stop].offset = offset; |
---|
| 327 | stops->stop[n_stop].rgba = (state.stop_color << 8) | state.stop_opacity; |
---|
[17276] | 328 | } |
---|
| 329 | |
---|
| 330 | static void |
---|
[18608] | 331 | rsvg_gradient_stop_handler_end (RsvgSaxHandler *self, const xmlChar *name) |
---|
[17276] | 332 | { |
---|
[18608] | 333 | RsvgSaxHandlerGstops *z = (RsvgSaxHandlerGstops *)self; |
---|
| 334 | RsvgHandle *ctx = z->ctx; |
---|
| 335 | |
---|
| 336 | if (!strcmp((char *)name, z->parent_tag)) |
---|
| 337 | { |
---|
| 338 | if (ctx->handler != NULL) |
---|
| 339 | { |
---|
| 340 | ctx->handler->free (ctx->handler); |
---|
| 341 | ctx->handler = &z->parent->super; |
---|
| 342 | } |
---|
| 343 | } |
---|
[17276] | 344 | } |
---|
| 345 | |
---|
[18608] | 346 | static RsvgSaxHandler * |
---|
| 347 | rsvg_gradient_stop_handler_new_clone (RsvgHandle *ctx, RsvgGradientStops *stops, |
---|
| 348 | const char * parent) |
---|
[17276] | 349 | { |
---|
[18608] | 350 | RsvgSaxHandlerGstops *gstops = g_new0 (RsvgSaxHandlerGstops, 1); |
---|
| 351 | |
---|
| 352 | gstops->super.free = rsvg_gradient_stop_handler_free; |
---|
| 353 | gstops->super.start_element = rsvg_gradient_stop_handler_start; |
---|
| 354 | gstops->super.end_element = rsvg_gradient_stop_handler_end; |
---|
| 355 | gstops->ctx = ctx; |
---|
| 356 | gstops->stops = stops; |
---|
| 357 | gstops->parent_tag = parent; |
---|
| 358 | |
---|
| 359 | gstops->parent = (RsvgSaxHandlerDefs*)ctx->handler; |
---|
| 360 | return &gstops->super; |
---|
[17276] | 361 | } |
---|
| 362 | |
---|
[18608] | 363 | static RsvgSaxHandler * |
---|
| 364 | rsvg_gradient_stop_handler_new (RsvgHandle *ctx, RsvgGradientStops **p_stops, |
---|
| 365 | const char * parent) |
---|
[17276] | 366 | { |
---|
[18608] | 367 | RsvgSaxHandlerGstops *gstops = g_new0 (RsvgSaxHandlerGstops, 1); |
---|
| 368 | RsvgGradientStops *stops = g_new (RsvgGradientStops, 1); |
---|
| 369 | |
---|
| 370 | gstops->super.free = rsvg_gradient_stop_handler_free; |
---|
| 371 | gstops->super.start_element = rsvg_gradient_stop_handler_start; |
---|
| 372 | gstops->super.end_element = rsvg_gradient_stop_handler_end; |
---|
| 373 | gstops->ctx = ctx; |
---|
| 374 | gstops->stops = stops; |
---|
| 375 | gstops->parent_tag = parent; |
---|
| 376 | |
---|
| 377 | stops->n_stop = 0; |
---|
| 378 | stops->stop = NULL; |
---|
| 379 | |
---|
| 380 | gstops->parent = (RsvgSaxHandlerDefs*)ctx->handler; |
---|
| 381 | *p_stops = stops; |
---|
| 382 | return &gstops->super; |
---|
[17276] | 383 | } |
---|
| 384 | |
---|
[18608] | 385 | /* exported to the paint server via rsvg-private.h */ |
---|
| 386 | void |
---|
| 387 | rsvg_linear_gradient_free (RsvgDefVal *self) |
---|
[17276] | 388 | { |
---|
[18608] | 389 | RsvgLinearGradient *z = (RsvgLinearGradient *)self; |
---|
| 390 | |
---|
| 391 | g_free (z->stops->stop); |
---|
| 392 | g_free (z->stops); |
---|
| 393 | g_free (self); |
---|
[17276] | 394 | } |
---|
| 395 | |
---|
[18608] | 396 | static void |
---|
| 397 | rsvg_start_linear_gradient (RsvgHandle *ctx, const xmlChar **atts) |
---|
[17276] | 398 | { |
---|
[18608] | 399 | RsvgState *state = &ctx->state[ctx->n_state - 1]; |
---|
| 400 | RsvgLinearGradient *grad = NULL; |
---|
| 401 | int i; |
---|
| 402 | const char *id = NULL; |
---|
| 403 | double x1 = 0., y1 = 0., x2 = 0., y2 = 0.; |
---|
| 404 | ArtGradientSpread spread = ART_GRADIENT_PAD; |
---|
| 405 | const char * xlink_href = NULL; |
---|
| 406 | gboolean got_x1, got_x2, got_y1, got_y2, got_spread, got_transform, cloned, shallow_cloned; |
---|
| 407 | double affine[6]; |
---|
[17276] | 408 | |
---|
[18608] | 409 | got_x1 = got_x2 = got_y1 = got_y2 = got_spread = got_transform = cloned = shallow_cloned = FALSE; |
---|
| 410 | |
---|
| 411 | /* 100% is the default */ |
---|
| 412 | x2 = rsvg_css_parse_normalized_length ("100%", ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 413 | |
---|
| 414 | /* todo: only handles numeric coordinates in gradientUnits = userSpace */ |
---|
| 415 | if (atts != NULL) |
---|
| 416 | { |
---|
| 417 | for (i = 0; atts[i] != NULL; i += 2) |
---|
| 418 | { |
---|
| 419 | if (!strcmp ((char *)atts[i], "id")) |
---|
| 420 | id = (const char *)atts[i + 1]; |
---|
| 421 | else if (!strcmp ((char *)atts[i], "x1")) { |
---|
| 422 | x1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 423 | got_x1 = TRUE; |
---|
| 424 | } |
---|
| 425 | else if (!strcmp ((char *)atts[i], "y1")) { |
---|
| 426 | y1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 427 | got_y1 = TRUE; |
---|
| 428 | } |
---|
| 429 | else if (!strcmp ((char *)atts[i], "x2")) { |
---|
| 430 | x2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 431 | got_x2 = TRUE; |
---|
| 432 | } |
---|
| 433 | else if (!strcmp ((char *)atts[i], "y2")) { |
---|
| 434 | y2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 435 | got_y2 = TRUE; |
---|
| 436 | } |
---|
| 437 | else if (!strcmp ((char *)atts[i], "spreadMethod")) |
---|
| 438 | { |
---|
| 439 | if (!strcmp ((char *)atts[i + 1], "pad")) { |
---|
| 440 | spread = ART_GRADIENT_PAD; |
---|
| 441 | got_spread = TRUE; |
---|
| 442 | } |
---|
| 443 | else if (!strcmp ((char *)atts[i + 1], "reflect")) { |
---|
| 444 | spread = ART_GRADIENT_REFLECT; |
---|
| 445 | got_spread = TRUE; |
---|
| 446 | } |
---|
| 447 | else if (!strcmp ((char *)atts[i + 1], "repeat")) { |
---|
| 448 | spread = ART_GRADIENT_REPEAT; |
---|
| 449 | got_spread = TRUE; |
---|
| 450 | } |
---|
| 451 | } |
---|
| 452 | else if (!strcmp ((char *)atts[i], "xlink:href")) |
---|
| 453 | xlink_href = (const char *)atts[i + 1]; |
---|
| 454 | else if (!strcmp ((char *)atts[i], "gradientTransform")) { |
---|
| 455 | got_transform = rsvg_parse_transform (affine, (const char *)atts[i + 1]); |
---|
| 456 | } |
---|
| 457 | } |
---|
| 458 | } |
---|
| 459 | |
---|
| 460 | if (xlink_href != NULL) |
---|
| 461 | { |
---|
| 462 | RsvgLinearGradient * parent = (RsvgLinearGradient*)rsvg_defs_lookup (ctx->defs, xlink_href+1); |
---|
| 463 | if (parent != NULL) |
---|
| 464 | { |
---|
| 465 | cloned = TRUE; |
---|
| 466 | grad = rsvg_clone_linear_gradient (parent, &shallow_cloned); |
---|
| 467 | ctx->handler = rsvg_gradient_stop_handler_new_clone (ctx, grad->stops, "linearGradient"); |
---|
| 468 | } |
---|
| 469 | } |
---|
| 470 | |
---|
| 471 | if (!cloned) |
---|
| 472 | { |
---|
| 473 | grad = g_new (RsvgLinearGradient, 1); |
---|
| 474 | grad->super.type = RSVG_DEF_LINGRAD; |
---|
| 475 | grad->super.free = rsvg_linear_gradient_free; |
---|
| 476 | ctx->handler = rsvg_gradient_stop_handler_new (ctx, &grad->stops, "linearGradient"); |
---|
| 477 | } |
---|
| 478 | |
---|
| 479 | rsvg_defs_set (ctx->defs, id, &grad->super); |
---|
| 480 | |
---|
| 481 | for (i = 0; i < 6; i++) |
---|
| 482 | grad->affine[i] = state->affine[i]; |
---|
[17276] | 483 | |
---|
[18608] | 484 | if (got_transform) |
---|
| 485 | art_affine_multiply (grad->affine, affine, grad->affine); |
---|
| 486 | |
---|
| 487 | /* state inherits parent/cloned information unless it's explicity gotten */ |
---|
| 488 | grad->x1 = (cloned && !got_x1) ? grad->x1 : x1; |
---|
| 489 | grad->y1 = (cloned && !got_y1) ? grad->y1 : y1; |
---|
| 490 | grad->x2 = (cloned && !got_x2) ? grad->x2 : x2; |
---|
| 491 | grad->y2 = (cloned && !got_y2) ? grad->y1 : y2; |
---|
| 492 | grad->spread = (cloned && !got_spread) ? grad->spread : spread; |
---|
[17276] | 493 | } |
---|
| 494 | |
---|
[18608] | 495 | /* exported to the paint server via rsvg-private.h */ |
---|
| 496 | void |
---|
| 497 | rsvg_radial_gradient_free (RsvgDefVal *self) |
---|
[17276] | 498 | { |
---|
[18608] | 499 | RsvgRadialGradient *z = (RsvgRadialGradient *)self; |
---|
| 500 | |
---|
| 501 | g_free (z->stops->stop); |
---|
| 502 | g_free (z->stops); |
---|
| 503 | g_free (self); |
---|
[17276] | 504 | } |
---|
| 505 | |
---|
| 506 | static void |
---|
[18608] | 507 | rsvg_start_radial_gradient (RsvgHandle *ctx, const xmlChar **atts, const char * tag) /* tag for conicalGradient */ |
---|
[17276] | 508 | { |
---|
[18608] | 509 | RsvgState *state = &ctx->state[ctx->n_state - 1]; |
---|
| 510 | RsvgRadialGradient *grad = NULL; |
---|
| 511 | int i; |
---|
| 512 | const char *id = NULL; |
---|
| 513 | double cx = 0., cy = 0., r = 0., fx = 0., fy = 0.; |
---|
| 514 | const char * xlink_href = NULL; |
---|
| 515 | ArtGradientSpread spread = ART_GRADIENT_PAD; |
---|
| 516 | gboolean got_cx, got_cy, got_r, got_fx, got_fy, got_spread, got_transform, cloned, shallow_cloned; |
---|
| 517 | double affine[6]; |
---|
| 518 | |
---|
| 519 | got_cx = got_cy = got_r = got_fx = got_fy = got_spread = got_transform = cloned = shallow_cloned = FALSE; |
---|
| 520 | |
---|
| 521 | /* setup defaults */ |
---|
| 522 | cx = rsvg_css_parse_normalized_length ("50%", ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 523 | cy = rsvg_css_parse_normalized_length ("50%", ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 524 | r = rsvg_css_parse_normalized_length ("50%", ctx->dpi, rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), state->font_size); |
---|
| 525 | |
---|
| 526 | /* todo: only handles numeric coordinates in gradientUnits = userSpace */ |
---|
| 527 | if (atts != NULL) |
---|
| 528 | { |
---|
| 529 | for (i = 0; atts[i] != NULL; i += 2) |
---|
| 530 | { |
---|
| 531 | if (!strcmp ((char *)atts[i], "id")) |
---|
| 532 | id = (const char *)atts[i + 1]; |
---|
| 533 | else if (!strcmp ((char *)atts[i], "cx")) { |
---|
| 534 | cx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 535 | got_cx = TRUE; |
---|
| 536 | } |
---|
| 537 | else if (!strcmp ((char *)atts[i], "cy")) { |
---|
| 538 | cy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 539 | got_cy = TRUE; |
---|
| 540 | } |
---|
| 541 | else if (!strcmp ((char *)atts[i], "r")) { |
---|
| 542 | r = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, |
---|
| 543 | rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), |
---|
| 544 | state->font_size); |
---|
| 545 | got_r = TRUE; |
---|
| 546 | } |
---|
| 547 | else if (!strcmp ((char *)atts[i], "fx")) { |
---|
| 548 | fx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 549 | got_fx = TRUE; |
---|
| 550 | } |
---|
| 551 | else if (!strcmp ((char *)atts[i], "fy")) { |
---|
| 552 | fy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 553 | got_fy = TRUE; |
---|
| 554 | } |
---|
| 555 | else if (!strcmp ((char *)atts[i], "xlink:href")) |
---|
| 556 | xlink_href = (const char *)atts[i + 1]; |
---|
| 557 | else if (!strcmp ((char *)atts[i], "gradientTransform")) { |
---|
| 558 | got_transform = rsvg_parse_transform (affine, (const char *)atts[i + 1]); |
---|
| 559 | } |
---|
| 560 | else if (!strcmp ((char *)atts[i], "spreadMethod")) |
---|
| 561 | { |
---|
| 562 | if (!strcmp ((char *)atts[i + 1], "pad")) { |
---|
| 563 | spread = ART_GRADIENT_PAD; |
---|
| 564 | got_spread = TRUE; |
---|
| 565 | } |
---|
| 566 | else if (!strcmp ((char *)atts[i + 1], "reflect")) { |
---|
| 567 | spread = ART_GRADIENT_REFLECT; |
---|
| 568 | got_spread = TRUE; |
---|
| 569 | } |
---|
| 570 | else if (!strcmp ((char *)atts[i + 1], "repeat")) { |
---|
| 571 | spread = ART_GRADIENT_REPEAT; |
---|
| 572 | got_spread = TRUE; |
---|
| 573 | } |
---|
| 574 | } |
---|
| 575 | } |
---|
| 576 | } |
---|
| 577 | |
---|
| 578 | if (xlink_href != NULL) |
---|
| 579 | { |
---|
| 580 | RsvgRadialGradient * parent = (RsvgRadialGradient*)rsvg_defs_lookup (ctx->defs, xlink_href+1); |
---|
| 581 | if (parent != NULL) |
---|
| 582 | { |
---|
| 583 | cloned = TRUE; |
---|
| 584 | grad = rsvg_clone_radial_gradient (parent, &shallow_cloned); |
---|
| 585 | ctx->handler = rsvg_gradient_stop_handler_new_clone (ctx, grad->stops, tag); |
---|
| 586 | } |
---|
[18274] | 587 | } |
---|
[18608] | 588 | if (!cloned) |
---|
| 589 | { |
---|
| 590 | grad = g_new (RsvgRadialGradient, 1); |
---|
| 591 | grad->super.type = RSVG_DEF_RADGRAD; |
---|
| 592 | grad->super.free = rsvg_radial_gradient_free; |
---|
| 593 | ctx->handler = rsvg_gradient_stop_handler_new (ctx, &grad->stops, tag); |
---|
| 594 | } |
---|
[18274] | 595 | |
---|
[18608] | 596 | if (!cloned || shallow_cloned) { |
---|
| 597 | if (!got_fx) { |
---|
| 598 | fx = cx; |
---|
| 599 | got_fx = TRUE; |
---|
| 600 | } |
---|
| 601 | if (!got_fy) { |
---|
| 602 | fy = cy; |
---|
| 603 | got_fy = TRUE; |
---|
| 604 | } |
---|
[17276] | 605 | } |
---|
[18608] | 606 | |
---|
| 607 | rsvg_defs_set (ctx->defs, id, &grad->super); |
---|
| 608 | |
---|
| 609 | for (i = 0; i < 6; i++) |
---|
| 610 | grad->affine[i] = state->affine[i]; |
---|
[17276] | 611 | |
---|
[18608] | 612 | if (got_transform) |
---|
| 613 | art_affine_multiply (grad->affine, affine, grad->affine); |
---|
| 614 | |
---|
| 615 | /* state inherits parent/cloned information unless it's explicity gotten */ |
---|
| 616 | grad->cx = (cloned && !got_cx) ? grad->cx : cx; |
---|
| 617 | grad->cy = (cloned && !got_cy) ? grad->cy : cy; |
---|
| 618 | grad->r = (cloned && !got_r) ? grad->r : r; |
---|
| 619 | grad->fx = (cloned && !got_fx) ? grad->fx : fx; |
---|
| 620 | grad->fy = (cloned && !got_fy) ? grad->fy : fy; |
---|
| 621 | grad->spread = (cloned && !got_spread) ? grad->spread : spread; |
---|
[17276] | 622 | } |
---|
| 623 | |
---|
[18608] | 624 | /* end gradients */ |
---|
| 625 | |
---|
[17276] | 626 | static void |
---|
[18608] | 627 | rsvg_style_handler_free (RsvgSaxHandler *self) |
---|
[18274] | 628 | { |
---|
[18608] | 629 | RsvgSaxHandlerStyle *z = (RsvgSaxHandlerStyle *)self; |
---|
| 630 | RsvgHandle *ctx = z->ctx; |
---|
| 631 | |
---|
| 632 | rsvg_parse_cssbuffer (ctx, z->style->str, z->style->len); |
---|
| 633 | |
---|
| 634 | g_string_free (z->style, TRUE); |
---|
| 635 | g_free (z); |
---|
[17276] | 636 | } |
---|
| 637 | |
---|
| 638 | static void |
---|
[18608] | 639 | rsvg_style_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) |
---|
[17276] | 640 | { |
---|
[18608] | 641 | RsvgSaxHandlerStyle *z = (RsvgSaxHandlerStyle *)self; |
---|
| 642 | g_string_append_len (z->style, (const char *)ch, len); |
---|
[17276] | 643 | } |
---|
| 644 | |
---|
| 645 | static void |
---|
[18608] | 646 | rsvg_style_handler_start (RsvgSaxHandler *self, const xmlChar *name, |
---|
| 647 | const xmlChar **atts) |
---|
[17276] | 648 | { |
---|
[18274] | 649 | } |
---|
| 650 | |
---|
| 651 | static void |
---|
[18608] | 652 | rsvg_style_handler_end (RsvgSaxHandler *self, const xmlChar *name) |
---|
[17276] | 653 | { |
---|
[18608] | 654 | RsvgSaxHandlerStyle *z = (RsvgSaxHandlerStyle *)self; |
---|
| 655 | RsvgHandle *ctx = z->ctx; |
---|
| 656 | |
---|
| 657 | if (!strcmp ((char *)name, "style")) |
---|
| 658 | { |
---|
| 659 | if (ctx->handler != NULL) |
---|
| 660 | { |
---|
| 661 | ctx->handler->free (ctx->handler); |
---|
| 662 | ctx->handler = &z->parent->super; |
---|
| 663 | } |
---|
| 664 | } |
---|
[17276] | 665 | } |
---|
| 666 | |
---|
[18351] | 667 | static void |
---|
[18608] | 668 | rsvg_start_style (RsvgHandle *ctx, const xmlChar **atts) |
---|
[18351] | 669 | { |
---|
[18608] | 670 | RsvgSaxHandlerStyle *handler = g_new0 (RsvgSaxHandlerStyle, 1); |
---|
| 671 | |
---|
| 672 | handler->super.free = rsvg_style_handler_free; |
---|
| 673 | handler->super.characters = rsvg_style_handler_characters; |
---|
| 674 | handler->super.start_element = rsvg_style_handler_start; |
---|
| 675 | handler->super.end_element = rsvg_style_handler_end; |
---|
| 676 | handler->ctx = ctx; |
---|
| 677 | |
---|
| 678 | handler->style = g_string_new (NULL); |
---|
| 679 | |
---|
| 680 | handler->parent = (RsvgSaxHandlerDefs*)ctx->handler; |
---|
| 681 | ctx->handler = &handler->super; |
---|
[18351] | 682 | } |
---|
[17276] | 683 | |
---|
[18608] | 684 | /* */ |
---|
[18351] | 685 | |
---|
[17276] | 686 | static void |
---|
[18608] | 687 | rsvg_defs_handler_free (RsvgSaxHandler *self) |
---|
[17276] | 688 | { |
---|
[18608] | 689 | g_free (self); |
---|
[17276] | 690 | } |
---|
| 691 | |
---|
| 692 | static void |
---|
[18608] | 693 | rsvg_defs_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) |
---|
[17276] | 694 | { |
---|
| 695 | } |
---|
| 696 | |
---|
| 697 | static void |
---|
[18608] | 698 | rsvg_defs_handler_start (RsvgSaxHandler *self, const xmlChar *name, |
---|
| 699 | const xmlChar **atts) |
---|
[17276] | 700 | { |
---|
[18608] | 701 | RsvgSaxHandlerDefs *z = (RsvgSaxHandlerDefs *)self; |
---|
| 702 | RsvgHandle *ctx = z->ctx; |
---|
| 703 | |
---|
| 704 | /* push the state stack */ |
---|
| 705 | if (ctx->n_state == ctx->n_state_max) |
---|
| 706 | ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1); |
---|
| 707 | if (ctx->n_state) |
---|
| 708 | rsvg_state_clone (&ctx->state[ctx->n_state], |
---|
| 709 | &ctx->state[ctx->n_state - 1]); |
---|
| 710 | else |
---|
| 711 | rsvg_state_init (ctx->state); |
---|
| 712 | ctx->n_state++; |
---|
[17276] | 713 | |
---|
[18608] | 714 | /** |
---|
| 715 | * conicalGradient isn't in the SVG spec and I'm not sure exactly what it does. libart definitely |
---|
| 716 | * has no analogue. But it does seem similar enough to a radialGradient that i'd rather get the |
---|
| 717 | * onscreen representation of the colour wrong than not have any colour displayed whatsoever |
---|
| 718 | */ |
---|
[17276] | 719 | |
---|
[18608] | 720 | if (!strcmp ((char *)name, "linearGradient")) |
---|
| 721 | rsvg_start_linear_gradient (ctx, atts); |
---|
| 722 | else if (!strcmp ((char *)name, "radialGradient")) |
---|
| 723 | rsvg_start_radial_gradient (ctx, atts, "radialGradient"); |
---|
| 724 | else if (!strcmp((char *)name, "conicalGradient")) |
---|
| 725 | rsvg_start_radial_gradient (ctx, atts, "conicalGradient"); |
---|
| 726 | else if (!strcmp ((char *)name, "style")) |
---|
| 727 | rsvg_start_style (ctx, atts); |
---|
[17276] | 728 | } |
---|
| 729 | |
---|
| 730 | static void |
---|
[18608] | 731 | rsvg_defs_handler_end (RsvgSaxHandler *self, const xmlChar *name) |
---|
[17276] | 732 | { |
---|
[18608] | 733 | RsvgSaxHandlerDefs *z = (RsvgSaxHandlerDefs *)self; |
---|
| 734 | RsvgHandle *ctx = z->ctx; |
---|
| 735 | |
---|
| 736 | if (!strcmp((char *)name, "defs")) |
---|
| 737 | { |
---|
| 738 | if (ctx->handler != NULL) |
---|
| 739 | { |
---|
| 740 | ctx->handler->free (ctx->handler); |
---|
| 741 | ctx->handler = NULL; |
---|
| 742 | } |
---|
| 743 | } |
---|
| 744 | |
---|
| 745 | /* pop the state stack */ |
---|
| 746 | ctx->n_state--; |
---|
| 747 | rsvg_state_finalize (&ctx->state[ctx->n_state]); |
---|
[17276] | 748 | } |
---|
| 749 | |
---|
| 750 | static void |
---|
[18608] | 751 | rsvg_start_defs (RsvgHandle *ctx, const xmlChar **atts) |
---|
[17276] | 752 | { |
---|
[18608] | 753 | RsvgSaxHandlerDefs *handler = g_new0 (RsvgSaxHandlerDefs, 1); |
---|
| 754 | |
---|
| 755 | handler->super.free = rsvg_defs_handler_free; |
---|
| 756 | handler->super.characters = rsvg_defs_handler_characters; |
---|
| 757 | handler->super.start_element = rsvg_defs_handler_start; |
---|
| 758 | handler->super.end_element = rsvg_defs_handler_end; |
---|
| 759 | handler->ctx = ctx; |
---|
| 760 | |
---|
| 761 | ctx->handler = &handler->super; |
---|
[17276] | 762 | } |
---|
| 763 | |
---|
[18608] | 764 | /* end defs */ |
---|
[17276] | 765 | |
---|
| 766 | static void |
---|
[18351] | 767 | rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts) |
---|
[17276] | 768 | { |
---|
[18608] | 769 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
[17276] | 770 | |
---|
[18608] | 771 | if (ctx->handler) |
---|
| 772 | { |
---|
| 773 | ctx->handler_nest++; |
---|
| 774 | if (ctx->handler->start_element != NULL) |
---|
| 775 | ctx->handler->start_element (ctx->handler, name, atts); |
---|
| 776 | } |
---|
| 777 | else |
---|
| 778 | { |
---|
| 779 | /* push the state stack */ |
---|
| 780 | if (ctx->n_state == ctx->n_state_max) |
---|
| 781 | ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1); |
---|
| 782 | if (ctx->n_state) |
---|
| 783 | rsvg_state_clone (&ctx->state[ctx->n_state], |
---|
| 784 | &ctx->state[ctx->n_state - 1]); |
---|
| 785 | else |
---|
| 786 | rsvg_state_init (ctx->state); |
---|
| 787 | ctx->n_state++; |
---|
| 788 | |
---|
| 789 | if (!strcmp ((char *)name, "svg")) |
---|
| 790 | rsvg_start_svg (ctx, atts); |
---|
| 791 | else if (!strcmp ((char *)name, "g")) |
---|
| 792 | rsvg_start_g (ctx, atts); |
---|
| 793 | else if (!strcmp ((char *)name, "path")) |
---|
| 794 | rsvg_start_path (ctx, atts); |
---|
| 795 | else if (!strcmp ((char *)name, "text")) |
---|
| 796 | rsvg_start_text (ctx, atts); |
---|
| 797 | else if (!strcmp ((char *)name, "image")) |
---|
| 798 | rsvg_start_image (ctx, atts); |
---|
| 799 | else if (!strcmp ((char *)name, "line")) |
---|
| 800 | rsvg_start_line (ctx, atts); |
---|
| 801 | else if (!strcmp ((char *)name, "rect")) |
---|
| 802 | rsvg_start_rect (ctx, atts); |
---|
| 803 | else if (!strcmp ((char *)name, "circle")) |
---|
| 804 | rsvg_start_circle (ctx, atts); |
---|
| 805 | else if (!strcmp ((char *)name, "ellipse")) |
---|
| 806 | rsvg_start_ellipse (ctx, atts); |
---|
| 807 | else if (!strcmp ((char *)name, "defs")) |
---|
| 808 | rsvg_start_defs (ctx, atts); |
---|
| 809 | else if (!strcmp ((char *)name, "polygon")) |
---|
| 810 | rsvg_start_polygon (ctx, atts); |
---|
| 811 | else if (!strcmp ((char *)name, "polyline")) |
---|
| 812 | rsvg_start_polyline (ctx, atts); |
---|
| 813 | |
---|
| 814 | /* see conicalGradient discussion above */ |
---|
| 815 | else if (!strcmp ((char *)name, "linearGradient")) |
---|
| 816 | rsvg_start_linear_gradient (ctx, atts); |
---|
| 817 | else if (!strcmp ((char *)name, "radialGradient")) |
---|
| 818 | rsvg_start_radial_gradient (ctx, atts, "radialGradient"); |
---|
| 819 | else if (!strcmp ((char *)name, "conicalGradient")) |
---|
| 820 | rsvg_start_radial_gradient (ctx, atts, "conicalGradient"); |
---|
[17276] | 821 | } |
---|
| 822 | } |
---|
| 823 | |
---|
| 824 | static void |
---|
| 825 | rsvg_end_element (void *data, const xmlChar *name) |
---|
| 826 | { |
---|
[18608] | 827 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
| 828 | |
---|
| 829 | if (ctx->handler_nest > 0) |
---|
| 830 | { |
---|
| 831 | if (ctx->handler->end_element != NULL) |
---|
| 832 | ctx->handler->end_element (ctx->handler, name); |
---|
| 833 | ctx->handler_nest--; |
---|
| 834 | } |
---|
| 835 | else |
---|
| 836 | { |
---|
| 837 | if (ctx->handler != NULL) |
---|
| 838 | { |
---|
| 839 | ctx->handler->free (ctx->handler); |
---|
| 840 | ctx->handler = NULL; |
---|
| 841 | } |
---|
[17276] | 842 | |
---|
[18608] | 843 | if (!strcmp ((char *)name, "g")) |
---|
| 844 | rsvg_end_g (ctx); |
---|
| 845 | |
---|
| 846 | /* pop the state stack */ |
---|
| 847 | ctx->n_state--; |
---|
| 848 | rsvg_state_finalize (&ctx->state[ctx->n_state]); |
---|
| 849 | } |
---|
[17276] | 850 | } |
---|
| 851 | |
---|
| 852 | static void |
---|
| 853 | rsvg_characters (void *data, const xmlChar *ch, int len) |
---|
| 854 | { |
---|
[18608] | 855 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
| 856 | |
---|
| 857 | if (ctx->handler && ctx->handler->characters != NULL) |
---|
| 858 | ctx->handler->characters (ctx->handler, ch, len); |
---|
[17276] | 859 | } |
---|
| 860 | |
---|
| 861 | static xmlEntityPtr |
---|
| 862 | rsvg_get_entity (void *data, const xmlChar *name) |
---|
| 863 | { |
---|
[18608] | 864 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
| 865 | |
---|
| 866 | return (xmlEntityPtr)g_hash_table_lookup (ctx->entities, name); |
---|
[17276] | 867 | } |
---|
| 868 | |
---|
| 869 | static void |
---|
| 870 | rsvg_entity_decl (void *data, const xmlChar *name, int type, |
---|
[18608] | 871 | const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) |
---|
[17276] | 872 | { |
---|
[18608] | 873 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
| 874 | GHashTable *entities = ctx->entities; |
---|
| 875 | xmlEntityPtr entity; |
---|
| 876 | char *dupname; |
---|
[17276] | 877 | |
---|
[18608] | 878 | entity = g_new0 (xmlEntity, 1); |
---|
| 879 | entity->type = type; |
---|
| 880 | entity->length = strlen (name); |
---|
| 881 | dupname = g_strdup (name); |
---|
| 882 | entity->name = dupname; |
---|
| 883 | entity->ExternalID = g_strdup (publicId); |
---|
| 884 | entity->SystemID = g_strdup (systemId); |
---|
| 885 | if (content) |
---|
| 886 | { |
---|
| 887 | entity->content = xmlMemStrdup (content); |
---|
| 888 | entity->length = strlen (content); |
---|
| 889 | } |
---|
| 890 | g_hash_table_insert (entities, dupname, entity); |
---|
[17276] | 891 | } |
---|
| 892 | |
---|
[18274] | 893 | static void |
---|
| 894 | rsvg_error_cb (void *data, const char *msg, ...) |
---|
| 895 | { |
---|
| 896 | va_list args; |
---|
| 897 | |
---|
| 898 | va_start (args, msg); |
---|
| 899 | vfprintf (stderr, msg, args); |
---|
| 900 | va_end (args); |
---|
| 901 | } |
---|
| 902 | |
---|
[17276] | 903 | static xmlSAXHandler rsvgSAXHandlerStruct = { |
---|
| 904 | NULL, /* internalSubset */ |
---|
| 905 | NULL, /* isStandalone */ |
---|
| 906 | NULL, /* hasInternalSubset */ |
---|
| 907 | NULL, /* hasExternalSubset */ |
---|
| 908 | NULL, /* resolveEntity */ |
---|
| 909 | rsvg_get_entity, /* getEntity */ |
---|
| 910 | rsvg_entity_decl, /* entityDecl */ |
---|
| 911 | NULL, /* notationDecl */ |
---|
| 912 | NULL, /* attributeDecl */ |
---|
| 913 | NULL, /* elementDecl */ |
---|
| 914 | NULL, /* unparsedEntityDecl */ |
---|
| 915 | NULL, /* setDocumentLocator */ |
---|
| 916 | NULL, /* startDocument */ |
---|
| 917 | NULL, /* endDocument */ |
---|
| 918 | rsvg_start_element, /* startElement */ |
---|
| 919 | rsvg_end_element, /* endElement */ |
---|
| 920 | NULL, /* reference */ |
---|
| 921 | rsvg_characters, /* characters */ |
---|
| 922 | NULL, /* ignorableWhitespace */ |
---|
| 923 | NULL, /* processingInstruction */ |
---|
| 924 | NULL, /* comment */ |
---|
| 925 | NULL, /* xmlParserWarning */ |
---|
[18274] | 926 | rsvg_error_cb, /* xmlParserError */ |
---|
| 927 | rsvg_error_cb, /* xmlParserFatalError */ |
---|
[17276] | 928 | NULL, /* getParameterEntity */ |
---|
[18608] | 929 | rsvg_characters, /* cdataCallback */ |
---|
| 930 | NULL /* */ |
---|
[17276] | 931 | }; |
---|
| 932 | |
---|
[18274] | 933 | GQuark |
---|
| 934 | rsvg_error_quark (void) |
---|
| 935 | { |
---|
[18608] | 936 | static GQuark q = 0; |
---|
| 937 | if (q == 0) |
---|
| 938 | q = g_quark_from_static_string ("rsvg-error-quark"); |
---|
| 939 | |
---|
| 940 | return q; |
---|
[18274] | 941 | } |
---|
| 942 | |
---|
| 943 | /** |
---|
| 944 | * rsvg_handle_new: |
---|
| 945 | * @void: |
---|
| 946 | * |
---|
| 947 | * Returns a new rsvg handle. Must be freed with @rsvg_handle_free. This |
---|
| 948 | * handle can be used for dynamically loading an image. You need to feed it |
---|
| 949 | * data using @rsvg_handle_write, then call @rsvg_handle_close when done. No |
---|
| 950 | * more than one image can be loaded with one handle. |
---|
| 951 | * |
---|
| 952 | * Return value: A new #RsvgHandle |
---|
| 953 | **/ |
---|
| 954 | RsvgHandle * |
---|
| 955 | rsvg_handle_new (void) |
---|
| 956 | { |
---|
[18608] | 957 | RsvgHandle *handle; |
---|
| 958 | |
---|
| 959 | handle = g_new0 (RsvgHandle, 1); |
---|
| 960 | handle->n_state = 0; |
---|
| 961 | handle->n_state_max = 16; |
---|
| 962 | handle->state = g_new (RsvgState, handle->n_state_max); |
---|
| 963 | handle->defs = rsvg_defs_new (); |
---|
| 964 | handle->handler_nest = 0; |
---|
| 965 | handle->entities = g_hash_table_new (g_str_hash, g_str_equal); |
---|
| 966 | handle->dpi = internal_dpi; |
---|
| 967 | |
---|
| 968 | handle->css_props = g_hash_table_new_full (g_str_hash, g_str_equal, |
---|
| 969 | g_free, g_free); |
---|
| 970 | |
---|
| 971 | handle->ctxt = NULL; |
---|
| 972 | |
---|
| 973 | return handle; |
---|
| 974 | } |
---|
[18274] | 975 | |
---|
[18608] | 976 | /** |
---|
| 977 | * rsvg_set_default_dpi |
---|
| 978 | * @dpi: Dots Per Inch (aka Pixels Per Inch) |
---|
| 979 | * |
---|
| 980 | * Sets the DPI for the all future outgoing pixbufs. Common values are |
---|
| 981 | * 72, 90, and 300 DPI. Passing a number <= 0 to #dpi will |
---|
| 982 | * reset the DPI to whatever the default value happens to be. |
---|
| 983 | */ |
---|
| 984 | void |
---|
| 985 | rsvg_set_default_dpi (double dpi) |
---|
| 986 | { |
---|
| 987 | if (dpi <= 0.) |
---|
| 988 | internal_dpi = RSVG_DEFAULT_DPI; |
---|
| 989 | else |
---|
| 990 | internal_dpi = dpi; |
---|
| 991 | } |
---|
[18274] | 992 | |
---|
[18608] | 993 | /** |
---|
| 994 | * rsvg_handle_set_dpi |
---|
| 995 | * @handle: An #RsvgHandle |
---|
| 996 | * @dpi: Dots Per Inch (aka Pixels Per Inch) |
---|
| 997 | * |
---|
| 998 | * Sets the DPI for the outgoing pixbuf. Common values are |
---|
| 999 | * 72, 90, and 300 DPI. Passing a number <= 0 to #dpi will |
---|
| 1000 | * reset the DPI to whatever the default value happens to be. |
---|
| 1001 | */ |
---|
| 1002 | void |
---|
| 1003 | rsvg_handle_set_dpi (RsvgHandle * handle, double dpi) |
---|
| 1004 | { |
---|
| 1005 | g_return_if_fail (handle != NULL); |
---|
| 1006 | |
---|
| 1007 | if (dpi <= 0.) |
---|
| 1008 | handle->dpi = internal_dpi; |
---|
| 1009 | else |
---|
| 1010 | handle->dpi = dpi; |
---|
[18274] | 1011 | } |
---|
| 1012 | |
---|
| 1013 | /** |
---|
| 1014 | * rsvg_handle_set_size_callback: |
---|
| 1015 | * @handle: An #RsvgHandle |
---|
| 1016 | * @size_func: A sizing function, or %NULL |
---|
| 1017 | * @user_data: User data to pass to @size_func, or %NULL |
---|
| 1018 | * @user_data_destroy: Destroy function for @user_data, or %NULL |
---|
| 1019 | * |
---|
| 1020 | * Sets the sizing function for the @handle. This function is called right |
---|
| 1021 | * after the size of the image has been loaded. The size of the image is passed |
---|
| 1022 | * in to the function, which may then modify these values to set the real size |
---|
| 1023 | * of the generated pixbuf. If the image has no associated size, then the size |
---|
| 1024 | * arguments are set to -1. |
---|
| 1025 | **/ |
---|
| 1026 | void |
---|
| 1027 | rsvg_handle_set_size_callback (RsvgHandle *handle, |
---|
[18608] | 1028 | RsvgSizeFunc size_func, |
---|
| 1029 | gpointer user_data, |
---|
| 1030 | GDestroyNotify user_data_destroy) |
---|
[18274] | 1031 | { |
---|
[18608] | 1032 | g_return_if_fail (handle != NULL); |
---|
| 1033 | |
---|
| 1034 | if (handle->user_data_destroy) |
---|
| 1035 | (* handle->user_data_destroy) (handle->user_data); |
---|
| 1036 | |
---|
| 1037 | handle->size_func = size_func; |
---|
| 1038 | handle->user_data = user_data; |
---|
| 1039 | handle->user_data_destroy = user_data_destroy; |
---|
[18274] | 1040 | } |
---|
| 1041 | |
---|
| 1042 | /** |
---|
| 1043 | * rsvg_handle_write: |
---|
| 1044 | * @handle: An #RsvgHandle |
---|
| 1045 | * @buf: Pointer to svg data |
---|
| 1046 | * @count: length of the @buf buffer in bytes |
---|
| 1047 | * @error: return location for errors |
---|
| 1048 | * |
---|
| 1049 | * Loads the next @count bytes of the image. This will return #TRUE if the data |
---|
| 1050 | * was loaded successful, and #FALSE if an error occurred. In the latter case, |
---|
| 1051 | * the loader will be closed, and will not accept further writes. If FALSE is |
---|
| 1052 | * returned, @error will be set to an error from the #RSVG_ERROR domain. |
---|
| 1053 | * |
---|
| 1054 | * Return value: #TRUE if the write was successful, or #FALSE if there was an |
---|
| 1055 | * error. |
---|
| 1056 | **/ |
---|
| 1057 | gboolean |
---|
| 1058 | rsvg_handle_write (RsvgHandle *handle, |
---|
[18608] | 1059 | const guchar *buf, |
---|
| 1060 | gsize count, |
---|
| 1061 | GError **error) |
---|
[18274] | 1062 | { |
---|
[18608] | 1063 | GError *real_error; |
---|
| 1064 | g_return_val_if_fail (handle != NULL, FALSE); |
---|
| 1065 | |
---|
| 1066 | handle->error = &real_error; |
---|
| 1067 | if (handle->ctxt == NULL) |
---|
| 1068 | { |
---|
| 1069 | handle->ctxt = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, handle, NULL, 0, NULL); |
---|
| 1070 | handle->ctxt->replaceEntities = TRUE; |
---|
| 1071 | } |
---|
| 1072 | |
---|
| 1073 | xmlParseChunk (handle->ctxt, buf, count, 0); |
---|
| 1074 | |
---|
| 1075 | handle->error = NULL; |
---|
| 1076 | /* FIXME: Error handling not implemented. */ |
---|
| 1077 | /* if (*real_error != NULL) |
---|
| 1078 | { |
---|
| 1079 | g_propagate_error (error, real_error); |
---|
| 1080 | return FALSE; |
---|
| 1081 | }*/ |
---|
[18274] | 1082 | return TRUE; |
---|
| 1083 | } |
---|
| 1084 | |
---|
| 1085 | /** |
---|
| 1086 | * rsvg_handle_close: |
---|
| 1087 | * @handle: An #RsvgHandle |
---|
| 1088 | * |
---|
| 1089 | * Closes @handle, to indicate that loading the image is complete. This will |
---|
| 1090 | * return #TRUE if the loader closed successfully. Note that @handle isn't |
---|
| 1091 | * freed until @rsvg_handle_free is called. |
---|
| 1092 | * |
---|
| 1093 | * Return value: #TRUE if the loader closed successfully, or #FALSE if there was |
---|
| 1094 | * an error. |
---|
| 1095 | **/ |
---|
| 1096 | gboolean |
---|
| 1097 | rsvg_handle_close (RsvgHandle *handle, |
---|
[18608] | 1098 | GError **error) |
---|
[18274] | 1099 | { |
---|
[18608] | 1100 | gchar chars[1] = { '\0' }; |
---|
| 1101 | GError *real_error; |
---|
| 1102 | |
---|
| 1103 | handle->error = &real_error; |
---|
| 1104 | |
---|
| 1105 | if (handle->ctxt != NULL) |
---|
| 1106 | { |
---|
| 1107 | xmlParseChunk (handle->ctxt, chars, 1, TRUE); |
---|
| 1108 | xmlFreeParserCtxt (handle->ctxt); |
---|
| 1109 | } |
---|
[18274] | 1110 | |
---|
[18608] | 1111 | /* FIXME: Error handling not implemented. */ |
---|
| 1112 | /* |
---|
| 1113 | if (real_error != NULL) |
---|
| 1114 | { |
---|
[18274] | 1115 | g_propagate_error (error, real_error); |
---|
| 1116 | return FALSE; |
---|
| 1117 | }*/ |
---|
[18608] | 1118 | return TRUE; |
---|
[17276] | 1119 | } |
---|
| 1120 | |
---|
[18274] | 1121 | /** |
---|
| 1122 | * rsvg_handle_get_pixbuf: |
---|
| 1123 | * @handle: An #RsvgHandle |
---|
| 1124 | * |
---|
| 1125 | * Returns the pixbuf loaded by #handle. The pixbuf returned will be reffed, so |
---|
| 1126 | * the caller of this function must assume that ref. If insufficient data has |
---|
| 1127 | * been read to create the pixbuf, or an error occurred in loading, then %NULL |
---|
| 1128 | * will be returned. Note that the pixbuf may not be complete until |
---|
| 1129 | * @rsvg_handle_close has been called. |
---|
| 1130 | * |
---|
| 1131 | * Return value: the pixbuf loaded by #handle, or %NULL. |
---|
| 1132 | **/ |
---|
| 1133 | GdkPixbuf * |
---|
| 1134 | rsvg_handle_get_pixbuf (RsvgHandle *handle) |
---|
| 1135 | { |
---|
[18608] | 1136 | g_return_val_if_fail (handle != NULL, NULL); |
---|
| 1137 | |
---|
| 1138 | if (handle->pixbuf) |
---|
| 1139 | return g_object_ref (handle->pixbuf); |
---|
[18274] | 1140 | |
---|
[18608] | 1141 | return NULL; |
---|
[18274] | 1142 | } |
---|
| 1143 | |
---|
| 1144 | /** |
---|
| 1145 | * rsvg_handle_free: |
---|
| 1146 | * @handle: An #RsvgHandle |
---|
| 1147 | * |
---|
| 1148 | * Frees #handle. |
---|
| 1149 | **/ |
---|
| 1150 | void |
---|
| 1151 | rsvg_handle_free (RsvgHandle *handle) |
---|
| 1152 | { |
---|
[18608] | 1153 | int i; |
---|
[18274] | 1154 | |
---|
[18608] | 1155 | if (handle->pango_context != NULL) |
---|
| 1156 | g_object_unref (handle->pango_context); |
---|
| 1157 | rsvg_defs_free (handle->defs); |
---|
| 1158 | |
---|
| 1159 | for (i = 0; i < handle->n_state; i++) |
---|
| 1160 | rsvg_state_finalize (&handle->state[i]); |
---|
| 1161 | g_free (handle->state); |
---|
| 1162 | |
---|
| 1163 | g_hash_table_foreach (handle->entities, rsvg_ctx_free_helper, NULL); |
---|
| 1164 | g_hash_table_destroy (handle->entities); |
---|
| 1165 | |
---|
| 1166 | g_hash_table_destroy (handle->css_props); |
---|
| 1167 | |
---|
| 1168 | if (handle->user_data_destroy) |
---|
| 1169 | (* handle->user_data_destroy) (handle->user_data); |
---|
| 1170 | if (handle->pixbuf) |
---|
| 1171 | g_object_unref (handle->pixbuf); |
---|
| 1172 | g_free (handle); |
---|
[17276] | 1173 | } |
---|
| 1174 | |
---|