eb499588548c758c2b7ed05a34468460788421fd
1 /** \file
2 * Enhanced Metafile Input and Output.
3 */
4 /*
5 * Authors:
6 * Ulf Erikson <ulferikson@users.sf.net>
7 *
8 * Copyright (C) 2006 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
12 /*
13 * References:
14 * - How to Create & Play Enhanced Metafiles in Win32
15 * http://support.microsoft.com/kb/q145999/
16 * - INFO: Windows Metafile Functions & Aldus Placeable Metafiles
17 * http://support.microsoft.com/kb/q66949/
18 * - Metafile Functions
19 * http://msdn.microsoft.com/library/en-us/gdi/metafile_0whf.asp
20 * - Metafile Structures
21 * http://msdn.microsoft.com/library/en-us/gdi/metafile_5hkj.asp
22 */
24 #ifdef WIN32
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include "win32.h"
31 #include "emf-win32-print.h"
32 #include "emf-win32-inout.h"
33 #include "inkscape.h"
34 #include "sp-path.h"
35 #include "style.h"
36 #include "color.h"
37 #include "display/curve.h"
38 #include "libnr/n-art-bpath.h"
39 #include "libnr/nr-point-matrix-ops.h"
40 #include "gtk/gtk.h"
41 #include "print.h"
42 #include "glibmm/i18n.h"
43 #include "extension/extension.h"
44 #include "extension/system.h"
45 #include "extension/print.h"
46 #include "extension/db.h"
47 #include "extension/output.h"
48 #include "document.h"
49 #include "display/nr-arena.h"
50 #include "display/nr-arena-item.h"
52 #include "libnr/nr-rect.h"
53 #include "libnr/nr-matrix.h"
54 #include "libnr/nr-pixblock.h"
56 #include <stdio.h>
57 #include <string.h>
59 #include <vector>
60 #include <string>
62 #include "io/sys.h"
64 #include "unit-constants.h"
66 #include "clear-n_.h"
69 #define PRINT_EMF_WIN32 "org.inkscape.print.emf.win32"
71 #ifndef PS_JOIN_MASK
72 #define PS_JOIN_MASK (PS_JOIN_BEVEL|PS_JOIN_MITER|PS_JOIN_ROUND)
73 #endif
76 namespace Inkscape {
77 namespace Extension {
78 namespace Internal {
81 EmfWin32::EmfWin32 (void) // The null constructor
82 {
83 return;
84 }
87 EmfWin32::~EmfWin32 (void) //The destructor
88 {
89 return;
90 }
93 bool
94 EmfWin32::check (Inkscape::Extension::Extension * module)
95 {
96 if (NULL == Inkscape::Extension::db.get(PRINT_EMF_WIN32))
97 return FALSE;
98 return TRUE;
99 }
102 static void
103 emf_print_document_to_file(SPDocument *doc, gchar const *filename)
104 {
105 Inkscape::Extension::Print *mod;
106 SPPrintContext context;
107 gchar const *oldconst;
108 gchar *oldoutput;
109 unsigned int ret;
111 sp_document_ensure_up_to_date(doc);
113 mod = Inkscape::Extension::get_print(PRINT_EMF_WIN32);
114 oldconst = mod->get_param_string("destination");
115 oldoutput = g_strdup(oldconst);
116 mod->set_param_string("destination", (gchar *)filename);
118 /* Start */
119 context.module = mod;
120 /* fixme: This has to go into module constructor somehow */
121 /* Create new arena */
122 mod->base = SP_ITEM(sp_document_root(doc));
123 mod->arena = NRArena::create();
124 mod->dkey = sp_item_display_key_new(1);
125 mod->root = sp_item_invoke_show(mod->base, mod->arena, mod->dkey, SP_ITEM_SHOW_DISPLAY);
126 /* Print document */
127 ret = mod->begin(doc);
128 if (ret) {
129 throw Inkscape::Extension::Output::save_failed();
130 }
131 sp_item_invoke_print(mod->base, &context);
132 ret = mod->finish();
133 /* Release arena */
134 sp_item_invoke_hide(mod->base, mod->dkey);
135 mod->base = NULL;
136 nr_arena_item_unref(mod->root);
137 mod->root = NULL;
138 nr_object_unref((NRObject *) mod->arena);
139 mod->arena = NULL;
140 /* end */
142 mod->set_param_string("destination", oldoutput);
143 g_free(oldoutput);
145 return;
146 }
149 void
150 EmfWin32::save (Inkscape::Extension::Output *mod, SPDocument *doc, const gchar *uri)
151 {
152 Inkscape::Extension::Extension * ext;
154 ext = Inkscape::Extension::db.get(PRINT_EMF_WIN32);
155 if (ext == NULL)
156 return;
158 // bool old_textToPath = ext->get_param_bool("textToPath");
159 // bool new_val = mod->get_param_bool("textToPath");
160 // ext->set_param_bool("textToPath", new_val);
162 gchar * final_name;
163 final_name = g_strdup_printf("%s", uri);
164 emf_print_document_to_file(doc, final_name);
165 g_free(final_name);
167 // ext->set_param_bool("textToPath", old_textToPath);
169 return;
170 }
174 typedef struct {
175 int type;
176 ENHMETARECORD *lpEMFR;
177 } EMF_OBJECT, *PEMF_OBJECT;
179 typedef struct emf_callback_data {
180 Glib::ustring *outsvg;
181 Glib::ustring *path;
182 struct SPStyle style;
183 bool stroke_set;
184 bool fill_set;
185 double xDPI, yDPI;
187 SIZEL sizeWnd;
188 SIZEL sizeView;
189 float PixelsX;
190 float PixelsY;
191 float MMX;
192 float MMY;
193 float dwInchesX;
194 float dwInchesY;
195 POINTL winorg;
196 POINTL vieworg;
197 double ScaleX, ScaleY;
199 int n_obj;
200 PEMF_OBJECT emf_obj;
201 } EMF_CALLBACK_DATA, *PEMF_CALLBACK_DATA;
204 static void
205 output_style(PEMF_CALLBACK_DATA d, int iType)
206 {
207 SVGOStringStream tmp_style;
208 char tmp[1024] = {0};
210 *(d->outsvg) += "\n\tstyle=\"";
211 if (iType == EMR_STROKEPATH || !d->fill_set) {
212 tmp_style << "fill:none;";
213 } else {
214 float rgb[3];
215 sp_color_get_rgb_floatv( &(d->style.fill.value.color), rgb );
216 snprintf(tmp, 1023,
217 "fill:#%02x%02x%02x;",
218 SP_COLOR_F_TO_U(rgb[0]),
219 SP_COLOR_F_TO_U(rgb[1]),
220 SP_COLOR_F_TO_U(rgb[2]));
221 tmp_style << tmp;
222 snprintf(tmp, 1023,
223 "fill-rule:%s;",
224 d->style.fill_rule.value != 0 ? "evenodd" : "nonzero");
225 tmp_style << tmp;
226 tmp_style << "fill-opacity:1;";
227 }
229 if (iType == EMR_FILLPATH || !d->stroke_set) {
230 tmp_style << "stroke:none;";
231 } else {
232 float rgb[3];
233 sp_color_get_rgb_floatv(&(d->style.stroke.value.color), rgb);
234 snprintf(tmp, 1023,
235 "stroke:#%02x%02x%02x;",
236 SP_COLOR_F_TO_U(rgb[0]),
237 SP_COLOR_F_TO_U(rgb[1]),
238 SP_COLOR_F_TO_U(rgb[2]));
239 tmp_style << tmp;
241 tmp_style << "stroke-width:" <<
242 MAX( 0.001, d->style.stroke_width.value ) << "px;";
244 tmp_style << "stroke-linejoin:" <<
245 (d->style.stroke_linejoin.computed == 0 ? "miter" :
246 d->style.stroke_linejoin.computed == 1 ? "round" :
247 d->style.stroke_linejoin.computed == 2 ? "bevel" :
248 "unknown") << ";";
250 if (d->style.stroke_linejoin.computed == 0) {
251 tmp_style << "stroke-miterlimit:" <<
252 MAX( 0.01, d->style.stroke_miterlimit.value ) << ";";
253 }
255 if (d->style.stroke_dasharray_set &&
256 d->style.stroke_dash.n_dash && d->style.stroke_dash.dash)
257 {
258 tmp_style << "stroke-dasharray:";
259 for (int i=0; i<d->style.stroke_dash.n_dash; i++) {
260 if (i)
261 tmp_style << ",";
262 tmp_style << d->style.stroke_dash.dash[i];
263 }
264 tmp_style << ";";
265 tmp_style << "stroke-dashoffset:0;";
266 } else {
267 tmp_style << "stroke-dasharray:none;";
268 }
269 tmp_style << "stroke-opacity:1;";
270 }
271 tmp_style << "\" ";
273 *(d->outsvg) += tmp_style.str().c_str();
274 }
277 static double
278 pix_x_to_point(PEMF_CALLBACK_DATA d, double px)
279 {
280 double tmp = px - d->winorg.x;
281 tmp *= (double) PX_PER_IN / d->ScaleX;
282 tmp += d->vieworg.x;
283 return tmp;
284 }
287 static double
288 pix_y_to_point(PEMF_CALLBACK_DATA d, double px)
289 {
290 double tmp = px - d->winorg.y;
291 tmp *= (double) PX_PER_IN / d->ScaleY;
292 tmp += d->vieworg.y;
293 return tmp;
294 }
297 static double
298 pix_size_to_point(PEMF_CALLBACK_DATA d, double px)
299 {
300 double tmp = px;
301 tmp *= (double) PX_PER_IN / d->ScaleX;
302 return tmp;
303 }
306 static void
307 select_pen(PEMF_CALLBACK_DATA d, int index)
308 {
309 PEMRCREATEPEN pEmr = NULL;
311 if (index >= 0 && index < d->n_obj)
312 pEmr = (PEMRCREATEPEN) d->emf_obj[index].lpEMFR;
314 if (!pEmr)
315 return;
317 switch (pEmr->lopn.lopnStyle) {
318 default:
319 {
320 d->style.stroke_dasharray_set = 0;
321 break;
322 }
323 }
325 if (pEmr->lopn.lopnWidth.x) {
326 d->style.stroke_width.value = pix_size_to_point( d, pEmr->lopn.lopnWidth.x );
327 } else { // this stroke should always be rendered as 1 pixel wide, independent of zoom level (can that be done in SVG?)
328 d->style.stroke_width.value = 1.0;
329 }
331 double r, g, b;
332 r = SP_COLOR_U_TO_F( GetRValue(pEmr->lopn.lopnColor) );
333 g = SP_COLOR_U_TO_F( GetGValue(pEmr->lopn.lopnColor) );
334 b = SP_COLOR_U_TO_F( GetBValue(pEmr->lopn.lopnColor) );
335 sp_color_set_rgb_float( &(d->style.stroke.value.color), r,g,b );
337 d->style.stroke_linejoin.computed = 1;
339 d->stroke_set = true;
340 }
343 static void
344 select_extpen(PEMF_CALLBACK_DATA d, int index)
345 {
346 PEMREXTCREATEPEN pEmr = NULL;
348 if (index >= 0 && index < d->n_obj)
349 pEmr = (PEMREXTCREATEPEN) d->emf_obj[index].lpEMFR;
351 if (!pEmr)
352 return;
354 switch (pEmr->elp.elpPenStyle & PS_STYLE_MASK) {
355 case PS_USERSTYLE:
356 {
357 if (pEmr->elp.elpNumEntries) {
358 d->style.stroke_dash.n_dash = pEmr->elp.elpNumEntries;
359 if (d->style.stroke_dash.dash)
360 delete[] d->style.stroke_dash.dash;
361 d->style.stroke_dash.dash = new double[pEmr->elp.elpNumEntries];
362 for (unsigned int i=0; i<pEmr->elp.elpNumEntries; i++) {
363 d->style.stroke_dash.dash[i] = pix_size_to_point( d, pEmr->elp.elpStyleEntry[i] );
364 }
365 d->style.stroke_dasharray_set = 1;
366 } else {
367 d->style.stroke_dasharray_set = 0;
368 }
369 break;
370 }
371 default:
372 {
373 d->style.stroke_dasharray_set = 0;
374 break;
375 }
376 }
378 switch (pEmr->elp.elpPenStyle & PS_ENDCAP_MASK) {
379 case PS_ENDCAP_ROUND:
380 {
381 d->style.stroke_linecap.computed = 1;
382 break;
383 }
384 case PS_ENDCAP_SQUARE:
385 {
386 d->style.stroke_linecap.computed = 2;
387 break;
388 }
389 case PS_ENDCAP_FLAT:
390 default:
391 {
392 d->style.stroke_linecap.computed = 0;
393 break;
394 }
395 }
397 switch (pEmr->elp.elpPenStyle & PS_JOIN_MASK) {
398 case PS_JOIN_BEVEL:
399 {
400 d->style.stroke_linejoin.computed = 2;
401 break;
402 }
403 case PS_JOIN_MITER:
404 {
405 d->style.stroke_linejoin.computed = 0;
406 break;
407 }
408 case PS_JOIN_ROUND:
409 default:
410 {
411 d->style.stroke_linejoin.computed = 1;
412 break;
413 }
414 }
416 d->style.stroke_width.value = pix_size_to_point( d, pEmr->elp.elpWidth );
418 double r, g, b;
419 r = SP_COLOR_U_TO_F( GetRValue(pEmr->elp.elpColor) );
420 g = SP_COLOR_U_TO_F( GetGValue(pEmr->elp.elpColor) );
421 b = SP_COLOR_U_TO_F( GetBValue(pEmr->elp.elpColor) );
423 sp_color_set_rgb_float( &(d->style.stroke.value.color), r,g,b );
425 d->stroke_set = true;
426 }
429 static void
430 select_brush(PEMF_CALLBACK_DATA d, int index)
431 {
432 PEMRCREATEBRUSHINDIRECT pEmr = NULL;
434 if (index >= 0 && index < d->n_obj)
435 pEmr = (PEMRCREATEBRUSHINDIRECT) d->emf_obj[index].lpEMFR;
437 if (!pEmr)
438 return;
440 if (pEmr->lb.lbStyle == BS_SOLID) {
441 double r, g, b;
442 r = SP_COLOR_U_TO_F( GetRValue(pEmr->lb.lbColor) );
443 g = SP_COLOR_U_TO_F( GetGValue(pEmr->lb.lbColor) );
444 b = SP_COLOR_U_TO_F( GetBValue(pEmr->lb.lbColor) );
445 sp_color_set_rgb_float( &(d->style.fill.value.color), r,g,b );
446 }
448 d->fill_set = true;
449 }
452 static void
453 delete_object(PEMF_CALLBACK_DATA d, int index)
454 {
455 if (index >= 0 && index < d->n_obj) {
456 d->emf_obj[index].type = 0;
457 if (d->emf_obj[index].lpEMFR)
458 free(d->emf_obj[index].lpEMFR);
459 d->emf_obj[index].lpEMFR = NULL;
460 }
461 }
464 static void
465 insert_object(PEMF_CALLBACK_DATA d, int index, int type, ENHMETARECORD *pObj)
466 {
467 if (index >= 0 && index < d->n_obj) {
468 delete_object(d, index);
469 d->emf_obj[index].type = type;
470 d->emf_obj[index].lpEMFR = pObj;
471 }
472 }
475 static int CALLBACK
476 myEnhMetaFileProc(HDC hDC, HANDLETABLE *lpHTable, ENHMETARECORD *lpEMFR, int nObj, LPARAM lpData)
477 {
478 PEMF_CALLBACK_DATA d;
479 SVGOStringStream tmp_outsvg;
480 SVGOStringStream tmp_path;
481 SVGOStringStream tmp_str;
483 d = (PEMF_CALLBACK_DATA) lpData;
485 switch (lpEMFR->iType)
486 {
487 case EMR_HEADER:
488 {
489 ENHMETAHEADER *pEmr = (ENHMETAHEADER *) lpEMFR;
490 tmp_outsvg << "<svg\n";
492 d->xDPI = 2540;
493 d->yDPI = 2540;
495 d->PixelsX = pEmr->rclFrame.right - pEmr->rclFrame.left;
496 d->PixelsY = pEmr->rclFrame.bottom - pEmr->rclFrame.top;
498 d->MMX = d->PixelsX / 100.0;
499 d->MMY = d->PixelsY / 100.0;
501 tmp_outsvg <<
502 " width=\"" << d->MMX << "mm\"\n" <<
503 " height=\"" << d->MMY << "mm\">\n";
505 if (pEmr->nHandles) {
506 d->n_obj = pEmr->nHandles;
507 d->emf_obj = new EMF_OBJECT[d->n_obj];
508 } else {
509 d->emf_obj = NULL;
510 }
512 break;
513 }
514 case EMR_POLYBEZIER:
515 {
516 PEMRPOLYBEZIER pEmr = (PEMRPOLYBEZIER) lpEMFR;
517 DWORD i,j;
519 if (pEmr->cptl<4)
520 break;
522 *(d->outsvg) += " <path ";
523 output_style(d, EMR_STROKEPATH);
524 *(d->outsvg) += "\n\td=\"";
526 tmp_str <<
527 "\n\tM " <<
528 pix_x_to_point( d, pEmr->aptl[0].x ) << " " <<
529 pix_x_to_point( d, pEmr->aptl[0].y) << " ";
531 for (i=1; i<pEmr->cptl; ) {
532 tmp_str << "\n\tC ";
533 for (j=0; j<3 && i<pEmr->cptl; j++,i++) {
534 tmp_str <<
535 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
536 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
537 }
538 }
540 *(d->outsvg) += tmp_str.str().c_str();
541 *(d->outsvg) += " \" /> \n";
543 break;
544 }
545 case EMR_POLYGON:
546 {
547 EMRPOLYGON *pEmr = (EMRPOLYGON *) lpEMFR;
548 DWORD i;
550 if (pEmr->cptl < 2)
551 break;
553 *(d->outsvg) += " <path ";
554 output_style(d, EMR_STROKEANDFILLPATH);
555 *(d->outsvg) += "\n\td=\"";
557 tmp_str <<
558 "\n\tM " <<
559 pix_x_to_point( d, pEmr->aptl[0].x ) << " " <<
560 pix_y_to_point( d, pEmr->aptl[0].y ) << " ";
562 for (i=1; i<pEmr->cptl; i++) {
563 tmp_str <<
564 "\n\tL " <<
565 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
566 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
567 }
569 *(d->outsvg) += tmp_str.str().c_str();
570 *(d->outsvg) += " z \" /> \n";
572 break;
573 }
574 case EMR_POLYLINE:
575 {
576 EMRPOLYLINE *pEmr = (EMRPOLYLINE *) lpEMFR;
577 DWORD i;
579 if (pEmr->cptl<2)
580 break;
582 *(d->outsvg) += " <path ";
583 output_style(d, EMR_STROKEPATH);
584 *(d->outsvg) += "\n\td=\"";
586 tmp_str <<
587 "\n\tM " <<
588 pix_x_to_point( d, pEmr->aptl[0].x ) << " " <<
589 pix_y_to_point( d, pEmr->aptl[0].y ) << " ";
591 for (i=1; i<pEmr->cptl; i++) {
592 tmp_str <<
593 "\n\tL " <<
594 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
595 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
596 }
598 *(d->outsvg) += tmp_str.str().c_str();
599 *(d->outsvg) += " \" /> \n";
601 break;
602 }
603 case EMR_POLYBEZIERTO:
604 {
605 PEMRPOLYBEZIERTO pEmr = (PEMRPOLYBEZIERTO) lpEMFR;
606 DWORD i,j;
608 for (i=0; i<pEmr->cptl;) {
609 tmp_path << "\n\tC ";
610 for (j=0; j<3 && i<pEmr->cptl; j++,i++) {
611 tmp_path <<
612 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
613 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
614 }
615 }
617 break;
618 }
619 case EMR_POLYLINETO:
620 {
621 PEMRPOLYLINETO pEmr = (PEMRPOLYLINETO) lpEMFR;
622 DWORD i;
624 for (i=0; i<pEmr->cptl;i++) {
625 tmp_path <<
626 "\n\tL " <<
627 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
628 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
629 }
631 break;
632 }
633 case EMR_POLYPOLYLINE:
634 break;
635 case EMR_POLYPOLYGON:
636 break;
637 case EMR_SETWINDOWEXTEX:
638 {
639 PEMRSETWINDOWEXTEX pEmr = (PEMRSETWINDOWEXTEX) lpEMFR;
641 d->sizeWnd = pEmr->szlExtent;
642 d->PixelsX = d->sizeWnd.cx;
643 d->PixelsY = d->sizeWnd.cy;
645 d->ScaleX = d->xDPI / (100*d->MMX / d->PixelsX);
646 d->ScaleY = d->yDPI / (100*d->MMY / d->PixelsY);
648 break;
649 }
650 case EMR_SETWINDOWORGEX:
651 {
652 PEMRSETWINDOWORGEX pEmr = (PEMRSETWINDOWORGEX) lpEMFR;
653 d->winorg = pEmr->ptlOrigin;
654 break;
655 }
656 case EMR_SETVIEWPORTEXTEX:
657 {
658 PEMRSETVIEWPORTEXTEX pEmr = (PEMRSETVIEWPORTEXTEX) lpEMFR;
660 d->sizeView = pEmr->szlExtent;
662 if (d->sizeWnd.cx && d->sizeWnd.cy) {
663 HDC hScreenDC = GetDC( NULL );
665 float scrPixelsX = (float)GetDeviceCaps( hScreenDC, HORZRES );
666 float scrPixelsY = (float)GetDeviceCaps( hScreenDC, VERTRES );
667 float scrMMX = (float)GetDeviceCaps( hScreenDC, HORZSIZE );
668 float scrMMY = (float)GetDeviceCaps( hScreenDC, VERTSIZE );
670 ReleaseDC( NULL, hScreenDC );
672 d->dwInchesX = d->sizeView.cx / (25.4f*scrPixelsX/scrMMX);
673 d->dwInchesY = d->sizeView.cy / (25.4f*scrPixelsY/scrMMY);
674 d->xDPI = d->sizeWnd.cx / d->dwInchesX;
675 d->yDPI = d->sizeWnd.cy / d->dwInchesY;
677 if (1) {
678 d->xDPI = 2540;
679 d->yDPI = 2540;
680 d->dwInchesX = d->PixelsX / d->xDPI;
681 d->dwInchesY = d->PixelsY / d->yDPI;
682 d->ScaleX = d->xDPI;
683 d->ScaleY = d->yDPI;
684 }
686 d->MMX = d->dwInchesX * MM_PER_IN;
687 d->MMY = d->dwInchesY * MM_PER_IN;
688 }
690 break;
691 }
692 case EMR_SETVIEWPORTORGEX:
693 {
694 PEMRSETVIEWPORTORGEX pEmr = (PEMRSETVIEWPORTORGEX) lpEMFR;
695 d->vieworg = pEmr->ptlOrigin;
696 break;
697 }
698 case EMR_SETBRUSHORGEX:
699 break;
700 case EMR_EOF:
701 {
702 tmp_outsvg << "</svg>\n";
703 break;
704 }
705 case EMR_SETPIXELV:
706 break;
707 case EMR_SETMAPPERFLAGS:
708 break;
709 case EMR_SETMAPMODE:
710 break;
711 case EMR_SETBKMODE:
712 break;
713 case EMR_SETPOLYFILLMODE:
714 {
715 PEMRSETPOLYFILLMODE pEmr = (PEMRSETPOLYFILLMODE) lpEMFR;
716 d->style.fill_rule.value =
717 (pEmr->iMode == WINDING ? 0 :
718 pEmr->iMode == ALTERNATE ? 1 : 0);
719 break;
720 }
721 case EMR_SETROP2:
722 break;
723 case EMR_SETSTRETCHBLTMODE:
724 break;
725 case EMR_SETTEXTALIGN:
726 break;
727 case EMR_SETCOLORADJUSTMENT:
728 break;
729 case EMR_SETTEXTCOLOR:
730 break;
731 case EMR_SETBKCOLOR:
732 break;
733 case EMR_OFFSETCLIPRGN:
734 break;
735 case EMR_MOVETOEX:
736 {
737 PEMRMOVETOEX pEmr = (PEMRMOVETOEX) lpEMFR;
738 tmp_path <<
739 "\n\tM " <<
740 pix_x_to_point( d, pEmr->ptl.x ) << " " <<
741 pix_y_to_point( d, pEmr->ptl.y ) << " ";
742 break;
743 }
744 case EMR_SETMETARGN:
745 break;
746 case EMR_EXCLUDECLIPRECT:
747 break;
748 case EMR_INTERSECTCLIPRECT:
749 break;
750 case EMR_SCALEVIEWPORTEXTEX:
751 break;
752 case EMR_SCALEWINDOWEXTEX:
753 break;
754 case EMR_SAVEDC:
755 break;
756 case EMR_RESTOREDC:
757 break;
758 case EMR_SETWORLDTRANSFORM:
759 break;
760 case EMR_MODIFYWORLDTRANSFORM:
761 break;
762 case EMR_SELECTOBJECT:
763 {
764 PEMRSELECTOBJECT pEmr = (PEMRSELECTOBJECT) lpEMFR;
765 unsigned int index = pEmr->ihObject;
767 if (index >= ENHMETA_STOCK_OBJECT) {
768 index -= ENHMETA_STOCK_OBJECT;
769 switch (index) {
770 case NULL_BRUSH:
771 d->fill_set = false;
772 break;
773 case BLACK_BRUSH:
774 case DKGRAY_BRUSH:
775 case GRAY_BRUSH:
776 case LTGRAY_BRUSH:
777 case WHITE_BRUSH:
778 {
779 float val = 0;
780 switch (index) {
781 case BLACK_BRUSH:
782 val = 0.0 / 255.0;
783 break;
784 case DKGRAY_BRUSH:
785 val = 64.0 / 255.0;
786 break;
787 case GRAY_BRUSH:
788 val = 128.0 / 255.0;
789 break;
790 case LTGRAY_BRUSH:
791 val = 192.0 / 255.0;
792 break;
793 case WHITE_BRUSH:
794 val = 255.0 / 255.0;
795 break;
796 }
797 sp_color_set_rgb_float( &(d->style.fill.value.color), val,val,val );
799 d->fill_set = true;
800 break;
801 }
802 case NULL_PEN:
803 d->stroke_set = false;
804 break;
805 case BLACK_PEN:
806 case WHITE_PEN:
807 {
808 float val = index == BLACK_PEN ? 0 : 1;
809 d->style.stroke_dasharray_set = 0;
810 d->style.stroke_width.value = 1.0;
811 sp_color_set_rgb_float( &(d->style.stroke.value.color), val,val,val );
813 d->stroke_set = true;
815 break;
816 }
817 }
818 } else {
819 if (index >= 0 && index < (unsigned int)d->n_obj) {
820 switch (d->emf_obj[index].type)
821 {
822 case EMR_CREATEPEN:
823 select_pen(d, index);
824 break;
825 case EMR_CREATEBRUSHINDIRECT:
826 select_brush(d, index);
827 break;
828 case EMR_EXTCREATEPEN:
829 select_extpen(d, index);
830 break;
831 }
832 }
833 }
834 break;
835 }
836 case EMR_CREATEPEN:
837 {
838 PEMRCREATEPEN pEmr = (PEMRCREATEPEN) lpEMFR;
839 int index = pEmr->ihPen;
841 EMRCREATEPEN *pPen =
842 (EMRCREATEPEN *) malloc( sizeof(EMREXTCREATEPEN) );
843 pPen->lopn = pEmr->lopn;
844 insert_object(d, index, EMR_CREATEPEN, (ENHMETARECORD *) pPen);
846 break;
847 }
848 case EMR_CREATEBRUSHINDIRECT:
849 {
850 PEMRCREATEBRUSHINDIRECT pEmr = (PEMRCREATEBRUSHINDIRECT) lpEMFR;
851 int index = pEmr->ihBrush;
853 EMRCREATEBRUSHINDIRECT *pBrush =
854 (EMRCREATEBRUSHINDIRECT *) malloc( sizeof(EMRCREATEBRUSHINDIRECT) );
855 pBrush->lb = pEmr->lb;
856 insert_object(d, index, EMR_CREATEBRUSHINDIRECT, (ENHMETARECORD *) pBrush);
858 break;
859 }
860 case EMR_DELETEOBJECT:
861 break;
862 case EMR_ANGLEARC:
863 break;
864 case EMR_ELLIPSE:
865 break;
866 case EMR_RECTANGLE:
867 break;
868 case EMR_ROUNDRECT:
869 break;
870 case EMR_ARC:
871 break;
872 case EMR_CHORD:
873 break;
874 case EMR_PIE:
875 break;
876 case EMR_SELECTPALETTE:
877 break;
878 case EMR_CREATEPALETTE:
879 break;
880 case EMR_SETPALETTEENTRIES:
881 break;
882 case EMR_RESIZEPALETTE:
883 break;
884 case EMR_REALIZEPALETTE:
885 break;
886 case EMR_EXTFLOODFILL:
887 break;
888 case EMR_LINETO:
889 {
890 PEMRLINETO pEmr = (PEMRLINETO) lpEMFR;
891 tmp_path <<
892 "\n\tL " <<
893 pix_x_to_point( d, pEmr->ptl.x ) << " " <<
894 pix_y_to_point( d, pEmr->ptl.y ) << " ";
895 break;
896 }
897 case EMR_ARCTO:
898 break;
899 case EMR_POLYDRAW:
900 break;
901 case EMR_SETARCDIRECTION:
902 break;
903 case EMR_SETMITERLIMIT:
904 {
905 PEMRSETMITERLIMIT pEmr = (PEMRSETMITERLIMIT) lpEMFR;
906 d->style.stroke_miterlimit.value = pix_size_to_point( d, pEmr->eMiterLimit );
908 if (d->style.stroke_miterlimit.value < 1)
909 d->style.stroke_miterlimit.value = 1.0;
911 break;
912 }
913 case EMR_BEGINPATH:
914 {
915 tmp_path << " d=\"";
916 *(d->path) = "";
917 break;
918 }
919 case EMR_ENDPATH:
920 {
921 tmp_path << "\"";
922 break;
923 }
924 case EMR_CLOSEFIGURE:
925 {
926 tmp_path << "\n\tz";
927 break;
928 }
929 case EMR_FILLPATH:
930 case EMR_STROKEANDFILLPATH:
931 case EMR_STROKEPATH:
932 {
933 *(d->outsvg) += " <path ";
934 output_style(d, lpEMFR->iType);
935 *(d->outsvg) += "\n\t";
936 *(d->outsvg) += *(d->path);
937 *(d->outsvg) += " /> \n";
938 break;
939 }
940 case EMR_FLATTENPATH:
941 break;
942 case EMR_WIDENPATH:
943 break;
944 case EMR_SELECTCLIPPATH:
945 break;
946 case EMR_ABORTPATH:
947 break;
948 case EMR_GDICOMMENT:
949 break;
950 case EMR_FILLRGN:
951 break;
952 case EMR_FRAMERGN:
953 break;
954 case EMR_INVERTRGN:
955 break;
956 case EMR_PAINTRGN:
957 break;
958 case EMR_EXTSELECTCLIPRGN:
959 break;
960 case EMR_BITBLT:
961 break;
962 case EMR_STRETCHBLT:
963 break;
964 case EMR_MASKBLT:
965 break;
966 case EMR_PLGBLT:
967 break;
968 case EMR_SETDIBITSTODEVICE:
969 break;
970 case EMR_STRETCHDIBITS:
971 break;
972 case EMR_EXTCREATEFONTINDIRECTW:
973 break;
974 case EMR_EXTTEXTOUTA:
975 break;
976 case EMR_EXTTEXTOUTW:
977 break;
978 case EMR_POLYBEZIER16:
979 {
980 PEMRPOLYBEZIER16 pEmr = (PEMRPOLYBEZIER16) lpEMFR;
981 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
982 DWORD i,j;
984 if (pEmr->cpts<4)
985 break;
987 *(d->outsvg) += " <path ";
988 output_style(d, EMR_STROKEPATH);
989 *(d->outsvg) += "\n\td=\"";
991 tmp_str <<
992 "\n\tM " <<
993 pix_x_to_point( d, apts[0].x ) << " " <<
994 pix_y_to_point( d, apts[0].y ) << " ";
996 for (i=1; i<pEmr->cpts; ) {
997 tmp_str << "\n\tC ";
998 for (j=0; j<3 && i<pEmr->cpts; j++,i++) {
999 tmp_str <<
1000 pix_x_to_point( d, apts[i].x ) << " " <<
1001 pix_y_to_point( d, apts[i].y ) << " ";
1002 }
1003 }
1005 *(d->outsvg) += tmp_str.str().c_str();
1006 *(d->outsvg) += " \" /> \n";
1008 break;
1009 }
1010 case EMR_POLYGON16:
1011 {
1012 PEMRPOLYGON16 pEmr = (PEMRPOLYGON16) lpEMFR;
1013 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
1014 unsigned int i;
1016 *(d->outsvg) += "<path ";
1017 output_style(d, EMR_STROKEANDFILLPATH);
1018 *(d->outsvg) += "\n\td=\"";
1020 // skip the first point?
1021 tmp_path << "\n\tM " <<
1022 pix_x_to_point( d, apts[1].x ) << " " <<
1023 pix_y_to_point( d, apts[1].y ) << " ";
1025 for (i=2; i<pEmr->cpts; i++) {
1026 tmp_path << "\n\tL " <<
1027 pix_x_to_point( d, apts[i].x ) << " " <<
1028 pix_y_to_point( d, apts[i].y ) << " ";
1029 }
1031 *(d->outsvg) += tmp_path.str().c_str();
1032 *(d->outsvg) += " z \" /> \n";
1034 break;
1035 }
1036 case EMR_POLYLINE16:
1037 {
1038 EMRPOLYLINE16 *pEmr = (EMRPOLYLINE16 *) lpEMFR;
1039 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
1040 DWORD i;
1042 if (pEmr->cpts<2)
1043 break;
1045 *(d->outsvg) += " <path ";
1046 output_style(d, EMR_STROKEPATH);
1047 *(d->outsvg) += "\n\td=\"";
1049 tmp_str <<
1050 "\n\tM " <<
1051 pix_x_to_point( d, apts[0].x ) << " " <<
1052 pix_y_to_point( d, apts[0].y ) << " ";
1054 for (i=1; i<pEmr->cpts; i++) {
1055 tmp_str <<
1056 "\n\tL " <<
1057 pix_x_to_point( d, apts[i].x ) << " " <<
1058 pix_y_to_point( d, apts[i].y ) << " ";
1059 }
1061 *(d->outsvg) += tmp_str.str().c_str();
1062 *(d->outsvg) += " \" /> \n";
1064 break;
1065 }
1066 case EMR_POLYBEZIERTO16:
1067 {
1068 PEMRPOLYBEZIERTO16 pEmr = (PEMRPOLYBEZIERTO16) lpEMFR;
1069 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
1070 DWORD i,j;
1072 for (i=0; i<pEmr->cpts;) {
1073 tmp_path << "\n\tC ";
1074 for (j=0; j<3 && i<pEmr->cpts; j++,i++) {
1075 tmp_path <<
1076 pix_x_to_point( d, apts[i].x ) << " " <<
1077 pix_y_to_point( d, apts[i].y ) << " ";
1078 }
1079 }
1081 break;
1082 }
1083 case EMR_POLYLINETO16:
1084 {
1085 PEMRPOLYLINETO16 pEmr = (PEMRPOLYLINETO16) lpEMFR;
1086 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
1087 DWORD i;
1089 for (i=0; i<pEmr->cpts;i++) {
1090 tmp_path <<
1091 "\n\tL " <<
1092 pix_x_to_point( d, apts[i].x ) << " " <<
1093 pix_y_to_point( d, apts[i].y ) << " ";
1094 }
1096 break;
1097 }
1098 case EMR_POLYPOLYLINE16:
1099 break;
1100 case EMR_POLYPOLYGON16:
1101 {
1102 PEMRPOLYPOLYGON16 pEmr = (PEMRPOLYPOLYGON16) lpEMFR;
1103 unsigned int n, i, j;
1105 *(d->outsvg) += "<path ";
1106 output_style(d, EMR_STROKEANDFILLPATH);
1107 *(d->outsvg) += "\n\td=\"";
1109 i = pEmr->nPolys-1; // ???
1110 for (n=0; n<pEmr->nPolys /*&& i<pEmr->cpts*/; n++) {
1111 SVGOStringStream poly_path;
1113 poly_path << "\n\tM " <<
1114 pix_x_to_point( d, pEmr->apts[i].x ) << " " <<
1115 pix_y_to_point( d, pEmr->apts[i].y ) << " ";
1116 i++;
1118 for (j=1; j<pEmr->aPolyCounts[n] /*&& i<pEmr->cpts*/; j++) {
1119 poly_path << "\n\tL " <<
1120 pix_x_to_point( d, pEmr->apts[i].x ) << " " <<
1121 pix_y_to_point( d, pEmr->apts[i].y ) << " ";
1122 i++;
1123 }
1125 *(d->outsvg) += poly_path.str().c_str();
1126 *(d->outsvg) += " z \n";
1127 }
1129 *(d->outsvg) += " \" /> \n";
1130 break;
1131 }
1132 case EMR_POLYDRAW16:
1133 break;
1134 case EMR_CREATEMONOBRUSH:
1135 break;
1136 case EMR_CREATEDIBPATTERNBRUSHPT:
1137 break;
1138 case EMR_EXTCREATEPEN:
1139 {
1140 PEMREXTCREATEPEN pEmr = (PEMREXTCREATEPEN) lpEMFR;
1141 int index = pEmr->ihPen;
1143 EMREXTCREATEPEN *pPen =
1144 (EMREXTCREATEPEN *) malloc( sizeof(EMREXTCREATEPEN) +
1145 sizeof(DWORD) * pEmr->elp.elpNumEntries );
1146 pPen->ihPen = pEmr->ihPen;
1147 pPen->offBmi = pEmr->offBmi;
1148 pPen->cbBmi = pEmr->cbBmi;
1149 pPen->offBits = pEmr->offBits;
1150 pPen->cbBits = pEmr->cbBits;
1151 pPen->elp = pEmr->elp;
1152 for (unsigned int i=0; i<pEmr->elp.elpNumEntries; i++) {
1153 pPen->elp.elpStyleEntry[i] = pEmr->elp.elpStyleEntry[i];
1154 }
1155 insert_object(d, index, EMR_EXTCREATEPEN, (ENHMETARECORD *) pPen);
1157 break;
1158 }
1159 case EMR_POLYTEXTOUTA:
1160 break;
1161 case EMR_POLYTEXTOUTW:
1162 break;
1163 case EMR_SETICMMODE:
1164 break;
1165 case EMR_CREATECOLORSPACE:
1166 break;
1167 case EMR_SETCOLORSPACE:
1168 break;
1169 case EMR_DELETECOLORSPACE:
1170 break;
1171 case EMR_GLSRECORD:
1172 break;
1173 case EMR_GLSBOUNDEDRECORD:
1174 break;
1175 case EMR_PIXELFORMAT:
1176 break;
1177 }
1179 *(d->outsvg) += tmp_outsvg.str().c_str();
1180 *(d->path) += tmp_path.str().c_str();
1182 return 1;
1183 }
1186 // Aldus Placeable Header ===================================================
1187 // Since we are a 32bit app, we have to be sure this structure compiles to
1188 // be identical to a 16 bit app's version. To do this, we use the #pragma
1189 // to adjust packing, we use a WORD for the hmf handle, and a SMALL_RECT
1190 // for the bbox rectangle.
1191 #pragma pack( push )
1192 #pragma pack( 2 )
1193 typedef struct
1194 {
1195 DWORD dwKey;
1196 WORD hmf;
1197 SMALL_RECT bbox;
1198 WORD wInch;
1199 DWORD dwReserved;
1200 WORD wCheckSum;
1201 } APMHEADER, *PAPMHEADER;
1202 #pragma pack( pop )
1205 SPDocument *
1206 EmfWin32::open( Inkscape::Extension::Input *mod, const gchar *uri )
1207 {
1208 EMF_CALLBACK_DATA d = {0};
1210 gsize bytesRead = 0;
1211 gsize bytesWritten = 0;
1212 GError* error = NULL;
1213 gchar *local_fn =
1214 g_filename_from_utf8( uri, -1, &bytesRead, &bytesWritten, &error );
1216 if (local_fn == NULL) {
1217 return NULL;
1218 }
1220 d.outsvg = new Glib::ustring("");
1221 d.path = new Glib::ustring("");
1223 CHAR *ansi_uri = (CHAR *) local_fn;
1224 gunichar2 *unicode_fn = g_utf8_to_utf16( local_fn, -1, NULL, NULL, NULL );
1225 WCHAR *unicode_uri = (WCHAR *) unicode_fn;
1227 // Try open as Enhanced Metafile
1228 HENHMETAFILE hemf;
1229 if (PrintWin32::is_os_wide())
1230 hemf = GetEnhMetaFileW(unicode_uri);
1231 else
1232 hemf = GetEnhMetaFileA(ansi_uri);
1234 if (!hemf) {
1235 // Try open as Windows Metafile
1236 HMETAFILE hmf;
1237 if (PrintWin32::is_os_wide())
1238 hmf = GetMetaFileW(unicode_uri);
1239 else
1240 hmf = GetMetaFileA(ansi_uri);
1242 METAFILEPICT mp;
1243 HDC hDC;
1245 if (!hmf) {
1246 if (PrintWin32::is_os_wide()) {
1247 WCHAR szTemp[MAX_PATH];
1249 DWORD dw = GetShortPathNameW( unicode_uri, szTemp, MAX_PATH );
1250 if (dw) {
1251 hmf = GetMetaFileW( szTemp );
1252 }
1253 } else {
1254 CHAR szTemp[MAX_PATH];
1256 DWORD dw = GetShortPathNameA( ansi_uri, szTemp, MAX_PATH );
1257 if (dw) {
1258 hmf = GetMetaFileA( szTemp );
1259 }
1260 }
1261 }
1263 if (hmf) {
1264 DWORD nSize = GetMetaFileBitsEx( hmf, 0, NULL );
1265 if (nSize) {
1266 BYTE *lpvData = new BYTE[nSize];
1267 if (lpvData) {
1268 DWORD dw = GetMetaFileBitsEx( hmf, nSize, lpvData );
1269 if (dw) {
1270 // Fill out a METAFILEPICT structure
1271 mp.mm = MM_ANISOTROPIC;
1272 mp.xExt = 1000;
1273 mp.yExt = 1000;
1274 mp.hMF = NULL;
1275 // Get a reference DC
1276 hDC = GetDC( NULL );
1277 // Make an enhanced metafile from the windows metafile
1278 hemf = SetWinMetaFileBits( nSize, lpvData, hDC, &mp );
1279 // Clean up
1280 ReleaseDC( NULL, hDC );
1281 }
1282 delete[] lpvData;
1283 }
1284 DeleteMetaFile( hmf );
1285 }
1286 } else {
1287 // Try open as Aldus Placeable Metafile
1288 HANDLE hFile;
1289 if (PrintWin32::is_os_wide())
1290 hFile = CreateFileW( unicode_uri, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
1291 else
1292 hFile = CreateFileA( ansi_uri, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
1293 if (hFile != INVALID_HANDLE_VALUE) {
1294 DWORD nSize = GetFileSize( hFile, NULL );
1295 if (nSize) {
1296 BYTE *lpvData = new BYTE[nSize];
1297 if (lpvData) {
1298 DWORD dw = ReadFile( hFile, lpvData, nSize, &nSize, NULL );
1299 if (dw) {
1300 if ( ((PAPMHEADER)lpvData)->dwKey == 0x9ac6cdd7l ) {
1301 // Fill out a METAFILEPICT structure
1302 mp.mm = MM_ANISOTROPIC;
1303 mp.xExt = ((PAPMHEADER)lpvData)->bbox.Right - ((PAPMHEADER)lpvData)->bbox.Left;
1304 mp.xExt = ( mp.xExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch);
1305 mp.yExt = ((PAPMHEADER)lpvData)->bbox.Bottom - ((PAPMHEADER)lpvData)->bbox.Top;
1306 mp.yExt = ( mp.yExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch);
1307 mp.hMF = NULL;
1308 // Get a reference DC
1309 hDC = GetDC( NULL );
1310 // Create an enhanced metafile from the bits
1311 hemf = SetWinMetaFileBits( nSize, lpvData+sizeof(APMHEADER), hDC, &mp );
1312 // Clean up
1313 ReleaseDC( NULL, hDC );
1314 }
1315 }
1316 delete[] lpvData;
1317 }
1318 }
1319 CloseHandle( hFile );
1320 }
1321 }
1322 }
1324 if (!hemf || !d.outsvg || !d.path) {
1325 if (d.outsvg)
1326 delete d.outsvg;
1327 if (d.path)
1328 delete d.path;
1329 if (local_fn)
1330 g_free(local_fn);
1331 if (unicode_fn)
1332 g_free(unicode_fn);
1333 return NULL;
1334 }
1336 EnumEnhMetaFile(NULL, hemf, myEnhMetaFileProc, (LPVOID) &d, NULL);
1337 DeleteEnhMetaFile(hemf);
1339 // std::cout << "SVG Output: " << std::endl << *(d.outsvg) << std::endl;
1341 SPDocument *doc = sp_document_new_from_mem(d.outsvg->c_str(), d.outsvg->length(), TRUE);
1343 delete d.outsvg;
1344 delete d.path;
1346 if (d.emf_obj) {
1347 int i;
1348 for (i=0; i<d.n_obj; i++)
1349 delete_object(&d, i);
1350 delete[] d.emf_obj;
1351 }
1353 if (d.style.stroke_dash.dash)
1354 delete[] d.style.stroke_dash.dash;
1356 if (local_fn)
1357 g_free(local_fn);
1358 if (unicode_fn)
1359 g_free(unicode_fn);
1361 return doc;
1362 }
1365 void
1366 EmfWin32::init (void)
1367 {
1368 Inkscape::Extension::Extension * ext;
1370 /* EMF in */
1371 ext = Inkscape::Extension::build_from_mem(
1372 "<inkscape-extension>\n"
1373 "<name>" N_("EMF Input") "</name>\n"
1374 "<id>org.inkscape.input.emf.win32</id>\n"
1375 "<input>\n"
1376 "<extension>.emf</extension>\n"
1377 "<mimetype>image/x-emf</mimetype>\n"
1378 "<filetypename>" N_("Enhanced Metafiles (*.emf)") "</filetypename>\n"
1379 "<filetypetooltip>" N_("Enhanced Metafiles") "</filetypetooltip>\n"
1380 "<output_extension>org.inkscape.output.emf.win32</output_extension>\n"
1381 "</input>\n"
1382 "</inkscape-extension>", new EmfWin32());
1384 /* WMF in */
1385 ext = Inkscape::Extension::build_from_mem(
1386 "<inkscape-extension>\n"
1387 "<name>" N_("WMF Input") "</name>\n"
1388 "<id>org.inkscape.input.wmf.win32</id>\n"
1389 "<input>\n"
1390 "<extension>.wmf</extension>\n"
1391 "<mimetype>image/x-wmf</mimetype>\n"
1392 "<filetypename>" N_("Windows Metafiles (*.wmf)") "</filetypename>\n"
1393 "<filetypetooltip>" N_("Windows Metafiles") "</filetypetooltip>\n"
1394 "<output_extension>org.inkscape.output.emf.win32</output_extension>\n"
1395 "</input>\n"
1396 "</inkscape-extension>", new EmfWin32());
1398 /* EMF out */
1399 ext = Inkscape::Extension::build_from_mem(
1400 "<inkscape-extension>\n"
1401 "<name>" N_("EMF Output") "</name>\n"
1402 "<id>org.inkscape.output.emf.win32</id>\n"
1403 "<output>\n"
1404 "<extension>.emf</extension>\n"
1405 "<mimetype>image/x-emf</mimetype>\n"
1406 "<filetypename>" N_("Enhanced Metafile (*.emf)") "</filetypename>\n"
1407 "<filetypetooltip>" N_("Enhanced Metafile") "</filetypetooltip>\n"
1408 "</output>\n"
1409 "</inkscape-extension>", new EmfWin32());
1411 return;
1412 }
1415 } } } /* namespace Inkscape, Extension, Implementation */
1418 #endif /* WIN32 */
1421 /*
1422 Local Variables:
1423 mode:cpp
1424 c-file-style:"stroustrup"
1425 c-file-offsets:((innamespace . 0)(inline-open . 0))
1426 indent-tabs-mode:nil
1427 fill-column:99
1428 End:
1429 */
1430 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :