java解析WMF文件

2012-03-11java

        最近實習單位佈置了一個任務,就是要用java解析微軟圖元文件wmf圖像文件的參數信息,懵懵懂懂作了一個禮拜,任務基本上完成了,在此過程當中有不少誤區,故在此跟你們分享一下本身的感覺,但願能給有一樣需求的IT人員一點點靈感,不至於陷入到程序中去。windows

   首先簡單介紹一下關於微軟的wmf文件:微軟的wmf文件分爲兩種一種是標準的圖元文件,一種是活動式圖元文件,活動式圖元文件與標準的圖元文件的主要區別是,活動式圖元文件包含了圖像的原始大小和縮放信息,本文主要介紹活動式圖元文件,關於兩種文件的具體定義可在百度百科中找到,本文主要講解用程序如何解析活動式圖元文件的座標信息和顏色信息。數組

  如下是活動式圖元文件的文件結構信息:app

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;ide

緊接文件縮放信息的是 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        0x0418blog

以上是wmf文件的結構信息,下面以一個例子說明:ip

 

字節圖

以上前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)能夠本身設置。

 

初次發表技術博文,但願指正出有錯誤的地方,謝謝!

相關文章
相關標籤/搜索