2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read Windows Enahanced Metafile Format %
21 % Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39 * Include declarations.
42 #include "MagickCore/studio.h"
43 #if defined(MAGICKCORE_WINGDI32_DELEGATE)
44 # if !defined(_MSC_VER)
45 # if defined(__CYGWIN__)
51 #pragma warning(disable: 4457)
52 #pragma warning(disable: 4458)
54 #pragma warning(default: 4457)
55 #pragma warning(default: 4458)
56 # pragma comment(lib, "gdiplus.lib")
59 #include "MagickCore/blob.h"
60 #include "MagickCore/blob-private.h"
61 #include "MagickCore/cache.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/geometry.h"
65 #include "MagickCore/image.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/list.h"
68 #include "MagickCore/magick.h"
69 #include "MagickCore/memory_.h"
70 #include "MagickCore/pixel.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/quantum-private.h"
73 #include "MagickCore/static.h"
74 #include "MagickCore/string_.h"
75 #include "MagickCore/module.h"
78 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88 % IsEMF() returns MagickTrue if the image format type, identified by the
89 % magick string, is a Microsoft Windows Enhanced MetaFile (EMF) file.
91 % The format of the ReadEMFImage method is:
93 % MagickBooleanType IsEMF(const unsigned char *magick,const size_t length)
95 % A description of each parameter follows:
97 % o magick: compare image format pattern against these bytes.
99 % o length: Specifies the length of the magick string.
102 static MagickBooleanType IsEMF(const unsigned char *magick,const size_t length)
106 if (memcmp(magick+40,"\040\105\115\106\000\000\001\000",8) == 0)
112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 % IsWMF() returns MagickTrue if the image format type, identified by the
123 % magick string, is a Windows MetaFile (WMF) file.
125 % The format of the ReadEMFImage method is:
127 % MagickBooleanType IsEMF(const unsigned char *magick,const size_t length)
129 % A description of each parameter follows:
131 % o magick: compare image format pattern against these bytes.
133 % o length: Specifies the length of the magick string.
136 static MagickBooleanType IsWMF(const unsigned char *magick,const size_t length)
140 if (memcmp(magick,"\327\315\306\232",4) == 0)
142 if (memcmp(magick,"\001\000\011\000",4) == 0)
148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152 % R e a d E M F I m a g e %
156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158 % ReadEMFImage() reads an Microsoft Windows Enhanced MetaFile (EMF) or
159 % Windows MetaFile (WMF) file using the Windows API and returns it. It
160 % allocates the memory necessary for the new Image structure and returns a
161 % pointer to the new image.
163 % The format of the ReadEMFImage method is:
165 % Image *ReadEMFImage(const ImageInfo *image_info,
166 % ExceptionInfo *exception)
168 % A description of each parameter follows:
170 % o image_info: the image info..
172 % o exception: return any errors or warnings in this structure.
176 #if defined(MAGICKCORE_WINGDI32_DELEGATE)
177 # if !defined(_MSC_VER)
178 # if defined(MAGICKCORE_HAVE__WFOPEN)
179 static size_t UTF8ToUTF16(const unsigned char *utf8,wchar_t *utf16)
181 register const unsigned char
184 if (utf16 != (wchar_t *) NULL)
193 Convert UTF-8 to UTF-16.
196 for (p=utf8; *p != '\0'; p++)
198 if ((*p & 0x80) == 0)
201 if ((*p & 0xE0) == 0xC0)
206 if ((*p & 0xC0) != 0x80)
211 if ((*p & 0xF0) == 0xE0)
216 if ((*p & 0xC0) != 0x80)
221 if ((*p & 0xC0) != 0x80)
233 Compute UTF-16 string length.
235 for (p=utf8; *p != '\0'; p++)
237 if ((*p & 0x80) == 0)
240 if ((*p & 0xE0) == 0xC0)
243 if ((*p & 0xC0) != 0x80)
247 if ((*p & 0xF0) == 0xE0)
250 if ((*p & 0xC0) != 0x80)
253 if ((*p & 0xC0) != 0x80)
262 static wchar_t *ConvertUTF8ToUTF16(const unsigned char *source)
270 length=UTF8ToUTF16(source,(wchar_t *) NULL);
277 Not UTF-8, just copy.
279 length=strlen((char *) source);
280 utf16=(wchar_t *) AcquireQuantumMemory(length+1,sizeof(*utf16));
281 if (utf16 == (wchar_t *) NULL)
282 return((wchar_t *) NULL);
283 for (i=0; i <= (ssize_t) length; i++)
287 utf16=(wchar_t *) AcquireQuantumMemory(length+1,sizeof(*utf16));
288 if (utf16 == (wchar_t *) NULL)
289 return((wchar_t *) NULL);
290 length=UTF8ToUTF16(source,utf16);
293 # endif /* MAGICKCORE_HAVE__WFOPEN */
295 static HENHMETAFILE ReadEnhMetaFile(const char *path,ssize_t *width,
298 #pragma pack( push, 2 )
307 } APMHEADER, *PAPMHEADER;
336 hTemp=GetEnhMetaFile(path);
337 #if defined(MAGICKCORE_HAVE__WFOPEN)
338 if (hTemp == (HENHMETAFILE) NULL)
343 unicode_path=ConvertUTF8ToUTF16((const unsigned char *) path);
344 if (unicode_path != (wchar_t *) NULL)
346 hTemp=GetEnhMetaFileW(unicode_path);
347 unicode_path=(wchar_t *) RelinquishMagickMemory(unicode_path);
351 if (hTemp != (HENHMETAFILE) NULL)
356 GetEnhMetaFileHeader(hTemp,sizeof(ENHMETAHEADER),&emfh);
357 *width=emfh.rclFrame.right-emfh.rclFrame.left;
358 *height=emfh.rclFrame.bottom-emfh.rclFrame.top;
361 hOld=GetMetaFile(path);
362 if (hOld != (HMETAFILE) NULL)
365 16bit windows metafile.
367 dwSize=GetMetaFileBitsEx(hOld,0,NULL);
370 DeleteMetaFile(hOld);
371 return((HENHMETAFILE) NULL);
373 pBits=(LPBYTE) AcquireQuantumMemory(dwSize,sizeof(*pBits));
374 if (pBits == (LPBYTE) NULL)
376 DeleteMetaFile(hOld);
377 return((HENHMETAFILE) NULL);
379 if (GetMetaFileBitsEx(hOld,dwSize,pBits) == 0)
381 pBits=(BYTE *) DestroyString((char *) pBits);
382 DeleteMetaFile(hOld);
383 return((HENHMETAFILE) NULL);
386 Make an enhanced metafile from the windows metafile.
388 mp.mm=MM_ANISOTROPIC;
393 hTemp=SetWinMetaFileBits(dwSize,pBits,hDC,&mp);
395 DeleteMetaFile(hOld);
396 pBits=(BYTE *) DestroyString((char *) pBits);
397 GetEnhMetaFileHeader(hTemp,sizeof(ENHMETAHEADER),&emfh);
398 *width=emfh.rclFrame.right-emfh.rclFrame.left;
399 *height=emfh.rclFrame.bottom-emfh.rclFrame.top;
403 Aldus Placeable metafile.
405 hFile=CreateFile(path,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,
407 if (hFile == INVALID_HANDLE_VALUE)
409 dwSize=GetFileSize(hFile,NULL);
410 pBits=(LPBYTE) AcquireQuantumMemory(dwSize,sizeof(*pBits));
411 ReadFile(hFile,pBits,dwSize,&dwSize,NULL);
413 if (((PAPMHEADER) pBits)->dwKey != 0x9ac6cdd7l)
415 pBits=(BYTE *) DestroyString((char *) pBits);
416 return((HENHMETAFILE) NULL);
419 Make an enhanced metafile from the placable metafile.
421 mp.mm=MM_ANISOTROPIC;
422 mp.xExt=((PAPMHEADER) pBits)->bbox.Right-((PAPMHEADER) pBits)->bbox.Left;
424 mp.xExt=(mp.xExt*2540l)/(DWORD) (((PAPMHEADER) pBits)->wInch);
425 mp.yExt=((PAPMHEADER)pBits)->bbox.Bottom-((PAPMHEADER) pBits)->bbox.Top;
427 mp.yExt=(mp.yExt*2540l)/(DWORD) (((PAPMHEADER) pBits)->wInch);
430 hTemp=SetWinMetaFileBits(dwSize,&(pBits[sizeof(APMHEADER)]),hDC,&mp);
432 pBits=(BYTE *) DestroyString((char *) pBits);
436 #define CENTIMETERS_INCH 2.54
438 static Image *ReadEMFImage(const ImageInfo *image_info,ExceptionInfo *exception)
477 image=AcquireImage(image_info,exception);
478 hemf=ReadEnhMetaFile(image_info->filename,&width,&height);
479 if (hemf == (HENHMETAFILE) NULL)
480 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
481 if ((image->columns == 0) || (image->rows == 0))
487 y_resolution=DefaultResolution;
488 x_resolution=DefaultResolution;
489 if (image->resolution.y > 0)
491 y_resolution=image->resolution.y;
492 if (image->units == PixelsPerCentimeterResolution)
493 y_resolution*=CENTIMETERS_INCH;
495 if (image->resolution.x > 0)
497 x_resolution=image->resolution.x;
498 if (image->units == PixelsPerCentimeterResolution)
499 x_resolution*=CENTIMETERS_INCH;
501 image->rows=(size_t) ((height/1000.0/CENTIMETERS_INCH)*y_resolution+0.5);
502 image->columns=(size_t) ((width/1000.0/CENTIMETERS_INCH)*
505 if (image_info->size != (char *) NULL)
507 image->columns=width;
509 (void) GetGeometry(image_info->size,(ssize_t *) NULL,(ssize_t *) NULL,
510 &image->columns,&image->rows);
512 status=SetImageExtent(image,image->columns,image->rows,exception);
513 if (status == MagickFalse)
514 return(DestroyImageList(image));
515 if (image_info->page != (char *) NULL)
529 geometry=GetPageGeometry(image_info->page);
530 p=strchr(geometry,'>');
531 if (p == (char *) NULL)
533 flags=ParseMetaGeometry(geometry,&sans,&sans,&image->columns,
535 if (image->resolution.x != 0.0)
536 image->columns=(size_t) floor((image->columns*image->resolution.x)+
538 if (image->resolution.y != 0.0)
539 image->rows=(size_t) floor((image->rows*image->resolution.y)+0.5);
544 flags=ParseMetaGeometry(geometry,&sans,&sans,&image->columns,
546 if (image->resolution.x != 0.0)
547 image->columns=(size_t) floor(((image->columns*image->resolution.x)/
548 DefaultResolution)+0.5);
549 if (image->resolution.y != 0.0)
550 image->rows=(size_t) floor(((image->rows*image->resolution.y)/
551 DefaultResolution)+0.5);
554 geometry=DestroyString(geometry);
557 if (hDC == (HDC) NULL)
559 DeleteEnhMetaFile(hemf);
560 ThrowReaderException(ResourceLimitError,"UnableToCreateADC");
563 Initialize the bitmap header info.
565 (void) ResetMagickMemory(&DIBinfo,0,sizeof(BITMAPINFO));
566 DIBinfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
567 DIBinfo.bmiHeader.biWidth=(LONG) image->columns;
568 DIBinfo.bmiHeader.biHeight=(-1)*(LONG) image->rows;
569 DIBinfo.bmiHeader.biPlanes=1;
570 DIBinfo.bmiHeader.biBitCount=32;
571 DIBinfo.bmiHeader.biCompression=BI_RGB;
572 hBitmap=CreateDIBSection(hDC,&DIBinfo,DIB_RGB_COLORS,(void **) &ppBits,NULL,
575 if (hBitmap == (HBITMAP) NULL)
577 DeleteEnhMetaFile(hemf);
578 ThrowReaderException(ResourceLimitError,"UnableToCreateBitmap");
580 hDC=CreateCompatibleDC(NULL);
581 if (hDC == (HDC) NULL)
583 DeleteEnhMetaFile(hemf);
584 DeleteObject(hBitmap);
585 ThrowReaderException(ResourceLimitError,"UnableToCreateADC");
587 hOldBitmap=(HBITMAP) SelectObject(hDC,hBitmap);
588 if (hOldBitmap == (HBITMAP) NULL)
590 DeleteEnhMetaFile(hemf);
592 DeleteObject(hBitmap);
593 ThrowReaderException(ResourceLimitError,"UnableToCreateBitmap");
596 Initialize the bitmap to the image background color.
599 for (y=0; y < (ssize_t) image->rows; y++)
601 for (x=0; x < (ssize_t) image->columns; x++)
603 pBits->rgbRed=ScaleQuantumToChar(image->background_color.red);
604 pBits->rgbGreen=ScaleQuantumToChar(image->background_color.green);
605 pBits->rgbBlue=ScaleQuantumToChar(image->background_color.blue);
611 rect.right=(LONG) image->columns;
612 rect.bottom=(LONG) image->rows;
614 Convert metafile pixels.
616 PlayEnhMetaFile(hDC,hemf,&rect);
618 for (y=0; y < (ssize_t) image->rows; y++)
620 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
621 if (q == (Quantum *) NULL)
623 for (x=0; x < (ssize_t) image->columns; x++)
625 SetPixelRed(image,ScaleCharToQuantum(pBits->rgbRed),q);
626 SetPixelGreen(image,ScaleCharToQuantum(pBits->rgbGreen),q);
627 SetPixelBlue(image,ScaleCharToQuantum(pBits->rgbBlue),q);
628 SetPixelAlpha(image,OpaqueAlpha,q);
630 q+=GetPixelChannels(image);
632 if (SyncAuthenticPixels(image,exception) == MagickFalse)
635 DeleteEnhMetaFile(hemf);
636 SelectObject(hDC,hOldBitmap);
638 DeleteObject(hBitmap);
639 return(GetFirstImageInList(image));
643 static inline void EMFSetDimensions(Image * image,Gdiplus::Image *source)
645 if ((image->resolution.x <= 0.0) || (image->resolution.y <= 0.0))
648 image->columns=(size_t) floor((Gdiplus::REAL) source->GetWidth()/
649 source->GetHorizontalResolution()*image->resolution.x+0.5);
650 image->rows=(size_t)floor((Gdiplus::REAL) source->GetHeight()/
651 source->GetVerticalResolution()*image->resolution.y+0.5);
654 static Image *ReadEMFImage(const ImageInfo *image_info,
655 ExceptionInfo *exception)
663 Gdiplus::GdiplusStartupInput
700 fileName[MagickPathExtent];
702 assert(image_info != (const ImageInfo *) NULL);
703 assert(image_info->signature == MagickCoreSignature);
704 if (image_info->debug != MagickFalse)
705 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
706 image_info->filename);
707 assert(exception != (ExceptionInfo *) NULL);
709 image=AcquireImage(image_info,exception);
710 if (Gdiplus::GdiplusStartup(&token,&startup_input,NULL) !=
712 ThrowReaderException(CoderError, "GdiplusStartupFailed");
713 MultiByteToWideChar(CP_UTF8,0,image->filename,-1,fileName,MagickPathExtent);
714 source=Gdiplus::Image::FromFile(fileName);
715 if (source == (Gdiplus::Image *) NULL)
717 Gdiplus::GdiplusShutdown(token);
718 ThrowReaderException(FileOpenError,"UnableToOpenFile");
721 image->resolution.x=source->GetHorizontalResolution();
722 image->resolution.y=source->GetVerticalResolution();
723 image->columns=(size_t) source->GetWidth();
724 image->rows=(size_t) source->GetHeight();
725 if (image_info->size != (char *) NULL)
727 (void) GetGeometry(image_info->size,(ssize_t *) NULL,(ssize_t *) NULL,
728 &image->columns,&image->rows);
729 image->resolution.x=source->GetHorizontalResolution()*image->columns/
731 image->resolution.y=source->GetVerticalResolution()*image->rows/
733 if (image->resolution.x == 0)
734 image->resolution.x=image->resolution.y;
735 else if (image->resolution.y == 0)
736 image->resolution.y=image->resolution.x;
738 image->resolution.x=image->resolution.y=MagickMin(
739 image->resolution.x,image->resolution.y);
740 EMFSetDimensions(image,source);
742 else if (image_info->density != (char *) NULL)
744 flags=ParseGeometry(image_info->density,&geometry_info);
745 image->resolution.x=geometry_info.rho;
746 image->resolution.y=geometry_info.sigma;
747 if ((flags & SigmaValue) == 0)
748 image->resolution.y=image->resolution.x;
749 EMFSetDimensions(image,source);
751 if (SetImageExtent(image,image->columns,image->rows,exception) == MagickFalse)
754 Gdiplus::GdiplusShutdown(token);
755 return(DestroyImageList(image));
757 image->alpha_trait=BlendPixelTrait;
758 if (image->ping != MagickFalse)
761 Gdiplus::GdiplusShutdown(token);
765 bitmap=new Gdiplus::Bitmap((INT) image->columns,(INT) image->rows,
766 PixelFormat32bppARGB);
767 graphics=Gdiplus::Graphics::FromImage(bitmap);
768 graphics->SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
769 graphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
770 graphics->SetTextRenderingHint(Gdiplus::TextRenderingHintClearTypeGridFit);
771 graphics->Clear(Gdiplus::Color((BYTE) ScaleQuantumToChar(
772 image->background_color.alpha),(BYTE) ScaleQuantumToChar(
773 image->background_color.red),(BYTE) ScaleQuantumToChar(
774 image->background_color.green),(BYTE) ScaleQuantumToChar(
775 image->background_color.blue)));
776 graphics->DrawImage(source,0,0,(INT) image->columns,(INT) image->rows);
780 rect=Gdiplus::Rect(0,0,(INT) image->columns,(INT) image->rows);
781 if (bitmap->LockBits(&rect,Gdiplus::ImageLockModeRead,PixelFormat32bppARGB,
782 &bitmap_data) != Gdiplus::Ok)
785 Gdiplus::GdiplusShutdown(token);
786 ThrowReaderException(FileOpenError,"UnableToReadImageData");
789 for (y=0; y < (ssize_t) image->rows; y++)
791 p=(unsigned char *) bitmap_data.Scan0+(y*abs(bitmap_data.Stride));
792 if (bitmap_data.Stride < 0)
793 q=GetAuthenticPixels(image,0,image->rows-y-1,image->columns,1,exception);
795 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
796 if (q == (Quantum *) NULL)
799 for (x=0; x < (ssize_t) image->columns; x++)
801 SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
802 SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
803 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
804 SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
805 q+=GetPixelChannels(image);
808 if (SyncAuthenticPixels(image,exception) == MagickFalse)
812 bitmap->UnlockBits(&bitmap_data);
814 Gdiplus::GdiplusShutdown(token);
817 # endif /* _MSC_VER */
818 #endif /* MAGICKCORE_EMF_DELEGATE */
821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
825 % R e g i s t e r E M F I m a g e %
829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
831 % RegisterEMFImage() adds attributes for the EMF image format to
832 % the list of supported formats. The attributes include the image format
833 % tag, a method to read and/or write the format, whether the format
834 % supports the saving of more than one frame to the same file or blob,
835 % whether the format supports native in-memory I/O, and a brief
836 % description of the format.
838 % The format of the RegisterEMFImage method is:
840 % size_t RegisterEMFImage(void)
843 ModuleExport size_t RegisterEMFImage(void)
848 entry=AcquireMagickInfo("EMF","EMF","Windows Enhanced Meta File");
849 #if defined(MAGICKCORE_WINGDI32_DELEGATE)
850 entry->decoder=ReadEMFImage;
852 entry->magick=(IsImageFormatHandler *) IsEMF;
853 entry->flags^=CoderBlobSupportFlag;
854 (void) RegisterMagickInfo(entry);
855 entry=AcquireMagickInfo("EMF","WMF","Windows Meta File");
856 #if defined(MAGICKCORE_WINGDI32_DELEGATE)
857 entry->decoder=ReadEMFImage;
859 entry->magick=(IsImageFormatHandler *) IsWMF;
860 entry->flags^=CoderBlobSupportFlag;
861 (void) RegisterMagickInfo(entry);
862 return(MagickImageCoderSignature);
866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
870 % U n r e g i s t e r E M F I m a g e %
874 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
876 % UnregisterEMFImage() removes format registrations made by the
877 % EMF module from the list of supported formats.
879 % The format of the UnregisterEMFImage method is:
881 % UnregisterEMFImage(void)
884 ModuleExport void UnregisterEMFImage(void)
886 (void) UnregisterMagickInfo("EMF");
887 (void) UnregisterMagickInfo("WMF");