Code

no need to list win32 stuff here?
[inkscape.git] / src / extension / internal / emf-win32-print.cpp
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