PageRenderTime 40ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/cairo-1.12.2/boilerplate/cairo-boilerplate-win32-printing.c

#
C | 407 lines | 285 code | 64 blank | 58 comment | 35 complexity | 39992f810f33fb445d13cc13572e5758 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, GPL-2.0, GPL-3.0
  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. /* We require Windows 2000 features such as GetDefaultPrinter() */
  29. #if !defined(WINVER) || (WINVER < 0x0500)
  30. # define WINVER 0x0500
  31. #endif
  32. #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
  33. # define _WIN32_WINNT 0x0500
  34. #endif
  35. #include "cairo-boilerplate-private.h"
  36. #if CAIRO_CAN_TEST_WIN32_PRINTING_SURFACE
  37. #include <cairo-win32.h>
  38. #include <cairo-paginated-surface-private.h>
  39. #include <windows.h>
  40. #if !defined(POSTSCRIPT_IDENTIFY)
  41. # define POSTSCRIPT_IDENTIFY 0x1015
  42. #endif
  43. #if !defined(PSIDENT_GDICENTRIC)
  44. # define PSIDENT_GDICENTRIC 0x0000
  45. #endif
  46. #if !defined(GET_PS_FEATURESETTING)
  47. # define GET_PS_FEATURESETTING 0x1019
  48. #endif
  49. #if !defined(FEATURESETTING_PSLEVEL)
  50. # define FEATURESETTING_PSLEVEL 0x0002
  51. #endif
  52. static cairo_status_t
  53. _cairo_win32_print_gdi_error (const char *context)
  54. {
  55. void *lpMsgBuf;
  56. DWORD last_error = GetLastError ();
  57. if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
  58. FORMAT_MESSAGE_FROM_SYSTEM,
  59. NULL,
  60. last_error,
  61. MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  62. (LPWSTR) &lpMsgBuf,
  63. 0, NULL)) {
  64. fprintf (stderr, "%s: Unknown GDI error", context);
  65. } else {
  66. fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf);
  67. LocalFree (lpMsgBuf);
  68. }
  69. fflush (stderr);
  70. /* We should switch off of last_status, but we'd either return
  71. * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there
  72. * is no CAIRO_STATUS_UNKNOWN_ERROR.
  73. */
  74. return CAIRO_STATUS_NO_MEMORY;
  75. }
  76. static cairo_user_data_key_t win32_closure_key;
  77. typedef struct _win32_target_closure {
  78. char *filename;
  79. int width;
  80. int height;
  81. cairo_surface_t *target;
  82. HDC dc;
  83. int left_margin;
  84. int bottom_margin;
  85. } win32_target_closure_t;
  86. static cairo_bool_t
  87. printer_is_postscript_level_3 (HDC dc)
  88. {
  89. DWORD word;
  90. INT ps_feature, ps_level;
  91. word = PSIDENT_GDICENTRIC;
  92. if (ExtEscape (dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0)
  93. return FALSE;
  94. ps_feature = FEATURESETTING_PSLEVEL;
  95. if (ExtEscape (dc, GET_PS_FEATURESETTING, sizeof(INT),
  96. (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0)
  97. return FALSE;
  98. if (ps_level >= 3)
  99. return TRUE;
  100. return FALSE;
  101. }
  102. static void
  103. create_printer_dc (win32_target_closure_t *ptc)
  104. {
  105. char *printer_name;
  106. DWORD size;
  107. int x_dpi, y_dpi, left_margin, top_margin, page_height, printable_height;
  108. XFORM xform;
  109. ptc->dc = NULL;
  110. GetDefaultPrinter (NULL, &size);
  111. printer_name = malloc (size);
  112. if (printer_name == NULL)
  113. return;
  114. if (GetDefaultPrinter (printer_name, &size) == 0) {
  115. free (printer_name);
  116. return;
  117. }
  118. /* printf("\nPrinting to : %s\n", printer_name); */
  119. ptc->dc = CreateDC (NULL, printer_name, NULL, NULL);
  120. free (printer_name);
  121. if (!printer_is_postscript_level_3 (ptc->dc)) {
  122. printf("The default printer driver must be a color PostScript level 3 printer\n");
  123. ptc->dc = NULL;
  124. return;
  125. }
  126. /* The printer device units on win32 are 1 unit == 1 dot and the
  127. * origin is the start of the printable area. We transform the
  128. * cordinate space to 1 unit is 1 point as expected by the
  129. * tests. As the page size is larger than the test surface, the
  130. * origin is translated down so that the each test is drawn at the
  131. * bottom left corner of the page. This is because the bottom left
  132. * corner of the PNG image that ghostscript creates is positioned
  133. * at origin of the PS coordinates (ie the bottom left of the
  134. * page). The left and bottom margins are stored in
  135. * win32_target_closure as size of the PNG image needs to be
  136. * increased because the test output is offset from the bottom
  137. * left by the non printable margins. After the PNG is created the
  138. * margins will be chopped off so the image matches the reference
  139. * image.
  140. */
  141. printable_height = GetDeviceCaps (ptc->dc, VERTRES);
  142. x_dpi = GetDeviceCaps (ptc->dc, LOGPIXELSX);
  143. y_dpi = GetDeviceCaps (ptc->dc, LOGPIXELSY);
  144. left_margin = GetDeviceCaps (ptc->dc, PHYSICALOFFSETX);
  145. top_margin = GetDeviceCaps (ptc->dc, PHYSICALOFFSETY);
  146. page_height = GetDeviceCaps (ptc->dc, PHYSICALHEIGHT);
  147. SetGraphicsMode (ptc->dc, GM_ADVANCED);
  148. xform.eM11 = x_dpi/72.0;
  149. xform.eM12 = 0;
  150. xform.eM21 = 0;
  151. xform.eM22 = y_dpi/72.0;
  152. xform.eDx = 0;
  153. xform.eDy = printable_height - ptc->height*y_dpi/72.0;
  154. if (!SetWorldTransform (ptc->dc, &xform)) {
  155. _cairo_win32_print_gdi_error ("cairo-boilerplate-win32-printing:SetWorldTransform");
  156. return;
  157. }
  158. ptc->left_margin = 72.0*left_margin/x_dpi;
  159. ptc->bottom_margin = 72.0*(page_height - printable_height - top_margin)/y_dpi;
  160. }
  161. static cairo_surface_t *
  162. _cairo_boilerplate_win32_printing_create_surface (const char *name,
  163. cairo_content_t content,
  164. double width,
  165. double height,
  166. double max_width,
  167. double max_height,
  168. cairo_boilerplate_mode_t mode,
  169. void **closure)
  170. {
  171. win32_target_closure_t *ptc;
  172. cairo_surface_t *surface;
  173. DOCINFO di;
  174. if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
  175. content = CAIRO_CONTENT_COLOR_ALPHA;
  176. *closure = ptc = xmalloc (sizeof (win32_target_closure_t));
  177. xasprintf (&ptc->filename, "%s.out.ps", name);
  178. xunlink (ptc->filename);
  179. memset (&di, 0, sizeof (DOCINFO));
  180. di.cbSize = sizeof (DOCINFO);
  181. di.lpszDocName = ptc->filename;
  182. di.lpszOutput = ptc->filename;
  183. ptc->width = width;
  184. ptc->height = height;
  185. create_printer_dc (ptc);
  186. if (ptc->dc == NULL) {
  187. printf("\nFailed to create printer\n");
  188. free (ptc->filename);
  189. free (ptc);
  190. return NULL;
  191. }
  192. StartDoc (ptc->dc, &di);
  193. StartPage (ptc->dc);
  194. surface = cairo_win32_printing_surface_create (ptc->dc);
  195. if (cairo_surface_status (surface)) {
  196. free (ptc->filename);
  197. free (ptc);
  198. return NULL;
  199. }
  200. cairo_surface_set_fallback_resolution (surface, 72., 72.);
  201. if (content == CAIRO_CONTENT_COLOR) {
  202. ptc->target = surface;
  203. surface = cairo_surface_create_similar (ptc->target,
  204. CAIRO_CONTENT_COLOR,
  205. width, height);
  206. } else {
  207. ptc->target = NULL;
  208. }
  209. if (cairo_surface_set_user_data (surface,
  210. &win32_closure_key,
  211. ptc,
  212. NULL) != CAIRO_STATUS_SUCCESS) {
  213. cairo_surface_destroy (surface);
  214. if (ptc->target != NULL)
  215. cairo_surface_destroy (ptc->target);
  216. free (ptc->filename);
  217. free (ptc);
  218. return NULL;
  219. }
  220. return surface;
  221. }
  222. static cairo_status_t
  223. _cairo_boilerplate_win32_printing_surface_write_to_png (cairo_surface_t *surface,
  224. const char *filename)
  225. {
  226. win32_target_closure_t *ptc = cairo_surface_get_user_data (surface, &win32_closure_key);
  227. char command[4096];
  228. cairo_surface_t *src_image, *dst_image;
  229. cairo_t *cr;
  230. cairo_status_t status;
  231. /* Both surface and ptc->target were originally created at the
  232. * same dimensions. We want a 1:1 copy here, so we first clear any
  233. * device offset on surface.
  234. *
  235. * In a more realistic use case of device offsets, the target of
  236. * this copying would be of a different size than the source, and
  237. * the offset would be desirable during the copy operation. */
  238. cairo_surface_set_device_offset (surface, 0, 0);
  239. if (ptc->target) {
  240. cairo_t *cr;
  241. cr = cairo_create (ptc->target);
  242. cairo_set_source_surface (cr, surface, 0, 0);
  243. cairo_paint (cr);
  244. cairo_show_page (cr);
  245. cairo_destroy (cr);
  246. cairo_surface_finish (surface);
  247. surface = ptc->target;
  248. }
  249. cairo_surface_finish (surface);
  250. EndPage (ptc->dc);
  251. EndDoc (ptc->dc);
  252. sprintf (command, "gs -q -r72 -g%dx%d -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pngalpha -sOutputFile=%s %s",
  253. ptc->width + ptc->left_margin, ptc->height + ptc->bottom_margin, filename, ptc->filename);
  254. if (system (command) != 0)
  255. return CAIRO_STATUS_WRITE_ERROR;
  256. /* Create a new image from the ghostscript image that has the
  257. * left and bottom margins removed */
  258. src_image = cairo_image_surface_create_from_png (filename);
  259. status = cairo_surface_status (src_image);
  260. if (status)
  261. return status;
  262. dst_image = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
  263. ptc->width,
  264. ptc->height);
  265. cr = cairo_create (dst_image);
  266. cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  267. cairo_set_source_surface (cr, src_image, -ptc->left_margin, 0);
  268. cairo_paint (cr);
  269. cairo_destroy (cr);
  270. cairo_surface_write_to_png (dst_image, filename);
  271. status = cairo_surface_status (dst_image);
  272. if (status)
  273. return status;
  274. cairo_surface_destroy (src_image);
  275. cairo_surface_destroy (dst_image);
  276. return CAIRO_STATUS_SUCCESS;
  277. }
  278. static cairo_surface_t *
  279. _cairo_boilerplate_win32_printing_get_image_surface (cairo_surface_t *surface,
  280. int page,
  281. int width,
  282. int height)
  283. {
  284. win32_target_closure_t *ptc = cairo_surface_get_user_data (surface,
  285. &win32_closure_key);
  286. char *filename;
  287. cairo_status_t status;
  288. /* XXX test paginated interface */
  289. if (page != 0)
  290. return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
  291. xasprintf (&filename, "%s.png", ptc->filename);
  292. status = _cairo_boilerplate_win32_printing_surface_write_to_png (surface, filename);
  293. if (status)
  294. return cairo_boilerplate_surface_create_in_error (status);
  295. surface = cairo_boilerplate_get_image_surface_from_png (filename,
  296. width,
  297. height,
  298. ptc->target == NULL);
  299. remove (filename);
  300. free (filename);
  301. return surface;
  302. }
  303. static void
  304. _cairo_boilerplate_win32_printing_cleanup (void *closure)
  305. {
  306. win32_target_closure_t *ptc = closure;
  307. if (ptc->target)
  308. cairo_surface_destroy (ptc->target);
  309. free (ptc->filename);
  310. free (ptc);
  311. DeleteDC (ptc->dc);
  312. }
  313. static const cairo_boilerplate_target_t targets[] = {
  314. {
  315. "win32-printing", "win32", ".ps", NULL,
  316. CAIRO_SURFACE_TYPE_WIN32_PRINTING,
  317. CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
  318. "cairo_win32_printing_surface_create",
  319. _cairo_boilerplate_win32_printing_create_surface,
  320. cairo_surface_create_similar,
  321. NULL, NULL,
  322. _cairo_boilerplate_win32_printing_get_image_surface,
  323. _cairo_boilerplate_win32_printing_surface_write_to_png,
  324. _cairo_boilerplate_win32_printing_cleanup,
  325. NULL, NULL, FALSE, TRUE, TRUE
  326. },
  327. {
  328. "win32-printing", "win32", ".ps", NULL,
  329. CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
  330. "cairo_win32_printing_surface_create",
  331. _cairo_boilerplate_win32_printing_create_surface,
  332. cairo_surface_create_similar,
  333. NULL, NULL,
  334. _cairo_boilerplate_win32_printing_get_image_surface,
  335. _cairo_boilerplate_win32_printing_surface_write_to_png,
  336. _cairo_boilerplate_win32_printing_cleanup,
  337. NULL, NULL, FALSE, TRUE, TRUE
  338. },
  339. };
  340. CAIRO_BOILERPLATE (win32_printing, targets)
  341. #else
  342. CAIRO_NO_BOILERPLATE (win32_printing)
  343. #endif