Code

b9d65f50cd80409f11549dc44f2dac777b70dd7b
[inkscape.git] / src / extension / internal / emf-win32-inout.cpp
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)
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;
149 void
150 EmfWin32::save (Inkscape::Extension::Output *mod, SPDocument *doc, const gchar *uri)
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;
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)
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();
277 static double
278 pix_x_to_point(PEMF_CALLBACK_DATA d, double px)
280     double tmp = px - d->winorg.x;
281     tmp *= (double) PX_PER_IN / d->ScaleX;
282     tmp += d->vieworg.x;
283     return tmp;
287 static double
288 pix_y_to_point(PEMF_CALLBACK_DATA d, double px)
290     double tmp = px - d->winorg.y;
291     tmp *= (double) PX_PER_IN / d->ScaleY;
292     tmp += d->vieworg.y;
293     return tmp;
297 static double
298 pix_size_to_point(PEMF_CALLBACK_DATA d, double px)
300     double tmp = px;
301     tmp *= (double) PX_PER_IN / d->ScaleX;
302     return tmp;
306 static void
307 select_pen(PEMF_CALLBACK_DATA d, int index)
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;
343 static void
344 select_extpen(PEMF_CALLBACK_DATA d, int index)
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;
429 static void
430 select_brush(PEMF_CALLBACK_DATA d, int index)
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;
452 static void
453 delete_object(PEMF_CALLBACK_DATA d, int index)
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     }
464 static void
465 insert_object(PEMF_CALLBACK_DATA d, int index, int type, ENHMETARECORD *pObj)
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     }
475 static int CALLBACK
476 myEnhMetaFileProc(HDC hDC, HANDLETABLE *lpHTable, ENHMETARECORD *lpEMFR, int nObj, LPARAM lpData)
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 < 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;
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
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 )
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     }
1352     
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;
1365 void
1366 EmfWin32::init (void)
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;
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 :