[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; |
---|
[18804] | 87 | gboolean has_vbox = FALSE; |
---|
[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 | { |
---|
[18804] | 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; |
---|
[18608] | 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 | } |
---|
[18804] | 155 | |
---|
[18608] | 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; |
---|
[18804] | 406 | gboolean obj_bbox = TRUE; |
---|
| 407 | gboolean got_x1, got_x2, got_y1, got_y2, got_spread, got_transform, got_bbox, cloned, shallow_cloned; |
---|
[18608] | 408 | double affine[6]; |
---|
[17276] | 409 | |
---|
[18804] | 410 | got_x1 = got_x2 = got_y1 = got_y2 = got_spread = got_transform = got_bbox = cloned = shallow_cloned = FALSE; |
---|
| 411 | |
---|
[18608] | 412 | if (atts != NULL) |
---|
| 413 | { |
---|
| 414 | for (i = 0; atts[i] != NULL; i += 2) |
---|
| 415 | { |
---|
| 416 | if (!strcmp ((char *)atts[i], "id")) |
---|
| 417 | id = (const char *)atts[i + 1]; |
---|
| 418 | else if (!strcmp ((char *)atts[i], "x1")) { |
---|
| 419 | x1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 420 | got_x1 = TRUE; |
---|
| 421 | } |
---|
| 422 | else if (!strcmp ((char *)atts[i], "y1")) { |
---|
| 423 | y1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 424 | got_y1 = TRUE; |
---|
| 425 | } |
---|
| 426 | else if (!strcmp ((char *)atts[i], "x2")) { |
---|
| 427 | x2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 428 | got_x2 = TRUE; |
---|
| 429 | } |
---|
| 430 | else if (!strcmp ((char *)atts[i], "y2")) { |
---|
| 431 | y2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 432 | got_y2 = TRUE; |
---|
| 433 | } |
---|
| 434 | else if (!strcmp ((char *)atts[i], "spreadMethod")) |
---|
| 435 | { |
---|
| 436 | if (!strcmp ((char *)atts[i + 1], "pad")) { |
---|
| 437 | spread = ART_GRADIENT_PAD; |
---|
| 438 | got_spread = TRUE; |
---|
| 439 | } |
---|
| 440 | else if (!strcmp ((char *)atts[i + 1], "reflect")) { |
---|
| 441 | spread = ART_GRADIENT_REFLECT; |
---|
| 442 | got_spread = TRUE; |
---|
| 443 | } |
---|
| 444 | else if (!strcmp ((char *)atts[i + 1], "repeat")) { |
---|
| 445 | spread = ART_GRADIENT_REPEAT; |
---|
| 446 | got_spread = TRUE; |
---|
| 447 | } |
---|
| 448 | } |
---|
| 449 | else if (!strcmp ((char *)atts[i], "xlink:href")) |
---|
| 450 | xlink_href = (const char *)atts[i + 1]; |
---|
[18804] | 451 | else if (!strcmp ((char *)atts[i], "gradientTransform")) |
---|
[18608] | 452 | got_transform = rsvg_parse_transform (affine, (const char *)atts[i + 1]); |
---|
[18804] | 453 | else if (!strcmp ((char *)atts[i], "gradientUnits")) { |
---|
| 454 | if (!strcmp ((char *)atts[i+1], "userSpaceOnUse")) |
---|
| 455 | obj_bbox = FALSE; |
---|
| 456 | got_bbox = TRUE; |
---|
[18608] | 457 | } |
---|
| 458 | } |
---|
| 459 | } |
---|
[18804] | 460 | |
---|
| 461 | /* set up 100% as the default if not gotten */ |
---|
| 462 | if (!got_x2) { |
---|
| 463 | if (obj_bbox) |
---|
| 464 | x2 = 1.0; |
---|
| 465 | else |
---|
| 466 | x2 = rsvg_css_parse_normalized_length ("100%", ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 467 | } |
---|
| 468 | |
---|
[18608] | 469 | if (xlink_href != NULL) |
---|
| 470 | { |
---|
| 471 | RsvgLinearGradient * parent = (RsvgLinearGradient*)rsvg_defs_lookup (ctx->defs, xlink_href+1); |
---|
| 472 | if (parent != NULL) |
---|
| 473 | { |
---|
| 474 | cloned = TRUE; |
---|
| 475 | grad = rsvg_clone_linear_gradient (parent, &shallow_cloned); |
---|
| 476 | ctx->handler = rsvg_gradient_stop_handler_new_clone (ctx, grad->stops, "linearGradient"); |
---|
| 477 | } |
---|
| 478 | } |
---|
| 479 | |
---|
| 480 | if (!cloned) |
---|
| 481 | { |
---|
| 482 | grad = g_new (RsvgLinearGradient, 1); |
---|
| 483 | grad->super.type = RSVG_DEF_LINGRAD; |
---|
| 484 | grad->super.free = rsvg_linear_gradient_free; |
---|
| 485 | ctx->handler = rsvg_gradient_stop_handler_new (ctx, &grad->stops, "linearGradient"); |
---|
| 486 | } |
---|
| 487 | |
---|
| 488 | rsvg_defs_set (ctx->defs, id, &grad->super); |
---|
| 489 | |
---|
| 490 | for (i = 0; i < 6; i++) |
---|
| 491 | grad->affine[i] = state->affine[i]; |
---|
[17276] | 492 | |
---|
[18608] | 493 | if (got_transform) |
---|
| 494 | art_affine_multiply (grad->affine, affine, grad->affine); |
---|
| 495 | |
---|
| 496 | /* state inherits parent/cloned information unless it's explicity gotten */ |
---|
[18804] | 497 | grad->obj_bbox = (cloned && !got_bbox) ? grad->obj_bbox : obj_bbox; |
---|
[18608] | 498 | grad->x1 = (cloned && !got_x1) ? grad->x1 : x1; |
---|
| 499 | grad->y1 = (cloned && !got_y1) ? grad->y1 : y1; |
---|
| 500 | grad->x2 = (cloned && !got_x2) ? grad->x2 : x2; |
---|
| 501 | grad->y2 = (cloned && !got_y2) ? grad->y1 : y2; |
---|
| 502 | grad->spread = (cloned && !got_spread) ? grad->spread : spread; |
---|
[17276] | 503 | } |
---|
| 504 | |
---|
[18608] | 505 | /* exported to the paint server via rsvg-private.h */ |
---|
| 506 | void |
---|
| 507 | rsvg_radial_gradient_free (RsvgDefVal *self) |
---|
[17276] | 508 | { |
---|
[18608] | 509 | RsvgRadialGradient *z = (RsvgRadialGradient *)self; |
---|
| 510 | |
---|
| 511 | g_free (z->stops->stop); |
---|
| 512 | g_free (z->stops); |
---|
| 513 | g_free (self); |
---|
[17276] | 514 | } |
---|
| 515 | |
---|
| 516 | static void |
---|
[18608] | 517 | rsvg_start_radial_gradient (RsvgHandle *ctx, const xmlChar **atts, const char * tag) /* tag for conicalGradient */ |
---|
[17276] | 518 | { |
---|
[18608] | 519 | RsvgState *state = &ctx->state[ctx->n_state - 1]; |
---|
| 520 | RsvgRadialGradient *grad = NULL; |
---|
| 521 | int i; |
---|
| 522 | const char *id = NULL; |
---|
| 523 | double cx = 0., cy = 0., r = 0., fx = 0., fy = 0.; |
---|
| 524 | const char * xlink_href = NULL; |
---|
| 525 | ArtGradientSpread spread = ART_GRADIENT_PAD; |
---|
[18804] | 526 | gboolean obj_bbox = TRUE; |
---|
| 527 | gboolean got_cx, got_cy, got_r, got_fx, got_fy, got_spread, got_transform, got_bbox, cloned, shallow_cloned; |
---|
[18608] | 528 | double affine[6]; |
---|
| 529 | |
---|
[18804] | 530 | got_cx = got_cy = got_r = got_fx = got_fy = got_spread = got_transform = got_bbox = cloned = shallow_cloned = FALSE; |
---|
[18608] | 531 | |
---|
| 532 | if (atts != NULL) |
---|
| 533 | { |
---|
| 534 | for (i = 0; atts[i] != NULL; i += 2) |
---|
| 535 | { |
---|
| 536 | if (!strcmp ((char *)atts[i], "id")) |
---|
| 537 | id = (const char *)atts[i + 1]; |
---|
| 538 | else if (!strcmp ((char *)atts[i], "cx")) { |
---|
| 539 | cx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 540 | got_cx = TRUE; |
---|
| 541 | } |
---|
| 542 | else if (!strcmp ((char *)atts[i], "cy")) { |
---|
| 543 | cy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 544 | got_cy = TRUE; |
---|
| 545 | } |
---|
| 546 | else if (!strcmp ((char *)atts[i], "r")) { |
---|
| 547 | r = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, |
---|
| 548 | rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), |
---|
| 549 | state->font_size); |
---|
| 550 | got_r = TRUE; |
---|
| 551 | } |
---|
| 552 | else if (!strcmp ((char *)atts[i], "fx")) { |
---|
| 553 | fx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
| 554 | got_fx = TRUE; |
---|
| 555 | } |
---|
| 556 | else if (!strcmp ((char *)atts[i], "fy")) { |
---|
| 557 | fy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 558 | got_fy = TRUE; |
---|
| 559 | } |
---|
| 560 | else if (!strcmp ((char *)atts[i], "xlink:href")) |
---|
| 561 | xlink_href = (const char *)atts[i + 1]; |
---|
| 562 | else if (!strcmp ((char *)atts[i], "gradientTransform")) { |
---|
| 563 | got_transform = rsvg_parse_transform (affine, (const char *)atts[i + 1]); |
---|
| 564 | } |
---|
| 565 | else if (!strcmp ((char *)atts[i], "spreadMethod")) |
---|
| 566 | { |
---|
| 567 | if (!strcmp ((char *)atts[i + 1], "pad")) { |
---|
| 568 | spread = ART_GRADIENT_PAD; |
---|
| 569 | got_spread = TRUE; |
---|
| 570 | } |
---|
| 571 | else if (!strcmp ((char *)atts[i + 1], "reflect")) { |
---|
| 572 | spread = ART_GRADIENT_REFLECT; |
---|
| 573 | got_spread = TRUE; |
---|
| 574 | } |
---|
| 575 | else if (!strcmp ((char *)atts[i + 1], "repeat")) { |
---|
| 576 | spread = ART_GRADIENT_REPEAT; |
---|
| 577 | got_spread = TRUE; |
---|
| 578 | } |
---|
| 579 | } |
---|
[18804] | 580 | else if (!strcmp ((char *)atts[i], "gradientUnits")) { |
---|
| 581 | if (!strcmp ((char *)atts[i+1], "userSpaceOnUse")) |
---|
| 582 | obj_bbox = FALSE; |
---|
| 583 | got_bbox = TRUE; |
---|
| 584 | } |
---|
[18608] | 585 | } |
---|
| 586 | } |
---|
| 587 | |
---|
| 588 | if (xlink_href != NULL) |
---|
| 589 | { |
---|
| 590 | RsvgRadialGradient * parent = (RsvgRadialGradient*)rsvg_defs_lookup (ctx->defs, xlink_href+1); |
---|
| 591 | if (parent != NULL) |
---|
| 592 | { |
---|
| 593 | cloned = TRUE; |
---|
| 594 | grad = rsvg_clone_radial_gradient (parent, &shallow_cloned); |
---|
| 595 | ctx->handler = rsvg_gradient_stop_handler_new_clone (ctx, grad->stops, tag); |
---|
| 596 | } |
---|
[18274] | 597 | } |
---|
[18608] | 598 | if (!cloned) |
---|
| 599 | { |
---|
| 600 | grad = g_new (RsvgRadialGradient, 1); |
---|
| 601 | grad->super.type = RSVG_DEF_RADGRAD; |
---|
| 602 | grad->super.free = rsvg_radial_gradient_free; |
---|
| 603 | ctx->handler = rsvg_gradient_stop_handler_new (ctx, &grad->stops, tag); |
---|
| 604 | } |
---|
[18274] | 605 | |
---|
[18804] | 606 | /* setup defaults */ |
---|
| 607 | if (!got_cx) { |
---|
| 608 | if (obj_bbox) |
---|
| 609 | cx = 0.5; |
---|
| 610 | else |
---|
| 611 | cx = rsvg_css_parse_normalized_length ("50%", ctx->dpi, (gdouble)ctx->width, state->font_size); |
---|
[17276] | 612 | } |
---|
[18804] | 613 | if (!got_cy) { |
---|
| 614 | if (obj_bbox) |
---|
| 615 | cy = 0.5; |
---|
| 616 | else |
---|
| 617 | cy = rsvg_css_parse_normalized_length ("50%", ctx->dpi, (gdouble)ctx->height, state->font_size); |
---|
| 618 | } |
---|
| 619 | if (!got_r) { |
---|
| 620 | if (obj_bbox) |
---|
| 621 | r = 0.5; |
---|
| 622 | else |
---|
| 623 | r = rsvg_css_parse_normalized_length ("50%", ctx->dpi, rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), state->font_size); |
---|
| 624 | } |
---|
| 625 | if (!got_fx) { |
---|
| 626 | fx = cx; |
---|
| 627 | } |
---|
| 628 | if (!got_fy) { |
---|
| 629 | fy = cy; |
---|
| 630 | } |
---|
[18608] | 631 | |
---|
| 632 | rsvg_defs_set (ctx->defs, id, &grad->super); |
---|
| 633 | |
---|
| 634 | for (i = 0; i < 6; i++) |
---|
| 635 | grad->affine[i] = state->affine[i]; |
---|
[17276] | 636 | |
---|
[18608] | 637 | if (got_transform) |
---|
| 638 | art_affine_multiply (grad->affine, affine, grad->affine); |
---|
| 639 | |
---|
| 640 | /* state inherits parent/cloned information unless it's explicity gotten */ |
---|
[18804] | 641 | grad->obj_bbox = (cloned && !got_bbox) ? grad->obj_bbox : obj_bbox; |
---|
[18608] | 642 | grad->cx = (cloned && !got_cx) ? grad->cx : cx; |
---|
| 643 | grad->cy = (cloned && !got_cy) ? grad->cy : cy; |
---|
| 644 | grad->r = (cloned && !got_r) ? grad->r : r; |
---|
| 645 | grad->fx = (cloned && !got_fx) ? grad->fx : fx; |
---|
| 646 | grad->fy = (cloned && !got_fy) ? grad->fy : fy; |
---|
| 647 | grad->spread = (cloned && !got_spread) ? grad->spread : spread; |
---|
[17276] | 648 | } |
---|
| 649 | |
---|
[18608] | 650 | /* end gradients */ |
---|
| 651 | |
---|
[17276] | 652 | static void |
---|
[18608] | 653 | rsvg_style_handler_free (RsvgSaxHandler *self) |
---|
[18274] | 654 | { |
---|
[18608] | 655 | RsvgSaxHandlerStyle *z = (RsvgSaxHandlerStyle *)self; |
---|
| 656 | RsvgHandle *ctx = z->ctx; |
---|
| 657 | |
---|
| 658 | rsvg_parse_cssbuffer (ctx, z->style->str, z->style->len); |
---|
| 659 | |
---|
| 660 | g_string_free (z->style, TRUE); |
---|
| 661 | g_free (z); |
---|
[17276] | 662 | } |
---|
| 663 | |
---|
| 664 | static void |
---|
[18608] | 665 | rsvg_style_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) |
---|
[17276] | 666 | { |
---|
[18608] | 667 | RsvgSaxHandlerStyle *z = (RsvgSaxHandlerStyle *)self; |
---|
| 668 | g_string_append_len (z->style, (const char *)ch, len); |
---|
[17276] | 669 | } |
---|
| 670 | |
---|
| 671 | static void |
---|
[18608] | 672 | rsvg_style_handler_start (RsvgSaxHandler *self, const xmlChar *name, |
---|
| 673 | const xmlChar **atts) |
---|
[17276] | 674 | { |
---|
[18274] | 675 | } |
---|
| 676 | |
---|
| 677 | static void |
---|
[18608] | 678 | rsvg_style_handler_end (RsvgSaxHandler *self, const xmlChar *name) |
---|
[17276] | 679 | { |
---|
[18608] | 680 | RsvgSaxHandlerStyle *z = (RsvgSaxHandlerStyle *)self; |
---|
| 681 | RsvgHandle *ctx = z->ctx; |
---|
| 682 | |
---|
| 683 | if (!strcmp ((char *)name, "style")) |
---|
| 684 | { |
---|
| 685 | if (ctx->handler != NULL) |
---|
| 686 | { |
---|
| 687 | ctx->handler->free (ctx->handler); |
---|
| 688 | ctx->handler = &z->parent->super; |
---|
| 689 | } |
---|
| 690 | } |
---|
[17276] | 691 | } |
---|
| 692 | |
---|
[18351] | 693 | static void |
---|
[18608] | 694 | rsvg_start_style (RsvgHandle *ctx, const xmlChar **atts) |
---|
[18351] | 695 | { |
---|
[18608] | 696 | RsvgSaxHandlerStyle *handler = g_new0 (RsvgSaxHandlerStyle, 1); |
---|
| 697 | |
---|
| 698 | handler->super.free = rsvg_style_handler_free; |
---|
| 699 | handler->super.characters = rsvg_style_handler_characters; |
---|
| 700 | handler->super.start_element = rsvg_style_handler_start; |
---|
| 701 | handler->super.end_element = rsvg_style_handler_end; |
---|
| 702 | handler->ctx = ctx; |
---|
| 703 | |
---|
| 704 | handler->style = g_string_new (NULL); |
---|
| 705 | |
---|
| 706 | handler->parent = (RsvgSaxHandlerDefs*)ctx->handler; |
---|
| 707 | ctx->handler = &handler->super; |
---|
[18351] | 708 | } |
---|
[17276] | 709 | |
---|
[18608] | 710 | /* */ |
---|
[18351] | 711 | |
---|
[17276] | 712 | static void |
---|
[18608] | 713 | rsvg_defs_handler_free (RsvgSaxHandler *self) |
---|
[17276] | 714 | { |
---|
[18608] | 715 | g_free (self); |
---|
[17276] | 716 | } |
---|
| 717 | |
---|
| 718 | static void |
---|
[18608] | 719 | rsvg_defs_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) |
---|
[17276] | 720 | { |
---|
| 721 | } |
---|
| 722 | |
---|
| 723 | static void |
---|
[18608] | 724 | rsvg_defs_handler_start (RsvgSaxHandler *self, const xmlChar *name, |
---|
| 725 | const xmlChar **atts) |
---|
[17276] | 726 | { |
---|
[18608] | 727 | RsvgSaxHandlerDefs *z = (RsvgSaxHandlerDefs *)self; |
---|
| 728 | RsvgHandle *ctx = z->ctx; |
---|
| 729 | |
---|
| 730 | /* push the state stack */ |
---|
| 731 | if (ctx->n_state == ctx->n_state_max) |
---|
| 732 | ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1); |
---|
| 733 | if (ctx->n_state) |
---|
| 734 | rsvg_state_clone (&ctx->state[ctx->n_state], |
---|
| 735 | &ctx->state[ctx->n_state - 1]); |
---|
| 736 | else |
---|
| 737 | rsvg_state_init (ctx->state); |
---|
| 738 | ctx->n_state++; |
---|
[17276] | 739 | |
---|
[18804] | 740 | /* |
---|
[18608] | 741 | * conicalGradient isn't in the SVG spec and I'm not sure exactly what it does. libart definitely |
---|
| 742 | * has no analogue. But it does seem similar enough to a radialGradient that i'd rather get the |
---|
| 743 | * onscreen representation of the colour wrong than not have any colour displayed whatsoever |
---|
| 744 | */ |
---|
[17276] | 745 | |
---|
[18608] | 746 | if (!strcmp ((char *)name, "linearGradient")) |
---|
| 747 | rsvg_start_linear_gradient (ctx, atts); |
---|
| 748 | else if (!strcmp ((char *)name, "radialGradient")) |
---|
| 749 | rsvg_start_radial_gradient (ctx, atts, "radialGradient"); |
---|
| 750 | else if (!strcmp((char *)name, "conicalGradient")) |
---|
| 751 | rsvg_start_radial_gradient (ctx, atts, "conicalGradient"); |
---|
| 752 | else if (!strcmp ((char *)name, "style")) |
---|
| 753 | rsvg_start_style (ctx, atts); |
---|
[17276] | 754 | } |
---|
| 755 | |
---|
| 756 | static void |
---|
[18608] | 757 | rsvg_defs_handler_end (RsvgSaxHandler *self, const xmlChar *name) |
---|
[17276] | 758 | { |
---|
[18608] | 759 | RsvgSaxHandlerDefs *z = (RsvgSaxHandlerDefs *)self; |
---|
| 760 | RsvgHandle *ctx = z->ctx; |
---|
| 761 | |
---|
| 762 | if (!strcmp((char *)name, "defs")) |
---|
| 763 | { |
---|
| 764 | if (ctx->handler != NULL) |
---|
| 765 | { |
---|
| 766 | ctx->handler->free (ctx->handler); |
---|
| 767 | ctx->handler = NULL; |
---|
| 768 | } |
---|
[18804] | 769 | ctx->in_defs = FALSE; |
---|
[18608] | 770 | } |
---|
| 771 | |
---|
| 772 | /* pop the state stack */ |
---|
| 773 | ctx->n_state--; |
---|
| 774 | rsvg_state_finalize (&ctx->state[ctx->n_state]); |
---|
[17276] | 775 | } |
---|
| 776 | |
---|
| 777 | static void |
---|
[18608] | 778 | rsvg_start_defs (RsvgHandle *ctx, const xmlChar **atts) |
---|
[17276] | 779 | { |
---|
[18608] | 780 | RsvgSaxHandlerDefs *handler = g_new0 (RsvgSaxHandlerDefs, 1); |
---|
| 781 | |
---|
| 782 | handler->super.free = rsvg_defs_handler_free; |
---|
| 783 | handler->super.characters = rsvg_defs_handler_characters; |
---|
| 784 | handler->super.start_element = rsvg_defs_handler_start; |
---|
| 785 | handler->super.end_element = rsvg_defs_handler_end; |
---|
| 786 | handler->ctx = ctx; |
---|
[18804] | 787 | |
---|
| 788 | ctx->in_defs = TRUE; |
---|
[18608] | 789 | ctx->handler = &handler->super; |
---|
[17276] | 790 | } |
---|
| 791 | |
---|
[18608] | 792 | /* end defs */ |
---|
[17276] | 793 | |
---|
| 794 | static void |
---|
[18351] | 795 | rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts) |
---|
[17276] | 796 | { |
---|
[18608] | 797 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
[17276] | 798 | |
---|
[18608] | 799 | if (ctx->handler) |
---|
| 800 | { |
---|
| 801 | ctx->handler_nest++; |
---|
| 802 | if (ctx->handler->start_element != NULL) |
---|
| 803 | ctx->handler->start_element (ctx->handler, name, atts); |
---|
| 804 | } |
---|
| 805 | else |
---|
| 806 | { |
---|
| 807 | /* push the state stack */ |
---|
| 808 | if (ctx->n_state == ctx->n_state_max) |
---|
| 809 | ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1); |
---|
| 810 | if (ctx->n_state) |
---|
| 811 | rsvg_state_clone (&ctx->state[ctx->n_state], |
---|
| 812 | &ctx->state[ctx->n_state - 1]); |
---|
| 813 | else |
---|
| 814 | rsvg_state_init (ctx->state); |
---|
| 815 | ctx->n_state++; |
---|
| 816 | |
---|
| 817 | if (!strcmp ((char *)name, "svg")) |
---|
| 818 | rsvg_start_svg (ctx, atts); |
---|
| 819 | else if (!strcmp ((char *)name, "g")) |
---|
| 820 | rsvg_start_g (ctx, atts); |
---|
| 821 | else if (!strcmp ((char *)name, "path")) |
---|
| 822 | rsvg_start_path (ctx, atts); |
---|
| 823 | else if (!strcmp ((char *)name, "text")) |
---|
| 824 | rsvg_start_text (ctx, atts); |
---|
| 825 | else if (!strcmp ((char *)name, "image")) |
---|
| 826 | rsvg_start_image (ctx, atts); |
---|
| 827 | else if (!strcmp ((char *)name, "line")) |
---|
| 828 | rsvg_start_line (ctx, atts); |
---|
| 829 | else if (!strcmp ((char *)name, "rect")) |
---|
| 830 | rsvg_start_rect (ctx, atts); |
---|
| 831 | else if (!strcmp ((char *)name, "circle")) |
---|
| 832 | rsvg_start_circle (ctx, atts); |
---|
| 833 | else if (!strcmp ((char *)name, "ellipse")) |
---|
| 834 | rsvg_start_ellipse (ctx, atts); |
---|
| 835 | else if (!strcmp ((char *)name, "defs")) |
---|
| 836 | rsvg_start_defs (ctx, atts); |
---|
| 837 | else if (!strcmp ((char *)name, "polygon")) |
---|
| 838 | rsvg_start_polygon (ctx, atts); |
---|
| 839 | else if (!strcmp ((char *)name, "polyline")) |
---|
| 840 | rsvg_start_polyline (ctx, atts); |
---|
| 841 | |
---|
| 842 | /* see conicalGradient discussion above */ |
---|
| 843 | else if (!strcmp ((char *)name, "linearGradient")) |
---|
| 844 | rsvg_start_linear_gradient (ctx, atts); |
---|
| 845 | else if (!strcmp ((char *)name, "radialGradient")) |
---|
| 846 | rsvg_start_radial_gradient (ctx, atts, "radialGradient"); |
---|
| 847 | else if (!strcmp ((char *)name, "conicalGradient")) |
---|
| 848 | rsvg_start_radial_gradient (ctx, atts, "conicalGradient"); |
---|
[17276] | 849 | } |
---|
| 850 | } |
---|
| 851 | |
---|
| 852 | static void |
---|
| 853 | rsvg_end_element (void *data, const xmlChar *name) |
---|
| 854 | { |
---|
[18608] | 855 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
| 856 | |
---|
[18804] | 857 | if (ctx->handler_nest > 0 && ctx->handler != NULL) |
---|
[18608] | 858 | { |
---|
| 859 | if (ctx->handler->end_element != NULL) |
---|
| 860 | ctx->handler->end_element (ctx->handler, name); |
---|
| 861 | ctx->handler_nest--; |
---|
| 862 | } |
---|
| 863 | else |
---|
| 864 | { |
---|
| 865 | if (ctx->handler != NULL) |
---|
| 866 | { |
---|
| 867 | ctx->handler->free (ctx->handler); |
---|
| 868 | ctx->handler = NULL; |
---|
| 869 | } |
---|
[17276] | 870 | |
---|
[18608] | 871 | if (!strcmp ((char *)name, "g")) |
---|
| 872 | rsvg_end_g (ctx); |
---|
[18804] | 873 | else if (!strcmp ((char *)name, "defs")) { |
---|
| 874 | ctx->in_defs = FALSE; |
---|
| 875 | } |
---|
[18608] | 876 | |
---|
| 877 | /* pop the state stack */ |
---|
| 878 | ctx->n_state--; |
---|
| 879 | rsvg_state_finalize (&ctx->state[ctx->n_state]); |
---|
| 880 | } |
---|
[17276] | 881 | } |
---|
| 882 | |
---|
| 883 | static void |
---|
| 884 | rsvg_characters (void *data, const xmlChar *ch, int len) |
---|
| 885 | { |
---|
[18608] | 886 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
| 887 | |
---|
| 888 | if (ctx->handler && ctx->handler->characters != NULL) |
---|
| 889 | ctx->handler->characters (ctx->handler, ch, len); |
---|
[17276] | 890 | } |
---|
| 891 | |
---|
| 892 | static xmlEntityPtr |
---|
| 893 | rsvg_get_entity (void *data, const xmlChar *name) |
---|
| 894 | { |
---|
[18608] | 895 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
| 896 | |
---|
| 897 | return (xmlEntityPtr)g_hash_table_lookup (ctx->entities, name); |
---|
[17276] | 898 | } |
---|
| 899 | |
---|
| 900 | static void |
---|
| 901 | rsvg_entity_decl (void *data, const xmlChar *name, int type, |
---|
[18608] | 902 | const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) |
---|
[17276] | 903 | { |
---|
[18608] | 904 | RsvgHandle *ctx = (RsvgHandle *)data; |
---|
| 905 | GHashTable *entities = ctx->entities; |
---|
| 906 | xmlEntityPtr entity; |
---|
| 907 | char *dupname; |
---|
[17276] | 908 | |
---|
[18608] | 909 | entity = g_new0 (xmlEntity, 1); |
---|
| 910 | entity->type = type; |
---|
| 911 | entity->length = strlen (name); |
---|
| 912 | dupname = g_strdup (name); |
---|
| 913 | entity->name = dupname; |
---|
| 914 | entity->ExternalID = g_strdup (publicId); |
---|
| 915 | entity->SystemID = g_strdup (systemId); |
---|
| 916 | if (content) |
---|
| 917 | { |
---|
| 918 | entity->content = xmlMemStrdup (content); |
---|
| 919 | entity->length = strlen (content); |
---|
| 920 | } |
---|
| 921 | g_hash_table_insert (entities, dupname, entity); |
---|
[17276] | 922 | } |
---|
| 923 | |
---|
[18274] | 924 | static void |
---|
| 925 | rsvg_error_cb (void *data, const char *msg, ...) |
---|
| 926 | { |
---|
| 927 | va_list args; |
---|
| 928 | |
---|
| 929 | va_start (args, msg); |
---|
| 930 | vfprintf (stderr, msg, args); |
---|
| 931 | va_end (args); |
---|
| 932 | } |
---|
| 933 | |
---|
[17276] | 934 | static xmlSAXHandler rsvgSAXHandlerStruct = { |
---|
| 935 | NULL, /* internalSubset */ |
---|
| 936 | NULL, /* isStandalone */ |
---|
| 937 | NULL, /* hasInternalSubset */ |
---|
| 938 | NULL, /* hasExternalSubset */ |
---|
| 939 | NULL, /* resolveEntity */ |
---|
| 940 | rsvg_get_entity, /* getEntity */ |
---|
| 941 | rsvg_entity_decl, /* entityDecl */ |
---|
| 942 | NULL, /* notationDecl */ |
---|
| 943 | NULL, /* attributeDecl */ |
---|
| 944 | NULL, /* elementDecl */ |
---|
| 945 | NULL, /* unparsedEntityDecl */ |
---|
| 946 | NULL, /* setDocumentLocator */ |
---|
| 947 | NULL, /* startDocument */ |
---|
| 948 | NULL, /* endDocument */ |
---|
| 949 | rsvg_start_element, /* startElement */ |
---|
| 950 | rsvg_end_element, /* endElement */ |
---|
| 951 | NULL, /* reference */ |
---|
| 952 | rsvg_characters, /* characters */ |
---|
| 953 | NULL, /* ignorableWhitespace */ |
---|
| 954 | NULL, /* processingInstruction */ |
---|
| 955 | NULL, /* comment */ |
---|
| 956 | NULL, /* xmlParserWarning */ |
---|
[18274] | 957 | rsvg_error_cb, /* xmlParserError */ |
---|
| 958 | rsvg_error_cb, /* xmlParserFatalError */ |
---|
[17276] | 959 | NULL, /* getParameterEntity */ |
---|
[18608] | 960 | rsvg_characters, /* cdataCallback */ |
---|
| 961 | NULL /* */ |
---|
[17276] | 962 | }; |
---|
| 963 | |
---|
[18804] | 964 | /** |
---|
| 965 | * rsvg_error_quark |
---|
| 966 | * |
---|
| 967 | * The error domain for RSVG |
---|
| 968 | * |
---|
| 969 | * Returns: The error domain |
---|
| 970 | */ |
---|
[18274] | 971 | GQuark |
---|
| 972 | rsvg_error_quark (void) |
---|
| 973 | { |
---|
[18608] | 974 | static GQuark q = 0; |
---|
| 975 | if (q == 0) |
---|
| 976 | q = g_quark_from_static_string ("rsvg-error-quark"); |
---|
| 977 | |
---|
| 978 | return q; |
---|
[18274] | 979 | } |
---|
| 980 | |
---|
[18804] | 981 | gboolean |
---|
| 982 | rsvg_handle_write_impl (RsvgHandle *handle, |
---|
| 983 | const guchar *buf, |
---|
| 984 | gsize count, |
---|
| 985 | GError **error) |
---|
| 986 | { |
---|
| 987 | GError *real_error; |
---|
| 988 | g_return_val_if_fail (handle != NULL, FALSE); |
---|
| 989 | |
---|
| 990 | handle->error = &real_error; |
---|
| 991 | if (handle->ctxt == NULL) |
---|
| 992 | { |
---|
| 993 | handle->ctxt = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, handle, NULL, 0, NULL); |
---|
| 994 | handle->ctxt->replaceEntities = TRUE; |
---|
| 995 | } |
---|
| 996 | |
---|
| 997 | xmlParseChunk (handle->ctxt, buf, count, 0); |
---|
| 998 | |
---|
| 999 | handle->error = NULL; |
---|
| 1000 | /* FIXME: Error handling not implemented. */ |
---|
| 1001 | /* if (*real_error != NULL) |
---|
| 1002 | { |
---|
| 1003 | g_propagate_error (error, real_error); |
---|
| 1004 | return FALSE; |
---|
| 1005 | }*/ |
---|
| 1006 | return TRUE; |
---|
| 1007 | } |
---|
| 1008 | |
---|
| 1009 | gboolean |
---|
| 1010 | rsvg_handle_close_impl (RsvgHandle *handle, |
---|
| 1011 | GError **error) |
---|
| 1012 | { |
---|
| 1013 | gchar chars[1] = { '\0' }; |
---|
| 1014 | GError *real_error; |
---|
| 1015 | |
---|
| 1016 | handle->error = &real_error; |
---|
| 1017 | |
---|
| 1018 | if (handle->ctxt != NULL) |
---|
| 1019 | { |
---|
| 1020 | xmlParseChunk (handle->ctxt, chars, 1, TRUE); |
---|
| 1021 | xmlFreeParserCtxt (handle->ctxt); |
---|
| 1022 | } |
---|
| 1023 | |
---|
| 1024 | /* FIXME: Error handling not implemented. */ |
---|
| 1025 | /* |
---|
| 1026 | if (real_error != NULL) |
---|
| 1027 | { |
---|
| 1028 | g_propagate_error (error, real_error); |
---|
| 1029 | return FALSE; |
---|
| 1030 | }*/ |
---|
| 1031 | return TRUE; |
---|
| 1032 | } |
---|
| 1033 | |
---|
| 1034 | void |
---|
| 1035 | rsvg_handle_free_impl (RsvgHandle *handle) |
---|
| 1036 | { |
---|
| 1037 | int i; |
---|
| 1038 | |
---|
| 1039 | if (handle->pango_context != NULL) |
---|
| 1040 | g_object_unref (handle->pango_context); |
---|
| 1041 | rsvg_defs_free (handle->defs); |
---|
| 1042 | |
---|
| 1043 | for (i = 0; i < handle->n_state; i++) |
---|
| 1044 | rsvg_state_finalize (&handle->state[i]); |
---|
| 1045 | g_free (handle->state); |
---|
| 1046 | |
---|
| 1047 | g_hash_table_foreach (handle->entities, rsvg_ctx_free_helper, NULL); |
---|
| 1048 | g_hash_table_destroy (handle->entities); |
---|
| 1049 | |
---|
| 1050 | g_hash_table_destroy (handle->css_props); |
---|
| 1051 | |
---|
| 1052 | if (handle->user_data_destroy) |
---|
| 1053 | (* handle->user_data_destroy) (handle->user_data); |
---|
| 1054 | if (handle->pixbuf) |
---|
| 1055 | g_object_unref (handle->pixbuf); |
---|
| 1056 | g_free (handle); |
---|
| 1057 | } |
---|
| 1058 | |
---|
[18274] | 1059 | /** |
---|
| 1060 | * rsvg_handle_new: |
---|
| 1061 | * |
---|
| 1062 | * Returns a new rsvg handle. Must be freed with @rsvg_handle_free. This |
---|
| 1063 | * handle can be used for dynamically loading an image. You need to feed it |
---|
| 1064 | * data using @rsvg_handle_write, then call @rsvg_handle_close when done. No |
---|
| 1065 | * more than one image can be loaded with one handle. |
---|
| 1066 | * |
---|
[18804] | 1067 | * Returns: A new #RsvgHandle |
---|
[18274] | 1068 | **/ |
---|
| 1069 | RsvgHandle * |
---|
| 1070 | rsvg_handle_new (void) |
---|
| 1071 | { |
---|
[18608] | 1072 | RsvgHandle *handle; |
---|
| 1073 | |
---|
| 1074 | handle = g_new0 (RsvgHandle, 1); |
---|
[18804] | 1075 | rsvg_handle_init (handle); |
---|
| 1076 | |
---|
| 1077 | handle->write = rsvg_handle_write_impl; |
---|
| 1078 | handle->close = rsvg_handle_close_impl; |
---|
| 1079 | handle->free = rsvg_handle_free_impl; |
---|
| 1080 | |
---|
| 1081 | return handle; |
---|
| 1082 | } |
---|
| 1083 | |
---|
| 1084 | void |
---|
| 1085 | rsvg_handle_init (RsvgHandle * handle) |
---|
| 1086 | { |
---|
[18608] | 1087 | handle->n_state = 0; |
---|
| 1088 | handle->n_state_max = 16; |
---|
| 1089 | handle->state = g_new (RsvgState, handle->n_state_max); |
---|
| 1090 | handle->defs = rsvg_defs_new (); |
---|
| 1091 | handle->handler_nest = 0; |
---|
| 1092 | handle->entities = g_hash_table_new (g_str_hash, g_str_equal); |
---|
| 1093 | handle->dpi = internal_dpi; |
---|
| 1094 | |
---|
| 1095 | handle->css_props = g_hash_table_new_full (g_str_hash, g_str_equal, |
---|
| 1096 | g_free, g_free); |
---|
| 1097 | |
---|
| 1098 | handle->ctxt = NULL; |
---|
| 1099 | } |
---|
[18274] | 1100 | |
---|
[18608] | 1101 | /** |
---|
| 1102 | * rsvg_set_default_dpi |
---|
| 1103 | * @dpi: Dots Per Inch (aka Pixels Per Inch) |
---|
| 1104 | * |
---|
| 1105 | * Sets the DPI for the all future outgoing pixbufs. Common values are |
---|
| 1106 | * 72, 90, and 300 DPI. Passing a number <= 0 to #dpi will |
---|
| 1107 | * reset the DPI to whatever the default value happens to be. |
---|
| 1108 | */ |
---|
| 1109 | void |
---|
| 1110 | rsvg_set_default_dpi (double dpi) |
---|
| 1111 | { |
---|
| 1112 | if (dpi <= 0.) |
---|
| 1113 | internal_dpi = RSVG_DEFAULT_DPI; |
---|
| 1114 | else |
---|
| 1115 | internal_dpi = dpi; |
---|
| 1116 | } |
---|
[18274] | 1117 | |
---|
[18608] | 1118 | /** |
---|
| 1119 | * rsvg_handle_set_dpi |
---|
| 1120 | * @handle: An #RsvgHandle |
---|
| 1121 | * @dpi: Dots Per Inch (aka Pixels Per Inch) |
---|
| 1122 | * |
---|
| 1123 | * Sets the DPI for the outgoing pixbuf. Common values are |
---|
| 1124 | * 72, 90, and 300 DPI. Passing a number <= 0 to #dpi will |
---|
| 1125 | * reset the DPI to whatever the default value happens to be. |
---|
| 1126 | */ |
---|
| 1127 | void |
---|
| 1128 | rsvg_handle_set_dpi (RsvgHandle * handle, double dpi) |
---|
| 1129 | { |
---|
| 1130 | g_return_if_fail (handle != NULL); |
---|
| 1131 | |
---|
| 1132 | if (dpi <= 0.) |
---|
| 1133 | handle->dpi = internal_dpi; |
---|
| 1134 | else |
---|
| 1135 | handle->dpi = dpi; |
---|
[18274] | 1136 | } |
---|
| 1137 | |
---|
| 1138 | /** |
---|
| 1139 | * rsvg_handle_set_size_callback: |
---|
| 1140 | * @handle: An #RsvgHandle |
---|
| 1141 | * @size_func: A sizing function, or %NULL |
---|
| 1142 | * @user_data: User data to pass to @size_func, or %NULL |
---|
| 1143 | * @user_data_destroy: Destroy function for @user_data, or %NULL |
---|
| 1144 | * |
---|
| 1145 | * Sets the sizing function for the @handle. This function is called right |
---|
| 1146 | * after the size of the image has been loaded. The size of the image is passed |
---|
| 1147 | * in to the function, which may then modify these values to set the real size |
---|
| 1148 | * of the generated pixbuf. If the image has no associated size, then the size |
---|
| 1149 | * arguments are set to -1. |
---|
| 1150 | **/ |
---|
| 1151 | void |
---|
| 1152 | rsvg_handle_set_size_callback (RsvgHandle *handle, |
---|
[18608] | 1153 | RsvgSizeFunc size_func, |
---|
| 1154 | gpointer user_data, |
---|
| 1155 | GDestroyNotify user_data_destroy) |
---|
[18274] | 1156 | { |
---|
[18608] | 1157 | g_return_if_fail (handle != NULL); |
---|
| 1158 | |
---|
| 1159 | if (handle->user_data_destroy) |
---|
| 1160 | (* handle->user_data_destroy) (handle->user_data); |
---|
| 1161 | |
---|
| 1162 | handle->size_func = size_func; |
---|
| 1163 | handle->user_data = user_data; |
---|
| 1164 | handle->user_data_destroy = user_data_destroy; |
---|
[18274] | 1165 | } |
---|
| 1166 | |
---|
| 1167 | /** |
---|
| 1168 | * rsvg_handle_write: |
---|
| 1169 | * @handle: An #RsvgHandle |
---|
| 1170 | * @buf: Pointer to svg data |
---|
| 1171 | * @count: length of the @buf buffer in bytes |
---|
| 1172 | * @error: return location for errors |
---|
| 1173 | * |
---|
| 1174 | * Loads the next @count bytes of the image. This will return #TRUE if the data |
---|
| 1175 | * was loaded successful, and #FALSE if an error occurred. In the latter case, |
---|
| 1176 | * the loader will be closed, and will not accept further writes. If FALSE is |
---|
| 1177 | * returned, @error will be set to an error from the #RSVG_ERROR domain. |
---|
| 1178 | * |
---|
[18804] | 1179 | * Returns: #TRUE if the write was successful, or #FALSE if there was an |
---|
[18274] | 1180 | * error. |
---|
| 1181 | **/ |
---|
| 1182 | gboolean |
---|
| 1183 | rsvg_handle_write (RsvgHandle *handle, |
---|
[18608] | 1184 | const guchar *buf, |
---|
| 1185 | gsize count, |
---|
| 1186 | GError **error) |
---|
[18274] | 1187 | { |
---|
[18804] | 1188 | if (handle->write) |
---|
| 1189 | return (*handle->write) (handle, buf, count, error); |
---|
| 1190 | |
---|
| 1191 | return FALSE; |
---|
[18274] | 1192 | } |
---|
| 1193 | |
---|
| 1194 | /** |
---|
| 1195 | * rsvg_handle_close: |
---|
[18804] | 1196 | * @handle: A #RsvgHandle |
---|
| 1197 | * @error: A #GError |
---|
[18274] | 1198 | * |
---|
| 1199 | * Closes @handle, to indicate that loading the image is complete. This will |
---|
| 1200 | * return #TRUE if the loader closed successfully. Note that @handle isn't |
---|
| 1201 | * freed until @rsvg_handle_free is called. |
---|
| 1202 | * |
---|
[18804] | 1203 | * Returns: #TRUE if the loader closed successfully, or #FALSE if there was |
---|
[18274] | 1204 | * an error. |
---|
| 1205 | **/ |
---|
| 1206 | gboolean |
---|
| 1207 | rsvg_handle_close (RsvgHandle *handle, |
---|
[18608] | 1208 | GError **error) |
---|
[18274] | 1209 | { |
---|
[18804] | 1210 | if (handle->close) |
---|
| 1211 | return (*handle->close) (handle, error); |
---|
| 1212 | |
---|
| 1213 | return FALSE; |
---|
[17276] | 1214 | } |
---|
| 1215 | |
---|
[18274] | 1216 | /** |
---|
| 1217 | * rsvg_handle_get_pixbuf: |
---|
| 1218 | * @handle: An #RsvgHandle |
---|
| 1219 | * |
---|
| 1220 | * Returns the pixbuf loaded by #handle. The pixbuf returned will be reffed, so |
---|
| 1221 | * the caller of this function must assume that ref. If insufficient data has |
---|
| 1222 | * been read to create the pixbuf, or an error occurred in loading, then %NULL |
---|
| 1223 | * will be returned. Note that the pixbuf may not be complete until |
---|
| 1224 | * @rsvg_handle_close has been called. |
---|
| 1225 | * |
---|
[18804] | 1226 | * Returns: the pixbuf loaded by #handle, or %NULL. |
---|
[18274] | 1227 | **/ |
---|
| 1228 | GdkPixbuf * |
---|
| 1229 | rsvg_handle_get_pixbuf (RsvgHandle *handle) |
---|
| 1230 | { |
---|
[18608] | 1231 | g_return_val_if_fail (handle != NULL, NULL); |
---|
| 1232 | |
---|
| 1233 | if (handle->pixbuf) |
---|
| 1234 | return g_object_ref (handle->pixbuf); |
---|
[18274] | 1235 | |
---|
[18608] | 1236 | return NULL; |
---|
[18274] | 1237 | } |
---|
| 1238 | |
---|
| 1239 | /** |
---|
| 1240 | * rsvg_handle_free: |
---|
| 1241 | * @handle: An #RsvgHandle |
---|
| 1242 | * |
---|
| 1243 | * Frees #handle. |
---|
| 1244 | **/ |
---|
| 1245 | void |
---|
| 1246 | rsvg_handle_free (RsvgHandle *handle) |
---|
| 1247 | { |
---|
[18804] | 1248 | if (handle->free) |
---|
| 1249 | (*handle->free) (handle); |
---|
[17276] | 1250 | } |
---|
| 1251 | |
---|