conyzhou的開發手記
阿新 • • 發佈:2019-01-08
(1)說明
這個轉換器是為了將RTF檔案流轉換成為記憶體中的段、句邏輯結構。
(2)RTF格式說明
參考的是微軟的RTF格式說明
(3)程式碼說明
i.首先是定義的邏輯結構,以類介面的形式提供以下是定義
class IParser;
class IParagraphs;
class IParagraph;
class ISentences;
class ISentence;
class IParaStyle;
class IParser
{
/* the method of rtf parser */
public:
/*
parse the rtf stream and create format text
char* stream - (in)the rtf stream buffer
return error code if need.
*/
virtual int parse( char* stream ) = 0;
/*
release object
*/
virtual void release() = 0;
/* the properties of parser */
public:
/*
get paragraphs of rtf
*/
virtual IParagraphs* get_paragraphs() = 0;
/*
get fonts count
*/
virtual int get_fntcount() = 0;
/*
get font
*/
virtual FontStyle* get_fnt( int nIndex ) = 0;
/*
get color count
*/
virtual int get_clrcount() = 0;
/*
get color
*/
virtual ColorStyle get_color( int nIndex ) = 0;
};
class IParagraphs
{
/* the methods of paragraphs */
public:
/*
add color into tables
*/
virtual void add( IParagraph* paragraph ) = 0;
/*
release object
*/
virtual void release() = 0;
/*
copy object
*/
virtual IParagraphs* copy() = 0;
/* the properties of paragraphs */
public:
/*
the paragraph count
*/
virtual int count() = 0;
/*
the paragraph item
*/
virtual IParagraph* item( int nIndex ) = 0;
/*
the top item
*/
virtual IParagraph* top() = 0;
/*
the header item
*/
virtual IParagraph* header() = 0;
};
class IParagraph
{
/* the methods of paragraph */
public:
/*
release object
*/
virtual void release() = 0;
/* the properties of paragraph */
public:
/*
get paragraph sentences
*/
virtual ISentences* get_sentences() = 0;
/*
get paragraph style
*/
virtual IParaStyle* get_style() = 0;
};
class ISentences
{
/* the methods of sentences */
public:
/*
add sentence into tables
*/
virtual void add( ISentence* sentence ) = 0;
/*
release object
*/
virtual void release() = 0;
/* the properties of paragraphs */
public:
/*
the sentence count
*/
virtual int count() = 0;
/*
the sentence item
*/
virtual ISentence* item( int nIndex ) = 0;
};
class ISentence
{
/* the methods of sentences */
public:
/*
release object
*/
virtual void release() = 0;
/* the properties of paragraphs */
public:
/*
content property
*/
virtual char* get_content() = 0;
virtual void set_content(char* content) = 0;
/*
sentence style property
*/
virtual ISentStyle* get_style() = 0;
virtual void set_style( ISentStyle* style ) = 0;
/*
sentence size
*/
virtual SIZE& get_size() = 0;
};
ii.實現說明,對應IParser介面的實現物件是XParser,實現主要用到幾個資料結構:
stack mstk 這個棧主要是為了處理{}配對用的,當棧空的時候表示流分析完成
stack mstatus 這個棧表示當前{}的屬性是什麼舉個例子{/fonttbl{/f0/fnil/fprq2/fcharset134 Times New Roman;}},當看到第一個{時壓入屬性fonttbl,當碰到第二個{時壓入屬性f,在棧中直到碰到}時再彈出,通過判斷棧頂來確定當前的要處理的屬性。
iii.主要程式碼說明,最主要的是程式碼解析部份的程式碼,主要對這部份程式碼做一個說明
int XParser::parse( char* stream )
{
char* psz = stream;
/* parse the rtf stream */
while( psz )
{
switch( *psz )
{
case '{':
{
/* push the '{' into stack and pop it find '}' */
mstk.push(*psz);
psz++;
/* get main key word and push it into stack */
ErrorCode nErrNum;
long nOff = 0;
if( ( nErrNum = (ErrorCode)parsestatus(psz,nOff) ) != ec_OK )
return nErrNum;
psz += nOff;
/* move stream pointer*/
break;
}
case '}':
{
/* if stack overflow donothing */
if( !mstk.size() )
return ec_StackOverflow;
/* pair '{' and pop it */
mstk.pop();
/* here trace debug info*/
tracedebug();
/* pop status */
mstatus.pop();
/* parse is finished */
if( !mstk.size() )
return ec_OK;
/* move stream pointer*/
psz++;
break;
}
case '//':
{
if( isbreak( *psz , *(psz+1) ) )
{
ErrorCode nErrNum;
long nOff = 0;
if( ( nErrNum = (ErrorCode)parseproperty(psz,nOff) ) != ec_OK )
return nErrNum;
psz += nOff;
}
else
{
ErrorCode nErrNum;
long nOff = 0;
if( ( nErrNum = (ErrorCode)parsedefault(psz,nOff) ) != ec_OK )
return nErrNum;
psz += nOff;
}
break;
}
default:
{
ErrorCode nErrNum;
long nOff = 0;
if( ( nErrNum = (ErrorCode)parsedefault(psz,nOff) ) != ec_OK )
return nErrNum;
psz += nOff;
break;
}
}
}
return ec_OK;
}
這裡要說明的因為文字內容中存在/{}這些特殊的字元,這些字元在RTF有特殊的意義,所以當碰到/字元時要向前再看一個字元,以確定是不後面跟著的是一個屬性字串,這就是isbreak( *psz , *(psz+1) )這句要做的事情,如果是就當屬性流做處理,如果不是就當成轉義字串做處理。
(4)其它
i.關於字FontStyle到LOGFONT的轉換,因為RTF中的字元的大小用的是halt-points 做單位要想生成相關LOGFONT中的lfHeight要做轉換程式碼如下:
HDC hdc = GetDC(NULL);
LONG yPerInch = ::GetDeviceCaps( hdc , LOGPIXELSY );
::ReleaseDC(NULL,hdc);
mlogfnt.lfHeight = style->mfntsize*yPerInch/144;
這是參考CFontDialog中一個函式實現的FillInLogFont
ii.程式碼是用兩天時間改寫的,寫完覺得如果用Lex & Yacc的話可能會寫得更安全些。
iii.對於Word儲存的RTF文件,這兒沒有做處理,它的文字內容儲存在不同的屬性中,如儲存title,bookmark中
所有程式碼可以從 http://www.xppt.com/code.rar 下載,如果程式碼有錯誤請回信:[email protected]