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_PDF_SURFACE
30
31 #include <cairo-pdf.h>
32 #include <cairo-pdf-surface-private.h>
33 #include <cairo-paginated-surface-private.h>
34
35 #if HAVE_SIGNAL_H
36 #include <signal.h>
37 #endif
38
39 #if HAVE_SYS_WAIT_H
40 #include <sys/wait.h>
41 #endif
42
43 #if ! CAIRO_HAS_RECORDING_SURFACE
44 #define CAIRO_SURFACE_TYPE_RECORDING CAIRO_INTERNAL_SURFACE_TYPE_RECORDING
45 #endif
46
47 static const cairo_user_data_key_t pdf_closure_key;
48
49 typedef struct _pdf_target_closure
50 {
51 char *filename;
52 int width;
53 int height;
54 cairo_surface_t *target;
55 } pdf_target_closure_t;
56
57 static cairo_surface_t *
_cairo_boilerplate_pdf_create_surface(const char * name,cairo_content_t content,double width,double height,double max_width,double max_height,cairo_boilerplate_mode_t mode,void ** closure)58 _cairo_boilerplate_pdf_create_surface (const char *name,
59 cairo_content_t content,
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 pdf_target_closure_t *ptc;
68 cairo_surface_t *surface;
69 cairo_status_t status;
70
71 /* Sanitize back to a real cairo_content_t value. */
72 if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
73 content = CAIRO_CONTENT_COLOR_ALPHA;
74
75 *closure = ptc = xmalloc (sizeof (pdf_target_closure_t));
76
77 ptc->width = ceil (width);
78 ptc->height = ceil (height);
79
80 xasprintf (&ptc->filename, "%s.out.pdf", name);
81 xunlink (ptc->filename);
82
83 surface = cairo_pdf_surface_create (ptc->filename, width, height);
84 if (cairo_surface_status (surface))
85 goto CLEANUP_FILENAME;
86
87 cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATE_DATE, NULL);
88 cairo_surface_set_fallback_resolution (surface, 72., 72.);
89
90 if (content == CAIRO_CONTENT_COLOR) {
91 ptc->target = surface;
92 surface = cairo_surface_create_similar (ptc->target,
93 CAIRO_CONTENT_COLOR,
94 ptc->width, ptc->height);
95 if (cairo_surface_status (surface))
96 goto CLEANUP_TARGET;
97 } else {
98 ptc->target = NULL;
99 }
100
101 status = cairo_surface_set_user_data (surface, &pdf_closure_key, ptc, NULL);
102 if (status == CAIRO_STATUS_SUCCESS)
103 return surface;
104
105 cairo_surface_destroy (surface);
106 surface = cairo_boilerplate_surface_create_in_error (status);
107
108 CLEANUP_TARGET:
109 cairo_surface_destroy (ptc->target);
110 CLEANUP_FILENAME:
111 free (ptc->filename);
112 free (ptc);
113 return surface;
114 }
115
116 static cairo_status_t
_cairo_boilerplate_pdf_finish_surface(cairo_surface_t * surface)117 _cairo_boilerplate_pdf_finish_surface (cairo_surface_t *surface)
118 {
119 pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface,
120 &pdf_closure_key);
121 cairo_status_t status;
122
123 if (ptc->target) {
124 /* Both surface and ptc->target were originally created at the
125 * same dimensions. We want a 1:1 copy here, so we first clear any
126 * device offset and scale on surface.
127 *
128 * In a more realistic use case of device offsets, the target of
129 * this copying would be of a different size than the source, and
130 * the offset would be desirable during the copy operation. */
131 double x_offset, y_offset;
132 double x_scale, y_scale;
133 cairo_surface_get_device_offset (surface, &x_offset, &y_offset);
134 cairo_surface_get_device_scale (surface, &x_scale, &y_scale);
135 cairo_surface_set_device_offset (surface, 0, 0);
136 cairo_surface_set_device_scale (surface, 1, 1);
137 cairo_t *cr;
138 cr = cairo_create (ptc->target);
139 cairo_set_source_surface (cr, surface, 0, 0);
140 cairo_paint (cr);
141 cairo_show_page (cr);
142 status = cairo_status (cr);
143 cairo_destroy (cr);
144 cairo_surface_set_device_offset (surface, x_offset, y_offset);
145 cairo_surface_set_device_scale (surface, x_scale, y_scale);
146
147 if (status)
148 return status;
149
150 cairo_surface_finish (surface);
151 status = cairo_surface_status (surface);
152 if (status)
153 return status;
154
155 surface = ptc->target;
156 }
157
158 cairo_surface_finish (surface);
159 status = cairo_surface_status (surface);
160 if (status)
161 return status;
162
163 return CAIRO_STATUS_SUCCESS;
164 }
165
166 static cairo_status_t
_cairo_boilerplate_pdf_surface_write_to_png(cairo_surface_t * surface,const char * filename)167 _cairo_boilerplate_pdf_surface_write_to_png (cairo_surface_t *surface,
168 const char *filename)
169 {
170 pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface, &pdf_closure_key);
171 char command[4096];
172 int exitstatus;
173
174 sprintf (command, "./pdf2png %s %s 1",
175 ptc->filename, filename);
176
177 exitstatus = system (command);
178 #if _XOPEN_SOURCE && HAVE_SIGNAL_H
179 if (WIFSIGNALED (exitstatus))
180 raise (WTERMSIG (exitstatus));
181 #endif
182 if (exitstatus)
183 return CAIRO_STATUS_WRITE_ERROR;
184
185 return CAIRO_STATUS_SUCCESS;
186 }
187
188 static cairo_surface_t *
_cairo_boilerplate_pdf_convert_to_image(cairo_surface_t * surface,int page)189 _cairo_boilerplate_pdf_convert_to_image (cairo_surface_t *surface,
190 int page)
191 {
192 pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface,
193 &pdf_closure_key);
194
195 return cairo_boilerplate_convert_to_image (ptc->filename, page+1);
196 }
197
198 static cairo_surface_t *
_cairo_boilerplate_pdf_get_image_surface(cairo_surface_t * surface,int page,int width,int height)199 _cairo_boilerplate_pdf_get_image_surface (cairo_surface_t *surface,
200 int page,
201 int width,
202 int height)
203 {
204 cairo_surface_t *image;
205 double x_offset, y_offset;
206 double x_scale, y_scale;
207
208 image = _cairo_boilerplate_pdf_convert_to_image (surface, page);
209 cairo_surface_get_device_offset (surface, &x_offset, &y_offset);
210 cairo_surface_get_device_scale (surface, &x_scale, &y_scale);
211 cairo_surface_set_device_offset (image, x_offset, y_offset);
212 cairo_surface_set_device_scale (image, x_scale, y_scale);
213 surface = _cairo_boilerplate_get_image_surface (image, 0, width, height);
214 cairo_surface_destroy (image);
215
216 return surface;
217 }
218
219 static void
_cairo_boilerplate_pdf_cleanup(void * closure)220 _cairo_boilerplate_pdf_cleanup (void *closure)
221 {
222 pdf_target_closure_t *ptc = closure;
223 if (ptc->target) {
224 cairo_surface_finish (ptc->target);
225 cairo_surface_destroy (ptc->target);
226 }
227 free (ptc->filename);
228 free (ptc);
229 }
230
231 static void
_cairo_boilerplate_pdf_force_fallbacks(cairo_surface_t * abstract_surface,double x_pixels_per_inch,double y_pixels_per_inch)232 _cairo_boilerplate_pdf_force_fallbacks (cairo_surface_t *abstract_surface,
233 double x_pixels_per_inch,
234 double y_pixels_per_inch)
235 {
236 pdf_target_closure_t *ptc = cairo_surface_get_user_data (abstract_surface,
237 &pdf_closure_key);
238
239 cairo_paginated_surface_t *paginated;
240 cairo_pdf_surface_t *surface;
241
242 if (ptc->target)
243 abstract_surface = ptc->target;
244
245 paginated = (cairo_paginated_surface_t*) abstract_surface;
246 surface = (cairo_pdf_surface_t*) paginated->target;
247 surface->force_fallbacks = TRUE;
248 cairo_surface_set_fallback_resolution (&paginated->base,
249 x_pixels_per_inch,
250 y_pixels_per_inch);
251 }
252
253 static const cairo_boilerplate_target_t targets[] = {
254 {
255 "pdf", "pdf", ".pdf", NULL,
256 CAIRO_SURFACE_TYPE_PDF,
257 CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
258 "cairo_pdf_surface_create",
259 _cairo_boilerplate_pdf_create_surface,
260 cairo_surface_create_similar,
261 _cairo_boilerplate_pdf_force_fallbacks,
262 _cairo_boilerplate_pdf_finish_surface,
263 _cairo_boilerplate_pdf_get_image_surface,
264 _cairo_boilerplate_pdf_surface_write_to_png,
265 _cairo_boilerplate_pdf_cleanup,
266 NULL, NULL, FALSE, TRUE, TRUE
267 },
268 {
269 "pdf", "pdf", ".pdf", NULL,
270 CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
271 "cairo_pdf_surface_create",
272 _cairo_boilerplate_pdf_create_surface,
273 cairo_surface_create_similar,
274 _cairo_boilerplate_pdf_force_fallbacks,
275 _cairo_boilerplate_pdf_finish_surface,
276 _cairo_boilerplate_pdf_get_image_surface,
277 _cairo_boilerplate_pdf_surface_write_to_png,
278 _cairo_boilerplate_pdf_cleanup,
279 NULL, NULL, FALSE, TRUE, TRUE
280 },
281 };
282 CAIRO_BOILERPLATE (pdf, targets)
283
284 #else
285
286 CAIRO_NO_BOILERPLATE (pdf)
287
288 #endif
289