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

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