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 d->style.stroke.value.color.set( 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 d->style.stroke.value.color.set( 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 d->style.fill.value.color.set( 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];
509 // Init the new emf_obj list elements to null, provided the
510 // dynamic allocation succeeded.
511 if ( d->emf_obj != NULL )
512 {
513 for( unsigned int i=0; i < d->n_obj; ++i )
514 d->emf_obj[i].lpEMFR = NULL;
515 } //if
517 } else {
518 d->emf_obj = NULL;
519 }
521 break;
522 }
523 case EMR_POLYBEZIER:
524 {
525 PEMRPOLYBEZIER pEmr = (PEMRPOLYBEZIER) lpEMFR;
526 DWORD i,j;
528 if (pEmr->cptl<4)
529 break;
531 *(d->outsvg) += " <path ";
532 output_style(d, EMR_STROKEPATH);
533 *(d->outsvg) += "\n\td=\"";
535 tmp_str <<
536 "\n\tM " <<
537 pix_x_to_point( d, pEmr->aptl[0].x ) << " " <<
538 pix_x_to_point( d, pEmr->aptl[0].y) << " ";
540 for (i=1; i<pEmr->cptl; ) {
541 tmp_str << "\n\tC ";
542 for (j=0; j<3 && i<pEmr->cptl; j++,i++) {
543 tmp_str <<
544 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
545 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
546 }
547 }
549 *(d->outsvg) += tmp_str.str().c_str();
550 *(d->outsvg) += " \" /> \n";
552 break;
553 }
554 case EMR_POLYGON:
555 {
556 EMRPOLYGON *pEmr = (EMRPOLYGON *) lpEMFR;
557 DWORD i;
559 if (pEmr->cptl < 2)
560 break;
562 *(d->outsvg) += " <path ";
563 output_style(d, EMR_STROKEANDFILLPATH);
564 *(d->outsvg) += "\n\td=\"";
566 tmp_str <<
567 "\n\tM " <<
568 pix_x_to_point( d, pEmr->aptl[0].x ) << " " <<
569 pix_y_to_point( d, pEmr->aptl[0].y ) << " ";
571 for (i=1; i<pEmr->cptl; i++) {
572 tmp_str <<
573 "\n\tL " <<
574 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
575 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
576 }
578 *(d->outsvg) += tmp_str.str().c_str();
579 *(d->outsvg) += " z \" /> \n";
581 break;
582 }
583 case EMR_POLYLINE:
584 {
585 EMRPOLYLINE *pEmr = (EMRPOLYLINE *) lpEMFR;
586 DWORD i;
588 if (pEmr->cptl<2)
589 break;
591 *(d->outsvg) += " <path ";
592 output_style(d, EMR_STROKEPATH);
593 *(d->outsvg) += "\n\td=\"";
595 tmp_str <<
596 "\n\tM " <<
597 pix_x_to_point( d, pEmr->aptl[0].x ) << " " <<
598 pix_y_to_point( d, pEmr->aptl[0].y ) << " ";
600 for (i=1; i<pEmr->cptl; i++) {
601 tmp_str <<
602 "\n\tL " <<
603 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
604 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
605 }
607 *(d->outsvg) += tmp_str.str().c_str();
608 *(d->outsvg) += " \" /> \n";
610 break;
611 }
612 case EMR_POLYBEZIERTO:
613 {
614 PEMRPOLYBEZIERTO pEmr = (PEMRPOLYBEZIERTO) lpEMFR;
615 DWORD i,j;
617 for (i=0; i<pEmr->cptl;) {
618 tmp_path << "\n\tC ";
619 for (j=0; j<3 && i<pEmr->cptl; j++,i++) {
620 tmp_path <<
621 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
622 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
623 }
624 }
626 break;
627 }
628 case EMR_POLYLINETO:
629 {
630 PEMRPOLYLINETO pEmr = (PEMRPOLYLINETO) lpEMFR;
631 DWORD i;
633 for (i=0; i<pEmr->cptl;i++) {
634 tmp_path <<
635 "\n\tL " <<
636 pix_x_to_point( d, pEmr->aptl[i].x ) << " " <<
637 pix_y_to_point( d, pEmr->aptl[i].y ) << " ";
638 }
640 break;
641 }
642 case EMR_POLYPOLYLINE:
643 break;
644 case EMR_POLYPOLYGON:
645 break;
646 case EMR_SETWINDOWEXTEX:
647 {
648 PEMRSETWINDOWEXTEX pEmr = (PEMRSETWINDOWEXTEX) lpEMFR;
650 d->sizeWnd = pEmr->szlExtent;
651 d->PixelsX = d->sizeWnd.cx;
652 d->PixelsY = d->sizeWnd.cy;
654 d->ScaleX = d->xDPI / (100*d->MMX / d->PixelsX);
655 d->ScaleY = d->yDPI / (100*d->MMY / d->PixelsY);
657 break;
658 }
659 case EMR_SETWINDOWORGEX:
660 {
661 PEMRSETWINDOWORGEX pEmr = (PEMRSETWINDOWORGEX) lpEMFR;
662 d->winorg = pEmr->ptlOrigin;
663 break;
664 }
665 case EMR_SETVIEWPORTEXTEX:
666 {
667 PEMRSETVIEWPORTEXTEX pEmr = (PEMRSETVIEWPORTEXTEX) lpEMFR;
669 d->sizeView = pEmr->szlExtent;
671 if (d->sizeWnd.cx && d->sizeWnd.cy) {
672 HDC hScreenDC = GetDC( NULL );
674 float scrPixelsX = (float)GetDeviceCaps( hScreenDC, HORZRES );
675 float scrPixelsY = (float)GetDeviceCaps( hScreenDC, VERTRES );
676 float scrMMX = (float)GetDeviceCaps( hScreenDC, HORZSIZE );
677 float scrMMY = (float)GetDeviceCaps( hScreenDC, VERTSIZE );
679 ReleaseDC( NULL, hScreenDC );
681 d->dwInchesX = d->sizeView.cx / (25.4f*scrPixelsX/scrMMX);
682 d->dwInchesY = d->sizeView.cy / (25.4f*scrPixelsY/scrMMY);
683 d->xDPI = d->sizeWnd.cx / d->dwInchesX;
684 d->yDPI = d->sizeWnd.cy / d->dwInchesY;
686 if (1) {
687 d->xDPI = 2540;
688 d->yDPI = 2540;
689 d->dwInchesX = d->PixelsX / d->xDPI;
690 d->dwInchesY = d->PixelsY / d->yDPI;
691 d->ScaleX = d->xDPI;
692 d->ScaleY = d->yDPI;
693 }
695 d->MMX = d->dwInchesX * MM_PER_IN;
696 d->MMY = d->dwInchesY * MM_PER_IN;
697 }
699 break;
700 }
701 case EMR_SETVIEWPORTORGEX:
702 {
703 PEMRSETVIEWPORTORGEX pEmr = (PEMRSETVIEWPORTORGEX) lpEMFR;
704 d->vieworg = pEmr->ptlOrigin;
705 break;
706 }
707 case EMR_SETBRUSHORGEX:
708 break;
709 case EMR_EOF:
710 {
711 tmp_outsvg << "</svg>\n";
712 break;
713 }
714 case EMR_SETPIXELV:
715 break;
716 case EMR_SETMAPPERFLAGS:
717 break;
718 case EMR_SETMAPMODE:
719 break;
720 case EMR_SETBKMODE:
721 break;
722 case EMR_SETPOLYFILLMODE:
723 {
724 PEMRSETPOLYFILLMODE pEmr = (PEMRSETPOLYFILLMODE) lpEMFR;
725 d->style.fill_rule.value =
726 (pEmr->iMode == WINDING ? 0 :
727 pEmr->iMode == ALTERNATE ? 1 : 0);
728 break;
729 }
730 case EMR_SETROP2:
731 break;
732 case EMR_SETSTRETCHBLTMODE:
733 break;
734 case EMR_SETTEXTALIGN:
735 break;
736 case EMR_SETCOLORADJUSTMENT:
737 break;
738 case EMR_SETTEXTCOLOR:
739 break;
740 case EMR_SETBKCOLOR:
741 break;
742 case EMR_OFFSETCLIPRGN:
743 break;
744 case EMR_MOVETOEX:
745 {
746 PEMRMOVETOEX pEmr = (PEMRMOVETOEX) lpEMFR;
747 tmp_path <<
748 "\n\tM " <<
749 pix_x_to_point( d, pEmr->ptl.x ) << " " <<
750 pix_y_to_point( d, pEmr->ptl.y ) << " ";
751 break;
752 }
753 case EMR_SETMETARGN:
754 break;
755 case EMR_EXCLUDECLIPRECT:
756 break;
757 case EMR_INTERSECTCLIPRECT:
758 break;
759 case EMR_SCALEVIEWPORTEXTEX:
760 break;
761 case EMR_SCALEWINDOWEXTEX:
762 break;
763 case EMR_SAVEDC:
764 break;
765 case EMR_RESTOREDC:
766 break;
767 case EMR_SETWORLDTRANSFORM:
768 break;
769 case EMR_MODIFYWORLDTRANSFORM:
770 break;
771 case EMR_SELECTOBJECT:
772 {
773 PEMRSELECTOBJECT pEmr = (PEMRSELECTOBJECT) lpEMFR;
774 unsigned int index = pEmr->ihObject;
776 if (index >= ENHMETA_STOCK_OBJECT) {
777 index -= ENHMETA_STOCK_OBJECT;
778 switch (index) {
779 case NULL_BRUSH:
780 d->fill_set = false;
781 break;
782 case BLACK_BRUSH:
783 case DKGRAY_BRUSH:
784 case GRAY_BRUSH:
785 case LTGRAY_BRUSH:
786 case WHITE_BRUSH:
787 {
788 float val = 0;
789 switch (index) {
790 case BLACK_BRUSH:
791 val = 0.0 / 255.0;
792 break;
793 case DKGRAY_BRUSH:
794 val = 64.0 / 255.0;
795 break;
796 case GRAY_BRUSH:
797 val = 128.0 / 255.0;
798 break;
799 case LTGRAY_BRUSH:
800 val = 192.0 / 255.0;
801 break;
802 case WHITE_BRUSH:
803 val = 255.0 / 255.0;
804 break;
805 }
806 d->style.fill.value.color.set( val, val, val );
808 d->fill_set = true;
809 break;
810 }
811 case NULL_PEN:
812 d->stroke_set = false;
813 break;
814 case BLACK_PEN:
815 case WHITE_PEN:
816 {
817 float val = index == BLACK_PEN ? 0 : 1;
818 d->style.stroke_dasharray_set = 0;
819 d->style.stroke_width.value = 1.0;
820 d->style.stroke.value.color.set( val, val, val );
822 d->stroke_set = true;
824 break;
825 }
826 }
827 } else {
828 if (index >= 0 && index < (unsigned int)d->n_obj) {
829 switch (d->emf_obj[index].type)
830 {
831 case EMR_CREATEPEN:
832 select_pen(d, index);
833 break;
834 case EMR_CREATEBRUSHINDIRECT:
835 select_brush(d, index);
836 break;
837 case EMR_EXTCREATEPEN:
838 select_extpen(d, index);
839 break;
840 }
841 }
842 }
843 break;
844 }
845 case EMR_CREATEPEN:
846 {
847 PEMRCREATEPEN pEmr = (PEMRCREATEPEN) lpEMFR;
848 int index = pEmr->ihPen;
850 EMRCREATEPEN *pPen =
851 (EMRCREATEPEN *) malloc( sizeof(EMREXTCREATEPEN) );
852 pPen->lopn = pEmr->lopn;
853 insert_object(d, index, EMR_CREATEPEN, (ENHMETARECORD *) pPen);
855 break;
856 }
857 case EMR_CREATEBRUSHINDIRECT:
858 {
859 PEMRCREATEBRUSHINDIRECT pEmr = (PEMRCREATEBRUSHINDIRECT) lpEMFR;
860 int index = pEmr->ihBrush;
862 EMRCREATEBRUSHINDIRECT *pBrush =
863 (EMRCREATEBRUSHINDIRECT *) malloc( sizeof(EMRCREATEBRUSHINDIRECT) );
864 pBrush->lb = pEmr->lb;
865 insert_object(d, index, EMR_CREATEBRUSHINDIRECT, (ENHMETARECORD *) pBrush);
867 break;
868 }
869 case EMR_DELETEOBJECT:
870 break;
871 case EMR_ANGLEARC:
872 break;
873 case EMR_ELLIPSE:
874 break;
875 case EMR_RECTANGLE:
876 break;
877 case EMR_ROUNDRECT:
878 break;
879 case EMR_ARC:
880 break;
881 case EMR_CHORD:
882 break;
883 case EMR_PIE:
884 break;
885 case EMR_SELECTPALETTE:
886 break;
887 case EMR_CREATEPALETTE:
888 break;
889 case EMR_SETPALETTEENTRIES:
890 break;
891 case EMR_RESIZEPALETTE:
892 break;
893 case EMR_REALIZEPALETTE:
894 break;
895 case EMR_EXTFLOODFILL:
896 break;
897 case EMR_LINETO:
898 {
899 PEMRLINETO pEmr = (PEMRLINETO) lpEMFR;
900 tmp_path <<
901 "\n\tL " <<
902 pix_x_to_point( d, pEmr->ptl.x ) << " " <<
903 pix_y_to_point( d, pEmr->ptl.y ) << " ";
904 break;
905 }
906 case EMR_ARCTO:
907 break;
908 case EMR_POLYDRAW:
909 break;
910 case EMR_SETARCDIRECTION:
911 break;
912 case EMR_SETMITERLIMIT:
913 {
914 PEMRSETMITERLIMIT pEmr = (PEMRSETMITERLIMIT) lpEMFR;
915 d->style.stroke_miterlimit.value = pix_size_to_point( d, pEmr->eMiterLimit );
917 if (d->style.stroke_miterlimit.value < 1)
918 d->style.stroke_miterlimit.value = 1.0;
920 break;
921 }
922 case EMR_BEGINPATH:
923 {
924 tmp_path << " d=\"";
925 *(d->path) = "";
926 break;
927 }
928 case EMR_ENDPATH:
929 {
930 tmp_path << "\"";
931 break;
932 }
933 case EMR_CLOSEFIGURE:
934 {
935 tmp_path << "\n\tz";
936 break;
937 }
938 case EMR_FILLPATH:
939 case EMR_STROKEANDFILLPATH:
940 case EMR_STROKEPATH:
941 {
942 *(d->outsvg) += " <path ";
943 output_style(d, lpEMFR->iType);
944 *(d->outsvg) += "\n\t";
945 *(d->outsvg) += *(d->path);
946 *(d->outsvg) += " /> \n";
947 break;
948 }
949 case EMR_FLATTENPATH:
950 break;
951 case EMR_WIDENPATH:
952 break;
953 case EMR_SELECTCLIPPATH:
954 break;
955 case EMR_ABORTPATH:
956 break;
957 case EMR_GDICOMMENT:
958 break;
959 case EMR_FILLRGN:
960 break;
961 case EMR_FRAMERGN:
962 break;
963 case EMR_INVERTRGN:
964 break;
965 case EMR_PAINTRGN:
966 break;
967 case EMR_EXTSELECTCLIPRGN:
968 break;
969 case EMR_BITBLT:
970 break;
971 case EMR_STRETCHBLT:
972 break;
973 case EMR_MASKBLT:
974 break;
975 case EMR_PLGBLT:
976 break;
977 case EMR_SETDIBITSTODEVICE:
978 break;
979 case EMR_STRETCHDIBITS:
980 break;
981 case EMR_EXTCREATEFONTINDIRECTW:
982 break;
983 case EMR_EXTTEXTOUTA:
984 break;
985 case EMR_EXTTEXTOUTW:
986 break;
987 case EMR_POLYBEZIER16:
988 {
989 PEMRPOLYBEZIER16 pEmr = (PEMRPOLYBEZIER16) lpEMFR;
990 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
991 DWORD i,j;
993 if (pEmr->cpts<4)
994 break;
996 *(d->outsvg) += " <path ";
997 output_style(d, EMR_STROKEPATH);
998 *(d->outsvg) += "\n\td=\"";
1000 tmp_str <<
1001 "\n\tM " <<
1002 pix_x_to_point( d, apts[0].x ) << " " <<
1003 pix_y_to_point( d, apts[0].y ) << " ";
1005 for (i=1; i<pEmr->cpts; ) {
1006 tmp_str << "\n\tC ";
1007 for (j=0; j<3 && i<pEmr->cpts; j++,i++) {
1008 tmp_str <<
1009 pix_x_to_point( d, apts[i].x ) << " " <<
1010 pix_y_to_point( d, apts[i].y ) << " ";
1011 }
1012 }
1014 *(d->outsvg) += tmp_str.str().c_str();
1015 *(d->outsvg) += " \" /> \n";
1017 break;
1018 }
1019 case EMR_POLYGON16:
1020 {
1021 PEMRPOLYGON16 pEmr = (PEMRPOLYGON16) lpEMFR;
1022 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
1023 unsigned int i;
1025 *(d->outsvg) += "<path ";
1026 output_style(d, EMR_STROKEANDFILLPATH);
1027 *(d->outsvg) += "\n\td=\"";
1029 // skip the first point?
1030 tmp_path << "\n\tM " <<
1031 pix_x_to_point( d, apts[1].x ) << " " <<
1032 pix_y_to_point( d, apts[1].y ) << " ";
1034 for (i=2; i<pEmr->cpts; i++) {
1035 tmp_path << "\n\tL " <<
1036 pix_x_to_point( d, apts[i].x ) << " " <<
1037 pix_y_to_point( d, apts[i].y ) << " ";
1038 }
1040 *(d->outsvg) += tmp_path.str().c_str();
1041 *(d->outsvg) += " z \" /> \n";
1043 break;
1044 }
1045 case EMR_POLYLINE16:
1046 {
1047 EMRPOLYLINE16 *pEmr = (EMRPOLYLINE16 *) lpEMFR;
1048 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
1049 DWORD i;
1051 if (pEmr->cpts<2)
1052 break;
1054 *(d->outsvg) += " <path ";
1055 output_style(d, EMR_STROKEPATH);
1056 *(d->outsvg) += "\n\td=\"";
1058 tmp_str <<
1059 "\n\tM " <<
1060 pix_x_to_point( d, apts[0].x ) << " " <<
1061 pix_y_to_point( d, apts[0].y ) << " ";
1063 for (i=1; i<pEmr->cpts; i++) {
1064 tmp_str <<
1065 "\n\tL " <<
1066 pix_x_to_point( d, apts[i].x ) << " " <<
1067 pix_y_to_point( d, apts[i].y ) << " ";
1068 }
1070 *(d->outsvg) += tmp_str.str().c_str();
1071 *(d->outsvg) += " \" /> \n";
1073 break;
1074 }
1075 case EMR_POLYBEZIERTO16:
1076 {
1077 PEMRPOLYBEZIERTO16 pEmr = (PEMRPOLYBEZIERTO16) lpEMFR;
1078 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
1079 DWORD i,j;
1081 for (i=0; i<pEmr->cpts;) {
1082 tmp_path << "\n\tC ";
1083 for (j=0; j<3 && i<pEmr->cpts; j++,i++) {
1084 tmp_path <<
1085 pix_x_to_point( d, apts[i].x ) << " " <<
1086 pix_y_to_point( d, apts[i].y ) << " ";
1087 }
1088 }
1090 break;
1091 }
1092 case EMR_POLYLINETO16:
1093 {
1094 PEMRPOLYLINETO16 pEmr = (PEMRPOLYLINETO16) lpEMFR;
1095 POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ?
1096 DWORD i;
1098 for (i=0; i<pEmr->cpts;i++) {
1099 tmp_path <<
1100 "\n\tL " <<
1101 pix_x_to_point( d, apts[i].x ) << " " <<
1102 pix_y_to_point( d, apts[i].y ) << " ";
1103 }
1105 break;
1106 }
1107 case EMR_POLYPOLYLINE16:
1108 break;
1109 case EMR_POLYPOLYGON16:
1110 {
1111 PEMRPOLYPOLYGON16 pEmr = (PEMRPOLYPOLYGON16) lpEMFR;
1112 unsigned int n, i, j;
1114 *(d->outsvg) += "<path ";
1115 output_style(d, EMR_STROKEANDFILLPATH);
1116 *(d->outsvg) += "\n\td=\"";
1118 i = pEmr->nPolys-1; // ???
1119 for (n=0; n<pEmr->nPolys /*&& i<pEmr->cpts*/; n++) {
1120 SVGOStringStream poly_path;
1122 poly_path << "\n\tM " <<
1123 pix_x_to_point( d, pEmr->apts[i].x ) << " " <<
1124 pix_y_to_point( d, pEmr->apts[i].y ) << " ";
1125 i++;
1127 for (j=1; j<pEmr->aPolyCounts[n] /*&& i<pEmr->cpts*/; j++) {
1128 poly_path << "\n\tL " <<
1129 pix_x_to_point( d, pEmr->apts[i].x ) << " " <<
1130 pix_y_to_point( d, pEmr->apts[i].y ) << " ";
1131 i++;
1132 }
1134 *(d->outsvg) += poly_path.str().c_str();
1135 *(d->outsvg) += " z \n";
1136 }
1138 *(d->outsvg) += " \" /> \n";
1139 break;
1140 }
1141 case EMR_POLYDRAW16:
1142 break;
1143 case EMR_CREATEMONOBRUSH:
1144 break;
1145 case EMR_CREATEDIBPATTERNBRUSHPT:
1146 break;
1147 case EMR_EXTCREATEPEN:
1148 {
1149 PEMREXTCREATEPEN pEmr = (PEMREXTCREATEPEN) lpEMFR;
1150 int index = pEmr->ihPen;
1152 EMREXTCREATEPEN *pPen =
1153 (EMREXTCREATEPEN *) malloc( sizeof(EMREXTCREATEPEN) +
1154 sizeof(DWORD) * pEmr->elp.elpNumEntries );
1155 pPen->ihPen = pEmr->ihPen;
1156 pPen->offBmi = pEmr->offBmi;
1157 pPen->cbBmi = pEmr->cbBmi;
1158 pPen->offBits = pEmr->offBits;
1159 pPen->cbBits = pEmr->cbBits;
1160 pPen->elp = pEmr->elp;
1161 for (unsigned int i=0; i<pEmr->elp.elpNumEntries; i++) {
1162 pPen->elp.elpStyleEntry[i] = pEmr->elp.elpStyleEntry[i];
1163 }
1164 insert_object(d, index, EMR_EXTCREATEPEN, (ENHMETARECORD *) pPen);
1166 break;
1167 }
1168 case EMR_POLYTEXTOUTA:
1169 break;
1170 case EMR_POLYTEXTOUTW:
1171 break;
1172 case EMR_SETICMMODE:
1173 break;
1174 case EMR_CREATECOLORSPACE:
1175 break;
1176 case EMR_SETCOLORSPACE:
1177 break;
1178 case EMR_DELETECOLORSPACE:
1179 break;
1180 case EMR_GLSRECORD:
1181 break;
1182 case EMR_GLSBOUNDEDRECORD:
1183 break;
1184 case EMR_PIXELFORMAT:
1185 break;
1186 }
1188 *(d->outsvg) += tmp_outsvg.str().c_str();
1189 *(d->path) += tmp_path.str().c_str();
1191 return 1;
1192 }
1195 // Aldus Placeable Header ===================================================
1196 // Since we are a 32bit app, we have to be sure this structure compiles to
1197 // be identical to a 16 bit app's version. To do this, we use the #pragma
1198 // to adjust packing, we use a WORD for the hmf handle, and a SMALL_RECT
1199 // for the bbox rectangle.
1200 #pragma pack( push )
1201 #pragma pack( 2 )
1202 typedef struct
1203 {
1204 DWORD dwKey;
1205 WORD hmf;
1206 SMALL_RECT bbox;
1207 WORD wInch;
1208 DWORD dwReserved;
1209 WORD wCheckSum;
1210 } APMHEADER, *PAPMHEADER;
1211 #pragma pack( pop )
1214 SPDocument *
1215 EmfWin32::open( Inkscape::Extension::Input *mod, const gchar *uri )
1216 {
1217 EMF_CALLBACK_DATA d = {0};
1219 gsize bytesRead = 0;
1220 gsize bytesWritten = 0;
1221 GError* error = NULL;
1222 gchar *local_fn =
1223 g_filename_from_utf8( uri, -1, &bytesRead, &bytesWritten, &error );
1225 if (local_fn == NULL) {
1226 return NULL;
1227 }
1229 d.outsvg = new Glib::ustring("");
1230 d.path = new Glib::ustring("");
1232 CHAR *ansi_uri = (CHAR *) local_fn;
1233 gunichar2 *unicode_fn = g_utf8_to_utf16( local_fn, -1, NULL, NULL, NULL );
1234 WCHAR *unicode_uri = (WCHAR *) unicode_fn;
1236 // Try open as Enhanced Metafile
1237 HENHMETAFILE hemf;
1238 if (PrintWin32::is_os_wide())
1239 hemf = GetEnhMetaFileW(unicode_uri);
1240 else
1241 hemf = GetEnhMetaFileA(ansi_uri);
1243 if (!hemf) {
1244 // Try open as Windows Metafile
1245 HMETAFILE hmf;
1246 if (PrintWin32::is_os_wide())
1247 hmf = GetMetaFileW(unicode_uri);
1248 else
1249 hmf = GetMetaFileA(ansi_uri);
1251 METAFILEPICT mp;
1252 HDC hDC;
1254 if (!hmf) {
1255 if (PrintWin32::is_os_wide()) {
1256 WCHAR szTemp[MAX_PATH];
1258 DWORD dw = GetShortPathNameW( unicode_uri, szTemp, MAX_PATH );
1259 if (dw) {
1260 hmf = GetMetaFileW( szTemp );
1261 }
1262 } else {
1263 CHAR szTemp[MAX_PATH];
1265 DWORD dw = GetShortPathNameA( ansi_uri, szTemp, MAX_PATH );
1266 if (dw) {
1267 hmf = GetMetaFileA( szTemp );
1268 }
1269 }
1270 }
1272 if (hmf) {
1273 DWORD nSize = GetMetaFileBitsEx( hmf, 0, NULL );
1274 if (nSize) {
1275 BYTE *lpvData = new BYTE[nSize];
1276 if (lpvData) {
1277 DWORD dw = GetMetaFileBitsEx( hmf, nSize, lpvData );
1278 if (dw) {
1279 // Fill out a METAFILEPICT structure
1280 mp.mm = MM_ANISOTROPIC;
1281 mp.xExt = 1000;
1282 mp.yExt = 1000;
1283 mp.hMF = NULL;
1284 // Get a reference DC
1285 hDC = GetDC( NULL );
1286 // Make an enhanced metafile from the windows metafile
1287 hemf = SetWinMetaFileBits( nSize, lpvData, hDC, &mp );
1288 // Clean up
1289 ReleaseDC( NULL, hDC );
1290 }
1291 delete[] lpvData;
1292 }
1293 DeleteMetaFile( hmf );
1294 }
1295 } else {
1296 // Try open as Aldus Placeable Metafile
1297 HANDLE hFile;
1298 if (PrintWin32::is_os_wide())
1299 hFile = CreateFileW( unicode_uri, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
1300 else
1301 hFile = CreateFileA( ansi_uri, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
1302 if (hFile != INVALID_HANDLE_VALUE) {
1303 DWORD nSize = GetFileSize( hFile, NULL );
1304 if (nSize) {
1305 BYTE *lpvData = new BYTE[nSize];
1306 if (lpvData) {
1307 DWORD dw = ReadFile( hFile, lpvData, nSize, &nSize, NULL );
1308 if (dw) {
1309 if ( ((PAPMHEADER)lpvData)->dwKey == 0x9ac6cdd7l ) {
1310 // Fill out a METAFILEPICT structure
1311 mp.mm = MM_ANISOTROPIC;
1312 mp.xExt = ((PAPMHEADER)lpvData)->bbox.Right - ((PAPMHEADER)lpvData)->bbox.Left;
1313 mp.xExt = ( mp.xExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch);
1314 mp.yExt = ((PAPMHEADER)lpvData)->bbox.Bottom - ((PAPMHEADER)lpvData)->bbox.Top;
1315 mp.yExt = ( mp.yExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch);
1316 mp.hMF = NULL;
1317 // Get a reference DC
1318 hDC = GetDC( NULL );
1319 // Create an enhanced metafile from the bits
1320 hemf = SetWinMetaFileBits( nSize, lpvData+sizeof(APMHEADER), hDC, &mp );
1321 // Clean up
1322 ReleaseDC( NULL, hDC );
1323 }
1324 }
1325 delete[] lpvData;
1326 }
1327 }
1328 CloseHandle( hFile );
1329 }
1330 }
1331 }
1333 if (!hemf || !d.outsvg || !d.path) {
1334 if (d.outsvg)
1335 delete d.outsvg;
1336 if (d.path)
1337 delete d.path;
1338 if (local_fn)
1339 g_free(local_fn);
1340 if (unicode_fn)
1341 g_free(unicode_fn);
1342 return NULL;
1343 }
1345 EnumEnhMetaFile(NULL, hemf, myEnhMetaFileProc, (LPVOID) &d, NULL);
1346 DeleteEnhMetaFile(hemf);
1348 // std::cout << "SVG Output: " << std::endl << *(d.outsvg) << std::endl;
1350 SPDocument *doc = sp_document_new_from_mem(d.outsvg->c_str(), d.outsvg->length(), TRUE);
1352 delete d.outsvg;
1353 delete d.path;
1355 if (d.emf_obj) {
1356 int i;
1357 for (i=0; i<d.n_obj; i++)
1358 delete_object(&d, i);
1359 delete[] d.emf_obj;
1360 }
1362 if (d.style.stroke_dash.dash)
1363 delete[] d.style.stroke_dash.dash;
1365 if (local_fn)
1366 g_free(local_fn);
1367 if (unicode_fn)
1368 g_free(unicode_fn);
1370 return doc;
1371 }
1374 void
1375 EmfWin32::init (void)
1376 {
1377 Inkscape::Extension::Extension * ext;
1379 /* EMF in */
1380 ext = Inkscape::Extension::build_from_mem(
1381 "<inkscape-extension>\n"
1382 "<name>" N_("EMF Input") "</name>\n"
1383 "<id>org.inkscape.input.emf.win32</id>\n"
1384 "<input>\n"
1385 "<extension>.emf</extension>\n"
1386 "<mimetype>image/x-emf</mimetype>\n"
1387 "<filetypename>" N_("Enhanced Metafiles (*.emf)") "</filetypename>\n"
1388 "<filetypetooltip>" N_("Enhanced Metafiles") "</filetypetooltip>\n"
1389 "<output_extension>org.inkscape.output.emf.win32</output_extension>\n"
1390 "</input>\n"
1391 "</inkscape-extension>", new EmfWin32());
1393 /* WMF in */
1394 ext = Inkscape::Extension::build_from_mem(
1395 "<inkscape-extension>\n"
1396 "<name>" N_("WMF Input") "</name>\n"
1397 "<id>org.inkscape.input.wmf.win32</id>\n"
1398 "<input>\n"
1399 "<extension>.wmf</extension>\n"
1400 "<mimetype>image/x-wmf</mimetype>\n"
1401 "<filetypename>" N_("Windows Metafiles (*.wmf)") "</filetypename>\n"
1402 "<filetypetooltip>" N_("Windows Metafiles") "</filetypetooltip>\n"
1403 "<output_extension>org.inkscape.output.emf.win32</output_extension>\n"
1404 "</input>\n"
1405 "</inkscape-extension>", new EmfWin32());
1407 /* EMF out */
1408 ext = Inkscape::Extension::build_from_mem(
1409 "<inkscape-extension>\n"
1410 "<name>" N_("EMF Output") "</name>\n"
1411 "<id>org.inkscape.output.emf.win32</id>\n"
1412 "<output>\n"
1413 "<extension>.emf</extension>\n"
1414 "<mimetype>image/x-emf</mimetype>\n"
1415 "<filetypename>" N_("Enhanced Metafile (*.emf)") "</filetypename>\n"
1416 "<filetypetooltip>" N_("Enhanced Metafile") "</filetypetooltip>\n"
1417 "</output>\n"
1418 "</inkscape-extension>", new EmfWin32());
1420 return;
1421 }
1424 } } } /* namespace Inkscape, Extension, Implementation */
1427 #endif /* WIN32 */
1430 /*
1431 Local Variables:
1432 mode:cpp
1433 c-file-style:"stroustrup"
1434 c-file-offsets:((innamespace . 0)(inline-open . 0))
1435 indent-tabs-mode:nil
1436 fill-column:99
1437 End:
1438 */
1439 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :