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

Revision 18805, 34.3 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18804, which included commits to RCS files with non-trunk default branches.
RevLine 
[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
49static double internal_dpi = RSVG_DEFAULT_DPI;
[18351]50
[17276]51static void
52rsvg_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
66static void
67rsvg_pixmap_destroy (guchar *pixels, gpointer data)
68{
[18608]69        g_free (pixels);
[17276]70}
71
72static void
[18274]73rsvg_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
210static void
[18608]211rsvg_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
233static void
[18608]234rsvg_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]242typedef struct _RsvgSaxHandlerDefs {
243        RsvgSaxHandler super;
244        RsvgHandle *ctx;
245} RsvgSaxHandlerDefs;
[17276]246
[18608]247typedef struct _RsvgSaxHandlerStyle {
248        RsvgSaxHandler super;
249        RsvgSaxHandlerDefs *parent;
250        RsvgHandle *ctx;
251        GString *style;
252} RsvgSaxHandlerStyle;
[17276]253
[18608]254typedef struct _RsvgSaxHandlerGstops {
255        RsvgSaxHandler super;
256        RsvgSaxHandlerDefs *parent;
257        RsvgHandle *ctx;
258        RsvgGradientStops *stops;
259        const char * parent_tag;
260} RsvgSaxHandlerGstops;
[17276]261
262static void
[18608]263rsvg_gradient_stop_handler_free (RsvgSaxHandler *self)
[17276]264{
[18608]265        g_free (self);
[17276]266}
267
268static void
[18608]269rsvg_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
330static void
[18608]331rsvg_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]346static RsvgSaxHandler *
347rsvg_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]363static RsvgSaxHandler *
364rsvg_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 */
386void
387rsvg_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]396static void
397rsvg_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 */
506void
507rsvg_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
516static void
[18608]517rsvg_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]652static void
[18608]653rsvg_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
664static void
[18608]665rsvg_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
671static void
[18608]672rsvg_style_handler_start (RsvgSaxHandler *self, const xmlChar *name,
673                                                  const xmlChar **atts)
[17276]674{
[18274]675}
676
677static void
[18608]678rsvg_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]693static void
[18608]694rsvg_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]712static void
[18608]713rsvg_defs_handler_free (RsvgSaxHandler *self)
[17276]714{
[18608]715        g_free (self);
[17276]716}
717
718static void
[18608]719rsvg_defs_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len)
[17276]720{
721}
722
723static void
[18608]724rsvg_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
756static void
[18608]757rsvg_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
777static void
[18608]778rsvg_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
794static void
[18351]795rsvg_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
852static void
853rsvg_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
883static void
884rsvg_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
892static xmlEntityPtr
893rsvg_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
900static void
901rsvg_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]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
[17276]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 */
[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]971GQuark
972rsvg_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]981gboolean
982rsvg_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
1009gboolean
1010rsvg_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
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
[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 **/
1069RsvgHandle *
1070rsvg_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
1084void
1085rsvg_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 */
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}
[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 */
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;
[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 **/
1151void
1152rsvg_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 **/
1182gboolean
1183rsvg_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 **/
1206gboolean
1207rsvg_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 **/
1228GdkPixbuf *
1229rsvg_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 **/
1245void
1246rsvg_handle_free (RsvgHandle *handle)
1247{
[18804]1248        if (handle->free)
1249                (*handle->free) (handle);
[17276]1250}
1251
Note: See TracBrowser for help on using the repository browser.