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_PS_SURFACE
30

            
31
#include <cairo-ps.h>
32

            
33
#include <cairo-ps-surface-private.h>
34
#include <cairo-paginated-surface-private.h>
35

            
36
#if HAVE_SIGNAL_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 ps_closure_key;
49

            
50
typedef struct _ps_target_closure {
51
    char		*filename;
52
    int 		 width;
53
    int 		 height;
54
    cairo_surface_t	*target;
55
    cairo_ps_level_t	 level;
56
} ps_target_closure_t;
57

            
58
static cairo_status_t
59
_cairo_boilerplate_ps_surface_set_creation_date (cairo_surface_t *abstract_surface,
60
						 time_t 	  date)
61
{
62
    cairo_paginated_surface_t *paginated = (cairo_paginated_surface_t*) abstract_surface;
63
    cairo_ps_surface_t *surface;
64

            
65
    if (cairo_surface_get_type (abstract_surface) != CAIRO_SURFACE_TYPE_PS)
66
	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
67

            
68
    surface = (cairo_ps_surface_t*) paginated->target;
69

            
70
    surface->has_creation_date = TRUE;
71
    surface->creation_date = date;
72

            
73
    return CAIRO_STATUS_SUCCESS;
74
}
75

            
76
static cairo_surface_t *
77
_cairo_boilerplate_ps_create_surface (const char		*name,
78
				      cairo_content_t		 content,
79
				      cairo_ps_level_t		 level,
80
				      double			 width,
81
				      double			 height,
82
				      double			 max_width,
83
				      double			 max_height,
84
				      cairo_boilerplate_mode_t	 mode,
85
				      void		       **closure)
86
{
87
    ps_target_closure_t *ptc;
88
    cairo_surface_t *surface;
89
    cairo_status_t status;
90

            
91
    /* Sanitize back to a real cairo_content_t value. */
92
    if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
93
	content = CAIRO_CONTENT_COLOR_ALPHA;
94

            
95
    *closure = ptc = xmalloc (sizeof (ps_target_closure_t));
96

            
97
    xasprintf (&ptc->filename, "%s.out.ps", name);
98
    xunlink (ptc->filename);
99

            
100
    ptc->level = level;
101
    ptc->width = ceil (width);
102
    ptc->height = ceil (height);
103

            
104
    surface = cairo_ps_surface_create (ptc->filename, width, height);
105
    if (cairo_surface_status (surface))
106
	goto CLEANUP_FILENAME;
107

            
108
    cairo_ps_surface_restrict_to_level (surface, level);
109
    _cairo_boilerplate_ps_surface_set_creation_date (surface, 0);
110
    cairo_surface_set_fallback_resolution (surface, 72., 72.);
111

            
112
    if (content == CAIRO_CONTENT_COLOR) {
113
	ptc->target = surface;
114
	surface = cairo_surface_create_similar (ptc->target,
115
						CAIRO_CONTENT_COLOR,
116
						ptc->width, ptc->height);
117
	if (cairo_surface_status (surface))
118
	    goto CLEANUP_TARGET;
119
    } else {
120
	ptc->target = NULL;
121
    }
122

            
123
    status = cairo_surface_set_user_data (surface, &ps_closure_key, ptc, NULL);
124
    if (status == CAIRO_STATUS_SUCCESS)
125
	return surface;
126

            
127
    cairo_surface_destroy (surface);
128
    surface = cairo_boilerplate_surface_create_in_error (status);
129

            
130
  CLEANUP_TARGET:
131
    cairo_surface_destroy (ptc->target);
132
  CLEANUP_FILENAME:
133
    free (ptc->filename);
134
    free (ptc);
135
    return surface;
136
}
137

            
138
static cairo_surface_t *
139
_cairo_boilerplate_ps2_create_surface (const char		 *name,
140
				       cairo_content_t		  content,
141
				       double			  width,
142
				       double			  height,
143
				       double			  max_width,
144
				       double			  max_height,
145
				       cairo_boilerplate_mode_t   mode,
146
				       void			**closure)
147
{
148
    return _cairo_boilerplate_ps_create_surface (name, content,
149
						 CAIRO_PS_LEVEL_2,
150
						 width, height,
151
						 max_width, max_height,
152
						 mode,
153
						 closure);
154
}
155

            
156
static cairo_surface_t *
157
_cairo_boilerplate_ps3_create_surface (const char		 *name,
158
				       cairo_content_t		  content,
159
				       double			  width,
160
				       double			  height,
161
				       double			  max_width,
162
				       double			  max_height,
163
				       cairo_boilerplate_mode_t   mode,
164
				       void			**closure)
165
{
166
    return _cairo_boilerplate_ps_create_surface (name, content,
167
						 CAIRO_PS_LEVEL_3,
168
						 width, height,
169
						 max_width, max_height,
170
						 mode,
171
						 closure);
172
}
173

            
174
static cairo_status_t
175
_cairo_boilerplate_ps_finish_surface (cairo_surface_t *surface)
176
{
177
    ps_target_closure_t *ptc = cairo_surface_get_user_data (surface,
178
							    &ps_closure_key);
179
    cairo_status_t status;
180

            
181
    if (ptc->target) {
182
	/* Both surface and ptc->target were originally created at the
183
	 * same dimensions. We want a 1:1 copy here, so we first clear any
184
	 * device offset and scale on surface.
185
	 *
186
	 * In a more realistic use case of device offsets, the target of
187
	 * this copying would be of a different size than the source, and
188
	 * the offset would be desirable during the copy operation. */
189
	double x_offset, y_offset;
190
	double x_scale, y_scale;
191
	cairo_surface_get_device_offset (surface, &x_offset, &y_offset);
192
	cairo_surface_get_device_scale (surface, &x_scale, &y_scale);
193
	cairo_surface_set_device_offset (surface, 0, 0);
194
	cairo_surface_set_device_scale (surface, 1, 1);
195
	cairo_t *cr;
196
	cr = cairo_create (ptc->target);
197
	cairo_set_source_surface (cr, surface, 0, 0);
198
	cairo_paint (cr);
199
	cairo_show_page (cr);
200
	status = cairo_status (cr);
201
	cairo_destroy (cr);
202
	cairo_surface_set_device_offset (surface, x_offset, y_offset);
203
	cairo_surface_set_device_scale (surface, x_scale, y_scale);
204

            
205
	if (status)
206
	    return status;
207

            
208
	cairo_surface_finish (surface);
209
	status = cairo_surface_status (surface);
210
	if (status)
211
	    return status;
212

            
213
	surface = ptc->target;
214
    }
215

            
216
    cairo_surface_finish (surface);
217
    return cairo_surface_status (surface);
218
}
219

            
220
static cairo_status_t
221
_cairo_boilerplate_ps_surface_write_to_png (cairo_surface_t *surface,
222
					    const char	    *filename)
223
{
224
    ps_target_closure_t *ptc = cairo_surface_get_user_data (surface,
225
							    &ps_closure_key);
226
    char    command[4096];
227
    int exitstatus;
228

            
229
    sprintf (command, "gs -q -r72 -g%dx%d -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pngalpha -sOutputFile=%s %s",
230
	     ptc->width, ptc->height, filename,
231
	     ptc->filename);
232
    exitstatus = system (command);
233
#if _XOPEN_SOURCE && HAVE_SIGNAL_H
234
    if (WIFSIGNALED (exitstatus))
235
	raise (WTERMSIG (exitstatus));
236
#endif
237
    if (exitstatus)
238
	return CAIRO_STATUS_WRITE_ERROR;
239

            
240
    return CAIRO_STATUS_SUCCESS;
241
}
242

            
243
static cairo_surface_t *
244
_cairo_boilerplate_ps_get_image_surface (cairo_surface_t *surface,
245
					 int		  page,
246
					 int		  width,
247
					 int		  height)
248
{
249
    ps_target_closure_t *ptc = cairo_surface_get_user_data (surface,
250
							    &ps_closure_key);
251
    double x_offset, y_offset;
252
    double x_scale, y_scale;
253
    char *filename;
254
    cairo_status_t status;
255

            
256
    if (page == 0)
257
	xasprintf (&filename, "%s.png", ptc->filename);
258
    else
259
	xasprintf (&filename, "%s-%%05d.png", ptc->filename);
260
    status = _cairo_boilerplate_ps_surface_write_to_png (surface, filename);
261
    if (status)
262
	return cairo_boilerplate_surface_create_in_error (status);
263

            
264
    if (page != 0) {
265
	free (filename);
266
	xasprintf (&filename, "%s-%05d.png", ptc->filename, page);
267
    }
268
    cairo_surface_t *converted = cairo_boilerplate_get_image_surface_from_png (filename,
269
									       ptc->width,
270
									       ptc->height,
271
									       ptc->target == NULL);
272

            
273
    remove (filename);
274
    free (filename);
275

            
276
    cairo_surface_t *image = cairo_image_surface_create(ptc->target ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32,
277
							width,
278
							height);
279
    cairo_surface_get_device_offset (surface, &x_offset, &y_offset);
280
    cairo_surface_get_device_scale (surface, &x_scale, &y_scale);
281
    cairo_surface_set_device_offset (converted, x_offset, y_offset);
282
    cairo_surface_set_device_scale (converted, x_scale, y_scale);
283
    cairo_t *cr = cairo_create (image);
284
    cairo_set_source_surface (cr, converted, 0, 0);
285
    cairo_paint (cr);
286
    cairo_destroy (cr);
287
    cairo_surface_destroy (converted);
288

            
289
    return image;
290
}
291

            
292
static void
293
_cairo_boilerplate_ps_cleanup (void *closure)
294
{
295
    ps_target_closure_t *ptc = closure;
296
    if (ptc->target) {
297
	cairo_surface_finish (ptc->target);
298
	cairo_surface_destroy (ptc->target);
299
    }
300
    free (ptc->filename);
301
    free (ptc);
302
}
303

            
304
static void
305
_cairo_boilerplate_ps_force_fallbacks (cairo_surface_t *abstract_surface,
306
				       double		 x_pixels_per_inch,
307
				       double		 y_pixels_per_inch)
308
{
309
    ps_target_closure_t *ptc = cairo_surface_get_user_data (abstract_surface,
310
							    &ps_closure_key);
311

            
312
    cairo_paginated_surface_t *paginated;
313
    cairo_ps_surface_t *surface;
314

            
315
    if (ptc->target)
316
	abstract_surface = ptc->target;
317

            
318
    paginated = (cairo_paginated_surface_t*) abstract_surface;
319
    surface = (cairo_ps_surface_t*) paginated->target;
320
    surface->force_fallbacks = TRUE;
321
    cairo_surface_set_fallback_resolution (&paginated->base,
322
					   x_pixels_per_inch,
323
					   y_pixels_per_inch);
324
}
325

            
326
static const cairo_boilerplate_target_t targets[] = {
327
    {
328
	"ps2", "ps", ".ps", NULL,
329
	CAIRO_SURFACE_TYPE_PS,
330
	CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
331
	"cairo_ps_surface_create",
332
	_cairo_boilerplate_ps2_create_surface,
333
	cairo_surface_create_similar,
334
	_cairo_boilerplate_ps_force_fallbacks,
335
	_cairo_boilerplate_ps_finish_surface,
336
	_cairo_boilerplate_ps_get_image_surface,
337
	_cairo_boilerplate_ps_surface_write_to_png,
338
	_cairo_boilerplate_ps_cleanup,
339
	NULL, NULL, FALSE, TRUE, TRUE
340
    },
341
    {
342
	"ps2", "ps", ".ps", NULL,
343
	CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
344
	"cairo_ps_surface_create",
345
	_cairo_boilerplate_ps2_create_surface,
346
	cairo_surface_create_similar,
347
	_cairo_boilerplate_ps_force_fallbacks,
348
	_cairo_boilerplate_ps_finish_surface,
349
	_cairo_boilerplate_ps_get_image_surface,
350
	_cairo_boilerplate_ps_surface_write_to_png,
351
	_cairo_boilerplate_ps_cleanup,
352
	NULL, NULL, FALSE, TRUE, TRUE
353
    },
354
    {
355
	"ps3", "ps", ".ps", NULL,
356
	CAIRO_SURFACE_TYPE_PS,
357
	CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
358
	"cairo_ps_surface_create",
359
	_cairo_boilerplate_ps3_create_surface,
360
	cairo_surface_create_similar,
361
	_cairo_boilerplate_ps_force_fallbacks,
362
	_cairo_boilerplate_ps_finish_surface,
363
	_cairo_boilerplate_ps_get_image_surface,
364
	_cairo_boilerplate_ps_surface_write_to_png,
365
	_cairo_boilerplate_ps_cleanup,
366
	NULL, NULL, FALSE, TRUE, TRUE
367
    },
368
    {
369
	"ps3", "ps", ".ps", NULL,
370
	CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
371
	"cairo_ps_surface_create",
372
	_cairo_boilerplate_ps3_create_surface,
373
	cairo_surface_create_similar,
374
	_cairo_boilerplate_ps_force_fallbacks,
375
	_cairo_boilerplate_ps_finish_surface,
376
	_cairo_boilerplate_ps_get_image_surface,
377
	_cairo_boilerplate_ps_surface_write_to_png,
378
	_cairo_boilerplate_ps_cleanup,
379
	NULL, NULL, FALSE, TRUE, TRUE
380
    },
381
};
382
1
CAIRO_BOILERPLATE (ps, targets)
383

            
384
#else
385

            
386
CAIRO_NO_BOILERPLATE (ps)
387

            
388
#endif