source: trunk/third/librsvg/rsvg.c @ 19402

Revision 19402, 34.3 KB checked in by rbasch, 21 years ago (diff)
From the librsvg mainline: Fix ending of the XML parser context.
Line 
1/* vim: set sw=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2/*
3   rsvg.c: SAX-based renderer for SVG files into a GdkPixbuf.
4
5   Copyright (C) 2000 Eazel, Inc.
6   Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU Library General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
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
16   Library General Public License for more details.
17
18   You should have received a copy of the GNU Library General Public
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.
22
23   Author: Raph Levien <raph@artofcode.com>
24*/
25
26#include "config.h"
27
28#include "rsvg.h"
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"
34
35#include <math.h>
36#include <string.h>
37#include <stdarg.h>
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
45/*
46 * This is configurable at runtime
47 */
48#define RSVG_DEFAULT_DPI 90.0
49static double internal_dpi = RSVG_DEFAULT_DPI;
50
51static void
52rsvg_ctx_free_helper (gpointer key, gpointer value, gpointer user_data)
53{
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);
64}
65
66static void
67rsvg_pixmap_destroy (guchar *pixels, gpointer data)
68{
69        g_free (pixels);
70}
71
72static void
73rsvg_start_svg (RsvgHandle *ctx, const xmlChar **atts)
74{
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 = FALSE;
88
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;       
149#endif
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                }
208}
209
210static void
211rsvg_start_g (RsvgHandle *ctx, const xmlChar **atts)
212{
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);
231}
232
233static void
234rsvg_end_g (RsvgHandle *ctx)
235{
236        RsvgState *state = &ctx->state[ctx->n_state - 1];
237       
238        if (state->opacity != 0xff)
239                rsvg_pop_opacity_group (ctx, state->opacity);
240}
241
242typedef struct _RsvgSaxHandlerDefs {
243        RsvgSaxHandler super;
244        RsvgHandle *ctx;
245} RsvgSaxHandlerDefs;
246
247typedef struct _RsvgSaxHandlerStyle {
248        RsvgSaxHandler super;
249        RsvgSaxHandlerDefs *parent;
250        RsvgHandle *ctx;
251        GString *style;
252} RsvgSaxHandlerStyle;
253
254typedef struct _RsvgSaxHandlerGstops {
255        RsvgSaxHandler super;
256        RsvgSaxHandlerDefs *parent;
257        RsvgHandle *ctx;
258        RsvgGradientStops *stops;
259        const char * parent_tag;
260} RsvgSaxHandlerGstops;
261
262static void
263rsvg_gradient_stop_handler_free (RsvgSaxHandler *self)
264{
265        g_free (self);
266}
267
268static void
269rsvg_gradient_stop_handler_start (RsvgSaxHandler *self, const xmlChar *name,
270                                                                  const xmlChar **atts)
271{
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;
328}
329
330static void
331rsvg_gradient_stop_handler_end (RsvgSaxHandler *self, const xmlChar *name)
332{
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                }
344}
345
346static RsvgSaxHandler *
347rsvg_gradient_stop_handler_new_clone (RsvgHandle *ctx, RsvgGradientStops *stops,
348                                                                          const char * parent)
349{
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;
361}
362
363static RsvgSaxHandler *
364rsvg_gradient_stop_handler_new (RsvgHandle *ctx, RsvgGradientStops **p_stops,
365                                                                const char * parent)
366{
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;
383}
384
385/* exported to the paint server via rsvg-private.h */
386void
387rsvg_linear_gradient_free (RsvgDefVal *self)
388{
389        RsvgLinearGradient *z = (RsvgLinearGradient *)self;
390       
391        g_free (z->stops->stop);
392        g_free (z->stops);
393        g_free (self);
394}
395
396static void
397rsvg_start_linear_gradient (RsvgHandle *ctx, const xmlChar **atts)
398{
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 obj_bbox = TRUE;
407        gboolean got_x1, got_x2, got_y1, got_y2, got_spread, got_transform, got_bbox, cloned, shallow_cloned;
408        double affine[6];
409
410        got_x1 = got_x2 = got_y1 = got_y2 = got_spread = got_transform = got_bbox = cloned = shallow_cloned = FALSE;
411               
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];
451                                        else if (!strcmp ((char *)atts[i], "gradientTransform"))
452                                                got_transform = rsvg_parse_transform (affine, (const char *)atts[i + 1]);
453                                        else if (!strcmp ((char *)atts[i], "gradientUnits")) {
454                                                if (!strcmp ((char *)atts[i+1], "userSpaceOnUse"))
455                                                        obj_bbox = FALSE;
456                                                got_bbox = TRUE;
457                                        }
458                                }
459                }
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
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];
492
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 */
497        grad->obj_bbox = (cloned && !got_bbox) ? grad->obj_bbox : obj_bbox;
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;
503}
504
505/* exported to the paint server via rsvg-private.h */
506void
507rsvg_radial_gradient_free (RsvgDefVal *self)
508{
509        RsvgRadialGradient *z = (RsvgRadialGradient *)self;
510       
511        g_free (z->stops->stop);
512        g_free (z->stops);
513        g_free (self);
514}
515
516static void
517rsvg_start_radial_gradient (RsvgHandle *ctx, const xmlChar **atts, const char * tag) /* tag for conicalGradient */
518{
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;
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;
528        double affine[6];
529       
530        got_cx = got_cy = got_r = got_fx = got_fy = got_spread = got_transform = got_bbox = cloned = shallow_cloned = FALSE;
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                                                }
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                                        }
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                                }
597    }
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                }
605
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);
612        }
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        }
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];
636
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 */
641        grad->obj_bbox = (cloned && !got_bbox) ? grad->obj_bbox : obj_bbox;
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;
648}
649
650/* end gradients */
651
652static void
653rsvg_style_handler_free (RsvgSaxHandler *self)
654{
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);
662}
663
664static void
665rsvg_style_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len)
666{
667        RsvgSaxHandlerStyle *z = (RsvgSaxHandlerStyle *)self;
668        g_string_append_len (z->style, (const char *)ch, len);
669}
670
671static void
672rsvg_style_handler_start (RsvgSaxHandler *self, const xmlChar *name,
673                                                  const xmlChar **atts)
674{
675}
676
677static void
678rsvg_style_handler_end (RsvgSaxHandler *self, const xmlChar *name)
679{
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                }
691}
692
693static void
694rsvg_start_style (RsvgHandle *ctx, const xmlChar **atts)
695{
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;
708}
709
710/* */
711
712static void
713rsvg_defs_handler_free (RsvgSaxHandler *self)
714{
715        g_free (self);
716}
717
718static void
719rsvg_defs_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len)
720{
721}
722
723static void
724rsvg_defs_handler_start (RsvgSaxHandler *self, const xmlChar *name,
725                                                 const xmlChar **atts)
726{
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++;
739
740        /*
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         */
745
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);
754}
755
756static void
757rsvg_defs_handler_end (RsvgSaxHandler *self, const xmlChar *name)
758{
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                                }
769                        ctx->in_defs = FALSE;
770                }
771       
772        /* pop the state stack */
773        ctx->n_state--;
774        rsvg_state_finalize (&ctx->state[ctx->n_state]);
775}
776
777static void
778rsvg_start_defs (RsvgHandle *ctx, const xmlChar **atts)
779{
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;
787
788        ctx->in_defs = TRUE;   
789        ctx->handler = &handler->super;
790}
791
792/* end defs */
793
794static void
795rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts)
796{
797        RsvgHandle *ctx = (RsvgHandle *)data;
798
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");
849    }
850}
851
852static void
853rsvg_end_element (void *data, const xmlChar *name)
854{
855        RsvgHandle *ctx = (RsvgHandle *)data;
856       
857        if (ctx->handler_nest > 0 && ctx->handler != NULL)
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                                }
870
871                        if (!strcmp ((char *)name, "g"))
872                                rsvg_end_g (ctx);
873                        else if (!strcmp ((char *)name, "defs")) {
874                                ctx->in_defs = FALSE;
875                        }
876                       
877                        /* pop the state stack */
878                        ctx->n_state--;
879                        rsvg_state_finalize (&ctx->state[ctx->n_state]);
880                }
881}
882
883static void
884rsvg_characters (void *data, const xmlChar *ch, int len)
885{
886        RsvgHandle *ctx = (RsvgHandle *)data;
887       
888        if (ctx->handler && ctx->handler->characters != NULL)
889                ctx->handler->characters (ctx->handler, ch, len);
890}
891
892static xmlEntityPtr
893rsvg_get_entity (void *data, const xmlChar *name)
894{
895        RsvgHandle *ctx = (RsvgHandle *)data;
896       
897        return (xmlEntityPtr)g_hash_table_lookup (ctx->entities, name);
898}
899
900static void
901rsvg_entity_decl (void *data, const xmlChar *name, int type,
902                                  const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
903{
904        RsvgHandle *ctx = (RsvgHandle *)data;
905        GHashTable *entities = ctx->entities;
906        xmlEntityPtr entity;
907        char *dupname;
908
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);
922}
923
924static void
925rsvg_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
934static 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 */
957    rsvg_error_cb, /* xmlParserError */
958    rsvg_error_cb, /* xmlParserFatalError */
959    NULL, /* getParameterEntity */
960    rsvg_characters, /* cdataCallback */
961    NULL /* externalSubset */
962        /* initialized */
963};
964
965/**
966 * rsvg_error_quark
967 *
968 * The error domain for RSVG
969 *
970 * Returns: The error domain
971 */
972GQuark
973rsvg_error_quark (void)
974{
975        static GQuark q = 0;
976        if (q == 0)
977                q = g_quark_from_static_string ("rsvg-error-quark");
978       
979        return q;
980}
981
982gboolean
983rsvg_handle_write_impl (RsvgHandle    *handle,
984                                                const guchar  *buf,
985                                                gsize          count,
986                                                GError       **error)
987{
988        GError *real_error;
989        g_return_val_if_fail (handle != NULL, FALSE);
990       
991        handle->error = &real_error;
992        if (handle->ctxt == NULL)
993                {
994                        handle->ctxt = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, handle, NULL, 0, NULL);
995                        handle->ctxt->replaceEntities = TRUE;
996                }
997       
998        xmlParseChunk (handle->ctxt, buf, count, 0);
999       
1000        handle->error = NULL;
1001        /* FIXME: Error handling not implemented. */
1002        /*  if (*real_error != NULL)
1003                {
1004                g_propagate_error (error, real_error);
1005                return FALSE;
1006                }*/
1007  return TRUE;
1008}
1009
1010gboolean
1011rsvg_handle_close_impl (RsvgHandle  *handle,
1012                                                GError     **error)
1013{
1014        GError *real_error;
1015       
1016        handle->error = &real_error;
1017       
1018        if (handle->ctxt != NULL)
1019                {
1020                        xmlParseChunk (handle->ctxt, "", 0, 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
1034void
1035rsvg_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
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 *
1067 * Returns: A new #RsvgHandle
1068 **/
1069RsvgHandle *
1070rsvg_handle_new (void)
1071{
1072        RsvgHandle *handle;
1073       
1074        handle = g_new0 (RsvgHandle, 1);
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
1084void
1085rsvg_handle_init (RsvgHandle * handle)
1086{
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}
1100
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 */
1109void
1110rsvg_set_default_dpi (double dpi)
1111{
1112        if (dpi <= 0.)
1113                internal_dpi = RSVG_DEFAULT_DPI;
1114        else
1115                internal_dpi = dpi;
1116}
1117
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 */
1127void
1128rsvg_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;
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 **/
1151void
1152rsvg_handle_set_size_callback (RsvgHandle     *handle,
1153                                                           RsvgSizeFunc    size_func,
1154                                                           gpointer        user_data,
1155                                                           GDestroyNotify  user_data_destroy)
1156{
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;
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 *
1179 * Returns: #TRUE if the write was successful, or #FALSE if there was an
1180 * error.
1181 **/
1182gboolean
1183rsvg_handle_write (RsvgHandle    *handle,
1184                                   const guchar  *buf,
1185                                   gsize          count,
1186                                   GError       **error)
1187{
1188        if (handle->write)
1189                return (*handle->write) (handle, buf, count, error);
1190
1191        return FALSE;
1192}
1193
1194/**
1195 * rsvg_handle_close:
1196 * @handle: A #RsvgHandle
1197 * @error: A #GError
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 *
1203 * Returns: #TRUE if the loader closed successfully, or #FALSE if there was
1204 * an error.
1205 **/
1206gboolean
1207rsvg_handle_close (RsvgHandle  *handle,
1208                                   GError     **error)
1209{
1210        if (handle->close)
1211                return (*handle->close) (handle, error);
1212
1213        return FALSE;
1214}
1215
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 *
1226 * Returns: the pixbuf loaded by #handle, or %NULL.
1227 **/
1228GdkPixbuf *
1229rsvg_handle_get_pixbuf (RsvgHandle *handle)
1230{
1231        g_return_val_if_fail (handle != NULL, NULL);
1232       
1233        if (handle->pixbuf)
1234                return g_object_ref (handle->pixbuf);
1235
1236        return NULL;
1237}
1238
1239/**
1240 * rsvg_handle_free:
1241 * @handle: An #RsvgHandle
1242 *
1243 * Frees #handle.
1244 **/
1245void
1246rsvg_handle_free (RsvgHandle *handle)
1247{
1248        if (handle->free)
1249                (*handle->free) (handle);
1250}
1251
Note: See TracBrowser for help on using the repository browser.