RTF文件结构分析及其应用
RTF是一種非常流行的文件結構,很多文字編輯器都支持它,VB等開發工具
甚至還提供了Richtxtbox的控件。編寫通用工具的程序員應該考慮在自己的軟
件中加入讀寫RTF文件的功能,這樣就需要對RTF的結構有充分的了解。而現在
最重要的信息發布手段莫過于WWW了,在編輯軟件中提供RTF到HTML的轉換也是程
序員應該考慮的事情。盡管WORD中已經有這個功能,但不能因此就對您的顧客
說:“先用我的程序存成RTF,然后再用WORD......”。
下面將對RTF文件結構分析及其應用進行討論。
一、RTF文件結構分析
RTF的結構并不復雜,但內容繁多,本文不可能一一說明,只能從總體上討
論一下(如果想閱讀詳盡的RTF文檔,則可在Internet上尋找或與筆者聯系。)
每個RTF文件都是一個文本文件,顯示時由RTF閱讀器格式化。文件開始處是
{\rtf,它作為RTF文件的標志是必不可少的,RTF閱讀器根據它來判斷一個文件
是否為RTF格式。然后是文件頭和正文,文件頭包括字體表、文件表、顏色表等
幾個數據結構,正文中的字體、表格的風格就是根據文件頭的信息來格式化的。
每個表用一對大括號括起來,當中包含了很多用字符“\”開始的命令。例如,
某個顏色表如下:
{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}
開始時用\colortbl標明大括號內是顏色表,接著是\red0\green0\blue0,
登記了一種顏色,這種顏色的紅綠藍分量都為0。其它表依此類推。文件頭之后是
正文,正文由版面格式化命令、文字和各種特殊命令組成。其中只有特殊命令
用大括號括起來,而版面格式化命令和文字是“開放式”的,從而把文字和命
令分離。文件結束時有一個“}”,和第一個“{”對應。在整個文件中,“}”
和“{”必須一一對應。這種格式是RTF閱讀器和轉換器算法的基礎。
RTF格式還有一個特別之處,就是有些字符在命令中有特殊的含義,所以當
它們作為文本出現時需要在它們的前面加一個“\”,例如“\”本身就要表示
為“\\”。事實上,這種形式在大多數編程語言中是很常見的。
二、算法分析
本節介紹的算法雖然是針對RTF的讀寫,但也是一般文件過濾器通用的方
法,適用于各種格式文件之間的轉換。具體來說就是把各種文件都轉換成一種
中間格式,再根據要求進行顯示或轉換。其中有個原則是一定要遵守的:程序必
須能過濾掉不認識的格式。各種文件都有其特殊的格式,在轉換過程中不可避免
會出現格式損失的現象,在算法中要考慮這種情況。對于RTF這類格式化文本
文件來說,最重要的是要正確地顯示或轉換文件的大小、顏色、字體等風格。
因此,在程序中應該用一個數據結構把這些信息存起來,這個結構就是所謂的
中間格式,怎樣規定悉聽尊便。以下是其流程圖:
三、難點分析
在開發過程中我們遇到了不少問題,其中有兩個問題特別有意思。
第一個問題是中文的表示方式。在RTF中中文用命令的形式表示:
“\'內碼”。內碼就是漢字機內碼。不過,請注意:RTF是文本文件,內碼
是用ASCII碼來儲存的,必須把它轉換成數字才能使用。例如,“電子與電腦”
在RTF中的形式是:
\'b5\'e7\'d7\'d3\'d3\'eb\'b5\'e7\'c4\'d4
第二個是圖片的問題,這也是本文的重點。RTF中圖片以兩種方式存在:第
一種方式是直接嵌入,以{\pict開始;第二種方式是作為OLE對象嵌入,這時以
{\object開始。當RTF處理器能直接使用OLE時,RTF文件中提供了OLE的數據;否
則,文件中直接提供圖片的數據,以{\result開始。在使用中最常見的圖片格式
是內含DIB BITMAP的元文件(METAFILE),這種格式在SDK中沒有說明,而且
在RTF中是以壓縮形式儲存的,所以在轉換時有一定困難。我們采取了一種比較
新的方法:先把META FILE的數據讀出來存成一個文件,然后用GetMetafile和
PlayMetafile函數把文件中的圖形打印在一個內存DC上,最后用抓圖的方法把
它存成BITMAP文件。以下是具體程序:
voidWmfToBmp(intwidth,intheight,char *metafile)
{
//width為圖片寬度,height為圖片高度,*metafile為元文件名
static HMETAFILE hmf;
charnum[4];
FILE *fh;
HDC hMemDC;
HBITMAP hbmp,hold;
BITMAPFILEHEADER hdr;
LPBITMAPINFOHEADER lpbmpih;
HGLOBAL hg;
BitmapCount++;
if(BitmapCount==3)
{
width=128;
height=132;
}
if(BitmapCount==1)
{
width=80;
height=50;
}
ToString10(BitmapCount,num);
//把字符串轉換成數字的函數
lstrcpy(bmpFile,FilePath);
lstrcat(bmpFile,num);
lstrcpy(gifFile,bmpFile);
lstrcat(bmpFile,".bmp");
lstrcat(gifFile,".gif");
hmf=GetMetaFile(metafile);
hDisplayDC=CreateDC("DISPLAY",NULL,NULL,NULL);
hMemDC=CreateCompatibleDC(hDisplayDC);
hg=GlobalAlloc(GHND,sizeof(BITMAPINFOHEADER));
lpbmpih=(LPBITMAPINFOHEADER)GlobalLock(hg);
lpbmpih->biSize=sizeof(BITMAPINFOHEADER);
lpbmpih->biWidth=width;
lpbmpih->biHeight=height;
lpbmpih->biPlanes=1;
lpbmpih->biBitCount=8;
lpbmpih->biCompression=0;
hbmp=CreateCompatibleBitmap(hDisplayDC,width,height);
GlobalUnlock(hg);
GlobalFree(hg);
hold=SelectObject(hMemDC,hbmp);
SetMapMode(hMemDC,MM_ANISOTROPIC);
SetWindowOrgEx(hMemDC,0,0,NULL);
SetViewportExtEx(hMemDC,width,height,NULL);
PlayMetaFile(hMemDC,hmf);
hbmp=SelectObject(hMemDC,hold);
if((fh=fopen(bmpFile,"w+b"))==NULL)
{
return;
}
hdr.bfType=0x4d42;
hdr.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD)+width*height;
hdr.bfReserved1=0;
hdr.bfReserved2=0;
hdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);
fwrite((LPSTR)&hdr,sizeof(BITMAPFILEHEADER),1,fh);
hg=GlobalAlloc(GHND,sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD));
lpbmpih=(LPBITMAPINFOHEADER)GlobalLock(hg);
lpbmpih->biSize=sizeof(BITMAPINFOHEADER);
lpbmpih->biWidth=width;
lpbmpih->biHeight=height;
lpbmpih->biPlanes=1;
lpbmpih->biBitCount=8;
lpbmpih->biCompression=0;
lpbmpih->biSizeImage=width*height;
lpbmpih->biClrUsed=256;
lpbmpih->biClrImportant=0;
j=GetDIBits(hMemDC,hbmp,0,height,NULL,(BITMAPINFO*)lpbmpih,DIB_RGB_COLORS);
lpbmpih->biSize=sizeof(BITMAPINFOHEADER);
fwrite(lpbmpih,sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD),1,fh);
GetDIBits(hMemDC,hbmp,0,height,(LPSTR)FileBuffer,(BITMAPINFO*)lpbmpih,DIB_RGB_COLS);
fwrite(FileBuffer,width*height,1,fh);
GlobalUnlock(hg);
GlobalFree(hg);
fclose(fh);
DeleteDC(hMemDC);
DeleteDC(hDisplayDC);
DeleteObject(hbmp);
DeleteObject(hold);
DeleteMetaFile(hmf);
}//end function WmfToBmp()
用這種方法還有一個好處,那就是當你從最底層開始開發RTF閱讀器時,只
需把PlayMetaFile的參數從內存DC換成屏幕DC就可以顯示圖片了。
四.RTF格式的擴展
最后討論一下RTF格式的擴展。RTF格式作為一個標準應該是統一的,但在
某種情況下進行擴展是必要的。最明顯的例子是微軟的WORD,它有自己獨有
的RTF命令。如果想使自己的軟件在技術上占有優勢,也可以通過創造新的RTF
命令來實現。例如,如果你希望在軟件中支持DHTML,則可在RTF中嵌入
{\dhtml或{\java之類的命令。由于RTF閱讀器有過濾不認識命令的功能,所以
這樣做不會影響RTF文件的通用性。
總結
以上是生活随笔為你收集整理的RTF文件结构分析及其应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你可能不知道的java、python、J
- 下一篇: android一键锁屏代码