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 |
---|
49 | static double internal_dpi = RSVG_DEFAULT_DPI; |
---|
50 | |
---|
51 | static void |
---|
52 | rsvg_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 | |
---|
66 | static void |
---|
67 | rsvg_pixmap_destroy (guchar *pixels, gpointer data) |
---|
68 | { |
---|
69 | g_free (pixels); |
---|
70 | } |
---|
71 | |
---|
72 | static void |
---|
73 | rsvg_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 | |
---|
210 | static void |
---|
211 | rsvg_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 | |
---|
233 | static void |
---|
234 | rsvg_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 | |
---|
242 | typedef struct _RsvgSaxHandlerDefs { |
---|
243 | RsvgSaxHandler super; |
---|
244 | RsvgHandle *ctx; |
---|
245 | } RsvgSaxHandlerDefs; |
---|
246 | |
---|
247 | typedef struct _RsvgSaxHandlerStyle { |
---|
248 | RsvgSaxHandler super; |
---|
249 | RsvgSaxHandlerDefs *parent; |
---|
250 | RsvgHandle *ctx; |
---|
251 | GString *style; |
---|
252 | } RsvgSaxHandlerStyle; |
---|
253 | |
---|
254 | typedef struct _RsvgSaxHandlerGstops { |
---|
255 | RsvgSaxHandler super; |
---|
256 | RsvgSaxHandlerDefs *parent; |
---|
257 | RsvgHandle *ctx; |
---|
258 | RsvgGradientStops *stops; |
---|
259 | const char * parent_tag; |
---|
260 | } RsvgSaxHandlerGstops; |
---|
261 | |
---|
262 | static void |
---|
263 | rsvg_gradient_stop_handler_free (RsvgSaxHandler *self) |
---|
264 | { |
---|
265 | g_free (self); |
---|
266 | } |
---|
267 | |
---|
268 | static void |
---|
269 | rsvg_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 | |
---|
330 | static void |
---|
331 | rsvg_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 | |
---|
346 | static RsvgSaxHandler * |
---|
347 | rsvg_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 | |
---|
363 | static RsvgSaxHandler * |
---|
364 | rsvg_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 */ |
---|
386 | void |
---|
387 | rsvg_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 | |
---|
396 | static void |
---|
397 | rsvg_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 */ |
---|
496 | void |
---|
497 | rsvg_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 | |
---|
506 | static void |
---|
507 | rsvg_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 | |
---|
626 | static void |
---|
627 | rsvg_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 | |
---|
638 | static void |
---|
639 | rsvg_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 | |
---|
645 | static void |
---|
646 | rsvg_style_handler_start (RsvgSaxHandler *self, const xmlChar *name, |
---|
647 | const xmlChar **atts) |
---|
648 | { |
---|
649 | } |
---|
650 | |
---|
651 | static void |
---|
652 | rsvg_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 | |
---|
667 | static void |
---|
668 | rsvg_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 | |
---|
686 | static void |
---|
687 | rsvg_defs_handler_free (RsvgSaxHandler *self) |
---|
688 | { |
---|
689 | g_free (self); |
---|
690 | } |
---|
691 | |
---|
692 | static void |
---|
693 | rsvg_defs_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) |
---|
694 | { |
---|
695 | } |
---|
696 | |
---|
697 | static void |
---|
698 | rsvg_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 | |
---|
730 | static void |
---|
731 | rsvg_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 | |
---|
750 | static void |
---|
751 | rsvg_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 | |
---|
766 | static void |
---|
767 | rsvg_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 | |
---|
824 | static void |
---|
825 | rsvg_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 | |
---|
852 | static void |
---|
853 | rsvg_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 | |
---|
861 | static xmlEntityPtr |
---|
862 | rsvg_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 | |
---|
869 | static void |
---|
870 | rsvg_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 | |
---|
893 | static void |
---|
894 | rsvg_error_cb (void *data, const char *msg, ...) |
---|
895 | { |
---|
896 | va_list args; |
---|
897 | |
---|
898 | va_start (args, msg); |
---|
899 | vfprintf (stderr, msg, args); |
---|
900 | va_end (args); |
---|
901 | } |
---|
902 | |
---|
903 | static xmlSAXHandler rsvgSAXHandlerStruct = { |
---|
904 | NULL, /* internalSubset */ |
---|
905 | NULL, /* isStandalone */ |
---|
906 | NULL, /* hasInternalSubset */ |
---|
907 | NULL, /* hasExternalSubset */ |
---|
908 | NULL, /* resolveEntity */ |
---|
909 | rsvg_get_entity, /* getEntity */ |
---|
910 | rsvg_entity_decl, /* entityDecl */ |
---|
911 | NULL, /* notationDecl */ |
---|
912 | NULL, /* attributeDecl */ |
---|
913 | NULL, /* elementDecl */ |
---|
914 | NULL, /* unparsedEntityDecl */ |
---|
915 | NULL, /* setDocumentLocator */ |
---|
916 | NULL, /* startDocument */ |
---|
917 | NULL, /* endDocument */ |
---|
918 | rsvg_start_element, /* startElement */ |
---|
919 | rsvg_end_element, /* endElement */ |
---|
920 | NULL, /* reference */ |
---|
921 | rsvg_characters, /* characters */ |
---|
922 | NULL, /* ignorableWhitespace */ |
---|
923 | NULL, /* processingInstruction */ |
---|
924 | NULL, /* comment */ |
---|
925 | NULL, /* xmlParserWarning */ |
---|
926 | rsvg_error_cb, /* xmlParserError */ |
---|
927 | rsvg_error_cb, /* xmlParserFatalError */ |
---|
928 | NULL, /* getParameterEntity */ |
---|
929 | rsvg_characters, /* cdataCallback */ |
---|
930 | NULL /* */ |
---|
931 | }; |
---|
932 | |
---|
933 | GQuark |
---|
934 | rsvg_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 | **/ |
---|
954 | RsvgHandle * |
---|
955 | rsvg_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 | */ |
---|
984 | void |
---|
985 | rsvg_set_default_dpi (double dpi) |
---|
986 | { |
---|
987 | if (dpi <= 0.) |
---|
988 | internal_dpi = RSVG_DEFAULT_DPI; |
---|
989 | else |
---|
990 | internal_dpi = dpi; |
---|
991 | } |
---|
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 | */ |
---|
1002 | void |
---|
1003 | rsvg_handle_set_dpi (RsvgHandle * handle, double dpi) |
---|
1004 | { |
---|
1005 | g_return_if_fail (handle != NULL); |
---|
1006 | |
---|
1007 | if (dpi <= 0.) |
---|
1008 | handle->dpi = internal_dpi; |
---|
1009 | else |
---|
1010 | handle->dpi = dpi; |
---|
1011 | } |
---|
1012 | |
---|
1013 | /** |
---|
1014 | * rsvg_handle_set_size_callback: |
---|
1015 | * @handle: An #RsvgHandle |
---|
1016 | * @size_func: A sizing function, or %NULL |
---|
1017 | * @user_data: User data to pass to @size_func, or %NULL |
---|
1018 | * @user_data_destroy: Destroy function for @user_data, or %NULL |
---|
1019 | * |
---|
1020 | * Sets the sizing function for the @handle. This function is called right |
---|
1021 | * after the size of the image has been loaded. The size of the image is passed |
---|
1022 | * in to the function, which may then modify these values to set the real size |
---|
1023 | * of the generated pixbuf. If the image has no associated size, then the size |
---|
1024 | * arguments are set to -1. |
---|
1025 | **/ |
---|
1026 | void |
---|
1027 | rsvg_handle_set_size_callback (RsvgHandle *handle, |
---|
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 | **/ |
---|
1057 | gboolean |
---|
1058 | rsvg_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 | **/ |
---|
1096 | gboolean |
---|
1097 | rsvg_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 | **/ |
---|
1133 | GdkPixbuf * |
---|
1134 | rsvg_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 | **/ |
---|
1150 | void |
---|
1151 | rsvg_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 | |
---|