Tizen 2.0 Release
[framework/graphics/cairo.git] / boilerplate / cairo-boilerplate-svg.c
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /*
3  * Copyright © 2004,2006 Red Hat, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without
7  * fee, provided that the above copyright notice appear in all copies
8  * and that both that copyright notice and this permission notice
9  * appear in supporting documentation, and that the name of
10  * Red Hat, Inc. not be used in advertising or publicity pertaining to
11  * distribution of the software without specific, written prior
12  * permission. Red Hat, Inc. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as
14  * is" without express or implied warranty.
15  *
16  * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18  * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
19  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
20  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
22  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author: Carl D. Worth <cworth@cworth.org>
25  */
26
27 #include "cairo-boilerplate-private.h"
28
29 #if CAIRO_CAN_TEST_SVG_SURFACE
30
31 #include <cairo-svg.h>
32 #include <cairo-svg-surface-private.h>
33 #include <cairo-paginated-surface-private.h>
34
35 #if HAVE_SIGNAL_H
36 #include <stdlib.h>
37 #include <signal.h>
38 #endif
39
40 #if HAVE_SYS_WAIT_H
41 #include <sys/wait.h>
42 #endif
43
44 #if ! CAIRO_HAS_RECORDING_SURFACE
45 #define CAIRO_SURFACE_TYPE_RECORDING CAIRO_INTERNAL_SURFACE_TYPE_RECORDING
46 #endif
47
48 static const cairo_user_data_key_t svg_closure_key;
49
50 typedef struct _svg_target_closure {
51     char    *filename;
52     int     width, height;
53     cairo_surface_t     *target;
54 } svg_target_closure_t;
55
56 static cairo_surface_t *
57 _cairo_boilerplate_svg_create_surface (const char                *name,
58                                        cairo_content_t            content,
59                                        cairo_svg_version_t        version,
60                                        double                     width,
61                                        double                     height,
62                                        double                     max_width,
63                                        double                     max_height,
64                                        cairo_boilerplate_mode_t   mode,
65                                        void                     **closure)
66 {
67     svg_target_closure_t *ptc;
68     cairo_surface_t *surface;
69     cairo_status_t status;
70
71     *closure = ptc = xmalloc (sizeof (svg_target_closure_t));
72
73     ptc->width = ceil (width);
74     ptc->height = ceil (height);
75
76     xasprintf (&ptc->filename, "%s.out.svg", name);
77     xunlink (ptc->filename);
78
79     surface = cairo_svg_surface_create (ptc->filename, width, height);
80     if (cairo_surface_status (surface))
81         goto CLEANUP_FILENAME;
82
83     cairo_svg_surface_restrict_to_version (surface, version);
84     cairo_surface_set_fallback_resolution (surface, 72., 72.);
85
86     if (content == CAIRO_CONTENT_COLOR) {
87         ptc->target = surface;
88         surface = cairo_surface_create_similar (ptc->target,
89                                                 CAIRO_CONTENT_COLOR,
90                                                 ptc->width, ptc->height);
91         if (cairo_surface_status (surface))
92             goto CLEANUP_TARGET;
93     } else
94         ptc->target = NULL;
95
96     status = cairo_surface_set_user_data (surface, &svg_closure_key, ptc, NULL);
97     if (status == CAIRO_STATUS_SUCCESS)
98         return surface;
99
100     cairo_surface_destroy (surface);
101     surface = cairo_boilerplate_surface_create_in_error (status);
102
103   CLEANUP_TARGET:
104     cairo_surface_destroy (ptc->target);
105   CLEANUP_FILENAME:
106     free (ptc->filename);
107     free (ptc);
108     return surface;
109 }
110
111 static cairo_surface_t *
112 _cairo_boilerplate_svg11_create_surface (const char                *name,
113                                          cairo_content_t            content,
114                                          double                     width,
115                                          double                     height,
116                                          double                     max_width,
117                                          double                     max_height,
118                                          cairo_boilerplate_mode_t   mode,
119                                          void                     **closure)
120 {
121     /* current default, but be explicit in case the default changes */
122     return _cairo_boilerplate_svg_create_surface (name, content,
123                                                   CAIRO_SVG_VERSION_1_1,
124                                                   width, height,
125                                                   max_width, max_height,
126                                                   mode,
127                                                   closure);
128 }
129
130 static cairo_surface_t *
131 _cairo_boilerplate_svg12_create_surface (const char                *name,
132                                          cairo_content_t            content,
133                                          double                     width,
134                                          double                     height,
135                                          double                     max_width,
136                                          double                     max_height,
137                                          cairo_boilerplate_mode_t   mode,
138                                          void                     **closure)
139 {
140     return _cairo_boilerplate_svg_create_surface (name, content,
141                                                   CAIRO_SVG_VERSION_1_2,
142                                                   width, height,
143                                                   max_width, max_height,
144                                                   mode,
145                                                   closure);
146 }
147
148 static cairo_status_t
149 _cairo_boilerplate_svg_finish_surface (cairo_surface_t *surface)
150 {
151     svg_target_closure_t *ptc = cairo_surface_get_user_data (surface,
152                                                              &svg_closure_key);
153     cairo_status_t status;
154
155     /* Both surface and ptc->target were originally created at the
156      * same dimensions. We want a 1:1 copy here, so we first clear any
157      * device offset on surface.
158      *
159      * In a more realistic use case of device offsets, the target of
160      * this copying would be of a different size than the source, and
161      * the offset would be desirable during the copy operation. */
162     cairo_surface_set_device_offset (surface, 0, 0);
163
164     if (ptc->target) {
165         cairo_t *cr;
166         cr = cairo_create (ptc->target);
167         cairo_set_source_surface (cr, surface, 0, 0);
168         cairo_paint (cr);
169         cairo_show_page (cr);
170         status = cairo_status (cr);
171         cairo_destroy (cr);
172
173         if (status)
174             return status;
175
176         cairo_surface_finish (surface);
177         status = cairo_surface_status (surface);
178         if (status)
179             return status;
180
181         surface = ptc->target;
182     }
183
184     cairo_surface_finish (surface);
185     status = cairo_surface_status (surface);
186     if (status)
187         return status;
188
189     return CAIRO_STATUS_SUCCESS;
190 }
191
192 static cairo_status_t
193 _cairo_boilerplate_svg_surface_write_to_png (cairo_surface_t *surface,
194                                              const char      *filename)
195 {
196     svg_target_closure_t *ptc = cairo_surface_get_user_data (surface,
197                                                              &svg_closure_key);
198     char    command[4096];
199     int exitstatus;
200
201     sprintf (command, "./svg2png %s %s",
202              ptc->filename, filename);
203
204     exitstatus = system (command);
205 #if _XOPEN_SOURCE && HAVE_SIGNAL_H
206     if (WIFSIGNALED (exitstatus))
207         raise (WTERMSIG (exitstatus));
208 #endif
209     if (exitstatus)
210         return CAIRO_STATUS_WRITE_ERROR;
211
212     return CAIRO_STATUS_SUCCESS;
213 }
214
215 static cairo_surface_t *
216 _cairo_boilerplate_svg_convert_to_image (cairo_surface_t *surface)
217 {
218     svg_target_closure_t *ptc = cairo_surface_get_user_data (surface,
219                                                              &svg_closure_key);
220
221     return cairo_boilerplate_convert_to_image (ptc->filename, 0);
222 }
223
224 static cairo_surface_t *
225 _cairo_boilerplate_svg_get_image_surface (cairo_surface_t *surface,
226                                           int              page,
227                                           int              width,
228                                           int              height)
229 {
230     cairo_surface_t *image;
231
232     if (page != 0)
233         return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
234
235     image = _cairo_boilerplate_svg_convert_to_image (surface);
236     cairo_surface_set_device_offset (image,
237                                      cairo_image_surface_get_width (image) - width,
238                                      cairo_image_surface_get_height (image) - height);
239     surface = _cairo_boilerplate_get_image_surface (image, 0, width, height);
240     cairo_surface_destroy (image);
241
242     return surface;
243 }
244
245 static void
246 _cairo_boilerplate_svg_cleanup (void *closure)
247 {
248     svg_target_closure_t *ptc = closure;
249     if (ptc->target != NULL) {
250         cairo_surface_finish (ptc->target);
251         cairo_surface_destroy (ptc->target);
252     }
253     free (ptc->filename);
254     free (ptc);
255 }
256
257 static void
258 _cairo_boilerplate_svg_force_fallbacks (cairo_surface_t *abstract_surface,
259                                        double            x_pixels_per_inch,
260                                        double            y_pixels_per_inch)
261 {
262     svg_target_closure_t *ptc = cairo_surface_get_user_data (abstract_surface,
263                                                              &svg_closure_key);
264
265     cairo_paginated_surface_t *paginated;
266     cairo_svg_surface_t *surface;
267
268     if (ptc->target)
269         abstract_surface = ptc->target;
270
271     paginated = (cairo_paginated_surface_t*) abstract_surface;
272     surface = (cairo_svg_surface_t*) paginated->target;
273     surface->force_fallbacks = TRUE;
274     cairo_surface_set_fallback_resolution (&paginated->base,
275                                            x_pixels_per_inch,
276                                            y_pixels_per_inch);
277 }
278
279 static const cairo_boilerplate_target_t targets[] = {
280     /* It seems we should be able to round-trip SVG content perfectly
281      * through librsvg and cairo, but for some mysterious reason, some
282      * systems get an error of 1 for some pixels on some of the text
283      * tests. XXX: I'd still like to chase these down at some point.
284      * For now just set the svg error tolerance to 1. */
285     {
286         "svg11", "svg", ".svg", NULL,
287         CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR_ALPHA, 1,
288         "cairo_svg_surface_create",
289         _cairo_boilerplate_svg11_create_surface,
290         cairo_surface_create_similar,
291         _cairo_boilerplate_svg_force_fallbacks,
292         _cairo_boilerplate_svg_finish_surface,
293         _cairo_boilerplate_svg_get_image_surface,
294         _cairo_boilerplate_svg_surface_write_to_png,
295         _cairo_boilerplate_svg_cleanup,
296         NULL, NULL, FALSE, TRUE, TRUE
297     },
298     {
299         "svg11", "svg", ".svg", NULL,
300         CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 1,
301         "cairo_svg_surface_create",
302         _cairo_boilerplate_svg11_create_surface,
303         cairo_surface_create_similar,
304         _cairo_boilerplate_svg_force_fallbacks,
305         _cairo_boilerplate_svg_finish_surface,
306         _cairo_boilerplate_svg_get_image_surface,
307         _cairo_boilerplate_svg_surface_write_to_png,
308         _cairo_boilerplate_svg_cleanup,
309         NULL, NULL, FALSE, TRUE, TRUE
310     },
311     {
312         "svg12", "svg", ".svg", NULL,
313         CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR_ALPHA, 1,
314         "cairo_svg_surface_create",
315         _cairo_boilerplate_svg12_create_surface,
316         cairo_surface_create_similar,
317         _cairo_boilerplate_svg_force_fallbacks,
318         _cairo_boilerplate_svg_finish_surface,
319         _cairo_boilerplate_svg_get_image_surface,
320         _cairo_boilerplate_svg_surface_write_to_png,
321         _cairo_boilerplate_svg_cleanup,
322         NULL, NULL, FALSE, TRUE, TRUE
323     },
324     {
325         "svg12", "svg", ".svg", NULL,
326         CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 1,
327         "cairo_svg_surface_create",
328         _cairo_boilerplate_svg12_create_surface,
329         cairo_surface_create_similar,
330         _cairo_boilerplate_svg_force_fallbacks,
331         _cairo_boilerplate_svg_finish_surface,
332         _cairo_boilerplate_svg_get_image_surface,
333         _cairo_boilerplate_svg_surface_write_to_png,
334         _cairo_boilerplate_svg_cleanup,
335         NULL, NULL, FALSE, TRUE, TRUE
336     },
337 };
338 CAIRO_BOILERPLATE (svg, targets)
339
340 #else
341
342 CAIRO_NO_BOILERPLATE (svg)
343
344 #endif