大熊的口袋

 

Convert Placeable WMF To EMF

  什么是PlaceableWMF?为了保证wmf矢量图片在不同设备上播放的效果(缩放比、大小等)一致,微软提供了一个名为Placeable WMF的文件格式。他实际上就是在普通的WMF文件之前加了一个额外的文件头来记录WMF在不同设备上播放时的映射方式和缩放信息。微软称之为WmfPlaceableFileHeader:
#include <pshpack2.h>   // set structure packing to 2

typedef 
struct
{
    INT16           Left;
    INT16           Top;
    INT16           Right;
    INT16           Bottom;
}
 PWMFRect16;

struct WmfPlaceableFileHeader
{
    UINT32          Key;            
// GDIP_WMF_PLACEABLEKEY
    INT16           Hmf;            // Metafile HANDLE number (always 0)
    PWMFRect16      BoundingBox;    // Coordinates in metafile units
    INT16           Inch;           // Number of metafile units per inch
    UINT32          Reserved;       // Reserved (always 0)
    INT16           Checksum;       // Checksum value for previous 10 WORDs
}
;

#include 
<poppack.h>
一般我们使用这个文件头来判断该文件是否为一个Placeable WMF文件:
#ifndef GDIP_WMF_PLACEABLEKEY 
# define GDIP_WMF_PLACEABLEKEY 
0x9ac6cdd7L
#endif

bool IsPlaceableWMFheader(const WmfPlaceableFileHeader *metafileheader)
{
    WORD 
*pw;
    WORD  cs;
    INT   i;
    
    
// check magic number.
    if (metafileheader->Key != GDIP_WMF_PLACEABLEKEY)    
    
{
        
return false;
    }

    
    
// test checksum of header.
    pw = (WORD *)metafileheader;
    cs 
= *pw;
    
++pw;

    
for (i = 0; i < 9; i++)    
    
{
        cs 
^= *pw;
        
++pw;
    }

    
    
if (cs != metafileheader->Checksum)    
    
{
        assert(
0 && L"校验和错误,WMF文件可能被破坏,但是微软允许播放!");
    }

    
    
// check resolution.
    if ((metafileheader->Inch <= 0||
        (metafileheader
->Inch > 2540))
    
{
        
return false;
    }

    
    
return true;
}


但是由于windows 2000之后就不再提供对16位gdi函数的支持。所以播放WMF文件都是将其转换为EMF文件来播放的。普通的WMF文件只需要调用SetWinMetaFileBits方法即可顺利转换为EMF播放。但是由于Placeable WMF包含了映射和缩放信息,我们必须在转换的时候要将这些信息保存到EMF中去。这就要关注下SetWinMetaFileBits方法的最后一个参数了。
WmfPlaceableFileHeader里的BoundingBox和Inch一起指定了该文件播放时的实际大小。我们在转换到EMF的时候必须利用这两个信息来生成转换后的EMF的实际长度和宽度。Inch指出了每英寸(inch)有多少个点(dot)。而BoundingBox则是以点(dot)为单位指出实际长度和宽度。那么可以由BoundingBox和Inch来算出以英寸(inch)为单位的长和宽:

WmfPlaceableFileHeader header;
double xInInch = double(header.BoundingBox.Right - header.BoundingBox.Left) / 

(
double)header.Inch;
double yInInch = double(header.BoundingBox.Bottom - header.BoundingBox.Top) / 

(
double)header.Inch;

 

得到实际长宽之后,就可以利用SetWinMetaFileBits来进行转换了。

HENHMETAFILE SetWinMetaFileBits(
  UINT cbBuffer,              
// size of buffer
  CONST BYTE *lpbBuffer,      // metafile data buffer
  HDC hdcRef,                 // handle to reference DC
  CONST METAFILEPICT *lpmfp   // size of metafile picture
);

 

其中最后一个METAFILEPICT类型参数记录转换后的长宽信息。

typedef struct tagMETAFILEPICT {
    LONG mm;
    LONG xExt;
    LONG yExt;
    HMETAFILE hMF;
}
 METAFILEPICT, *LPMETAFILEPICT;


METAFILEPICT中有一个mm成员记录的是映射模式,一般我们选择为MM_ANISOTROPIC。他的映射单位为MM_HIMETRIC模式下的0.01mm。他使用窗口当前的ViewPort。xExt和yExt则分别代表着长和宽。其他模式下的含义请查阅msdn。由于MM_ANISOTROPIC模式下的逻辑单位为0.01毫米,所以我们必须将以英寸为单位的长宽转化为以0.01毫米为单位。
1英寸 == 2.539999918 厘米(公分) == 2539.999918 (0.01毫米)
所以可以设置一个METAFILEPICT提供给SetWinMetaFileBits进行转换:

METAFILEPICT mfp;
METAFILEPICT mfp;
mfp.mm 
= MM_ANISOTROPIC;
mfp.xExt 
= unsigned(xInInch * 2539.999918 + 0.5);
mfp.yExt 
= unsigned(yInInch * 2539.999918 + 0.5);
mfp.hMF 
= NULL;
HENHMETAFILE hEnhMetafile 
= ::SetWinMetaFileBits(size - sizeof

(WmfPlaceableFileHeader), header 
+ sizeof(WmfPlaceableFileHeader),NULL, &mfp);

 

最终的转换函数代码如下:

HENHMETAFILE CreateEnhMetafileFromBuff(BYTE *buff, unsigned size)
{
 
// Placeable Metafile
 if (IsPlaceableWMFheader((WmfPlaceableFileHeader*)buff)) 
{
  
const WmfPlaceableFileHeader *placeable = 

(WmfPlaceableFileHeader
*)buff;
  
// 以twip为单位
  const unsigned& w = placeable->BoundingBox.Right - placeable-

>BoundingBox.Left;
  
const unsigned& h = placeable->BoundingBox.Bottom - placeable-

>BoundingBox.Top;
  
const double& scale = 2539.999918f / (double)placeable->Inch;
  
const unsigned& nw = unsigned((double)w * scale + 0.5);
  
const unsigned& nh = unsigned((double)h * scale + 0.5);

  METAFILEPICT mfp;
  mfp.mm 
= MM_ANISOTROPIC;
  mfp.xExt 
= nw;
  mfp.yExt 
= nh;
  mfp.hMF 
= NULL;
  
return ::SetWinMetaFileBits(
   size 
- sizeof(WmfPlaceableFileHeader), 
   buff 
+ sizeof(WmfPlaceableFileHeader), 
   NULL, 
&mfp);
 }
 

 
// EnhMetafile or Metafile
 HENHMETAFILE hEnhMetafile = ::SetEnhMetaFileBits(size, buff);
 
if (hEnhMetafile == NULL) // WMF
 {
  HDC hDC 
= GetDC(NULL);
  hEnhMetafile 
= ::SetWinMetaFileBits(
   size, buff, hDC, NULL);
  ReleaseDC(NULL, hDC);
 }

 
return hEnhMetafile; 
}



 

posted on 2008-08-28 09:58 大熊的口袋 阅读(2893) 评论(3)  编辑 收藏 引用 所属分类: win32

评论

# re: Convert Placeable WMF To EMF 2009-12-24 14:25 neil

HENHMETAFILE CreateEnhMetafileFromBuff(BYTE *buff, unsigned size)

buff 和size 具体指什么  回复  更多评论   

# re: Convert Placeable WMF To EMF 2009-12-26 15:03 zwp

@neil
wmf的数据起始指针和数据大小啊~~
  回复  更多评论   

# re: Convert Placeable WMF To EMF 2012-02-25 20:50 陈砚羲

博主:
您好!请问用java能解析活动式wmf文件的坐标信息吗?并且是设备坐标信息,我现在能解析出逻辑坐标信息,但就是在转换的时候出了点问题,那个比例因子好难把握。您能指点一下吗?  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


导航

统计

公告

常用链接

留言簿(2)

随笔分类

随笔档案

win32 & debug

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜