]> granicus.if.org Git - imagemagick/blob - coders/wmf.c
4f748810c63ba6879f83f2d789d93e1ca3fa0945
[imagemagick] / coders / wmf.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                             W   W   M   M  FFFFF                            %
7 %                             W   W   MM MM  F                                %
8 %                             W W W   M M M  FFF                              %
9 %                             WW WW   M   M  F                                %
10 %                             W   W   M   M  F                                %
11 %                                                                             %
12 %                                                                             %
13 %                        Read Windows Metafile Format                         %
14 %                                                                             %
15 %                              Software Design                                %
16 %                              Bob Friesenhahn                                %
17 %                            Dec 2000 - May 2001                              %
18 %                            Oct 2001 - May 2002                              %
19 %                                                                             %
20 %                           Port to libwmf 0.2 API                            %
21 %                            Francis J. Franklin                              %
22 %                            May 2001 - Oct 2001                              %
23 %                                                                             %
24 %                                                                             %
25 %  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
26 %  dedicated to making software imaging solutions freely available.           %
27 %                                                                             %
28 %  You may not use this file except in compliance with the License.  You may  %
29 %  obtain a copy of the License at                                            %
30 %                                                                             %
31 %    http://www.imagemagick.org/script/license.php                            %
32 %                                                                             %
33 %  Unless required by applicable law or agreed to in writing, software        %
34 %  distributed under the License is distributed on an "AS IS" BASIS,          %
35 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
36 %  See the License for the specific language governing permissions and        %
37 %  limitations under the License.                                             %
38 %                                                                             %
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 */
41 \f
42 /*
43   Include declarations.
44 */
45 #include "magick/studio.h"
46 #include "magick/property.h"
47 #include "magick/blob.h"
48 #include "magick/blob-private.h"
49 #include "magick/color.h"
50 #include "magick/color-private.h"
51 #include "magick/constitute.h"
52 #include "magick/exception.h"
53 #include "magick/exception-private.h"
54 #include "magick/image.h"
55 #include "magick/image-private.h"
56 #include "magick/list.h"
57 #include "magick/log.h"
58 #include "magick/magick.h"
59 #include "magick/memory_.h"
60 #include "magick/monitor.h"
61 #include "magick/monitor-private.h"
62 #include "magick/paint.h"
63 #include "magick/quantum-private.h"
64 #include "magick/static.h"
65 #include "magick/string_.h"
66 #include "magick/module.h"
67 #include "magick/type.h"
68 #include "magick/module.h"
69 #include "wand/MagickWand.h"
70
71 #if defined(MAGICKCORE_WMF_DELEGATE) || defined(MAGICKCORE_WMFLITE_DELEGATE)
72
73 #define ERR(API)  ((API)->err != wmf_E_None)
74 #define XC(x) ((double) x)
75 #define YC(y) ((double) y)
76
77 #if !defined(M_PI)
78 #  define M_PI  MagickPI
79 #endif
80
81 #if defined(MAGICKCORE_HAVE_FT2BUILD_H)
82 #  include <ft2build.h>
83 #endif
84
85 #include "libwmf/fund.h"
86 #include "libwmf/types.h"
87 #include "libwmf/api.h"
88 #undef SRCCOPY
89 #undef SRCPAINT
90 #undef SRCAND
91 #undef SRCINVERT
92 #undef SRCERASE
93 #undef NOTSRCCOPY
94 #undef NOTSRCERASE
95 #undef MERGECOPY
96 #undef MERGEPAINT
97 #undef PATCOPY
98 #undef PATPAINT
99 #undef PATINVERT
100 #undef DSTINVERT
101 #undef BLACKNESS
102 #undef WHITENESS
103
104 /* The following additinal undefs were required for MinGW */
105 #undef BS_HOLLOW
106 #undef PS_STYLE_MASK
107 #undef PS_ENDCAP_ROUND
108 #undef PS_ENDCAP_SQUARE
109 #undef PS_ENDCAP_FLAT
110 #undef PS_ENDCAP_MASK
111 #undef PS_JOIN_ROUND
112 #undef PS_JOIN_BEVEL
113 #undef PS_JOIN_MITER
114 #undef PS_COSMETIC
115 #undef PS_GEOMETRIC
116 #undef PS_TYPE_MASK
117 #undef STRETCH_ANDSCANS
118 #undef STRETCH_ORSCANS
119 #undef STRETCH_DELETESCANS
120 #undef STRETCH_HALFTONE
121 #undef ETO_OPAQUE
122 #undef ETO_CLIPPED
123 #undef ETO_GLYPH_INDEX
124 #undef ETO_RTLREADING
125
126 #include "libwmf/defs.h"
127 #include "libwmf/ipa.h"
128 #include "libwmf/color.h"
129 #include "libwmf/macro.h"
130
131 /* Unit conversions */
132 #define TWIPS_PER_INCH        1440
133 #define CENTIMETERS_PER_INCH  2.54
134 #define POINTS_PER_INCH       72
135
136 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
137 # define wmf_api_create(api,flags,options) wmf_lite_create(api,flags,options)
138 # define wmf_api_destroy(api) wmf_lite_destroy(api)
139 # undef WMF_FONT_PSNAME
140 # define WMF_FONT_PSNAME(F) ((F)->user_data ? ((wmf_magick_font_t*) (F)->user_data)->ps_name : 0)
141
142 typedef struct _wmf_magick_font_t wmf_magick_font_t;
143
144 struct _wmf_magick_font_t
145 {
146   char*  ps_name;
147   double pointsize;
148 };
149
150 #endif
151
152 typedef struct _wmf_magick_t wmf_magick_t;
153
154 struct _wmf_magick_t
155 {
156   /* Bounding box */
157   wmfD_Rect
158     bbox;
159
160   /* Scale and translation factors */
161   double
162     scale_x,
163     scale_y,
164     translate_x,
165     translate_y,
166     rotate;
167
168   /* Vector output */
169   DrawingWand
170     *draw_wand;
171
172   /* ImageMagick image */
173   Image
174     *image;
175
176   /* ImageInfo */
177   const ImageInfo
178     *image_info;
179
180   /* DrawInfo */
181   DrawInfo
182     *draw_info;
183
184   /* Pattern ID */
185   unsigned long
186     pattern_id;
187
188   /* Clip path flag */
189   MagickBooleanType
190     clipping;
191
192   /* Clip path ID */
193   unsigned long
194     clip_mask_id;
195
196   /* Push depth */
197   long
198     push_depth;
199 };
200
201
202 #define WMF_MAGICK_GetData(Z) ((wmf_magick_t*)((Z)->device_data))
203 #define WMF_MAGICK_GetFontData(Z) \
204   ((wmf_magick_font_t*)((wmfFontData *)Z->font_data)->user_data)
205
206 #define WmfDrawingWand (((wmf_magick_t*)((API)->device_data))->draw_wand)
207
208 /* Enum to control whether util_set_brush applies brush to fill or
209    stroke. */
210 typedef enum
211 {
212   BrushApplyFill,
213   BrushApplyStroke
214 } BrushApply;
215
216
217 /* Enum to specify arc type */
218 typedef enum
219 {
220   magick_arc_ellipse = 0,
221   magick_arc_open,
222   magick_arc_pie,
223   magick_arc_chord
224 }
225 magick_arc_t;
226
227 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
228 static void  lite_font_init (wmfAPI* API, wmfAPI_Options* options);
229 static void  lite_font_map(wmfAPI* API,wmfFont* font);
230 static float lite_font_stringwidth(wmfAPI* API, wmfFont* font, char* str);
231 #endif
232
233 static void         draw_fill_color_rgb(wmfAPI* API, const wmfRGB* rgb);
234 static void         draw_stroke_color_rgb(wmfAPI* API, const wmfRGB* rgb);
235 static void         draw_pattern_push(wmfAPI* API, unsigned long id, unsigned long columns, unsigned long rows);
236 static int          ipa_blob_read(void* wand);
237 static int          ipa_blob_seek(void* wand,long position);
238 static long         ipa_blob_tell(void* wand);
239 static void         ipa_bmp_draw(wmfAPI * API, wmfBMP_Draw_t * bmp_draw);
240 static void         ipa_bmp_free(wmfAPI * API, wmfBMP * bmp);
241 static void         ipa_bmp_read(wmfAPI * API, wmfBMP_Read_t * bmp_read);
242 static void         ipa_device_begin(wmfAPI * API);
243 static void         ipa_device_close(wmfAPI * API);
244 static void         ipa_device_end(wmfAPI * API);
245 static void         ipa_device_open(wmfAPI * API);
246 static void         ipa_draw_arc(wmfAPI * API, wmfDrawArc_t * draw_arc);
247 static void         ipa_draw_chord(wmfAPI * API, wmfDrawArc_t * draw_arc);
248 static void         ipa_draw_ellipse(wmfAPI * API, wmfDrawArc_t * draw_arc);
249 static void         ipa_draw_line(wmfAPI * API, wmfDrawLine_t * draw_line);
250 static void         ipa_draw_pie(wmfAPI * API, wmfDrawArc_t * draw_arc);
251 static void         ipa_draw_pixel(wmfAPI * API, wmfDrawPixel_t * draw_pixel);
252 static void         ipa_draw_polygon(wmfAPI * API, wmfPolyLine_t * poly_line);
253 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
254 static void         ipa_draw_polypolygon(wmfAPI * API, wmfPolyPoly_t* polypolygon);
255 #endif
256 static void         ipa_draw_rectangle(wmfAPI * API, wmfDrawRectangle_t * draw_rect);
257 static void         ipa_draw_text(wmfAPI * API, wmfDrawText_t * draw_text);
258 static void         ipa_flood_exterior(wmfAPI * API, wmfFlood_t * flood);
259 static void         ipa_flood_interior(wmfAPI * API, wmfFlood_t * flood);
260 static void         ipa_functions(wmfAPI * API);
261 static void         ipa_poly_line(wmfAPI * API, wmfPolyLine_t * poly_line);
262 static void         ipa_region_clip(wmfAPI * API, wmfPolyRectangle_t * poly_rect);
263 static void         ipa_region_frame(wmfAPI * API, wmfPolyRectangle_t * poly_rect);
264 static void         ipa_region_paint(wmfAPI * API, wmfPolyRectangle_t * poly_rect);
265 static void         ipa_rop_draw(wmfAPI * API, wmfROP_Draw_t * rop_draw);
266 static void         ipa_udata_copy(wmfAPI * API, wmfUserData_t * userdata);
267 static void         ipa_udata_free(wmfAPI * API, wmfUserData_t * userdata);
268 static void         ipa_udata_init(wmfAPI * API, wmfUserData_t * userdata);
269 static void         ipa_udata_set(wmfAPI * API, wmfUserData_t * userdata);
270 static int          magick_progress_callback(void* wand,float quantum);
271 static void         util_draw_arc(wmfAPI * API, wmfDrawArc_t * draw_arc,magick_arc_t finish);
272 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
273 /*static int          util_font_weight( const char* font );*/
274 #endif
275 static double       util_pointsize( wmfAPI* API, wmfFont* font, char* str, double font_height);
276 static void         util_set_brush(wmfAPI * API, wmfDC * dc, const BrushApply brush_apply);
277 static void         util_set_pen(wmfAPI * API, wmfDC * dc);
278
279 /* Progress callback */
280 int magick_progress_callback(void *context,float quantum)
281 {
282   Image
283     *image;
284
285   MagickBooleanType
286     status;
287
288   MagickOffsetType
289     offset;
290
291   MagickSizeType
292     span;
293
294   image=(Image *) context;
295   assert(image->signature == MagickSignature);
296   offset=(MagickOffsetType) floor((double) (100.0*quantum));
297   span=100;
298   status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
299     GetBlobSize(image));
300   return(status == MagickTrue ? 0 : 1);
301 }
302
303 /* Set fill color */
304 static void draw_fill_color_string(DrawingWand *drawing_wand,const char *color)
305 {
306   PixelWand
307     *fill_color;
308
309   fill_color=NewPixelWand();
310   PixelSetColor(fill_color,color);
311   DrawSetFillColor(drawing_wand,fill_color);
312   fill_color=DestroyPixelWand(fill_color);
313 }
314 static void draw_fill_color_rgb( wmfAPI* API, const wmfRGB* rgb )
315 {
316   PixelWand
317     *fill_color;
318
319   fill_color=NewPixelWand();
320   PixelSetRedQuantum(fill_color,ScaleCharToQuantum(rgb->r));
321   PixelSetGreenQuantum(fill_color,ScaleCharToQuantum(rgb->g));
322   PixelSetBlueQuantum(fill_color,ScaleCharToQuantum(rgb->b));
323   PixelSetOpacityQuantum(fill_color,OpaqueOpacity);
324   DrawSetFillColor(WmfDrawingWand,fill_color);
325   fill_color=DestroyPixelWand(fill_color);
326 }
327
328 /* Set stroke color */
329 static void draw_stroke_color_string(DrawingWand *drawing_wand,const char *color)
330 {
331   PixelWand
332     *stroke_color;
333
334   stroke_color=NewPixelWand();
335   PixelSetColor(stroke_color,color);
336   DrawSetStrokeColor(drawing_wand,stroke_color);
337   stroke_color=DestroyPixelWand(stroke_color);
338 }
339
340 static void draw_stroke_color_rgb( wmfAPI* API, const wmfRGB* rgb )
341 {
342   PixelWand
343     *stroke_color;
344
345   stroke_color=NewPixelWand();
346   PixelSetRedQuantum(stroke_color,ScaleCharToQuantum(rgb->r));
347   PixelSetGreenQuantum(stroke_color,ScaleCharToQuantum(rgb->g));
348   PixelSetBlueQuantum(stroke_color,ScaleCharToQuantum(rgb->b));
349   PixelSetOpacityQuantum(stroke_color,OpaqueOpacity);
350   DrawSetStrokeColor(WmfDrawingWand,stroke_color);
351   stroke_color=DestroyPixelWand(stroke_color);
352 }
353
354 /* Set under color */
355 static void draw_under_color_string(DrawingWand *drawing_wand,const char *color)
356 {
357   PixelWand
358     *under_color;
359
360   under_color=NewPixelWand();
361   PixelSetColor(under_color,color);
362   DrawSetTextUnderColor(drawing_wand,under_color);
363   under_color=DestroyPixelWand(under_color);
364 }
365
366 static void draw_pattern_push( wmfAPI* API,
367                                unsigned long id,
368                                unsigned long columns,
369                                unsigned long rows )
370 {
371   char
372     pattern_id[30];
373
374   (void) FormatMagickString(pattern_id,MaxTextExtent,"brush_%lu",id);
375   (void) DrawPushPattern(WmfDrawingWand,pattern_id,0,0,columns,rows);
376 }
377
378 /* Pattern/Bit BLT with raster operation (ROP) support.  Invoked by
379    META_PATBLT, which is equivalent to Windows PatBlt() call, or by
380    META_DIBBITBLT which is equivalent to Windows BitBlt() call. */
381
382 /* The BitBlt function transfers pixels from a rectangular area in one
383    device wand called the 'source', to a rectangular area of the
384    same size in another device wand, called the 'destination'. */
385
386 static void ipa_rop_draw(wmfAPI * API, wmfROP_Draw_t * rop_draw)
387 {
388 /*   wmfBrush */
389 /*     *brush = WMF_DC_BRUSH(rop_draw->dc); */
390
391 /*   wmfBMP */
392 /*     *brush_bmp = WMF_BRUSH_BITMAP(brush); */
393
394   if (TO_FILL(rop_draw) == 0)
395     return;
396
397   /* Save graphic wand */
398   (void) PushDrawingWand(WmfDrawingWand);
399
400   /* FIXME: finish implementing (once we know what it is supposed to do!) */
401
402   /*
403   struct _wmfROP_Draw_t
404   {       wmfDC* dc;
405
406     wmfD_Coord TL;
407     wmfD_Coord BR;
408
409     U32 ROP;
410
411     double pixel_width;
412     double pixel_height;
413   };
414   */
415
416 /*   if (brush_bmp && brush_bmp->data != 0) */
417 /*     printf("Have an image!\n"); */
418
419   switch (rop_draw->ROP) /* Ternary raster operations */
420     {
421     case SRCCOPY: /* dest = source */
422       printf("ipa_rop_draw SRCCOPY ROP mode not implemented\n");
423       break;
424     case SRCPAINT: /* dest = source OR dest */
425       printf("ipa_rop_draw SRCPAINT ROP mode not implemented\n");
426       break;
427     case SRCAND: /* dest = source AND dest */
428       printf("ipa_rop_draw SRCAND ROP mode not implemented\n");
429       break;
430     case SRCINVERT: /* dest = source XOR dest */
431       printf("ipa_rop_draw SRCINVERT ROP mode not implemented\n");
432       break;
433     case SRCERASE: /* dest = source AND (NOT dest) */
434       printf("ipa_rop_draw SRCERASE ROP mode not implemented\n");
435       break;
436     case NOTSRCCOPY: /* dest = (NOT source) */
437       printf("ipa_rop_draw NOTSRCCOPY ROP mode not implemented\n");
438       break;
439     case NOTSRCERASE: /* dest = (NOT src) AND (NOT dest) */
440       printf("ipa_rop_draw NOTSRCERASE ROP mode not implemented\n");
441       break;
442     case MERGECOPY: /* dest = (source AND pattern) */
443       printf("ipa_rop_draw MERGECOPY ROP mode not implemented\n");
444       break;
445     case MERGEPAINT: /* dest = (NOT source) OR dest */
446       printf("ipa_rop_draw MERGEPAINT ROP mode not implemented\n");
447       break;
448     case PATCOPY: /* dest = pattern */
449       util_set_brush(API, rop_draw->dc, BrushApplyFill);
450       break;
451     case PATPAINT: /* dest = DPSnoo */
452       printf("ipa_rop_draw PATPAINT ROP mode not implemented\n");
453       break;
454     case PATINVERT: /* dest = pattern XOR dest */
455       printf("ipa_rop_draw PATINVERT ROP mode not implemented\n");
456       break;
457     case DSTINVERT: /* dest = (NOT dest) */
458       printf("ipa_rop_draw DSTINVERT ROP mode not implemented\n");
459       break;
460     case BLACKNESS: /* dest = BLACK */
461       draw_fill_color_string(WmfDrawingWand,"black");
462       break;
463     case WHITENESS: /* dest = WHITE */
464       draw_fill_color_string(WmfDrawingWand,"white");
465       break;
466     default:
467       printf("ipa_rop_draw 0x%x ROP mode not implemented\n", rop_draw->ROP);
468       break;
469     }
470
471   DrawRectangle(WmfDrawingWand,
472                  XC(rop_draw->TL.x), YC(rop_draw->TL.y),
473                  XC(rop_draw->BR.x), YC(rop_draw->BR.y));
474
475   /* Restore graphic wand */
476   (void) PopDrawingWand(WmfDrawingWand);
477 }
478
479 static void ipa_bmp_draw(wmfAPI *API, wmfBMP_Draw_t *bmp_draw)
480 {
481   wmf_magick_t
482     *ddata = WMF_MAGICK_GetData(API);
483
484   ExceptionInfo
485     exception;
486
487   Image
488     *image;
489
490   MagickWand
491     *magick_wand;
492
493   MagickRealType
494     height,
495     width;
496
497   PixelPacket
498     white;
499
500   if (bmp_draw->bmp.data == 0)
501     return;
502
503   GetExceptionInfo(&exception);
504   image = (Image*)bmp_draw->bmp.data;
505   if (!image)
506     {
507        InheritException(&ddata->image->exception,&exception);
508        return;
509     }
510
511   if (bmp_draw->crop.x || bmp_draw->crop.y ||
512      (bmp_draw->crop.w != bmp_draw->bmp.width) ||
513      (bmp_draw->crop.h != bmp_draw->bmp.height))
514     {
515       /* Image needs to be cropped */
516       Image
517         *crop_image;
518
519       RectangleInfo
520         crop_info;
521
522       crop_info.x = bmp_draw->crop.x;
523       crop_info.y = bmp_draw->crop.y;
524       crop_info.width = bmp_draw->crop.w;
525       crop_info.height = bmp_draw->crop.h;
526
527       crop_image = CropImage( image, &crop_info, &exception );
528       if (crop_image)
529         {
530           image=DestroyImageList(image);
531           image = crop_image;
532           bmp_draw->bmp.data = (void*)image;
533         }
534       else
535         InheritException(&ddata->image->exception,&exception);
536     }
537
538   QueryColorDatabase( "white", &white, &exception );
539
540   if ( ddata->image_info->texture ||
541        !(IsColorEqual(&ddata->image_info->background_color,&white)) ||
542        ddata->image_info->background_color.opacity != OpaqueOpacity )
543   {
544     MagickPixelPacket
545       white;
546
547     /*
548       Set image white background to transparent so that it may be
549       overlaid over non-white backgrounds.
550     */
551     QueryMagickColor( "white", &white, &exception );
552     TransparentPaintImage( image, &white, QuantumRange, MagickFalse );
553   }
554
555   width = fabs(bmp_draw->pixel_width * (double) bmp_draw->crop.w);
556   height = fabs(bmp_draw->pixel_height * (double) bmp_draw->crop.h);
557   magick_wand=NewMagickWandFromImage(image);
558   (void) DrawComposite(WmfDrawingWand, CopyCompositeOp,
559     XC(bmp_draw->pt.x) * ddata->scale_x, YC(bmp_draw->pt.y) * ddata->scale_y,
560     width * ddata->scale_x, height * ddata->scale_y, magick_wand);
561   magick_wand=DestroyMagickWand(magick_wand);
562
563 #if 0
564   printf("bmp_draw->bmp.data   = 0x%lx\n", (long)bmp_draw->bmp.data);
565   printf("registry id          = %li\n", id);
566   /* printf("pixel_width          = %g\n", bmp_draw->pixel_width); */
567   /* printf("pixel_height         = %g\n", bmp_draw->pixel_height); */
568   printf("bmp_draw->bmp WxH    = %ix%i\n", bmp_draw->bmp.width, bmp_draw->bmp.height);
569   printf("bmp_draw->crop WxH   = %ix%i\n", bmp_draw->crop.w, bmp_draw->crop.h);
570   printf("bmp_draw->crop x,y   = %i,%i\n", bmp_draw->crop.x, bmp_draw->crop.y);
571   printf("image size WxH       = %lux%lu\n", image->columns, image->rows);
572 #endif
573 }
574
575 static void ipa_bmp_read(wmfAPI * API, wmfBMP_Read_t * bmp_read) {
576   wmf_magick_t
577     *ddata = WMF_MAGICK_GetData(API);
578
579   ExceptionInfo
580     exception;
581
582   Image
583     *image;
584
585   ImageInfo
586     *image_info;
587
588   bmp_read->bmp.data = 0;
589
590   GetExceptionInfo(&exception);
591
592   image_info=CloneImageInfo((ImageInfo *) 0);
593   (void) CopyMagickString(image_info->magick,"DIB",MaxTextExtent);
594   if (bmp_read->width || bmp_read->height)
595     {
596       char
597         size[MaxTextExtent];
598
599       (void) FormatMagickString(size,MaxTextExtent,"%ux%u",bmp_read->width,
600         bmp_read->height);
601       CloneString(&image_info->size,size);
602     }
603 #if 0
604   printf("ipa_bmp_read: buffer=0x%lx length=%ld, width=%i, height=%i\n",
605    (long) bmp_read->buffer, bmp_read->length,
606    bmp_read->width, bmp_read->height);
607 #endif
608   image=BlobToImage(image_info, (const void *) bmp_read->buffer,
609     bmp_read->length, &exception);
610   image_info=DestroyImageInfo(image_info);
611   if (image == (Image *) NULL)
612     {
613       char
614         description[MaxTextExtent];
615
616       (void) FormatMagickString(description,MaxTextExtent,
617         "packed DIB at offset %ld",bmp_read->offset);
618       (void) ThrowMagickException(&ddata->image->exception,GetMagickModule(),
619         CorruptImageError,exception.reason,"`%s'",exception.description);
620     }
621   else
622     {
623 #if 0
624       printf("ipa_bmp_read: rows=%ld,columns=%ld\n\n", image->rows, image->columns);
625 #endif
626
627       bmp_read->bmp.data   = (void*)image;
628       bmp_read->bmp.width  = (U16)image->columns;
629       bmp_read->bmp.height = (U16)image->rows;
630     }
631 }
632
633 static void ipa_bmp_free(wmfAPI * API, wmfBMP * bmp)
634 {
635   (void) API;
636   DestroyImageList((Image*)bmp->data);
637   bmp->data = (void*) 0;
638   bmp->width = (U16) 0;
639   bmp->height = (U16) 0;
640 }
641
642 /*
643   This called by wmf_play() the *first* time the meta file is played
644  */
645 static void ipa_device_open(wmfAPI * API)
646 {
647   wmf_magick_t
648     *ddata = WMF_MAGICK_GetData (API);
649
650   ddata->pattern_id = 0;
651   ddata->clipping = MagickFalse;
652   ddata->clip_mask_id = 0;
653
654   ddata->push_depth = 0;
655
656   ddata->draw_wand = DrawAllocateWand(ddata->draw_info,ddata->image);
657 }
658
659 /*
660   This called by wmf_api_destroy()
661  */
662 static void ipa_device_close(wmfAPI * API)
663 {
664   wmf_magick_t
665     *ddata = WMF_MAGICK_GetData(API);
666
667   DestroyDrawingWand(ddata->draw_wand);
668   DestroyDrawInfo(ddata->draw_info);
669   RelinquishMagickMemory(WMF_MAGICK_GetFontData(API)->ps_name);
670 }
671
672 /*
673   This called from the beginning of each play for initial page setup
674  */
675 static void ipa_device_begin(wmfAPI * API)
676 {
677   char
678     comment[MaxTextExtent];
679
680   wmf_magick_t
681     *ddata = WMF_MAGICK_GetData(API);
682
683   /* Make SVG output happy */
684   (void) PushDrawingWand(WmfDrawingWand);
685
686   DrawSetViewbox(WmfDrawingWand, 0, 0, ddata->image->columns, ddata->image->rows );
687
688   (void) FormatMagickString(comment,MaxTextExtent,"Created by ImageMagick %s",
689     GetMagickVersion((unsigned long *) NULL));
690   DrawComment(WmfDrawingWand,comment);
691
692   /* Scale width and height to image */
693   DrawScale(WmfDrawingWand, ddata->scale_x, ddata->scale_y);
694
695   /* Translate to TL corner of bounding box */
696   DrawTranslate(WmfDrawingWand, ddata->translate_x, ddata->translate_y);
697
698   /* Apply rotation */
699   DrawRotate(WmfDrawingWand, ddata->rotate);
700
701   if (ddata->image_info->texture == NULL)
702     {
703       PixelWand
704         *background_color;
705
706       /* Draw rectangle in background color */
707       background_color=NewPixelWand();
708       PixelSetQuantumColor(background_color,&ddata->image->background_color);
709       DrawSetFillColor(WmfDrawingWand,background_color);
710       background_color=DestroyPixelWand(background_color);
711       DrawRectangle(WmfDrawingWand,
712                      XC(ddata->bbox.TL.x),YC(ddata->bbox.TL.y),
713                      XC(ddata->bbox.BR.x),YC(ddata->bbox.BR.y));
714     }
715   else
716     {
717       /* Draw rectangle with texture image the SVG way */
718       Image
719         *image;
720
721       ImageInfo
722         *image_info;
723
724       ExceptionInfo
725         exception;
726
727       GetExceptionInfo(&exception);
728
729       image_info = CloneImageInfo((ImageInfo *) 0);
730       (void) CopyMagickString(image_info->filename,ddata->image_info->texture,
731         MaxTextExtent);
732       if ( ddata->image_info->size )
733         CloneString(&image_info->size,ddata->image_info->size);
734
735       image = ReadImage(image_info,&exception);
736       image_info=DestroyImageInfo(image_info);
737       if (image)
738         {
739           char
740             pattern_id[30];
741
742           MagickWand
743             *magick_wand;
744
745           (void) CopyMagickString(image->magick,"MIFF",MaxTextExtent);
746           DrawPushDefs(WmfDrawingWand);
747           draw_pattern_push(API,ddata->pattern_id,image->columns,image->rows);
748           magick_wand=NewMagickWandFromImage(image);
749           (void) DrawComposite(WmfDrawingWand,CopyCompositeOp,0,0,
750             image->columns,image->rows,magick_wand);
751           magick_wand=DestroyMagickWand(magick_wand);
752           (void) DrawPopPattern(WmfDrawingWand);
753           DrawPopDefs(WmfDrawingWand);
754           (void) FormatMagickString(pattern_id,MaxTextExtent,"#brush_%lu",
755             ddata->pattern_id);
756           (void) DrawSetFillPatternURL(WmfDrawingWand,pattern_id);
757           ++ddata->pattern_id;
758
759           DrawRectangle(WmfDrawingWand,
760                          XC(ddata->bbox.TL.x),YC(ddata->bbox.TL.y),
761                          XC(ddata->bbox.BR.x),YC(ddata->bbox.BR.y));
762           image=DestroyImageList(image);
763         }
764       else
765         {
766           LogMagickEvent(CoderEvent,GetMagickModule(),
767             "reading texture image failed!");
768           InheritException(&ddata->image->exception,&exception);
769         }
770     }
771
772   DrawSetClipRule(WmfDrawingWand,EvenOddRule); /* Default for WMF is ALTERNATE polygon fill mode */
773   draw_fill_color_string(WmfDrawingWand,"none"); /* Default brush is WHITE_BRUSH */
774   draw_stroke_color_string(WmfDrawingWand,"none"); /* Default pen is BLACK_PEN */
775   DrawSetStrokeLineCap(WmfDrawingWand,ButtCap); /* Default linecap is PS_ENDCAP_FLAT */
776   DrawSetStrokeLineJoin(WmfDrawingWand,MiterJoin); /* Default linejoin is PS_JOIN_MITER */
777   draw_under_color_string(WmfDrawingWand,"white"); /* Default text box is white */
778 }
779
780 /*
781   This called from the end of each play for page termination
782  */
783 static void ipa_device_end(wmfAPI * API)
784 {
785   wmf_magick_t
786     *ddata = WMF_MAGICK_GetData(API);
787
788   /* Reset any existing clip paths by popping wand */
789   if (ddata->clipping)
790     (void) PopDrawingWand(WmfDrawingWand);
791   ddata->clipping = MagickFalse;
792
793   /* Make SVG output happy */
794   (void) PopDrawingWand(WmfDrawingWand);
795 }
796
797 static void ipa_flood_interior(wmfAPI * API, wmfFlood_t * flood)
798 {
799   /* Save graphic wand */
800   (void) PushDrawingWand(WmfDrawingWand);
801
802   draw_fill_color_rgb(API,&(flood->color));
803
804   DrawColor(WmfDrawingWand,XC(flood->pt.x), YC(flood->pt.y),
805             FillToBorderMethod);
806
807   /* Restore graphic wand */
808   (void) PopDrawingWand(WmfDrawingWand);
809 }
810
811 static void ipa_flood_exterior(wmfAPI * API, wmfFlood_t * flood)
812 {
813   /* Save graphic wand */
814   (void) PushDrawingWand(WmfDrawingWand);
815
816   draw_fill_color_rgb(API,&(flood->color));
817
818   if (flood->type == FLOODFILLSURFACE)
819     DrawColor(WmfDrawingWand, XC(flood->pt.x), YC(flood->pt.y),
820               FloodfillMethod);
821   else
822     DrawColor(WmfDrawingWand, XC(flood->pt.x), YC(flood->pt.y),
823               FillToBorderMethod);
824
825   /* Restore graphic wand */
826   (void) PopDrawingWand(WmfDrawingWand);
827 }
828
829 static void ipa_draw_pixel(wmfAPI * API, wmfDrawPixel_t * draw_pixel)
830 {
831   /* Save graphic wand */
832   (void) PushDrawingWand(WmfDrawingWand);
833
834   draw_stroke_color_string(WmfDrawingWand,"none");
835
836   draw_fill_color_rgb(API,&(draw_pixel->color));
837
838   DrawRectangle(WmfDrawingWand,
839                  XC(draw_pixel->pt.x),
840                  YC(draw_pixel->pt.y),
841                  XC(draw_pixel->pt.x + draw_pixel->pixel_width),
842                  YC(draw_pixel->pt.y + draw_pixel->pixel_height));
843
844   /* Restore graphic wand */
845   (void) PopDrawingWand(WmfDrawingWand);
846 }
847
848 static void ipa_draw_pie(wmfAPI * API, wmfDrawArc_t * draw_arc)
849 {
850   util_draw_arc(API, draw_arc, magick_arc_pie);
851 }
852
853 static void ipa_draw_chord(wmfAPI * API, wmfDrawArc_t * draw_arc)
854 {
855   util_draw_arc(API, draw_arc, magick_arc_chord);
856 }
857
858 static void ipa_draw_arc(wmfAPI * API, wmfDrawArc_t * draw_arc)
859 {
860   util_draw_arc(API, draw_arc, magick_arc_open);
861 }
862
863 static void ipa_draw_ellipse(wmfAPI * API, wmfDrawArc_t * draw_arc)
864 {
865   util_draw_arc(API, draw_arc, magick_arc_ellipse);
866 }
867
868 static void util_draw_arc(wmfAPI * API,
869           wmfDrawArc_t * draw_arc, magick_arc_t finish)
870 {
871   wmfD_Coord
872     BR,
873     O,
874     TL,
875     center,
876     end,
877     start;
878
879   double
880     phi_e = 360,
881     phi_s = 0;
882
883   double
884     Rx,
885     Ry;
886
887   /* Save graphic wand */
888   (void) PushDrawingWand(WmfDrawingWand);
889
890   if (TO_FILL(draw_arc) || TO_DRAW(draw_arc))
891     {
892       center.x = (draw_arc->TL.x + draw_arc->BR.x) / 2;
893       center.y = (draw_arc->TL.y + draw_arc->BR.y) / 2;
894       start = center;
895       end = center;
896
897       if (finish != magick_arc_ellipse)
898         {
899           draw_arc->start.x += center.x;
900           draw_arc->start.y += center.y;
901
902           draw_arc->end.x += center.x;
903           draw_arc->end.y += center.y;
904         }
905
906       TL = draw_arc->TL;
907       BR = draw_arc->BR;
908
909       O = center;
910
911       if (finish != magick_arc_ellipse)
912         {
913           start = draw_arc->start;
914           end = draw_arc->end;
915         }
916
917       Rx = (BR.x - TL.x) / 2;
918       Ry = (BR.y - TL.y) / 2;
919
920       if (finish != magick_arc_ellipse)
921         {
922           start.x -= O.x;
923           start.y -= O.y;
924
925           end.x -= O.x;
926           end.y -= O.y;
927
928           phi_s = atan2((double) start.y, (double) start.x) * 180 / MagickPI;
929           phi_e = atan2((double) end.y, (double) end.x) * 180 / MagickPI;
930
931           if (phi_e <= phi_s)
932             phi_e += 360;
933         }
934
935       util_set_pen(API, draw_arc->dc);
936       if (finish == magick_arc_open)
937         draw_fill_color_string(WmfDrawingWand,"none");
938       else
939         util_set_brush(API, draw_arc->dc, BrushApplyFill);
940
941       if (finish == magick_arc_ellipse)
942         DrawEllipse(WmfDrawingWand, XC(O.x), YC(O.y), Rx, Ry, 0, 360);
943       else if (finish == magick_arc_pie)
944         {
945           DrawPathStart(WmfDrawingWand);
946           DrawPathMoveToAbsolute(WmfDrawingWand, XC(O.x+start.x), 
947             YC(O.y+start.y));
948           DrawPathEllipticArcAbsolute(WmfDrawingWand, Rx, Ry, 0, MagickFalse,
949             MagickTrue, XC(O.x+end.x), YC(O.y+end.y));
950           DrawPathLineToAbsolute(WmfDrawingWand, XC(O.x), YC(O.y));
951           DrawPathClose(WmfDrawingWand);
952           DrawPathFinish(WmfDrawingWand);
953         }
954         else if (finish == magick_arc_chord)
955         {
956           DrawArc(WmfDrawingWand, XC(draw_arc->TL.x), YC(draw_arc->TL.y),
957             XC(draw_arc->BR.x), XC(draw_arc->BR.y), phi_s, phi_e);
958           DrawLine(WmfDrawingWand, XC(draw_arc->BR.x-start.x), 
959             YC(draw_arc->BR.y-start.y), XC(draw_arc->BR.x-end.x),
960             YC(draw_arc->BR.y-end.y));
961         }
962         else      /* if (finish == magick_arc_open) */
963           DrawArc(WmfDrawingWand, XC(draw_arc->TL.x), YC(draw_arc->TL.y),
964             XC(draw_arc->BR.x), XC(draw_arc->BR.y), phi_s, phi_e);
965     }
966
967   /* Restore graphic wand */
968   (void) PopDrawingWand(WmfDrawingWand);
969 }
970
971 static void ipa_draw_line(wmfAPI * API, wmfDrawLine_t * draw_line)
972 {
973   /* Save graphic wand */
974   (void) PushDrawingWand(WmfDrawingWand);
975
976   if (TO_DRAW(draw_line))
977     {
978       util_set_pen(API, draw_line->dc);
979       DrawLine(WmfDrawingWand,
980                XC(draw_line->from.x), YC(draw_line->from.y),
981                XC(draw_line->to.x), YC(draw_line->to.y));
982     }
983
984   /* Restore graphic wand */
985   (void) PopDrawingWand(WmfDrawingWand);
986 }
987
988 static void ipa_poly_line(wmfAPI * API, wmfPolyLine_t * polyline)
989 {
990   if (polyline->count <= 2)
991     return;
992
993   if (TO_DRAW(polyline))
994     {
995       int
996         point;
997
998       /* Save graphic wand */
999       (void) PushDrawingWand(WmfDrawingWand);
1000
1001       util_set_pen(API, polyline->dc);
1002
1003       DrawPathStart(WmfDrawingWand);
1004       DrawPathMoveToAbsolute(WmfDrawingWand,
1005                              XC(polyline->pt[0].x),
1006                              YC(polyline->pt[0].y));
1007       for (point = 1; point < polyline->count; point++)
1008         {
1009           DrawPathLineToAbsolute(WmfDrawingWand,
1010                                  XC(polyline->pt[point].x),
1011                                  YC(polyline->pt[point].y));
1012         }
1013       DrawPathFinish(WmfDrawingWand);
1014
1015       /* Restore graphic wand */
1016       (void) PopDrawingWand(WmfDrawingWand);
1017     }
1018 }
1019
1020 static void ipa_draw_polygon(wmfAPI * API, wmfPolyLine_t * polyline)
1021 {
1022   if (polyline->count <= 2)
1023     return;
1024
1025   if (TO_FILL(polyline) || TO_DRAW(polyline))
1026     {
1027       int
1028         point;
1029
1030       /* Save graphic wand */
1031       (void) PushDrawingWand(WmfDrawingWand);
1032
1033       util_set_pen(API, polyline->dc);
1034       util_set_brush(API, polyline->dc, BrushApplyFill);
1035
1036       DrawPathStart(WmfDrawingWand);
1037       DrawPathMoveToAbsolute(WmfDrawingWand,
1038                              XC(polyline->pt[0].x),
1039                              YC(polyline->pt[0].y));
1040       for (point = 1; point < polyline->count; point++)
1041         {
1042           DrawPathLineToAbsolute(WmfDrawingWand,
1043                                  XC(polyline->pt[point].x),
1044                                  YC(polyline->pt[point].y));
1045         }
1046       DrawPathClose(WmfDrawingWand);
1047       DrawPathFinish(WmfDrawingWand);
1048
1049       /* Restore graphic wand */
1050       (void) PopDrawingWand(WmfDrawingWand);
1051     }
1052 }
1053
1054 /* Draw a polypolygon.  A polypolygon is a list of polygons */
1055 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
1056 static void ipa_draw_polypolygon(wmfAPI * API, wmfPolyPoly_t* polypolygon)
1057 {
1058   if (TO_FILL(polypolygon) || TO_DRAW(polypolygon))
1059     {
1060       int
1061         polygon,
1062         point;
1063
1064       wmfPolyLine_t
1065         polyline;
1066
1067       /* Save graphic wand */
1068       (void) PushDrawingWand(WmfDrawingWand);
1069
1070       util_set_pen(API, polypolygon->dc);
1071       util_set_brush(API, polypolygon->dc, BrushApplyFill);
1072
1073       DrawPathStart(WmfDrawingWand);
1074       for (polygon = 0; polygon < polypolygon->npoly; polygon++)
1075         {
1076           polyline.dc = polypolygon->dc;
1077           polyline.pt = polypolygon->pt[polygon];
1078           polyline.count = polypolygon->count[polygon];
1079           if ((polyline.count > 2) && polyline.pt)
1080             {
1081               DrawPathMoveToAbsolute(WmfDrawingWand,
1082                                      XC(polyline.pt[0].x),
1083                                      YC(polyline.pt[0].y));
1084               for (point = 1; point < polyline.count; point++)
1085                 {
1086                   DrawPathLineToAbsolute(WmfDrawingWand,
1087                                          XC(polyline.pt[point].x),
1088                                          YC(polyline.pt[point].y));
1089                 }
1090               DrawPathClose(WmfDrawingWand);
1091             }
1092         }
1093       DrawPathFinish(WmfDrawingWand);
1094
1095       /* Restore graphic wand */
1096       (void) PopDrawingWand(WmfDrawingWand);
1097     }
1098 }
1099 #endif
1100
1101 static void ipa_draw_rectangle(wmfAPI * API, wmfDrawRectangle_t * draw_rect)
1102 {
1103   /* Save graphic wand */
1104   (void) PushDrawingWand(WmfDrawingWand);
1105
1106   if (TO_FILL(draw_rect) || TO_DRAW(draw_rect))
1107     {
1108       util_set_pen(API, draw_rect->dc);
1109       util_set_brush(API, draw_rect->dc, BrushApplyFill);
1110
1111       if ((draw_rect->width > 0) || (draw_rect->height > 0))
1112         DrawRoundRectangle(WmfDrawingWand,
1113                            XC(draw_rect->TL.x), YC(draw_rect->TL.y),
1114                            XC(draw_rect->BR.x), YC(draw_rect->BR.y),
1115                            draw_rect->width / 2, draw_rect->height / 2);
1116       else
1117         DrawRectangle(WmfDrawingWand,
1118                       XC(draw_rect->TL.x), YC(draw_rect->TL.y),
1119                       XC(draw_rect->BR.x), YC(draw_rect->BR.y));
1120     }
1121
1122   /* Restore graphic wand */
1123   (void) PopDrawingWand(WmfDrawingWand);
1124 }
1125
1126 /* Draw an un-filled rectangle using the current brush */
1127 static void ipa_region_frame(wmfAPI * API, wmfPolyRectangle_t * poly_rect)
1128 {
1129   /* Save graphic wand */
1130   (void) PushDrawingWand(WmfDrawingWand);
1131
1132   if (TO_FILL(poly_rect) || TO_DRAW(poly_rect))
1133     {
1134       long
1135         i;
1136
1137       draw_fill_color_string(WmfDrawingWand,"none");
1138       util_set_brush(API, poly_rect->dc, BrushApplyStroke);
1139
1140       for (i = 0; i < (long) poly_rect->count; i++)
1141         {
1142           DrawRectangle(WmfDrawingWand,
1143                          XC(poly_rect->TL[i].x), YC(poly_rect->TL[i].y),
1144                          XC(poly_rect->BR[i].x), YC(poly_rect->BR[i].y));
1145         }
1146     }
1147
1148   /* Restore graphic wand */
1149   (void) PopDrawingWand(WmfDrawingWand);
1150 }
1151
1152 static void ipa_region_paint(wmfAPI * API, wmfPolyRectangle_t * poly_rect)
1153 {
1154
1155   if (poly_rect->count == 0)
1156     return;
1157
1158   /* Save graphic wand */
1159   (void) PushDrawingWand(WmfDrawingWand);
1160
1161   if (TO_FILL (poly_rect))
1162     {
1163       long
1164         i;
1165
1166       draw_stroke_color_string(WmfDrawingWand,"none");
1167       util_set_brush(API, poly_rect->dc, BrushApplyFill);
1168
1169       for (i = 0; i < (long) poly_rect->count; i++)
1170         {
1171           DrawRectangle(WmfDrawingWand,
1172                          XC(poly_rect->TL[i].x), YC(poly_rect->TL[i].y),
1173                          XC(poly_rect->BR[i].x), YC(poly_rect->BR[i].y));
1174         }
1175     }
1176
1177   /* Restore graphic wand */
1178   (void) PopDrawingWand(WmfDrawingWand);
1179 }
1180
1181 static void ipa_region_clip(wmfAPI *API, wmfPolyRectangle_t *poly_rect)
1182 {
1183   long
1184     i;
1185
1186   wmf_magick_t
1187     *ddata = WMF_MAGICK_GetData (API);
1188
1189   /* Reset any existing clip paths by popping wand */
1190   if (ddata->clipping)
1191     (void) PopDrawingWand(WmfDrawingWand);
1192   ddata->clipping = MagickFalse;
1193
1194   if (poly_rect->count > 0)
1195     {
1196       char
1197         clip_mask_id[30];
1198
1199       /* Define clip path */
1200       ddata->clip_mask_id++;
1201       DrawPushDefs(WmfDrawingWand);
1202       (void) FormatMagickString(clip_mask_id,MaxTextExtent,"clip_%lu",
1203         ddata->clip_mask_id);
1204       DrawPushClipPath(WmfDrawingWand,clip_mask_id);
1205       (void) PushDrawingWand(WmfDrawingWand);
1206       for (i = 0; i < (long) poly_rect->count; i++)
1207         {
1208           DrawRectangle(WmfDrawingWand,
1209                          XC(poly_rect->TL[i].x), YC(poly_rect->TL[i].y),
1210                          XC(poly_rect->BR[i].x), YC(poly_rect->BR[i].y));
1211         }
1212       (void) PopDrawingWand(WmfDrawingWand);
1213       DrawPopClipPath(WmfDrawingWand);
1214       DrawPopDefs(WmfDrawingWand);
1215
1216       /* Push wand for new clip paths */
1217       (void) PushDrawingWand(WmfDrawingWand);
1218       (void) DrawSetClipPath(WmfDrawingWand,clip_mask_id);
1219       ddata->clipping = MagickTrue;
1220     }
1221 }
1222
1223 static void ipa_functions(wmfAPI *API)
1224 {
1225   wmf_magick_t
1226     *ddata = 0;
1227
1228   wmfFunctionReference
1229     *FR = (wmfFunctionReference *) API->function_reference;
1230
1231   /*
1232      IPA function reference links
1233    */
1234   FR->device_open = ipa_device_open;
1235   FR->device_close = ipa_device_close;
1236   FR->device_begin = ipa_device_begin;
1237   FR->device_end = ipa_device_end;
1238   FR->flood_interior = ipa_flood_interior;
1239   FR->flood_exterior = ipa_flood_exterior;
1240   FR->draw_pixel = ipa_draw_pixel;
1241   FR->draw_pie = ipa_draw_pie;
1242   FR->draw_chord = ipa_draw_chord;
1243   FR->draw_arc = ipa_draw_arc;
1244   FR->draw_ellipse = ipa_draw_ellipse;
1245   FR->draw_line = ipa_draw_line;
1246   FR->poly_line = ipa_poly_line;
1247   FR->draw_polygon = ipa_draw_polygon;
1248 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
1249   FR->draw_polypolygon = ipa_draw_polypolygon;
1250 #endif
1251   FR->draw_rectangle = ipa_draw_rectangle;
1252   FR->rop_draw = ipa_rop_draw;
1253   FR->bmp_draw = ipa_bmp_draw;
1254   FR->bmp_read = ipa_bmp_read;
1255   FR->bmp_free = ipa_bmp_free;
1256   FR->draw_text = ipa_draw_text;
1257   FR->udata_init = ipa_udata_init;
1258   FR->udata_copy = ipa_udata_copy;
1259   FR->udata_set = ipa_udata_set;
1260   FR->udata_free = ipa_udata_free;
1261   FR->region_frame = ipa_region_frame;
1262   FR->region_paint = ipa_region_paint;
1263   FR->region_clip = ipa_region_clip;
1264
1265   /*
1266      Allocate device data structure
1267    */
1268   ddata = (wmf_magick_t *) wmf_malloc(API, sizeof(wmf_magick_t));
1269   if (ERR(API))
1270     return;
1271
1272   (void) ResetMagickMemory((void *) ddata, 0, sizeof(wmf_magick_t));
1273   API->device_data = (void *) ddata;
1274
1275   /*
1276      Device data defaults
1277    */
1278   ddata->image = 0;
1279 }
1280
1281 static void ipa_draw_text(wmfAPI * API, wmfDrawText_t * draw_text)
1282 {
1283   double
1284     angle = 0,      /* text rotation angle */
1285     bbox_height,    /* bounding box height */
1286     bbox_width,      /* bounding box width */
1287     pointsize = 0;    /* pointsize to output font with desired height */
1288
1289   TypeMetric
1290     metrics;
1291
1292   wmfD_Coord
1293     BL,        /* bottom left of bounding box */
1294     BR,        /* bottom right of bounding box */
1295     TL,        /* top left of bounding box */
1296     TR;        /* top right of bounding box */
1297
1298   wmfD_Coord
1299     point;      /* text placement point */
1300
1301   wmfFont
1302     *font;
1303
1304   wmf_magick_t
1305     * ddata = WMF_MAGICK_GetData(API);
1306
1307   point = draw_text->pt;
1308
1309   /* Choose bounding box and calculate its width and height */
1310   {
1311     double dx,
1312       dy;
1313
1314     if ( draw_text->flags)
1315       {
1316         TL = draw_text->TL;
1317         BR = draw_text->BR;
1318         TR.x = draw_text->BR.x;
1319         TR.y = draw_text->TL.y;
1320         BL.x = draw_text->TL.x;
1321         BL.y = draw_text->BR.y;
1322       }
1323     else
1324       {
1325         TL = draw_text->bbox.TL;
1326         BR = draw_text->bbox.BR;
1327         TR = draw_text->bbox.TR;
1328         BL = draw_text->bbox.BL;
1329       }
1330     dx = ((TR.x - TL.x) + (BR.x - BL.x)) / 2;
1331     dy = ((TR.y - TL.y) + (BR.y - BL.y)) / 2;
1332     bbox_width = hypot(dx,dy);
1333     dx = ((BL.x - TL.x) + (BR.x - TR.x)) / 2;
1334     dy = ((BL.y - TL.y) + (BR.y - TR.y)) / 2;
1335     bbox_height = hypot(dx,dy);
1336   }
1337
1338   font = WMF_DC_FONT(draw_text->dc);
1339
1340   /* Convert font_height to equivalent pointsize */
1341   pointsize = util_pointsize( API, font, draw_text->str, draw_text->font_height);
1342
1343   /* Save graphic wand */
1344   (void) PushDrawingWand(WmfDrawingWand);
1345
1346 #if 0
1347   printf("\nipa_draw_text\n");
1348   printf("Text                    = \"%s\"\n", draw_text->str);
1349   /* printf("WMF_FONT_NAME:          = \"%s\"\n", WMF_FONT_NAME(font)); */
1350   printf("WMF_FONT_PSNAME:        = \"%s\"\n", WMF_FONT_PSNAME(font));
1351   printf("Bounding box            TL=%g,%g BR=%g,%g\n",
1352          TL.x, TL.y, BR.x, BR.y );
1353   /* printf("Text box                = %gx%g\n", bbox_width, bbox_height); */
1354   /* printf("WMF_FONT_HEIGHT         = %i\n", (int)WMF_FONT_HEIGHT(font)); */
1355   printf("Pointsize               = %g\n", pointsize);
1356   fflush(stdout);
1357 #endif
1358
1359   /*
1360    * Obtain font metrics if required
1361    *
1362    */
1363   if ((WMF_DC_TEXTALIGN(draw_text->dc) & TA_CENTER) ||
1364       (WMF_TEXT_UNDERLINE(font)) || (WMF_TEXT_STRIKEOUT(font)))
1365     {
1366       Image
1367         *image = ddata->image;
1368
1369       DrawInfo
1370         *draw_info;
1371
1372       draw_info=ddata->draw_info;
1373       draw_info->font=WMF_FONT_PSNAME(font);
1374       draw_info->pointsize = pointsize;
1375       draw_info->text=draw_text->str;
1376
1377       if (GetTypeMetrics(image, draw_info, &metrics) != MagickFalse)
1378         {
1379           /* Center the text if it is not yet centered and should be */
1380           if ((WMF_DC_TEXTALIGN(draw_text->dc) & TA_CENTER))
1381             {
1382               double
1383                 text_width = metrics.width * (ddata->scale_y / ddata->scale_x);
1384
1385 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
1386               point.x -= text_width / 2;
1387 #else
1388               point.x += bbox_width / 2 - text_width / 2;
1389 #endif
1390             }
1391         }
1392       draw_info->font=NULL;
1393       draw_info->text=NULL;
1394     }
1395
1396   /* Set text background color */
1397   if (draw_text->flags & ETO_OPAQUE)
1398     {
1399       /* Draw bounding-box background color (META_EXTTEXTOUT mode) */
1400       draw_stroke_color_string(WmfDrawingWand,"none");
1401       draw_fill_color_rgb(API,WMF_DC_BACKGROUND(draw_text->dc));
1402       DrawRectangle(WmfDrawingWand,
1403                     XC(draw_text->TL.x),YC(draw_text->TL.y),
1404                     XC(draw_text->BR.x),YC(draw_text->BR.y));
1405       draw_fill_color_string(WmfDrawingWand,"none");
1406     }
1407   else
1408     {
1409       /* Set text undercolor */
1410       if (WMF_DC_OPAQUE(draw_text->dc))
1411         {
1412           wmfRGB
1413             *box = WMF_DC_BACKGROUND(draw_text->dc);
1414
1415           PixelWand
1416             *under_color;
1417
1418           under_color=NewPixelWand();
1419           PixelSetRedQuantum(under_color,ScaleCharToQuantum(box->r));
1420           PixelSetGreenQuantum(under_color,ScaleCharToQuantum(box->g));
1421           PixelSetBlueQuantum(under_color,ScaleCharToQuantum(box->b));
1422           PixelSetOpacityQuantum(under_color,OpaqueOpacity);
1423           DrawSetTextUnderColor(WmfDrawingWand,under_color);
1424           under_color=DestroyPixelWand(under_color);
1425         }
1426       else
1427         draw_under_color_string(WmfDrawingWand,"none");
1428     }
1429
1430   /* Set text clipping (META_EXTTEXTOUT mode) */
1431   if ( draw_text->flags & ETO_CLIPPED)
1432     {
1433     }
1434
1435   /* Set stroke color */
1436   draw_stroke_color_string(WmfDrawingWand,"none");
1437
1438   /* Set fill color */
1439   draw_fill_color_rgb(API,WMF_DC_TEXTCOLOR(draw_text->dc));
1440
1441   /* Output font size */
1442   (void) DrawSetFontSize(WmfDrawingWand,pointsize);
1443
1444   /* Output Postscript font name */
1445   (void) DrawSetFont(WmfDrawingWand, WMF_FONT_PSNAME(font));
1446
1447   /* Translate coordinates so target is 0,0 */
1448   DrawTranslate(WmfDrawingWand, XC(point.x), YC(point.y));
1449
1450   /* Transform horizontal scale to draw text at 1:1 ratio */
1451   DrawScale(WmfDrawingWand, ddata->scale_y / ddata->scale_x, 1.0);
1452
1453   /* Apply rotation */
1454   /* ImageMagick's drawing rotation is clockwise from horizontal
1455      while WMF drawing rotation is counterclockwise from horizontal */
1456   angle = fabs(RadiansToDegrees(2 * MagickPI - WMF_TEXT_ANGLE(font)));
1457   if (angle == 360)
1458     angle = 0;
1459   if (angle != 0)
1460     DrawRotate(WmfDrawingWand, angle);
1461
1462   /*
1463    * Render text
1464    *
1465    */
1466
1467   /* Output string */
1468   DrawAnnotation(WmfDrawingWand, 0, 0, (unsigned char*)draw_text->str);
1469
1470   /* Underline text the Windows way (at the bottom) */
1471   if (WMF_TEXT_UNDERLINE(font))
1472     {
1473       double
1474         line_height;
1475
1476       wmfD_Coord
1477         ulBR,      /* bottom right of underline rectangle */
1478         ulTL;      /* top left of underline rectangle */
1479
1480       line_height = ((double)1/(ddata->scale_x))*metrics.underline_thickness;
1481       if (metrics.underline_thickness < 1.5)
1482         line_height *= 0.55;
1483       ulTL.x = 0;
1484       ulTL.y = fabs(metrics.descent) - line_height;
1485       ulBR.x = metrics.width;
1486       ulBR.y = fabs(metrics.descent);
1487
1488       DrawRectangle(WmfDrawingWand,
1489                     XC(ulTL.x), YC(ulTL.y), XC(ulBR.x), YC(ulBR.y));
1490     }
1491
1492   /* Strikeout text the Windows way */
1493   if (WMF_TEXT_STRIKEOUT(font))
1494     {
1495       double line_height;
1496
1497       wmfD_Coord
1498         ulBR,      /* bottom right of strikeout rectangle */
1499         ulTL;      /* top left of strikeout rectangle */
1500
1501       line_height = ((double)1/(ddata->scale_x))*metrics.underline_thickness;
1502
1503       if (metrics.underline_thickness < 2.0)
1504         line_height *= 0.55;
1505       ulTL.x = 0;
1506       ulTL.y = -(((double) metrics.ascent) / 2 + line_height / 2);
1507       ulBR.x = metrics.width;
1508       ulBR.y = -(((double) metrics.ascent) / 2 - line_height / 2);
1509
1510       DrawRectangle(WmfDrawingWand,
1511                     XC(ulTL.x), YC(ulTL.y), XC(ulBR.x), YC(ulBR.y));
1512
1513     }
1514
1515   /* Restore graphic wand */
1516   (void) PopDrawingWand(WmfDrawingWand);
1517
1518 #if 0
1519   (void) PushDrawingWand(WmfDrawingWand);
1520   draw_stroke_color_string(WmfDrawingWand,"red");
1521   draw_fill_color_string(WmfDrawingWand,"none");
1522   DrawRectangle(WmfDrawingWand,
1523                 XC(TL.x), YC(TL.y),
1524                 XC(BR.x), YC(BR.y));
1525   draw_stroke_color_string(WmfDrawingWand,"none");
1526   (void) PopDrawingWand(WmfDrawingWand);
1527 #endif
1528
1529 }
1530
1531 static void ipa_udata_init(wmfAPI * API, wmfUserData_t * userdata)
1532 {
1533   (void) API;
1534   (void) userdata;
1535   /* wmf_magick_t* ddata = WMF_MAGICK_GetData (API); */
1536
1537 }
1538
1539 static void ipa_udata_copy(wmfAPI * API, wmfUserData_t * userdata)
1540 {
1541   (void) API;
1542   (void) userdata;
1543   /* wmf_magick_t* ddata = WMF_MAGICK_GetData (API); */
1544
1545 }
1546
1547 static void ipa_udata_set(wmfAPI * API, wmfUserData_t * userdata)
1548 {
1549   (void) API;
1550   (void) userdata;
1551   /* wmf_magick_t* ddata = WMF_MAGICK_GetData (API); */
1552
1553 }
1554
1555 static void ipa_udata_free(wmfAPI * API, wmfUserData_t * userdata)
1556 {
1557   (void) API;
1558   (void) userdata;
1559   /* wmf_magick_t* ddata = WMF_MAGICK_GetData (API); */
1560
1561 }
1562
1563 static inline double MagickMin(const double x,const double y)
1564 {
1565   if (x < y)
1566     return(x);
1567   return(y);
1568 }
1569
1570 static void util_set_brush(wmfAPI * API, wmfDC * dc, const BrushApply brush_apply)
1571 {
1572   wmf_magick_t
1573     *ddata = WMF_MAGICK_GetData(API);
1574
1575   wmfBrush
1576     *brush = WMF_DC_BRUSH(dc);
1577
1578   /* Set polygon fill rule */
1579   switch (WMF_DC_POLYFILL(dc))  /* Is this correct ?? */
1580     {
1581     case WINDING:
1582       DrawSetClipRule(WmfDrawingWand,NonZeroRule);
1583       break;
1584
1585     case ALTERNATE:
1586     default:
1587       DrawSetClipRule(WmfDrawingWand,EvenOddRule);
1588       break;
1589     }
1590
1591   switch (WMF_BRUSH_STYLE(brush))
1592     {
1593     case BS_SOLID /* 0 */:
1594       /* WMF_BRUSH_COLOR specifies brush color, WMF_BRUSH_HATCH
1595          ignored */
1596       {
1597         if ( brush_apply == BrushApplyStroke )
1598           draw_stroke_color_rgb(API,WMF_BRUSH_COLOR(brush));
1599         else
1600           draw_fill_color_rgb(API,WMF_BRUSH_COLOR(brush));
1601         break;
1602       }
1603     case BS_HOLLOW /* 1 */:    /* BS_HOLLOW & BS_NULL share enum */
1604       /* WMF_BRUSH_COLOR and WMF_BRUSH_HATCH ignored */
1605       {
1606         if ( brush_apply == BrushApplyStroke )
1607           draw_stroke_color_string(WmfDrawingWand,"none");
1608         else
1609           draw_fill_color_string(WmfDrawingWand,"none");
1610         break;
1611       }
1612     case BS_HATCHED /* 2 */:
1613       /* WMF_BRUSH_COLOR specifies the hatch color, WMF_BRUSH_HATCH
1614          specifies the hatch brush style. If WMF_DC_OPAQUE, then
1615          WMF_DC_BACKGROUND specifies hatch background color.  */
1616       {
1617         DrawPushDefs(WmfDrawingWand);
1618         draw_pattern_push(API, ddata->pattern_id, 8, 8);
1619         (void) PushDrawingWand(WmfDrawingWand);
1620
1621         if (WMF_DC_OPAQUE(dc))
1622           {
1623             if ( brush_apply == BrushApplyStroke )
1624               draw_stroke_color_rgb(API,WMF_DC_BACKGROUND(dc));
1625             else
1626               draw_fill_color_rgb(API,WMF_DC_BACKGROUND(dc));
1627
1628             DrawRectangle(WmfDrawingWand, 0, 0, 7, 7 );
1629           }
1630
1631         DrawSetStrokeAntialias(WmfDrawingWand, MagickFalse);
1632         DrawSetStrokeWidth(WmfDrawingWand, 1);
1633
1634         draw_stroke_color_rgb(API,WMF_BRUSH_COLOR(brush));
1635
1636         switch ((unsigned int) WMF_BRUSH_HATCH(brush))
1637           {
1638
1639           case HS_HORIZONTAL:  /* ----- */
1640             {
1641               DrawLine(WmfDrawingWand, 0, 3, 7,3);
1642               break;
1643             }
1644           case HS_VERTICAL:  /* ||||| */
1645             {
1646               DrawLine(WmfDrawingWand, 3, 0, 3, 7);
1647               break;
1648             }
1649           case HS_FDIAGONAL:  /* \\\\\ */
1650             {
1651               DrawLine(WmfDrawingWand, 0, 0, 7, 7);
1652               break;
1653             }
1654           case HS_BDIAGONAL:  /* / */
1655             {
1656               DrawLine(WmfDrawingWand, 0, 7, 7, 0 );
1657               break;
1658             }
1659           case HS_CROSS:  /* +++++ */
1660             {
1661               DrawLine(WmfDrawingWand, 0, 3, 7, 3 );
1662               DrawLine(WmfDrawingWand, 3, 0, 3, 7 );
1663               break;
1664             }
1665           case HS_DIAGCROSS:  /* xxxxx */
1666             {
1667               DrawLine(WmfDrawingWand, 0, 0, 7, 7 );
1668               DrawLine(WmfDrawingWand, 0, 7, 7, 0 );
1669               break;
1670             }
1671           default:
1672             {
1673               printf("util_set_brush: unexpected brush hatch enumeration %u\n",
1674                      (unsigned int)WMF_BRUSH_HATCH(brush));
1675             }
1676           }
1677         (void) PopDrawingWand(WmfDrawingWand);
1678         (void) DrawPopPattern(WmfDrawingWand);
1679         DrawPopDefs(WmfDrawingWand);
1680         {
1681           char
1682             pattern_id[30];
1683
1684           (void) FormatMagickString(pattern_id,MaxTextExtent,"#brush_%lu",
1685             ddata->pattern_id);
1686           if (brush_apply == BrushApplyStroke )
1687             (void) DrawSetStrokePatternURL(WmfDrawingWand,pattern_id);
1688           else
1689             (void) DrawSetFillPatternURL(WmfDrawingWand,pattern_id);
1690           ++ddata->pattern_id;
1691         }
1692         break;
1693       }
1694     case BS_PATTERN /* 3 */:
1695       /* WMF_BRUSH_COLOR ignored, WMF_BRUSH_HATCH provides handle to
1696          bitmap */
1697       {
1698         printf("util_set_brush: BS_PATTERN not supported\n");
1699         break;
1700       }
1701     case BS_INDEXED /* 4 */:
1702       {
1703         printf("util_set_brush: BS_INDEXED not supported\n");
1704         break;
1705       }
1706     case BS_DIBPATTERN /* 5 */:
1707       {
1708         wmfBMP
1709           *brush_bmp = WMF_BRUSH_BITMAP(brush);
1710
1711         if (brush_bmp && brush_bmp->data != 0)
1712           {
1713             CompositeOperator
1714               mode;
1715
1716             const Image
1717               *image;
1718
1719             ExceptionInfo
1720               exception;
1721
1722             MagickWand
1723               *magick_wand;
1724
1725             GetExceptionInfo(&exception);
1726
1727             image = (Image*)brush_bmp->data;
1728
1729             mode = CopyCompositeOp;  /* Default is copy */
1730             switch (WMF_DC_ROP(dc))
1731               {
1732                 /* Binary raster ops */
1733               case R2_BLACK:
1734                 printf("util_set_brush: R2_BLACK ROP2 mode not supported!\n");
1735                 break;
1736               case R2_NOTMERGEPEN:
1737                 printf("util_set_brush: R2_NOTMERGEPEN ROP2 mode not supported!\n");
1738                 break;
1739               case R2_MASKNOTPEN:
1740                 printf("util_set_brush R2_MASKNOTPEN ROP2 mode not supported!\n");
1741                 break;
1742               case R2_NOTCOPYPEN:
1743                 printf("util_set_brush: R2_NOTCOPYPEN ROP2 mode not supported!\n");
1744                 break;
1745               case R2_MASKPENNOT:
1746                 printf("util_set_brush: R2_MASKPENNOT ROP2 mode not supported!\n");
1747                 break;
1748               case R2_NOT:
1749                 printf("util_set_brush: R2_NOT ROP2 mode not supported!\n");
1750                 break;
1751               case R2_XORPEN:
1752                 printf("util_set_brush: R2_XORPEN ROP2 mode not supported!\n");
1753                 break;
1754               case R2_NOTMASKPEN:
1755                 printf("util_set_brush: R2_NOTMASKPEN ROP2 mode not supported!\n");
1756                 break;
1757               case R2_MASKPEN:
1758                 printf("util_set_brush: R2_MASKPEN ROP2 mode not supported!\n");
1759                 break;
1760               case R2_NOTXORPEN:
1761                 printf("util_set_brush: R2_NOTXORPEN ROP2 mode not supported!\n");
1762                 break;
1763               case R2_NOP:
1764                 printf("util_set_brush: R2_NOP ROP2 mode not supported!\n");
1765                 break;
1766               case R2_MERGENOTPEN:
1767                 printf("util_set_brush: R2_MERGENOTPEN ROP2 mode not supported!\n");
1768                 break;
1769               case R2_COPYPEN:
1770                 mode = CopyCompositeOp;
1771                 break;
1772               case R2_MERGEPENNOT:
1773                 printf("util_set_brush: R2_MERGEPENNOT ROP2 mode not supported!\n");
1774                 break;
1775               case R2_MERGEPEN:
1776                 printf("util_set_brush: R2_MERGEPEN ROP2 mode not supported!\n");
1777                 break;
1778               case R2_WHITE:
1779                 printf("util_set_brush: R2_WHITE ROP2 mode not supported!\n");
1780                 break;
1781               default:
1782                 {
1783                   printf("util_set_brush: unexpected ROP2 enumeration %u!\n",
1784                          (unsigned int)WMF_DC_ROP(dc));
1785                 }
1786               }
1787
1788             DrawPushDefs(WmfDrawingWand);
1789             draw_pattern_push(API, ddata->pattern_id, brush_bmp->width,
1790               brush_bmp->height);
1791             magick_wand=NewMagickWandFromImage(image);
1792             (void) DrawComposite(WmfDrawingWand,mode, 0, 0, brush_bmp->width,
1793               brush_bmp->height, magick_wand);
1794             magick_wand=DestroyMagickWand(magick_wand);
1795             (void) DrawPopPattern(WmfDrawingWand);
1796             DrawPopDefs(WmfDrawingWand);
1797
1798             {
1799               char
1800                 pattern_id[30];
1801
1802               (void) FormatMagickString(pattern_id,MaxTextExtent,"#brush_%lu",
1803                 ddata->pattern_id);
1804
1805               if ( brush_apply == BrushApplyStroke )
1806                 (void) DrawSetStrokePatternURL(WmfDrawingWand,pattern_id);
1807               else
1808                 (void) DrawSetFillPatternURL(WmfDrawingWand,pattern_id);
1809               ++ddata->pattern_id;
1810             }
1811           }
1812         else
1813           printf("util_set_brush: no BMP image data!\n");
1814
1815         break;
1816       }
1817     case BS_DIBPATTERNPT /* 6 */:
1818       /* WMF_BRUSH_COLOR ignored, WMF_BRUSH_HATCH provides pointer to
1819          DIB */
1820       {
1821         printf("util_set_brush: BS_DIBPATTERNPT not supported\n");
1822         break;
1823       }
1824     case BS_PATTERN8X8 /* 7 */:
1825       {
1826         printf("util_set_brush: BS_PATTERN8X8 not supported\n");
1827         break;
1828       }
1829     case BS_DIBPATTERN8X8 /* 8 */:
1830       {
1831         printf("util_set_brush: BS_DIBPATTERN8X8 not supported\n");
1832         break;
1833       }
1834     default:
1835       {
1836       }
1837     }
1838 }
1839
1840 static inline double MagickMax(const double x,const double y)
1841 {
1842   if (x > y)
1843     return(x);
1844   return(y);
1845 }
1846
1847 static void util_set_pen(wmfAPI * API, wmfDC * dc)
1848 {
1849   wmf_magick_t
1850     *ddata = WMF_MAGICK_GetData(API);
1851
1852   wmfPen
1853     *pen = 0;
1854
1855   double
1856     pen_width,
1857     pixel_width;
1858
1859   unsigned int
1860     pen_style,
1861     pen_type;
1862
1863   pen = WMF_DC_PEN(dc);
1864
1865   pen_width = (WMF_PEN_WIDTH(pen) + WMF_PEN_HEIGHT(pen)) / 2;
1866
1867   /* Pixel width is inverse of pixel scale */
1868   pixel_width = (((double) 1 / (ddata->scale_x)) +
1869                  ((double) 1 / (ddata->scale_y))) / 2;
1870
1871   /* Don't allow pen_width to be much less than pixel_width in order
1872      to avoid dissapearing or spider-web lines */
1873   pen_width = MagickMax(pen_width, pixel_width*0.8);
1874
1875   pen_style = (unsigned int) WMF_PEN_STYLE(pen);
1876   pen_type = (unsigned int) WMF_PEN_TYPE(pen);
1877
1878   /* Pen style specified? */
1879   if (pen_style == PS_NULL)
1880     {
1881       draw_stroke_color_string(WmfDrawingWand,"none");
1882       return;
1883     }
1884
1885   DrawSetStrokeAntialias(WmfDrawingWand, MagickTrue );
1886   DrawSetStrokeWidth(WmfDrawingWand, (unsigned long) MagickMax(0.0, pen_width));
1887
1888   {
1889     LineCap
1890       linecap;
1891
1892     switch ((unsigned int) WMF_PEN_ENDCAP(pen))
1893       {
1894       case PS_ENDCAP_SQUARE:
1895         linecap = SquareCap;
1896         break;
1897       case PS_ENDCAP_ROUND:
1898         linecap = RoundCap;
1899         break;
1900       case PS_ENDCAP_FLAT:
1901       default:
1902         linecap = ButtCap;
1903         break;
1904       }
1905     DrawSetStrokeLineCap(WmfDrawingWand, linecap);
1906   }
1907
1908   {
1909     LineJoin
1910       linejoin;
1911
1912     switch ((unsigned int) WMF_PEN_JOIN(pen))
1913       {
1914       case PS_JOIN_BEVEL:
1915         linejoin = BevelJoin;
1916         break;
1917       case PS_JOIN_ROUND:
1918         linejoin = RoundJoin;
1919         break;
1920       case PS_JOIN_MITER:
1921       default:
1922         linejoin = MiterJoin;
1923         break;
1924       }
1925     DrawSetStrokeLineJoin(WmfDrawingWand,linejoin);
1926   }
1927
1928   {
1929     double
1930       dasharray[7];
1931
1932     switch (pen_style)
1933       {
1934       case PS_DASH:    /* -------  */
1935         {
1936           /* Pattern 18,7 */
1937           dasharray[0] = pixel_width * 18;
1938           dasharray[1] = pixel_width * 7;
1939           dasharray[2] = 0;
1940
1941           DrawSetStrokeAntialias(WmfDrawingWand,MagickFalse);
1942           (void) DrawSetStrokeDashArray(WmfDrawingWand,2,dasharray);
1943           break;
1944         }
1945       case PS_ALTERNATE:
1946       case PS_DOT:    /* .......  */
1947         {
1948           /* Pattern 3,3 */
1949           dasharray[0] = pixel_width * 3;
1950           dasharray[1] = pixel_width * 3;
1951           dasharray[2] = 0;
1952
1953           DrawSetStrokeAntialias(WmfDrawingWand,MagickFalse);
1954           (void) DrawSetStrokeDashArray(WmfDrawingWand,2,dasharray);
1955           break;
1956         }
1957       case PS_DASHDOT:    /* _._._._  */
1958         {
1959           /* Pattern 9,6,3,6 */
1960           dasharray[0] = pixel_width * 9;
1961           dasharray[1] = pixel_width * 6;
1962           dasharray[2] = pixel_width * 3;
1963           dasharray[3] = pixel_width * 6;
1964           dasharray[4] = 0;
1965
1966           DrawSetStrokeAntialias(WmfDrawingWand,MagickFalse);
1967           (void) DrawSetStrokeDashArray(WmfDrawingWand,4,dasharray);
1968           break;
1969         }
1970       case PS_DASHDOTDOT:  /* _.._.._  */
1971         {
1972           /* Pattern 9,3,3,3,3,3 */
1973           dasharray[0] = pixel_width * 9;
1974           dasharray[1] = pixel_width * 3;
1975           dasharray[2] = pixel_width * 3;
1976           dasharray[3] = pixel_width * 3;
1977           dasharray[4] = pixel_width * 3;
1978           dasharray[5] = pixel_width * 3;
1979           dasharray[6] = 0;
1980
1981           DrawSetStrokeAntialias(WmfDrawingWand,MagickFalse);
1982           (void) DrawSetStrokeDashArray(WmfDrawingWand,6,dasharray);
1983           break;
1984         }
1985       case PS_INSIDEFRAME:  /* There is nothing to do in this case... */
1986       case PS_SOLID:
1987       default:
1988         {
1989           (void) DrawSetStrokeDashArray(WmfDrawingWand,0,(double *)NULL);
1990           break;
1991         }
1992       }
1993   }
1994
1995   draw_stroke_color_rgb(API,WMF_PEN_COLOR(pen));
1996 }
1997
1998 /* Estimate font pointsize based on Windows font parameters */
1999 static double util_pointsize( wmfAPI* API, wmfFont* font, char* str, double font_height)
2000 {
2001   wmf_magick_t
2002     *ddata = WMF_MAGICK_GetData(API);
2003
2004   Image
2005     *image = ddata->image;
2006
2007   TypeMetric
2008     metrics;
2009
2010   DrawInfo
2011     *draw_info;
2012
2013   double
2014     pointsize = 0;
2015
2016   draw_info=ddata->draw_info;
2017   if (draw_info == (const DrawInfo *) NULL)
2018     return 0;
2019
2020   draw_info->font=WMF_FONT_PSNAME(font);
2021   draw_info->pointsize=font_height;
2022   draw_info->text=str;
2023
2024   if (GetTypeMetrics(image, draw_info, &metrics) != MagickFalse)
2025     {
2026
2027       if (strlen(str) == 1)
2028         {
2029           pointsize = (font_height *
2030                        ( font_height / (metrics.ascent + fabs(metrics.descent))));
2031           draw_info->pointsize = pointsize;
2032           if (GetTypeMetrics(image, draw_info, &metrics) != MagickFalse)
2033             pointsize *= (font_height / ( metrics.ascent + fabs(metrics.descent)));
2034         }
2035       else
2036         {
2037           pointsize = (font_height * (font_height / (metrics.height)));
2038           draw_info->pointsize = pointsize;
2039           if (GetTypeMetrics(image, draw_info, &metrics) != MagickFalse)
2040             pointsize *= (font_height / metrics.height);
2041
2042         }
2043 #if 0
2044       draw_info.pointsize = pointsize;
2045       if (GetTypeMetrics(image, &draw_info, &metrics) != MagickFalse)
2046         pointsize *= (font_height / (metrics.ascent + fabs(metrics.descent)));
2047       pointsize *= 1.114286; /* Magic number computed through trial and error */
2048 #endif
2049     }
2050
2051   draw_info->font=NULL;
2052   draw_info->text=NULL;
2053 #if 0
2054   printf("String    = %s\n", str);
2055   printf("Font      = %s\n", WMF_FONT_PSNAME(font));
2056   printf("lfHeight  = %g\n", font_height);
2057   printf("bounds    = %g,%g %g,%g\n", metrics.bounds.x1, metrics.bounds.y1,
2058          metrics.bounds.x2,metrics.bounds.y2);
2059   printf("ascent    = %g\n", metrics.ascent);
2060   printf("descent   = %g\n", metrics.descent);
2061   printf("height    = %g\n", metrics.height);
2062   printf("Pointsize = %g\n", pointsize);
2063 #endif
2064
2065   return floor(pointsize);
2066 }
2067
2068 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
2069 /* Estimate weight based on font name */
2070 /*
2071 static int util_font_weight( const char* font )
2072 {
2073   int
2074     weight;
2075
2076   weight = 400;
2077   if ((strstr(font,"Normal") || strstr(font,"Regular")))
2078     weight = 400;
2079   else if ( strstr(font,"Bold") )
2080     {
2081       weight = 700;
2082       if ((strstr(font,"Semi") || strstr(font,"Demi")))
2083         weight = 600;
2084       if ( (strstr(font,"Extra") || strstr(font,"Ultra")))
2085         weight = 800;
2086     }
2087   else if ( strstr(font,"Light") )
2088     {
2089       weight = 300;
2090       if ( (strstr(font,"Extra") || strstr(font,"Ultra")))
2091         weight = 200;
2092     }
2093   else if ((strstr(font,"Heavy") || strstr(font,"Black")))
2094     weight = 900;
2095   else if ( strstr(font,"Thin") )
2096     weight = 100;
2097   return weight;
2098 }
2099 */
2100
2101 /*
2102  * Returns width of string in points, assuming (unstretched) font size of 1pt
2103  * (similar to wmf_ipa_font_stringwidth)
2104  *
2105  * This extremely odd at best, particularly since player/meta.h has access
2106  * to the corrected font_height (as drawtext.font_height) when it invokes the
2107  * stringwidth callback.  It should be possible to compute the real stringwidth!
2108  */
2109 static float lite_font_stringwidth( wmfAPI* API, wmfFont* font, char* str)
2110 {
2111   wmf_magick_t
2112     *ddata = WMF_MAGICK_GetData(API);
2113
2114   Image
2115     *image = ddata->image;
2116
2117   DrawInfo
2118     *draw_info;
2119
2120   TypeMetric
2121     metrics;
2122
2123   float
2124     stringwidth = 0;
2125
2126   double
2127     orig_x_resolution,
2128     orig_y_resolution;
2129
2130   ResolutionType
2131     orig_resolution_units;
2132
2133   orig_x_resolution = image->x_resolution;
2134   orig_y_resolution = image->y_resolution;
2135   orig_resolution_units = image->units;
2136
2137   draw_info=ddata->draw_info;
2138   if (draw_info == (const DrawInfo *) NULL)
2139     return 0;
2140
2141   draw_info->font=WMF_FONT_PSNAME(font);
2142   draw_info->pointsize=12;
2143   draw_info->text=str;
2144
2145   image->x_resolution = 72;
2146   image->y_resolution = 72;
2147   image->units = PixelsPerInchResolution;
2148
2149   if (GetTypeMetrics(image, draw_info, &metrics) != MagickFalse)
2150     stringwidth = ((metrics.width * 72)/(image->x_resolution * draw_info->pointsize)); /* *0.916348; */
2151
2152   draw_info->font=NULL;
2153   draw_info->text=NULL;
2154
2155 #if 0
2156   printf("\nlite_font_stringwidth\n");
2157   printf("string                  = \"%s\"\n", str);
2158   printf("WMF_FONT_NAME           = \"%s\"\n", WMF_FONT_NAME(font));
2159   printf("WMF_FONT_PSNAME         = \"%s\"\n", WMF_FONT_PSNAME(font));
2160   printf("stringwidth             = %g\n", stringwidth);
2161   /* printf("WMF_FONT_HEIGHT         = %i\n", (int)WMF_FONT_HEIGHT(font)); */
2162   /* printf("WMF_FONT_WIDTH          = %i\n", (int)WMF_FONT_WIDTH(font)); */
2163   fflush(stdout);
2164 #endif
2165
2166   image->x_resolution = orig_x_resolution;
2167   image->y_resolution = orig_y_resolution;
2168   image->units = orig_resolution_units;
2169
2170   return stringwidth;
2171 }
2172
2173 /* Map font (similar to wmf_ipa_font_map) */
2174
2175 /* Mappings to Postscript fonts: family, normal, italic, bold, bolditalic */
2176 static wmfFontMap WMFFontMap[] = {
2177   { (char *) "Courier",            (char *) "Courier",     (char *) "Courier-Oblique",   (char *) "Courier-Bold", (char *) "Courier-BoldOblique"   },   { (char *) "Helvetica",          (char *) "Helvetica",   (char *) "Helvetica-Oblique", (char *) "Helvetica-Bold", (char *) "Helvetica-BoldOblique" },   { (char *) "Modern",             (char *) "Courier",     (char *) "Courier-Oblique",   (char *) "Courier-Bold", (char *) "Courier-BoldOblique"   },   { (char *) "Monotype Corsiva",   (char *) "Courier",     (char *) "Courier-Oblique",   (char *) "Courier-Bold", (char *) "Courier-BoldOblique"   },
2178   { (char *) "News Gothic",        (char *) "Helvetica",   (char *) "Helvetica-Oblique", (char *) "Helvetica-Bold", (char *) "Helvetica-BoldOblique" },
2179   { (char *) "Symbol",             (char *) "Symbol",      (char *) "Symbol",            (char *) "Symbol", (char *) "Symbol"                },
2180   { (char *) "System",             (char *) "Courier",     (char *) "Courier-Oblique",   (char *) "Courier-Bold", (char *) "Courier-BoldOblique"   },
2181   { (char *) "Times",              (char *) "Times-Roman", (char *) "Times-Italic",      (char *) "Times-Bold", (char *) "Times-BoldItalic"      },
2182   {  (char *) NULL,                (char *) NULL,           (char *) NULL,               (char *) NULL,  (char *) NULL                   }
2183 };
2184
2185
2186 /* Mapping between base name and Ghostscript family name */
2187 static wmfMapping SubFontMap[] =
2188 {
2189   { (char *) "Arial", (char *) "Helvetica", FT_ENCODING_NONE },
2190   { (char *) "Courier", (char *) "Courier", FT_ENCODING_NONE },
2191   { (char *) "Fixed", (char *) "Courier", FT_ENCODING_NONE },
2192   { (char *) "Helvetica", (char *) "Helvetica", FT_ENCODING_NONE },
2193   { (char *) "Sans", (char *) "Helvetica", FT_ENCODING_NONE },
2194   { (char *) "Sym", (char *) "Symbol", FT_ENCODING_NONE },
2195   { (char *) "Terminal", (char *) "Courier", FT_ENCODING_NONE },
2196   { (char *) "Times", (char *) "Times", FT_ENCODING_NONE },
2197   { (char *) "Wingdings", (char *) "Symbol", FT_ENCODING_NONE },
2198   { (char *)  NULL, (char *) NULL, FT_ENCODING_NONE }
2199 };
2200
2201 static void lite_font_map( wmfAPI* API, wmfFont* font)
2202 {
2203   wmfFontData
2204     *font_data;
2205
2206   wmf_magick_font_t
2207     *magick_font;
2208
2209   wmf_magick_t
2210     *ddata = WMF_MAGICK_GetData(API);
2211
2212   ExceptionInfo
2213     exception;
2214
2215   const TypeInfo
2216     *type_info,
2217     *type_info_base;
2218
2219   const char
2220     *wmf_font_name;
2221
2222   if (font == 0)
2223     return;
2224
2225   font_data = (wmfFontData*)API->font_data;
2226   font->user_data = font_data->user_data;
2227   magick_font = (wmf_magick_font_t*)font->user_data;
2228   wmf_font_name = WMF_FONT_NAME(font);
2229
2230   if (magick_font->ps_name != (char *) NULL)
2231     magick_font->ps_name=DestroyString(magick_font->ps_name);
2232
2233   GetExceptionInfo(&exception);
2234   type_info_base=GetTypeInfo("*",&exception);
2235   if (type_info_base == 0)
2236     {
2237       InheritException(&ddata->image->exception,&exception);
2238       return;
2239     }
2240
2241   /* Certain short-hand font names are not the proper Windows names
2242      and should be promoted to the proper names */
2243   if (LocaleCompare(wmf_font_name,"Times") == 0)
2244     wmf_font_name = "Times New Roman";
2245   else if (LocaleCompare(wmf_font_name,"Courier") == 0)
2246     wmf_font_name = "Courier New";
2247
2248   /* Look for a family-based best-match */
2249   if (!magick_font->ps_name)
2250     {
2251       int
2252         target_weight;
2253
2254       if (WMF_FONT_WEIGHT(font) == 0)
2255         target_weight = 400;
2256       else
2257         target_weight = WMF_FONT_WEIGHT(font);
2258       type_info=GetTypeInfoByFamily(wmf_font_name,AnyStyle,AnyStretch,
2259         target_weight,&exception);
2260       if (type_info == (const TypeInfo *) NULL)
2261         type_info=GetTypeInfoByFamily(wmf_font_name,AnyStyle,AnyStretch,0,
2262           &exception);
2263       if (type_info != (const TypeInfo *) NULL)
2264         CloneString(&magick_font->ps_name,type_info->name);
2265     }
2266
2267   /* Now let's try simple substitution mappings from WMFFontMap */
2268   if (!magick_font->ps_name)
2269     {
2270       char
2271         target[MaxTextExtent];
2272
2273       int
2274         target_weight = 400,
2275         want_italic = MagickFalse,
2276         want_bold = MagickFalse,
2277         i;
2278
2279       if ( WMF_FONT_WEIGHT(font) != 0 )
2280         target_weight = WMF_FONT_WEIGHT(font);
2281
2282       if ( (target_weight > 550) || ((strstr(wmf_font_name,"Bold") ||
2283                                      strstr(wmf_font_name,"Heavy") ||
2284                                      strstr(wmf_font_name,"Black"))) )
2285         want_bold = MagickTrue;
2286
2287       if ( (WMF_FONT_ITALIC(font)) || ((strstr(wmf_font_name,"Italic") ||
2288                                        strstr(wmf_font_name,"Oblique"))) )
2289         want_italic = MagickTrue;
2290
2291       (void) CopyMagickString(target,"Times",MaxTextExtent);
2292       for( i=0; SubFontMap[i].name != NULL; i++ )
2293         {
2294           if (LocaleCompare(wmf_font_name, SubFontMap[i].name) == 0)
2295             {
2296               (void) CopyMagickString(target,SubFontMap[i].mapping,
2297                 MaxTextExtent);
2298               break;
2299             }
2300         }
2301
2302       for( i=0; WMFFontMap[i].name != NULL; i++ )
2303         {
2304           if (LocaleNCompare(WMFFontMap[i].name,target,strlen(WMFFontMap[i].name)) == 0)
2305             {
2306               if (want_bold && want_italic)
2307                 CloneString(&magick_font->ps_name,WMFFontMap[i].bolditalic);
2308               else if (want_italic)
2309                 CloneString(&magick_font->ps_name,WMFFontMap[i].italic);
2310               else if (want_bold)
2311                 CloneString(&magick_font->ps_name,WMFFontMap[i].bold);
2312               else
2313                 CloneString(&magick_font->ps_name,WMFFontMap[i].normal);
2314             }
2315         }
2316     }
2317
2318 #if 0
2319   printf("\nlite_font_map\n");
2320   printf("WMF_FONT_NAME           = \"%s\"\n", WMF_FONT_NAME(font));
2321   printf("WMF_FONT_WEIGHT         = %i\n",  WMF_FONT_WEIGHT(font));
2322   printf("WMF_FONT_PSNAME         = \"%s\"\n", WMF_FONT_PSNAME(font));
2323   fflush(stdout);
2324 #endif
2325
2326 }
2327
2328 /* Initialize API font structures */
2329 static void lite_font_init( wmfAPI* API, wmfAPI_Options* options)
2330 {
2331   wmfFontData
2332     *font_data;
2333
2334   (void) options;
2335   API->fonts = 0;
2336
2337   /* Allocate wmfFontData data structure */
2338   API->font_data = wmf_malloc(API,sizeof(wmfFontData));
2339   if (ERR (API))
2340     return;
2341
2342   font_data = (wmfFontData*)API->font_data;
2343
2344   /* Assign function to map font (type wmfMap) */
2345   font_data->map = lite_font_map;
2346
2347   /* Assign function to return string width in points (type wmfStringWidth) */
2348   font_data->stringwidth = lite_font_stringwidth;
2349
2350   /* Assign user data, not used by libwmflite (type void*) */
2351   font_data->user_data = wmf_malloc(API,sizeof(wmf_magick_font_t));
2352   if (ERR(API))
2353     return;
2354   ((wmf_magick_font_t*)font_data->user_data)->ps_name = 0;
2355   ((wmf_magick_font_t*)font_data->user_data)->pointsize = 0;
2356 }
2357
2358 #endif /* MAGICKCORE_WMFLITE_DELEGATE */
2359
2360 /* BLOB read byte */
2361 static int ipa_blob_read(void* wand)
2362 {
2363   return ReadBlobByte((Image*)wand);
2364 }
2365
2366 /* BLOB seek */
2367 static int ipa_blob_seek(void* wand,long position)
2368 {
2369   return (int)SeekBlob((Image*)wand,(MagickOffsetType) position,SEEK_SET);
2370 }
2371
2372 /* BLOB tell */
2373 static long ipa_blob_tell(void* wand)
2374 {
2375   return (long)TellBlob((Image*)wand);
2376 }
2377
2378 static Image *ReadWMFImage(const ImageInfo * image_info, ExceptionInfo * exception)
2379 {
2380   Image
2381     *image;
2382
2383   float
2384     wmf_width,
2385     wmf_height;
2386
2387   double
2388     bounding_height,
2389     bounding_width,
2390     image_height,
2391     image_height_inch,
2392     image_width,
2393     image_width_inch,
2394     resolution_y,
2395     resolution_x,
2396     units_per_inch;
2397
2398   unsigned long
2399     wmf_options_flags = 0;
2400
2401   wmf_error_t
2402     wmf_error;
2403
2404   wmf_magick_t
2405     *ddata = 0;
2406
2407   wmfAPI
2408     *API = 0;
2409
2410   wmfAPI_Options
2411     wmf_api_options;
2412
2413   wmfD_Rect
2414     bbox;
2415
2416   image = AcquireImage(image_info);
2417   if (OpenBlob(image_info,image,ReadBinaryBlobMode,exception) == MagickFalse)
2418     {
2419       if (image->debug != MagickFalse)
2420         {
2421           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2422             "  OpenBlob failed");
2423           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2424             "leave ReadWMFImage()");
2425         }
2426       image=DestroyImageList(image);
2427       return((Image *) NULL);
2428     }
2429
2430   /*
2431    * Create WMF API
2432    *
2433    */
2434
2435   /* Register callbacks */
2436   wmf_options_flags |= WMF_OPT_FUNCTION;
2437   (void) ResetMagickMemory(&wmf_api_options, 0, sizeof(wmf_api_options));
2438   wmf_api_options.function = ipa_functions;
2439
2440   /* Ignore non-fatal errors */
2441   wmf_options_flags |= WMF_OPT_IGNORE_NONFATAL;
2442
2443   wmf_error = wmf_api_create(&API, wmf_options_flags, &wmf_api_options);
2444   if (wmf_error != wmf_E_None)
2445     {
2446       if (API)
2447         wmf_api_destroy(API);
2448       if (image->debug != MagickFalse)
2449         {
2450           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2451             "  wmf_api_create failed");
2452           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2453             "leave ReadWMFImage()");
2454         }
2455       ThrowReaderException(DelegateError,"UnableToInitializeWMFLibrary");
2456     }
2457
2458   /* Register progress monitor */
2459   wmf_status_function(API,image,magick_progress_callback);
2460
2461   ddata = WMF_MAGICK_GetData(API);
2462   ddata->image = image;
2463   ddata->image_info = image_info;
2464   ddata->draw_info = CloneDrawInfo((const ImageInfo *) NULL, (const DrawInfo *) NULL);
2465   RelinquishMagickMemory(ddata->draw_info->font);
2466   RelinquishMagickMemory(ddata->draw_info->text);
2467
2468
2469 #if defined(MAGICKCORE_WMFLITE_DELEGATE)
2470   /* Must initialize font subystem for WMFlite interface */
2471   lite_font_init (API,&wmf_api_options); /* similar to wmf_ipa_font_init in src/font.c */
2472   /* wmf_arg_fontdirs (API,options); */ /* similar to wmf_arg_fontdirs in src/wmf.c */
2473
2474 #endif
2475
2476   /*
2477    * Open BLOB input via libwmf API
2478    *
2479    */
2480   wmf_error = wmf_bbuf_input(API,ipa_blob_read,ipa_blob_seek,
2481                              ipa_blob_tell,(void*)image);
2482   if (wmf_error != wmf_E_None)
2483     {
2484       wmf_api_destroy(API);
2485       if (image->debug != MagickFalse)
2486         {
2487           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2488             "  wmf_bbuf_input failed");
2489           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2490             "leave ReadWMFImage()");
2491         }
2492       ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
2493         image->filename);
2494       image=DestroyImageList(image);
2495       return((Image *) NULL);
2496     }
2497
2498   /*
2499    * Scan WMF file
2500    *
2501    */
2502   if (image->debug != MagickFalse)
2503     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2504       "  Scanning WMF to obtain bounding box");
2505   wmf_error = wmf_scan(API, 0, &bbox);
2506   if (wmf_error != wmf_E_None)
2507     {
2508       wmf_api_destroy(API);
2509       if (image->debug != MagickFalse)
2510         {
2511           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512             "  wmf_scan failed with wmf_error %d", wmf_error);
2513           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2514             "leave ReadWMFImage()");
2515         }
2516       ThrowReaderException(DelegateError,"FailedToScanFile");
2517     }
2518
2519   /*
2520    * Compute dimensions and scale factors
2521    *
2522    */
2523
2524   ddata->bbox = bbox;
2525
2526   /* User specified resolution */
2527   resolution_y=DefaultResolution;
2528   if (image->y_resolution > 0)
2529     {
2530       resolution_y = image->y_resolution;
2531       if (image->units == PixelsPerCentimeterResolution)
2532         resolution_y *= CENTIMETERS_PER_INCH;
2533     }
2534
2535   resolution_x=DefaultResolution;
2536   if (image->x_resolution > 0)
2537     {
2538       resolution_x = image->x_resolution;
2539       if (image->units == PixelsPerCentimeterResolution)
2540         resolution_x *= CENTIMETERS_PER_INCH;
2541     }
2542
2543   /* Obtain output size expressed in metafile units */
2544   wmf_error = wmf_size(API, &wmf_width, &wmf_height);
2545   if (wmf_error != wmf_E_None)
2546     {
2547       wmf_api_destroy(API);
2548       if (image->debug != MagickFalse)
2549         {
2550           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551             "  wmf_size failed with wmf_error %d", wmf_error);
2552           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2553             "leave ReadWMFImage()");
2554         }
2555       ThrowReaderException(DelegateError,"FailedToComputeOutputSize");
2556     }
2557
2558   /* Obtain (or guess) metafile units */
2559   if ((API)->File->placeable)
2560     units_per_inch = (API)->File->pmh->Inch;
2561   else if ( (wmf_width*wmf_height) < 1024*1024)
2562     units_per_inch = POINTS_PER_INCH;  /* MM_TEXT */
2563   else
2564     units_per_inch = TWIPS_PER_INCH;  /* MM_TWIPS */
2565
2566   /* Calculate image width and height based on specified DPI
2567      resolution */
2568   image_width_inch  = (double) wmf_width / units_per_inch;
2569   image_height_inch = (double) wmf_height / units_per_inch;
2570   image_width       = image_width_inch * resolution_x;
2571   image_height      = image_height_inch * resolution_y;
2572
2573   /* Compute bounding box scale factors and origin translations
2574    *
2575    * This all just a hack since libwmf does not currently seem to
2576    * provide the mapping between LOGICAL coordinates and DEVICE
2577    * coordinates. This mapping is necessary in order to know
2578    * where to place the logical bounding box within the image.
2579    *
2580    */
2581
2582   bounding_width  = bbox.BR.x - bbox.TL.x;
2583   bounding_height = bbox.BR.y - bbox.TL.y;
2584
2585   ddata->scale_x = image_width/bounding_width;
2586   ddata->translate_x = 0-bbox.TL.x;
2587   ddata->rotate = 0;
2588
2589   /* Heuristic: guess that if the vertical coordinates mostly span
2590      negative values, then the image must be inverted. */
2591   if ( fabs(bbox.BR.y) > fabs(bbox.TL.y) )
2592     {
2593       /* Normal (Origin at top left of image) */
2594       ddata->scale_y = (image_height/bounding_height);
2595       ddata->translate_y = 0-bbox.TL.y;
2596     }
2597   else
2598     {
2599       /* Inverted (Origin at bottom left of image) */
2600       ddata->scale_y = (-image_height/bounding_height);
2601       ddata->translate_y = 0-bbox.BR.y;
2602     }
2603
2604   if (image->debug != MagickFalse)
2605     {
2606       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2607          "  Placeable metafile:          %s",
2608          (API)->File->placeable ? "Yes" : "No");
2609
2610       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2611         "  Size in metafile units:      %gx%g",wmf_width,wmf_height);
2612       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2613         "  Metafile units/inch:         %g",units_per_inch);
2614       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615         "  Size in inches:              %gx%g",
2616         image_width_inch,image_height_inch);
2617       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2618         "  Bounding Box:                %g,%g %g,%g",
2619         bbox.TL.x, bbox.TL.y, bbox.BR.x, bbox.BR.y);
2620       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2621         "  Bounding width x height:     %gx%g",bounding_width,
2622         bounding_height);
2623       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2624         "  Output resolution:           %gx%g",resolution_x,resolution_y);
2625       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2626         "  Image size:                  %gx%g",image_width,image_height);
2627       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2628         "  Bounding box scale factor:   %g,%g",ddata->scale_x,
2629         ddata->scale_y);
2630       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2631         "  Translation:                 %g,%g",
2632         ddata->translate_x, ddata->translate_y);
2633     }
2634
2635 #if 0
2636 #if 0
2637   {
2638     typedef struct _wmfPlayer_t wmfPlayer_t;
2639     struct _wmfPlayer_t
2640     {
2641       wmfPen   default_pen;
2642       wmfBrush default_brush;
2643       wmfFont  default_font;
2644
2645       wmfDC* dc; /* current dc */
2646     };
2647
2648     wmfDC
2649       *dc;
2650
2651 #define WMF_ELICIT_DC(API) (((wmfPlayer_t*)((API)->player_data))->dc)
2652
2653     dc = WMF_ELICIT_DC(API);
2654
2655     printf("dc->Window.Ox     = %d\n", dc->Window.Ox);
2656     printf("dc->Window.Oy     = %d\n", dc->Window.Oy);
2657     printf("dc->Window.width  = %d\n", dc->Window.width);
2658     printf("dc->Window.height = %d\n", dc->Window.height);
2659     printf("dc->pixel_width   = %g\n", dc->pixel_width);
2660     printf("dc->pixel_height  = %g\n", dc->pixel_height);
2661 #if defined(MAGICKCORE_WMFLITE_DELEGATE)  /* Only in libwmf 0.3 */
2662     printf("dc->Ox            = %.d\n", dc->Ox);
2663     printf("dc->Oy            = %.d\n", dc->Oy);
2664     printf("dc->width         = %.d\n", dc->width);
2665     printf("dc->height        = %.d\n", dc->height);
2666 #endif
2667
2668   }
2669 #endif
2670
2671 #endif
2672
2673   /*
2674    * Create canvas image
2675    *
2676    */
2677   image->rows=(unsigned long) ceil(image_height);
2678   image->columns=(unsigned long) ceil(image_width);
2679
2680   if (image_info->ping != MagickFalse)
2681     {
2682       wmf_api_destroy(API);
2683       (void) CloseBlob(image);
2684       if (image->debug != MagickFalse)
2685         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2686           "leave ReadWMFImage()");
2687       return(GetFirstImageInList(image));
2688     }
2689   if (image->debug != MagickFalse)
2690     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2691        "  Creating canvas image with size %ldx%ld",
2692        image->rows,image->columns);
2693
2694   /*
2695    * Set solid background color
2696    */
2697   {
2698     unsigned long
2699       column,
2700       row;
2701
2702     PixelPacket
2703       *pixel,
2704       background_color;
2705
2706     background_color = image_info->background_color;
2707     image->background_color = background_color;
2708     if (background_color.opacity != OpaqueOpacity)
2709       image->matte = MagickTrue;
2710
2711     for (row=0; row < image->rows; row++)
2712       {
2713         pixel=QueueAuthenticPixels(image,0,row,image->columns,1,exception);
2714         if (pixel == (PixelPacket *) NULL)
2715           break;
2716         for (column=image->columns; column; column--)
2717           *pixel++ = background_color;
2718         if (SyncAuthenticPixels(image,exception) == MagickFalse)
2719           break;
2720       }
2721   }
2722   /*
2723    * Play file to generate Vector drawing commands
2724    *
2725    */
2726
2727   if (image->debug != MagickFalse)
2728     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2729       "  Playing WMF to prepare vectors");
2730
2731   wmf_error = wmf_play(API, 0, &bbox);
2732   if (wmf_error != wmf_E_None)
2733     {
2734       wmf_api_destroy(API);
2735       if (image->debug != MagickFalse)
2736         {
2737           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2738             "  Playing WMF failed with wmf_error %d", wmf_error);
2739           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2740             "leave ReadWMFImage()");
2741         }
2742       ThrowReaderException(DelegateError,"FailedToRenderFile");
2743     }
2744
2745   /*
2746    * Scribble on canvas image
2747    *
2748    */
2749
2750   if (image->debug != MagickFalse)
2751     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2752       "  Rendering WMF vectors");
2753   DrawRender(ddata->draw_wand);
2754
2755   /* Check for and report any rendering error */
2756   if (image->exception.severity != UndefinedException)
2757     (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2758       ddata->image->exception.reason,"`%s'",ddata->image->exception.description);
2759
2760   if (image->debug != MagickFalse)
2761     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"leave ReadWMFImage()");
2762
2763   /* Cleanup allocated data */
2764   wmf_api_destroy(API);
2765   (void) CloseBlob(image);
2766
2767   /* Return image */
2768   return image;
2769 }
2770 /* #endif */
2771 #endif /* MAGICKCORE_WMF_DELEGATE || MAGICKCORE_WMFLITE_DELEGATE */
2772 \f
2773 /*
2774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2775 %                                                                             %
2776 %                                                                             %
2777 %                                                                             %
2778 %   R e g i s t e r W M F I m a g e                                           %
2779 %                                                                             %
2780 %                                                                             %
2781 %                                                                             %
2782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2783 %
2784 %  RegisterWMFImage() adds attributes for the WMF image format to
2785 %  the list of supported formats.  The attributes include the image format
2786 %  tag, a method to read and/or write the format, whether the format
2787 %  supports the saving of more than one frame to the same file or blob,
2788 %  whether the format supports native in-memory I/O, and a brief
2789 %  description of the format.
2790 %
2791 %  The format of the RegisterWMFImage method is:
2792 %
2793 %      unsigned long RegisterWMFImage(void)
2794 %
2795 */
2796 ModuleExport unsigned long RegisterWMFImage(void)
2797 {
2798   MagickInfo
2799     *entry;
2800
2801   entry = SetMagickInfo("WMZ");
2802 #if defined(MAGICKCORE_WMF_DELEGATE) || defined(MAGICKCORE_WMFLITE_DELEGATE)
2803   entry->decoder=ReadWMFImage;
2804 #endif
2805   entry->description=ConstantString("Compressed Windows Meta File");
2806   entry->blob_support=MagickTrue;
2807   entry->seekable_stream=MagickTrue;
2808   entry->module=ConstantString("WMZ");
2809   (void) RegisterMagickInfo(entry);
2810   entry=SetMagickInfo("WMF");
2811 #if defined(MAGICKCORE_WMF_DELEGATE) || defined(MAGICKCORE_WMFLITE_DELEGATE)
2812   entry->decoder=ReadWMFImage;
2813 #endif
2814   entry->description=ConstantString("Windows Meta File");
2815   entry->blob_support=MagickTrue;
2816   entry->seekable_stream=MagickTrue;
2817   entry->module=ConstantString("WMF");
2818   (void) RegisterMagickInfo(entry);
2819   return(MagickImageCoderSignature);
2820 }
2821 \f
2822 /*
2823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2824 %                                                                             %
2825 %                                                                             %
2826 %                                                                             %
2827 %   U n r e g i s t e r W M F I m a g e                                       %
2828 %                                                                             %
2829 %                                                                             %
2830 %                                                                             %
2831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2832 %
2833 %  UnregisterWMFImage() removes format registrations made by the
2834 %  WMF module from the list of supported formats.
2835 %
2836 %  The format of the UnregisterWMFImage method is:
2837 %
2838 %      UnregisterWMFImage(void)
2839 %
2840 */
2841 ModuleExport void UnregisterWMFImage(void)
2842 {
2843   (void) UnregisterMagickInfo("WMZ");
2844   (void) UnregisterMagickInfo("WMF");
2845 }