文档章节

java解析WMF文件

timer_xi
 timer_xi
发布于 2014/08/18 17:08
字数 1865
阅读 9
收藏 0

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) << 8) | ((int) bf[i] & 0xff);
   } else if (i < 19) {
    index ^= (((int) bf[i + 1] & 0xff) << 8) | ((int) bf[i] & 0xff);
   }
  }
  if (index != ((((int) bf[21] & 0xff) << 8) | ((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)可以自己设置。

 

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

本文出自 “陈砚羲” 博客,转载请与作者联系!

© 著作权归作者所有

timer_xi
粉丝 2
博文 53
码字总数 64113
作品 0
深圳
程序员
私信 提问
Aspose.Slides for Java 8.4.0 发布

Aspose.Slides for Java 8.4.0 发布,该版本引入了计算连接器线形状的角度机制;修复了导出 PDF 时丢失 WMF、EMF 和其他图像的问题;修复了导出 PDF 和缩略图的文本渲染问题;改进了图表渲染...

oschina
2014/04/02
475
0
【XML系列】详解JDOM解析XML

【XML系列】详解JDOM解析XML 一、前言 JDOM是Breet Mclaughlin和Jason Hunter两大Java高手的创作成果,2000年初,JDOM作为一个开放源代码项目正式开始研发。JDOM是一种解析XML的Java工具包。...

磊神Ray
2011/11/01
196
0
关于java的DNS解析IP缓存问题

java对DNS解析IP进行缓存,默认缓存超时时间为-1(在重启JVM前永久缓存), 在第一次访问某域名后将会缓存解析到的IP地址,之后直接从缓存里获得所需的信息(如IP地址),而无需再访问DNS服务...

Sub
2013/08/09
798
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
2018/08/08
0
0
JVM规范系列第5章:加载、链接与初始化

加载是根据特定名称查找类或接口类型的二进制表示(Binary Representation),并由此二进制表示创建类或接口的过程。 加载,就是指去寻找类或接口的过程。 链接是为了让类或接口可以被 Java...

陈树义
2018/12/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

哪些情况下适合使用云服务器?

我们一直在说云服务器价格适中,具备弹性扩展机制,适合部署中小规模的网站或应用。那么云服务器到底适用于哪些情况呢?如果您需要经常原始计算能力,那么使用独立服务器就能满足需求,因为他...

云漫网络Ruan
今天
5
0
Java 中的 String 有没有长度限制

转载: https://juejin.im/post/5d53653f5188257315539f9a String是Java中很重要的一个数据类型,除了基本数据类型以外,String是被使用的最广泛的了,但是,关于String,其实还是有很多东西...

低至一折起
今天
15
0
OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
11
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
9
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部