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