1 /** \file\r
2 * Enhanced Metafile Printing.\r
3 */\r
4 /*\r
5 * Authors:\r
6 * Ulf Erikson <ulferikson@users.sf.net>\r
7 *\r
8 * Copyright (C) 2006 Authors\r
9 *\r
10 * Released under GNU GPL, read the file 'COPYING' for more information\r
11 */\r
12 /*\r
13 * References:\r
14 * - How to Create & Play Enhanced Metafiles in Win32\r
15 * http://support.microsoft.com/kb/q145999/\r
16 * - INFO: Windows Metafile Functions & Aldus Placeable Metafiles\r
17 * http://support.microsoft.com/kb/q66949/\r
18 * - Metafile Functions\r
19 * http://msdn.microsoft.com/library/en-us/gdi/metafile_0whf.asp\r
20 * - Metafile Structures\r
21 * http://msdn.microsoft.com/library/en-us/gdi/metafile_5hkj.asp\r
22 */\r
23 \r
24 #ifdef WIN32\r
25 \r
26 #ifdef HAVE_CONFIG_H\r
27 # include "config.h"\r
28 #endif\r
29 \r
30 #include <string.h>\r
31 #include <signal.h>\r
32 #include <errno.h>\r
33 \r
34 #include "libnr/n-art-bpath.h"\r
35 #include "libnr/nr-point-matrix-ops.h"\r
36 #include "libnr/nr-rect.h"\r
37 #include "libnr/nr-matrix.h"\r
38 #include "libnr/nr-matrix-fns.h"\r
39 #include "libnr/nr-path.h"\r
40 #include "libnr/nr-pixblock.h"\r
41 #include "display/canvas-bpath.h"\r
42 #include "sp-item.h"\r
43 \r
44 #include "glib.h"\r
45 #include "gtk/gtkdialog.h"\r
46 #include "gtk/gtkbox.h"\r
47 #include "gtk/gtkstock.h"\r
48 \r
49 #include "glibmm/i18n.h"\r
50 #include "enums.h"\r
51 #include "document.h"\r
52 #include "style.h"\r
53 #include "sp-paint-server.h"\r
54 #include "inkscape_version.h"\r
55 \r
56 #include "win32.h"\r
57 #include "emf-win32-print.h"\r
58 \r
59 #include "unit-constants.h"\r
60 \r
61 #include "extension/extension.h"\r
62 #include "extension/system.h"\r
63 #include "extension/print.h"\r
64 \r
65 #include "io/sys.h"\r
66 \r
67 #include "macros.h"\r
68 \r
69 #define WIN32_LEAN_AND_MEAN\r
70 #include <windows.h>\r
71 \r
72 namespace Inkscape {\r
73 namespace Extension {\r
74 namespace Internal {\r
75 \r
76 static float dwDPI = 2540;\r
77 \r
78 \r
79 PrintEmfWin32::PrintEmfWin32 (void):\r
80 hdc(NULL),\r
81 hbrush(NULL),\r
82 hpen(NULL),\r
83 fill_path(NULL)\r
84 {\r
85 }\r
86 \r
87 \r
88 PrintEmfWin32::~PrintEmfWin32 (void)\r
89 {\r
90 if (hdc) {\r
91 HENHMETAFILE metafile = CloseEnhMetaFile( hdc );\r
92 if ( metafile ) {\r
93 DeleteEnhMetaFile( metafile );\r
94 }\r
95 DeleteDC( hdc );\r
96 }\r
97 \r
98 /* restore default signal handling for SIGPIPE */\r
99 #if !defined(_WIN32) && !defined(__WIN32__)\r
100 (void) signal(SIGPIPE, SIG_DFL);\r
101 #endif\r
102 return;\r
103 }\r
104 \r
105 \r
106 unsigned int\r
107 PrintEmfWin32::setup (Inkscape::Extension::Print *mod)\r
108 {\r
109 return TRUE;\r
110 }\r
111 \r
112 \r
113 unsigned int\r
114 PrintEmfWin32::begin (Inkscape::Extension::Print *mod, SPDocument *doc)\r
115 {\r
116 gchar const *utf8_fn = mod->get_param_string("destination");\r
117 \r
118 gsize bytesRead = 0;\r
119 gsize bytesWritten = 0;\r
120 GError* error = NULL;\r
121 gchar *local_fn =\r
122 g_filename_from_utf8( utf8_fn, -1, &bytesRead, &bytesWritten, &error );\r
123 \r
124 if (local_fn == NULL) {\r
125 return 1;\r
126 }\r
127 \r
128 CHAR *ansi_uri = (CHAR *) local_fn;\r
129 gunichar2 *unicode_fn = g_utf8_to_utf16( local_fn, -1, NULL, NULL, NULL );\r
130 WCHAR *unicode_uri = (WCHAR *) unicode_fn;\r
131 \r
132 // width and height in px\r
133 _width = sp_document_width(doc);\r
134 _height = sp_document_height(doc);\r
135 \r
136 NRRect d;\r
137 bool pageBoundingBox;\r
138 pageBoundingBox = mod->get_param_bool("pageBoundingBox");\r
139 if (pageBoundingBox) {\r
140 d.x0 = d.y0 = 0;\r
141 d.x1 = _width;\r
142 d.y1 = _height;\r
143 } else {\r
144 SPItem* doc_item = SP_ITEM(sp_document_root(doc));\r
145 sp_item_invoke_bbox(doc_item, &d, sp_item_i2r_affine(doc_item), TRUE);\r
146 }\r
147 \r
148 d.x0 *= IN_PER_PX;\r
149 d.y0 *= IN_PER_PX;\r
150 d.x1 *= IN_PER_PX;\r
151 d.y1 *= IN_PER_PX;\r
152 \r
153 float dwInchesX = (d.x1 - d.x0);\r
154 float dwInchesY = (d.y1 - d.y0);\r
155 \r
156 // dwInchesX x dwInchesY in .01mm units\r
157 SetRect( &rc, 0, 0, (int) ceil(dwInchesX*2540), (int) ceil(dwInchesY*2540) );\r
158 \r
159 // Get a Reference DC\r
160 HDC hScreenDC = GetDC( NULL );\r
161 \r
162 // Get the physical characteristics of the reference DC\r
163 float PixelsX = (float) GetDeviceCaps( hScreenDC, HORZRES );\r
164 float PixelsY = (float) GetDeviceCaps( hScreenDC, VERTRES );\r
165 float MMX = (float) GetDeviceCaps( hScreenDC, HORZSIZE );\r
166 float MMY = (float) GetDeviceCaps( hScreenDC, VERTSIZE );\r
167 \r
168 // Create the Metafile\r
169 if (PrintWin32::is_os_wide())\r
170 hdc = CreateEnhMetaFileW( hScreenDC, unicode_uri, &rc, NULL );\r
171 else\r
172 hdc = CreateEnhMetaFileA( hScreenDC, ansi_uri, &rc, NULL );\r
173 \r
174 // Release the reference DC\r
175 ReleaseDC( NULL, hScreenDC );\r
176 \r
177 // Did we get a good metafile?\r
178 if (hdc == NULL)\r
179 {\r
180 g_free(local_fn);\r
181 g_free(unicode_fn);\r
182 return 1;\r
183 }\r
184 \r
185 // Anisotropic mapping mode\r
186 SetMapMode( hdc, MM_ANISOTROPIC );\r
187 \r
188 // Set the Windows extent\r
189 SetWindowExtEx( hdc, (int) (dwInchesX*dwDPI), (int) (dwInchesY*dwDPI), NULL );\r
190 \r
191 // Set the viewport extent to reflect\r
192 // dwInchesX" x dwInchesY" in device units\r
193 SetViewportExtEx( hdc,\r
194 (int) ((float) dwInchesX*25.4f*PixelsX/MMX),\r
195 (int) ((float) dwInchesY*25.4f*PixelsY/MMY),\r
196 NULL );\r
197 \r
198 SetRect( &rc, 0, 0, (int) ceil(dwInchesX*dwDPI), (int) ceil(dwInchesY*dwDPI) );\r
199 \r
200 g_free(local_fn);\r
201 g_free(unicode_fn);\r
202 \r
203 return 0;\r
204 }\r
205 \r
206 \r
207 unsigned int\r
208 PrintEmfWin32::finish (Inkscape::Extension::Print *mod)\r
209 {\r
210 if (!hdc) return 0;\r
211 \r
212 flush_fill(); // flush any pending fills\r
213 \r
214 HENHMETAFILE metafile = CloseEnhMetaFile( hdc );\r
215 if ( metafile ) {\r
216 DeleteEnhMetaFile( metafile );\r
217 }\r
218 DeleteDC( hdc );\r
219 \r
220 hdc = NULL;\r
221 \r
222 return 0;\r
223 }\r
224 \r
225 \r
226 unsigned int\r
227 PrintEmfWin32::comment (Inkscape::Extension::Print * module,\r
228 const char *comment)\r
229 {\r
230 if (!hdc) return 0;\r
231 \r
232 flush_fill(); // flush any pending fills\r
233 \r
234 return 0;\r
235 }\r
236 \r
237 \r
238 void\r
239 PrintEmfWin32::create_brush(SPStyle const *style)\r
240 {\r
241 float rgb[3];\r
242 \r
243 if (style) {\r
244 sp_color_get_rgb_floatv( &style->fill.value.color, rgb );\r
245 hbrush = CreateSolidBrush( RGB(255*rgb[0], 255*rgb[1], 255*rgb[2]) );\r
246 hbrushOld = (HBRUSH) SelectObject( hdc, hbrush );\r
247 \r
248 SetPolyFillMode( hdc,\r
249 style->fill_rule.computed == 0 ? WINDING :\r
250 style->fill_rule.computed == 2 ? ALTERNATE : ALTERNATE );\r
251 } else { // if (!style)\r
252 hbrush = CreateSolidBrush( RGB(255, 255, 255) );\r
253 hbrushOld = (HBRUSH) SelectObject( hdc, hbrush );\r
254 SetPolyFillMode( hdc, ALTERNATE );\r
255 }\r
256 }\r
257 \r
258 \r
259 void\r
260 PrintEmfWin32::destroy_brush()\r
261 {\r
262 SelectObject( hdc, hbrushOld );\r
263 if (hbrush)\r
264 DeleteObject( hbrush );\r
265 hbrush = NULL;\r
266 }\r
267 \r
268 \r
269 void\r
270 PrintEmfWin32::create_pen(SPStyle const *style)\r
271 {\r
272 if (style) {\r
273 float rgb[3];\r
274 \r
275 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);\r
276 \r
277 LOGBRUSH lb = {0};\r
278 lb.lbStyle = BS_SOLID;\r
279 lb.lbColor = RGB( 255*rgb[0], 255*rgb[1], 255*rgb[2] );\r
280 \r
281 int linestyle = PS_SOLID;\r
282 int linecap = 0;\r
283 int linejoin = 0;\r
284 DWORD n_dash = 0;\r
285 DWORD *dash = NULL;\r
286 float oldmiterlimit;\r
287 \r
288 DWORD linewidth = MAX( 1, (DWORD) (style->stroke_width.computed * IN_PER_PX * dwDPI) );\r
289 \r
290 if (style->stroke_linecap.computed == 0) {\r
291 linecap = PS_ENDCAP_FLAT;\r
292 }\r
293 else if (style->stroke_linecap.computed == 1) {\r
294 linecap = PS_ENDCAP_ROUND;\r
295 }\r
296 else if (style->stroke_linecap.computed == 2) {\r
297 linecap = PS_ENDCAP_SQUARE;\r
298 }\r
299 \r
300 if (style->stroke_linejoin.computed == 0) {\r
301 linejoin = PS_JOIN_MITER;\r
302 }\r
303 else if (style->stroke_linejoin.computed == 1) {\r
304 linejoin = PS_JOIN_ROUND;\r
305 }\r
306 else if (style->stroke_linejoin.computed == 2) {\r
307 linejoin = PS_JOIN_BEVEL;\r
308 }\r
309 \r
310 if (style->stroke_dash.n_dash &&\r
311 style->stroke_dash.dash )\r
312 {\r
313 int i = 0;\r
314 while (linestyle != PS_USERSTYLE &&\r
315 (i < style->stroke_dash.n_dash)) {\r
316 if (style->stroke_dash.dash[i] > 0.00000001)\r
317 linestyle = PS_USERSTYLE;\r
318 i++;\r
319 }\r
320 \r
321 if (linestyle == PS_USERSTYLE) {\r
322 n_dash = style->stroke_dash.n_dash;\r
323 dash = new DWORD[n_dash];\r
324 for (i = 0; i < style->stroke_dash.n_dash; i++) {\r
325 dash[i] = (DWORD) (style->stroke_dash.dash[i] * IN_PER_PX * dwDPI);\r
326 }\r
327 }\r
328 }\r
329 \r
330 hpen = ExtCreatePen(\r
331 PS_GEOMETRIC | linestyle | linecap | linejoin,\r
332 linewidth,\r
333 &lb,\r
334 n_dash,\r
335 dash );\r
336 \r
337 if ( !hpen && linestyle == PS_USERSTYLE ) {\r
338 hpen = ExtCreatePen(\r
339 PS_GEOMETRIC | PS_SOLID | linecap | linejoin,\r
340 linewidth,\r
341 &lb,\r
342 0,\r
343 NULL );\r
344 }\r
345 \r
346 if ( !hpen ) {\r
347 hpen = CreatePen(\r
348 PS_SOLID,\r
349 linewidth,\r
350 lb.lbColor );\r
351 }\r
352 \r
353 hpenOld = (HPEN) SelectObject( hdc, hpen );\r
354 \r
355 if (linejoin == PS_JOIN_MITER) {\r
356 float miterlimit = style->stroke_miterlimit.value;\r
357 if (miterlimit < 1)\r
358 miterlimit = 4.0;\r
359 SetMiterLimit(\r
360 hdc,\r
361 miterlimit * IN_PER_PX * dwDPI,\r
362 &oldmiterlimit );\r
363 }\r
364 \r
365 if (n_dash) {\r
366 delete[] dash;\r
367 }\r
368 }\r
369 else { // if (!style)\r
370 hpen = CreatePen( PS_SOLID, 1, RGB(0, 0, 0) );\r
371 hpenOld = (HPEN) SelectObject( hdc, hpen );\r
372 }\r
373 }\r
374 \r
375 \r
376 void\r
377 PrintEmfWin32::destroy_pen()\r
378 {\r
379 SelectObject( hdc, hpenOld );\r
380 if (hpen)\r
381 DeleteObject( hpen );\r
382 hpen = NULL;\r
383 }\r
384 \r
385 \r
386 void\r
387 PrintEmfWin32::flush_fill()\r
388 {\r
389 if (fill_path) {\r
390 print_bpath(fill_path, &fill_transform, &fill_pbox);\r
391 FillPath( hdc );\r
392 destroy_brush();\r
393 delete[] fill_path;\r
394 fill_path = NULL;\r
395 }\r
396 }\r
397 \r
398 \r
399 NArtBpath *\r
400 PrintEmfWin32::copy_bpath(const NArtBpath *bp)\r
401 {\r
402 NArtBpath *tmp = (NArtBpath *) bp;\r
403 int num = 1;\r
404 \r
405 while (tmp->code != NR_END) {\r
406 num++;\r
407 tmp += 1;\r
408 }\r
409 \r
410 tmp = new NArtBpath[num];\r
411 while (num--) {\r
412 tmp[num] = bp[num];\r
413 }\r
414 \r
415 return tmp;\r
416 }\r
417 \r
418 \r
419 int\r
420 PrintEmfWin32::cmp_bpath(const NArtBpath *bp1, const NArtBpath *bp2)\r
421 {\r
422 if (!bp1 || !bp2) {\r
423 return 1;\r
424 }\r
425 \r
426 while (bp1->code != NR_END && bp2->code != NR_END) {\r
427 if (bp1->code != bp2->code) {\r
428 return 1;\r
429 }\r
430 \r
431 if ( fabs(bp1->x1 - bp2->x1) > 0.00000001 ||\r
432 fabs(bp1->y1 - bp2->y1) > 0.00000001 ||\r
433 fabs(bp1->x2 - bp2->x2) > 0.00000001 ||\r
434 fabs(bp1->y2 - bp2->y2) > 0.00000001 ||\r
435 fabs(bp1->x3 - bp2->x3) > 0.00000001 ||\r
436 fabs(bp1->y3 - bp2->y3) > 0.00000001 )\r
437 {\r
438 return 1;\r
439 }\r
440 \r
441 bp1 += 1;\r
442 bp2 += 1;\r
443 }\r
444 \r
445 return bp1->code != NR_END || bp2->code != NR_END;\r
446 }\r
447 \r
448 \r
449 unsigned int\r
450 PrintEmfWin32::fill(Inkscape::Extension::Print *mod,\r
451 NRBPath const *bpath, NRMatrix const *transform, SPStyle const *style,\r
452 NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)\r
453 {\r
454 if (!hdc) return 0;\r
455 \r
456 flush_fill(); // flush any pending fills\r
457 \r
458 if (style->fill.type == SP_PAINT_TYPE_COLOR) {\r
459 create_brush(style);\r
460 } else {\r
461 // create_brush(NULL);\r
462 return 0;\r
463 }\r
464 \r
465 fill_path = copy_bpath( bpath->path );\r
466 fill_transform = *transform;\r
467 fill_pbox = *pbox;\r
468 \r
469 // postpone fill in case of stroke-and-fill\r
470 \r
471 return 0;\r
472 }\r
473 \r
474 \r
475 unsigned int\r
476 PrintEmfWin32::stroke (Inkscape::Extension::Print *mod,\r
477 const NRBPath *bpath, const NRMatrix *transform, const SPStyle *style,\r
478 const NRRect *pbox, const NRRect *dbox, const NRRect *bbox)\r
479 {\r
480 if (!hdc) return 0;\r
481 \r
482 bool stroke_and_fill = ( cmp_bpath( bpath->path, fill_path ) == 0 );\r
483 \r
484 if (!stroke_and_fill) {\r
485 flush_fill(); // flush any pending fills\r
486 }\r
487 \r
488 if (style->stroke.type == SP_PAINT_TYPE_COLOR) {\r
489 create_pen(style);\r
490 } else {\r
491 // create_pen(NULL);\r
492 return 0;\r
493 }\r
494 \r
495 print_bpath(bpath->path, transform, pbox);\r
496 \r
497 if (stroke_and_fill) {\r
498 StrokeAndFillPath( hdc );\r
499 destroy_brush();\r
500 delete[] fill_path;\r
501 fill_path = NULL;\r
502 } else {\r
503 StrokePath( hdc );\r
504 }\r
505 \r
506 destroy_pen();\r
507 \r
508 return 0;\r
509 }\r
510 \r
511 \r
512 unsigned int\r
513 PrintEmfWin32::print_bpath(const NArtBpath *bp, const NRMatrix *transform, NRRect const *pbox)\r
514 {\r
515 unsigned int closed;\r
516 NR::Matrix tf = *transform;\r
517 \r
518 BeginPath( hdc );\r
519 closed = FALSE;\r
520 while (bp->code != NR_END) {\r
521 using NR::X;\r
522 using NR::Y;\r
523 \r
524 NR::Point p1(bp->c(1) * tf);\r
525 NR::Point p2(bp->c(2) * tf);\r
526 NR::Point p3(bp->c(3) * tf);\r
527 \r
528 p1[X] = (p1[X] * IN_PER_PX * dwDPI);\r
529 p2[X] = (p2[X] * IN_PER_PX * dwDPI);\r
530 p3[X] = (p3[X] * IN_PER_PX * dwDPI);\r
531 p1[Y] = (p1[Y] * IN_PER_PX * dwDPI);\r
532 p2[Y] = (p2[Y] * IN_PER_PX * dwDPI);\r
533 p3[Y] = (p3[Y] * IN_PER_PX * dwDPI);\r
534 \r
535 LONG const x1 = (LONG) round(p1[X]);\r
536 LONG const y1 = (LONG) round(rc.bottom-p1[Y]);\r
537 LONG const x2 = (LONG) round(p2[X]);\r
538 LONG const y2 = (LONG) round(rc.bottom-p2[Y]);\r
539 LONG const x3 = (LONG) round(p3[X]);\r
540 LONG const y3 = (LONG) round(rc.bottom-p3[Y]);\r
541 \r
542 switch (bp->code) {\r
543 case NR_MOVETO:\r
544 if (closed) {\r
545 CloseFigure( hdc );\r
546 }\r
547 closed = TRUE;\r
548 MoveToEx( hdc, x3, y3, NULL );\r
549 break;\r
550 case NR_MOVETO_OPEN:\r
551 if (closed) {\r
552 CloseFigure( hdc );\r
553 }\r
554 closed = FALSE;\r
555 MoveToEx( hdc, x3, y3, NULL );\r
556 break;\r
557 case NR_LINETO:\r
558 LineTo( hdc, x3, y3 );\r
559 break;\r
560 case NR_CURVETO:\r
561 {\r
562 POINT pt[3];\r
563 pt[0].x = x1;\r
564 pt[0].y = y1;\r
565 pt[1].x = x2;\r
566 pt[1].y = y2;\r
567 pt[2].x = x3;\r
568 pt[2].y = y3;\r
569 \r
570 PolyBezierTo( hdc, pt, 3 );\r
571 break;\r
572 }\r
573 default:\r
574 break;\r
575 }\r
576 bp += 1;\r
577 }\r
578 if (closed) {\r
579 CloseFigure( hdc );\r
580 }\r
581 EndPath( hdc );\r
582 \r
583 return closed;\r
584 }\r
585 \r
586 \r
587 bool\r
588 PrintEmfWin32::textToPath(Inkscape::Extension::Print * ext)\r
589 {\r
590 return ext->get_param_bool("textToPath");\r
591 }\r
592 \r
593 \r
594 void\r
595 PrintEmfWin32::init (void)\r
596 {\r
597 Inkscape::Extension::Extension * ext;\r
598 \r
599 /* EMF print */\r
600 ext = Inkscape::Extension::build_from_mem(\r
601 "<inkscape-extension>\n"\r
602 "<name>Enhanced Metafile Print</name>\n"\r
603 "<id>org.inkscape.print.emf.win32</id>\n"\r
604 "<param name=\"destination\" type=\"string\"></param>\n"\r
605 "<param name=\"textToPath\" type=\"boolean\">TRUE</param>\n"\r
606 "<param name=\"pageBoundingBox\" type=\"boolean\">TRUE</param>\n"\r
607 "<print/>\n"\r
608 "</inkscape-extension>", new PrintEmfWin32());\r
609 \r
610 return;\r
611 }\r
612 \r
613 \r
614 } /* namespace Internal */\r
615 } /* namespace Extension */\r
616 } /* namespace Inkscape */\r
617 \r
618 #endif /* WIN32 */\r
619 \r
620 /*\r
621 Local Variables:\r
622 mode:c++\r
623 c-file-style:"stroustrup"\r
624 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
625 indent-tabs-mode:nil\r
626 fill-column:99\r
627 End:\r
628 */\r
629 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r