java解析WMF文件

2012-03-11

        最近实习单位布置了一个任务,就是要用java解析微软图元文件wmf图像文件的参数信息,懵懵懂懂做了一个礼拜,任务基本上完成了,在此过程中有很多误区,故在此跟大家分享一下自己的感受,希望能给有同样需求的IT人员一点点灵感,不至于陷入到程序中去。

   首先简单介绍一下关于微软的wmf文件:微软的wmf文件分为两种一种是标准的图元文件,一种是活动式图元文件,活动式图元文件与标准的图元文件的主要区别是,活动式图元文件包含了图像的原始大小和缩放信息,本文主要介绍活动式图元文件,关于两种文件的具体定义可在百度百科中找到,本文主要讲解用程序如何解析活动式图元文件的坐标信息和颜色信息。

  以下是活动式图元文件的文件结构信息:

WMF 文件格式:
文件缩放信息:0x16字节
typedef struct _PlaceableMetaHeader
{
  DWORD Key;           /* 固定大小以相反顺序出现 9AC6CDD7h */
  WORD  Handle;        /* Metafile HANDLE number (always 0) */
  SHORT Left;          /* Left coordinate in metafile units */
  SHORT Top;           /* Top coordinate in metafile units */
  SHORT Right;         /* Right coordinate in metafile units */
  SHORT Bottom;        /* Bottom coordinate in metafile units */
  WORD  Inch;          /* Number of metafile units per inch */
  DWORD Reserved;      /* Reserved (always 0) */
  WORD  Checksum;      /* Checksum value for previous 10 WORDs */
} PLACEABLEMETAHEADER;

紧接文件缩放信息的是 WMFHEAD, 0x12字节
typedef struct _WindowsMetaHeader
{
  WORD  FileType;       /* Type of metafile (0=memory, 1=disk) */
  WORD  HeaderSize;     /* Size of header in WORDS (always 9) */
  WORD  Version;        /* Version of Microsoft Windows used */
  DWORD FileSize;       /* Total size of the metafile in WORDs */
  WORD  NumOfObjects;   /* Number of objects in the file */
  DWORD MaxRecordSize;  /* The size of largest record in WORDs */
  WORD  NumOfParams;    /* Not Used (always 0) */
} WMFHEAD;

紧接WMFHEAD的是WMFRECORD
typedef struct _StandardMetaRecord
{
    DWORD Size;          /* Total size of the record in WORDs */
    WORD  Function;      /* Function number (defined in WINDOWS.H) */
    WORD  Parameters[];  /* Parameter values passed to function */
} WMFRECORD;

每一个record中储存的是Windows GDI绘图函数的代码及每个函数对应的参数.这样的
话整个wmf文件就由这样的函数编码与参数组成。就像下面这样:
Record Name        Function Number
AbortDoc        0x0052
Arc        0x0817
Chord        0x0830
DeleteObject        0x01f0
Ellipse        0x0418

以上是wmf文件的结构信息,下面以一个例子说明:



字节图

以上前20个字节记录了文件的原始大小与一些规定的信息,

typedef struct _PlaceableMetaHeader
{
  DWORD Key;           /* Magic number (always 9AC6CDD7h) */
  WORD  Handle;        /* Metafile HANDLE number (always 0) */
  SHORT Left;          /* Left coordinate in metafile units */
  SHORT Top;           /* Top coordinate in metafile units */
  SHORT Right;         /* Right coordinate in metafile units */
  SHORT Bottom;        /* Bottom coordinate in metafile units */
  WORD  Inch;          /* Number of metafile units per inch */
  DWORD Reserved;      /* Reserved (always 0) */
  WORD  Checksum;      /* Checksum value for previous 10 WORDs */
} PLACEABLEMETAHEADER;

根据上面wmf文件的格式可知为:

d7 cd c6 9a(这是规定的信息)

00 00(这是保留信息) 

91 00 bf f3 c8 02 a7 f5(这是图元文件的最左端与最右端坐标信息)

20 01(记录了每英寸的逻辑单位数)

00 00 00 00 (保留字段)

70 52(文件校验位) ,校验代码如下:

/**
  * 判断此文件是否为活动式wmf文件
  *
  * @param path
  * @throws IOException
  */
private byte[] opinionHead(String path) throws IOException {
  FileInputStream fs = new FileInputStream(path);
  // 对头22个字节进行判断
  byte bf[] = new byte[22];
  fs.close();// 关闭文件流
  System.out.println((bf[0] & 0xff) + "\t" + (bf[1] & 0xff) + "\t"
    + (bf[2] & 0xff) + "\t" + (bf[3] & 0xff));
  // 对开始的四个字节进行校验
  if (((bf[0] & 0xff) != 215) || ((bf[1] & 0xff) != 205)
    || ((bf[2] & 0xff) != 198) || ((bf[3] & 0xff) != 154)) {
   bf = null;
   throw new IOException("此文件不为活动式wmf文件.....");
  }
  // 验证文件校验位
  int index = 0;
  for (int i = 0; i < 20; i += 2) {
   if (i == 0) {
    index = (((int) bf[i + 1] & 0xff) << | ((int) bf[i] & 0xff);
   } else if (i < 19) {
    index ^= (((int) bf[i + 1] & 0xff) << | ((int) bf[i] & 0xff);
   }
  }
  if (index != ((((int) bf[21] & 0xff) << | ((int) bf[20] & 0xff))) {
   bf = null;
   throw new IOException("此wmf文件已损坏!");
  }
  return bf;
}



接下来的信息为文件头信息:

typedef struct _WindowsMetaHeader
{
  WORD  FileType;       /* Type of metafile (0=memory, 1=disk) */
  WORD  HeaderSize;     /* Size of header in WORDS (always 9) */
  WORD  Version;        /* Version of Microsoft Windows used */
  DWORD FileSize;       /* Total size of the metafile in WORDs */
  WORD  NumOfObjects;   /* Number of objects in the file */
  DWORD MaxRecordSize;  /* The size of largest record in WORDs */
  WORD  NumOfParams;    /* Not Used (always 0) */
} WMFHEAD;


01 00(当为0的时候表示此文件只存在于内存,当为1的时候表示此文件是以文件形式存在的)

09 00(是一个固定值,表示文件头的大小总是9个字大小,即18个字节)

00 03(使用的windows版本号一般是windows 3系列)

4d 00 00 00 (文件大小以字为单位不包含 文件缩放信息的22字节)

02 00(Number of objects in the file )

0f 00 00 00(此文件的最大记录数)
00 00(保留位)



接下来是记录信息:

typedef struct _StandardMetaRecord
{
    DWORD Size;          /* Total size of the record in WORDs */
    WORD  Function;      /* Function number (defined in WINDOWS.H) */
    WORD  Parameters[];  /* Parameter values passed to function */
} WMFRECORD;

根据上面给出的文件二进制信息,

可知第一个记录的大小为: 04 00 00 00(即8字节大小)

对应函数的十六进制为01 03

函数参数为 00 08



第二个记录的大小为:05 00 00 00

对应函数的十六进制为:02 0b

函数参数为:f3 bf,00 91

...........................

依此类推,关于windows gdi函数与十六进制的对应关系,由于一些原因,本人只收集了一部分,希望有这方面资料的高手能贴上来,供大家分享。

AbortDoc 0x0052
Arc 0x0817
Chord 0x0830
DeleteObject 0x01f0
Ellipse 0x0418
EndDoc 0x005E
EndPage 0x0050
ExcludeClipRect 0x0415
ExtFloodFill 0x0548
FillRegion 0x0228
FloodFill 0x0419
FrameRegion 0x0429
IntersectClipRect 0x0416
InvertRegion 0x012A
LineTo 0x0213
MoveTo 0x0214
OffsetClipRgn 0x0220
OffsetViewportOrg 0x0211
OffsetWindowOrg 0x020F
PaintRegion 0x012B
PatBlt 0x061D
Pie 0x081A
RealizePalette 0x0035
Rectangle 0x041B
ResetDc 0x014C
ResizePalette 0x0139
RestoreDC 0x0127
RoundRect 0x061C
SaveDC 0x001E
ScaleViewportExt 0x0412
ScaleWindowExt 0x0410
SelectClipRegion 0x012C
SelectObject 0x012D
SelectPalette 0x0234
SetTextAlign 0x012E
SetBkColor 0x0201
SetBkMode 0x0102
SetDibToDev 0x0d33
SetMapMode 0x0103
SetMapperFlags 0x0231
SetPalEntries 0x0037
SetPixel 0x041F
SetPolyFillMode 0x0106
SetRelabs 0x0105
SetROP2 0x0104
SetStretchBltMode 0x0107
SetTextAlign 0x012E
SetTextCharExtra 0x0108
SetTextColor 0x0209
SetTextJustification 0x020A
SetViewportExt 0x020E
SetViewportOrg 0x020D
SetWindowExt 0x020C
SetWindowOrg 0x020B
StartDoc 0x014D
StartPage 0x004F

关于各个函数的功能大家也可在百度百科中找到。

  然而事情并没有就这么完,光是知道上面的那些并不能解析出我们需要的信息,因为上面得到的坐标信息是文件的逻辑坐标信息,关于逻辑坐标转换为设备坐标需要用到一些数学知识,公式如下:

下面的公式是将窗口(逻辑)坐标转化为视口(设备)坐标: 
 
xViewport   =   (xWindow   -   xWinOrg)*xViewExt/xWinExt   +   xViewOrg
yViewport   =   (yWindow   -   yWinOrg)*yViewExt/yWinExt   +   yViewOrg
(xWindow,yWindow)是待转换的逻辑点,(xViewport,yViewport)是转换后的设备坐标。 
设备坐标的视口原点(xViewOrg,yViewOrg)和逻辑坐标的窗口原点(xWinOrg,yWinOrg)默认情况下均被设置成(0,0),但具体情况下可以改变;
(xWinExt,yWinExt)是逻辑坐标的窗口范围;(xViewExt,yViewExt)是设备坐标的窗口范围,在多数映射方式下,范围是映射方式所隐含的,


下面给出具体的说明:

根据以上给出的字节信息:

此文件的逻辑坐标为(在windows gdi函数中设置逻辑坐标的函数十六进制为020b):(0091,f3bf);对应上面公式的(xWindow   ,yWindow   )

此文件的逻辑横纵轴的长度为(在windows gdi函数中设置逻辑坐标系横纵轴的函数十六进制为020c):(0237,01e8); 对应上面公式的(xWinOrg,yWinOrg)

现在对活动式wmf文件的解析基本上已经差不多了,关于上面公式的(xViewExt,yViewExt)可以自己设置。



初次发表技术博文,希望指正出有错误的地方,谢谢!

图详见本人51博客地址:
http://chenyanxi.blog.51cto.com

猜你喜欢

转载自chenyan-xi1226-163-com.iteye.com/blog/1575931