java wmf_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文件的结构信息,下面以一个例子说明:

23c05f704f2863e68a20459555119cdd.png

以上前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)可以自己设置。

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将WMF转换为SVG并设置stroke-width,可以使用Java的Apache Batik库。Apache Batik是一个用于处理SVG的Java库,它提供了将WMF转换为SVG的功能。 以下是一个将WMF转换为SVG并设置stroke-width的示例代码: ```java // 读取WMF文件 InputStream is = new FileInputStream("example.wmf"); // 创建转换器 WMFTranscoder transcoder = new WMFTranscoder(); // 设置转换参数 TranscodingHints hints = new TranscodingHints(); hints.put(ImageTranscoder.KEY_WIDTH, (float) 400); hints.put(ImageTranscoder.KEY_HEIGHT, (float) 300); transcoder.setTranscodingHints(hints); // 执行转换 TranscoderInput input = new TranscoderInput(is); ByteArrayOutputStream os = new ByteArrayOutputStream(); TranscoderOutput output = new TranscoderOutput(os); transcoder.transcode(input, output); // 将转换后的SVG字符串解析为DOM对象 String svgString = os.toString("UTF-8"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputSource isource = new InputSource(new StringReader(svgString)); Document doc = builder.parse(isource); // 设置stroke-width Element root = doc.getDocumentElement(); NodeList pathNodes = root.getElementsByTagName("path"); for (int i = 0; i < pathNodes.getLength(); i++) { Element path = (Element) pathNodes.item(i); path.setAttribute("stroke-width", "2"); } // 将DOM对象写入文件 Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.transform(new DOMSource(doc), new StreamResult(new File("example.svg"))); ``` 在上面的代码中,首先读取WMF文件并创建WMFTranscoder对象。然后设置转换参数并执行转换,将转换后的SVG字符串解析为DOM对象。接着,遍历所有的path元素并设置stroke-width属性的值为2。最后,将DOM对象写入SVG文件。 注意:在上面的代码中,设置的stroke-width值为2,可以根据需要进行修改。另外,还需要在代码中添加异常处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值