Update change log.
[platform/upstream/cairo.git] / boilerplate / cairo-boilerplate-win32-printing.c
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /*
3  * Copyright © 2004,2006 Red Hat, Inc.
4  * Copyright © 2007, Adrian Johnson
5  *
6  * Permission to use, copy, modify, distribute, and sell this software
7  * and its documentation for any purpose is hereby granted without
8  * fee, provided that the above copyright notice appear in all copies
9  * and that both that copyright notice and this permission notice
10  * appear in supporting documentation, and that the name of
11  * Red Hat, Inc. not be used in advertising or publicity pertaining to
12  * distribution of the software without specific, written prior
13  * permission. Red Hat, Inc. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as
15  * is" without express or implied warranty.
16  *
17  * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
18  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
19  * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
20  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
21  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
22  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
23  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  *
25  * Authors: Carl D. Worth <cworth@cworth.org>
26  *          Adrian Johnson <ajohnson@redneon.com>
27  */
28
29 /* We require Windows 2000 features such as GetDefaultPrinter() */
30 #if !defined(WINVER) || (WINVER < 0x0500)
31 # define WINVER 0x0500
32 #endif
33 #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
34 # define _WIN32_WINNT 0x0500
35 #endif
36
37 #include "cairo-boilerplate-private.h"
38
39 #if CAIRO_CAN_TEST_WIN32_PRINTING_SURFACE
40
41 #include <cairo-win32.h>
42 #include <cairo-paginated-surface-private.h>
43
44 #include <windows.h>
45
46 #if !defined(POSTSCRIPT_IDENTIFY)
47 # define POSTSCRIPT_IDENTIFY 0x1015
48 #endif
49
50 #if !defined(PSIDENT_GDICENTRIC)
51 # define PSIDENT_GDICENTRIC 0x0000
52 #endif
53
54 #if !defined(GET_PS_FEATURESETTING)
55 # define GET_PS_FEATURESETTING 0x1019
56 #endif
57
58 #if !defined(FEATURESETTING_PSLEVEL)
59 # define FEATURESETTING_PSLEVEL 0x0002
60 #endif
61
62 static cairo_status_t
63 _cairo_win32_print_gdi_error (const char *context)
64 {
65     void *lpMsgBuf;
66     DWORD last_error = GetLastError ();
67
68     if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
69                          FORMAT_MESSAGE_FROM_SYSTEM,
70                          NULL,
71                          last_error,
72                          MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
73                          (LPWSTR) &lpMsgBuf,
74                          0, NULL)) {
75         fprintf (stderr, "%s: Unknown GDI error", context);
76     } else {
77         fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf);
78
79         LocalFree (lpMsgBuf);
80     }
81
82     fflush (stderr);
83
84     /* We should switch off of last_status, but we'd either return
85      * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there
86      * is no CAIRO_STATUS_UNKNOWN_ERROR.
87      */
88     return CAIRO_STATUS_NO_MEMORY;
89 }
90
91 static cairo_user_data_key_t win32_closure_key;
92
93 typedef struct _win32_target_closure {
94     char *filename;
95     int width;
96     int height;
97     cairo_surface_t *target;
98     HDC dc;
99     int left_margin;
100     int bottom_margin;
101 } win32_target_closure_t;
102
103 static cairo_bool_t
104 printer_is_postscript_level_3 (HDC dc)
105 {
106     DWORD word;
107     INT ps_feature, ps_level;
108
109     word = PSIDENT_GDICENTRIC;
110     if (ExtEscape (dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0)
111         return FALSE;
112
113     ps_feature = FEATURESETTING_PSLEVEL;
114     if (ExtEscape (dc, GET_PS_FEATURESETTING, sizeof(INT),
115                    (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0)
116         return FALSE;
117
118     if (ps_level >= 3)
119         return TRUE;
120
121     return FALSE;
122 }
123
124 static void
125 create_printer_dc (win32_target_closure_t *ptc)
126 {
127     char *printer_name;
128     DWORD size;
129     int x_dpi, y_dpi, left_margin, top_margin, page_height, printable_height;
130     XFORM xform;
131
132     ptc->dc = NULL;
133     GetDefaultPrinter (NULL, &size);
134     printer_name = malloc (size);
135
136     if (printer_name == NULL)
137         return;
138
139     if (GetDefaultPrinter (printer_name, &size) == 0) {
140         free (printer_name);
141         return;
142     }
143
144     /* printf("\nPrinting to : %s\n", printer_name); */
145     ptc->dc = CreateDC (NULL, printer_name, NULL, NULL);
146     free (printer_name);
147
148     if (!printer_is_postscript_level_3 (ptc->dc)) {
149         printf("The default printer driver must be a color PostScript level 3 printer\n");
150         ptc->dc = NULL;
151         return;
152     }
153
154     /* The printer device units on win32 are 1 unit == 1 dot and the
155      * origin is the start of the printable area. We transform the
156      * cordinate space to 1 unit is 1 point as expected by the
157      * tests. As the page size is larger than the test surface, the
158      * origin is translated down so that the each test is drawn at the
159      * bottom left corner of the page. This is because the bottom left
160      * corner of the PNG image that ghostscript creates is positioned
161      * at origin of the PS coordinates (ie the bottom left of the
162      * page).  The left and bottom margins are stored in
163      * win32_target_closure as size of the PNG image needs to be
164      * increased because the test output is offset from the bottom
165      * left by the non printable margins. After the PNG is created the
166      * margins will be chopped off so the image matches the reference
167      * image.
168      */
169     printable_height = GetDeviceCaps (ptc->dc, VERTRES);
170     x_dpi = GetDeviceCaps (ptc->dc, LOGPIXELSX);
171     y_dpi = GetDeviceCaps (ptc->dc, LOGPIXELSY);
172     left_margin = GetDeviceCaps (ptc->dc, PHYSICALOFFSETX);
173     top_margin = GetDeviceCaps (ptc->dc, PHYSICALOFFSETY);
174     page_height = GetDeviceCaps (ptc->dc, PHYSICALHEIGHT);
175
176     SetGraphicsMode (ptc->dc, GM_ADVANCED);
177     xform.eM11 = x_dpi/72.0;
178     xform.eM12 = 0;
179     xform.eM21 = 0;
180     xform.eM22 = y_dpi/72.0;
181     xform.eDx = 0;
182     xform.eDy = printable_height - ptc->height*y_dpi/72.0;
183     if (!SetWorldTransform (ptc->dc, &xform)) {
184         _cairo_win32_print_gdi_error ("cairo-boilerplate-win32-printing:SetWorldTransform");
185         return;
186     }
187
188     ptc->left_margin = 72.0*left_margin/x_dpi;
189     ptc->bottom_margin = 72.0*(page_height - printable_height - top_margin)/y_dpi;
190 }
191
192 static cairo_surface_t *
193 _cairo_boilerplate_win32_printing_create_surface (const char                *name,
194                                                   cairo_content_t            content,
195                                                   double                     width,
196                                                   double                     height,
197                                                   double                     max_width,
198                                                   double                     max_height,
199                                                   cairo_boilerplate_mode_t   mode,
200                                                   void                     **closure)
201 {
202     win32_target_closure_t *ptc;
203     cairo_surface_t *surface;
204     DOCINFO di;
205
206     if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
207         content = CAIRO_CONTENT_COLOR_ALPHA;
208
209     *closure = ptc = xmalloc (sizeof (win32_target_closure_t));
210
211     xasprintf (&ptc->filename, "%s.out.ps", name);
212     xunlink (ptc->filename);
213
214     memset (&di, 0, sizeof (DOCINFO));
215     di.cbSize = sizeof (DOCINFO);
216     di.lpszDocName = ptc->filename;
217     di.lpszOutput = ptc->filename;
218
219     ptc->width = width;
220     ptc->height = height;
221
222     create_printer_dc (ptc);
223     if (ptc->dc == NULL) {
224         printf("\nFailed to create printer\n");
225         free (ptc->filename);
226         free (ptc);
227         return NULL;
228     }
229     StartDoc (ptc->dc, &di);
230     StartPage (ptc->dc);
231     surface = cairo_win32_printing_surface_create (ptc->dc);
232     if (cairo_surface_status (surface)) {
233         free (ptc->filename);
234         free (ptc);
235         return NULL;
236     }
237     cairo_surface_set_fallback_resolution (surface, 72., 72.);
238
239     if (content == CAIRO_CONTENT_COLOR) {
240         ptc->target = surface;
241         surface = cairo_surface_create_similar (ptc->target,
242                                                 CAIRO_CONTENT_COLOR,
243                                                 width, height);
244     } else {
245         ptc->target = NULL;
246     }
247
248     if (cairo_surface_set_user_data (surface,
249                                      &win32_closure_key,
250                                      ptc,
251                                      NULL) != CAIRO_STATUS_SUCCESS) {
252         cairo_surface_destroy (surface);
253         if (ptc->target != NULL)
254             cairo_surface_destroy (ptc->target);
255         free (ptc->filename);
256         free (ptc);
257         return NULL;
258     }
259
260     return surface;
261 }
262
263 static cairo_status_t
264 _cairo_boilerplate_win32_printing_surface_write_to_png (cairo_surface_t *surface,
265                                                         const char      *filename)
266 {
267     win32_target_closure_t *ptc = cairo_surface_get_user_data (surface, &win32_closure_key);
268     char command[4096];
269     cairo_surface_t *src_image, *dst_image;
270     cairo_t *cr;
271     cairo_status_t status;
272
273     /* Both surface and ptc->target were originally created at the
274      * same dimensions. We want a 1:1 copy here, so we first clear any
275      * device offset on surface.
276      *
277      * In a more realistic use case of device offsets, the target of
278      * this copying would be of a different size than the source, and
279      * the offset would be desirable during the copy operation. */
280     cairo_surface_set_device_offset (surface, 0, 0);
281
282     if (ptc->target) {
283         cairo_t *cr;
284         cr = cairo_create (ptc->target);
285         cairo_set_source_surface (cr, surface, 0, 0);
286         cairo_paint (cr);
287         cairo_show_page (cr);
288         cairo_destroy (cr);
289
290         cairo_surface_finish (surface);
291         surface = ptc->target;
292     }
293
294     cairo_surface_finish (surface);
295     EndPage (ptc->dc);
296     EndDoc (ptc->dc);
297     sprintf (command, "gs -q -r72 -g%dx%d -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pngalpha -sOutputFile=%s %s",
298              ptc->width + ptc->left_margin, ptc->height + ptc->bottom_margin, filename, ptc->filename);
299
300     if (system (command) != 0)
301         return CAIRO_STATUS_WRITE_ERROR;
302
303     /* Create a new image from the ghostscript image that has the
304      * left and bottom margins removed */
305
306     src_image = cairo_image_surface_create_from_png (filename);
307     status = cairo_surface_status (src_image);
308     if (status)
309         return status;
310
311     dst_image = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
312                                             ptc->width,
313                                             ptc->height);
314     cr = cairo_create (dst_image);
315     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
316     cairo_set_source_surface (cr, src_image, -ptc->left_margin, 0);
317     cairo_paint (cr);
318     cairo_destroy (cr);
319
320     cairo_surface_write_to_png (dst_image, filename);
321     status = cairo_surface_status (dst_image);
322     if (status)
323         return status;
324
325     cairo_surface_destroy (src_image);
326     cairo_surface_destroy (dst_image);
327
328     return CAIRO_STATUS_SUCCESS;
329 }
330
331 static cairo_surface_t *
332 _cairo_boilerplate_win32_printing_get_image_surface (cairo_surface_t *surface,
333                                                      int              page,
334                                                      int              width,
335                                                      int              height)
336 {
337     win32_target_closure_t *ptc = cairo_surface_get_user_data (surface,
338                                                                &win32_closure_key);
339     char *filename;
340     cairo_status_t status;
341
342     /* XXX test paginated interface */
343     if (page != 0)
344         return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
345
346     xasprintf (&filename, "%s.png", ptc->filename);
347     status = _cairo_boilerplate_win32_printing_surface_write_to_png (surface, filename);
348     if (status)
349         return cairo_boilerplate_surface_create_in_error (status);
350
351     surface = cairo_boilerplate_get_image_surface_from_png (filename,
352                                                             width,
353                                                             height,
354                                                             ptc->target == NULL);
355
356     remove (filename);
357     free (filename);
358
359     return surface;
360 }
361
362 static void
363 _cairo_boilerplate_win32_printing_cleanup (void *closure)
364 {
365     win32_target_closure_t *ptc = closure;
366
367     if (ptc->target)
368         cairo_surface_destroy (ptc->target);
369     free (ptc->filename);
370     free (ptc);
371     DeleteDC (ptc->dc);
372 }
373
374 static const cairo_boilerplate_target_t targets[] = {
375     {
376         "win32-printing", "win32", ".ps", NULL,
377         CAIRO_SURFACE_TYPE_WIN32_PRINTING,
378         CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
379         "cairo_win32_printing_surface_create",
380         _cairo_boilerplate_win32_printing_create_surface,
381         cairo_surface_create_similar,
382         NULL, NULL,
383         _cairo_boilerplate_win32_printing_get_image_surface,
384         _cairo_boilerplate_win32_printing_surface_write_to_png,
385         _cairo_boilerplate_win32_printing_cleanup,
386         NULL, NULL, FALSE, TRUE, TRUE
387     },
388     {
389         "win32-printing", "win32", ".ps", NULL,
390         CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
391         "cairo_win32_printing_surface_create",
392         _cairo_boilerplate_win32_printing_create_surface,
393         cairo_surface_create_similar,
394         NULL, NULL,
395         _cairo_boilerplate_win32_printing_get_image_surface,
396         _cairo_boilerplate_win32_printing_surface_write_to_png,
397         _cairo_boilerplate_win32_printing_cleanup,
398         NULL, NULL, FALSE, TRUE, TRUE
399     },
400 };
401 CAIRO_BOILERPLATE (win32_printing, targets)
402
403 #else
404
405 CAIRO_NO_BOILERPLATE (win32_printing)
406
407 #endif